Merge branch 'espruino:master' into master
204
apps.json
|
@ -3,7 +3,7 @@
|
|||
"id": "fwupdate",
|
||||
"name": "Firmware Update",
|
||||
"version": "0.02",
|
||||
"description": "Uploads new Espruino firmwares to Bangle.js 2",
|
||||
"description": "[BETA] Uploads new Espruino firmwares to Bangle.js 2. For now, please use the instructions under https://www.espruino.com/Bangle.js2#firmware-updates",
|
||||
"icon": "app.png",
|
||||
"type": "RAM",
|
||||
"tags": "tools,system",
|
||||
|
@ -11,7 +11,7 @@
|
|||
"custom": "custom.html",
|
||||
"customConnect": true,
|
||||
"storage": [],
|
||||
"sortorder": -20
|
||||
"sortorder": 20
|
||||
},
|
||||
{
|
||||
"id": "boot",
|
||||
|
@ -33,10 +33,11 @@
|
|||
"id": "hebrew_calendar",
|
||||
"name": "Hebrew Calendar",
|
||||
"shortName": "HebCal",
|
||||
"version": "0.03",
|
||||
"version": "0.04",
|
||||
"description": "lists the date according to the hebrew calendar",
|
||||
"icon": "app.png",
|
||||
"tags": "",
|
||||
"allow_emulator": false,
|
||||
"tags": "tool,locale",
|
||||
"supports": [
|
||||
"BANGLEJS",
|
||||
"BANGLEJS2"
|
||||
|
@ -47,6 +48,10 @@
|
|||
"name": "hebrew_calendar.app.js",
|
||||
"url": "app.js"
|
||||
},
|
||||
{
|
||||
"name": "hebrewDate",
|
||||
"url": "hebrewDate.js"
|
||||
},
|
||||
{
|
||||
"name": "hebrew_calendar.img",
|
||||
"url": "app-icon.js",
|
||||
|
@ -54,10 +59,25 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{ "id": "golfscore",
|
||||
"name": "Golf Score",
|
||||
"shortName":"golfscore",
|
||||
"version":"0.02",
|
||||
"description": "keeps track of strokes during a golf game",
|
||||
"icon": "app.png",
|
||||
"tags": "outdoors",
|
||||
"allow_emulator": true,
|
||||
"supports" : ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"golfscore.app.js","url":"app.js"},
|
||||
{"name":"golfscore.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "messages",
|
||||
"name": "Messages",
|
||||
"version": "0.10",
|
||||
"version": "0.12",
|
||||
"description": "App to display notifications from iOS and Gadgetbridge",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
|
@ -72,13 +92,14 @@
|
|||
{"name":"messages","url":"lib.js"}
|
||||
],
|
||||
"data": [{"name":"messages.json"},{"name":"messages.settings.json"}],
|
||||
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot-notify.gif"}],
|
||||
"sortorder": -9
|
||||
},
|
||||
{
|
||||
"id": "android",
|
||||
"name": "Android Integration",
|
||||
"shortName": "Android",
|
||||
"version": "0.04",
|
||||
"version": "0.05",
|
||||
"description": "Display notifications/music/etc from Gadgetbridge on Android. This replaces the old Gadgetbridge widget.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,system,messages,notifications",
|
||||
|
@ -95,7 +116,7 @@
|
|||
{
|
||||
"id": "ios",
|
||||
"name": "iOS Integration",
|
||||
"version": "0.06",
|
||||
"version": "0.07",
|
||||
"description": "Display notifications/music/etc from iOS devices",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,system,ios,apple,messages,notifications",
|
||||
|
@ -197,7 +218,7 @@
|
|||
{
|
||||
"id": "locale",
|
||||
"name": "Languages",
|
||||
"version": "0.13",
|
||||
"version": "0.14",
|
||||
"description": "Translations for different countries",
|
||||
"icon": "locale.png",
|
||||
"type": "locale",
|
||||
|
@ -282,7 +303,7 @@
|
|||
{
|
||||
"id": "gbridge",
|
||||
"name": "Gadgetbridge",
|
||||
"version": "0.24",
|
||||
"version": "0.25",
|
||||
"description": "(NOT RECOMMENDED) Handles Gadgetbridge notifications from Android. This is now replaced by the 'Android' app.",
|
||||
"icon": "app.png",
|
||||
"type": "widget",
|
||||
|
@ -538,7 +559,7 @@
|
|||
"icon": "clock-impword.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS"],
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"screenshots": [{"url":"bangle1-impercise-word-clock-screenshot.png"}],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
|
@ -824,7 +845,7 @@
|
|||
{
|
||||
"id": "weather",
|
||||
"name": "Weather",
|
||||
"version": "0.12",
|
||||
"version": "0.13",
|
||||
"description": "Show Gadgetbridge weather report",
|
||||
"icon": "icon.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
|
@ -1127,7 +1148,7 @@
|
|||
{
|
||||
"id": "qrcode",
|
||||
"name": "Custom QR Code",
|
||||
"version": "0.03",
|
||||
"version": "0.04",
|
||||
"description": "Use this to upload a customised QR code to Bangle.js",
|
||||
"icon": "app.png",
|
||||
"tags": "qrcode",
|
||||
|
@ -2147,14 +2168,15 @@
|
|||
{ "id": "snek",
|
||||
"name": "The snek game",
|
||||
"shortName":"Snek",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "A snek game where you control a snek to eat all the apples!",
|
||||
"icon": "snek-icon.js",
|
||||
"screenshots": [{"url":"screenshot_snek.png"}],
|
||||
"icon": "snek.png",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"tags": "game,fun",
|
||||
"storage": [
|
||||
{"name":"snek.app.js","url":"snek.js"},
|
||||
{"name":"snek.img","url":"snek-icon.js","evaluate":true}
|
||||
{"name":"snek.img","url":"snek.icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -3274,7 +3296,7 @@
|
|||
{
|
||||
"id": "dtlaunch",
|
||||
"name": "Desktop Launcher",
|
||||
"version": "0.05",
|
||||
"version": "0.07",
|
||||
"description": "Desktop style App Launcher with six (four for Bangle 2) apps per page - fast access if you have lots of apps installed.",
|
||||
"screenshots": [{"url":"shot1.png"},{"url":"shot2.png"},{"url":"shot3.png"}],
|
||||
"icon": "icon.png",
|
||||
|
@ -3285,8 +3307,11 @@
|
|||
"storage": [
|
||||
{"name":"dtlaunch.app.js","url":"app-b1.js", "supports": ["BANGLEJS"]},
|
||||
{"name":"dtlaunch.app.js","url":"app-b2.js", "supports": ["BANGLEJS2"]},
|
||||
{"name":"dtlaunch.settings.js","url":"settings-b1.js", "supports": ["BANGLEJS"]},
|
||||
{"name":"dtlaunch.settings.js","url":"settings-b2.js", "supports": ["BANGLEJS2"]},
|
||||
{"name":"dtlaunch.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
],
|
||||
"data": [{"name":"dtlaunch.json"}]
|
||||
},
|
||||
{
|
||||
"id": "HRV",
|
||||
|
@ -3795,7 +3820,7 @@
|
|||
"id": "gbmusic",
|
||||
"name": "Gadgetbridge Music Controls",
|
||||
"shortName": "Music Controls",
|
||||
"version": "0.07",
|
||||
"version": "0.08",
|
||||
"description": "Control the music on your Gadgetbridge-connected phone",
|
||||
"icon": "icon.png",
|
||||
"screenshots": [{"url":"screenshot_v1.png"},{"url":"screenshot_v2.png"}],
|
||||
|
@ -3870,7 +3895,7 @@
|
|||
"id": "qmsched",
|
||||
"name": "Quiet Mode Schedule and Widget",
|
||||
"shortName": "Quiet Mode",
|
||||
"version": "0.04",
|
||||
"version": "0.05",
|
||||
"description": "Automatically turn Quiet Mode on or off at set times, and change LCD options while Quiet Mode is active.",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot_b1_main.png"},{"url":"screenshot_b1_edit.png"},{"url":"screenshot_b1_lcd.png"},
|
||||
|
@ -3988,11 +4013,11 @@
|
|||
{
|
||||
"id": "thermom",
|
||||
"name": "Thermometer",
|
||||
"version": "0.03",
|
||||
"version": "0.04",
|
||||
"description": "Displays the current temperature in degree Celsius, updated every 20 seconds",
|
||||
"icon": "app.png",
|
||||
"tags": "tool",
|
||||
"supports": ["BANGLEJS"],
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"thermom.app.js","url":"app.js"},
|
||||
|
@ -4117,14 +4142,17 @@
|
|||
{
|
||||
"id": "vectorclock",
|
||||
"name": "Vector Clock",
|
||||
"version": "0.02",
|
||||
"version": "0.03",
|
||||
"description": "A digital clock that uses the built-in vector font.",
|
||||
"icon": "app.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS"],
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"screenshots": [{"url":"bangle1-vector-clock-screenshot.png"}],
|
||||
"screenshots": [
|
||||
{"url":"bangle2-vector-clock-screenshot.png"},
|
||||
{"url":"bangle1-vector-clock-screenshot.png"}
|
||||
],
|
||||
"storage": [
|
||||
{"name":"vectorclock.app.js","url":"app.js"},
|
||||
{"name":"vectorclock.img","url":"app-icon.js","evaluate":true}
|
||||
|
@ -4442,9 +4470,9 @@
|
|||
"name": "A Battery Widget (with percentage)",
|
||||
"shortName":"A Battery Widget",
|
||||
"icon": "widget.png",
|
||||
"version":"1.01",
|
||||
"version":"1.02",
|
||||
"type": "widget",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"description": "Simple and slim battery widget with charge status and percentage",
|
||||
"tags": "widget,battery",
|
||||
|
@ -4711,7 +4739,7 @@
|
|||
{ "id": "pooqroman",
|
||||
"name": "pooq Roman watch face",
|
||||
"shortName":"pooq Roman",
|
||||
"version":"0.0.0",
|
||||
"version":"0.03",
|
||||
"description": "A classic watch face with a certain dynamicity. Most amusing in 24h mode. Slide up to show more hands, down for less(!). By design does not support standard widgets, sorry!",
|
||||
"icon": "app.png",
|
||||
"type": "clock",
|
||||
|
@ -4746,7 +4774,7 @@
|
|||
{
|
||||
"id": "weatherClock",
|
||||
"name": "Weather Clock",
|
||||
"version": "0.03",
|
||||
"version": "0.04",
|
||||
"description": "A clock which displays current weather conditions (requires Gadgetbridge and Weather apps).",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screens/screen1.png"}],
|
||||
|
@ -4829,9 +4857,10 @@
|
|||
"id": "ptlaunch",
|
||||
"name": "Pattern Launcher",
|
||||
"shortName": "Pattern Launcher",
|
||||
"version": "0.02",
|
||||
"version": "0.10",
|
||||
"description": "Directly launch apps from the clock screen with custom patterns.",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"main_menu_add.png"}, {"url":"add_pattern.png"}, {"url":"select_app.png"}, {"url":"main_menu_manage.png"}, {"url":"manage_patterns.png"}],
|
||||
"tags": "tools",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
|
@ -4842,20 +4871,105 @@
|
|||
],
|
||||
"data": [{"name":"ptlaunch.patterns.json"}]
|
||||
},
|
||||
{ "id": "clicompleteclk",
|
||||
"name": "CLI complete clock",
|
||||
"shortName":"CLI cmplt clock",
|
||||
"version":"0.01",
|
||||
"description": "Command line styled clock with lots of information",
|
||||
"icon": "app.png",
|
||||
"allow_emulator": true,
|
||||
"type": "clock",
|
||||
"tags": "clock,cli,command,bash,shell,weather,hrt",
|
||||
"supports" : ["BANGLEJS", "BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"clicompleteclk.app.js","url":"app.js"},
|
||||
{"name":"clicompleteclk.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
||||
{
|
||||
"id": "rebble",
|
||||
"name": "Rebble Clock",
|
||||
"shortName": "Rebble",
|
||||
"version": "0.02",
|
||||
"description": "A Pebble style clock, with configurable background, three sidebars including steps, day, date, sunrise, sunset, long live the rebellion",
|
||||
"readme": "README.md",
|
||||
"icon": "rebble.png",
|
||||
"dependencies": {"mylocation":"app"},
|
||||
"screenshots": [{"url":"screenshot_rebble.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"rebble.app.js","url":"rebble.app.js"},
|
||||
{"name":"rebble.settings.js","url":"rebble.settings.js"},
|
||||
{"name":"rebble.img","url":"rebble.icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "snaky",
|
||||
"name": "Snaky",
|
||||
"shortName":"Snaky",
|
||||
"version":"0.01",
|
||||
"description": "The classic snake game. Eat apples and don't bite your tail. Control the snake with the touch screen.",
|
||||
"tags": "game,fun",
|
||||
"icon": "snaky.png",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"snaky.app.js","url":"snaky.js"},
|
||||
{"name":"snaky.img","url":"snaky-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "clicompleteclk",
|
||||
"name": "CLI complete clock",
|
||||
"shortName":"CLI cmplt clock",
|
||||
"version":"0.03",
|
||||
"description": "Command line styled clock with lots of information",
|
||||
"icon": "app.png",
|
||||
"allow_emulator": true,
|
||||
"type": "clock",
|
||||
"tags": "clock,cli,command,bash,shell,weather,hrt",
|
||||
"supports" : ["BANGLEJS", "BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"clicompleteclk.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"clicompleteclk.settings.js","url":"settings.js"}
|
||||
],
|
||||
"data": [{"name":"clicompleteclk.json"}]
|
||||
},
|
||||
{
|
||||
"id":"awairmonitor",
|
||||
"name":"Awair Monitor",
|
||||
"icon": "app.png",
|
||||
"allow_emulator": true,
|
||||
"version":"0.01",
|
||||
"description": "Displays the level of CO2, VOC, PM 2.5, Humidity and Temperature, from your Awair device.",
|
||||
"tags": "tool,health",
|
||||
"readme":"README.md",
|
||||
"supports":["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"awairmonitor.app.js","url":"app.js"},
|
||||
{"name":"awairmonitor.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "pooqround",
|
||||
"name": "pooq Round watch face",
|
||||
"shortName":"pooq Round",
|
||||
"version":"0.00",
|
||||
"description": "A 24 hour analogue watchface with high legibility and a novel style.",
|
||||
"icon": "app.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"allow_emulator":true,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"pooqround.app.js","url":"app.js"},
|
||||
{"name":"pooqround.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [
|
||||
{"name":"pooqround.json"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "coretemp",
|
||||
"name": "Core Temp Display",
|
||||
"version": "0.01",
|
||||
"description": "Display CoreTemp device sensor data",
|
||||
"icon": "coretemp.png",
|
||||
"type": "app",
|
||||
"tags": "health",
|
||||
"readme": "README.md",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"coretemp.boot.js","url":"boot.js"},
|
||||
{"name":"coretemp.app.js","url":"coretemp.js"},
|
||||
{"name":"coretemp.img","url":"coretemp-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
Fix music control
|
||||
0.03: Handling of message actions (ok/clear)
|
||||
0.04: Android icon now goes to settings page with 'find phone'
|
||||
0.05: Fix handling of message actions
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
// Message response
|
||||
Bangle.messageResponse = (msg,response) => {
|
||||
if (msg.id=="call") return gbSend({ t: "call", n:response?"ACCEPT":"REJECT" });
|
||||
if (isFinite(msg.id)) return gbSend({ t: "notify", n:response?"OPEN":"DISMISS" });
|
||||
if (isFinite(msg.id)) return gbSend({ t: "notify", n:response?"OPEN":"DISMISS", id: msg.id });
|
||||
// error/warn here?
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: Beta version for Bangle 2 paired with Chrome (2021/12/11)
|
|
@ -0,0 +1,22 @@
|
|||
# Awair Monitor
|
||||
|
||||
Displays the level of CO2, VOC, PM 2.5, Humidity and Temperature, from your Awair device.
|
||||
|
||||
* What you need:
|
||||
* A BangleJS 2
|
||||
* An Awair device [with local API enabled](https://support.getawair.com/hc/en-us/articles/360049221014-Awair-Local-API-Feature)
|
||||
* The web app [awair_to_bangle.html](awair_to_bangle.html) that will retrive the data from your Awair device and sent it to your BangleJS 2 through Chrome's Bluetooth LE connection
|
||||
* How to get started
|
||||
* Open awair_to_bangle.html with a text/code editor and input the IP address of your Awair on top (const awair_ip_1 = "192.168.xx.xx")
|
||||
* Launch the Awair Monitor app on your BangleJS
|
||||
* Open awair_to_bangle.html on Chrome and click "Connect BangleJS" - it connects to your watch the same way as the Bangle app store
|
||||
* Once connected to the watch with the app running, the watch app is updated once per second
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Creator
|
||||
[@alainsaas](https://github.com/alainsaas)
|
||||
|
||||
Contributions are welcome, send me your Pull Requests!
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwgP/AD38g4FD8EAAoeAgE/AoUD/EfAgP+AYMPDgQPBw4FB/F///DAoPwAQPjAQPBAQPxDgJVCAoP4gYaCCwIcBAoM/8P8h0HjEP8f4h0Gp0H4/44lj5+H4/54lzj/jx/5/lyDgIFDh/xAoQRBAoXsuY8Bx4jCAoeEkYFB447CAoRxBOAPxM4RmC8IFD4ZZD/8H/DHDh/+AoaSBUAIABCoYATVwS2Ct4FE84REXQQLCk4RJAo0XGxY="))
|
|
@ -0,0 +1,98 @@
|
|||
Graphics.prototype.setFontMichroma36 = function() {
|
||||
g.setFontCustom(atob("AAAAAAAAAAAAAAAAeAAAAAeAAAAAeAAAAAeAAAAAAAAAAAAAAAAAAAAAAAGAAAAA+AAAAD+AAAAP+AAAA/8AAAD/wAAAf/AAAB/4AAAH/gAAAf+AAAB/4AAAH/gAAAf+AAAAfwAAAAfAAAAAcAAAAAAAAAAAAAAAAAAAAAAAA///AAD///wAH///4AP///8APwAD+APAAAeAeAAAeAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAeAPAAAeAPwAD+AP///8AH///4AD///wAA///AAAAAAAAAAAAAAAAAAAAAEAAAAAOAAAAAfAAAAA+AAAAB8AAAAD8AAAAH4AAAAPwAAAAPgAAAAfAAAAAf///+Af///+Af///+Af///+AAAAAAAAAAAAAAAAAAAAAAAAAA/Af+AD/A/+AH/B/+AP/D/+APwD4eAPADweAfADweAeADweAeADweAeADweAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAPgeAeAPAeAeAPAeAeAPAeAeAPAeAfAPAeAPw/AeAP/+AeAH/+AeAD/8AeAB/wAOAAAAAAAAAAAAAAAAAAAAAAAAAB8APgAD8AP4AH8AP8AP8AP8APgAB+AfAAAeAeAAAeAeAAAPAeAAAPAeAAAPAeAAAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAeAfAeAeAPx/h+AP///+AH///8AD///4AB/h/gAAAAAAAAAAAAAAAAAAAAAAeAAAAA/AAAAA/AAAAB/AAAAD/AAAAH/AAAAPvAAAAPPAAAAfPAAAA+PAAAB8PAAAD4PAAADwPAAAHwPAAAPgPAAAfAPAAA+APAAA8APAAB8APAAD4APAAHwAPAAPgAPAAPAAPAAfAAPAAf///+Af///+Af///+Af///+AAAAPAAAAAPAAAAAPAAAAAPAAAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAf/8PgAf/8P4Af/8P8Af/8P8AeB4A+AeB4AeAeDwAeAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAfAeDwAeAeD4A+AeD+D+AeB//8AeB//4AeA//4AAAP/AAAAAAAAAAAAAAAAAAAAAAAAAAA///AAD///wAH///4AH///8AP4fB+APAeAeAfA8AeAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAfA8APAPA+AeAPgeAeAP8fh+AH8f/8AD8P/8AA8H/4AAAB/gAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAeAAAAAeAAAAAeAAAAAeAAAAAeAAACAeAAAGAeAAAOAeAAAeAeAAA+AeAAD+AeAAH8AeAAP4AeAAfwAeAA/gAeAB/AAeAD+AAeAP4AAeAfwAAeA/gAAeB/AAAeD+AAAeH8AAAefwAAAe/gAAAf/AAAAf+AAAAf8AAAAf4AAAAfgAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAMAAB+B/wAD/j/4AH/3/8AP///+AP//A+AfB+AeAeA+AeAeA+APAeA+APAeA+APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA+APAeA+APAeA+APAeA+AOAeA+AeAPh/A+AP///+AP/3/8AH/3/8AB/D/wAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAD/4HAAH/8HwAP/+H4AP5/H8AfAfA8AeAPAeAeAPAeAeAPAeAeAHgfAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHAPAeAPAOAeAPAeAPAPAeAPwfB+AP///8AH///4AD///wAA///AAAAAAAAAAAAAAAAAAAAAAAAAAAB8DwAAB8HwAAB8HwAAB8DwAAAAAAAAAAAAA"), 46, atob("CBIkESMjJCMjIyMjCA=="), 36+(1<<8)+(1<<16));
|
||||
};
|
||||
|
||||
var drawTimeout;
|
||||
|
||||
function queueNextDraw() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, 1000 - (Date.now() % 1000));
|
||||
}
|
||||
|
||||
var locale = require("locale");
|
||||
|
||||
var bt_current_co2 = 0;
|
||||
var bt_current_voc = 0;
|
||||
var bt_current_pm25 = 0;
|
||||
var bt_current_humi = 0;
|
||||
var bt_current_temp = 0;
|
||||
var bt_last_update = 0;
|
||||
|
||||
var last_update = 0;
|
||||
var bt_co2_history = new Array(10).fill(0);
|
||||
var bt_voc_history = new Array(10).fill(0);
|
||||
var bt_pm25_history = new Array(10).fill(0);
|
||||
var bt_humi_history = new Array(10).fill(0);
|
||||
var bt_temp_history = new Array(10).fill(0);
|
||||
|
||||
var internal_last_update = -1;
|
||||
|
||||
function draw() {
|
||||
g.reset().clearRect(0,24,g.getWidth(),g.getHeight());
|
||||
|
||||
var date = new Date();
|
||||
g.setFontAlign(0,0);
|
||||
g.setFont("Michroma36").drawString(locale.time(date,1), g.getWidth()/2, 56);
|
||||
|
||||
g.setFont("6x8");
|
||||
g.drawString(locale.date(new Date(),1), g.getWidth()/2, 80);
|
||||
|
||||
g.setFont("6x8");
|
||||
g.drawString("CO2", 20, 100);
|
||||
g.drawString("VOC", 55, 100);
|
||||
g.drawString("PM25", 90, 100);
|
||||
g.drawString("Humi", 125, 100);
|
||||
g.drawString("Temp", 160, 100);
|
||||
|
||||
g.setFont("HaxorNarrow7x17");
|
||||
g.drawString(""+bt_current_co2, 18, 110);
|
||||
g.drawString(""+bt_current_voc, 53, 110);
|
||||
g.drawString(""+bt_current_pm25, 88, 110);
|
||||
g.drawString(""+bt_current_humi, 123, 110);
|
||||
g.drawString(""+bt_current_temp, 158, 110);
|
||||
|
||||
if (last_update != bt_last_update) {
|
||||
last_update = bt_last_update;
|
||||
internal_last_update = last_update;
|
||||
if (last_update % 10 == 0) {
|
||||
bt_co2_history.shift(); bt_co2_history.push(bt_current_co2);
|
||||
bt_voc_history.shift(); bt_voc_history.push(bt_current_voc);
|
||||
bt_pm25_history.shift(); bt_pm25_history.push(bt_current_pm25);
|
||||
bt_humi_history.shift(); bt_humi_history.push(bt_current_humi);
|
||||
bt_temp_history.shift(); bt_temp_history.push(bt_current_temp);
|
||||
}
|
||||
}
|
||||
|
||||
if (internal_last_update == -1) {
|
||||
g.drawString("Waiting for connection", 88, 164);
|
||||
} else if (internal_last_update > last_update + 5) {
|
||||
g.drawString("Trying to reconnect since " + (internal_last_update - last_update), 88, 164);
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
// max height = 32
|
||||
g.drawLine(10+i*2, 150-(Math.min(Math.max(bt_co2_history[i],400), 1200)-400)/25, 10+i*2, 150);
|
||||
g.drawLine(45+i*2, 150-(Math.min(Math.max(bt_voc_history[i],0), 1440)-0)/45, 45+i*2, 150);
|
||||
g.drawLine(80+i*2, 150-(Math.min(Math.max(bt_pm25_history[i],0), 32)-0)/1, 80+i*2, 150);
|
||||
g.drawLine(115+i*2, 150-(Math.min(Math.max(bt_humi_history[i],20), 60)-20)/1.25, 115+i*2, 150);
|
||||
g.drawLine(150+i*2, 150-(Math.min(Math.max(bt_temp_history[i],19), 27)-19)*4, 150+i*2, 150);
|
||||
|
||||
// target humidity level
|
||||
g.setColor("#00F").drawLine(115, 150-(40-20)/1.25, 115+18, 150-(40-20)/1.25);
|
||||
g.reset();
|
||||
}
|
||||
|
||||
if (internal_last_update != -1) { internal_last_update++; }
|
||||
queueNextDraw();
|
||||
}
|
||||
|
||||
// init
|
||||
require("FontHaxorNarrow7x17").add(Graphics);
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
draw();
|
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 146 KiB |
|
@ -0,0 +1,195 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script src="https://puck-js.com/puck.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
// Don't forget to enable the Local API on your Awair before using this
|
||||
// https://support.getawair.com/hc/en-us/articles/360049221014-Awair-Local-API-Feature
|
||||
|
||||
const awair_ip_1 = "192.168.2.2"; // <- INPUT YOUR AWAIR IP ADDRESS HERE
|
||||
const awair_name_1 = "Awair";
|
||||
|
||||
var bt_connection;
|
||||
var is_connected = false;
|
||||
var reconnect_counter = 5;
|
||||
var reconnect_attempt_counter = 1;
|
||||
|
||||
window.onload = function() {
|
||||
var chart_co2;
|
||||
var chart_voc;
|
||||
var chart_pm;
|
||||
var chart_temperature;
|
||||
var chart_humidity;
|
||||
var dataPoints_1 = [];
|
||||
var posx = 0;
|
||||
|
||||
$.getJSON("http://"+awair_ip_1+"/air-data/latest", function(data) {
|
||||
$.each(data, function(key, value){
|
||||
if (dataPoints_1[key] === undefined) { dataPoints_1[key] = []; }
|
||||
if (key === "temp" || key === "humid") { dataPoints_1[key].push({x: posx, y: parseFloat(value)}); }
|
||||
else { dataPoints_1[key].push({x: posx, y: parseInt(value)}); }
|
||||
});
|
||||
|
||||
posx++;
|
||||
|
||||
chart_co2 = new CanvasJS.Chart("chartContainer_co2",{
|
||||
title:{ text:"CO2", fontFamily: "helvetica", fontColor: "#F7FAFC", fontSize: 16, horizontalAlign: "left", padding: { left: 30 } },
|
||||
axisX:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
|
||||
axisY:{ minimum: 0, labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
|
||||
legend: { fontColor: "#F7FAFC", horizontalAlign: "center", verticalAlign: "bottom" },
|
||||
data: [ { type: "line", lineColor: "#6648FF", showInLegend: true, legendText: awair_name_1, dataPoints : dataPoints_1.co2 }]
|
||||
});
|
||||
chart_voc = new CanvasJS.Chart("chartContainer_voc",{
|
||||
title:{ text:"VOC", fontFamily: "helvetica", fontColor: "#F7FAFC", fontSize: 16, horizontalAlign: "left", padding: { left: 30 } },
|
||||
axisX:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
|
||||
axisY:{ minimum: 0, labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
|
||||
legend: { fontColor: "#F7FAFC", horizontalAlign: "center", verticalAlign: "bottom" },
|
||||
data: [ { type: "line", lineColor: "#6648FF", showInLegend: true, legendText: awair_name_1, dataPoints : dataPoints_1.voc }]
|
||||
});
|
||||
chart_pm = new CanvasJS.Chart("chartContainer_pm",{
|
||||
title:{ text:"PM", fontFamily: "helvetica", fontColor: "#F7FAFC", fontSize: 16, horizontalAlign: "left", padding: { left: 30 } },
|
||||
axisX:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
|
||||
axisY:{ minimum: 0, labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
|
||||
legend: { fontColor: "#F7FAFC", horizontalAlign: "center", verticalAlign: "bottom" },
|
||||
data: [ { type: "line", lineColor: "#6648FF", showInLegend: true, legendText: awair_name_1, dataPoints : dataPoints_1.pm25 }]
|
||||
});
|
||||
chart_humidity = new CanvasJS.Chart("chartContainer_humidity",{
|
||||
title:{ text:"Humidity", fontFamily: "helvetica", fontColor: "#F7FAFC", fontSize: 16, horizontalAlign: "left", padding: { left: 30 } },
|
||||
axisX:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
|
||||
axisY:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
|
||||
legend: { fontColor: "#F7FAFC", horizontalAlign: "center", verticalAlign: "bottom" },
|
||||
data: [ { type: "line", lineColor: "#6648FF", showInLegend: true, legendText: awair_name_1, dataPoints : dataPoints_1.humid }]
|
||||
});
|
||||
chart_temperature = new CanvasJS.Chart("chartContainer_temperature",{
|
||||
title:{ text:"Temperature", fontFamily: "helvetica", fontColor: "#F7FAFC", fontSize: 16, horizontalAlign: "left", padding: { left: 30 } },
|
||||
axisX:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
|
||||
axisY:{ labelFontColor: "#F7FAFC", gridColor: "#2D3748", lineColor: "#2D3748", tickColor: "#2D3748" },
|
||||
legend: { fontColor: "#F7FAFC", horizontalAlign: "center", verticalAlign: "bottom" },
|
||||
data: [ { type: "line", lineColor: "#6648FF", showInLegend: true, legendText: awair_name_1, dataPoints : dataPoints_1.temp }]
|
||||
});
|
||||
|
||||
chart_co2.set("backgroundColor", "#1A202C");
|
||||
chart_voc.set("backgroundColor", "#1A202C");
|
||||
chart_pm.set("backgroundColor", "#1A202C");
|
||||
chart_humidity.set("backgroundColor", "#1A202C");
|
||||
chart_temperature.set("backgroundColor", "#1A202C");
|
||||
|
||||
updateChart();
|
||||
});
|
||||
|
||||
function updateChart() {
|
||||
$.getJSON("http://"+awair_ip_1+"/air-data/latest", function(data) {
|
||||
$.each(data, function(key, value){
|
||||
if (dataPoints_1[key] === undefined) { dataPoints_1[key] = []; }
|
||||
if (key === "temp" || key === "humid") { dataPoints_1[key].push({x: posx, y: parseFloat(value)}); }
|
||||
else { dataPoints_1[key].push({x: posx, y: parseInt(value)}); }
|
||||
});
|
||||
|
||||
posx++;
|
||||
chart_co2.render();
|
||||
chart_voc.render();
|
||||
chart_pm.render();
|
||||
chart_temperature.render();
|
||||
chart_humidity.render();
|
||||
|
||||
chart_co2.title.set("text", "CO2 level (ppm)");
|
||||
chart_voc.title.set("text", "VOC level (ppb)");
|
||||
chart_pm.title.set("text", "PM2.5 level (ug/m³)");
|
||||
chart_humidity.title.set("text", "Humidity level (%)");
|
||||
chart_temperature.title.set("text", "Temperature level (°C)");
|
||||
|
||||
let current_co2 = dataPoints_1['co2'][dataPoints_1['co2'].length-1].y;
|
||||
let current_voc = dataPoints_1['voc'][dataPoints_1['voc'].length-1].y;
|
||||
let current_pm25 = dataPoints_1['pm25'][dataPoints_1['pm25'].length-1].y;
|
||||
let current_humi = dataPoints_1['humid'][dataPoints_1['humid'].length-1].y;
|
||||
let current_temp = dataPoints_1['temp'][dataPoints_1['temp'].length-1].y;
|
||||
let last_update = dataPoints_1['temp'].length-1;
|
||||
if (is_connected && bt_connection.isOpen) {
|
||||
bt_connection.write('\x10bt_current_co2='+current_co2+';bt_current_voc='+current_voc+';bt_current_pm25='+current_pm25+';bt_current_humi='+current_humi+';bt_current_temp='+current_temp+';bt_last_update='+last_update+';\n');
|
||||
|
||||
console.log("Sent data through Bluetooth");
|
||||
} else if (is_connected && !bt_connection.isOpen) {
|
||||
console.log("Disconnected - Next attempt to reconnect in " + reconnect_counter);
|
||||
reconnect_counter--;
|
||||
|
||||
if (reconnect_counter <= 0) {
|
||||
reconnect_counter = 10 * reconnect_attempt_counter;
|
||||
reconnect_attempt_counter++;
|
||||
|
||||
console.log("Trying to reconnect");
|
||||
bt_connection.reconnect(function(c) {
|
||||
console.log("Reconnect callback");
|
||||
if (!c) {
|
||||
console.log("Couldn't reconnect");
|
||||
return;
|
||||
}
|
||||
bt_connection = c;
|
||||
is_connected = true;
|
||||
reconnect_attempt_counter = 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
setTimeout(function(){updateChart()}, 1000);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function connectBT() {
|
||||
console.log("Connect BT");
|
||||
Puck.connect(function(c) {
|
||||
console.log("Connect callback");
|
||||
if (!c) {
|
||||
console.log("Couldn't connect");
|
||||
return;
|
||||
}
|
||||
bt_connection = c;
|
||||
is_connected = true;
|
||||
reconnect_attempt_counter = 1;
|
||||
});
|
||||
}
|
||||
|
||||
function disconnectBT() {
|
||||
if (is_connected && bt_connection) {
|
||||
bt_connection.close();
|
||||
is_connected = false;
|
||||
console.log("Closed Bluetooth connection");
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<script type="text/javascript" src="https://canvasjs.com/assets/script/jquery-1.11.1.min.js"></script>
|
||||
<script type="text/javascript" src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
|
||||
</head>
|
||||
<body style="background-color:#1A202C;">
|
||||
|
||||
<p style="color: #F7FAFC">
|
||||
<b>How to use</b>
|
||||
<br/><br/>
|
||||
Step 1: Enable the Local API on your Awair: https://support.getawair.com/hc/en-us/articles/360049221014-Awair-Local-API-Feature
|
||||
<br/><br/>
|
||||
Step 2: Modify this HTML file to input the IP address of your Awair on top (const awair_ip_1 = "192.168.xx.xx")
|
||||
<br/><br/>
|
||||
Step 3: Launch the Awair Monitor app on your BangleJS
|
||||
<br/><br/>
|
||||
Step 4: Click "Connect BangleJS"
|
||||
<br/><br/>
|
||||
Step 5: Optionally, open the web inspector's console (Right click > Inspector > Console) to read the bluetooth logs
|
||||
</p>
|
||||
|
||||
<center>
|
||||
<button onclick="connectBT();">Connect BangleJS</button>
|
||||
<button onclick="disconnectBT();">Disconnect BangleJS</button>
|
||||
</center>
|
||||
|
||||
<br/><br/>
|
||||
|
||||
<div id="chartContainer_co2" style="height: 300px; max-width: 920px; margin: 0px auto; margin-bottom: 64px;"></div>
|
||||
<div id="chartContainer_voc" style="height: 300px; max-width: 920px; margin: 0px auto; margin-bottom: 64px;"></div>
|
||||
<div id="chartContainer_pm" style="height: 300px; max-width: 920px; margin: 0px auto; margin-bottom: 64px;"></div>
|
||||
<div id="chartContainer_humidity" style="height: 300px; max-width: 920px; margin: 0px auto; margin-bottom: 64px;"></div>
|
||||
<div id="chartContainer_temperature" style="height: 300px; max-width: 920px; margin: 0px auto; margin-bottom: 64px;"></div>
|
||||
</body>
|
||||
</html>
|
After Width: | Height: | Size: 3.9 KiB |
|
@ -1 +1,2 @@
|
|||
0.01: New clock!
|
||||
0.02: Load steps from Health Tracking app (if installed)
|
||||
|
|
|
@ -3,13 +3,14 @@
|
|||
Command line styled clock with lots of information:
|
||||
|
||||
It can show the following (depending on availability) information:
|
||||
* Time
|
||||
* Day of week
|
||||
* Date
|
||||
* Weather condition (requires weather app)
|
||||
* Temperature (requires weather app)
|
||||
* Steps (requires a step widget)
|
||||
* Heart rate (when screen is on and unlocked)
|
||||
* Time data:
|
||||
* Time
|
||||
* Day of week
|
||||
* Date
|
||||
* Additional information (can be toggled via settings):
|
||||
* Weather conditions and temperature (requires app [Weather](https://banglejs.com/apps/#weather))
|
||||
* Steps (requires app [Health Tracking](https://banglejs.com/apps/#health%20tracking) or a step widget)
|
||||
* Heart rate (when screen is on and unlocked)
|
||||
|
||||
## TODO
|
||||
* Make time font bigger
|
||||
|
|
|
@ -1,31 +1,60 @@
|
|||
const storage = require('Storage');
|
||||
const locale = require("locale");
|
||||
|
||||
const font = "12x20";
|
||||
const fontsize = 1;
|
||||
const font12 = g.getFonts().includes("12x20");
|
||||
const font = font12 ? "12x20" : "6x8";
|
||||
const fontsize = font12 ? 1: 2;
|
||||
const fontheight = 19;
|
||||
|
||||
const marginTop = 10;
|
||||
const marginTop = 5;
|
||||
const marginLeftTopic = 3; // margin of topics
|
||||
const marginLeftData = 68; // margin of data values
|
||||
const marginLeftData = font12 ? 64 : 75; // margin of data values
|
||||
|
||||
const topicColor = g.theme.dark ? "#fff" : "#000";
|
||||
const textColor = g.theme.dark ? "#0f0" : "#080";
|
||||
const textColorRed = g.theme.dark ? "#FF0000" : "#FF0000";
|
||||
|
||||
let hrtValue;
|
||||
let hrtValueIsOld = false;
|
||||
|
||||
let localTempValue;
|
||||
let weatherTempString;
|
||||
let lastHeartRateRowIndex;
|
||||
let lastStepsRowIndex;
|
||||
let i = 2;
|
||||
|
||||
let settings;
|
||||
|
||||
function loadSettings() {
|
||||
settings = storage.readJSON('clicompleteclk.json', 1) || {};
|
||||
}
|
||||
|
||||
function setting(key) {
|
||||
if (!settings) { loadSettings(); }
|
||||
const DEFAULTS = {
|
||||
'battery': true,
|
||||
'batteryLvl': 30,
|
||||
'weather': true,
|
||||
'steps': true,
|
||||
'heartrate': true
|
||||
};
|
||||
return (key in settings) ? settings[key] : DEFAULTS[key];
|
||||
}
|
||||
|
||||
|
||||
let showBattery = setting('battery');
|
||||
let batteryWarnLevel = setting('batteryLvl');
|
||||
let showWeather = setting('weather');
|
||||
let showSteps = setting('steps');
|
||||
let showHeartRate = setting('heartrate');
|
||||
|
||||
|
||||
// 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;
|
||||
drawAll(false);
|
||||
drawAll(true);
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
}
|
||||
|
||||
|
@ -42,15 +71,13 @@ function updateTime(now){
|
|||
if (!Bangle.isLCDOn()) return;
|
||||
writeLineTopic("TIME", 1);
|
||||
writeLine(locale.time(now,1),1);
|
||||
if(now.getMinutes() == 0)
|
||||
drawInfo(now);
|
||||
}
|
||||
|
||||
function drawInfo(now) {
|
||||
if (now == undefined)
|
||||
if (now == undefined)
|
||||
now = new Date();
|
||||
|
||||
let i = 2;
|
||||
i = 2;
|
||||
|
||||
writeLineTopic("DOWK", i);
|
||||
writeLine(locale.dow(now),i);
|
||||
|
@ -60,15 +87,29 @@ function drawInfo(now) {
|
|||
writeLine(locale.date(now,1),i);
|
||||
i++;
|
||||
|
||||
/*
|
||||
writeLineTopic("BAT", i);
|
||||
const b = E.getBattery();
|
||||
writeLine(b + "%", i); // TODO make bars
|
||||
i++;
|
||||
*/
|
||||
if (showBattery) {
|
||||
writeLineTopic("BATT", i);
|
||||
const b = E.getBattery();
|
||||
writeLine(b + "%", i, b < batteryWarnLevel ? textColorRed : textColor);
|
||||
i++;
|
||||
}
|
||||
|
||||
// weather
|
||||
var weatherJson = getWeather();
|
||||
if (showWeather) {
|
||||
drawWeather();
|
||||
}
|
||||
|
||||
if (showSteps) {
|
||||
drawSteps(i);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (showHeartRate) {
|
||||
drawHeartRate(i);
|
||||
}
|
||||
}
|
||||
|
||||
function drawWeather() {
|
||||
const weatherJson = getWeather();
|
||||
if(weatherJson && weatherJson.weather){
|
||||
const currentWeather = weatherJson.weather;
|
||||
|
||||
|
@ -82,19 +123,22 @@ function drawInfo(now) {
|
|||
writeLine(weatherTempValue,i);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// steps
|
||||
if (stepsWidget() != undefined) {
|
||||
function drawSteps(i) {
|
||||
if (!showSteps) return;
|
||||
if (i == undefined)
|
||||
i = lastStepsRowIndex;
|
||||
const steps = getSteps();
|
||||
if (steps != undefined) {
|
||||
writeLineTopic("STEP", i);
|
||||
const steps = stepsWidget().getSteps();
|
||||
writeLine(steps, i);
|
||||
i++;
|
||||
}
|
||||
|
||||
drawHeartRate(i);
|
||||
lastStepsRowIndex = i;
|
||||
}
|
||||
|
||||
function drawHeartRate(i) {
|
||||
if (!showHeartRate) return;
|
||||
if (i == undefined)
|
||||
i = lastHeartRateRowIndex;
|
||||
writeLineTopic("HRTM", i);
|
||||
|
@ -103,8 +147,6 @@ function drawHeartRate(i) {
|
|||
writeLine(hrtValue,i);
|
||||
else
|
||||
writeLine(hrtValue,i, topicColor);
|
||||
} else {
|
||||
writeLine("...",i);
|
||||
}
|
||||
lastHeartRateRowIndex = i;
|
||||
}
|
||||
|
@ -128,52 +170,23 @@ function writeLine(str,line,pColor){
|
|||
g.drawString(str,marginLeftData,y);
|
||||
}
|
||||
|
||||
// EVENTS:
|
||||
|
||||
// turn on HRM when the LCD is unlocked
|
||||
Bangle.on('lock', function(isLocked) {
|
||||
if (!isLocked) {
|
||||
Bangle.setHRMPower(1,"clicompleteclk");
|
||||
if (hrtValue == undefined)
|
||||
hrtValue = "...";
|
||||
else
|
||||
hrtValueIsOld = true;
|
||||
drawHeartRate();
|
||||
} else {
|
||||
hrtValueIsOld = true;
|
||||
Bangle.setHRMPower(0,"clicompleteclk");
|
||||
function getSteps() {
|
||||
var steps = 0;
|
||||
let health;
|
||||
try {
|
||||
health = require("health");
|
||||
} catch (e) {
|
||||
// Module health not found
|
||||
}
|
||||
});
|
||||
|
||||
Bangle.on('lcdPower',function(on) {
|
||||
if (on) {
|
||||
drawAll(true);
|
||||
} else {
|
||||
hrtValueIsOld = true;
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
Bangle.on('HRM', function(hrm) {
|
||||
//if(hrm.confidence > 90){
|
||||
hrtValueIsOld = false;
|
||||
hrtValue = hrm.bpm;
|
||||
if (Bangle.isLCDOn())
|
||||
drawHeartRate();
|
||||
//} else {
|
||||
// hrtValue = undefined;
|
||||
//}
|
||||
});
|
||||
|
||||
|
||||
function stepsWidget() {
|
||||
if (WIDGETS.activepedom !== undefined) {
|
||||
return WIDGETS.activepedom;
|
||||
if (health != undefined) {
|
||||
health.readDay(new Date(), h=>steps+=h.steps);
|
||||
} else if (WIDGETS.wpedom !== undefined) {
|
||||
return WIDGETS.wpedom;
|
||||
return WIDGETS.wpedom.getSteps();
|
||||
} else if (WIDGETS.activepedom !== undefined) {
|
||||
return WIDGETS.activepedom.getSteps();
|
||||
}
|
||||
return undefined;
|
||||
return steps;
|
||||
}
|
||||
|
||||
function getWeather() {
|
||||
|
@ -181,8 +194,57 @@ function getWeather() {
|
|||
return jsonWeather;
|
||||
}
|
||||
|
||||
// EVENTS:
|
||||
|
||||
// turn on HRM when the LCD is unlocked
|
||||
Bangle.on('lock', function(isLocked) {
|
||||
if (!isLocked) {
|
||||
if (showHeartRate) {
|
||||
Bangle.setHRMPower(1,"clicompleteclk");
|
||||
if (hrtValue == undefined)
|
||||
hrtValue = "...";
|
||||
else
|
||||
hrtValueIsOld = true;
|
||||
}
|
||||
} else {
|
||||
if (showHeartRate) {
|
||||
hrtValueIsOld = true;
|
||||
Bangle.setHRMPower(0,"clicompleteclk");
|
||||
}
|
||||
}
|
||||
// Update steps and heart rate
|
||||
drawSteps();
|
||||
drawHeartRate();
|
||||
});
|
||||
|
||||
Bangle.on('lcdPower',function(on) {
|
||||
if (on) {
|
||||
drawAll(true);
|
||||
} else {
|
||||
if (showHeartRate) {
|
||||
hrtValueIsOld = true;
|
||||
}
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
if (showHeartRate) {
|
||||
Bangle.on('HRM', function(hrm) {
|
||||
//if(hrm.confidence > 90){
|
||||
hrtValueIsOld = false;
|
||||
hrtValue = hrm.bpm;
|
||||
if (Bangle.isLCDOn())
|
||||
drawHeartRate();
|
||||
//} else {
|
||||
// hrtValue = undefined;
|
||||
//}
|
||||
});
|
||||
}
|
||||
|
||||
g.clear();
|
||||
Bangle.setUI("clock");
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
loadSettings();
|
||||
drawAll(true);
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
(function(back) {
|
||||
const storage = require('Storage');
|
||||
let settings = storage.readJSON('clicompleteclk.json', 1) || {};
|
||||
function save(key, value) {
|
||||
settings[key] = value;
|
||||
storage.write('clicompleteclk.json', settings);
|
||||
}
|
||||
E.showMenu({
|
||||
'': { 'title': 'CLI complete clk' },
|
||||
'Show battery': {
|
||||
value: "battery" in settings ? settings.battery : false,
|
||||
format: () => (settings.battery ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.battery = !settings.battery;
|
||||
save('battery', settings.battery);
|
||||
},
|
||||
},
|
||||
'Battery warn': {
|
||||
value: "batteryLvl" in settings ? settings.batteryLvl : 30,
|
||||
min: 0,
|
||||
max : 100,
|
||||
step: 10,
|
||||
format: x => {
|
||||
return x + "%";
|
||||
},
|
||||
onchange: x => save('batteryLvl', x),
|
||||
},
|
||||
'Show weather': {
|
||||
value: "weather" in settings ? settings.weather : false,
|
||||
format: () => (settings.weather ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.weather = !settings.weather;
|
||||
save('weather', settings.weather);
|
||||
},
|
||||
},
|
||||
'Show steps': {
|
||||
value: "steps" in settings ? settings.steps : false,
|
||||
format: () => (settings.steps ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.steps = !settings.steps;
|
||||
save('steps', settings.steps);
|
||||
},
|
||||
},
|
||||
'Show heartrate': {
|
||||
value: "heartrate" in settings ? settings.heartrate : false,
|
||||
format: () => (settings.heartrate ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
settings.heartrate = !settings.heartrate;
|
||||
save('heartrate', settings.heartrate);
|
||||
},
|
||||
},
|
||||
'< Back': back,
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
0.1: New app
|
|
@ -0,0 +1,20 @@
|
|||
# CoreTemp display
|
||||
|
||||
Basic bare-bones example of connecting to a bluetooth [CoreTemp](https://corebodytemp.com/) device and displaying the current body core temperature readings.
|
||||
|
||||
## Usage
|
||||
|
||||
On startup connects to a CoreTemp device (1809/2A1C) and emits a "Core, temp" value for each reading.
|
||||
The app simply displays these readings on screen.
|
||||
|
||||
## TODO
|
||||
|
||||
* Integrate with other tracking/sports apps to log data.
|
||||
* Add device selection
|
||||
* Provide enable/disable option
|
||||
* Check status, add Retry/reconnect
|
||||
* Also provide skin temp reading
|
||||
|
||||
## Creator
|
||||
|
||||
Ivor Hewitt
|
|
@ -0,0 +1,23 @@
|
|||
(function() {
|
||||
var gatt;
|
||||
|
||||
//Would it be better to scan by uuid rather than name?
|
||||
NRF.requestDevice({ timeout: 20000, filters: [{ name: 'CORE [a]' }] }).then(function(device) {
|
||||
return device.gatt.connect();
|
||||
}).then(function(g) {
|
||||
gatt = g;
|
||||
return gatt.getPrimaryService("1809");
|
||||
}).then(function(service) {
|
||||
return service.getCharacteristic("2A1C");
|
||||
}).then(function(characteristic) {
|
||||
characteristic.on('characteristicvaluechanged', function(event) {
|
||||
var dv = event.target.value;
|
||||
var core = (dv.buffer[2]*256+dv.buffer[1])/100;
|
||||
Bangle.emit('Core',{
|
||||
temp:core
|
||||
});
|
||||
});
|
||||
return characteristic.startNotifications();
|
||||
}).then(function() {
|
||||
});
|
||||
})();
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEw4UA///k0DxUFgsDCY8KwAfJlQLHhWglWq1WgBIcCA4QCB1WoComq0+iBYWqCwl//4OBAAQxChWlv/2BYIlCBYUqv9VvQLBwA9BBYWlqtV/QLBGoRIBgQLBr9aBYQ2BBYMKroLBtQLCgALClIKC1AXG1NVuoFBF4sC09V+woCBAJHCgWXq9oPQZrDgWdq9gBZG9rqgCTwSbCgVVqysDBYkK6tWYoa/DkEJ6vaaIgWBaAILCbQhUCBYXoc4wNBBZWqBfBtB1ALKKZILCR4J3FToQLBU4KPEWoQLNZYILIa4NVcYReEcYOnqtaDAbvDgALBcg4EBlNVqtqDoOgd4YoBBYNWytWCwQdCgQLBAAVaBYkA0oLDuwLFkv1BgZGDAAMJuoKCroWEGAOnDAVftShGr////1tDdG14LB+wiEAAdqHAjTHBYgA=="))
|
|
@ -0,0 +1,19 @@
|
|||
Bangle.setLCDPower(1);
|
||||
Bangle.setLCDTimeout(0);
|
||||
var btm = g.getHeight()-1;
|
||||
|
||||
function onCore(c) {
|
||||
var px = g.getWidth()/2;
|
||||
g.setFontAlign(0,0);
|
||||
g.clearRect(0,24,g.getWidth(),80);
|
||||
var str = c.temp + "C";
|
||||
g.setFontVector(40).drawString(str,px,45);
|
||||
}
|
||||
Bangle.on('Core', onCore);
|
||||
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
g.reset().setFont("6x8",2).setFontAlign(0,0);
|
||||
g.drawString("Please wait...",g.getWidth()/2,g.getHeight()/2 - 16);
|
After Width: | Height: | Size: 4.8 KiB |
|
@ -3,3 +3,5 @@
|
|||
0.03: cycle thru pages
|
||||
0.04: reset to clock after 2 mins of inactivity
|
||||
0.05: add Bangle 2 version
|
||||
0.06: Adds settings page (hide clocks or launchers)
|
||||
0.06: Adds setting for directly launching app on touch for Bangle 2
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
*
|
||||
*/
|
||||
|
||||
var settings = Object.assign({
|
||||
showClocks: true,
|
||||
showLaunchers: true,
|
||||
}, require('Storage').readJSON("dtlaunch.json", true) || {});
|
||||
|
||||
function wdog(handle,timeout){
|
||||
if(handle !== undefined){
|
||||
wdog.handle = handle;
|
||||
|
@ -17,7 +22,13 @@ function wdog(handle,timeout){
|
|||
wdog(load,120000)
|
||||
|
||||
var s = require("Storage");
|
||||
var apps = s.list(/\.info$/).map(app=>{var a=s.readJSON(app,1);return a&&{name:a.name,type:a.type,icon:a.icon,sortorder:a.sortorder,src:a.src};}).filter(app=>app && (app.type=="app" || app.type=="clock" || !app.type));
|
||||
var apps = s.list(/\.info$/).map(app=>{
|
||||
var a=s.readJSON(app,1);
|
||||
return a && {
|
||||
name:a.name, type:a.type, icon:a.icon, sortorder:a.sortorder, src:a.src
|
||||
};}).filter(
|
||||
app=>app && (app.type=="app" || (app.type=="clock" && settings.showClocks) || (app.type=="launch" && settings.showLaunchers) || !app.type));
|
||||
|
||||
apps.sort((a,b)=>{
|
||||
var n=(0|a.sortorder)-(0|b.sortorder);
|
||||
if (n) return n; // do sortorder first
|
||||
|
|
|
@ -2,8 +2,20 @@
|
|||
*
|
||||
*/
|
||||
|
||||
var settings = Object.assign({
|
||||
showClocks: true,
|
||||
showLaunchers: true,
|
||||
direct: false,
|
||||
}, require('Storage').readJSON("dtlaunch.json", true) || {});
|
||||
|
||||
var s = require("Storage");
|
||||
var apps = s.list(/\.info$/).map(app=>{var a=s.readJSON(app,1);return a&&{name:a.name,type:a.type,icon:a.icon,sortorder:a.sortorder,src:a.src};}).filter(app=>app && (app.type=="app" || app.type=="clock" || !app.type));
|
||||
var apps = s.list(/\.info$/).map(app=>{
|
||||
var a=s.readJSON(app,1);
|
||||
return a && {
|
||||
name:a.name, type:a.type, icon:a.icon, sortorder:a.sortorder, src:a.src
|
||||
};}).filter(
|
||||
app=>app && (app.type=="app" || (app.type=="clock" && settings.showClocks) || (app.type=="launch" && settings.showLaunchers) || !app.type));
|
||||
|
||||
apps.sort((a,b)=>{
|
||||
var n=(0|a.sortorder)-(0|b.sortorder);
|
||||
if (n) return n; // do sortorder first
|
||||
|
@ -28,7 +40,7 @@ const YOFF = 30;
|
|||
function draw_icon(p,n,selected) {
|
||||
var x = (n%2)*72+XOFF;
|
||||
var y = n>1?72+YOFF:YOFF;
|
||||
(selected?g.setColor(g.theme.fgH):g.setColor(g.theme.bg)).fillRect(x+10,y+2,x+60,y+52);
|
||||
(selected?g.setColor(g.theme.fgH):g.setColor(g.theme.bg)).fillRect(x+11,y+3,x+60,y+52);
|
||||
g.clearRect(x+12,y+4,x+59,y+51);
|
||||
g.setColor(g.theme.fg);
|
||||
try{g.drawImage(apps[p*4+n].icon,x+12,y+4);} catch(e){}
|
||||
|
@ -52,7 +64,7 @@ function drawPage(p){
|
|||
}
|
||||
for (var i=0;i<4;i++) {
|
||||
if (!apps[p*4+i]) return i;
|
||||
draw_icon(p,i,selected==i);
|
||||
draw_icon(p,i,selected==i && !settings.direct);
|
||||
}
|
||||
g.flip();
|
||||
}
|
||||
|
@ -81,9 +93,9 @@ Bangle.on("touch",(_,p)=>{
|
|||
for (i=0;i<4;i++){
|
||||
if((page*4+i)<Napps){
|
||||
if (isTouched(p,i)) {
|
||||
draw_icon(page,i,true);
|
||||
if (selected>=0) {
|
||||
if (selected!=i){
|
||||
draw_icon(page,i,true && !settings.direct);
|
||||
if (selected>=0 || settings.direct) {
|
||||
if (selected!=i && !settings.direct){
|
||||
draw_icon(page,selected,false);
|
||||
} else {
|
||||
load(apps[page*4+i].src);
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
(function(back) {
|
||||
var FILE = "dtlaunch.json";
|
||||
|
||||
var settings = Object.assign({
|
||||
showClocks: true,
|
||||
showLaunchers: true
|
||||
}, require('Storage').readJSON(FILE, true) || {});
|
||||
|
||||
function writeSettings() {
|
||||
require('Storage').writeJSON(FILE, settings);
|
||||
}
|
||||
|
||||
E.showMenu({
|
||||
"" : { "title" : "Desktop launcher" },
|
||||
"< Back" : () => back(),
|
||||
'Show clocks': {
|
||||
value: settings.showClocks,
|
||||
format: v => v?"On":"Off",
|
||||
onchange: v => {
|
||||
settings.showClocks = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
'Show launchers': {
|
||||
value: settings.showLaunchers,
|
||||
format: v => v?"On":"Off",
|
||||
onchange: v => {
|
||||
settings.showLaunchers = v;
|
||||
writeSettings();
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
|
@ -0,0 +1,42 @@
|
|||
(function(back) {
|
||||
var FILE = "dtlaunch.json";
|
||||
|
||||
var settings = Object.assign({
|
||||
showClocks: true,
|
||||
showLaunchers: true,
|
||||
direct: false
|
||||
}, require('Storage').readJSON(FILE, true) || {});
|
||||
|
||||
function writeSettings() {
|
||||
require('Storage').writeJSON(FILE, settings);
|
||||
}
|
||||
|
||||
E.showMenu({
|
||||
"" : { "title" : "Desktop launcher" },
|
||||
"< Back" : () => back(),
|
||||
'Show clocks': {
|
||||
value: settings.showClocks,
|
||||
format: v => v?"On":"Off",
|
||||
onchange: v => {
|
||||
settings.showClocks = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
'Show launchers': {
|
||||
value: settings.showLaunchers,
|
||||
format: v => v?"On":"Off",
|
||||
onchange: v => {
|
||||
settings.showLaunchers = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
'Direct launch': {
|
||||
value: settings.direct,
|
||||
format: v => v?"On":"Off",
|
||||
onchange: v => {
|
||||
settings.direct = v;
|
||||
writeSettings();
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
|
@ -3,6 +3,8 @@
|
|||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<p><b>THIS IS CURRENTLY BETA - PLEASE USE THE NORMAL FIRMWARE UPDATE
|
||||
INSTRUCTIONS FOR <a href="https://www.espruino.com/Bangle.js#firmware-updates" target="_blank">BANGLE.JS</a> 1 AND <a href="https://www.espruino.com/Bangle.js2#firmware-updates" target="_blank">BANGLE.JS 2</a></b>. For usage on Bangle.js 2 you'll likely need to have an updated bootloader.</p>
|
||||
<div id="fw-unknown">
|
||||
<p><b>Firmware updates using the App Loader are only possible on
|
||||
Bangle.js 2. For firmware updates on Bangle.js 1 please
|
||||
|
@ -85,7 +87,7 @@ function checkForFileOnServer() {
|
|||
});
|
||||
}
|
||||
|
||||
var regex = new RegExp("_banglejs2");
|
||||
var regex = new RegExp("_banglejs2.*zip$");
|
||||
|
||||
var domFirmwareList = document.getElementById("latest-firmware-list");
|
||||
var domFirmware = document.getElementById("latest-firmware");
|
||||
|
@ -107,7 +109,7 @@ function checkForFileOnServer() {
|
|||
});
|
||||
console.log("Finished check for firmware files...");
|
||||
var fwlinks = document.querySelectorAll(".fw-link");
|
||||
for (var i=0;i<fwlinks.length;fwlinks++)
|
||||
for (var i=0;i<fwlinks.length;i++)
|
||||
fwlinks[i].addEventListener("click", e => {
|
||||
e.preventDefault();
|
||||
var url = e.target.href;
|
||||
|
|
|
@ -5,3 +5,4 @@
|
|||
0.05: Setting to disable double/triple press control, remove touch controls setting, reduce fadeout flicker
|
||||
0.06: Bangle.js 2 support
|
||||
0.07: Fix "previous" button image
|
||||
0.08: Fix scrolling title background color
|
||||
|
|
|
@ -91,7 +91,7 @@ function rScroller(l) {
|
|||
y = l.y+l.h/2;
|
||||
l.offset = l.offset%w;
|
||||
g.setClipRect(l.x, l.y, l.x+l.w-1, l.y+l.h-1)
|
||||
.setColor(l.col)
|
||||
.setColor(l.col).setBgColor(l.bgCol) // need to set colors: iScroll calls this function outside Layout
|
||||
.setFontAlign(-1, 0) // left center
|
||||
.clearRect(l.x, l.y, l.x+l.w-1, l.y+l.h-1)
|
||||
.drawString(l.label, l.x-l.offset+40, y)
|
||||
|
|
|
@ -24,3 +24,5 @@
|
|||
0.22: Respect Quiet Mode
|
||||
0.23: Allow notification dismiss to remove from phone too
|
||||
0.24: tag HRM power requests to allow this to work alongside other widgets/apps (fix #799)
|
||||
0.25: workaround call notification
|
||||
Fix inflated step number
|
||||
|
|
|
@ -184,7 +184,7 @@
|
|||
case "call":
|
||||
var note = { size: 55, title: event.name, id: "call",
|
||||
body: event.number, icon:require("heatshrink").decompress(atob("jEYwIMJj4CCwACJh4CCCIMOAQMGAQMHAQMDAQMBCIMB4PwgHz/EAn4CBj4CBg4CBgACCAAw="))}
|
||||
if (event.cmd === "incoming") {
|
||||
if (event.cmd === "incoming" || event.cmd === "") {
|
||||
require("notify").show(note);
|
||||
if (!(require('Storage').readJSON('setting.json',1)||{}).quiet) {
|
||||
Bangle.buzz();
|
||||
|
@ -262,7 +262,7 @@
|
|||
// Send a summary of activity to Gadgetbridge
|
||||
function sendActivity(hrm) {
|
||||
var steps = currentSteps - lastSentSteps;
|
||||
lastSentSteps = 0;
|
||||
lastSentSteps = currentSteps;
|
||||
gbSend({ t: "act", stp: steps, hrm:hrm });
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: multiple player score support
|
|
@ -0,0 +1,37 @@
|
|||
# Golf Score
|
||||
|
||||
Lets you keep track of strokes during a game of Golf.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## Usage
|
||||
|
||||
1. Open the app,
|
||||
1. scroll to setup
|
||||
2. set the number of holes (18 by default, but can be configured)
|
||||
3. set the number of players (4 by default, but can be 1-20)
|
||||
4. click back
|
||||
5. scroll to a hole (hole 1)
|
||||
6. scroll to a player and set the number of strokes they took (repeat as needed)
|
||||
7. click next hole and repeat #6 and #7 as needed; or click back
|
||||
8. at any time, check the score card for a sum total of all the strokes for each player
|
||||
|
||||
## Features
|
||||
|
||||
Track strokes for multiple players (1-20)
|
||||
Set number of holes on course
|
||||
|
||||
## Controls
|
||||
|
||||
N/A
|
||||
|
||||
## Requests
|
||||
|
||||
Michael Salaverry (github.com/barakplasma)
|
||||
|
||||
## Creator
|
||||
|
||||
Michael Salaverry
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwIEBgOABQcD4AFDg1wAokYDokOAokDDwkBDwkADwn4nAFD/geDgP8gYFEDwn8gFgDocA+AFCkE/A4IABg//Aoc//4RDn/+Goc/8AFJj4FLEQYFGh4FLIAYFGg4FKh5sBApEfnhTEAok+Aol8vihEAon4AocB+F4ZQYFF8AFDg/AAocPAouAKYcfXQQFHjzEEhjvDA"))
|
|
@ -0,0 +1,113 @@
|
|||
// @ts-check
|
||||
// @ts-ignore
|
||||
const menu = require("graphical_menu");
|
||||
/**
|
||||
* @type {{showMenu: (config) => void}}
|
||||
*/
|
||||
let E;
|
||||
/**
|
||||
* @type {{clear: () => void}}
|
||||
*/
|
||||
let g;
|
||||
|
||||
let holes_count = 18;
|
||||
let player_count = 4;
|
||||
/**
|
||||
* @type {number[][]}
|
||||
*/
|
||||
let course = new Array(holes_count).map(() => new Array(player_count).fill(0));
|
||||
|
||||
const main_menu = {
|
||||
"": {
|
||||
"title": "-- Golf --"
|
||||
},
|
||||
"Setup": function () { E.showMenu(setup_menu); },
|
||||
"Score Card": function () {
|
||||
calculate_score();
|
||||
E.showMenu(score_card);
|
||||
},
|
||||
};
|
||||
|
||||
function calculate_score() {
|
||||
let scores = course.reduce((acc, hole) => {
|
||||
hole.forEach((stroke_count, player) => {
|
||||
acc[player] = acc[player]+stroke_count;
|
||||
});
|
||||
return acc;
|
||||
}, new Array(player_count).fill(0));
|
||||
|
||||
score_card = {
|
||||
"": {
|
||||
"title": "score card"
|
||||
},
|
||||
"< Back": function () { E.showMenu(main_menu); },
|
||||
};
|
||||
|
||||
for (let player = 0; player < player_count; player++) {
|
||||
score_card["Player - " + (player + 1)] = {
|
||||
value: scores[player]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let score_card = {};
|
||||
|
||||
const setup_menu = {
|
||||
"": {
|
||||
"title": "-- Golf Setup --"
|
||||
},
|
||||
"Holes": {
|
||||
value: holes_count,
|
||||
min: 1, max: 20, step: 1, wrap: true,
|
||||
onchange: v => { holes_count = v; add_holes(); }
|
||||
},
|
||||
"Players": {
|
||||
value: player_count,
|
||||
min: 1, max: 10, step: 1, wrap: true,
|
||||
onchange: v => { player_count = v; }
|
||||
},
|
||||
"< Back": function () { E.showMenu(main_menu); },
|
||||
};
|
||||
|
||||
function inc_hole(i, player) { return function (v) { course[i][player] = v; }; }
|
||||
|
||||
function add_holes() {
|
||||
for (let j = 0; j < 20; j++) {
|
||||
delete main_menu["Hole - " + (j + 1)];
|
||||
}
|
||||
for (let i = 0; i < holes_count; i++) {
|
||||
course[i] = new Array(player_count).fill(0);
|
||||
main_menu["Hole - " + (i + 1)] = goto_hole_menu(i);
|
||||
}
|
||||
E.showMenu(main_menu);
|
||||
}
|
||||
|
||||
function goto_hole_menu(i) {
|
||||
return function () {
|
||||
E.showMenu(hole_menu(i));
|
||||
};
|
||||
}
|
||||
|
||||
function hole_menu(i) {
|
||||
let menu = {
|
||||
"": {
|
||||
"title": `-- Hole ${i + 1}--`
|
||||
},
|
||||
"Next hole": goto_hole_menu(i + 1),
|
||||
"< Back": function () { E.showMenu(main_menu); },
|
||||
};
|
||||
|
||||
for (let player = 0; player < player_count; player++) {
|
||||
menu[`player - ${player + 1}`] = {
|
||||
value: course[i][player],
|
||||
min: 1, max: 20, step: 1, wrap: true,
|
||||
onchange: inc_hole(i, player)
|
||||
};
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
g.clear();
|
||||
add_holes();
|
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.2 KiB |
|
@ -1,3 +1,4 @@
|
|||
0.01: New App!
|
||||
0.02: using TS and rollup to bundle
|
||||
0.03: bug fixes and support bangle 1
|
||||
0.03: bug fixes and support bangle 1
|
||||
0.04: removing TS
|
|
@ -1,5 +1,5 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2021 Michael Salaverry
|
||||
Copyright (c) 2016-20 Ionică Bizău <bizauionica@gmail.com> (https://ionicabizau.net)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
|
|
@ -1,17 +1,26 @@
|
|||
!function(){"use strict";
|
||||
/*!
|
||||
* This script was taked from this page and ported to Node.js by Ionic Bizu
|
||||
* http://www.shamash.org/help/javadate.shtml
|
||||
*
|
||||
* This script was adapted from C sources written by
|
||||
* Scott E. Lee, which contain the following copyright notice:
|
||||
*
|
||||
* Copyright 1993-1995, Scott E. Lee, all rights reserved.
|
||||
* Permission granted to use, copy, modify, distribute and sell so long as
|
||||
* the above copyright and this permission statement are retained in all
|
||||
* copies. THERE IS NO WARRANTY - USE AT YOUR OWN RISK.
|
||||
*
|
||||
* Bill Hastings
|
||||
* RBI Software Systems
|
||||
* bhastings@rbi.com
|
||||
*/var t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};var i=new function(t,i,e,o,r,n,h,a,s,f,u,l,v,c){this[0]=t,this[1]=i,this[2]=e,this[3]=o,this[4]=r,this[5]=n,this[6]=h,this[7]=a,this[8]=s,this[9]=f,this[10]=u,this[11]=l,this[12]=v,this[13]=c}("Tishri","Heshvan","Kislev","Tevet","Shevat","AdarI","AdarII","Nisan","Iyyar","Sivan","Tammuz","Av","Elul"),e=new function(t,i,e,o,r,n,h,a,s,f,u,l,v,c,y,d,m,M,b){this[0]=t,this[1]=i,this[2]=e,this[3]=o,this[4]=r,this[5]=n,this[6]=h,this[7]=a,this[8]=s,this[9]=f,this[10]=u,this[11]=l,this[12]=v,this[13]=c,this[14]=y,this[15]=d,this[16]=m,this[17]=M,this[18]=b}(12,12,13,12,12,13,12,13,12,12,13,12,12,13,12,12,13,12,13);g.clear();let o=new Date,r=function(o){var r,n,h=0,a=0,s=0,f=0,u=0,l=0,v=0;function c(t){var i,o,r,n;for(f=Math.floor((t+310)/6940),r=void 0,n=void 0,r=31524,n=(r+=45971*f)>>16,n+=2744*f,o=Math.floor(n/25920),r=(n-=25920*o)<<16|65535&r,i=Math.floor(r/25920),l=o<<16|i,v=r-=25920*i;l<t-6940+310;)f++,v+=179876755,l+=Math.floor(v/25920),v%=25920;for(u=0;u<18&&!(l>t-74);u++)v+=765433*e[u],l+=Math.floor(v/25920),v%=25920}function y(t,i,e){var o=i,r=o%7;return(e>=19440||!(2==t||5==t||7==t||10==t||13==t||16==t||18==t)&&2==r&&e>=9924||(3==t||6==t||8==t||11==t||14==t||17==t||0==t)&&1==r&&e>=16789)&&(o++,7==++r&&(r=0)),3!=r&&5!=r&&0!=r||o++,o}var d=o;return"object"===(void 0===d?"undefined":t(d))&&(r=o.getMonth()+1,n=o.getDate(),d=o.getFullYear()),function(t){var i,o=0,r=0,n=t-347997;if(c(n),n>=(o=y(u,l,v))){if(s=19*f+u+1,n<o+59)return void(n<o+30?(h=1,a=n-o+1):(h=2,a=n-o-29));v+=765433*e[u],l+=Math.floor(v/25920),r=y((u+1)%19,l,v%=25920)}else{if(s=19*f+u,n>=o-177)return void(n>o-30?(h=13,a=n-o+30):n>o-60?(h=12,a=n-o+60):n>o-89?(h=11,a=n-o+89):n>o-119?(h=10,a=n-o+119):n>o-148?(h=9,a=n-o+148):(h=8,a=n-o+178));if(13==e[(s-1)%19]){if(h=7,(a=n-o+207)>0)return;if(h--,(a+=30)>0)return;h--,a+=30}else{if(h=6,(a=n-o+207)>0)return;h--,a+=30}if(a>0)return;if(h--,(a+=29)>0)return;r=o,c(l-365),o=y(u,l,v)}if(l=n-o-29,355==(i=r-o)||385==i){if(l<=30)return h=2,void(a=l);l-=30}else{if(l<=29)return h=2,void(a=l);l-=29}h=3,a=l}(function(t,i,e){var o=0,r=0,n=void 0;return o=t<0?t+4801:t+4800,i>2?r=i-3:(r=i+9,o--),n=Math.floor(146097*Math.floor(o/100)/4),n+=Math.floor(o%100*1461/4),n+=Math.floor((153*r+2)/5),n+=e-32045}(d,r,n)),{year:s,month:h,date:a,month_name:i[h-1]}}(o);var n={"":{title:"Hebrew Date"},cal:{value:require("locale").date(o,1),onchange:()=>{}},date:{value:r.date,onchange:()=>{}},month:{value:r.month_name,onchange:()=>{}},year:{value:r.year,onchange:()=>{}}};E.showMenu(n)}();
|
||||
g.clear();
|
||||
|
||||
let now = new Date();
|
||||
|
||||
let today = require('hebrewDate').hebrewDate(now);
|
||||
|
||||
var mainmenu = {
|
||||
"": {
|
||||
"title": "Hebrew Date"
|
||||
},
|
||||
greg: {
|
||||
// @ts-ignore
|
||||
value: require('locale').date(now, 1),
|
||||
},
|
||||
date: {
|
||||
value: today.date,
|
||||
},
|
||||
month: {
|
||||
value: today.month_name,
|
||||
},
|
||||
year: {
|
||||
value: today.year,
|
||||
}
|
||||
};
|
||||
// @ts-ignore
|
||||
E.showMenu(mainmenu);
|
|
@ -1,6 +1,5 @@
|
|||
/*!
|
||||
* This script was taked from this page and ported to Node.js by Ionic Bizu
|
||||
* http://www.shamash.org/help/javadate.shtml
|
||||
* This script was taked from this page http://www.shamash.org/help/javadate.shtml and ported to Node.js by Ionică Bizău in https://github.com/IonicaBizau/hebrew-date
|
||||
*
|
||||
* This script was adapted from C sources written by
|
||||
* Scott E. Lee, which contain the following copyright notice:
|
||||
|
@ -14,33 +13,11 @@
|
|||
* RBI Software Systems
|
||||
* bhastings@rbi.com
|
||||
*/
|
||||
|
||||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
||||
|
||||
var GREG_SDN_OFFSET = 32045,
|
||||
DAYS_PER_5_MONTHS = 153,
|
||||
DAYS_PER_4_YEARS = 1461,
|
||||
DAYS_PER_400_YEARS = 146097;
|
||||
|
||||
var HALAKIM_PER_HOUR = 1080,
|
||||
HALAKIM_PER_DAY = 25920,
|
||||
HALAKIM_PER_LUNAR_CYCLE = 29 * HALAKIM_PER_DAY + 13753,
|
||||
HALAKIM_PER_METONIC_CYCLE = HALAKIM_PER_LUNAR_CYCLE * (12 * 19 + 7);
|
||||
|
||||
var HEB_SDN_OFFSET = 347997,
|
||||
NEW_MOON_OF_CREATION = 31524,
|
||||
NOON = 18 * HALAKIM_PER_HOUR,
|
||||
AM3_11_20 = 9 * HALAKIM_PER_HOUR + 204,
|
||||
AM9_32_43 = 15 * HALAKIM_PER_HOUR + 589;
|
||||
|
||||
var SUN = 0,
|
||||
MON = 1,
|
||||
TUES = 2,
|
||||
WED = 3,
|
||||
THUR = 4,
|
||||
FRI = 5,
|
||||
SAT = 6;
|
||||
|
||||
var GREG_SDN_OFFSET = 32045, DAYS_PER_5_MONTHS = 153, DAYS_PER_4_YEARS = 1461, DAYS_PER_400_YEARS = 146097;
|
||||
var HALAKIM_PER_HOUR = 1080, HALAKIM_PER_DAY = 25920, HALAKIM_PER_LUNAR_CYCLE = 29 * HALAKIM_PER_DAY + 13753, HALAKIM_PER_METONIC_CYCLE = HALAKIM_PER_LUNAR_CYCLE * (12 * 19 + 7);
|
||||
var HEB_SDN_OFFSET = 347997, NEW_MOON_OF_CREATION = 31524, NOON = 18 * HALAKIM_PER_HOUR, AM3_11_20 = 9 * HALAKIM_PER_HOUR + 204, AM9_32_43 = 15 * HALAKIM_PER_HOUR + 589;
|
||||
var SUN = 0, MON = 1, TUES = 2, WED = 3, THUR = 4, FRI = 5, SAT = 6;
|
||||
function weekdayarr(d0, d1, d2, d3, d4, d5, d6) {
|
||||
this[0] = d0;
|
||||
this[1] = d1;
|
||||
|
@ -50,7 +27,6 @@ function weekdayarr(d0, d1, d2, d3, d4, d5, d6) {
|
|||
this[5] = d5;
|
||||
this[6] = d6;
|
||||
}
|
||||
|
||||
function gregmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11) {
|
||||
this[0] = m0;
|
||||
this[1] = m1;
|
||||
|
@ -65,8 +41,7 @@ function gregmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11) {
|
|||
this[10] = m10;
|
||||
this[11] = m11;
|
||||
}
|
||||
|
||||
function hebrewmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13?: any) {
|
||||
function hebrewmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13) {
|
||||
this[0] = m0;
|
||||
this[1] = m1;
|
||||
this[2] = m2;
|
||||
|
@ -82,7 +57,6 @@ function hebrewmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m
|
|||
this[12] = m12;
|
||||
this[13] = m13;
|
||||
}
|
||||
|
||||
function monthsperyeararr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18) {
|
||||
this[0] = m0;
|
||||
this[1] = m1;
|
||||
|
@ -104,12 +78,7 @@ function monthsperyeararr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12,
|
|||
this[17] = m17;
|
||||
this[18] = m18;
|
||||
}
|
||||
|
||||
var gWeekday = new weekdayarr("Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur"),
|
||||
gMonth = new gregmontharr("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"),
|
||||
hMonth = new hebrewmontharr("Tishri", "Heshvan", "Kislev", "Tevet", "Shevat", "AdarI", "AdarII", "Nisan", "Iyyar", "Sivan", "Tammuz", "Av", "Elul"),
|
||||
mpy = new monthsperyeararr(12, 12, 13, 12, 12, 13, 12, 13, 12, 12, 13, 12, 12, 13, 12, 12, 13, 12, 13);
|
||||
|
||||
var gWeekday = new weekdayarr("Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur"), gMonth = new gregmontharr("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"), hMonth = new hebrewmontharr("Tishri", "Heshvan", "Kislev", "Tevet", "Shevat", "AdarI", "AdarII", "Nisan", "Iyyar", "Sivan", "Tammuz", "Av", "Elul"), mpy = new monthsperyeararr(12, 12, 13, 12, 12, 13, 12, 13, 12, 12, 13, 12, 12, 13, 12, 12, 13, 12, 13);
|
||||
/**
|
||||
* hebrewDate
|
||||
* Convert the Gregorian dates into Hebrew calendar dates.
|
||||
|
@ -124,55 +93,36 @@ var gWeekday = new weekdayarr("Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "S
|
|||
* - `month_name`: The Hebrew month name.
|
||||
* - `date`: The Hebrew date.
|
||||
*/
|
||||
export const hebrewDate = function (inputDateOrYear: Date) {
|
||||
function hebrewDate(inputDateOrYear) {
|
||||
var inputMonth, inputDate;
|
||||
|
||||
var hebrewMonth = 0,
|
||||
hebrewDate = 0,
|
||||
hebrewYear = 0,
|
||||
metonicCycle = 0,
|
||||
metonicYear = 0,
|
||||
moladDay = 0,
|
||||
moladHalakim = 0;
|
||||
|
||||
var hebrewMonth = 0, hebrewDate = 0, hebrewYear = 0, metonicCycle = 0, metonicYear = 0, moladDay = 0, moladHalakim = 0;
|
||||
function GregorianToSdn(inputYear, inputMonth, inputDay) {
|
||||
|
||||
var year = 0,
|
||||
month = 0,
|
||||
sdn = void 0;
|
||||
|
||||
var year = 0, month = 0, sdn = void 0;
|
||||
// Make year a positive number
|
||||
if (inputYear < 0) {
|
||||
year = inputYear + 4801;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
year = inputYear + 4800;
|
||||
}
|
||||
|
||||
// Adjust the start of the year
|
||||
if (inputMonth > 2) {
|
||||
month = inputMonth - 3;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
month = inputMonth + 9;
|
||||
year--;
|
||||
}
|
||||
|
||||
sdn = Math.floor(Math.floor(year / 100) * DAYS_PER_400_YEARS / 4);
|
||||
sdn += Math.floor(year % 100 * DAYS_PER_4_YEARS / 4);
|
||||
sdn += Math.floor((month * DAYS_PER_5_MONTHS + 2) / 5);
|
||||
sdn += inputDay - GREG_SDN_OFFSET;
|
||||
|
||||
return sdn;
|
||||
}
|
||||
|
||||
function SdnToHebrew(sdn) {
|
||||
var tishri1 = 0,
|
||||
tishri1After = 0,
|
||||
yearLength = 0,
|
||||
inputDay = sdn - HEB_SDN_OFFSET;
|
||||
|
||||
var tishri1 = 0, tishri1After = 0, yearLength = 0, inputDay = sdn - HEB_SDN_OFFSET;
|
||||
FindTishriMolad(inputDay);
|
||||
tishri1 = Tishri1(metonicYear, moladDay, moladHalakim);
|
||||
|
||||
if (inputDay >= tishri1) {
|
||||
// It found Tishri 1 at the start of the year.
|
||||
hebrewYear = metonicCycle * 19 + metonicYear + 1;
|
||||
|
@ -180,7 +130,8 @@ export const hebrewDate = function (inputDateOrYear: Date) {
|
|||
if (inputDay < tishri1 + 30) {
|
||||
hebrewMonth = 1;
|
||||
hebrewDate = inputDay - tishri1 + 1;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
hebrewMonth = 2;
|
||||
hebrewDate = inputDay - tishri1 - 29;
|
||||
}
|
||||
|
@ -191,7 +142,8 @@ export const hebrewDate = function (inputDateOrYear: Date) {
|
|||
moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY);
|
||||
moladHalakim = moladHalakim % HALAKIM_PER_DAY;
|
||||
tishri1After = Tishri1((metonicYear + 1) % 19, moladDay, moladHalakim);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// It found Tishri 1 at the end of the year.
|
||||
hebrewYear = metonicCycle * 19 + metonicYear;
|
||||
if (inputDay >= tishri1 - 177) {
|
||||
|
@ -199,44 +151,56 @@ export const hebrewDate = function (inputDateOrYear: Date) {
|
|||
if (inputDay > tishri1 - 30) {
|
||||
hebrewMonth = 13;
|
||||
hebrewDate = inputDay - tishri1 + 30;
|
||||
} else if (inputDay > tishri1 - 60) {
|
||||
}
|
||||
else if (inputDay > tishri1 - 60) {
|
||||
hebrewMonth = 12;
|
||||
hebrewDate = inputDay - tishri1 + 60;
|
||||
} else if (inputDay > tishri1 - 89) {
|
||||
}
|
||||
else if (inputDay > tishri1 - 89) {
|
||||
hebrewMonth = 11;
|
||||
hebrewDate = inputDay - tishri1 + 89;
|
||||
} else if (inputDay > tishri1 - 119) {
|
||||
}
|
||||
else if (inputDay > tishri1 - 119) {
|
||||
hebrewMonth = 10;
|
||||
hebrewDate = inputDay - tishri1 + 119;
|
||||
} else if (inputDay > tishri1 - 148) {
|
||||
}
|
||||
else if (inputDay > tishri1 - 148) {
|
||||
hebrewMonth = 9;
|
||||
hebrewDate = inputDay - tishri1 + 148;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
hebrewMonth = 8;
|
||||
hebrewDate = inputDay - tishri1 + 178;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (mpy[(hebrewYear - 1) % 19] == 13) {
|
||||
hebrewMonth = 7;
|
||||
hebrewDate = inputDay - tishri1 + 207;
|
||||
if (hebrewDate > 0) return;
|
||||
if (hebrewDate > 0)
|
||||
return;
|
||||
hebrewMonth--;
|
||||
hebrewDate += 30;
|
||||
if (hebrewDate > 0) return;
|
||||
hebrewMonth--;
|
||||
hebrewDate += 30;
|
||||
} else {
|
||||
hebrewMonth = 6;
|
||||
hebrewDate = inputDay - tishri1 + 207;
|
||||
if (hebrewDate > 0) return;
|
||||
if (hebrewDate > 0)
|
||||
return;
|
||||
hebrewMonth--;
|
||||
hebrewDate += 30;
|
||||
}
|
||||
if (hebrewDate > 0) return;
|
||||
else {
|
||||
hebrewMonth = 6;
|
||||
hebrewDate = inputDay - tishri1 + 207;
|
||||
if (hebrewDate > 0)
|
||||
return;
|
||||
hebrewMonth--;
|
||||
hebrewDate += 30;
|
||||
}
|
||||
if (hebrewDate > 0)
|
||||
return;
|
||||
hebrewMonth--;
|
||||
hebrewDate += 29;
|
||||
if (hebrewDate > 0) return;
|
||||
if (hebrewDate > 0)
|
||||
return;
|
||||
// We need the length of the year to figure this out,so find Tishri 1 of this year.
|
||||
tishri1After = tishri1;
|
||||
FindTishriMolad(moladDay - 365);
|
||||
|
@ -253,7 +217,8 @@ export const hebrewDate = function (inputDateOrYear: Date) {
|
|||
return;
|
||||
}
|
||||
moladDay -= 30;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// Heshvan has 29 days
|
||||
if (moladDay <= 29) {
|
||||
hebrewMonth = 2;
|
||||
|
@ -266,7 +231,6 @@ export const hebrewDate = function (inputDateOrYear: Date) {
|
|||
hebrewMonth = 3;
|
||||
hebrewDate = moladDay;
|
||||
}
|
||||
|
||||
function FindTishriMolad(inputDay) {
|
||||
// Estimate the metonic cycle number. Note that this may be an under
|
||||
// estimate because there are 6939.6896 days in a metonic cycle not
|
||||
|
@ -287,18 +251,15 @@ export const hebrewDate = function (inputDateOrYear: Date) {
|
|||
}
|
||||
// Find the molad of Tishri closest to this date.
|
||||
for (metonicYear = 0; metonicYear < 18; metonicYear++) {
|
||||
if (moladDay > inputDay - 74) break;
|
||||
if (moladDay > inputDay - 74)
|
||||
break;
|
||||
moladHalakim += HALAKIM_PER_LUNAR_CYCLE * mpy[metonicYear];
|
||||
moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY);
|
||||
moladHalakim = moladHalakim % HALAKIM_PER_DAY;
|
||||
}
|
||||
}
|
||||
|
||||
function MoladOfMetonicCycle() {
|
||||
var r1 = void 0,
|
||||
r2 = void 0,
|
||||
d1 = void 0,
|
||||
d2 = void 0;
|
||||
var r1 = void 0, r2 = void 0, d1 = void 0, d2 = void 0;
|
||||
// Start with the time of the first molad after creation.
|
||||
r1 = NEW_MOON_OF_CREATION;
|
||||
// Calculate gMetonicCycle * HALAKIM_PER_METONIC_CYCLE. The upper 32
|
||||
|
@ -317,42 +278,34 @@ export const hebrewDate = function (inputDateOrYear: Date) {
|
|||
moladDay = d2 << 16 | d1;
|
||||
moladHalakim = r1;
|
||||
}
|
||||
|
||||
function Tishri1(metonicYear, moladDay, moladHalakim) {
|
||||
var tishri1 = moladDay,
|
||||
dow = tishri1 % 7,
|
||||
leapYear = metonicYear == 2 || metonicYear == 5 || metonicYear == 7 || metonicYear == 10 || metonicYear == 13 || metonicYear == 16 || metonicYear == 18,
|
||||
lastWasLeapYear = metonicYear == 3 || metonicYear == 6 || metonicYear == 8 || metonicYear == 11 || metonicYear == 14 || metonicYear == 17 || metonicYear == 0;
|
||||
|
||||
var tishri1 = moladDay, dow = tishri1 % 7, leapYear = metonicYear == 2 || metonicYear == 5 || metonicYear == 7 || metonicYear == 10 || metonicYear == 13 || metonicYear == 16 || metonicYear == 18, lastWasLeapYear = metonicYear == 3 || metonicYear == 6 || metonicYear == 8 || metonicYear == 11 || metonicYear == 14 || metonicYear == 17 || metonicYear == 0;
|
||||
// Apply rules 2,3 and 4
|
||||
if (moladHalakim >= NOON || !leapYear && dow == TUES && moladHalakim >= AM3_11_20 || lastWasLeapYear && dow == MON && moladHalakim >= AM9_32_43) {
|
||||
tishri1++;
|
||||
dow++;
|
||||
if (dow == 7) dow = 0;
|
||||
if (dow == 7)
|
||||
dow = 0;
|
||||
}
|
||||
|
||||
// Apply rule 1 after the others because it can cause an additional delay of one day.
|
||||
if (dow == WED || dow == FRI || dow == SUN) {
|
||||
tishri1++;
|
||||
}
|
||||
|
||||
return tishri1;
|
||||
}
|
||||
|
||||
var inputYear: Date | number = inputDateOrYear;
|
||||
|
||||
var inputYear = inputDateOrYear;
|
||||
if ((typeof inputYear === "undefined" ? "undefined" : _typeof(inputYear)) === "object") {
|
||||
inputMonth = inputDateOrYear.getMonth() + 1;
|
||||
inputDate = inputDateOrYear.getDate();
|
||||
inputYear = inputDateOrYear.getFullYear();
|
||||
}
|
||||
|
||||
SdnToHebrew(GregorianToSdn(inputYear, inputMonth, inputDate));
|
||||
|
||||
return {
|
||||
year: hebrewYear,
|
||||
month: hebrewMonth,
|
||||
date: hebrewDate,
|
||||
month_name: hMonth[hebrewMonth - 1]
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
exports.hebrewDate = hebrewDate;
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"name": "hebrew_calendar",
|
||||
"version": "0.0.3",
|
||||
"description": "Bangle.js app for seeing hebrew calendar",
|
||||
"main": "app.js",
|
||||
"types": "app.d.ts",
|
||||
"scripts": {
|
||||
"build": "rollup -c"
|
||||
},
|
||||
"author": {
|
||||
"name": "Michael Salaverry",
|
||||
"url": "https://github.com/barakplasma"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-typescript": "^4.1.1",
|
||||
"rollup": "^2.10.2",
|
||||
"rollup-plugin-terser": "^5.3.0",
|
||||
"terser": "^4.7.0",
|
||||
"tslib": "^2.0.0",
|
||||
"typescript": "^3.9.2"
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
import typescript from '@rollup/plugin-typescript';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
|
||||
export default {
|
||||
input: './src/app.ts',
|
||||
output: {
|
||||
dir: '.',
|
||||
format: 'iife',
|
||||
name: 'hebrew_calendar'
|
||||
},
|
||||
plugins: [
|
||||
typescript(),
|
||||
terser(),
|
||||
]
|
||||
};
|
|
@ -1,34 +0,0 @@
|
|||
declare var Bangle: any;
|
||||
declare var g: any;
|
||||
declare var E: any;
|
||||
declare var require: any;
|
||||
|
||||
g.clear();
|
||||
|
||||
let now = new Date();
|
||||
import { hebrewDate } from "./hebrewDate";
|
||||
|
||||
let today = hebrewDate(now);
|
||||
|
||||
var mainmenu = {
|
||||
"" : {
|
||||
"title" : "Hebrew Date"
|
||||
},
|
||||
cal: {
|
||||
value: require('locale').date(now,1),
|
||||
onchange : () => {}
|
||||
},
|
||||
date: {
|
||||
value : today.date,
|
||||
onchange : () => {}
|
||||
},
|
||||
month: {
|
||||
value : today.month_name,
|
||||
onchange : () => {}
|
||||
},
|
||||
year: {
|
||||
value : today.year,
|
||||
onchange : () => {}
|
||||
}
|
||||
};
|
||||
E.showMenu(mainmenu);
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "es2015",
|
||||
"noImplicitAny": false,
|
||||
"target": "es2015"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
0.01: New App!
|
||||
0.02: Stopped watchface from flashing every interval
|
||||
0.03: Move to Bangle.setUI to launcher support
|
||||
0.04: Tweaks for compatibility with BangleJS2
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Imprecise Word Clock
|
||||
|
||||
This clock tells time in very rough approximation, as in "Late morning" or "Early afternoon." Good for vacations and weekends. Press button 1 to see the time in accurate, digital form. But do you really need to know the exact time?
|
||||
This clock tells time in very rough approximation, as in "Late morning" or "Early afternoon." Good for vacations and weekends. Touch the screen to see the time in accurate, digital form. But do you really need to know the exact time?
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
A remix of word clock
|
||||
by Gordon Williams https://github.com/gfwilliams
|
||||
- Changes the representation of time to be more general
|
||||
- Shows accurate digital time when button 1 is pressed
|
||||
- Toggles showing of accurate digital time when screen touched.
|
||||
*/
|
||||
/* jshint esversion: 6 */
|
||||
|
||||
|
@ -34,14 +34,16 @@ const timeOfDay = {
|
|||
};
|
||||
|
||||
|
||||
var big = g.getWidth()>200;
|
||||
// offsets and increments
|
||||
const xs = 35;
|
||||
const ys = 31;
|
||||
const dy = 22;
|
||||
const dx = 25;
|
||||
const xs = big ? 35 : 20;
|
||||
const ys = big ? 31 : 28;
|
||||
const dx = big ? 25 : 20;
|
||||
const dy = big ? 22 : 16;
|
||||
|
||||
|
||||
// font size and color
|
||||
const fontSize = 3; // "6x8"
|
||||
const fontSize = big ? 3 : 2; // "6x8"
|
||||
const passivColor = 0x3186 /*grey*/ ;
|
||||
const activeColorNight = 0xF800 /*red*/ ;
|
||||
const activeColorDay = 0xFFFF /* white */;
|
||||
|
@ -115,6 +117,8 @@ function drawWordClock() {
|
|||
|
||||
// check whether we need to redraw the watchface
|
||||
if (hidx !== hidxPrev) {
|
||||
// Turn off showDigitalTime
|
||||
showDigitalTime = false;
|
||||
// draw allWords
|
||||
var c;
|
||||
var y = ys;
|
||||
|
@ -138,15 +142,14 @@ function drawWordClock() {
|
|||
hidxPrev = hidx;
|
||||
}
|
||||
|
||||
// Display digital time while button 1 is pressed
|
||||
g.clearRect(0, 215, 240, 240);
|
||||
// Display digital time when button is pressed or screen touched
|
||||
g.clearRect(0, big ? 215 : 160, big ? 240 : 176, big ? 240 : 176);
|
||||
if (showDigitalTime){
|
||||
g.setColor(activeColor);
|
||||
g.drawString(time, 120, 215);
|
||||
g.drawString(time, big ? 120 : 90, big ? 215 : 160);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Bangle.on('lcdPower', function(on) {
|
||||
if (on) drawWordClock();
|
||||
});
|
||||
|
@ -157,17 +160,14 @@ Bangle.drawWidgets();
|
|||
setInterval(drawWordClock, 1E4);
|
||||
drawWordClock();
|
||||
|
||||
// Show digital time while top button is pressed (if we have physical buttons)
|
||||
if (global.BTN3) setWatch(function() {
|
||||
showDigitalTime = BTN1.read();
|
||||
drawWordClock();
|
||||
}, BTN1, {repeat:true,edge:"both"});
|
||||
|
||||
// If LCD pressed (on Bangle.js 2) draw digital time
|
||||
Bangle.on('drag',e=>{
|
||||
var pressed = e.b!=0;
|
||||
if (pressed!=showDigitalTime) {
|
||||
showDigitalTime = pressed;
|
||||
// If LCD pressed, toggle drawing digital time
|
||||
Bangle.on('touch',e=>{
|
||||
if (showDigitalTime){
|
||||
showDigitalTime = false;
|
||||
drawWordClock();
|
||||
} else {
|
||||
showDigitalTime = true;
|
||||
drawWordClock();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -4,4 +4,7 @@
|
|||
0.04: Added common bundleId's
|
||||
0.05: Added more bundleId's (app-id's which can be used to
|
||||
determine a friendly app name in the notifications)
|
||||
0.06: Fix (not) popupping up old messages
|
||||
0.06: Fix (not) popupping up old messages
|
||||
0.07: Added more details from music (instead of Undefined)
|
||||
Added more app identifiers
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# iOS integration app
|
||||
|
||||
This is the iOS integration app for Bangle.js. This app allows you to receive
|
||||
notifications from your iPhone. The Apple Notification Center Service (ANCS)
|
||||
sends all the messages to your watch.
|
||||
|
||||
You can allow this if you connect your Bangle to your iPhone. It will be
|
||||
prompted for immediatly after you connect the Bangle to the iPhone.
|
||||
|
||||
### Connecting your Bangle(2).js to your iPhone
|
||||
The Bangle watches are Bluetooth Low Energy (BLE) devices. Sometimes they
|
||||
will not be seen/detected by the Bluetooth scanner in your iPhone settings
|
||||
menu.
|
||||
|
||||
To resolve this, you can download numerous apps who can actually scan
|
||||
for BLE devices. There are great ones out there, free and paid.
|
||||
|
||||
We really like WebBLE, which we also recommend to load apps on your
|
||||
watch with your iOS device, as Safari does not support WebBluetooth
|
||||
for now. It's just a few bucks/pounds/euro's.
|
||||
|
||||
If you like to try a free app first, you can always use NRF Toolbox or
|
||||
Bluetooth BLE Device Finder to find and connect your Bangle.
|
||||
|
||||
## Requests
|
||||
|
||||
Please file any issues on https://github.com/espruino/BangleApps/issues/new?title=ios%20app
|
||||
|
||||
## Creator
|
||||
|
||||
Gordon Williams
|
|
@ -65,14 +65,16 @@ E.on('notify',msg=>{
|
|||
"com.apple.facetime": "FaceTime",
|
||||
"com.apple.mobilecal": "Calendar",
|
||||
"com.apple.mobilemail": "Mail",
|
||||
"com.apple.mobilephone": "Phone",
|
||||
"com.apple.MobileSMS": "SMS Message",
|
||||
"com.apple.Passbook": "iOS Wallet",
|
||||
"com.apple.podcasts": "Podcasts",
|
||||
"com.apple.reminders": "Reminders",
|
||||
"com.apple.shortcuts": "Shortcuts",
|
||||
"com.atebits.Tweetie2": "Twitter",
|
||||
"com.burbn.instagram" : "Instagram",
|
||||
"com.facebook.Facebook": "Facebook",
|
||||
"com.facebook.Messenger": "FB Messenger",
|
||||
"com.facebook.Messenger": "Messenger",
|
||||
"com.google.Chromecast" : "Google Home",
|
||||
"com.google.Gmail" : "GMail",
|
||||
"com.google.hangouts" : "Hangouts",
|
||||
|
@ -81,22 +83,26 @@ E.on('notify',msg=>{
|
|||
"com.ifttt.ifttt" : "IFTTT",
|
||||
"com.jumbo.app" : "Jumbo",
|
||||
"com.linkedin.LinkedIn" : "LinkedIn",
|
||||
"com.microsoft.Office.Outlook" : "Outlook Mail",
|
||||
"com.nestlabs.jasper.release" : "Nest",
|
||||
"com.netflix.Netflix" : "Netflix",
|
||||
"com.reddit.Reddit" : "Reddit",
|
||||
"com.skype.skype": "Skype",
|
||||
"com.skype.SkypeForiPad": "Skype",
|
||||
"com.spotify.client": "Spotify",
|
||||
"com.strava.stravaride": "Strava",
|
||||
"com.tinyspeck.chatlyio": "Slack",
|
||||
"com.toyopagroup.picaboo": "Snapchat",
|
||||
"com.ubercab.UberClient": "Uber",
|
||||
"com.ubercab.UberEats": "UberEats",
|
||||
"com.vilcsak.bitcoin2": "Coinbase",
|
||||
"com.wordfeud.free": "WordFeud",
|
||||
"com.zhiliaoapp.musically": "TikTok",
|
||||
"net.whatsapp.WhatsApp": "WhatsApp",
|
||||
"nl.ah.Appie": "Albert Heijn",
|
||||
"nl.postnl.TrackNTrace": "PostNL",
|
||||
"ph.telegra.Telegraph": "Telegram",
|
||||
"tv.twitch": "Twitch",
|
||||
|
||||
// could also use NRF.ancsGetAppInfo(msg.appId) here
|
||||
};
|
||||
|
@ -104,7 +110,7 @@ E.on('notify',msg=>{
|
|||
'2019':"'"
|
||||
};
|
||||
var replacer = ""; //(n)=>print('Unknown unicode '+n.toString(16));
|
||||
if (appNames[msg.appId]) msg.a
|
||||
//if (appNames[msg.appId]) msg.a
|
||||
require("messages").pushMessage({
|
||||
t : msg.event,
|
||||
id : msg.uid,
|
||||
|
@ -122,9 +128,10 @@ E.on('AMS',a=>{
|
|||
function push(m) {
|
||||
var msg = { t : "modify", id : "music", title:"Music" };
|
||||
if (a.id=="artist") msg.artist = m;
|
||||
else if (a.id=="album") msg.artist = m;
|
||||
else if (a.id=="title") msg.tracl = m;
|
||||
else return; // duration? need to reformat
|
||||
else if (a.id=="album") msg.album = m;
|
||||
else if (a.id=="title") msg.track = m;
|
||||
else if (a.id=="duration") msg.dur = m;
|
||||
else return;
|
||||
require("messages").pushMessage(msg);
|
||||
}
|
||||
if (a.truncated) NRF.amsGetMusicInfo(a.id).then(push)
|
||||
|
|
|
@ -13,3 +13,4 @@
|
|||
0.11: Added translations for nl_NL and changes one formatting
|
||||
0.12: Fixed nl_NL formatting, because the full months won't fit on the Bangle.js2's screen
|
||||
0.13: Now use shorter de_DE date format to more closely match other languages for size
|
||||
0.14: Added some first translations for Messages in nl_NL
|
||||
|
|
|
@ -40,7 +40,16 @@ const charFallbacks = {
|
|||
"č":"c",
|
||||
"ř":"r",
|
||||
"ő":"o",
|
||||
"ě":"e"
|
||||
"ě":"e",
|
||||
"ę":"e",
|
||||
"ą":"a",
|
||||
"ó":"o",
|
||||
"ż":"z",
|
||||
"ź":"z",
|
||||
"ń":"n",
|
||||
"ł":"l",
|
||||
"ś":"s",
|
||||
"ć":"c",
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -189,7 +198,8 @@ var locales = {
|
|||
day: "zondag,maandag,dinsdag,woensdag,donderdag,vrijdag,zaterdag",
|
||||
abmonth: "jan,feb,mrt,apr,mei,jun,jul,aug,sep,okt,nov,dec",
|
||||
month: "januari,februari,maart,april,mei,juni,juli,augustus,september,oktober,november,december",
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nee", No: "Nee", ok: "ok", on: "aan", off: "uit", "< Back": "< Terug" }
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nee", No: "Nee", ok: "ok", on: "aan", off: "uit",
|
||||
"< Back": "< Terug", "Delete": "Verwijderen", "Mark Unread": "Markeer als ongelezen" }
|
||||
},
|
||||
"en_NL": { // English date units with Dutch number, currency and navigation units.
|
||||
lang: "en_NL",
|
||||
|
@ -603,6 +613,24 @@ var locales = {
|
|||
day: "Domingo,Segunda-feira,Terça-feira,Quarta-feira,Quinta-feira,Sexta-feira,Sábado",
|
||||
trans: { yes: "sim", Yes: "Sim", no: "não", No: "Não", ok: "ok", on: "on", off: "off" }
|
||||
},
|
||||
"pl_PL": {
|
||||
lang: "pl_PL",
|
||||
decimal_point: ",",
|
||||
thousands_sep: " ",
|
||||
currency_symbol: "zł",
|
||||
int_curr_symbol: "PLN",
|
||||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°C",
|
||||
ampm: { 0: "", 1: "" },
|
||||
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mar 2021 // 01.03.2021
|
||||
abmonth: "Sty,Lut,Mar,Kwi,Maj,Cze,Lip,Sie,Wrz,Paź,Lis,Gru",
|
||||
month: "Styczeń,Luty,Marzec,Kwiecień,Maj,Czerwiec,Lipiec,Sierpień,Wrzesień,Październik,Listopad,Grudzień",
|
||||
abday: "Ndz,Pon,Wt,Śr,Czw,Pt,Sob",
|
||||
day: "Niedziela,Poniedziałek,Wtorek,Środa,Czwartek,Piątek,Sobota",
|
||||
trans: { yes: "tak", Yes: "Tak", no: "nie", No: "Nie", ok: "ok", on: "on", off: "off", "< Back": "< Wstecz" }
|
||||
},
|
||||
/*,
|
||||
"he_IL": { // This won't work until we get a font - see https://github.com/espruino/BangleApps/issues/399
|
||||
codePage : "ISO8859-8",
|
||||
|
|
|
@ -12,4 +12,9 @@
|
|||
buzz on new message (fix #999)
|
||||
0.09: Message now disappears after 60s if no action taken and clock loads (fix 922)
|
||||
Fix phone icon (#1014)
|
||||
0.10: Respect the 'new' attribute if it was set from iOS integrations
|
||||
0.10: Respect the 'new' attribute if it was set from iOS integrations
|
||||
0.11: Open app when touching the widget (Bangle.js 2 only)
|
||||
0.12: Extra app-specific notification icons
|
||||
New animated notifcationicon (instead of large blinking 'MESSAGES')
|
||||
Added screenshots
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
# Messages app
|
||||
|
||||
**THIS APP IS CURRENTLY BETA**
|
||||
|
||||
This app handles the display of messages and message notifications. It stores
|
||||
a list of currently received messages and allows them to be listed, viewed,
|
||||
and responded to.
|
||||
|
@ -17,7 +15,17 @@ and `Messages`:
|
|||
* `Repeat` - How often should buzzes repeat - the default of 4 means the Bangle will buzz every 4 seconds
|
||||
* `Unread Timer` - when a new message is received we go into the Messages app.
|
||||
If there is no user input for this amount of time then the app will exit and return
|
||||
to the clock where `MESSAGES` will be shown in the Widget bar.
|
||||
to the clock where a ringing bell will be shown in the Widget bar.
|
||||
|
||||
## Images
|
||||
_1. Screenshot of a notification_
|
||||
|
||||

|
||||
|
||||
_2. What the notify icon looks like (it's touchable on Bangle.js2!)_
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## Requests
|
||||
|
@ -27,3 +35,11 @@ Please file any issues on https://github.com/espruino/BangleApps/issues/new?titl
|
|||
## Creator
|
||||
|
||||
Gordon Williams
|
||||
|
||||
## Contributors
|
||||
|
||||
[Jeroen Peters](https://github.com/jeroenpeters1986)
|
||||
|
||||
## Attributions
|
||||
|
||||
Icons used in this app are from https://icons8.com
|
||||
|
|
|
@ -65,6 +65,12 @@ function saveMessages() {
|
|||
function getBackImage() {
|
||||
return atob("FhYBAAAAEAAAwAAHAAA//wH//wf//g///BwB+DAB4EAHwAAPAAA8AADwAAPAAB4AAHgAB+AH/wA/+AD/wAH8AA==");
|
||||
}
|
||||
function getNotificationImage() {
|
||||
return atob("HBKBAD///8H///iP//8cf//j4//8f5//j/x/8//j/H//H4//4PB//EYj/44HH/Hw+P4//8fH//44///xH///g////A==");
|
||||
}
|
||||
function getFBIcon() {
|
||||
return atob("GBiBAAAAAAAAAAAYAAD/AAP/wAf/4A/48A/g8B/g+B/j+B/n+D/n/D8A/B8A+B+B+B/n+A/n8A/n8Afn4APnwADnAAAAAAAAAAAAAA==");
|
||||
}
|
||||
function getPosImage() {
|
||||
return atob("GRSBAAAAAYAAAcAAAeAAAfAAAfAAAfAAAfAAAfAAAfBgAfA4AfAeAfAPgfAD4fAA+fAAP/AAD/AAA/AAAPAAADAAAA==");
|
||||
}
|
||||
|
@ -74,18 +80,28 @@ function getNegImage() {
|
|||
function getMessageImage(msg) {
|
||||
if (msg.img) return atob(msg.img);
|
||||
var s = (msg.src||"").toLowerCase();
|
||||
if (s=="calendar") return atob("GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgAGB//+B//+B//+B9m2B//+B//+Btm2B//+B//+Btm+B//+B//+A//8AAAAAAAAAAAAA==");
|
||||
if (s=="facebook") return getFBIcon();
|
||||
if (s=="hangouts") return atob("FBaBAAH4AH/gD/8B//g//8P//H5n58Y+fGPnxj5+d+fmfj//4//8H//B//gH/4A/8AA+AAHAABgAAAA=");
|
||||
if (s=="instagram") return atob("GBiBAf////////////////wAP/n/n/P/z/f/b/eB7/c87/d+7/d+7/d+7/d+7/c87/eB7/f/7/P/z/n/n/wAP////////////////w==");
|
||||
if (s=="gmail") return getNotificationImage();
|
||||
if (s=="google home") return atob("GBiCAAAAAAAAAAAAAAAAAAAAAoAAAAAACqAAAAAAKqwAAAAAqroAAAACquqAAAAKq+qgAAAqr/qoAACqv/6qAAKq//+qgA6r///qsAqr///6sAqv///6sAqv///6sAqv///6sA6v///6sA6v///qsA6qqqqqsA6qqqqqsA6qqqqqsAP7///vwAAAAAAAAAAAAAAAAA==");
|
||||
if (s=="mail") return getNotificationImage();
|
||||
if (s=="messenger") return getFBIcon();
|
||||
if (s=="outlook mail") return getNotificationImage();
|
||||
if (s=="phone") return atob("FxeBABgAAPgAAfAAB/AAD+AAH+AAP8AAP4AAfgAA/AAA+AAA+AAA+AAB+AAB+AAB+OAB//AB//gB//gA//AA/8AAf4AAPAA=");
|
||||
if (s=="skype") return atob("GhoBB8AAB//AA//+Af//wH//+D///w/8D+P8Afz/DD8/j4/H4fP5/A/+f4B/n/gP5//B+fj8fj4/H8+DB/PwA/x/A/8P///B///gP//4B//8AD/+AAA+AA==");
|
||||
if (s=="hangouts") return atob("FBaBAAH4AH/gD/8B//g//8P//H5n58Y+fGPnxj5+d+fmfj//4//8H//B//gH/4A/8AA+AAHAABgAAAA=");
|
||||
if (s=="whatsapp") return atob("GBiBAAB+AAP/wAf/4A//8B//+D///H9//n5//nw//vw///x///5///4///8e//+EP3/APn/wPn/+/j///H//+H//8H//4H//wMB+AA==");
|
||||
if (s=="telegram") return atob("GBiBAAAAAAAAAAAAAAAAAwAAHwAA/wAD/wAf3gD/Pgf+fh/4/v/z/P/H/D8P/Acf/AM//AF/+AF/+AH/+ADz+ADh+ADAcAAAMAAAAA==");
|
||||
if (s=="slack") return atob("GBiBAAAAAAAAAABAAAHvAAHvAADvAAAPAB/PMB/veD/veB/mcAAAABzH8B3v+B3v+B3n8AHgAAHuAAHvAAHvAADGAAAAAAAAAAAAAA==");
|
||||
if (s=="sms message") return getNotificationImage();
|
||||
if (s=="twitter") return atob("GhYBAABgAAB+JgA/8cAf/ngH/5+B/8P8f+D///h///4f//+D///g///wD//8B//+AP//gD//wAP/8AB/+AB/+AH//AAf/AAAYAAA");
|
||||
if (s=="telegram") return atob("GBiBAAAAAAAAAAAAAAAAAwAAHwAA/wAD/wAf3gD/Pgf+fh/4/v/z/P/H/D8P/Acf/AM//AF/+AF/+AH/+ADz+ADh+ADAcAAAMAAAAA==");
|
||||
if (s=="whatsapp") return atob("GBiBAAB+AAP/wAf/4A//8B//+D///H9//n5//nw//vw///x///5///4///8e//+EP3/APn/wPn/+/j///H//+H//8H//4H//wMB+AA==");
|
||||
if (s=="wordfeud") return atob("GBgCWqqqqqqlf//////9v//////+v/////++v/////++v8///Lu+v8///L++v8///P/+v8v//P/+v9v//P/+v+fx/P/+v+Pk+P/+v/PN+f/+v/POuv/+v/Ofdv/+v/NvM//+v/I/Y//+v/k/k//+v/i/w//+v/7/6//+v//////+v//////+f//////9Wqqqqqql");
|
||||
if (msg.id=="music") return atob("FhaBAH//+/////////////h/+AH/4Af/gB/+H3/7/f/v9/+/3/7+f/vB/w8H+Dwf4PD/x/////////////3//+A=");
|
||||
if (msg.id=="back") return getBackImage();
|
||||
return atob("HBKBAD///8H///iP//8cf//j4//8f5//j/x/8//j/H//H4//4PB//EYj/44HH/Hw+P4//8fH//44///xH///g////A==");
|
||||
return getNotificationImage();
|
||||
}
|
||||
|
||||
|
||||
function showMapMessage(msg) {
|
||||
var m;
|
||||
var distance, street, target, eta;
|
||||
|
@ -127,7 +143,7 @@ function showMapMessage(msg) {
|
|||
function showMusicMessage(msg) {
|
||||
function fmtTime(s) {
|
||||
var m = Math.floor(s/60);
|
||||
s = (s%60).toString().padStart(2,0);
|
||||
s = (parseInt(s%60)).toString().padStart(2,0);
|
||||
return m+":"+s;
|
||||
}
|
||||
|
||||
|
@ -141,7 +157,7 @@ function showMusicMessage(msg) {
|
|||
{type:"h", fillx:1, bgCol:colBg, c: [
|
||||
{ type:"btn", src:getBackImage, cb:back },
|
||||
{ type:"v", fillx:1, c: [
|
||||
{ type:"txt", font:fontLarge, label:msg.artist, pad:2 },
|
||||
{ type:"txt", font:fontMedium, label:msg.artist, pad:2 },
|
||||
{ type:"txt", font:fontMedium, label:msg.album, pad:2 }
|
||||
]}
|
||||
]},
|
||||
|
@ -223,7 +239,7 @@ function showMessage(msgid) {
|
|||
var body = (lines.length>4) ? lines.slice(0,4).join("\n")+"..." : lines.join("\n");
|
||||
layout = new Layout({ type:"v", c: [
|
||||
{type:"h", fillx:1, bgCol:colBg, c: [
|
||||
{ type:"btn", src:getMessageImage(msg), cb:()=>{
|
||||
{ type:"btn", src:getMessageImage(msg), pad: 3, cb:()=>{
|
||||
cancelReloadTimeout(); // don't auto-reload to clock now
|
||||
showMessageSettings(msg);
|
||||
}},
|
||||
|
|
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 2.9 KiB |
|
@ -1,9 +1,9 @@
|
|||
WIDGETS["messages"]={area:"tl",width:0,draw:function() {
|
||||
Bangle.removeListener('touch', this.touch);
|
||||
if (!this.width) return;
|
||||
var c = (Date.now()-this.t)/1000;
|
||||
g.reset().setBgColor((c&1) ? "#0f0" : "#030").setColor((c&1) ? "#000" : "#fff");
|
||||
g.clearRect(this.x,this.y,this.x+this.width,this.y+23);
|
||||
g.setFont("6x8:1x2").setFontAlign(0,0).drawString("MESSAGES", this.x+this.width/2, this.y+12);
|
||||
g.reset().clearRect(this.x,this.y,this.x+this.width,this.y+23);
|
||||
g.drawImage((c&1) ? atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+DAADDAADDAADDwAPD8A/DOBzDDn/DA//DAHvDAPvjAPvjAPvjAPvh///gf/vAAD+AAB8AAAAA==") : atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+D///D///A//8CP/xDj/HD48DD+B8D/D+D/3vD/vvj/vvj/vvj/vvh/v/gfnvAAD+AAB8AAAAA=="), this.x, this.y);
|
||||
//if (c<60) Bangle.setLCDPower(1); // keep LCD on for 1 minute
|
||||
let settings = require('Storage').readJSON("messages.settings.json", true) || {};
|
||||
if (settings.repeat===undefined) settings.repeat = 4;
|
||||
|
@ -12,6 +12,7 @@ WIDGETS["messages"]={area:"tl",width:0,draw:function() {
|
|||
WIDGETS["messages"].buzz(); // buzz every 4 seconds
|
||||
}
|
||||
setTimeout(()=>WIDGETS["messages"].draw(), 1000);
|
||||
if (process.env.HWVERSION>1) Bangle.on('touch', this.touch);
|
||||
},show:function(quiet) {
|
||||
WIDGETS["messages"].t=Date.now(); // first time
|
||||
WIDGETS["messages"].l=Date.now()-10000; // last buzz
|
||||
|
@ -33,6 +34,10 @@ WIDGETS["messages"]={area:"tl",width:0,draw:function() {
|
|||
if (c=="-") Bangle.buzz(500).then(()=>setTimeout(b,100));
|
||||
}
|
||||
b();
|
||||
},touch:function(b,c) {
|
||||
var w=WIDGETS["messages"];
|
||||
if (!w||!w.width||c.x<w.x||c.x>w.x+w.width||c.y<w.y||c.y>w.y+23) return;
|
||||
load("messages.app.js");
|
||||
}};
|
||||
/* We might have returned here if we were in the Messages app for a
|
||||
message but then the watch was never viewed. In that case we don't
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
0.02: included deployment of pebble.settings.js in apps.json
|
||||
0.03: Changed time+calendar font to LECO1976Regular, changed to slanting boot
|
||||
0.04: Fix widget hiding code (fix #1046)
|
||||
0.05: Fix typo in settings - Purple
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
storage.write(SETTINGS_FILE, settings)
|
||||
}
|
||||
|
||||
var color_options = ['Green','Orange','Cyan','Perple','Red','Blue'];
|
||||
var color_options = ['Green','Orange','Cyan','Purple','Red','Blue'];
|
||||
var bg_code = ['#0f0','#ff0','#0ff','#f0f','#f00','#00f'];
|
||||
|
||||
E.showMenu({
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
0.01: Initial check-in.
|
||||
0.02: Make internal menu time out + small fixes.
|
||||
0.03: Autolight feature.
|
|
@ -13,9 +13,12 @@ you can alter the number of hands on the display. When the watch is unlocked, sl
|
|||
There's also a setting that displays the second hand, but only if the watch is perfectly face-to-the-sky, in case you want
|
||||
the ability to check the _exact_ time, hands free, without the impact on battery life this usually entails.
|
||||
|
||||
Although we genrally obey the system-wide theming, you can long press on the display for a menu of additional options specific to the face.
|
||||
Although we generally obey the system-wide theming, you can long press on the display for a menu of additional options specific to the face.
|
||||
You can also override the system 12/24 hour setting just for this face here, since it's, well, a rather different experience than with numeric displays.
|
||||
|
||||
By default, there is a backlight that comes on when you twist your wrist. This, of course, somewhat increases power draw and could be
|
||||
annoying in an intentionally dark environment, so there is an option to disable it.
|
||||
|
||||
One other thing: there's some integration with system timers and alarms; they will show as small pips at the appropriate places
|
||||
in the day around the display. When they come within an hour, the pips turn to crosses relating to the minute hand, and the minute
|
||||
hand turns itself on. When timers are mere seconds away, the display changes again and the second hand activates itself, so you
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* -*- mode: Javascript; c-basic-offset: 2; indent-tabs-mode: nil; coding: latin-1 -*- */
|
||||
// pooqRoman
|
||||
//
|
||||
// Copyright (c) 2021 Stephen P Spackman
|
||||
|
@ -54,8 +55,9 @@ class Options {
|
|||
this.id = this.constructor.id;
|
||||
this.file = `${this.id}.json`;
|
||||
this.backing = storage.readJSON(this.file, true) || {};
|
||||
this.defaults = this.constructor.defaults;
|
||||
Object.keys(this.defaults).forEach(k => this.bless(k));
|
||||
Object.setPrototypeOf(this.backing, this.constructor.defaults);
|
||||
this.reactivator = _ => this.active();
|
||||
Object.keys(this.constructor.defaults).forEach(k => this.bless(k));
|
||||
}
|
||||
|
||||
writeBack(delay) {
|
||||
|
@ -71,29 +73,41 @@ class Options {
|
|||
|
||||
bless(k) {
|
||||
Object.defineProperty(this, k, {
|
||||
get: () => this.backing[k] == null ? this.defaults[k] : this.backing[k],
|
||||
get: () => this.backing[k],
|
||||
set: v => {
|
||||
this.backing[k] = v;
|
||||
// Ten second writeback delay, since the user will roll values up and down.
|
||||
this.writeBack(10000);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
showMenu(m) {
|
||||
if (m instanceof Function) m = m();
|
||||
if (m) {
|
||||
for (const k in m) if ('init' in m[k]) m[k].value = m[k].init();
|
||||
m[''].selected = -1; // Workaround for self-selection bug.
|
||||
Bangle.on('drag', this.reactivator);
|
||||
this.active();
|
||||
} else {
|
||||
if (this.bored) clearTimeout(this.bored);
|
||||
this.bored = null;
|
||||
Bangle.removeListener('drag', this.reactivator);
|
||||
this.emit('done');
|
||||
}
|
||||
g.clear(true);
|
||||
E.showMenu(m);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.backing = {};
|
||||
this.writeBack(0);
|
||||
active() {
|
||||
if (this.bored) clearTimeout(this.bored);
|
||||
this.bored = setTimeout(_ => this.showMenu(), 15000);
|
||||
}
|
||||
|
||||
interact() {this.showMenu(this.menu);}
|
||||
reset() {
|
||||
this.backing = {__proto__: this.constructor.defaults};
|
||||
this.writeBack(0);
|
||||
}
|
||||
}
|
||||
|
||||
class RomanOptions extends Options {
|
||||
|
@ -101,7 +115,7 @@ class RomanOptions extends Options {
|
|||
super();
|
||||
this.menu = {
|
||||
'': {title: '* face options *'},
|
||||
'< Back': _ => {this.showMenu(); this.emit('done');},
|
||||
'< Back': _ => this.showMenu(),
|
||||
Ticks: {
|
||||
init: _ => this.resolution,
|
||||
min: 0, max: 3,
|
||||
|
@ -124,9 +138,15 @@ class RomanOptions extends Options {
|
|||
onchange: x => this.calendric = x,
|
||||
format: x => ['none', 'day', 'date'][x]
|
||||
},
|
||||
Defaults: _ => {this.reset();}
|
||||
'Auto-Illum.': {
|
||||
init: _ => this.autolight,
|
||||
onchange: x => this.autolight = x
|
||||
},
|
||||
Defaults: _ => {this.reset(); this.interact();}
|
||||
};
|
||||
}
|
||||
|
||||
interact() {this.showMenu(this.menu);}
|
||||
}
|
||||
|
||||
RomanOptions.id = 'pooqroman';
|
||||
|
@ -147,7 +167,8 @@ RomanOptions.defaults = {
|
|||
hubFg: g.theme.fg,
|
||||
alarmFg: '#f00',
|
||||
timerFg: '#0f0',
|
||||
active: g.theme.fg2,
|
||||
activeFg: g.theme.fg2,
|
||||
autolight: true,
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -434,7 +455,7 @@ class Sidebar {
|
|||
}
|
||||
static gpsColour(o) {
|
||||
const fix = Bangle.getGPSFix();
|
||||
return fix && fix.fix ? o.active : o.barFg;
|
||||
return fix && fix.fix ? o.activeFg : o.barFg;
|
||||
}
|
||||
doPower() {
|
||||
const c = Bangle.isCharging();
|
||||
|
@ -455,7 +476,7 @@ class Sidebar {
|
|||
if (Bangle.isCompassOn()) {
|
||||
const c = Bangle.getCompass();
|
||||
const a = c && this.rate <= 1000;
|
||||
this.g.setColor(a ? this.options.active : this.options.barFg).drawImage(
|
||||
this.g.setColor(a ? this.options.activeFg : this.options.barFg).drawImage(
|
||||
compassI,
|
||||
this.x + 4 + imageWidth(compassI) / 2,
|
||||
this.y + 4 + imageHeight(compassI) / 2,
|
||||
|
@ -470,7 +491,7 @@ class Sidebar {
|
|||
class Roman {
|
||||
constructor(g, events) {
|
||||
this.g = g;
|
||||
this.state = {};
|
||||
this.state = null;
|
||||
const options = this.options = new RomanOptions();
|
||||
this.events = events.loadFromSystem(this.options);
|
||||
this.timescales = [1000, [1000, 60000], 60000, 3600000];
|
||||
|
@ -480,7 +501,7 @@ class Roman {
|
|||
this.seconds = Roman.hand(g, 1, 0.9, 60, _ => options.secondFg);
|
||||
}
|
||||
|
||||
reset() {this.state = {}; this.g.clear(true);}
|
||||
reset() {this.state = null;}
|
||||
|
||||
doIcons(which) {this.state.iconsOk = null;}
|
||||
|
||||
|
@ -544,7 +565,7 @@ class Roman {
|
|||
|
||||
render(d, rate) {
|
||||
const g = this.g;
|
||||
const state = this.state;
|
||||
const state = this.state || (g.clear(true), this.state = {});
|
||||
const options = this.options;
|
||||
const events = this.events;
|
||||
events.clean(d, -39600000); // 11h
|
||||
|
@ -647,15 +668,15 @@ class Clock {
|
|||
this.options.on('done', () => this.start());
|
||||
|
||||
this.listeners = {
|
||||
lcdPower: on => on ? this.active() : this.inactive(),
|
||||
charging: () => {face.doIcons('charging'); this.active();},
|
||||
lock: () => {face.doIcons('locked'); this.active();},
|
||||
charging: _ => {face.doIcons('charging'); this.active();},
|
||||
lock: _ => {face.doIcons('locked'); this.active();},
|
||||
faceUp: up => {this.conservative = !up; this.active();},
|
||||
twist: _ => this.options.autolight && Bangle.setLCDPower(true),
|
||||
drag: e => {
|
||||
if (this.t0) {
|
||||
if (e.b) {
|
||||
e.x > this.xN && (this.xN = e.x) || e.x > this.xX && (this.xX = e.x);
|
||||
e.y > this.yN && (this.yN = e.y) || e.y > this.yX && (this.xY = e.y);
|
||||
e.x < this.xN && (this.xN = e.x) || e.x > this.xX && (this.xX = e.x);
|
||||
e.y < this.yN && (this.yN = e.y) || e.y > this.yX && (this.yX = e.y);
|
||||
} else if (this.xX - this.xN < 20) {
|
||||
if (e.y - this.e0.y < -50) {
|
||||
this.options.resolution > 0 && this.options.resolution--;
|
||||
|
@ -697,6 +718,7 @@ class Clock {
|
|||
this.exception && clearTimeout(this.exception);
|
||||
this.interval && clearInterval(this.interval);
|
||||
this.timeout = this.exception = this.interval = this.rate = null;
|
||||
this.face.reset(); // Cancel any ongoing background rendering
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -711,7 +733,6 @@ class Clock {
|
|||
}
|
||||
const delay = rate - now % rate + 1;
|
||||
this.refresh = true;
|
||||
|
||||
if (rate !== prev) {
|
||||
this.inactive();
|
||||
this.redraw(rate);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.00: Initial check-in.
|
|
@ -0,0 +1,39 @@
|
|||
# pooq Round: a maximally readable, naturally 24hr, analogue watch face
|
||||
|
||||
This is a normal watch face for telling the time.
|
||||
It is unusual in that it uses a pie chart for the hour hand. This is much easier and
|
||||
more precise to read at a glance than a conventional hand, and as a bonus can distinguish
|
||||
midnight (all black) from noon (all white).
|
||||
|
||||
The day and date are optionally displayed, typographically smooshed into the corners.
|
||||
Either you'll like that, or you won't.
|
||||
|
||||
## Options
|
||||
|
||||
Because sometimes I don't want to burn what I'm cooking and others I'm lazy and just want to know if it's afternoon yet,
|
||||
you can alter the number of ‘hands’ on the display. When the watch is unlocked, slide up to add dots representing the minute and second,
|
||||
or down to remove the distraction. There's also a setting that displays the second hand, but only if the watch is perfectly face-to-the-sky,
|
||||
in case you want the ability to check the _exact_ time, hands free, without the impact on battery life this usually entails.
|
||||
|
||||
Although we generally obey the system-wide theming, you can long press on the display for a menu of additional options specific to the face.
|
||||
We don't observe the system 12/24 setting, since it the design of the face is equally good in either interpretation.
|
||||
|
||||
By default, there is a backlight that comes on when you twist your wrist. This, of course, somewhat increases power draw and could be
|
||||
annoying in an intentionally dark environment, so there is an option to disable it.
|
||||
|
||||
## Limitations
|
||||
|
||||
Since this is intended as a design exercise, it does not and will probably never support the Bangle's standard widgets.
|
||||
Sorry about that, but control of all the pixels was just too important to me.
|
||||
|
||||
There's also no support for internationalisation at present. This irks me, but since every month and day name is hand-drawn,
|
||||
there's no fix other than hard work. Talk to me about it if there's a language you'd like.
|
||||
|
||||
## Feedback
|
||||
|
||||
[I'd be happy to hear your feedback](https://www.github.com/stephenPspackman) if you have comments or find any bugs, or (most especially)
|
||||
if you find this work interesting.
|
||||
|
||||
## By
|
||||
|
||||
Made by [Stephen P Spackman](https://www.github.com/stephenPspackman).
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkB/4AW+ABCgAJEE4IXMh8ADAMPCoYXUK4gXMAAJHCN4oSG+I+CC4gEBC5gNBO4wuGC44IBGASPEC5ovHIox3JL4hdIR5xdIC54uIC5wWIC5hGKC5pGJC5QKCC6YKDCxIXIBQTCBC6IKDC6QKEC6IKFh52KC4gLHC5wLIC5oLJC5gLKC5YALC/4XfQZYAKh4X/C5B4V/4XYJChGBC7JIT/4wVh/wGCouBC6vwI4hIQagQWDDB5dBC45JNEwIXHGBhFDLwgYNCQQXKDBCgEC5QZFB4oGBA4IA="))
|
|
@ -0,0 +1,600 @@
|
|||
/* -*- mode: Javascript; c-basic-offset: 2; indent-tabs-mode: nil; coding: latin-1 -*- */
|
||||
// pooqRound
|
||||
|
||||
// Copyright (c) 2021 Stephen P Spackman
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// Notes:
|
||||
//
|
||||
// This only works for Bangle 2.
|
||||
|
||||
const isString = x => typeof x === 'string';
|
||||
const imageWidth = i => isString(i) ? i.charCodeAt(0) : i.width;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/* System integration */
|
||||
|
||||
const storage = require('Storage');
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/* Face-specific options */
|
||||
|
||||
class Options {
|
||||
// Protocol: subclasses must have static id and defaults fields.
|
||||
// Only fields named in the defaults will be saved.
|
||||
constructor() {
|
||||
this.id = this.constructor.id;
|
||||
this.file = `${this.id}.json`;
|
||||
this.backing = storage.readJSON(this.file, true) || {};
|
||||
Object.setPrototypeOf(this.backing, this.constructor.defaults);
|
||||
this.reactivator = _ => this.active();
|
||||
Object.keys(this.constructor.defaults).forEach(k => this.bless(k));
|
||||
}
|
||||
|
||||
writeBack(delay) {
|
||||
if (this.timeout) clearTimeout(this.timeout);
|
||||
this.timeout = setTimeout(
|
||||
() => {
|
||||
this.timeout = null;
|
||||
storage.writeJSON(this.file, this.backing);
|
||||
},
|
||||
delay
|
||||
);
|
||||
}
|
||||
|
||||
bless(k) {
|
||||
Object.defineProperty(this, k, {
|
||||
get: () => this.backing[k],
|
||||
set: v => {
|
||||
this.backing[k] = v;
|
||||
// Ten second writeback delay, since the user will roll values up and down.
|
||||
this.writeBack(10000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
showMenu(m) {
|
||||
if (m instanceof Function) m = m();
|
||||
if (m) {
|
||||
for (const k in m) if ('init' in m[k]) m[k].value = m[k].init();
|
||||
m[''].selected = -1; // Workaround for self-selection bug.
|
||||
Bangle.on('drag', this.reactivator);
|
||||
this.active();
|
||||
} else {
|
||||
if (this.bored) clearTimeout(this.bored);
|
||||
this.bored = null;
|
||||
Bangle.removeListener('drag', this.reactivator);
|
||||
this.emit('done');
|
||||
}
|
||||
g.clear(true);
|
||||
E.showMenu(m);
|
||||
}
|
||||
|
||||
active() {
|
||||
if (this.bored) clearTimeout(this.bored);
|
||||
this.bored = setTimeout(_ => this.showMenu(), 15000);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.backing = {__proto__: this.constructor.defaults};
|
||||
this.writeBack(0);
|
||||
}
|
||||
}
|
||||
|
||||
class RoundOptions extends Options {
|
||||
constructor() {
|
||||
super();
|
||||
this.menu = () => ({
|
||||
'': {title: '* face options *'},
|
||||
'< Back': _ => this.showMenu(),
|
||||
Ticks: {
|
||||
init: _ => this.resolution,
|
||||
min: 0, max: 3,
|
||||
onchange: x => this.resolution = x,
|
||||
format: x => ['seconds', 'seconds (up)', 'minutes', 'hours'][x]
|
||||
},
|
||||
Calendar: {
|
||||
init: _ => this.calendric,
|
||||
min: 0, max: 5,
|
||||
onchange: x => this.calendric = x,
|
||||
format: x => ['none', 'day', 'date', 'both', 'month', 'full'][x],
|
||||
},
|
||||
'Auto-Illum.': {
|
||||
init: _ => this.autolight,
|
||||
onchange: x => this.autolight = x
|
||||
},
|
||||
Defaults: _ => {this.reset(); this.interact();}
|
||||
});
|
||||
}
|
||||
|
||||
interact() {this.showMenu(this.menu);}
|
||||
}
|
||||
|
||||
RoundOptions.id = 'pooqround';
|
||||
|
||||
RoundOptions.defaults = {
|
||||
resolution: 1,
|
||||
calendric: 5,
|
||||
dayFg: '#fff',
|
||||
nightFg: '#000',
|
||||
autolight: true,
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/* Assets (generated by resourcer.js, in this directory) */
|
||||
|
||||
const heatshrink = require('heatshrink');
|
||||
const dec = x => E.toString(heatshrink.decompress(atob(x)));
|
||||
const y10F = [
|
||||
dec(
|
||||
'g///EAh////AA4IIBgPwgE+gAOBg/AngXB+EPAYM8gfggEfgF8D4OAj4dB8EDAYI' +
|
||||
'fBBAISBAAMOAYUB4AECnEAkAuBgEQBAPgIYX8IYX/wYDCEwIiMMgUYgECCIZlBAY' +
|
||||
'N4CoRUBIoMP8AZBge8CgMB8+BCAPw+F/gf8jxDB/0D4BGBEQMPAYIeBoAfBnEwge' +
|
||||
'Ah0cB4MDx4PBgHn4EB8E7LQM8h/eJ4MDBgIpB+H+g/wnE/WwMMO4P8LwM/XAJLBT' +
|
||||
'gY7BAAN/wC9CQwV+jwDB/4pBgP/EQKYBBIIxBPQP+SATfCIYIiCO4I9BBwM//hlB' +
|
||||
'PQJlCwYGBTAPgIgM4CYM8hwKBMoODegPA8F+gZlBewP4hz/BE4QrBGgM/LAV//4+' +
|
||||
'BAYJyBPwM/KQMeGQMPFwM8H4UHBIPwGQNwn4yBnhxBGQJxBGQK5BGQKWDOwUACAM' +
|
||||
'D/BDCNYPg///8E5HwR2BIwMDSgK0FSocMAYTLBAAYpBQAPnDwJGBEwK+B/hlB+F8' +
|
||||
'TARABTAJABTAPBMoR+BMoKXBDoX5DwIuBMoUPS4THCGwJbBhAaBvh5B+EHwPAOwP' +
|
||||
'guA1BvCcB4E8nxlBn1/VoIyBwDKBO4SGCgA='
|
||||
), 48, dec('hgAI'), 34
|
||||
];const y1F = [
|
||||
dec(
|
||||
'g//AAPggE/AoX8gF/AoX+gF8CoU+gHwAoUPgAZBEIQFGCIodFFIo1FIIoADnAFEj' +
|
||||
'gFEh0AhA1EiAFCgeAFIf/4A1DFQIED/5MDGB6OEjAECHIIYDhkAuAFCjwFEj6DEn' +
|
||||
'+AAod74AFD/PgvAtC+Hwv/wgZSBvEfLwc8RISOBGAJsBVAXgggEBE4PgIgJLC8E8' +
|
||||
'I4fgXQS/B8IhBGwOA8YFCgfA9+eAoMB4H/j/ACIPA/kPCQJCB/DMDMoMBboYVBKo' +
|
||||
'IDBSYeAAoYlCAATpEg/4Xwc/QIcPFoJcBQIP8GILXCDYLXBbId//BeCL4QwDgIwD' +
|
||||
'AAIXBDAQfCEYSPBAoaPCPQKPCAoZgBAoYvBAoIXBBAIFB/ALDEoJHBAoaPDaQSPB' +
|
||||
'AoKcBJgY9DTQX/EoKmCC4SyCYYJJB+CHBj+Aj8ASYJNBBINwIIOAM4ILDAYN/wAB' +
|
||||
'BB4JBBI45vCRYgADApEHL4pHB8AECFIPhAYLCCAggFBAgaNCYwgFEbAkAwAFEc4S' +
|
||||
'PCj/+LIKPBv6PEAoRnBFIMDFYLXCKoTLDa4YRDBYIdDh4FDMoQ1DK4ZBBMQIDBJY' +
|
||||
'bWBFIMEIIQpBgxxBgZRBh8AAYN8AoQVBjgbBAoTZBvwRCvEBF4IdB+E/OIp9CJgZ' +
|
||||
'BCQQUAA='
|
||||
), 48, dec('hgAI'), 48
|
||||
];const y10sF = [
|
||||
dec(
|
||||
'j/+gP//0PgE8mEAmHwgfBBQINB8AWDgcAoEGAYMMj///H///wBwNgAQPAAQMgg8B' +
|
||||
'wE+hkA9kwg8Y+F4mP/4Fg/AVD4EBgcCg0MnEMmfgmH94PD4f+hkHIIgbBg44B/ng' +
|
||||
'h/H/H8n4IBg4QBhwUC//Bgf+FYMwAIPAjHDwPjg//gEPLgUAOYMAn/+DAM8j1gmH' +
|
||||
'h8fDBAMIHIRwDQAJtBg/8mH+gHPwEDCII/DAAM+n8B/v+h0+jkwuEw8fhV4UD8Yr' +
|
||||
'DjxDB/0Ch88CoLEB+fPwK0BKIOACoQA='
|
||||
), 48, dec('hAAI'), 22
|
||||
];const y1sF = [
|
||||
dec(
|
||||
'j///0A/4ABgfAgEPgwNBg0MAYMMjwDBvAWB//gh4DBEAUDgEgAYQeBgcDEwQSCCY' +
|
||||
'oDCiACBwFgGoOBwEAnODBwPhw/Ag+Bw/gv0Bwf/+EBwAkBgPgCYOA4EQgIeB8ASB' +
|
||||
'g/AgcGnuAg0N8fAnkfIwPwnEB/40BgE8IYX8AYN/7hDB/kcg4xBv4TBC4kcLgUcv' +
|
||||
'4ZBIgJIBHoNgHoJ8BgOGKQMHhijBnkYHoQlEv4DBRYWAv+eOgPwmEDg4mBXIXwni' +
|
||||
'SBDwRICSwIABWIM/HoM//57BEoMGv7dC/DrCLoU4eYfAv4kB8f/wPB98HLgP4TQM' +
|
||||
'B+EGh0PvE8QwN/+EP8E/LAK6CBIMAwPg+EDDwNgh8GJQP8h8Hz/gN4P+gBMBJIMA'
|
||||
), 48, dec('hEHhAAGA'), 31
|
||||
];const d10F = [
|
||||
dec(
|
||||
'AAXgjEAjkHgEDwPAgFwvEAh0f///44CB/ICB/4aDAQMcAQMDwAhBuAhBj0B4EH4E' +
|
||||
'wgP4h0Av4JBj3gnEHzkHgPjwF4/Fwh/+CQP/HwMD4E4gJLCvAuBj0ADgOGg+B8fA' +
|
||||
'uF5FoMeDQPH/l4vP8g/+vg4BzkAg/gA='
|
||||
), 49, dec('hcMhYA=='), 27
|
||||
];const d1F = [
|
||||
dec(
|
||||
'AB1/+AECj///4FCAgP/8EAgf/4F//EAg4CBgf8gEPwAUBn0AhwaCAYMeAoUPgEcA' +
|
||||
'oUHAowRFDoopFGopBFJopZGBgIKCABlAIIcA4AFDgIFEgZBCAoMHAohVBAoY6CHg' +
|
||||
'U/Aol/AogADGoQFUABEMAQM/AQN8bIRZBRgJ5BLILhBgP3LIcD84rDg/HWYcPw4F' +
|
||||
'Dj4PBAoU+Aol8Aon4PocB+CJDgfgAoXgh/ATYX4v+AU4X//w/DbYQFCCwJ3PvDIE' +
|
||||
'NYQCCdoJ6CgfAiCGCI4NwgEeFwISCLoMeJwJdCnkfHYd4v4FD+f5AoUB9/BAoUD/' +
|
||||
'4jCh8HG4IpCh5DBAIMeE4Q/BvjMCfoP8Z4Uf//wCgInB/5lCABs+AoicBAAUDAok' +
|
||||
'P9wFDv+OCAjUCHQP4AoY5BAoUHEIIFCv5JBAoLQBLQYqEApQpDArIAJv5IBnBTCV' +
|
||||
'4McJAQFBcYLvBB4IkBd4N4cYQBBeoLdBCYIFDngFECoIFDOwIdCc4QpCFwIZCjwu' +
|
||||
'BEoU8FwIxCvAIBEIPB+AUBJIP/8AmBLYWAd4RnBdx4XCcYf/Dgn//AuEP4LjBXoJ' +
|
||||
'AC//vQYT0BBIKDC+CZBOIM/wAFDVYIFCgIrBAoUDPoIdCO4QnBaQYnBGoQVBIIZI' +
|
||||
'CJoTNCLIY4CAYIaDAAKRCAASRDAAIaEYAQtDYAI5DRgZFCAAYuCQoQuBAgIFBvEH' +
|
||||
'AgIFB+CgBAAMB86lE76EBFwX/GocPNoYmBIwk/HQl8LpIAQRId/SoYDB4ZJCUoPn' +
|
||||
'VoUHwP3Y4YYBY4k+Y4h5BdILhBd4YFFCIodFFIo1FIIpNFLIplGAArMFn6oBHYMA' +
|
||||
'DYQFBgP5E4IFBgfgUgIFCwBZBEAL1BPYZbDA4Z7DLYRtCBYYlDBoIxCEYMBHoIvC' +
|
||||
'HAI7Dh5PBI4X/LIX//7+Dn52Eh4QCA=='
|
||||
), 48, dec('ikPigAGA'), 48
|
||||
];const dowF = [
|
||||
dec(
|
||||
'gf8AYNwgEP/4FBvEAj//wEAnkAn0H4EAjwNBgPgAoQZBAoMOgHwAongCIQFDDoIF' +
|
||||
'FDoPggYFBF4IFBGoI7B+AFCE4NwCIIlCuAdBIYU4gPwn5VBjEA//+M4d//AFDh4W' +
|
||||
'BB4IgBAAX/B4n/PoQACJQIcEAokHAqAXFEYhLF/6tCApIADn4ED/zFBAAX8gaGBA' +
|
||||
'AZZFQIR2GdQQYRBYgXFEYoWRKQQWCLoRrEHgoAIg7LEj7LEn4bEvk+AodwhwFD+C' +
|
||||
'5E8DFEAqIdFFIo1FIIpNFLIoEEAtShCVwQEDVwIFDKAJBvAAv/Bgn/RIjzGjwFEW' +
|
||||
'YicBAqAXFEYh6CRIgFKTYzjEAwt/AxxvDHAkf//AAgMDPIgVBGAnwAoYRBIYk/S4' +
|
||||
'kDMIgeBFIQEBBYRTBCAZ3FAggAMg4zEj7LEn7LEv++AodzxwFD+ePAofjw4FVDoo' +
|
||||
'pFv+eIImcJomYLImAAoZeEAtTyBAAQFEVYIFDSQIvhAojaCFwgABh4YEngFEuAqJ' +
|
||||
'gPAAocDApYuEgP/fgl/+B9HAAv+Aon8HQMOIAkeAokcAohaDAoM4Aol4AohmDAoJ' +
|
||||
'BDAoJsDAo7vhABbJDAo9/AojEFMYbKMArCBDFI41FWIYABggFEgbuCDYMPLIQbBj' +
|
||||
'//wBdCn0H4DZCvEBb4YZBdYZBBAofgCIQFDDoIFFDoPggYFBF4IFBGoI7B+AFCE4' +
|
||||
'NwCIIlCuAdBIYU4gPwn5VBjC7B/y0Dv/4YwcPCwMAjJlCAAM584FDufDCAUA8eBA' +
|
||||
'p/zC4n5EYj1BAoc//4RDU4IFDA=='
|
||||
), 48, dec('kElkMljsljw='), 48
|
||||
];const mF = [
|
||||
dec(
|
||||
'/AEDvEH4AFCgPAnwMDh0B+AGD8EPwAFCg8AvgMDuED8AMEj4MDDwI0DhwOB/4ACC' +
|
||||
'4M/AoX8HgIMDCoI0EAAI0EgA0DnACBGgXHL4Q0Bjn+IYXAgfOCwRpBnPHEQmcuAG' +
|
||||
'DBg3csAGDj4mCAAX/QwhkBWSEDDIp3BAoZ3BBgkeDIp9FOYQMJDIomGh5NFv/wVo' +
|
||||
'YABYIgZBYIYABgKWBHAcPHAKsCgF4VoJDD4AVCIYbtBfAnwgYDBg+Ag6bBEQM8EQ' +
|
||||
'KoCDwMDwP9EQI0Bnk9540DZ4Y/CZ4Y0BbggwBDIY0BgP8JIbcB7yBE/pjDEAOQbZ' +
|
||||
'8fRwT7DAAL7E/4zEjh9EKwLCEnB9BBhIZFgPzEwkP/jcFe4iYBdYLcEAwr5CBgYj' +
|
||||
'Hh65BAxU/AwjNCIhEH/BkEGYqTCRwYMFACE4AonHZ4kcIQkB5yOEnPHIYmcuAMK7' +
|
||||
'lgNJJQBJojkBKSB3BDIk/DIkBBgseDIpmEOYwMGDIsAOYkAgxBGGYjzBIwoMDXYI' +
|
||||
'tCaAQFCCwP8jiECCwMBBhAZGEwwzHIAxNGTY5UKTYIMEjkORwomEnEHBhQZFgPzT' +
|
||||
'gkP/hBEv+ACYivFe3adBAAfwAwoNFGYJkGh/+Axc/AwkfAoggFg/4ZgwzDj4GDiD' +
|
||||
'7CAAPxRQswNIp1FBgnH4TPE/0gC4fO8wMDnPHsAMDzl2BhXcsxpFBgZQB+xqE/4z' +
|
||||
'DAAMCLRJ3BwaWFBgvjDAkfuAGEu4MFfoYZBW4v/eIn/8CzEvEHBocB4E+BgcOgIn' +
|
||||
'DgHgh+ANAcAvgMDuED8AMEj4MDDwI0DhyECAAQXBn4FCf4MBBgYVBGggABGghrBD' +
|
||||
'gQqCGgJ0BL4QJBTYJDCBIMBJYRpCJoIAEUIoMGPIgmDVAYMFKgQAODJh3BBgkeDI' +
|
||||
'p9FnAMLDIomGh5NFv/we372/exgZDe0BpCDIbBBDIl/EwonBAogMEHIIZDD4KUBH' +
|
||||
'wYFDCAPBOwQWCjgMHDI4mGGYwcC+JNFiDAFOIswEAmDDAn8kAME8QYEjwMDAAN2Y' +
|
||||
'QtgTonmYQoMDEwP2YQoZEgECJoozEv5NEj/+LQaYB8YMDn0fM4mAu4MDnEHuAMD8' +
|
||||
'KVEIAPgEwn+WAuAK4LABj7PDwEAvhJBCwUB8EP8EffQMOgH4C4ITB+EHAYN4RwMA' +
|
||||
'ng/BE4PwDYITCnw2BF4YKBF4LwDgInBKYLoFFQIAJgZCBAAZdCTYjOE/p6DgE954' +
|
||||
'fEziUDgE544ME7gtEj/OExUP7hAEnJTKAAxuBFoa4BOokfBgkB4AzEniZBewhaEB' +
|
||||
'goZGj61BRxMHWQIADjwJCIgLICJQQABDIL9BAAKoBg4iCgYTBKoZABhwnDJoJCDg' +
|
||||
'4OCAAQXBewIABJoI5DHQSLBAAP8B4I6CcQgANgbVEOg0fEAkB8KOEnBNBVBIMMjh' +
|
||||
'yEWo0MhhSPgJoBwCZDNwp2BJor2LJpjAFAAImEJwI2BAAfwj4GEXYgMBAwKlFv4G' +
|
||||
'GFQpYFXQx0BAwx6DLQIGCIIgeCIAkHBgoAPn4FEh/8HQpPEn0fVCPhO4kfZ4hvGg' +
|
||||
'YSEgRGFngFEgf4AwkfSws/EwgtBBhQZFEw0cOwIHEuF4AocHWIL2LBgsHGoaBBn7' +
|
||||
'SD+DZEnzIFI4MPAoS1CAwbVRTYqoGWosB/p7EnvPD4mcbgk544ME7jcF5wmKh/cI' +
|
||||
'Ak5LUvhGYk4VAIfDwBaEBgsB4AZEjkOGYnA4AA=='
|
||||
), 49, dec('k0jk0kksmj0lk8lAwIA='), 52
|
||||
];
|
||||
const lockI = dec('hURwMAj0P485w1h3/4g15wFgjPmgOAs+Yg0B//AA');
|
||||
const lockSI = dec('hMNwMAjkfjHMt/8g1zgOc4FnmEf/AA==');
|
||||
const batteryI = dec('hERwMAjH/ABw');
|
||||
const chargeI = dec('g8NwMAgkYsHDh0fw8MmFhwUA');
|
||||
const HRMI = dec('iERwMAjk4l10t/29/3AIfn+ek6VTlPX9d3/U3/Ef/EP+EH8ED4EBwAA=');
|
||||
const compassI = dec('hMJwMAhEEg8Dwfh2Pc43BwA=');
|
||||
const y100I = dec('h8RwMAvk5/n6nOwm9w9lnzH+mO4sc4405xk7jE2mEssEd4EbgE+gE4A=');
|
||||
const y100sI = dec('hcKwMAsOWvHZ+c2s1s4uYmcD4EwA');
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/* Status */
|
||||
|
||||
const status = (p, i) => function (g, x, y, rl) { // Nested arrows are currently broken!
|
||||
if (!p()) return x;
|
||||
if (rl) x -= imageWidth(i);
|
||||
g.setColor(g.theme.fg).drawImage(i, x, y);
|
||||
return rl ? x - 1 : x + imageWidth(i) + 1;
|
||||
};
|
||||
|
||||
const doLocked = status(_ => Bangle.isLocked(), lockI);
|
||||
const doPower = (g, x, y, rl) => {
|
||||
const c = Bangle.isCharging();
|
||||
const b = E.getBattery();
|
||||
if (!c && b > 50) return x;
|
||||
if (rl) x -= imageWidth(batteryI);
|
||||
g.setColor(g.theme.fg).drawImage(batteryI, x, y);
|
||||
g.setColor(b <= 10 ? '#f00' : b <= 30 ? '#ff0' : '#0f0');
|
||||
let h = 13 * (100 - b) / 100;
|
||||
g.fillRect(x + 1, y + 2 + h, x + 6, y + 15);
|
||||
if (c) g.setColor(g.theme.bg).drawImage(chargeI, x, y + 2);
|
||||
return rl ? x - 1 : x + imageWidth(batteryI) + 1;
|
||||
};
|
||||
|
||||
const doHRM = status(_ => Bangle.isHRMOn(), HRMI); // Might show Bangle.getHRM().bpm if confident?
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/* Watch face */
|
||||
|
||||
class Round {
|
||||
constructor(g) {
|
||||
this.g = g;
|
||||
this.b = Graphics.createArrayBuffer(g.getWidth(), g.getHeight(), 1, {msb: true});
|
||||
this.bI = {
|
||||
width: this.b.getWidth(), height: this.b.getHeight(), bpp: this.b.getBPP(),
|
||||
buffer: this.b.buffer, transparent: 0
|
||||
};
|
||||
this.c = Graphics.createArrayBuffer(g.getWidth(), g.getHeight(), 1, {msb: true});
|
||||
this.cI = {
|
||||
width: this.c.getWidth(), height: this.c.getHeight(), bpp: this.c.getBPP(),
|
||||
buffer: this.c.buffer, transparent: 0
|
||||
};
|
||||
this.options = new RoundOptions();
|
||||
this.timescales = [1000, 0, 60000, 900000];
|
||||
this.state = {};
|
||||
// Precomputed polygons for the border areas.
|
||||
this.tl = [0, 0, 58, 0, 0, 58];
|
||||
this.tr = [176, 0, 176, 58, 119, 0];
|
||||
this.bl = [0, 176, 0, 119, 58, 176];
|
||||
this.br = [176, 176, 119, 176, 176, 119];
|
||||
this.xc = g.getWidth() / 2;
|
||||
this.yc = g.getHeight() / 2;
|
||||
this.minR = 5;
|
||||
this.secR = 3;
|
||||
this.r = this.xc - this.minR;
|
||||
}
|
||||
|
||||
reset() {this.state = {}; this.g.clear(true);}
|
||||
|
||||
doIcons(which) {
|
||||
this.state[which] = null;
|
||||
this.render(new Date()); // Not quite right, I think.
|
||||
}
|
||||
|
||||
pie(f, a0, a1, invert) {
|
||||
if (!invert) return this.pie(f, a1, a0 + 1, true);
|
||||
let t0 = Math.tan(a0 * 2 * Math.PI), t1 = Math.tan(a1 * 2 * Math.PI);
|
||||
let i0 = Math.floor(a0 * 4 + 0.5), i1 = Math.floor(a1 * 4 + 0.5);
|
||||
let x = f.getWidth() / 2, y = f.getHeight() / 2;
|
||||
let poly = [
|
||||
x + (i1 & 2 ? -x : x) * (i1 & 1 ? 1 : t1),
|
||||
y + (i1 & 2 ? y : -y) / (i1 & 1 ? t1 : 1),
|
||||
x,
|
||||
y,
|
||||
x + (i0 & 2 ? -x : x) * (i0 & 1 ? 1 : t0),
|
||||
y + (i0 & 2 ? y : -y) / (i0 & 1 ? t0 : 1),
|
||||
];
|
||||
if (i1 - i0 > 4) i1 = i0 + 4;
|
||||
for (i0++; i0 <= i1; i0++) poly.push(
|
||||
3 * i0 & 2 ? f.getWidth() : 0, i0 & 2 ? f.getHeight() : 0
|
||||
);
|
||||
f.setColor(0).fillPoly(poly);
|
||||
}
|
||||
|
||||
hand(t, d, c0, r0, c1, r1) {
|
||||
t *= Math.PI / 30;
|
||||
const r = this.r;
|
||||
const z = 2 * r0 + 1;
|
||||
const x = this.xc + r * Math.sin(t), y = this.yc - r * Math.cos(t);
|
||||
const x0 = x - r0, y0 = y - r0;
|
||||
d = d ? d[0] : Graphics.createArrayBuffer(z, z, 16, {msb: true});
|
||||
for (let i = 0; i < z; i++) for (let j = 0; j < z; j++) {
|
||||
d.setPixel(i, j, g.getPixel(x0 + i, y0 + j));
|
||||
}
|
||||
g.setColor(c0).fillCircle(x, y, r0);
|
||||
if (c1 !== undefined) g.setColor(c1).fillCircle(x, y, r1);
|
||||
return [d, x0, y0];
|
||||
}
|
||||
|
||||
render(d) {
|
||||
const g = this.g;
|
||||
const b = this.b, bI = this.bI;
|
||||
const c = this.c, cI = this.cI;
|
||||
const state = this.state;
|
||||
const options = this.options;
|
||||
const cal = options.calendric;
|
||||
const res = options.resolution;
|
||||
const dow = (cal == 1 || cal > 2) && d.getDay();
|
||||
const ts = res < 2 && d.getSeconds();
|
||||
const tm = res < 3 && d.getMinutes() + ts / 60;
|
||||
const th = d.getHours() + d.getMinutes() / 60;
|
||||
const dd = cal > 1 && d.getDate();
|
||||
const dm = cal > 3 && d.getMonth();
|
||||
const dy = cal > 4 && d.getFullYear();
|
||||
const xc = this.xc, yc = this.yc, r = this.r;
|
||||
const dlr = xc * 3/4, dlw = 8, dlhw = 4;
|
||||
|
||||
// Restore saveunders for fast-moving, overdrawing indicators.
|
||||
if (state.sd) g.drawImage.apply(g, state.sd);
|
||||
if (state.md) g.drawImage.apply(g, state.md);
|
||||
|
||||
if (dow !== state.dow) {
|
||||
g.setColor(g.theme.bg).fillPoly(this.tl);
|
||||
if (dow === +dow) {
|
||||
g.setColor(g.theme.fg).setFontCustom.apply(g, dowF).drawString(dow, 5, 5);
|
||||
}
|
||||
state.dow = dow;
|
||||
}
|
||||
|
||||
const locked = Bangle.isLocked();
|
||||
const charging = Bangle.isCharging();
|
||||
const battery = E.getBattery();
|
||||
const HRMOn = Bangle.isHRMOn();
|
||||
if (dy !== state.dy ||
|
||||
locked !== state.locked ||
|
||||
charging !== state.charging ||
|
||||
Math.abs(battery - state.battery) > 2 ||
|
||||
HRMOn !== state.HRMOn
|
||||
) {
|
||||
g.setColor(g.theme.bg).fillPoly(this.tr);
|
||||
const u = dy % 10;
|
||||
if (charging || battery < 50 || HRMOn || locked && dy !== +dy) {
|
||||
let x = 172, y = 5;
|
||||
x = doLocked(g, x, y, true);
|
||||
x = doPower(g, x, y, true);
|
||||
x = doHRM(g, x, y, true);
|
||||
if (dy === +dy) {
|
||||
g.setColor(g.theme.fg).drawImage(y100sI, 145, 23);
|
||||
g.setFontCustom.apply(g, y10sF).drawString((dy - u) / 10 % 10, 157, 23);
|
||||
g.setFontCustom.apply(g, y1sF).drawString(u, 165, 23);
|
||||
}
|
||||
} else if (dy === +dy) {
|
||||
g.setColor(g.theme.fg);
|
||||
if (locked) g.drawImage(lockSI, 136, 5);
|
||||
else g.drawImage(y100I, 130, 5);
|
||||
g.setFontCustom.apply(g, y10F).drawString((dy - u) / 10 % 10, 146, 5);
|
||||
g.setFontCustom.apply(g, y1F).drawString(u, 160, 5);
|
||||
}
|
||||
state.dy = dy;
|
||||
state.locked = Bangle.isLocked();
|
||||
state.charging = Bangle.isCharging();
|
||||
state.battery = E.getBattery() - E.getBattery() % 2;
|
||||
state.HRMOn = Bangle.isHRMOn();
|
||||
}
|
||||
if (dm !== state.dm) {
|
||||
g.setColor(g.theme.bg).fillPoly(this.bl);
|
||||
if (dm === +dm) {
|
||||
g.setColor(g.theme.fg).setFontCustom.apply(g, mF);
|
||||
g.drawString(String.fromCharCode(49 + dm), 5, 124);
|
||||
}
|
||||
state.dm = dm;
|
||||
}
|
||||
if (dd !== state.dd) {
|
||||
g.setColor(g.theme.bg).fillPoly(this.br);
|
||||
if (dd === +dd) {
|
||||
let u = dd % 10;
|
||||
g.setColor(g.theme.fg).setFontAlign(1, 1);
|
||||
g.setFontCustom.apply(g, d10F).drawString((dd - u) / 10, 152, 172);
|
||||
g.setFontAlign(-1, 1);
|
||||
g.setFontCustom.apply(g, d1F).drawString(u, 152, 172);
|
||||
g.setFontAlign(-1, -1);
|
||||
}
|
||||
}
|
||||
if (th !== state.th) {
|
||||
state.th = th;
|
||||
b.clear(true).fillCircle(88, 88, r - 1);
|
||||
g.setColor(options.nightFg).drawImage(bI);
|
||||
if (th < 12) this.pie(b, th / 12, 1, true);
|
||||
else this.pie(b, 1, th / 12, true);
|
||||
g.setColor(options.dayFg).drawImage(bI);
|
||||
}
|
||||
state.md = tm === +tm ?
|
||||
this.hand(tm, state.md, g.theme.bg, this.minR, g.theme.fg, this.minR - 1) :
|
||||
null;
|
||||
state.sd = ts === +ts ?
|
||||
this.hand(ts, state.sd, g.theme.fg2, this.secR) :
|
||||
null;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/* Master clock */
|
||||
|
||||
class Clock {
|
||||
constructor(face) {
|
||||
this.face = face;
|
||||
this.timescales = face.timescales;
|
||||
this.options = face.options;
|
||||
this.rates = {};
|
||||
this.faceUp = null;
|
||||
|
||||
this.options.on('done', () => this.start());
|
||||
|
||||
this.listeners = {
|
||||
lcdPower: on => on ? this.active() : this.inactive(),
|
||||
charging: () => {face.doIcons('charging'); this.active();},
|
||||
lock: () => {face.doIcons('locked'); this.active();},
|
||||
faceUp: up => {
|
||||
this.conservative = !up;
|
||||
this.faceUp = up;
|
||||
this.active();
|
||||
},
|
||||
twist: _ => this.options.autolight && Bangle.setLCDPower(true),
|
||||
drag: e => {
|
||||
if (this.t0) {
|
||||
if (e.b) {
|
||||
e.x < this.xN && (this.xN = e.x) || e.x > this.xX && (this.xX = e.x);
|
||||
e.y < this.yN && (this.yN = e.y) || e.y > this.yX && (this.yX = e.y);
|
||||
} else if (this.xX - this.xN < 20) {
|
||||
if (e.y - this.e0.y < -50) {
|
||||
this.options.resolution > 0 && this.options.resolution--;
|
||||
this.rates.clock = this.timescales[this.options.resolution];
|
||||
this.active();
|
||||
} else if (e.y - this.e0.y > 50) {
|
||||
this.options.resolution < this.timescales.length - 1 &&
|
||||
this.options.resolution++;
|
||||
this.rates.clock = this.timescales[this.options.resolution];
|
||||
this.active();
|
||||
} else if (this.yX - this.yN < 20 && Date.now() - this.t0 > 500) {
|
||||
this.stop();
|
||||
this.options.interact();
|
||||
}
|
||||
this.t0 = null;
|
||||
}
|
||||
} else if (e.b) {
|
||||
this.t0 = Date.now(); this.e0 = e;
|
||||
this.xN = this.xX = e.x; this.yN = this.yX = e.y;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
redraw(rate) {
|
||||
const now = this.updated = new Date();
|
||||
if (this.refresh) this.face.reset();
|
||||
this.refresh = false;
|
||||
rate = this.face.render(now, rate);
|
||||
if (rate !== this.rates.face) {
|
||||
this.rates.face = rate;
|
||||
this.active();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
inactive() {
|
||||
this.timeout && clearTimeout(this.timeout);
|
||||
this.exception && clearTimeout(this.exception);
|
||||
this.interval && clearInterval(this.interval);
|
||||
this.timeout = this.exception = this.interval = this.rate = null;
|
||||
this.face.reset(); // Cancel any ongoing background rendering
|
||||
return this;
|
||||
}
|
||||
|
||||
active() {
|
||||
const prev = this.rate;
|
||||
const now = Date.now();
|
||||
let rate = Infinity;
|
||||
for (const k in this.rates) {
|
||||
let r = this.rates[k];
|
||||
r === +r || (r = r[+this.conservative])
|
||||
r < rate && (rate = r);
|
||||
}
|
||||
const delay = rate - now % rate + 1;
|
||||
this.refresh = true;
|
||||
|
||||
if (rate !== prev) {
|
||||
this.inactive();
|
||||
this.redraw(rate);
|
||||
if (rate < 31622400000) { // A year!
|
||||
this.timeout = setTimeout(
|
||||
() => {
|
||||
this.inactive();
|
||||
this.interval = setInterval(() => this.redraw(rate), rate);
|
||||
if (delay > 1000) this.redraw(rate);
|
||||
this.rate = rate;
|
||||
}, delay
|
||||
);
|
||||
}
|
||||
} else if (rate > 1000) {
|
||||
if (!this.exception) this.exception = setTimeout(() => {
|
||||
this.redraw(rate);
|
||||
this.exception = null;
|
||||
}, this.updated + 1000 - Date.now());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.inactive();
|
||||
for (const l in this.listeners) {
|
||||
Bangle.removeListener(l, this.listeners[l]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
start() {
|
||||
this.inactive(); // Reset to known state.
|
||||
this.conservative = false;
|
||||
this.rates.clock = this.timescales[this.options.resolution];
|
||||
this.active();
|
||||
for (const l in this.listeners) {
|
||||
Bangle.on(l, this.listeners[l]);
|
||||
}
|
||||
Bangle.setUI('clock');
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/* Main */
|
||||
|
||||
const clock = new Clock(new Round(g)).start();
|
After Width: | Height: | Size: 2.9 KiB |
|
@ -1,2 +1,4 @@
|
|||
0.01: Initial creation of the pattern launch app
|
||||
0.02: Turn on lcd when launching an app if the lock screen was disabled in the settings
|
||||
0.03: Make tap to confirm new pattern more reliable. Also allow for easier creation of single circle patterns.
|
||||
0.10: Improve the management of existing patterns: Draw the linked pattern on the left hand side of the app name within a scroller, similar to the default launcher. Slighlty clean up the code to make it less horrible.
|
|
@ -8,25 +8,32 @@ Create patterns and link them to apps in the Pattern Launcher app.
|
|||
|
||||
Then launch the linked apps directly from the clock screen by simply drawing the desired pattern.
|
||||
|
||||
## Screenshots and detailed steps
|
||||
## Add Pattern Screenshots
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## Manage Pattern Screenshots
|
||||
|
||||

|
||||

|
||||
|
||||
## Detailed Steps
|
||||
|
||||
From the main menu you can:
|
||||
- Add a new pattern and link it to an app (first entry)
|
||||
- To create a new pattern first select "Add Pattern"
|
||||
- Now draw any pattern you like, this will later launch the linked app from the clock screen
|
||||
- You can also draw a single-circle pattern (meaning a single tap on one circle) instead of drawing a 'complex' pattern
|
||||
- If you don't like the pattern, simply re-draw it. The previous pattern will be discarded.
|
||||
- If you are happy with the pattern tap on screen or press the button to continue
|
||||
- Now select the app you want to launch with the pattern.
|
||||
- Note, you can bind multiple patterns to the same app.
|
||||
- Remove linked patterns (second entry)
|
||||
- To remove a pattern first select "Remove Pattern"
|
||||
- You will now see a list of apps that have patterns linked to them
|
||||
- Simply select the app that you want to unlink. This will remove the saved pattern, but not the app itself!
|
||||
- Note, that you can not actually preview the patterns. This makes removing patterns that are linked to the same app annoying. sorry!
|
||||
- Manage created patterns (second entry)
|
||||
- To manage your patterns first select "Manage Patterns"
|
||||
- You will now see a scrollabe list of patterns + linked apps
|
||||
- If you want to deletion a pattern (and unlink the app) simply tap on it, and confirm the deletion
|
||||
- Disable the lock screen on the clock screen from the settings (third entry)
|
||||
- To launch the app from the pattern on the clock screen the watch must be unlocked.
|
||||
- If this annoys you, you can disable the lock on the clock screen from the setting here
|
||||
|
|
|
@ -1,26 +1,6 @@
|
|||
var storage = require("Storage");
|
||||
|
||||
var DEBUG = false;
|
||||
var log = (message) => {
|
||||
if (DEBUG) {
|
||||
console.log(JSON.stringify(message));
|
||||
}
|
||||
};
|
||||
|
||||
var CIRCLE_RADIUS = 25;
|
||||
var CIRCLE_RADIUS_2 = CIRCLE_RADIUS * CIRCLE_RADIUS;
|
||||
|
||||
var CIRCLES = [
|
||||
{ x: 25, y: 25, i: 0 },
|
||||
{ x: 87, y: 25, i: 1 },
|
||||
{ x: 150, y: 25, i: 2 },
|
||||
{ x: 25, y: 87, i: 3 },
|
||||
{ x: 87, y: 87, i: 4 },
|
||||
{ x: 150, y: 87, i: 5 },
|
||||
{ x: 25, y: 150, i: 6 },
|
||||
{ x: 87, y: 150, i: 7 },
|
||||
{ x: 150, y: 150, i: 8 },
|
||||
];
|
||||
var storage = require("Storage");
|
||||
|
||||
var showMainMenu = () => {
|
||||
log("loading patterns");
|
||||
|
@ -36,7 +16,7 @@ var showMainMenu = () => {
|
|||
},
|
||||
"Add Pattern": () => {
|
||||
log("creating pattern");
|
||||
createPattern().then((pattern) => {
|
||||
recognizeAndDrawPattern().then((pattern) => {
|
||||
log("got pattern");
|
||||
log(pattern);
|
||||
log(pattern.length);
|
||||
|
@ -73,17 +53,32 @@ var showMainMenu = () => {
|
|||
});
|
||||
});
|
||||
},
|
||||
"Remove Pattern": () => {
|
||||
"Manage Patterns": () => {
|
||||
log("selecting pattern through app");
|
||||
getStoredPatternViaApp(storedPatterns).then((pattern) => {
|
||||
E.showMessage("Deleting...");
|
||||
delete storedPatterns[pattern];
|
||||
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
||||
showMainMenu();
|
||||
showScrollerContainingAppsWithPatterns().then((selected) => {
|
||||
var pattern = selected.pattern;
|
||||
var appName = selected.appName;
|
||||
if (pattern === "back") {
|
||||
showMainMenu();
|
||||
} else {
|
||||
E.showPrompt(appName + "\n\npattern:\n" + pattern, {
|
||||
title: "Delete?",
|
||||
buttons: { Yes: true, No: false },
|
||||
}).then((confirm) => {
|
||||
if (confirm) {
|
||||
E.showMessage("Deleting...");
|
||||
delete storedPatterns[pattern];
|
||||
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
||||
showMainMenu();
|
||||
} else {
|
||||
showMainMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
Settings: () => {
|
||||
var settings = storedPatterns["settings"] || {};
|
||||
var settings = storedPatterns.settings || {};
|
||||
|
||||
var settingsmenu = {
|
||||
"": {
|
||||
|
@ -98,7 +93,7 @@ var showMainMenu = () => {
|
|||
if (settings.lockDisabled) {
|
||||
settingsmenu["Enable lock"] = () => {
|
||||
settings.lockDisabled = false;
|
||||
storedPatterns["settings"] = settings;
|
||||
storedPatterns.settings = settings;
|
||||
Bangle.setOptions({ lockTimeout: 1000 * 30 });
|
||||
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
||||
showMainMenu();
|
||||
|
@ -106,7 +101,7 @@ var showMainMenu = () => {
|
|||
} else {
|
||||
settingsmenu["Disable lock"] = () => {
|
||||
settings.lockDisabled = true;
|
||||
storedPatterns["settings"] = settings;
|
||||
storedPatterns.settings = settings;
|
||||
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
||||
Bangle.setOptions({ lockTimeout: 1000 * 60 * 60 * 24 * 365 });
|
||||
showMainMenu();
|
||||
|
@ -119,12 +114,8 @@ var showMainMenu = () => {
|
|||
E.showMenu(mainmenu);
|
||||
};
|
||||
|
||||
var drawCircle = (circle) => {
|
||||
g.fillCircle(circle.x, circle.y, CIRCLE_RADIUS);
|
||||
};
|
||||
|
||||
var positions = [];
|
||||
var createPattern = () => {
|
||||
var recognizeAndDrawPattern = () => {
|
||||
return new Promise((resolve) => {
|
||||
E.showMenu();
|
||||
g.clear();
|
||||
|
@ -147,13 +138,29 @@ var createPattern = () => {
|
|||
setWatch(() => finishHandler(), BTN);
|
||||
setTimeout(() => Bangle.on("tap", finishHandler), 250);
|
||||
|
||||
positions = [];
|
||||
var dragHandler = (position) => {
|
||||
log(position);
|
||||
positions.push(position);
|
||||
|
||||
debounce().then(() => {
|
||||
if (isFinished) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This might actually be a 'tap' event.
|
||||
// Use this check in addition to the actual tap handler to make it more reliable
|
||||
if (pattern.length > 0 && positions.length === 2) {
|
||||
if (
|
||||
positions[0].x === positions[1].x &&
|
||||
positions[0].y === positions[1].y
|
||||
) {
|
||||
finishHandler();
|
||||
positions = [];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
E.showMessage("Calculating...");
|
||||
var t0 = Date.now();
|
||||
|
||||
|
@ -269,18 +276,7 @@ var createPattern = () => {
|
|||
|
||||
log("redrawing");
|
||||
g.clear();
|
||||
g.setColor(0, 0, 0);
|
||||
CIRCLES.forEach((circle) => drawCircle(circle));
|
||||
|
||||
g.setColor(1, 1, 1);
|
||||
g.setFontAlign(0, 0);
|
||||
g.setFont("6x8", 4);
|
||||
pattern.forEach((circleIndex, patternIndex) => {
|
||||
var circle = CIRCLES[circleIndex];
|
||||
g.drawString(patternIndex + 1, circle.x, circle.y);
|
||||
});
|
||||
var t2 = Date.now();
|
||||
log(t2 - t0);
|
||||
drawCirclesWithPattern(pattern);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -341,56 +337,256 @@ var getSelectedApp = () => {
|
|||
});
|
||||
};
|
||||
|
||||
var getStoredPatternViaApp = (storedPatterns) => {
|
||||
E.showMessage("Loading patterns...");
|
||||
log("getStoredPatternViaApp");
|
||||
//////
|
||||
// manage pattern related variables and functions
|
||||
// - draws all saved patterns and their linked app names
|
||||
// - uses the scroller to allow the user to browse through them
|
||||
//////
|
||||
|
||||
var scrollerFont = g.getFonts().includes("12x20") ? "12x20" : "6x8:2";
|
||||
|
||||
var drawBackButton = (r) => {
|
||||
g.clearRect(r.x, r.y, r.x + r.w - 1, r.y + r.h - 1);
|
||||
g.setFont(scrollerFont)
|
||||
.setFontAlign(-1, 0)
|
||||
.drawString("< Back", 64, r.y + 32);
|
||||
};
|
||||
|
||||
var drawAppWithPattern = (i, r, storedPatterns) => {
|
||||
log("draw app with pattern");
|
||||
log({ i: i, r: r, storedPatterns: storedPatterns });
|
||||
var storedPattern = storedPatterns[i];
|
||||
var pattern = storedPattern.pattern;
|
||||
var app = storedPattern.app;
|
||||
|
||||
g.clearRect(r.x, r.y, r.x + r.w - 1, r.y + r.h - 1);
|
||||
|
||||
g.drawLine(r.x, r.y, 176, r.y);
|
||||
|
||||
drawCirclesWithPattern(pattern, {
|
||||
enableCaching: true,
|
||||
scale: 0.33,
|
||||
offset: { x: 1, y: 3 + r.y },
|
||||
});
|
||||
|
||||
g.setColor(0, 0, 0);
|
||||
if (!storedPattern.wrappedAppName) {
|
||||
storedPattern.wrappedAppName = g
|
||||
.wrapString(app.name, g.getWidth() - 64)
|
||||
.join("\n");
|
||||
}
|
||||
log(g.getWidth());
|
||||
log(storedPattern.wrappedAppName);
|
||||
g.setFont(scrollerFont)
|
||||
.setFontAlign(-1, 0)
|
||||
.drawString(storedPattern.wrappedAppName, 64, r.y + 32);
|
||||
};
|
||||
|
||||
var showScrollerContainingAppsWithPatterns = () => {
|
||||
var storedPatternsArray = getStoredPatternsArray();
|
||||
log("drawing scroller for stored patterns");
|
||||
log(storedPatternsArray);
|
||||
log(storedPatternsArray.length);
|
||||
|
||||
g.clear();
|
||||
|
||||
var c = Math.max(storedPatternsArray.length + 1, 3);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
var selectPatternMenu = {
|
||||
"": {
|
||||
title: "Select App",
|
||||
},
|
||||
"< Cancel": () => {
|
||||
log("cancel");
|
||||
showMainMenu();
|
||||
},
|
||||
};
|
||||
|
||||
log(storedPatterns);
|
||||
var patterns = Object.keys(storedPatterns);
|
||||
log(patterns);
|
||||
|
||||
patterns.forEach((pattern) => {
|
||||
if (pattern) {
|
||||
if (storedPatterns[pattern]) {
|
||||
var app = storedPatterns[pattern].app;
|
||||
if (!!app && !!app.name) {
|
||||
var appName = app.name;
|
||||
var i = 0;
|
||||
while (appName in selectPatternMenu[app.name]) {
|
||||
appName = app.name + i;
|
||||
i++;
|
||||
}
|
||||
selectPatternMenu[appName] = () => {
|
||||
log("pattern via app selected");
|
||||
log(pattern);
|
||||
log(app);
|
||||
resolve(pattern);
|
||||
};
|
||||
}
|
||||
E.showScroller({
|
||||
h: 64,
|
||||
c: c,
|
||||
draw: (i, r) => {
|
||||
log("draw");
|
||||
log({ i: i, r: r });
|
||||
if (i <= 0) {
|
||||
drawBackButton(r);
|
||||
} else if (i <= storedPatternsArray.length) {
|
||||
drawAppWithPattern(i - 1, r, storedPatternsArray);
|
||||
}
|
||||
}
|
||||
},
|
||||
select: (i) => {
|
||||
log("selected: " + i);
|
||||
var pattern = "back";
|
||||
var appName = "";
|
||||
if (i > 0) {
|
||||
var storedPattern = storedPatternsArray[i - 1];
|
||||
pattern = storedPattern.pattern.join("");
|
||||
appName = storedPattern.app.name;
|
||||
}
|
||||
clearCircleDrawingCache();
|
||||
resolve({ pattern: pattern, appName: appName });
|
||||
},
|
||||
});
|
||||
|
||||
E.showMenu(selectPatternMenu);
|
||||
});
|
||||
};
|
||||
|
||||
showMainMenu();
|
||||
//////
|
||||
// storage related functions:
|
||||
// - stored patterns
|
||||
// - stored settings
|
||||
//////
|
||||
|
||||
var getStoredPatternsMap = () => {
|
||||
log("loading stored patterns map");
|
||||
var storedPatternsMap = storage.readJSON("ptlaunch.patterns.json", 1) || {};
|
||||
delete storedPatternsMap.settings;
|
||||
log(storedPatternsMap);
|
||||
return storedPatternsMap;
|
||||
};
|
||||
|
||||
var getStoredPatternsArray = () => {
|
||||
var storedPatternsMap = getStoredPatternsMap();
|
||||
log("converting stored patterns map to array");
|
||||
var patterns = Object.keys(storedPatternsMap);
|
||||
var storedPatternsArray = [];
|
||||
for (var i = 0; i < patterns.length; i++) {
|
||||
var pattern = "" + patterns[i];
|
||||
storedPatternsArray.push({
|
||||
pattern: pattern
|
||||
.split("")
|
||||
.map((circleIndex) => parseInt(circleIndex, 10)),
|
||||
app: storedPatternsMap[pattern].app,
|
||||
});
|
||||
}
|
||||
log(storedPatternsArray);
|
||||
return storedPatternsArray;
|
||||
};
|
||||
|
||||
//////
|
||||
// lib functions
|
||||
// circle related variables and functions:
|
||||
// - the circle array itself
|
||||
// - the radius and the squared radius of the circles
|
||||
// - circle draw function
|
||||
//////
|
||||
|
||||
var CIRCLE_RADIUS = 25;
|
||||
var CIRCLE_RADIUS_2 = CIRCLE_RADIUS * CIRCLE_RADIUS;
|
||||
|
||||
var CIRCLES = [
|
||||
{ x: 25, y: 25, i: 0 },
|
||||
{ x: 87, y: 25, i: 1 },
|
||||
{ x: 150, y: 25, i: 2 },
|
||||
{ x: 25, y: 87, i: 3 },
|
||||
{ x: 87, y: 87, i: 4 },
|
||||
{ x: 150, y: 87, i: 5 },
|
||||
{ x: 25, y: 150, i: 6 },
|
||||
{ x: 87, y: 150, i: 7 },
|
||||
{ x: 150, y: 150, i: 8 },
|
||||
];
|
||||
|
||||
var drawCircle = (circle, drawBuffer, scale) => {
|
||||
if (!drawBuffer) {
|
||||
drawBuffer = g;
|
||||
}
|
||||
if (!scale) {
|
||||
scale = 1;
|
||||
}
|
||||
|
||||
var x = circle.x * scale;
|
||||
var y = circle.y * scale;
|
||||
var r = CIRCLE_RADIUS * scale;
|
||||
|
||||
log("drawing circle");
|
||||
log({ x: x, y: y, r: r });
|
||||
|
||||
drawBuffer.fillCircle(x, y, r);
|
||||
};
|
||||
|
||||
var cachedCirclesDrawings = {};
|
||||
|
||||
var clearCircleDrawingCache = () => {
|
||||
cachedCirclesDrawings = {};
|
||||
};
|
||||
|
||||
var drawCirclesWithPattern = (pattern, options) => {
|
||||
if (!pattern || pattern.length === 0) {
|
||||
pattern = [];
|
||||
}
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
var enableCaching = options.enableCaching;
|
||||
var scale = options.scale;
|
||||
var offset = options.offset;
|
||||
if (!enableCaching) {
|
||||
enableCaching = false;
|
||||
}
|
||||
if (!scale) {
|
||||
scale = 1;
|
||||
}
|
||||
if (!offset) {
|
||||
offset = { x: 0, y: 0 };
|
||||
}
|
||||
|
||||
log("drawing circles with pattern, scale and offset");
|
||||
log(pattern);
|
||||
log(scale);
|
||||
log(offset);
|
||||
|
||||
// cache drawn patterns. especially useful for the manage pattern menu
|
||||
var image = cachedCirclesDrawings[pattern.join("")];
|
||||
if (!image) {
|
||||
log("circle image not cached");
|
||||
var drawBuffer = Graphics.createArrayBuffer(
|
||||
g.getWidth() * scale,
|
||||
g.getHeight() * scale,
|
||||
1,
|
||||
{ msb: true }
|
||||
);
|
||||
|
||||
drawBuffer.setColor(1);
|
||||
CIRCLES.forEach((circle) => drawCircle(circle, drawBuffer, scale));
|
||||
|
||||
drawBuffer.setColor(0);
|
||||
drawBuffer.setFontAlign(0, 0);
|
||||
drawBuffer.setFont("6x8", 4 * scale);
|
||||
pattern.forEach((circleIndex, patternIndex) => {
|
||||
var circle = CIRCLES[circleIndex];
|
||||
drawBuffer.drawString(
|
||||
patternIndex + 1,
|
||||
circle.x * scale,
|
||||
circle.y * scale
|
||||
);
|
||||
});
|
||||
|
||||
image = {
|
||||
width: drawBuffer.getWidth(),
|
||||
height: drawBuffer.getHeight(),
|
||||
bpp: 1,
|
||||
buffer: drawBuffer.buffer,
|
||||
};
|
||||
|
||||
if (enableCaching) {
|
||||
cachedCirclesDrawings[pattern.join("")] = image;
|
||||
}
|
||||
} else {
|
||||
log("using cached circle image");
|
||||
}
|
||||
|
||||
g.drawImage(image, offset.x, offset.y);
|
||||
};
|
||||
|
||||
var cloneCirclesArray = () => {
|
||||
var circlesClone = Array(CIRCLES.length);
|
||||
|
||||
for (var i = 0; i < CIRCLES.length; i++) {
|
||||
circlesClone[i] = CIRCLES[i];
|
||||
}
|
||||
|
||||
return circlesClone;
|
||||
};
|
||||
|
||||
//////
|
||||
// misc lib functions
|
||||
//////
|
||||
|
||||
var log = (message) => {
|
||||
if (DEBUG) {
|
||||
console.log(JSON.stringify(message));
|
||||
}
|
||||
};
|
||||
|
||||
var debounceTimeoutId;
|
||||
var debounce = (delay) => {
|
||||
if (debounceTimeoutId) {
|
||||
|
@ -405,12 +601,8 @@ var debounce = (delay) => {
|
|||
});
|
||||
};
|
||||
|
||||
var cloneCirclesArray = () => {
|
||||
var circlesClone = Array(CIRCLES.length);
|
||||
//////
|
||||
// run main function
|
||||
//////
|
||||
|
||||
for (var i = 0; i < CIRCLES.length; i++) {
|
||||
circlesClone[i] = CIRCLES[i];
|
||||
}
|
||||
|
||||
return circlesClone;
|
||||
};
|
||||
showMainMenu();
|
||||
|
|
|
@ -5,21 +5,6 @@ var log = (message) => {
|
|||
}
|
||||
};
|
||||
|
||||
var CIRCLE_RADIUS = 25;
|
||||
var CIRCLE_RADIUS_2 = CIRCLE_RADIUS * CIRCLE_RADIUS;
|
||||
|
||||
var CIRCLES = [
|
||||
{ x: 25, y: 25, i: 0 },
|
||||
{ x: 87, y: 25, i: 1 },
|
||||
{ x: 150, y: 25, i: 2 },
|
||||
{ x: 25, y: 87, i: 3 },
|
||||
{ x: 87, y: 87, i: 4 },
|
||||
{ x: 150, y: 87, i: 5 },
|
||||
{ x: 25, y: 150, i: 6 },
|
||||
{ x: 87, y: 150, i: 7 },
|
||||
{ x: 150, y: 150, i: 8 },
|
||||
];
|
||||
|
||||
var storedPatterns;
|
||||
var positions = [];
|
||||
var dragHandler = (position) => {
|
||||
|
@ -28,7 +13,20 @@ var dragHandler = (position) => {
|
|||
debounce().then(() => {
|
||||
log(positions.length);
|
||||
|
||||
var circlesClone = cloneCirclesArray();
|
||||
var CIRCLE_RADIUS = 25;
|
||||
var CIRCLE_RADIUS_2 = CIRCLE_RADIUS * CIRCLE_RADIUS;
|
||||
|
||||
var circles = [
|
||||
{ x: 25, y: 25, i: 0 },
|
||||
{ x: 87, y: 25, i: 1 },
|
||||
{ x: 150, y: 25, i: 2 },
|
||||
{ x: 25, y: 87, i: 3 },
|
||||
{ x: 87, y: 87, i: 4 },
|
||||
{ x: 150, y: 87, i: 5 },
|
||||
{ x: 25, y: 150, i: 6 },
|
||||
{ x: 87, y: 150, i: 7 },
|
||||
{ x: 150, y: 150, i: 8 },
|
||||
];
|
||||
var pattern = [];
|
||||
|
||||
var step = Math.floor(positions.length / 100) + 1;
|
||||
|
@ -38,92 +36,92 @@ var dragHandler = (position) => {
|
|||
for (var i = 0; i < positions.length; i += step) {
|
||||
p = positions[i];
|
||||
|
||||
circle = circlesClone[0];
|
||||
circle = circles[0];
|
||||
if (circle) {
|
||||
a = p.x - circle.x;
|
||||
b = p.y - circle.y;
|
||||
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||
pattern.push(circle.i);
|
||||
circlesClone.splice(0, 1);
|
||||
circles.splice(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
circle = circlesClone[1];
|
||||
circle = circles[1];
|
||||
if (circle) {
|
||||
a = p.x - circle.x;
|
||||
b = p.y - circle.y;
|
||||
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||
pattern.push(circle.i);
|
||||
circlesClone.splice(1, 1);
|
||||
circles.splice(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
circle = circlesClone[2];
|
||||
circle = circles[2];
|
||||
if (circle) {
|
||||
a = p.x - circle.x;
|
||||
b = p.y - circle.y;
|
||||
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||
pattern.push(circle.i);
|
||||
circlesClone.splice(2, 1);
|
||||
circles.splice(2, 1);
|
||||
}
|
||||
}
|
||||
|
||||
circle = circlesClone[3];
|
||||
circle = circles[3];
|
||||
if (circle) {
|
||||
a = p.x - circle.x;
|
||||
b = p.y - circle.y;
|
||||
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||
pattern.push(circle.i);
|
||||
circlesClone.splice(3, 1);
|
||||
circles.splice(3, 1);
|
||||
}
|
||||
}
|
||||
|
||||
circle = circlesClone[4];
|
||||
circle = circles[4];
|
||||
if (circle) {
|
||||
a = p.x - circle.x;
|
||||
b = p.y - circle.y;
|
||||
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||
pattern.push(circle.i);
|
||||
circlesClone.splice(4, 1);
|
||||
circles.splice(4, 1);
|
||||
}
|
||||
}
|
||||
|
||||
circle = circlesClone[5];
|
||||
circle = circles[5];
|
||||
if (circle) {
|
||||
a = p.x - circle.x;
|
||||
b = p.y - circle.y;
|
||||
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||
pattern.push(circle.i);
|
||||
circlesClone.splice(5, 1);
|
||||
circles.splice(5, 1);
|
||||
}
|
||||
}
|
||||
|
||||
circle = circlesClone[6];
|
||||
circle = circles[6];
|
||||
if (circle) {
|
||||
a = p.x - circle.x;
|
||||
b = p.y - circle.y;
|
||||
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||
pattern.push(circle.i);
|
||||
circlesClone.splice(6, 1);
|
||||
circles.splice(6, 1);
|
||||
}
|
||||
}
|
||||
circle = circlesClone[7];
|
||||
circle = circles[7];
|
||||
if (circle) {
|
||||
a = p.x - circle.x;
|
||||
b = p.y - circle.y;
|
||||
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||
pattern.push(circle.i);
|
||||
circlesClone.splice(7, 1);
|
||||
circles.splice(7, 1);
|
||||
}
|
||||
}
|
||||
|
||||
circle = circlesClone[8];
|
||||
circle = circles[8];
|
||||
if (circle) {
|
||||
a = p.x - circle.x;
|
||||
b = p.y - circle.y;
|
||||
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
|
||||
pattern.push(circle.i);
|
||||
circlesClone.splice(8, 1);
|
||||
circles.splice(8, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -163,16 +161,6 @@ var debounce = (delay) => {
|
|||
});
|
||||
};
|
||||
|
||||
var cloneCirclesArray = () => {
|
||||
var circlesClone = Array(CIRCLES.length);
|
||||
|
||||
for (var i = 0; i < CIRCLES.length; i++) {
|
||||
circlesClone[i] = CIRCLES[i];
|
||||
}
|
||||
|
||||
return circlesClone;
|
||||
};
|
||||
|
||||
(function () {
|
||||
var sui = Bangle.setUI;
|
||||
Bangle.setUI = function (mode, cb) {
|
||||
|
|
Before Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.6 KiB |
|
@ -2,3 +2,4 @@
|
|||
0.02: Add widget
|
||||
0.03: Bangle.js 2 support
|
||||
0.04: Move Quiet Mode LCD options from global settings to this app
|
||||
0.05: Avoid immediately redrawing widgets on load
|
|
@ -1,32 +1,36 @@
|
|||
WIDGETS["qmsched"] = {
|
||||
area: "tl", width: 24, draw: function() {
|
||||
const mode = (require("Storage").readJSON("setting.json", 1) || {}).quiet|0;
|
||||
if (mode===0) { // Off
|
||||
if (this.width!==0) {
|
||||
this.width = 0;
|
||||
Bangle.drawWidgets();
|
||||
(function() {
|
||||
WIDGETS["qmsched"] = {
|
||||
area: "tl",
|
||||
width: ((require("Storage").readJSON("setting.json", 1) || {}).quiet|0) ? 24 : 0,
|
||||
draw: function() {
|
||||
const mode = (require("Storage").readJSON("setting.json", 1) || {}).quiet|0;
|
||||
if (mode===0) { // Off
|
||||
if (this.width!==0) {
|
||||
this.width = 0;
|
||||
Bangle.drawWidgets();
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// not Off: make sure width is correct
|
||||
if (this.width!==24) {
|
||||
this.width = 24;
|
||||
Bangle.drawWidgets();
|
||||
return; // drawWidgets will call draw again
|
||||
}
|
||||
let x = this.x, y = this.y;
|
||||
g.clearRect(x, y, x+23, y+23);
|
||||
// quiet mode: draw red one-way-street sign (dim red on Bangle.js 1)
|
||||
x = this.x+11;y = this.y+11; // center of widget
|
||||
g.setColor(process.env.HWVERSION===2 ? 1 : 0.8, 0, 0).fillCircle(x, y, 8);
|
||||
g.setColor(g.theme.bg).fillRect(x-6, y-2, x+6, y+2);
|
||||
if (mode>1) {return;} // no alarms
|
||||
// alarms still on: draw alarm icon in bottom-right corner
|
||||
x = this.x+18;y = this.y+17; // center of alarm
|
||||
g.setColor(1, 1, 0)
|
||||
.fillCircle(x, y, 3) // alarm body
|
||||
.fillRect(x-5, y+2, x+5, y+3) // bottom ridge
|
||||
.fillRect(x-1, y-5, x+1, y+5).drawLine(x, y-6, x, y+6) // top+bottom
|
||||
.drawLine(x+5, y-3, x+3, y-5).drawLine(x-5, y-3, x-3, y-5); // wriggles
|
||||
},
|
||||
};
|
||||
// not Off: make sure width is correct
|
||||
if (this.width!==24) {
|
||||
this.width = 24;
|
||||
Bangle.drawWidgets();
|
||||
return; // drawWidgets will call draw again
|
||||
}
|
||||
let x = this.x, y = this.y;
|
||||
g.clearRect(x, y, x+23, y+23);
|
||||
// quiet mode: draw red one-way-street sign (dim red on Bangle.js 1)
|
||||
x = this.x+11;y = this.y+11; // center of widget
|
||||
g.setColor(process.env.HWVERSION===2 ? 1 : 0.8, 0, 0).fillCircle(x, y, 8);
|
||||
g.setColor(g.theme.bg).fillRect(x-6, y-2, x+6, y+2);
|
||||
if (mode>1) {return;} // no alarms
|
||||
// alarms still on: draw alarm icon in bottom-right corner
|
||||
x = this.x+18;y = this.y+17; // center of alarm
|
||||
g.setColor(1, 1, 0)
|
||||
.fillCircle(x, y, 3) // alarm body
|
||||
.fillRect(x-5, y+2, x+5, y+3) // bottom ridge
|
||||
.fillRect(x-1, y-5, x+1, y+5).drawLine(x, y-6, x, y+6) // top+bottom
|
||||
.drawLine(x+5, y-3, x+3, y-5).drawLine(x-5, y-3, x-3, y-5); // wriggles
|
||||
},
|
||||
};
|
||||
})();
|
|
@ -1,3 +1,4 @@
|
|||
0.01: New App!
|
||||
0.02: Add posibillity to generate Wifi code.
|
||||
0.03: Forces integer scaling and adds more configuration (error correction, description, display)
|
||||
0.04: Allow scanning of QR codes from camera or file
|
||||
|
|
|
@ -3,34 +3,71 @@
|
|||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<b>Datasource: </b></br>
|
||||
<input type="radio" id="useTEXT" name="mode" checked/>
|
||||
<label for="useTEXT">Use text (for example an URL):</label>
|
||||
<input type="text" id="text" class="form-input" value="www.espruino.com">
|
||||
<hr>
|
||||
|
||||
<label for="useTEXT">Text</label></br>
|
||||
<input type="radio" id="useWIFI" name="mode"/>
|
||||
<label for="useWIFI">Use Wifi Credentials:</label>
|
||||
<input type="text" id="ssid" class="form-input" value="">
|
||||
<p>Wifi password: <input type="password" id="password" class="form-input" value=""></p>
|
||||
<div class="form-group">
|
||||
<label for="encryption" class="control-label">Encryption</label>
|
||||
<div class="input-group">
|
||||
<select name="encryption" id="encryption" class="form-control">
|
||||
<option value="WPA">WPA/WPA2</option>
|
||||
<option value="WEP">WEP</option>
|
||||
<option value="nopass">None</option>
|
||||
</select>
|
||||
</div>
|
||||
<label for="useWIFI">Wifi Credentials</label></br>
|
||||
<input type="radio" id="useFILE" name="mode"/>
|
||||
<label for="useFILE">QR image</label></br>
|
||||
<input type="radio" id="useCAM" name="mode"/>
|
||||
<label for="useCAM">QR scan</label></br>
|
||||
<hr>
|
||||
|
||||
<div id="srcText">
|
||||
<p>Text/URL: <input type="text" id="text" class="form-input" value="http://www.espruino.com"></p>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="hidden" name="hidden"/>
|
||||
<label for="hidden">Wifi is hidden</label>
|
||||
|
||||
<div id="srcScanCam">
|
||||
<div>
|
||||
<video id="qrVideo" align="center" width="50%"></video>
|
||||
</div>
|
||||
<div>
|
||||
<select id="camList">
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<button id="flashToggle">Flash: <span id="flashState">off</span></button>
|
||||
</div>
|
||||
<br>
|
||||
Detected QR code:
|
||||
<span id="camQrResult">None</span>
|
||||
<br>
|
||||
<button id="startButton" class="btn btn-primary">Start</button>
|
||||
<button id="stopButton" class="btn btn-primary">Stop</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="srcScanFile">
|
||||
<input type="file" id="fileSelector">
|
||||
<br>
|
||||
Detected QR code:
|
||||
<span id="fileQrResult">None</span>
|
||||
</div>
|
||||
|
||||
<div id="srcWifi">
|
||||
<p>Wifi name: <input type="text" id="ssid" class="form-input" value=""></p>
|
||||
<p>Wifi password: <input type="password" id="password" class="form-input" value=""></p>
|
||||
<div class="form-group">
|
||||
<label for="encryption" class="control-label">Encryption</label>
|
||||
<div class="input-group">
|
||||
<select name="encryption" id="encryption" class="form-control">
|
||||
<option value="WPA">WPA/WPA2</option>
|
||||
<option value="WEP">WEP</option>
|
||||
<option value="nopass">None</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="hidden" name="hidden"/>
|
||||
<label for="hidden">Wifi is hidden</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<p id="errors" style="color:Tomato;"></p>
|
||||
<p>Try your QR Code: <div id="qrcode"></div></p>
|
||||
|
||||
|
||||
<hr>
|
||||
<p>Additional options:</p>
|
||||
<input type="checkbox" id="preventIntegerScaling" name="preventIntegerScaling"/>
|
||||
|
@ -51,7 +88,7 @@
|
|||
<option value="3">Q - Quartile - 25%</option>
|
||||
<option value="2">H - High - 30%</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<p>Click <button id="upload" class="btn btn-primary">Upload</button></p>
|
||||
|
||||
|
||||
|
@ -59,25 +96,75 @@
|
|||
<script src="../../core/lib/qrcode.min.js"></script><!-- https://davidshimjs.github.io/qrcodejs/ -->
|
||||
<script src="../../core/lib/heatshrink.js"></script>
|
||||
<script src="../../core/lib/imageconverter.js"></script>
|
||||
|
||||
<script src="./qr-scanner.umd.min.js"></script><!-- https://github.com/nimiq/qr-scanner -->
|
||||
<script>
|
||||
var targetSize = 200;
|
||||
|
||||
function onInit(device) {
|
||||
if (device && device.info && device.info.g) {
|
||||
border = 4;
|
||||
targetSize = Math.min(device.info.g.width - border, device.info.g.height - border);
|
||||
var targetSize = 200;
|
||||
var deviceWidth = targetSize;
|
||||
var deviceHeight = targetSize;
|
||||
var border = 4;
|
||||
|
||||
var scanner = null;
|
||||
var qrcode = null;
|
||||
|
||||
|
||||
function onInit(device) {
|
||||
console.info("onInit" + device);
|
||||
if (device && device.info && device.info.g) {
|
||||
deviceWidth = device.info.g.width;
|
||||
deviceHeight = device.info.g.height;
|
||||
}
|
||||
refreshQRCode();
|
||||
}
|
||||
qrcode = new QRCode("qrcode", {
|
||||
text: document.getElementById("text").value,
|
||||
colorDark : "#000000",
|
||||
colorLight : "#ffffff",
|
||||
});
|
||||
refreshQRCode();
|
||||
}
|
||||
|
||||
//https://github.com/evgeni/qifi/blob/gh-pages/index.html#L168
|
||||
function escapeString (string) {
|
||||
const updateFlashAvailability = () => {
|
||||
scanner.hasFlash().then(hasFlash => {
|
||||
document.getElementById('flashToggle').style.display = hasFlash ? 'inline-block' : 'none';
|
||||
});
|
||||
};
|
||||
|
||||
function setResult(label, result) {
|
||||
console.info("setResult " + result);
|
||||
label.textContent = result;
|
||||
scanner.stop();
|
||||
refreshQRCode();
|
||||
}
|
||||
|
||||
function initQrScanner() {
|
||||
console.info("initQrScanner");
|
||||
QrScanner.WORKER_PATH = './qr-scanner-worker.min.js';
|
||||
if (scanner == null) {
|
||||
scanner = new QrScanner(document.getElementById('qrVideo'), result => setResult(document.getElementById('camQrResult'), result), error => {
|
||||
document.getElementById('camQrResult').textContent = error;
|
||||
document.getElementById('camQrResult').style.color = 'inherit';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function initQrCam(){
|
||||
scanner.start().then(() => {
|
||||
updateFlashAvailability();
|
||||
QrScanner.listCameras(true).then(cameras => cameras.forEach(camera => {
|
||||
const option = document.createElement('option');
|
||||
option.value = camera.id;
|
||||
option.text = camera.label;
|
||||
document.getElementById('camList').add(option);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
function toggleVis(id){
|
||||
console.info("Got id", id);
|
||||
["srcScanFile", "srcText", "srcWifi", "srcScanCam"].forEach(function (item){
|
||||
document.getElementById(item).style.display = "none";
|
||||
});
|
||||
if (id != undefined && id != null) document.getElementById(id).style.display = "block";
|
||||
refreshQRCode();
|
||||
}
|
||||
|
||||
toggleVis("srcText");
|
||||
|
||||
//https://github.com/evgeni/qifi/blob/gh-pages/index.html#L168
|
||||
function escapeString (string) {
|
||||
var to_escape = ['\\', ';', ',', ':', '"'];
|
||||
var hex_only = /^[0-9a-f]+$/i;
|
||||
var output = "";
|
||||
|
@ -90,83 +177,116 @@
|
|||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
function generateWifiString(ssid, password, hidden,encryption){
|
||||
//https://github.com/evgeni/qifi/blob/gh-pages/index.html#L198
|
||||
var qrstring = 'WIFI:S:'+escapeString(ssid)+';T:'+encryption+';P:'+escapeString(password)+';';
|
||||
if (hidden) {
|
||||
qrstring += 'H:true';
|
||||
}
|
||||
return qrstring;
|
||||
}
|
||||
function refreshQRCode(){
|
||||
document.getElementById("errors").innerText="";
|
||||
qrcode.clear(); // clear the code.
|
||||
var qrText = "";
|
||||
if(document.getElementById("useWIFI").checked){
|
||||
const ssid = document.getElementById("ssid").value;
|
||||
const password = document.getElementById("password").value;
|
||||
const encryption = document.getElementById("encryption").value;
|
||||
const hidden = document.getElementById("hidden").checked;
|
||||
const wifiString = generateWifiString(ssid, password, hidden, encryption);
|
||||
qrText= wifiString;
|
||||
} else {
|
||||
qrText = document.getElementById("text").value;
|
||||
function generateWifiString(ssid, password, hidden,encryption){
|
||||
//https://github.com/evgeni/qifi/blob/gh-pages/index.html#L198
|
||||
var qrstring = 'WIFI:S:'+escapeString(ssid)+';T:'+encryption+';P:'+escapeString(password)+';';
|
||||
if (hidden) {
|
||||
qrstring += 'H:true';
|
||||
}
|
||||
return qrstring;
|
||||
}
|
||||
qrcode._htOption.text = qrText;
|
||||
qrcode._htOption.correctLevel = parseInt(document.getElementById("correction").value);
|
||||
try {
|
||||
qrcode.makeCode(qrText);
|
||||
} catch (error) {
|
||||
document.getElementById("errors").innerText="Error: QR could not be created.";
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
var finalSizeQr=targetSize;
|
||||
var finalSizeCanvas=targetSize;
|
||||
|
||||
var integerScale = Math.max(Math.floor(targetSize / (qrcode._oQRCode.moduleCount + 1)),1);
|
||||
if (integerScale == 1) document.getElementById("errors").innerText = "Warning, QR will probably be too small to properly scan. Try less data or less error correction.";
|
||||
|
||||
if (!document.getElementById("preventIntegerScaling").checked){
|
||||
|
||||
finalSizeQr = integerScale * (qrcode._oQRCode.moduleCount + 1);
|
||||
finalSizeCanvas = finalSizeQr - 1;
|
||||
}
|
||||
|
||||
qrcode._htOption.width = finalSizeQr;
|
||||
qrcode._htOption.height = finalSizeQr;
|
||||
|
||||
document.getElementsByTagName("canvas")[0].width = finalSizeCanvas;
|
||||
document.getElementsByTagName("canvas")[0].height = finalSizeCanvas;
|
||||
try {
|
||||
qrcode.makeCode(qrText);
|
||||
} catch (error) {
|
||||
document.getElementById("errors").innerText="Error: QR could not be created.";
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
var qrcode;
|
||||
function refreshQRCode(){
|
||||
if (qrcode == null){
|
||||
qrcode = new QRCode("qrcode", {
|
||||
text: document.getElementById("text").value,
|
||||
colorDark : "#000000",
|
||||
colorLight : "#ffffff"
|
||||
});
|
||||
}
|
||||
document.getElementById("errors").innerText="";
|
||||
qrcode.clear(); // clear the code.
|
||||
var qrText = "";
|
||||
if(document.getElementById("useWIFI").checked){
|
||||
const ssid = document.getElementById("ssid").value;
|
||||
const password = document.getElementById("password").value;
|
||||
const encryption = document.getElementById("encryption").value;
|
||||
const hidden = document.getElementById("hidden").checked;
|
||||
const wifiString = generateWifiString(ssid, password, hidden, encryption);
|
||||
qrText= wifiString;
|
||||
} else if (document.getElementById("useCAM").checked) {
|
||||
qrText= document.getElementById("camQrResult").innerText;
|
||||
} else if (document.getElementById("useFILE").checked) {
|
||||
qrText= document.getElementById("fileQrResult").innerText;
|
||||
} else {
|
||||
qrText = document.getElementById("text").value;
|
||||
}
|
||||
|
||||
document.getElementById("ssid").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("text").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("password").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("encryption").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("hidden").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("useTEXT").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("useWIFI").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("preventIntegerScaling").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("correction").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("upload").addEventListener("click", function() {
|
||||
var content = document.getElementById("text").value;
|
||||
if(document.getElementById("useWIFI").checked){
|
||||
content = document.getElementById("ssid").value
|
||||
|
||||
|
||||
console.info("Given qrtext was: " + qrText);
|
||||
qrcode._htOption.text = qrText;
|
||||
qrcode._htOption.correctLevel = parseInt(document.getElementById("correction").value);
|
||||
try {
|
||||
qrcode.makeCode(qrText);
|
||||
} catch (error) {
|
||||
document.getElementById("errors").innerText="Error: QR could not be created.";
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
targetSize = Math.min(deviceWidth - border, deviceHeight - border);
|
||||
console.info("Targetsize: " + targetSize);
|
||||
var finalSizeQr=targetSize;
|
||||
var finalSizeCanvas=targetSize;
|
||||
|
||||
var integerScale = Math.max(Math.floor(targetSize / (qrcode._oQRCode.moduleCount + 1)),1);
|
||||
if (integerScale == 1) document.getElementById("errors").innerText = "Warning, QR will probably be too small to properly scan. Try less data or less error correction.";
|
||||
|
||||
console.info("IntegerScale: " + integerScale);
|
||||
|
||||
if (!document.getElementById("preventIntegerScaling").checked){
|
||||
finalSizeQr = integerScale * (qrcode._oQRCode.moduleCount + 1);
|
||||
finalSizeCanvas = finalSizeQr - 1;
|
||||
}
|
||||
|
||||
console.info("FinalSizeQr: " + finalSizeQr);
|
||||
console.info("FinalSizeCanvas: " + finalSizeCanvas);
|
||||
|
||||
qrcode._htOption.width = finalSizeQr;
|
||||
qrcode._htOption.height = finalSizeQr;
|
||||
|
||||
document.getElementsByTagName("canvas")[0].width = finalSizeCanvas;
|
||||
document.getElementsByTagName("canvas")[0].height = finalSizeCanvas;
|
||||
try {
|
||||
qrcode.makeCode(qrText);
|
||||
} catch (error) {
|
||||
document.getElementById("errors").innerText="Error: QR could not be created.";
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
if(!(document.getElementById("description").value === "")){
|
||||
content = document.getElementById("description").value;
|
||||
}
|
||||
var img = imageconverter.canvastoString(document.getElementsByTagName("canvas")[0],{mode:"1bit",output:"string",compression:true});
|
||||
var app = `var img = ${img};
|
||||
|
||||
document.getElementById("useTEXT").addEventListener("change",function(){toggleVis("srcText");});
|
||||
document.getElementById("useCAM").addEventListener("change",function(){
|
||||
initQrScanner();
|
||||
initQrCam();
|
||||
toggleVis("srcScanCam");
|
||||
});
|
||||
document.getElementById("useFILE").addEventListener("change",function(){
|
||||
initQrScanner();
|
||||
toggleVis("srcScanFile");
|
||||
});
|
||||
document.getElementById("useWIFI").addEventListener("change",function(){toggleVis("srcWifi");});
|
||||
document.getElementById("ssid").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("text").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("password").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("encryption").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("hidden").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("useTEXT").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("useCAM").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("useFILE").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("useWIFI").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("preventIntegerScaling").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("correction").addEventListener("change",refreshQRCode);
|
||||
document.getElementById("upload").addEventListener("click", function() {
|
||||
var content = document.getElementById("text").value;
|
||||
if(document.getElementById("useWIFI").checked){
|
||||
content = document.getElementById("ssid").value
|
||||
}
|
||||
if(!(document.getElementById("description").value === "")){
|
||||
content = document.getElementById("description").value;
|
||||
}
|
||||
var img = imageconverter.canvastoString(document.getElementsByTagName("canvas")[0],{mode:"1bit",output:"string",compression:true});
|
||||
var app = `var img = ${img};
|
||||
${document.getElementById("boostBacklight").checked ? 'Bangle.setLCDBrightness(1);' : ''}
|
||||
${document.getElementById("stayOn").checked ? 'Bangle.setLCDTimeout(0);' : ''}
|
||||
${document.getElementById("hideDescription").checked ? '' : `var content = ${JSON.stringify(content)};`}
|
||||
|
@ -178,14 +298,40 @@ g.drawString(content,g.getWidth()/2,g.getHeight()-(g.getHeight()-img[1])/4));
|
|||
`}
|
||||
g.setColor(1,1,1);
|
||||
`;
|
||||
sendCustomizedApp({
|
||||
storage:[
|
||||
{name:"qrcode.app.js", url:"app.js", content:app},
|
||||
]
|
||||
sendCustomizedApp({
|
||||
storage:[{name:"qrcode.app.js", url:"app.js", content:app},]
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
document.getElementById('camList').addEventListener('change', event => {
|
||||
scanner.setCamera(event.target.value).then(updateFlashAvailability);
|
||||
});
|
||||
|
||||
document.getElementById('flashToggle').addEventListener('click', () => {
|
||||
scanner.toggleFlash().then(() => document.getElementById('flashState').textContent = scanner.isFlashOn() ? 'on' : 'off');
|
||||
});
|
||||
|
||||
document.getElementById('startButton').addEventListener('click', () => {
|
||||
scanner.start();
|
||||
});
|
||||
|
||||
document.getElementById('stopButton').addEventListener('click', () => {
|
||||
scanner.stop();
|
||||
});
|
||||
|
||||
document.getElementById('fileSelector').addEventListener('change', event => {
|
||||
const file = document.getElementById('fileSelector').files[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
QrScanner.scanImage(file)
|
||||
.then(result => setResult(document.getElementById('fileQrResult'), result))
|
||||
.catch(e => setResult(document.getElementById('fileQrResult'), e || 'No QR code found.'));
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
'use strict';(function(){function T(a,b){let c=[],d="";b=a.readBits([8,16,16][b]);for(let d=0;d<b;d++){let b=a.readBits(8);c.push(b)}try{d+=decodeURIComponent(c.map(a=>`%${("0"+a.toString(16)).substr(-2)}`).join(""))}catch(e){}return{bytes:c,text:d}}function U(a,b){a=new V(a);let c=9>=b?0:26>=b?1:2;for(b={text:"",bytes:[],chunks:[],version:b};4<=a.available();){var d=a.readBits(4);if(d===t.Terminator)return b;if(d===t.ECI)0===a.readBits(1)?b.chunks.push({type:r.ECI,assignmentNumber:a.readBits(7)}):
|
||||
0===a.readBits(1)?b.chunks.push({type:r.ECI,assignmentNumber:a.readBits(14)}):0===a.readBits(1)?b.chunks.push({type:r.ECI,assignmentNumber:a.readBits(21)}):b.chunks.push({type:r.ECI,assignmentNumber:-1});else if(d===t.Numeric){var e=a;d=[];for(var f="",g=e.readBits([10,12,14][c]);3<=g;){var h=e.readBits(10);if(1E3<=h)throw Error("Invalid numeric value above 999");var k=Math.floor(h/100),n=Math.floor(h/10)%10;h%=10;d.push(48+k,48+n,48+h);f+=k.toString()+n.toString()+h.toString();g-=3}if(2===g){g=e.readBits(7);
|
||||
if(100<=g)throw Error("Invalid numeric value above 99");e=Math.floor(g/10);g%=10;d.push(48+e,48+g);f+=e.toString()+g.toString()}else if(1===g){e=e.readBits(4);if(10<=e)throw Error("Invalid numeric value above 9");d.push(48+e);f+=e.toString()}d={bytes:d,text:f};b.text+=d.text;b.bytes.push(...d.bytes);b.chunks.push({type:r.Numeric,text:d.text})}else if(d===t.Alphanumeric){e=a;d=[];f="";for(g=e.readBits([9,11,13][c]);2<=g;)n=e.readBits(11),k=Math.floor(n/45),n%=45,d.push(B[k].charCodeAt(0),B[n].charCodeAt(0)),
|
||||
f+=B[k]+B[n],g-=2;1===g&&(e=e.readBits(6),d.push(B[e].charCodeAt(0)),f+=B[e]);d={bytes:d,text:f};b.text+=d.text;b.bytes.push(...d.bytes);b.chunks.push({type:r.Alphanumeric,text:d.text})}else if(d===t.Byte)d=T(a,c),b.text+=d.text,b.bytes.push(...d.bytes),b.chunks.push({type:r.Byte,bytes:d.bytes,text:d.text});else if(d===t.Kanji){f=a;d=[];e=f.readBits([8,10,12][c]);for(g=0;g<e;g++)k=f.readBits(13),k=Math.floor(k/192)<<8|k%192,k=7936>k?k+33088:k+49472,d.push(k>>8,k&255);f=(new TextDecoder("shift-jis")).decode(Uint8Array.from(d));
|
||||
d={bytes:d,text:f};b.text+=d.text;b.bytes.push(...d.bytes);b.chunks.push({type:r.Kanji,bytes:d.bytes,text:d.text})}else d===t.StructuredAppend&&b.chunks.push({type:r.StructuredAppend,currentSequence:a.readBits(4),totalSequence:a.readBits(4),parity:a.readBits(8)})}if(0===a.available()||0===a.readBits(a.available()))return b}function J(a,b){return a^b}function W(a,b,c,d){b.degree()<c.degree()&&([b,c]=[c,b]);let e=a.zero;for(var f=a.one;c.degree()>=d/2;){var g=b;let d=e;b=c;e=f;if(b.isZero())return null;
|
||||
c=g;f=a.zero;g=b.getCoefficient(b.degree());for(g=a.inverse(g);c.degree()>=b.degree()&&!c.isZero();){let d=c.degree()-b.degree(),e=a.multiply(c.getCoefficient(c.degree()),g);f=f.addOrSubtract(a.buildMonomial(d,e));c=c.addOrSubtract(b.multiplyByMonomial(d,e))}f=f.multiplyPoly(e).addOrSubtract(d);if(c.degree()>=b.degree())return null}d=f.getCoefficient(0);if(0===d)return null;a=a.inverse(d);return[f.multiply(a),c.multiply(a)]}function X(a,b){let c=new Uint8ClampedArray(a.length);c.set(a);a=new Y(285,
|
||||
256,0);var d=new w(a,c),e=new Uint8ClampedArray(b),f=!1;for(var g=0;g<b;g++){var h=d.evaluateAt(a.exp(g+a.generatorBase));e[e.length-1-g]=h;0!==h&&(f=!0)}if(!f)return c;d=new w(a,e);d=W(a,a.buildMonomial(b,1),d,b);if(null===d)return null;b=d[0];g=b.degree();if(1===g)b=[b.getCoefficient(1)];else{e=Array(g);f=0;for(h=1;h<a.size&&f<g;h++)0===b.evaluateAt(h)&&(e[f]=a.inverse(h),f++);b=f!==g?null:e}if(null==b)return null;d=d[1];e=b.length;f=Array(e);for(g=0;g<e;g++){h=a.inverse(b[g]);let c=1;for(let d=
|
||||
0;d<e;d++)g!==d&&(c=a.multiply(c,J(1,a.multiply(b[d],h))));f[g]=a.multiply(d.evaluateAt(h),a.inverse(c));0!==a.generatorBase&&(f[g]=a.multiply(f[g],h))}d=f;for(e=0;e<b.length;e++){f=c.length-1-a.log(b[e]);if(0>f)return null;c[f]^=d[e]}return c}function E(a,b){a^=b;for(b=0;a;)b++,a&=a-1;return b}function C(a,b){return b<<1|a}function Z(a,b,c){c=aa[c.dataMask];let d=a.height;var e=17+4*b.versionNumber,f=A.createEmpty(e,e);f.setRegion(0,0,9,9,!0);f.setRegion(e-8,0,8,9,!0);f.setRegion(0,e-8,9,8,!0);for(var g of b.alignmentPatternCenters)for(var h of b.alignmentPatternCenters)6===
|
||||
g&&6===h||6===g&&h===e-7||g===e-7&&6===h||f.setRegion(g-2,h-2,5,5,!0);f.setRegion(6,9,1,e-17,!0);f.setRegion(9,6,e-17,1,!0);6<b.versionNumber&&(f.setRegion(e-11,0,3,6,!0),f.setRegion(0,e-11,6,3,!0));b=f;g=[];e=h=0;f=!0;for(let k=d-1;0<k;k-=2){6===k&&k--;for(let n=0;n<d;n++){let m=f?d-1-n:n;for(let d=0;2>d;d++){let f=k-d;if(!b.get(f,m)){e++;let b=a.get(f,m);c({y:m,x:f})&&(b=!b);h=h<<1|b;8===e&&(g.push(h),h=e=0)}}}f=!f}return g}function ba(a){var b=a.height,c=Math.floor((b-17)/4);if(6>=c)return K[c-
|
||||
1];c=0;for(var d=5;0<=d;d--)for(var e=b-9;e>=b-11;e--)c=C(a.get(e,d),c);d=0;for(e=5;0<=e;e--)for(let c=b-9;c>=b-11;c--)d=C(a.get(e,c),d);a=Infinity;let f;for(let e of K){if(e.infoBits===c||e.infoBits===d)return e;b=E(c,e.infoBits);b<a&&(f=e,a=b);b=E(d,e.infoBits);b<a&&(f=e,a=b)}if(3>=a)return f}function ca(a){let b=0;for(var c=0;8>=c;c++)6!==c&&(b=C(a.get(c,8),b));for(c=7;0<=c;c--)6!==c&&(b=C(a.get(8,c),b));var d=a.height;c=0;for(var e=d-1;e>=d-7;e--)c=C(a.get(8,e),c);for(e=d-8;e<d;e++)c=C(a.get(e,
|
||||
8),c);a=Infinity;d=null;for(let {bits:f,formatInfo:g}of da){if(f===b||f===c)return g;e=E(b,f);e<a&&(d=g,a=e);b!==c&&(e=E(c,f),e<a&&(d=g,a=e))}return 3>=a?d:null}function ea(a,b,c){let d=b.errorCorrectionLevels[c],e=[],f=0;d.ecBlocks.forEach(a=>{for(let b=0;b<a.numBlocks;b++)e.push({numDataCodewords:a.dataCodewordsPerBlock,codewords:[]}),f+=a.dataCodewordsPerBlock+d.ecCodewordsPerBlock});if(a.length<f)return null;a=a.slice(0,f);b=d.ecBlocks[0].dataCodewordsPerBlock;for(c=0;c<b;c++)for(var g of e)g.codewords.push(a.shift());
|
||||
if(1<d.ecBlocks.length)for(g=d.ecBlocks[0].numBlocks,b=d.ecBlocks[1].numBlocks,c=0;c<b;c++)e[g+c].codewords.push(a.shift());for(;0<a.length;)for(let b of e)b.codewords.push(a.shift());return e}function L(a){let b=ba(a);if(!b)return null;var c=ca(a);if(!c)return null;a=Z(a,b,c);var d=ea(a,b,c.errorCorrectionLevel);if(!d)return null;c=d.reduce((a,b)=>a+b.numDataCodewords,0);c=new Uint8ClampedArray(c);a=0;for(let b of d){d=X(b.codewords,b.codewords.length-b.numDataCodewords);if(!d)return null;for(let e=
|
||||
0;e<b.numDataCodewords;e++)c[a++]=d[e]}try{return U(c,b.versionNumber)}catch(e){return null}}function M(a,b,c,d){var e=a.x-b.x+c.x-d.x;let f=a.y-b.y+c.y-d.y;if(0===e&&0===f)return{a11:b.x-a.x,a12:b.y-a.y,a13:0,a21:c.x-b.x,a22:c.y-b.y,a23:0,a31:a.x,a32:a.y,a33:1};{let h=b.x-c.x;var g=d.x-c.x;let k=b.y-c.y,n=d.y-c.y;c=h*n-g*k;g=(e*n-g*f)/c;e=(h*f-e*k)/c;return{a11:b.x-a.x+g*b.x,a12:b.y-a.y+g*b.y,a13:g,a21:d.x-a.x+e*d.x,a22:d.y-a.y+e*d.y,a23:e,a31:a.x,a32:a.y,a33:1}}}function fa(a,b,c,d){a=M(a,b,c,d);
|
||||
return{a11:a.a22*a.a33-a.a23*a.a32,a12:a.a13*a.a32-a.a12*a.a33,a13:a.a12*a.a23-a.a13*a.a22,a21:a.a23*a.a31-a.a21*a.a33,a22:a.a11*a.a33-a.a13*a.a31,a23:a.a13*a.a21-a.a11*a.a23,a31:a.a21*a.a32-a.a22*a.a31,a32:a.a12*a.a31-a.a11*a.a32,a33:a.a11*a.a22-a.a12*a.a21}}function ha(a,b){var c=fa({x:3.5,y:3.5},{x:b.dimension-3.5,y:3.5},{x:b.dimension-6.5,y:b.dimension-6.5},{x:3.5,y:b.dimension-3.5}),d=M(b.topLeft,b.topRight,b.alignmentPattern,b.bottomLeft),e=d.a11*c.a11+d.a21*c.a12+d.a31*c.a13,f=d.a12*c.a11+
|
||||
d.a22*c.a12+d.a32*c.a13,g=d.a13*c.a11+d.a23*c.a12+d.a33*c.a13,h=d.a11*c.a21+d.a21*c.a22+d.a31*c.a23,k=d.a12*c.a21+d.a22*c.a22+d.a32*c.a23,n=d.a13*c.a21+d.a23*c.a22+d.a33*c.a23,m=d.a11*c.a31+d.a21*c.a32+d.a31*c.a33,l=d.a12*c.a31+d.a22*c.a32+d.a32*c.a33,p=d.a13*c.a31+d.a23*c.a32+d.a33*c.a33;c=A.createEmpty(b.dimension,b.dimension);d=(a,b)=>{const c=g*a+n*b+p;return{x:(e*a+h*b+m)/c,y:(f*a+k*b+l)/c}};for(let e=0;e<b.dimension;e++)for(let f=0;f<b.dimension;f++){let b=d(f+.5,e+.5);c.set(f,e,a.get(Math.floor(b.x),
|
||||
Math.floor(b.y)))}return{matrix:c,mappingFunction:d}}function x(a){return a.reduce((a,c)=>a+c)}function ia(a,b,c){let d=y(a,b),e=y(b,c),f=y(a,c),g,h,k;e>=d&&e>=f?[g,h,k]=[b,a,c]:f>=e&&f>=d?[g,h,k]=[a,b,c]:[g,h,k]=[a,c,b];0>(k.x-h.x)*(g.y-h.y)-(k.y-h.y)*(g.x-h.x)&&([g,k]=[k,g]);return{bottomLeft:g,topLeft:h,topRight:k}}function ja(a,b,c,d){d=(x(z(a,c,d,5))/7+x(z(a,b,d,5))/7+x(z(c,a,d,5))/7+x(z(b,a,d,5))/7)/4;if(1>d)throw Error("Invalid module size");b=Math.round(y(a,b)/d);a=Math.round(y(a,c)/d);a=
|
||||
Math.floor((b+a)/2)+7;switch(a%4){case 0:a++;break;case 2:a--}return{dimension:a,moduleSize:d}}function N(a,b,c,d){let e=[{x:Math.floor(a.x),y:Math.floor(a.y)}];var f=Math.abs(b.y-a.y)>Math.abs(b.x-a.x);if(f){var g=Math.floor(a.y);var h=Math.floor(a.x);a=Math.floor(b.y);b=Math.floor(b.x)}else g=Math.floor(a.x),h=Math.floor(a.y),a=Math.floor(b.x),b=Math.floor(b.y);let k=Math.abs(a-g),n=Math.abs(b-h),m=Math.floor(-k/2),l=g<a?1:-1,p=h<b?1:-1,q=!0;for(let v=g,u=h;v!==a+l;v+=l){g=f?u:v;h=f?v:u;if(c.get(g,
|
||||
h)!==q&&(q=!q,e.push({x:g,y:h}),e.length===d+1))break;m+=n;if(0<m){if(u===b)break;u+=p;m-=k}}c=[];for(f=0;f<d;f++)e[f]&&e[f+1]?c.push(y(e[f],e[f+1])):c.push(0);return c}function z(a,b,c,d){let e=b.y-a.y,f=b.x-a.x;b=N(a,b,c,Math.ceil(d/2));a=N(a,{x:a.x-f,y:a.y-e},c,Math.ceil(d/2));c=b.shift()+a.shift()-1;return a.concat(c).concat(...b)}function F(a,b){let c=x(a)/x(b),d=0;b.forEach((b,f)=>{d+=Math.pow(a[f]-b*c,2)});return{averageSize:c,error:d}}function O(a,b,c){try{let d=z(a,{x:-1,y:a.y},c,b.length),
|
||||
e=z(a,{x:a.x,y:-1},c,b.length),f=z(a,{x:Math.max(0,a.x-a.y)-1,y:Math.max(0,a.y-a.x)-1},c,b.length),g=z(a,{x:Math.min(c.width,a.x+a.y)+1,y:Math.min(c.height,a.y+a.x)+1},c,b.length),h=F(d,b),k=F(e,b),n=F(f,b),m=F(g,b),l=(h.averageSize+k.averageSize+n.averageSize+m.averageSize)/4;return Math.sqrt(h.error*h.error+k.error*k.error+n.error*n.error+m.error*m.error)+(Math.pow(h.averageSize-l,2)+Math.pow(k.averageSize-l,2)+Math.pow(n.averageSize-l,2)+Math.pow(m.averageSize-l,2))/l}catch(d){return Infinity}}
|
||||
function H(a,b){for(var c=Math.round(b.x);a.get(c,Math.round(b.y));)c--;for(var d=Math.round(b.x);a.get(d,Math.round(b.y));)d++;c=(c+d)/2;for(d=Math.round(b.y);a.get(Math.round(c),d);)d--;for(b=Math.round(b.y);a.get(Math.round(c),b);)b++;return{x:c,y:(d+b)/2}}function ka(a){var b=[],c=[];let d=[];var e=[];for(let m=0;m<=a.height;m++){var f=0,g=!1;let l=[0,0,0,0,0];for(let b=-1;b<=a.width;b++){var h=a.get(b,m);if(h===g)f++;else{l=[l[1],l[2],l[3],l[4],f];f=1;g=h;var k=x(l)/7;k=Math.abs(l[0]-k)<k&&Math.abs(l[1]-
|
||||
k)<k&&Math.abs(l[2]-3*k)<3*k&&Math.abs(l[3]-k)<k&&Math.abs(l[4]-k)<k&&!h;var n=x(l.slice(-3))/3;h=Math.abs(l[2]-n)<n&&Math.abs(l[3]-n)<n&&Math.abs(l[4]-n)<n&&h;if(k){let a=b-l[3]-l[4],d=a-l[2];k={startX:d,endX:a,y:m};n=c.filter(b=>d>=b.bottom.startX&&d<=b.bottom.endX||a>=b.bottom.startX&&d<=b.bottom.endX||d<=b.bottom.startX&&a>=b.bottom.endX&&1.5>l[2]/(b.bottom.endX-b.bottom.startX)&&.5<l[2]/(b.bottom.endX-b.bottom.startX));0<n.length?n[0].bottom=k:c.push({top:k,bottom:k})}if(h){let a=b-l[4],c=a-
|
||||
l[3];h={startX:c,y:m,endX:a};k=e.filter(b=>c>=b.bottom.startX&&c<=b.bottom.endX||a>=b.bottom.startX&&c<=b.bottom.endX||c<=b.bottom.startX&&a>=b.bottom.endX&&1.5>l[2]/(b.bottom.endX-b.bottom.startX)&&.5<l[2]/(b.bottom.endX-b.bottom.startX));0<k.length?k[0].bottom=h:e.push({top:h,bottom:h})}}}b.push(...c.filter(a=>a.bottom.y!==m&&2<=a.bottom.y-a.top.y));c=c.filter(a=>a.bottom.y===m);d.push(...e.filter(a=>a.bottom.y!==m));e=e.filter(a=>a.bottom.y===m)}b.push(...c.filter(a=>2<=a.bottom.y-a.top.y));d.push(...e);
|
||||
c=[];for(var m of b)2>m.bottom.y-m.top.y||(b=(m.top.startX+m.top.endX+m.bottom.startX+m.bottom.endX)/4,e=(m.top.y+m.bottom.y+1)/2,a.get(Math.round(b),Math.round(e))&&(f=[m.top.endX-m.top.startX,m.bottom.endX-m.bottom.startX,m.bottom.y-m.top.y+1],f=x(f)/f.length,g=O({x:Math.round(b),y:Math.round(e)},[1,1,3,1,1],a),c.push({score:g,x:b,y:e,size:f})));if(3>c.length)return null;c.sort((a,b)=>a.score-b.score);m=[];for(b=0;b<Math.min(c.length,5);++b){e=c[b];f=[];for(var l of c)l!==e&&f.push(Object.assign(Object.assign({},
|
||||
l),{score:l.score+Math.pow(l.size-e.size,2)/e.size}));f.sort((a,b)=>a.score-b.score);m.push({points:[e,f[0],f[1]],score:e.score+f[0].score+f[1].score})}m.sort((a,b)=>a.score-b.score);let {topRight:p,topLeft:q,bottomLeft:v}=ia(...m[0].points);m=P(a,d,p,q,v);l=[];m&&l.push({alignmentPattern:{x:m.alignmentPattern.x,y:m.alignmentPattern.y},bottomLeft:{x:v.x,y:v.y},dimension:m.dimension,topLeft:{x:q.x,y:q.y},topRight:{x:p.x,y:p.y}});m=H(a,p);b=H(a,q);c=H(a,v);(a=P(a,d,m,b,c))&&l.push({alignmentPattern:{x:a.alignmentPattern.x,
|
||||
y:a.alignmentPattern.y},bottomLeft:{x:c.x,y:c.y},topLeft:{x:b.x,y:b.y},topRight:{x:m.x,y:m.y},dimension:a.dimension});return 0===l.length?null:l}function P(a,b,c,d,e){let f,g;try{({dimension:f,moduleSize:g}=ja(d,c,e,a))}catch(m){return null}var h=c.x-d.x+e.x,k=c.y-d.y+e.y;c=(y(d,e)+y(d,c))/2/g;e=1-3/c;let n={x:d.x+e*(h-d.x),y:d.y+e*(k-d.y)};b=b.map(b=>{const c=(b.top.startX+b.top.endX+b.bottom.startX+b.bottom.endX)/4;b=(b.top.y+b.bottom.y+1)/2;if(a.get(Math.floor(c),Math.floor(b))){var d=O({x:Math.floor(c),
|
||||
y:Math.floor(b)},[1,1,1],a)+y({x:c,y:b},n);return{x:c,y:b,score:d}}}).filter(a=>!!a).sort((a,b)=>a.score-b.score);return{alignmentPattern:15<=c&&b.length?b[0]:n,dimension:f}}function Q(a){var b=ka(a);if(!b)return null;for(let e of b){b=ha(a,e);var c=b.matrix;if(null==c)c=null;else{var d=L(c);if(d)c=d;else{for(d=0;d<c.width;d++)for(let a=d+1;a<c.height;a++)c.get(d,a)!==c.get(a,d)&&(c.set(d,a,!c.get(d,a)),c.set(a,d,!c.get(a,d)));c=L(c)}}if(c)return{binaryData:c.bytes,data:c.text,chunks:c.chunks,version:c.version,
|
||||
location:{topRightCorner:b.mappingFunction(e.dimension,0),topLeftCorner:b.mappingFunction(0,0),bottomRightCorner:b.mappingFunction(e.dimension,e.dimension),bottomLeftCorner:b.mappingFunction(0,e.dimension),topRightFinderPattern:e.topRight,topLeftFinderPattern:e.topLeft,bottomLeftFinderPattern:e.bottomLeft,bottomRightAlignmentPattern:e.alignmentPattern},matrix:b.matrix}}return null}function R(a,b){Object.keys(b).forEach(c=>{a[c]=b[c]})}function I(a,b,c,d={}){let e=Object.create(null);R(e,la);R(e,d);
|
||||
d="onlyInvert"===e.inversionAttempts||"invertFirst"===e.inversionAttempts;var f="attemptBoth"===e.inversionAttempts||d;var g=e.greyScaleWeights,h=e.canOverwriteImage,k=b*c;if(a.length!==4*k)throw Error("Malformed data passed to binarizer.");var n=0;if(h){var m=new Uint8ClampedArray(a.buffer,n,k);n+=k}m=new S(b,c,m);if(g.useIntegerApproximation)for(var l=0;l<c;l++)for(var p=0;p<b;p++){var q=4*(l*b+p);m.set(p,l,g.red*a[q]+g.green*a[q+1]+g.blue*a[q+2]+128>>8)}else for(l=0;l<c;l++)for(p=0;p<b;p++)q=4*
|
||||
(l*b+p),m.set(p,l,g.red*a[q]+g.green*a[q+1]+g.blue*a[q+2]);g=Math.ceil(b/8);l=Math.ceil(c/8);p=g*l;if(h){var v=new Uint8ClampedArray(a.buffer,n,p);n+=p}v=new S(g,l,v);for(p=0;p<l;p++)for(q=0;q<g;q++){var u=Infinity,r=0;for(var t=0;8>t;t++)for(let a=0;8>a;a++){let b=m.get(8*q+a,8*p+t);u=Math.min(u,b);r=Math.max(r,b)}t=(u+r)/2;t=Math.min(255,1.11*t);24>=r-u&&(t=u/2,0<p&&0<q&&(r=(v.get(q,p-1)+2*v.get(q-1,p)+v.get(q-1,p-1))/4,u<r&&(t=r)));v.set(q,p,t)}h?(p=new Uint8ClampedArray(a.buffer,n,k),n+=k,p=new A(p,
|
||||
b)):p=A.createEmpty(b,c);q=null;f&&(h?(a=new Uint8ClampedArray(a.buffer,n,k),q=new A(a,b)):q=A.createEmpty(b,c));for(b=0;b<l;b++)for(a=0;a<g;a++){c=g-3;c=2>a?2:a>c?c:a;h=l-3;h=2>b?2:b>h?h:b;k=0;for(n=-2;2>=n;n++)for(u=-2;2>=u;u++)k+=v.get(c+n,h+u);c=k/25;for(h=0;8>h;h++)for(k=0;8>k;k++)n=8*a+h,u=8*b+k,r=m.get(n,u),p.set(n,u,r<=c),f&&q.set(n,u,!(r<=c))}f=f?{binarized:p,inverted:q}:{binarized:p};let {binarized:w,inverted:x}=f;(f=Q(d?x:w))||"attemptBoth"!==e.inversionAttempts&&"invertFirst"!==e.inversionAttempts||
|
||||
(f=Q(d?w:x));return f}class A{constructor(a,b){this.width=b;this.height=a.length/b;this.data=a}static createEmpty(a,b){return new A(new Uint8ClampedArray(a*b),a)}get(a,b){return 0>a||a>=this.width||0>b||b>=this.height?!1:!!this.data[b*this.width+a]}set(a,b,c){this.data[b*this.width+a]=c?1:0}setRegion(a,b,c,d,e){for(let f=b;f<b+d;f++)for(let b=a;b<a+c;b++)this.set(b,f,!!e)}}class S{constructor(a,b,c){this.width=a;a*=b;if(c&&c.length!==a)throw Error("Wrong buffer size");this.data=c||new Uint8ClampedArray(a)}get(a,
|
||||
b){return this.data[b*this.width+a]}set(a,b,c){this.data[b*this.width+a]=c}}class V{constructor(a){this.bitOffset=this.byteOffset=0;this.bytes=a}readBits(a){if(1>a||32<a||a>this.available())throw Error("Cannot read "+a.toString()+" bits");var b=0;if(0<this.bitOffset){b=8-this.bitOffset;var c=a<b?a:b;b-=c;b=(this.bytes[this.byteOffset]&255>>8-c<<b)>>b;a-=c;this.bitOffset+=c;8===this.bitOffset&&(this.bitOffset=0,this.byteOffset++)}if(0<a){for(;8<=a;)b=b<<8|this.bytes[this.byteOffset]&255,this.byteOffset++,
|
||||
a-=8;0<a&&(c=8-a,b=b<<a|(this.bytes[this.byteOffset]&255>>c<<c)>>c,this.bitOffset+=a)}return b}available(){return 8*(this.bytes.length-this.byteOffset)-this.bitOffset}}var r;(function(a){a.Numeric="numeric";a.Alphanumeric="alphanumeric";a.Byte="byte";a.Kanji="kanji";a.ECI="eci";a.StructuredAppend="structuredappend"})(r||(r={}));var t;(function(a){a[a.Terminator=0]="Terminator";a[a.Numeric=1]="Numeric";a[a.Alphanumeric=2]="Alphanumeric";a[a.Byte=4]="Byte";a[a.Kanji=8]="Kanji";a[a.ECI=7]="ECI";a[a.StructuredAppend=
|
||||
3]="StructuredAppend"})(t||(t={}));let B="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".split("");class w{constructor(a,b){if(0===b.length)throw Error("No coefficients.");this.field=a;let c=b.length;if(1<c&&0===b[0]){let d=1;for(;d<c&&0===b[d];)d++;if(d===c)this.coefficients=a.zero.coefficients;else for(this.coefficients=new Uint8ClampedArray(c-d),a=0;a<this.coefficients.length;a++)this.coefficients[a]=b[d+a]}else this.coefficients=b}degree(){return this.coefficients.length-1}isZero(){return 0===
|
||||
this.coefficients[0]}getCoefficient(a){return this.coefficients[this.coefficients.length-1-a]}addOrSubtract(a){if(this.isZero())return a;if(a.isZero())return this;let b=this.coefficients;a=a.coefficients;b.length>a.length&&([b,a]=[a,b]);let c=new Uint8ClampedArray(a.length),d=a.length-b.length;for(var e=0;e<d;e++)c[e]=a[e];for(e=d;e<a.length;e++)c[e]=b[e-d]^a[e];return new w(this.field,c)}multiply(a){if(0===a)return this.field.zero;if(1===a)return this;let b=this.coefficients.length,c=new Uint8ClampedArray(b);
|
||||
for(let d=0;d<b;d++)c[d]=this.field.multiply(this.coefficients[d],a);return new w(this.field,c)}multiplyPoly(a){if(this.isZero()||a.isZero())return this.field.zero;let b=this.coefficients,c=b.length;a=a.coefficients;let d=a.length,e=new Uint8ClampedArray(c+d-1);for(let h=0;h<c;h++){let c=b[h];for(let b=0;b<d;b++){var f=h+b,g=this.field.multiply(c,a[b]);e[f]=e[h+b]^g}}return new w(this.field,e)}multiplyByMonomial(a,b){if(0>a)throw Error("Invalid degree less than 0");if(0===b)return this.field.zero;
|
||||
let c=this.coefficients.length;a=new Uint8ClampedArray(c+a);for(let d=0;d<c;d++)a[d]=this.field.multiply(this.coefficients[d],b);return new w(this.field,a)}evaluateAt(a){let b=0;if(0===a)return this.getCoefficient(0);let c=this.coefficients.length;if(1===a)return this.coefficients.forEach(a=>{b^=a}),b;b=this.coefficients[0];for(let d=1;d<c;d++)b=J(this.field.multiply(a,b),this.coefficients[d]);return b}}class Y{constructor(a,b,c){this.primitive=a;this.size=b;this.generatorBase=c;this.expTable=Array(this.size);
|
||||
this.logTable=Array(this.size);a=1;for(b=0;b<this.size;b++)this.expTable[b]=a,a*=2,a>=this.size&&(a=(a^this.primitive)&this.size-1);for(a=0;a<this.size-1;a++)this.logTable[this.expTable[a]]=a;this.zero=new w(this,Uint8ClampedArray.from([0]));this.one=new w(this,Uint8ClampedArray.from([1]))}multiply(a,b){return 0===a||0===b?0:this.expTable[(this.logTable[a]+this.logTable[b])%(this.size-1)]}inverse(a){if(0===a)throw Error("Can't invert 0");return this.expTable[this.size-this.logTable[a]-1]}buildMonomial(a,
|
||||
b){if(0>a)throw Error("Invalid monomial degree less than 0");if(0===b)return this.zero;a=new Uint8ClampedArray(a+1);a[0]=b;return new w(this,a)}log(a){if(0===a)throw Error("Can't take log(0)");return this.logTable[a]}exp(a){return this.expTable[a]}}let K=[{infoBits:null,versionNumber:1,alignmentPatternCenters:[],errorCorrectionLevels:[{ecCodewordsPerBlock:7,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:10,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:16}]},{ecCodewordsPerBlock:13,
|
||||
ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:13}]},{ecCodewordsPerBlock:17,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:9}]}]},{infoBits:null,versionNumber:2,alignmentPatternCenters:[6,18],errorCorrectionLevels:[{ecCodewordsPerBlock:10,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:34}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:28}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:22}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:16}]}]},
|
||||
{infoBits:null,versionNumber:3,alignmentPatternCenters:[6,22],errorCorrectionLevels:[{ecCodewordsPerBlock:15,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:55}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:13}]}]},{infoBits:null,versionNumber:4,alignmentPatternCenters:[6,26],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:1,
|
||||
dataCodewordsPerBlock:80}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:32}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:9}]}]},{infoBits:null,versionNumber:5,alignmentPatternCenters:[6,30],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:43}]},{ecCodewordsPerBlock:18,
|
||||
ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:15},{numBlocks:2,dataCodewordsPerBlock:16}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:11},{numBlocks:2,dataCodewordsPerBlock:12}]}]},{infoBits:null,versionNumber:6,alignmentPatternCenters:[6,34],errorCorrectionLevels:[{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:68}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:27}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:19}]},
|
||||
{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:15}]}]},{infoBits:31892,versionNumber:7,alignmentPatternCenters:[6,22,38],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:78}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:31}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:4,dataCodewordsPerBlock:15}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:13},
|
||||
{numBlocks:1,dataCodewordsPerBlock:14}]}]},{infoBits:34236,versionNumber:8,alignmentPatternCenters:[6,24,42],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:97}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:38},{numBlocks:2,dataCodewordsPerBlock:39}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:18},{numBlocks:2,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:14},
|
||||
{numBlocks:2,dataCodewordsPerBlock:15}]}]},{infoBits:39577,versionNumber:9,alignmentPatternCenters:[6,26,46],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:36},{numBlocks:2,dataCodewordsPerBlock:37}]},{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:16},{numBlocks:4,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:12},
|
||||
{numBlocks:4,dataCodewordsPerBlock:13}]}]},{infoBits:42195,versionNumber:10,alignmentPatternCenters:[6,28,50],errorCorrectionLevels:[{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:68},{numBlocks:2,dataCodewordsPerBlock:69}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:43},{numBlocks:1,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:19},{numBlocks:2,dataCodewordsPerBlock:20}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,
|
||||
dataCodewordsPerBlock:15},{numBlocks:2,dataCodewordsPerBlock:16}]}]},{infoBits:48118,versionNumber:11,alignmentPatternCenters:[6,30,54],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:81}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:50},{numBlocks:4,dataCodewordsPerBlock:51}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:22},{numBlocks:4,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:3,
|
||||
dataCodewordsPerBlock:12},{numBlocks:8,dataCodewordsPerBlock:13}]}]},{infoBits:51042,versionNumber:12,alignmentPatternCenters:[6,32,58],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:92},{numBlocks:2,dataCodewordsPerBlock:93}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:36},{numBlocks:2,dataCodewordsPerBlock:37}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:20},{numBlocks:6,dataCodewordsPerBlock:21}]},
|
||||
{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:14},{numBlocks:4,dataCodewordsPerBlock:15}]}]},{infoBits:55367,versionNumber:13,alignmentPatternCenters:[6,34,62],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:107}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:37},{numBlocks:1,dataCodewordsPerBlock:38}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:20},{numBlocks:4,dataCodewordsPerBlock:21}]},
|
||||
{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:11},{numBlocks:4,dataCodewordsPerBlock:12}]}]},{infoBits:58893,versionNumber:14,alignmentPatternCenters:[6,26,46,66],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:115},{numBlocks:1,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:40},{numBlocks:5,dataCodewordsPerBlock:41}]},{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:16},
|
||||
{numBlocks:5,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:12},{numBlocks:5,dataCodewordsPerBlock:13}]}]},{infoBits:63784,versionNumber:15,alignmentPatternCenters:[6,26,48,70],errorCorrectionLevels:[{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:87},{numBlocks:1,dataCodewordsPerBlock:88}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:41},{numBlocks:5,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:30,
|
||||
ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:24},{numBlocks:7,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:12},{numBlocks:7,dataCodewordsPerBlock:13}]}]},{infoBits:68472,versionNumber:16,alignmentPatternCenters:[6,26,50,74],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:98},{numBlocks:1,dataCodewordsPerBlock:99}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:45},{numBlocks:3,dataCodewordsPerBlock:46}]},
|
||||
{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:19},{numBlocks:2,dataCodewordsPerBlock:20}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:15},{numBlocks:13,dataCodewordsPerBlock:16}]}]},{infoBits:70749,versionNumber:17,alignmentPatternCenters:[6,30,54,78],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:107},{numBlocks:5,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:46},
|
||||
{numBlocks:1,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:22},{numBlocks:15,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:17,dataCodewordsPerBlock:15}]}]},{infoBits:76311,versionNumber:18,alignmentPatternCenters:[6,30,56,82],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:120},{numBlocks:1,dataCodewordsPerBlock:121}]},{ecCodewordsPerBlock:26,
|
||||
ecBlocks:[{numBlocks:9,dataCodewordsPerBlock:43},{numBlocks:4,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:22},{numBlocks:1,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:19,dataCodewordsPerBlock:15}]}]},{infoBits:79154,versionNumber:19,alignmentPatternCenters:[6,30,58,86],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:113},{numBlocks:4,
|
||||
dataCodewordsPerBlock:114}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:44},{numBlocks:11,dataCodewordsPerBlock:45}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:21},{numBlocks:4,dataCodewordsPerBlock:22}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:9,dataCodewordsPerBlock:13},{numBlocks:16,dataCodewordsPerBlock:14}]}]},{infoBits:84390,versionNumber:20,alignmentPatternCenters:[6,34,62,90],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,
|
||||
dataCodewordsPerBlock:107},{numBlocks:5,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:41},{numBlocks:13,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:24},{numBlocks:5,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:15},{numBlocks:10,dataCodewordsPerBlock:16}]}]},{infoBits:87683,versionNumber:21,alignmentPatternCenters:[6,28,50,72,94],errorCorrectionLevels:[{ecCodewordsPerBlock:28,
|
||||
ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:116},{numBlocks:4,dataCodewordsPerBlock:117}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:22},{numBlocks:6,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:16},{numBlocks:6,dataCodewordsPerBlock:17}]}]},{infoBits:92361,versionNumber:22,alignmentPatternCenters:[6,26,50,74,98],errorCorrectionLevels:[{ecCodewordsPerBlock:28,
|
||||
ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:111},{numBlocks:7,dataCodewordsPerBlock:112}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:24},{numBlocks:16,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:34,dataCodewordsPerBlock:13}]}]},{infoBits:96236,versionNumber:23,alignmentPatternCenters:[6,30,54,74,102],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,
|
||||
dataCodewordsPerBlock:121},{numBlocks:5,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:47},{numBlocks:14,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:16,dataCodewordsPerBlock:15},{numBlocks:14,dataCodewordsPerBlock:16}]}]},{infoBits:102084,versionNumber:24,alignmentPatternCenters:[6,28,54,80,106],errorCorrectionLevels:[{ecCodewordsPerBlock:30,
|
||||
ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:117},{numBlocks:4,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:45},{numBlocks:14,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:24},{numBlocks:16,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:30,dataCodewordsPerBlock:16},{numBlocks:2,dataCodewordsPerBlock:17}]}]},{infoBits:102881,versionNumber:25,alignmentPatternCenters:[6,
|
||||
32,58,84,110],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:106},{numBlocks:4,dataCodewordsPerBlock:107}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:47},{numBlocks:13,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:24},{numBlocks:22,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:15},{numBlocks:13,dataCodewordsPerBlock:16}]}]},
|
||||
{infoBits:110507,versionNumber:26,alignmentPatternCenters:[6,30,58,86,114],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:114},{numBlocks:2,dataCodewordsPerBlock:115}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:46},{numBlocks:4,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:28,dataCodewordsPerBlock:22},{numBlocks:6,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:33,dataCodewordsPerBlock:16},
|
||||
{numBlocks:4,dataCodewordsPerBlock:17}]}]},{infoBits:110734,versionNumber:27,alignmentPatternCenters:[6,34,62,90,118],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:122},{numBlocks:4,dataCodewordsPerBlock:123}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:45},{numBlocks:3,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:23},{numBlocks:26,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:30,
|
||||
ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:15},{numBlocks:28,dataCodewordsPerBlock:16}]}]},{infoBits:117786,versionNumber:28,alignmentPatternCenters:[6,26,50,74,98,122],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:117},{numBlocks:10,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:45},{numBlocks:23,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:24},{numBlocks:31,
|
||||
dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:15},{numBlocks:31,dataCodewordsPerBlock:16}]}]},{infoBits:119615,versionNumber:29,alignmentPatternCenters:[6,30,54,78,102,126],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:116},{numBlocks:7,dataCodewordsPerBlock:117}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:21,dataCodewordsPerBlock:45},{numBlocks:7,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,
|
||||
ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:23},{numBlocks:37,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:15},{numBlocks:26,dataCodewordsPerBlock:16}]}]},{infoBits:126325,versionNumber:30,alignmentPatternCenters:[6,26,52,78,104,130],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:115},{numBlocks:10,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:47},
|
||||
{numBlocks:10,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:24},{numBlocks:25,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:23,dataCodewordsPerBlock:15},{numBlocks:25,dataCodewordsPerBlock:16}]}]},{infoBits:127568,versionNumber:31,alignmentPatternCenters:[6,30,56,82,108,134],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:115},{numBlocks:3,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,
|
||||
ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:46},{numBlocks:29,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:42,dataCodewordsPerBlock:24},{numBlocks:1,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:23,dataCodewordsPerBlock:15},{numBlocks:28,dataCodewordsPerBlock:16}]}]},{infoBits:133589,versionNumber:32,alignmentPatternCenters:[6,34,60,86,112,138],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:115}]},
|
||||
{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:46},{numBlocks:23,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:24},{numBlocks:35,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:15},{numBlocks:35,dataCodewordsPerBlock:16}]}]},{infoBits:136944,versionNumber:33,alignmentPatternCenters:[6,30,58,86,114,142],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:115},
|
||||
{numBlocks:1,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:14,dataCodewordsPerBlock:46},{numBlocks:21,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:29,dataCodewordsPerBlock:24},{numBlocks:19,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:15},{numBlocks:46,dataCodewordsPerBlock:16}]}]},{infoBits:141498,versionNumber:34,alignmentPatternCenters:[6,34,62,90,118,146],errorCorrectionLevels:[{ecCodewordsPerBlock:30,
|
||||
ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:115},{numBlocks:6,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:14,dataCodewordsPerBlock:46},{numBlocks:23,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:44,dataCodewordsPerBlock:24},{numBlocks:7,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:59,dataCodewordsPerBlock:16},{numBlocks:1,dataCodewordsPerBlock:17}]}]},{infoBits:145311,versionNumber:35,alignmentPatternCenters:[6,
|
||||
30,54,78,102,126,150],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:121},{numBlocks:7,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:47},{numBlocks:26,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:39,dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:15},{numBlocks:41,dataCodewordsPerBlock:16}]}]},
|
||||
{infoBits:150283,versionNumber:36,alignmentPatternCenters:[6,24,50,76,102,128,154],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:121},{numBlocks:14,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:47},{numBlocks:34,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:46,dataCodewordsPerBlock:24},{numBlocks:10,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:2,
|
||||
dataCodewordsPerBlock:15},{numBlocks:64,dataCodewordsPerBlock:16}]}]},{infoBits:152622,versionNumber:37,alignmentPatternCenters:[6,28,54,80,106,132,158],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:122},{numBlocks:4,dataCodewordsPerBlock:123}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:29,dataCodewordsPerBlock:46},{numBlocks:14,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:49,dataCodewordsPerBlock:24},{numBlocks:10,dataCodewordsPerBlock:25}]},
|
||||
{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:24,dataCodewordsPerBlock:15},{numBlocks:46,dataCodewordsPerBlock:16}]}]},{infoBits:158308,versionNumber:38,alignmentPatternCenters:[6,32,58,84,110,136,162],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:122},{numBlocks:18,dataCodewordsPerBlock:123}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:46},{numBlocks:32,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:48,
|
||||
dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:42,dataCodewordsPerBlock:15},{numBlocks:32,dataCodewordsPerBlock:16}]}]},{infoBits:161089,versionNumber:39,alignmentPatternCenters:[6,26,54,82,110,138,166],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:20,dataCodewordsPerBlock:117},{numBlocks:4,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:40,dataCodewordsPerBlock:47},{numBlocks:7,dataCodewordsPerBlock:48}]},
|
||||
{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:43,dataCodewordsPerBlock:24},{numBlocks:22,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:15},{numBlocks:67,dataCodewordsPerBlock:16}]}]},{infoBits:167017,versionNumber:40,alignmentPatternCenters:[6,30,58,86,114,142,170],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:118},{numBlocks:6,dataCodewordsPerBlock:119}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:18,
|
||||
dataCodewordsPerBlock:47},{numBlocks:31,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:34,dataCodewordsPerBlock:24},{numBlocks:34,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:20,dataCodewordsPerBlock:15},{numBlocks:61,dataCodewordsPerBlock:16}]}]}],da=[{bits:21522,formatInfo:{errorCorrectionLevel:1,dataMask:0}},{bits:20773,formatInfo:{errorCorrectionLevel:1,dataMask:1}},{bits:24188,formatInfo:{errorCorrectionLevel:1,dataMask:2}},{bits:23371,formatInfo:{errorCorrectionLevel:1,
|
||||
dataMask:3}},{bits:17913,formatInfo:{errorCorrectionLevel:1,dataMask:4}},{bits:16590,formatInfo:{errorCorrectionLevel:1,dataMask:5}},{bits:20375,formatInfo:{errorCorrectionLevel:1,dataMask:6}},{bits:19104,formatInfo:{errorCorrectionLevel:1,dataMask:7}},{bits:30660,formatInfo:{errorCorrectionLevel:0,dataMask:0}},{bits:29427,formatInfo:{errorCorrectionLevel:0,dataMask:1}},{bits:32170,formatInfo:{errorCorrectionLevel:0,dataMask:2}},{bits:30877,formatInfo:{errorCorrectionLevel:0,dataMask:3}},{bits:26159,
|
||||
formatInfo:{errorCorrectionLevel:0,dataMask:4}},{bits:25368,formatInfo:{errorCorrectionLevel:0,dataMask:5}},{bits:27713,formatInfo:{errorCorrectionLevel:0,dataMask:6}},{bits:26998,formatInfo:{errorCorrectionLevel:0,dataMask:7}},{bits:5769,formatInfo:{errorCorrectionLevel:3,dataMask:0}},{bits:5054,formatInfo:{errorCorrectionLevel:3,dataMask:1}},{bits:7399,formatInfo:{errorCorrectionLevel:3,dataMask:2}},{bits:6608,formatInfo:{errorCorrectionLevel:3,dataMask:3}},{bits:1890,formatInfo:{errorCorrectionLevel:3,
|
||||
dataMask:4}},{bits:597,formatInfo:{errorCorrectionLevel:3,dataMask:5}},{bits:3340,formatInfo:{errorCorrectionLevel:3,dataMask:6}},{bits:2107,formatInfo:{errorCorrectionLevel:3,dataMask:7}},{bits:13663,formatInfo:{errorCorrectionLevel:2,dataMask:0}},{bits:12392,formatInfo:{errorCorrectionLevel:2,dataMask:1}},{bits:16177,formatInfo:{errorCorrectionLevel:2,dataMask:2}},{bits:14854,formatInfo:{errorCorrectionLevel:2,dataMask:3}},{bits:9396,formatInfo:{errorCorrectionLevel:2,dataMask:4}},{bits:8579,formatInfo:{errorCorrectionLevel:2,
|
||||
dataMask:5}},{bits:11994,formatInfo:{errorCorrectionLevel:2,dataMask:6}},{bits:11245,formatInfo:{errorCorrectionLevel:2,dataMask:7}}],aa=[a=>0===(a.y+a.x)%2,a=>0===a.y%2,a=>0===a.x%3,a=>0===(a.y+a.x)%3,a=>0===(Math.floor(a.y/2)+Math.floor(a.x/3))%2,a=>0===a.x*a.y%2+a.x*a.y%3,a=>0===(a.y*a.x%2+a.y*a.x%3)%2,a=>0===((a.y+a.x)%2+a.y*a.x%3)%2],y=(a,b)=>Math.sqrt(Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2)),la={inversionAttempts:"attemptBoth",greyScaleWeights:{red:.2126,green:.7152,blue:.0722,useIntegerApproximation:!1},
|
||||
canOverwriteImage:!0};I.default=I;let G="dontInvert",D={red:77,green:150,blue:29,useIntegerApproximation:!0};self.onmessage=a=>{let b=a.data.data;switch(a.data.type){case "decode":a=I(b.data,b.width,b.height,{inversionAttempts:G,greyScaleWeights:D});self.postMessage({type:"qrResult",data:a?a.data:null});break;case "grayscaleWeights":D.red=b.red;D.green=b.green;D.blue=b.blue;D.useIntegerApproximation=b.useIntegerApproximation;break;case "inversionMode":switch(b){case "original":G="dontInvert";break;
|
||||
case "invert":G="onlyInvert";break;case "both":G="attemptBoth";break;default:throw Error("Invalid inversion mode");}break;case "close":self.close()}}})()
|
||||
//# sourceMappingURL=qr-scanner-worker.min.js.map
|
|
@ -0,0 +1,20 @@
|
|||
'use strict';(function(d,a){"object"===typeof exports&&"undefined"!==typeof module?module.exports=a():"function"===typeof define&&define.amd?define(a):(d=d||self,d.QrScanner=a())})(this,function(){class d{static hasCamera(){return d.listCameras(!1).then(a=>!!a.length).catch(()=>!1)}static listCameras(a=!1){if(!navigator.mediaDevices)return Promise.resolve([]);let b=null;return(a?navigator.mediaDevices.getUserMedia({audio:!1,video:!0}).then(a=>b=a).catch(()=>{}):Promise.resolve()).then(()=>navigator.mediaDevices.enumerateDevices()).then(a=>
|
||||
a.filter(a=>"videoinput"===a.kind).map((a,b)=>({id:a.deviceId,label:a.label||(0===b?"Default Camera":`Camera ${b+1}`)}))).finally(()=>{if(b)for(let a of b.getTracks())a.stop(),b.removeTrack(a)})}constructor(a,b,c=this._onDecodeError,f=this._calculateScanRegion,k="environment"){this.$video=a;this.$canvas=document.createElement("canvas");this._onDecode=b;this._legacyCanvasSize=d.DEFAULT_CANVAS_SIZE;this._preferredCamera=k;this._flashOn=this._paused=this._active=!1;"number"===typeof c?(this._legacyCanvasSize=
|
||||
c,console.warn("You're using a deprecated version of the QrScanner constructor which will be removed in the future")):this._onDecodeError=c;"number"===typeof f?(this._legacyCanvasSize=f,console.warn("You're using a deprecated version of the QrScanner constructor which will be removed in the future")):this._calculateScanRegion=f;this._scanRegion=this._calculateScanRegion(a);this._onPlay=this._onPlay.bind(this);this._onLoadedMetaData=this._onLoadedMetaData.bind(this);this._onVisibilityChange=this._onVisibilityChange.bind(this);
|
||||
a.disablePictureInPicture=!0;a.playsInline=!0;a.muted=!0;let g=!1;a.hidden&&(a.hidden=!1,g=!0);document.body.contains(a)||(document.body.appendChild(a),g=!0);requestAnimationFrame(()=>{let b=window.getComputedStyle(a);"none"===b.display&&(a.style.setProperty("display","block","important"),g=!0);"visible"!==b.visibility&&(a.style.setProperty("visibility","visible","important"),g=!0);g&&(console.warn("QrScanner has overwritten the video hiding style to avoid Safari stopping the playback."),a.style.opacity=
|
||||
0,a.style.width=0,a.style.height=0)});a.addEventListener("play",this._onPlay);a.addEventListener("loadedmetadata",this._onLoadedMetaData);document.addEventListener("visibilitychange",this._onVisibilityChange);this._qrEnginePromise=d.createQrEngine()}hasFlash(){let a=null;return(this.$video.srcObject?Promise.resolve(this.$video.srcObject.getVideoTracks()[0]):this._getCameraStream().then(({stream:b})=>{console.warn("Call hasFlash after successfully starting the scanner to avoid creating a temporary video stream");
|
||||
a=b;return b.getVideoTracks()[0]})).then(a=>"torch"in a.getSettings()).catch(()=>!1).finally(()=>{if(a)for(let b of a.getTracks())b.stop(),a.removeTrack(b)})}isFlashOn(){return this._flashOn}toggleFlash(){return this._flashOn?this.turnFlashOff():this.turnFlashOn()}turnFlashOn(){if(this._flashOn)return Promise.resolve();this._flashOn=!0;return!this._active||this._paused?Promise.resolve():this.hasFlash().then(a=>a?this.$video.srcObject.getVideoTracks()[0].applyConstraints({advanced:[{torch:!0}]}):Promise.reject("No flash available")).catch(()=>
|
||||
{this._flashOn=!1;throw e;})}turnFlashOff(){if(this._flashOn)return this._flashOn=!1,this._restartVideoStream()}destroy(){this.$video.removeEventListener("loadedmetadata",this._onLoadedMetaData);this.$video.removeEventListener("play",this._onPlay);document.removeEventListener("visibilitychange",this._onVisibilityChange);this.stop();d._postWorkerMessage(this._qrEnginePromise,"close")}start(){if(this._active&&!this._paused)return Promise.resolve();"https:"!==window.location.protocol&&console.warn("The camera stream is only accessible if the page is transferred via https.");
|
||||
this._active=!0;if(document.hidden)return Promise.resolve();this._paused=!1;return this.$video.srcObject?(this.$video.play(),Promise.resolve()):this._getCameraStream().then(({stream:a,facingMode:b})=>{this.$video.srcObject=a;this.$video.play();this._setVideoMirror(b);this._flashOn&&(this._flashOn=!1,this.turnFlashOn().catch(()=>{}))}).catch(a=>{this._active=!1;throw a;})}stop(){this.pause();this._active=!1}pause(a=!1){this._paused=!0;if(!this._active)return Promise.resolve(!0);this.$video.pause();
|
||||
let b=()=>{const a=this.$video.srcObject?this.$video.srcObject.getTracks():[];for(const b of a)b.stop(),this.$video.srcObject.removeTrack(b);this.$video.srcObject=null};return a?(b(),Promise.resolve(!0)):(new Promise(a=>setTimeout(a,300))).then(()=>{if(!this._paused)return!1;b();return!0})}setCamera(a){if(a===this._preferredCamera)return Promise.resolve();this._preferredCamera=a;return this._restartVideoStream()}static scanImage(a,b=null,c=null,f=null,k=!1,g=!1){let h=c instanceof Worker,l=Promise.all([c||
|
||||
d.createQrEngine(),d._loadImage(a)]).then(([a,g])=>{c=a;let l;[f,l]=this._drawToCanvas(g,b,f,k);return c instanceof Worker?(h||c.postMessage({type:"inversionMode",data:"both"}),new Promise((a,b)=>{let k,g,h;g=f=>{"qrResult"===f.data.type&&(c.removeEventListener("message",g),c.removeEventListener("error",h),clearTimeout(k),null!==f.data.data?a(f.data.data):b(d.NO_QR_CODE_FOUND))};h=a=>{c.removeEventListener("message",g);c.removeEventListener("error",h);clearTimeout(k);b("Scanner error: "+(a?a.message||
|
||||
a:"Unknown Error"))};c.addEventListener("message",g);c.addEventListener("error",h);k=setTimeout(()=>h("timeout"),1E4);let m=l.getImageData(0,0,f.width,f.height);c.postMessage({type:"decode",data:m},[m.data.buffer])})):new Promise((a,b)=>{let k=setTimeout(()=>b("Scanner error: timeout"),1E4);c.detect(f).then(c=>{c.length?a(c[0].rawValue):b(d.NO_QR_CODE_FOUND)}).catch(a=>b("Scanner error: "+(a.message||a))).finally(()=>clearTimeout(k))})});b&&g&&(l=l.catch(()=>d.scanImage(a,null,c,f,k)));return l=l.finally(()=>
|
||||
{h||d._postWorkerMessage(c,"close")})}setGrayscaleWeights(a,b,c,f=!0){d._postWorkerMessage(this._qrEnginePromise,"grayscaleWeights",{red:a,green:b,blue:c,useIntegerApproximation:f})}setInversionMode(a){d._postWorkerMessage(this._qrEnginePromise,"inversionMode",a)}static createQrEngine(a=d.WORKER_PATH){return("BarcodeDetector"in window&&BarcodeDetector.getSupportedFormats?BarcodeDetector.getSupportedFormats():Promise.resolve([])).then(b=>-1!==b.indexOf("qr_code")?new BarcodeDetector({formats:["qr_code"]}):
|
||||
new Worker(a))}_onPlay(){this._scanRegion=this._calculateScanRegion(this.$video);this._scanFrame()}_onLoadedMetaData(){this._scanRegion=this._calculateScanRegion(this.$video)}_onVisibilityChange(){document.hidden?this.pause():this._active&&this.start()}_calculateScanRegion(a){let b=Math.round(2/3*Math.min(a.videoWidth,a.videoHeight));return{x:Math.round((a.videoWidth-b)/2),y:Math.round((a.videoHeight-b)/2),width:b,height:b,downScaledWidth:this._legacyCanvasSize,downScaledHeight:this._legacyCanvasSize}}_scanFrame(){if(!this._active||
|
||||
this.$video.paused||this.$video.ended)return!1;requestAnimationFrame(()=>{1>=this.$video.readyState?this._scanFrame():this._qrEnginePromise.then(a=>d.scanImage(this.$video,this._scanRegion,a,this.$canvas)).then(this._onDecode,a=>{this._active&&(-1!==(a.message||a).indexOf("service unavailable")&&(this._qrEnginePromise=d.createQrEngine()),this._onDecodeError(a))}).then(()=>this._scanFrame())})}_onDecodeError(a){a!==d.NO_QR_CODE_FOUND&&console.log(a)}_getCameraStream(){if(!navigator.mediaDevices)return Promise.reject("Camera not found.");
|
||||
let a="environment"===this._preferredCamera||"user"===this._preferredCamera?"facingMode":"deviceId",b=[{width:{min:1024}},{width:{min:768}},{}];return[...b.map(b=>Object.assign({},b,{[a]:{exact:this._preferredCamera}})),...b].reduceRight((a,b)=>()=>navigator.mediaDevices.getUserMedia({video:b,audio:!1}).then(a=>({stream:a,facingMode:this._getFacingMode(a)||(b.facingMode?this._preferredCamera:"environment"===this._preferredCamera?"user":"environment")})).catch(a),()=>Promise.reject("Camera not found."))()}_restartVideoStream(){let a=
|
||||
this._paused;return this.pause(!0).then(b=>{if(b&&!a&&this._active)return this.start()})}_setVideoMirror(a){this.$video.style.transform="scaleX("+("user"===a?-1:1)+")"}_getFacingMode(a){return(a=a.getVideoTracks()[0])?/rear|back|environment/i.test(a.label)?"environment":/front|user|face/i.test(a.label)?"user":null:null}static _drawToCanvas(a,b=null,c=null,f=!1){c=c||document.createElement("canvas");let d=b&&b.x?b.x:0,g=b&&b.y?b.y:0,h=b&&b.width?b.width:a.width||a.videoWidth,l=b&&b.height?b.height:
|
||||
a.height||a.videoHeight;f||(f=b&&b.downScaledWidth?b.downScaledWidth:h,b=b&&b.downScaledHeight?b.downScaledHeight:l,c.width!==f&&(c.width=f),c.height!==b&&(c.height=b));b=c.getContext("2d",{alpha:!1});b.imageSmoothingEnabled=!1;b.drawImage(a,d,g,h,l,0,0,c.width,c.height);return[c,b]}static _loadImage(a){if(a instanceof HTMLCanvasElement||a instanceof HTMLVideoElement||window.ImageBitmap&&a instanceof window.ImageBitmap||window.OffscreenCanvas&&a instanceof window.OffscreenCanvas)return Promise.resolve(a);
|
||||
if(a instanceof Image)return d._awaitImageLoad(a).then(()=>a);if(a instanceof File||a instanceof Blob||a instanceof URL||"string"===typeof a){let b=new Image;b.src=a instanceof File||a instanceof Blob?URL.createObjectURL(a):a;return d._awaitImageLoad(b).then(()=>{(a instanceof File||a instanceof Blob)&&URL.revokeObjectURL(b.src);return b})}return Promise.reject("Unsupported image type.")}static _awaitImageLoad(a){return new Promise((b,c)=>{if(a.complete&&0!==a.naturalWidth)b();else{let f,d;f=()=>
|
||||
{a.removeEventListener("load",f);a.removeEventListener("error",d);b()};d=()=>{a.removeEventListener("load",f);a.removeEventListener("error",d);c("Image load error")};a.addEventListener("load",f);a.addEventListener("error",d)}})}static _postWorkerMessage(a,b,c){return Promise.resolve(a).then(a=>{a instanceof Worker&&a.postMessage({type:b,data:c})})}}d.DEFAULT_CANVAS_SIZE=400;d.NO_QR_CODE_FOUND="No QR code found";d.WORKER_PATH="qr-scanner-worker.min.js";return d})
|
||||
//# sourceMappingURL=qr-scanner.umd.min.js.map
|
|
@ -0,0 +1,2 @@
|
|||
0.01: First release
|
||||
0.02: Fix dependancies, fix type to Purple
|
|
@ -0,0 +1,26 @@
|
|||
# Rebble
|
||||
|
||||
*A Pebble style clock, with configurable background, three sidebars including steps, day, date, sunrise, sunset, long live the rebellion*
|
||||
|
||||
* Designed specifically for Bangle 2
|
||||
* A choice of 6 different background colous through its setting menu. Goto Settings, App/Widget settings, Rebble.
|
||||
* Supports the Light and Dark themes
|
||||
* Low power drain, only redraws once per minute
|
||||
* Has 3 sidebars that cycle including steps, day, date, sunrise, sunset
|
||||
* Tap top or bottom right to instantly cycle to the next sidebar
|
||||
* Uses pedometer widget to get latest step count
|
||||
* Dependant apps are installed when Rebble installs
|
||||
* Uses the whole screen, widgets are made invisible but still run in the background
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## Future Enhancements
|
||||
|
||||
* Support for Weather Icons in the Steps Sidebar
|
||||
* Improved small font
|
||||
* Improved icons
|
||||
|
||||
Written by: [Hugh Barney](https://github.com/hughbarney) For support and discussion please post in the [Bangle JS Forum](http://forum.espruino.com/microcosms/1424/)
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("oFA4X/AAIHBw3Aiv3HmE/HQQAF/gPEnWqAAOpy2VqoFB3gPIBoIABtQPJ1PVqv1q3qB5OlrNVEIQPK2tlBwOptQPIyvdH4VtrQPI3tbqtdB4OaB5FVH4NV0pgBB5F13//MIIPJ1O2TgWV/o/I1fbB4WpqoPI1NvB4REBJ5APD/wPBD5JOBB4WVqwPH0oPE0oPJ/NX//6AoNVF5HZq3pq2qSYIPI6tX+pNBB5Ol6v6B4IABH5P7//b1oPBN5GlLwPr9IPK1IPC/SvK1QPCOAIPL6te//5B5lW/5ABL5APB/wPB3IPJ1Y/C/yuBF5APC9X+yo/K34LB3QPBtQPJ//23SPB1QPI3eVs2qJwIPJ1flqyeBtQPJtZPBLwIPKzf/1ROCB5OWAQJOBB5QsBAAQGBf5FlB5tVvoPMNQO9B4daB5O+B4aPIqtX35tBB5M1qtbB4i/HB4WvOAjvGB4IpBIQIADB46aBB4t8B49VB54AFB6zrB1Wm1RTBywPI0oPCeQOaB4+ltOlq2V02VqwPOrQPIF5w/PFQIvPB71pH4uqX8g"))
|
After Width: | Height: | Size: 4.5 KiB |
|
@ -0,0 +1,38 @@
|
|||
(function(back) {
|
||||
const SETTINGS_FILE = "rebble.json";
|
||||
|
||||
// initialize with default settings...
|
||||
let s = {'bg': '#0f0', 'color': 'Green'}
|
||||
|
||||
// ...and overwrite them with any saved values
|
||||
// This way saved values are preserved if a new version adds more settings
|
||||
const storage = require('Storage')
|
||||
let settings = storage.readJSON(SETTINGS_FILE, 1) || s;
|
||||
const saved = settings || {}
|
||||
for (const key in saved) {
|
||||
s[key] = saved[key]
|
||||
}
|
||||
|
||||
function save() {
|
||||
settings = s
|
||||
storage.write(SETTINGS_FILE, settings)
|
||||
}
|
||||
|
||||
var color_options = ['Green','Orange','Cyan','Purple','Red','Blue'];
|
||||
var bg_code = ['#0f0','#ff0','#0ff','#f0f','#f00','#00f'];
|
||||
|
||||
E.showMenu({
|
||||
'': { 'title': 'Rebble Clock' },
|
||||
'< Back': back,
|
||||
'Colour': {
|
||||
value: 0 | color_options.indexOf(s.color),
|
||||
min: 0, max: 5,
|
||||
format: v => color_options[v],
|
||||
onchange: v => {
|
||||
s.color = color_options[v];
|
||||
s.bg = bg_code[v];
|
||||
save();
|
||||
},
|
||||
}
|
||||
});
|
||||
})
|
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.3 KiB |
|
@ -58,7 +58,7 @@ function resetSettings() {
|
|||
settings = storage.readJSON('setting.json', 1);
|
||||
if (!settings) resetSettings();
|
||||
|
||||
const boolFormat = v => v ? "On" : "Off";
|
||||
const boolFormat = v => v ? /*LANG*/"On" : /*LANG*/"Off";
|
||||
|
||||
function showMainMenu() {
|
||||
var beepMenuItem;
|
||||
|
@ -77,7 +77,7 @@ function showMainMenu() {
|
|||
};
|
||||
} else { // Bangle.js 1
|
||||
var beepV = [false, true, "vib"];
|
||||
var beepN = ["Off", "Piezo", "Vibrate"];
|
||||
var beepN = [/*LANG*/"Off", /*LANG*/"Piezo", /*LANG*/"Vibrate"];
|
||||
beepMenuItem = {
|
||||
value: Math.max(0 | beepV.indexOf(settings.beep),0),
|
||||
min: 0, max: beepV.length-1,
|
||||
|
@ -95,10 +95,10 @@ function showMainMenu() {
|
|||
const mainmenu = {
|
||||
'': { 'title': 'Settings' },
|
||||
'< Back': ()=>load(),
|
||||
'App Settings': ()=>showAppSettingsMenu(),
|
||||
'BLE': ()=>showBLEMenu(),
|
||||
'Beep': beepMenuItem,
|
||||
'Vibration': {
|
||||
/*LANG*/'App Settings': ()=>showAppSettingsMenu(),
|
||||
/*LANG*/'BLE': ()=>showBLEMenu(),
|
||||
/*LANG*/'Beep': beepMenuItem,
|
||||
/*LANG*/'Vibration': {
|
||||
value: settings.vibrate,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
|
@ -110,7 +110,7 @@ function showMainMenu() {
|
|||
}
|
||||
}
|
||||
},
|
||||
"Quiet Mode": {
|
||||
/*LANG*/"Quiet Mode": {
|
||||
value: settings.quiet|0,
|
||||
format: v => ["Off", "Alarms", "Silent"][v%3],
|
||||
onchange: v => {
|
||||
|
@ -120,13 +120,13 @@ function showMainMenu() {
|
|||
if ("qmsched" in WIDGETS) WIDGETS["qmsched"].draw();
|
||||
},
|
||||
},
|
||||
'Locale': ()=>showLocaleMenu(),
|
||||
'Select Clock': ()=>showClockMenu(),
|
||||
'Set Time': ()=>showSetTimeMenu(),
|
||||
'LCD': ()=>showLCDMenu(),
|
||||
'Theme': ()=>showThemeMenu(),
|
||||
'Utils': ()=>showUtilMenu(),
|
||||
'Turn Off': ()=>{ if (Bangle.softOff) Bangle.softOff(); else Bangle.off() },
|
||||
/*LANG*/'Locale': ()=>showLocaleMenu(),
|
||||
/*LANG*/'Select Clock': ()=>showClockMenu(),
|
||||
/*LANG*/'Set Time': ()=>showSetTimeMenu(),
|
||||
/*LANG*/'LCD': ()=>showLCDMenu(),
|
||||
/*LANG*/'Theme': ()=>showThemeMenu(),
|
||||
/*LANG*/'Utils': ()=>showUtilMenu(),
|
||||
/*LANG*/'Turn Off': ()=>{ if (Bangle.softOff) Bangle.softOff(); else Bangle.off() },
|
||||
};
|
||||
|
||||
return E.showMenu(mainmenu);
|
||||
|
|