diff --git a/apps.json b/apps.json index 0a6cebba7..35430e235 100644 --- a/apps.json +++ b/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,12 +11,12 @@ "custom": "custom.html", "customConnect": true, "storage": [], - "sortorder": -20 + "sortorder": 20 }, { "id": "boot", "name": "Bootloader", - "version": "0.38", + "version": "0.39", "description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings", "icon": "bootloader.png", "type": "bootloader", @@ -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.11", + "version": "0.16", "description": "App to display notifications from iOS and Gadgetbridge", "icon": "app.png", "type": "app", @@ -72,14 +92,15 @@ {"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", - "description": "Display notifications/music/etc from Gadgetbridge on Android. This replaces the old Gadgetbridge widget.", + "version": "0.05", + "description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.", "icon": "app.png", "tags": "tool,system,messages,notifications", "dependencies": {"messages":"app"}, @@ -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", @@ -111,7 +132,7 @@ { "id": "health", "name": "Health Tracking", - "version": "0.08", + "version": "0.09", "description": "Logs health data and provides an app to view it (requires firmware 2v10.100 or later)", "icon": "app.png", "tags": "tool,system,health", @@ -146,7 +167,7 @@ { "id": "setting", "name": "Settings", - "version": "0.36", + "version": "0.39", "description": "A menu for setting up Bangle.js", "icon": "settings.png", "tags": "tool,system", @@ -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,8 +303,8 @@ { "id": "gbridge", "name": "Gadgetbridge", - "version": "0.24", - "description": "(NOT RECOMMENDED) Handles Gadgetbridge notifications from Android. This is now replaced by the 'Android' app.", + "version": "0.25", + "description": "(NOT RECOMMENDED) Displays Gadgetbridge notifications from Android. Please use the 'Android' Bangle.js app instead.", "icon": "app.png", "type": "widget", "tags": "tool,system,android,widget", @@ -533,12 +554,12 @@ { "id": "impwclock", "name": "Imprecise Word Clock", - "version": "0.03", + "version": "0.04", "description": "Imprecise word clock for vacations, weekends, and those who never need accurate time.", "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": [ @@ -747,7 +768,7 @@ "id": "recorder", "name": "Recorder (BETA)", "shortName": "Recorder", - "version": "0.04", + "version": "0.05", "description": "Record GPS position, heart rate and more in the background, then download to your PC.", "icon": "app.png", "tags": "tool,outdoors,gps,widget", @@ -824,7 +845,7 @@ { "id": "weather", "name": "Weather", - "version": "0.12", + "version": "0.14", "description": "Show Gadgetbridge weather report", "icon": "icon.png", "screenshots": [{"url":"screenshot.png"}], @@ -950,7 +971,7 @@ { "id": "widbt", "name": "Bluetooth Widget", - "version": "0.07", + "version": "0.08", "description": "Show the current Bluetooth connection status in the top right of the clock", "icon": "widget.png", "type": "widget", @@ -1127,7 +1148,7 @@ { "id": "qrcode", "name": "Custom QR Code", - "version": "0.03", + "version": "0.05", "description": "Use this to upload a customised QR code to Bangle.js", "icon": "app.png", "tags": "qrcode", @@ -1480,7 +1501,7 @@ { "id": "gpsinfo", "name": "GPS Info", - "version": "0.05", + "version": "0.06", "description": "An application that displays information about altitude, lat/lon, satellites and time", "icon": "gps-info.png", "type": "app", @@ -1569,7 +1590,7 @@ { "id": "widpedom", "name": "Pedometer widget", - "version": "0.19", + "version": "0.20", "description": "Daily pedometer widget", "icon": "widget.png", "type": "widget", @@ -1678,14 +1699,15 @@ "id": "rtorch", "name": "Red Torch", "shortName": "RedTorch", - "version": "0.01", - "description": "Turns screen RED to help you see in the dark without breaking your night vision. Select from the launcher or press BTN3,BTN1,BTN3,BTN1 quickly to start when in any app that shows widgets", + "version": "0.02", + "description": "Turns screen RED to help you see in the dark without breaking your night vision. Select from the launcher or on Bangle 1 press BTN3,BTN1,BTN3,BTN1 quickly to start when in any app that shows widgets", "icon": "app.png", "tags": "tool,torch", - "supports": ["BANGLEJS"], + "supports": ["BANGLEJS","BANGLEJS2"], + "allow_emulator": true, "storage": [ {"name":"rtorch.app.js","url":"app.js"}, - {"name":"rtorch.wid.js","url":"widget.js"}, + {"name":"rtorch.wid.js","url":"widget.js", "supports": ["BANGLEJS"]}, {"name":"rtorch.img","url":"app-icon.js","evaluate":true} ] }, @@ -2005,7 +2027,7 @@ "id": "chronowid", "name": "Chrono Widget", "shortName": "Chrono Widget", - "version": "0.04", + "version": "0.05", "description": "Chronometer (timer) which runs as widget.", "icon": "app.png", "tags": "tool,widget", @@ -2147,21 +2169,22 @@ { "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} ] }, { "id": "calculator", "name": "Calculator", "shortName": "Calculator", - "version": "0.04", + "version": "0.05", "description": "Basic calculator reminiscent of MacOs's one. Handy for small calculus.", "icon": "calculator.png", "screenshots": [{"url":"screenshot_calculator.png"}], @@ -2406,7 +2429,7 @@ { "id": "calendar", "name": "Calendar", - "version": "0.03", + "version": "0.05", "description": "Simple calendar", "icon": "calendar.png", "screenshots": [{"url":"screenshot_calendar.png"}], @@ -3274,7 +3297,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 +3308,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", @@ -3452,8 +3478,8 @@ { "id": "speedalt2", "name": "GPS Adventure Sports II", - "shortName": "GPS Adv Sport II", - "version": "0.07", + "shortName":"GPS Adv Sport II", + "version":"1.10", "description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.", "icon": "app.png", "type": "app", @@ -3870,7 +3896,7 @@ "id": "qmsched", "name": "Quiet Mode Schedule and Widget", "shortName": "Quiet Mode", - "version": "0.04", + "version": "0.06", "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 +4014,12 @@ { "id": "thermom", "name": "Thermometer", - "version": "0.03", - "description": "Displays the current temperature in degree Celsius, updated every 20 seconds", + "version": "0.05", + "description": "Displays the current temperature in degree Celsius/Fahrenheit (depending on locale), updates every 10 seconds with average of last 5 readings.", "icon": "app.png", "tags": "tool", - "supports": ["BANGLEJS"], + "supports": ["BANGLEJS", "BANGLEJS2"], + "screenshots": [{"url":"screenshot.png"}], "allow_emulator": true, "storage": [ {"name":"thermom.app.js","url":"app.js"}, @@ -4037,7 +4064,7 @@ { "id": "hcclock", "name": "Hi-Contrast Clock", - "version": "0.02", + "version": "0.03", "description": "Hi-Contrast Clock : A simple yet very bold clock that aims to be readable in high luninosity environments. Uses big 10x5 pixel digits. Use BTN 1 to switch background and foreground colors.", "icon": "hcclock-icon.png", "type": "clock", @@ -4054,7 +4081,7 @@ "id": "thermomF", "name": "Fahrenheit Temp", "version": "0.01", - "description": "A modification of the Thermometer App to display temprature in Fahrenheit", + "description": "[NOT RECOMMENDED] A modification of the Thermometer App to display temprature in Fahrenheit. Please use the 'Thermometer App' and install 'Languages' to get the temperature in the correct format for your locale.", "icon": "thermf.png", "tags": "tool", "supports": ["BANGLEJS"], @@ -4117,14 +4144,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} @@ -4180,10 +4210,10 @@ "id": "pastel", "name": "Pastel Clock", "shortName": "Pastel", - "version": "0.08", + "version": "0.09", "description": "A Configurable clock with custom fonts and background. Has a cyclic information line that includes, day, date, battery, sunrise and sunset times", "icon": "pastel.png", - "dependencies": {"mylocation":"app"}, + "dependencies": {"mylocation":"app", "widpedom":"app"}, "screenshots": [{"url":"screenshot_pastel.png"}], "type": "clock", "tags": "clock", @@ -4442,9 +4472,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", @@ -4457,7 +4487,7 @@ "name": "LCARS Clock", "shortName":"LCARS", "icon": "lcars.png", - "version":"0.06", + "version":"0.08", "readme": "README.md", "supports": ["BANGLEJS2"], "description": "Library Computer Access Retrieval System (LCARS) clock.", @@ -4466,7 +4496,8 @@ "screenshots": [{"url":"screenshot.png"}], "storage": [ {"name":"lcars.app.js","url":"lcars.app.js"}, - {"name":"lcars.img","url":"lcars.icon.js","evaluate":true} + {"name":"lcars.img","url":"lcars.icon.js","evaluate":true}, + {"name":"lcars.settings.js","url":"lcars.settings.js"} ] }, { "id": "binwatch", @@ -4621,7 +4652,7 @@ "id": "sensible", "name": "SensiBLE", "shortName": "SensiBLE", - "version": "0.03", + "version": "0.05", "description": "Collect, display and advertise real-time sensor data.", "icon": "sensible.png", "screenshots": [ @@ -4666,6 +4697,8 @@ "tags": "tool,timer", "readme":"README.md", "supports":["BANGLEJS2"], + "screenshots": [{"url":"screenshot1.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"}], + "allow_emulator": true, "storage": [ {"name":"a_speech_timer.app.js","url":"app.js"}, {"name":"a_speech_timer.img","url":"app-icon.js","evaluate":true} @@ -4694,8 +4727,9 @@ "id": "pebble", "name": "Pebble Clock", "shortName": "Pebble", - "version": "0.04", + "version": "0.06", "description": "A pebble style clock to keep the rebellion going", + "dependencies": {"widpedom":"app"}, "readme": "README.md", "icon": "pebble.png", "screenshots": [{"url":"pebble_screenshot.png"}], @@ -4711,7 +4745,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 +4780,7 @@ { "id": "weatherClock", "name": "Weather Clock", - "version": "0.03", + "version": "0.05", "description": "A clock which displays current weather conditions (requires Gadgetbridge and Weather apps).", "icon": "app.png", "screenshots": [{"url":"screens/screen1.png"}], @@ -4806,6 +4840,25 @@ {"name": "flow.img", "url": "app-icon.js","evaluate": true } ] }, + { "id": "tinydraw", + "name": "TinyDraw", + "shortName":"TinyDraw", + "version":"0.01", + "type": "app", + "description": "Draw stuff in your wrist", + "icon": "app.png", + "allow_emulator": true, + "tags": "tools, keyboard, text, scribble", + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"tinydraw.app.js","url":"app.js"}, + {"name":"tinydraw.img","url":"app-icon.js","evaluate":true} + ], + "screenshots":[ + { "url":"screenshot.png" } + ] + }, { "id": "scribble", "name": "Scribble", "shortName":"Scribble", @@ -4829,9 +4882,10 @@ "id": "ptlaunch", "name": "Pattern Launcher", "shortName": "Pattern Launcher", - "version": "0.02", + "version": "0.13", "description": "Directly launch apps from the clock screen with custom patterns.", "icon": "app.png", + "screenshots": [{"url":"manage_patterns_light.png"}], "tags": "tools", "supports": ["BANGLEJS2"], "readme": "README.md", @@ -4842,20 +4896,466 @@ ], "data": [{"name":"ptlaunch.patterns.json"}] }, - { "id": "clicompleteclk", - "name": "CLI complete clock", - "shortName":"CLI cmplt clock", - "version":"0.02", - "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": "slimehunt", + "name": "Slime Hunt", + "shortName":"SlimeHunt", + "icon": "app.png", + "version":"0.02", + "description": "Fight against slimes in turn based combat, try to get the highscore!", + "tags": "rpg,slime", + "supports" : ["BANGLEJS"], + "readme": "README.md", + "storage": [ + {"name":"slimehunt.app.js","url":"app.js"}, + {"name":"slimehunt.img","url":"app-icon.js","evaluate":true} ] -} + }, + { + "id": "rebble", + "name": "Rebble Clock", + "shortName": "Rebble", + "version": "0.03", + "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", "widpedom":"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.app.js","url":"app.js"}, + {"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", + "screenshots": [{"url":"screenshot.png"}], + "allow_emulator": true, + "version":"0.03", + "description": "Displays the level of CO2, VOC, PM 2.5, Humidity and Temperature, from your Awair device.", + "type": "clock", + "tags": "clock,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.01", + "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": "CoreTemp", + "version": "0.02", + "description": "Display CoreTemp device sensor data", + "icon": "coretemp.png", + "type": "app", + "tags": "health", + "readme": "README.md", + "supports": ["BANGLEJS","BANGLEJS2"], + "storage": [ + {"name":"coretemp.wid.js","url":"widget.js"}, + {"name":"coretemp.app.js","url":"coretemp.js"}, + {"name":"coretemp.settings.js","url":"settings.js"}, + {"name":"coretemp.img","url":"coretemp-icon.js","evaluate":true}, + {"name":"coretemp.boot.js","url":"boot.js"} + ], + "data": [{"name":"coretemp.json","url":"app-settings.json"}], + "screenshots": [{"url":"screenshot.png"}] + }, + { + "id": "showimg", + "name": "simple image viewer", + "shortName":"showImage", + "version":"0.2", + "description": "Displays the image in \"showimg.user.img\". The file has to be uploaded via the espruino IDE. Returns to watch face after 60s or button push. I use it to display my vaccination certificate.", + "icon": "app.png", + "tags": "tool", + "supports" : ["BANGLEJS2"], + "storage": [ + {"name":"showimg.app.js","url":"app.js"}, + {"name":"showimg.img","url":"app-icon.js","evaluate":true} + ] + }, + { + "id": "lapcounter", + "name": "Lap Counter", + "version": "0.01", + "description": "Click button to count laps. Shows count and total time snapshot (like a stopwatch, but laid back).", + "icon": "app.png", + "screenshots": [{"url":"screenshot.png"}], + "type": "app", + "tags": "tool,outdoors", + "readme":"README.md", + "supports": ["BANGLEJS", "BANGLEJS2"], + "allow_emulator": true, + "storage": [ + {"name":"lapcounter.app.js","url":"app.js"}, + {"name":"lapcounter.img","url":"app-icon.js","evaluate":true} + ] + }, + { + "id": "pebbled", + "name": "Pebble Clock with distance", + "shortName": "Pebble + distance", + "version": "0.1", + "description": "Fork of Pebble Clock with distance in KM. Both step count and the distance are on the main screen. Default step length = 0.75m (can be changed in settings).", + "readme": "README.md", + "icon": "pebbled.png", + "screenshots": [{"url":"pebble_screenshot.png"}], + "type": "clock", + "tags": "clock,distance", + "supports": ["BANGLEJS2"], + "storage": [ + {"name":"pebbled.app.js","url":"pebbled.app.js"}, + {"name":"pebbled.settings.js","url":"pebbled.settings.js"}, + {"name":"pebbled.img","url":"pebbled.icon.js","evaluate":true} + ] + }, + { "id": "circlesclock", + "name": "Circles clock", + "shortName":"Circles clock", + "version":"0.03", + "description": "A clock with circles for different data at the bottom in a probably familiar style", + "icon": "app.png", + "screenshots": [{"url":"screenshot.png"}], + "dependencies": {"widpedom":"app"}, + "type": "clock", + "tags": "clock", + "supports" : ["BANGLEJS2"], + "allow_emulator":true, + "readme": "README.md", + "storage": [ + {"name":"circlesclock.app.js","url":"app.js"}, + {"name":"circlesclock.img","url":"app-icon.js","evaluate":true}, + {"name":"circlesclock.settings.js","url":"settings.js"} + ], + "data": [ + {"name":"circlesclock.json"} + ] + }, + { "id": "contourclock", + "name": "Contour Clock", + "shortName" : "Contour Clock", + "version":"0.01", + "icon": "app.png", + "description": "A Minimalist clockface with large Digits. Looks best with the dark theme", + "screenshots" : [{"url":"screenshot.png"}], + "tags": "clock", + "allow_emulator":true, + "supports" : ["BANGLEJS2"], + "type": "clock", + "storage": [ + {"name":"contourclock.app.js","url":"app.js"}, + {"name":"contourclock.img","url":"app-icon.js","evaluate":true} + ] + }, + { + "id": "ltherm", + "name": "Localized Thermometer", + "shortName": "Thermometer", + "version": "0.01", + "description": "Displays the current temperature in localized units.", + "icon": "thermf.png", + "tags": "tool", + "supports": ["BANGLEJS2"], + "allow_emulator": true, + "readme": "README.md", + "storage": [ + {"name":"ltherm.app.js","url":"app.js"}, + {"name":"ltherm.img","url":"icon.js","evaluate":true} + ] + }, + { + "id": "slash", + "name": "Slash Watch", + "shortName":"Slash", + "icon": "slash.png", + "screenshots": [{"url":"screenshot.png"}], + "version":"0.01", + "description": "Slash Watch based on Pebble watch face by Nikki.", + "tags": "clock", + "type": "clock", + "supports":["BANGLEJS2"], + "readme": "README.md", + "allow_emulator": true, + "storage": [ + {"name":"slash.app.js","url":"app.js"}, + {"name":"slash.img","url":"app-icon.js","evaluate":true} + ] + }, + { + "id": "promenu", + "name": "Pro Menu", + "version": "0.01", + "description": "Replace Bangle.js 1's built in menu function.", + "icon": "icon.png", + "type": "boot", + "tags": "system", + "supports": ["BANGLEJS"], + "screenshots": [{"url":"pro-menu-screenshot.png"}], + "storage": [ + {"name":"promenu.boot.js","url":"boot.js"}, + {"name":"promenu.img","url":"promenuIcon.js","evaluate":true} + ] + }, + { + "id": "touchtimer", + "name": "Touch Timer", + "shortName": "Touch Timer", + "version": "0.02", + "description": "Quickly and easily create a timer with touch-only input. The time can be easily set with a number pad.", + "icon": "app.png", + "tags": "tools", + "supports": ["BANGLEJS2"], + "readme": "README.md", + "screenshots": [{"url":"0_light_timer_edit.png"},{"url":"1_light_timer_ready.png"},{"url":"2_light_timer_running.png"},{"url":"3_light_timer_finished.png"}], + "storage": [ + { "name": "touchtimer.app.js", "url": "app.js" }, + { "name":"touchtimer.settings.js", "url":"settings.js"}, + { "name": "touchtimer.img", "url": "app-icon.js", "evaluate": true } + ], + "data": [{"name":"touchtimer.data.json"}] + }, + { + "id": "teatimer", + "name": "Tea Timer", + "version": "1.00", + "description": "A simple timer. You can easyly set up the time.", + "icon": "teatimer.png", + "type": "app", + "tags": "tool", + "supports": ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"teatimer.app.js","url":"app.js"}, + {"name":"teatimer.img","url":"app-icon.js","evaluate":true} + ], + "screenshots": [ + {"url":"TeatimerStart.jpg"}, + {"url":"TeatimerHelp.jpg"}, + {"url":"TeatimerRun.jpg"}, + {"url":"TeatimerUp.jpg"} + ] + }, + { + "id": "swp2clk", + "name": "Swipe back to the Clock", + "shortName": "Swipe to Clock", + "version": "0.01", + "description": "Let's you swipe from left to right on any app to return back to the clock face. Please configure in the settings app after installing to activate, since its disabled by default.", + "icon": "app.png", + "type": "boot", + "tags": "tools", + "supports": ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + { "name": "swp2clk.boot.js", "url": "boot.js" }, + {"name":"swp2clk.settings.js","url":"settings.js"} + ], + "data": [{"name":"swp2clk.data.json"}] + }, + { + "id":"colorwheel", + "name":"Color Wheel", + "tags":"app,tool", + "version":"0.01", + "description":"a tappable wheel of good-looking colors", + "readme":"README.md", + "supports":["BANGLEJS2"], + "allow_emulator":true, + "icon":"colorwheel.png", + "storage": [ + {"name":"colorwheel.app.js","url":"app.js"}, + {"name":"colorwheel.img","url":"app-icon.js","evaluate":true} + ] + }, + { "id": "minimal_clock", + "name": "Minimal Analog Clock", + "shortName":"Minimal Clock", + "version":"0.03", + "description": "a minimal analog clock - just with some hands and no clock face", + "icon": "app-icon.png", + "type": "clock", + "tags": "clock", + "supports" : ["BANGLEJS2"], + "allow_emulator": true, + "screenshots": [{"url":"app-screenshot.png"}], + "readme": "README.md", + "storage": [ + {"name":"minimal_clock.app.js","url":"app.js"}, + {"name":"minimal_clock.img","url":"app-icon.js","evaluate":true} + ] + }, + { "id": "simple_clock", + "name": "Simple Analog Clock", + "shortName":"Simple Clock", + "version":"0.02", + "description": "a simple, yet stylish, analog clock", + "icon": "app-icon.png", + "type": "clock", + "tags": "clock", + "supports" : ["BANGLEJS2"], + "allow_emulator": true, + "screenshots": [{"url":"app-screenshot.png"}], + "readme": "README.md", + "storage": [ + {"name":"simple_clock.app.js","url":"app.js"}, + {"name":"simple_clock.img","url":"app-icon.js","evaluate":true} + ] + }, + { "id": "colorful_clock", + "name": "Colorful Analog Clock", + "shortName":"Colorful Clock", + "version":"0.02", + "description": "a colorful analog clock", + "icon": "app-icon.png", + "type": "clock", + "tags": "clock", + "supports" : ["BANGLEJS2"], + "allow_emulator": true, + "screenshots": [{"url":"app-screenshot.png"}], + "readme": "README.md", + "storage": [ + {"name":"colorful_clock.app.js","url":"app.js"}, + {"name":"colorful_clock.img","url":"app-icon.js","evaluate":true} + ] + }, + { "id": "themesetter", + "name": "Theme Setter", + "shortName":"Theme Setter", + "version":"0.04", + "description": "a comfortable way to configure theme colors", + "icon": "app-icon.png", + "type": "app", + "tags": "tool", + "supports" : ["BANGLEJS2"], + "allow_emulator": true, + "screenshots": [{"url":"app-screenshot.png"}], + "readme": "README.md", + "storage": [ + {"name":"themesetter.app.js","url":"app.js"}, + {"name":"themesetter.img","url":"app-icon.js","evaluate":true} + ] + }, + { + "id": "widviztime", + "name": "Widget Autohide Widget", + "shortName": "Viz Time Widget", + "version": "0.01", + "description": "The widgets will be shown for four seconds after the device is unlocked.", + "icon": "eye.png", + "type": "widget", + "tags": "widget", + "readme":"README.md", + "supports": ["BANGLEJS","BANGLEJS2"], + "storage": [ + {"name":"widviztime.wid.js","url":"widget.js"} + ] + }, + { + "id": "supf", + "name": "Simple Clock with Date", + "shortName": "supf Clock", + "version": "0.01", + "description": "Simple Clock with seconds and date in custom language. Install 'Languages' to get localized names.", + "icon": "icon.png", + "screenshots": [{"url":"screenshot_supf.png"}], + "type": "clock", + "tags": "clock", + "supports": ["BANGLEJS2"], + "allow_emulator": true, + "readme": "README.md", + "storage": [ + {"name":"supf.app.js","url":"app.js"}, + {"name":"supf.img","url":"icon.js","evaluate":true} + ] + }, + { "id": "andark", + "name": "Analog Dark", + "shortName":"AnDark", + "version":"0.04", + "description": "analog clock face without disturbing widgets", + "icon": "andark_icon.png", + "type": "clock", + "tags": "clock", + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"andark.app.js","url":"app.js"}, + {"name":"andark.img","url":"app_icon.js","evaluate":true} + ] + }, + { + "id": "diract", + "name": "DirAct", + "shortName": "DirAct", + "version": "0.01", + "description": "Proximity interaction detection.", + "icon": "diract.png", + "type": "app", + "tags": "tool,sensors", + "supports" : [ "BANGLEJS2" ], + "allow_emulator": false, + "readme": "README.md", + "storage": [ + { "name": "diract.app.js", "url": "diract.js" }, + { "name": "diract.img", "url": "diract-icon.js", "evaluate": true } + ] + } ] diff --git a/apps/alarm/alarm.js b/apps/alarm/alarm.js index bb5722106..a655dad1e 100644 --- a/apps/alarm/alarm.js +++ b/apps/alarm/alarm.js @@ -21,8 +21,8 @@ function showAlarm(alarm) { Bangle.loadWidgets(); Bangle.drawWidgets(); E.showPrompt(msg,{ - title:alarm.timer ? "TIMER!" : "ALARM!", - buttons : {"Sleep":true,"Ok":false} // default is sleep so it'll come back in 10 mins + title:alarm.timer ? /*LANG*/"TIMER!" : /*LANG*/"ALARM!", + buttons : {/*LANG*/"Sleep":true,/*LANG*/"Ok":false} // default is sleep so it'll come back in 10 mins }).then(function(sleep) { buzzCount = 0; if (sleep) { diff --git a/apps/alarm/app.js b/apps/alarm/app.js index 53c7154bc..17062d44a 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -33,16 +33,16 @@ function getCurrentHr() { function showMainMenu() { const menu = { '': { 'title': 'Alarm/Timer' }, - '< Back' : ()=>{load();}, - 'New Alarm': ()=>editAlarm(-1), - 'New Timer': ()=>editTimer(-1) + /*LANG*/'< Back' : ()=>{load();}, + /*LANG*/'New Alarm': ()=>editAlarm(-1), + /*LANG*/'New Timer': ()=>editTimer(-1) }; alarms.forEach((alarm,idx)=>{ if (alarm.timer) { - txt = "TIMER "+(alarm.on?"on ":"off ")+formatMins(alarm.timer); + txt = /*LANG*/"TIMER "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatMins(alarm.timer); } else { - txt = "ALARM "+(alarm.on?"on ":"off ")+formatTime(alarm.hr); - if (alarm.rp) txt += " (repeat)"; + txt = /*LANG*/"ALARM "+(alarm.on?/*LANG*/"on ":/*LANG*/"off ")+formatTime(alarm.hr); + if (alarm.rp) txt += /*LANG*/" (repeat)"; } menu[txt] = function() { if (alarm.timer) editTimer(idx); @@ -70,27 +70,27 @@ function editAlarm(alarmIndex) { as = a.as; } const menu = { - '': { 'title': 'Alarm' }, - '< Back' : showMainMenu, - 'Hours': { + '': { 'title': /*LANG*/'Alarm' }, + /*LANG*/'< Back' : showMainMenu, + /*LANG*/'Hours': { value: hrs, onchange: function(v){if (v<0)v=23;if (v>23)v=0;hrs=v;this.value=v;} // no arrow fn -> preserve 'this' }, - 'Minutes': { + /*LANG*/'Minutes': { value: mins, onchange: function(v){if (v<0)v=59;if (v>59)v=0;mins=v;this.value=v;} // no arrow fn -> preserve 'this' }, - 'Enabled': { + /*LANG*/'Enabled': { value: en, format: v=>v?"On":"Off", onchange: v=>en=v }, - 'Repeat': { + /*LANG*/'Repeat': { value: en, format: v=>v?"Yes":"No", onchange: v=>repeat=v }, - 'Auto snooze': { + /*LANG*/'Auto snooze': { value: as, format: v=>v?"Yes":"No", onchange: v=>as=v @@ -108,14 +108,14 @@ function editAlarm(alarmIndex) { last : day, rp : repeat, as: as }; } - menu["> Save"] = function() { + menu[/*LANG*/"> Save"] = function() { if (newAlarm) alarms.push(getAlarm()); else alarms[alarmIndex] = getAlarm(); require("Storage").write("alarm.json",JSON.stringify(alarms)); showMainMenu(); }; if (!newAlarm) { - menu["> Delete"] = function() { + menu[/*LANG*/"> Delete"] = function() { alarms.splice(alarmIndex,1); require("Storage").write("alarm.json",JSON.stringify(alarms)); showMainMenu(); @@ -136,18 +136,18 @@ function editTimer(alarmIndex) { en = a.on; } const menu = { - '': { 'title': 'Timer' }, - 'Hours': { + '': { 'title': /*LANG*/'Timer' }, + /*LANG*/'Hours': { value: hrs, onchange: function(v){if (v<0)v=23;if (v>23)v=0;hrs=v;this.value=v;} // no arrow fn -> preserve 'this' }, - 'Minutes': { + /*LANG*/'Minutes': { value: mins, onchange: function(v){if (v<0)v=59;if (v>59)v=0;mins=v;this.value=v;} // no arrow fn -> preserve 'this' }, - 'Enabled': { + /*LANG*/'Enabled': { value: en, - format: v=>v?"On":"Off", + format: v=>v?/*LANG*/"On":/*LANG*/"Off", onchange: v=>en=v } }; diff --git a/apps/alarm/boot.js b/apps/alarm/boot.js index 47dae5361..dffb3a37f 100644 --- a/apps/alarm/boot.js +++ b/apps/alarm/boot.js @@ -7,7 +7,7 @@ active = active.sort((a,b)=>(a.hr-b.hr)+(a.last-b.last)*24); var hr = time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600); if (!require('Storage').read("alarm.js")) { - console.log("No alarm app!"); + console.log(/*LANG*/"No alarm app!"); require('Storage').write('alarm.json',"[]"); } else { var t = 3600000*(active[0].hr-hr); diff --git a/apps/andark/ChangeLog b/apps/andark/ChangeLog new file mode 100644 index 000000000..341868930 --- /dev/null +++ b/apps/andark/ChangeLog @@ -0,0 +1,4 @@ +0.01: Release +0.02: Rename app +0.03: Add type "clock" +0.04: changed update cylce, when locked diff --git a/apps/andark/README.md b/apps/andark/README.md new file mode 100644 index 000000000..3770c1017 --- /dev/null +++ b/apps/andark/README.md @@ -0,0 +1,10 @@ +# Analog Clock + +## Features + +* second hand +* date +* battery percantage +* no widgets + +![logo](andark_screen.png) diff --git a/apps/andark/andark_icon.png b/apps/andark/andark_icon.png new file mode 100644 index 000000000..cded02071 Binary files /dev/null and b/apps/andark/andark_icon.png differ diff --git a/apps/andark/andark_screen.png b/apps/andark/andark_screen.png new file mode 100644 index 000000000..2ac54c1cd Binary files /dev/null and b/apps/andark/andark_screen.png differ diff --git a/apps/andark/app.js b/apps/andark/app.js new file mode 100644 index 000000000..efa00ce6f --- /dev/null +++ b/apps/andark/app.js @@ -0,0 +1,125 @@ +const c={"x":g.getWidth()/2,"y":g.getHeight()/2}; +let zahlpos=[]; +let unlock = false; + +function zeiger(len,dia,tim){ + const x =c.x+ Math.cos(tim)*len/2, + y =c.y + Math.sin(tim)*len/2, + d={"d":3,"x":dia/2*Math.cos(tim+Math.PI/2),"y":dia/2*Math.sin(tim+Math.PI/2)}, + pol=[c.x-d.x,c.y-d.y,c.x+d.x,c.y+d.y,x+d.x,y+d.y,x-d.x,y-d.y]; + return pol; + +} + +function draw(){ + const d=new Date(); + let m=d.getMinutes(), h=d.getHours(), s=d.getSeconds(); + //draw black rectangle in the middle to clear screen from scale and hands + g.setColor(0,0,0); + g.fillRect(10,10,2*c.x-10,2*c.x-10); + g.setColor(1,1,1); + + if(h>12){ + h=h-12; + } + //calculates the position of the minute, second and hour hand + h=2*Math.PI/12*(h+m/60)-Math.PI/2; + //more accurate + //m=2*Math.PI/60*(m+s/60)-Math.PI/2; + m=2*Math.PI/60*(m)-Math.PI/2; + + s=2*Math.PI/60*s-Math.PI/2; + g.setFontAlign(0,0); + g.setFont("Vector",10); + let dateStr = " "+require("locale").date(d)+" "; + g.drawString(dateStr, c.x, c.y+20, true); + // g.drawString(d.getDate(),1.4*c.x,c.y,true); + g.drawString(Math.round(E.getBattery()/5)*5+"%",c.x,c.y+40,true); + drawlet(); + //g.setColor(1,0,0); + const hz = zeiger(100,5,h); + g.fillPoly(hz,true); + // g.setColor(1,1,1); + const minz = zeiger(150,5,m); + g.fillPoly(minz,true); + if (unlock){ + const sekz = zeiger(150,2,s); + g.fillPoly(sekz,true); + } + g.fillCircle(c.x,c.y,4); + + + +} +//draws the scale once the app is startet +function drawScale(){ + for(let i=-14;i<47;i++){ + const win=i*2*Math.PI/60; + let d=2; + if(i%5==0){d=5;} + g.fillPoly(zeiger(300,d,win),true); + g.setColor(0,0,0); + g.fillRect(10,10,2*c.x-10,2*c.x-10); + g.setColor(1,1,1); + } +} + +//draws the numbers on the screen + +function drawlet(){ + g.setFont("Vector",20); + for(let i = 0;i<12;i++){ + g.drawString(zahlpos[i][0],zahlpos[i][1],zahlpos[i][2]); + } +} +//calcultes the Position of the numbers when app starts and saves them in an array +function setlet(){ + let sk=1; + for(let i=-10;i<50;i+=5){ + let win=i*2*Math.PI/60; + let xsk =c.x+2+Math.cos(win)*(c.x-10), + ysk =c.y+2+Math.sin(win)*(c.x-10); + if(sk==3){xsk-=10;} + if(sk==6){ysk-=10;} + if(sk==9){xsk+=10;} + if(sk==12){ysk+=10;} + if(sk==10){xsk+=3;} + zahlpos.push([sk,xsk,ysk]); + sk+=1; + } +} +setlet(); +// Clear the screen once, at startup +g.setBgColor(0,0,0); +g.clear(); +drawScale(); +draw(); + +let secondInterval= setInterval(draw, 1000); +// Stop updates when LCD is off, restart when on + +Bangle.on('lcdPower',on=>{ + if (secondInterval) clearInterval(secondInterval); + secondInterval = undefined; + if (on) { + secondInterval = setInterval(draw, 1000); + draw(); // draw immediately + }else{ + } +}); +Bangle.on('lock',on=>{ + if (secondInterval) clearInterval(secondInterval); + secondInterval = undefined; + if (!on) { + secondInterval = setInterval(draw, 1000); + unlock = true; + draw(); // draw immediately + }else{ + secondInterval = setInterval(draw, 60000); + unlock = false; + draw(); + } + }); + +// Show launcher when middle button pressed +Bangle.setUI("clock"); diff --git a/apps/andark/app_icon.js b/apps/andark/app_icon.js new file mode 100644 index 000000000..b213fe5c8 --- /dev/null +++ b/apps/andark/app_icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwgIEBoUAiAKCgUCBQUEColEAYUQhAmKCwgeCAAcCgEDjwEBkEAg8TBocNgYFDh8GAYMDxkPjEA8EAwkHJgIcBAoPfAoYWCBYYFIgfvAoX4FYRJEAp9gAomYNAOAArPwAogAC4AFiRoIFJLgIFJuADCg//Q4U//4FDj4FEAAV4Aoi0CSxBsCA==")) \ No newline at end of file diff --git a/apps/android/ChangeLog b/apps/android/ChangeLog index 35fa0e386..c2c4ea6be 100644 --- a/apps/android/ChangeLog +++ b/apps/android/ChangeLog @@ -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 diff --git a/apps/android/boot.js b/apps/android/boot.js index 97e3a5641..59ffe006d 100644 --- a/apps/android/boot.js +++ b/apps/android/boot.js @@ -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? }; })(); diff --git a/apps/authentiwatch/ChangeLog b/apps/authentiwatch/ChangeLog index e1b8ed5bc..7a902a731 100644 --- a/apps/authentiwatch/ChangeLog +++ b/apps/authentiwatch/ChangeLog @@ -1,4 +1,4 @@ -0.04: Fix tapping at very bottom of list, exit on inactivity -0.03: Add "Calculating" placeholder, update JSON save format -0.02: Fix JSON save format 0.01: First release +0.02: Fix JSON save format +0.03: Add "Calculating" placeholder, update JSON save format +0.04: Fix tapping at very bottom of list, exit on inactivity diff --git a/apps/awairmonitor/ChangeLog b/apps/awairmonitor/ChangeLog new file mode 100644 index 000000000..71d6399c4 --- /dev/null +++ b/apps/awairmonitor/ChangeLog @@ -0,0 +1,3 @@ +0.01: Beta version for Bangle 2 paired with Chrome (2021/12/11) +0.02: The app is now a clock, the data is greyed after the connection is lost (2021/12/22) +0.03: Set the Awair's IP directly on the webpage (2021/12/27) diff --git a/apps/awairmonitor/README.md b/apps/awairmonitor/README.md new file mode 100644 index 000000000..f4c7c42c4 --- /dev/null +++ b/apps/awairmonitor/README.md @@ -0,0 +1,21 @@ +# 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 retrieve the data from your Awair device and sent it to your BangleJS 2 through Chrome's Bluetooth LE connection +* How to get started + * Launch the Awair Monitor app on your BangleJS + * Open awair_to_bangle.html on Chrome (desktop or Android), input the IP address of your Awair device, 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 + +![](screenshot.png) + +![](awair-monitor-photo.jpg) + +## Creator +[@alainsaas](https://github.com/alainsaas) + +Contributions are welcome, send me your Pull Requests! diff --git a/apps/awairmonitor/app-icon.js b/apps/awairmonitor/app-icon.js new file mode 100644 index 000000000..9d4dcf4a3 --- /dev/null +++ b/apps/awairmonitor/app-icon.js @@ -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=")) diff --git a/apps/awairmonitor/app.js b/apps/awairmonitor/app.js new file mode 100644 index 000000000..9123a9c2c --- /dev/null +++ b/apps/awairmonitor/app.js @@ -0,0 +1,108 @@ +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; + +var display_frozen = false; + +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); + + if (last_update != bt_last_update) { + display_frozen = false; + 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) && (internal_last_update < last_update + 60)) { + g.drawString("Trying to reconnect since " + (internal_last_update - last_update), 88, 164); + } else if (internal_last_update > last_update + 5) { + display_frozen = true; + g.drawString("Waiting for connection", 88, 164); + } + + if (display_frozen) { g.setColor("#888"); } + + 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); + + for (i = 0; i < 10; i++) { + if (display_frozen) { g.setColor("#888"); } + + // 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 +Bangle.setUI("clock"); +require("FontHaxorNarrow7x17").add(Graphics); +g.clear(); +Bangle.loadWidgets(); +Bangle.drawWidgets(); +draw(); diff --git a/apps/awairmonitor/app.png b/apps/awairmonitor/app.png new file mode 100644 index 000000000..26a5d0cff Binary files /dev/null and b/apps/awairmonitor/app.png differ diff --git a/apps/awairmonitor/awair-monitor-photo.jpg b/apps/awairmonitor/awair-monitor-photo.jpg new file mode 100644 index 000000000..8b62faa24 Binary files /dev/null and b/apps/awairmonitor/awair-monitor-photo.jpg differ diff --git a/apps/awairmonitor/awair_to_bangle.html b/apps/awairmonitor/awair_to_bangle.html new file mode 100644 index 000000000..69c52499f --- /dev/null +++ b/apps/awairmonitor/awair_to_bangle.html @@ -0,0 +1,673 @@ + + + + + + + + + + +

+How to use +

+Step 1: Enable the Local API on your Awair: https://support.getawair.com/hc/en-us/articles/360049221014-Awair-Local-API-Feature +

+Step 2: Launch the Awair Monitor app on your BangleJS +

+Step 3: Input your Awair IP address and click the Connect button: + + +

+Step 4: Optionally, open the web inspector's console (Right click > Inspector > Console) to read the Bluetooth logs +

+Step 5: Once you are done, click the Disconnect button to properly close the Blutooth connection +

+

+ +

+ +
+
+
+
+
+ +(buf)); + } + + function str2ab(str) { + var buf = new ArrayBuffer(str.length); + var bufView = new Uint8Array(buf); + for (var i=0, strLen=str.length; i Device UUIDs: ' + device.uuids.join('\n' + ' '.repeat(21))); + device.addEventListener('gattserverdisconnected', function() { + log(1, "Disconnected (gattserverdisconnected)"); + connection.close(); + }); + connection.device = device; + connection.reconnect(callback); + }).catch(function(error) { + log(1, 'ERROR: ' + error); + connection.close(); + }); + + connection.reconnect = function(callback) { + connection.device.gatt.connect().then(function(server) { + log(1, "Connected"); + btServer = server; + return server.getPrimaryService(NORDIC_SERVICE); + }).then(function(service) { + log(2, "Got service"); + btService = service; + return btService.getCharacteristic(NORDIC_RX); + }).then(function (characteristic) { + rxCharacteristic = characteristic; + log(2, "RX characteristic:"+JSON.stringify(rxCharacteristic)); + rxCharacteristic.addEventListener('characteristicvaluechanged', function(event) { + var dataview = event.target.value; + var data = ab2str(dataview.buffer); + if (data.length > chunkSize) { + log(2, "Received packet of length "+data.length+", increasing chunk size"); + chunkSize = data.length; + } + if (puck.flowControl) { + for (var i=0;i pause upload"); + flowControlXOFF = true; + } else if (ch==17) {// XON + log(2,"XON received => resume upload"); + flowControlXOFF = false; + } else + remove = false; + if (remove) { // remove character + data = data.substr(0,i-1)+data.substr(i+1); + i--; + } + } + } + log(3, "Received "+JSON.stringify(data)); + connection.emit('data', data); + }); + return rxCharacteristic.startNotifications(); + }).then(function() { + return btService.getCharacteristic(NORDIC_TX); + }).then(function (characteristic) { + txCharacteristic = characteristic; + log(2, "TX characteristic:"+JSON.stringify(txCharacteristic)); + }).then(function() { + connection.txInProgress = false; + connection.isOpen = true; + connection.isOpening = false; + isBusy = false; + queue = []; + callback(connection); + connection.emit('open'); + // if we had any writes queued, do them now + connection.write(); + }).catch(function(error) { + log(1, 'ERROR: ' + error); + connection.close(); + }); + }; + + return connection; + }; + + // ---------------------------------------------------------- + var connection; + /* convenience function... Write data, call the callback with data: + callbackNewline = false => if no new data received for ~0.2 sec + callbackNewline = true => after a newline */ + function write(data, callback, callbackNewline) { + if (!checkIfSupported()) return; + + let result; + /// If there wasn't a callback function, then promisify + if (typeof callback !== 'function') { + callbackNewline = callback; + + result = new Promise((resolve, reject) => callback = (value, err) => { + if (err) reject(err); + else resolve(value); + }); + } + + if (isBusy) { + log(3, "Busy - adding Puck.write to queue"); + queue.push({type:"write", data:data, callback:callback, callbackNewline:callbackNewline}); + return result; + } + + var cbTimeout; + function onWritten() { + if (callbackNewline) { + connection.cb = function(d) { + var newLineIdx = connection.received.indexOf("\n"); + if (newLineIdx>=0) { + var l = connection.received.substr(0,newLineIdx); + connection.received = connection.received.substr(newLineIdx+1); + connection.cb = undefined; + if (cbTimeout) clearTimeout(cbTimeout); + cbTimeout = undefined; + if (callback) + callback(l); + isBusy = false; + handleQueue(); + } + }; + } + // wait for any received data if we have a callback... + var maxTime = 300; // 30 sec - Max time we wait in total, even if getting data + var dataWaitTime = callbackNewline ? 100/*10 sec if waiting for newline*/ : 3/*300ms*/; + var maxDataTime = dataWaitTime; // max time we wait after having received data + cbTimeout = setTimeout(function timeout() { + cbTimeout = undefined; + if (maxTime) maxTime--; + if (maxDataTime) maxDataTime--; + if (connection.hadData) maxDataTime=dataWaitTime; + if (maxDataTime && maxTime) { + cbTimeout = setTimeout(timeout, 100); + } else { + connection.cb = undefined; + if (callback) + callback(connection.received); + isBusy = false; + handleQueue(); + connection.received = ""; + } + connection.hadData = false; + }, 100); + } + + if (connection && (connection.isOpen || connection.isOpening)) { + if (!connection.txInProgress) connection.received = ""; + isBusy = true; + connection.write(data, onWritten); + return result + } + + connection = connect(function(puck) { + if (!puck) { + connection = undefined; + if (callback) callback(null); + return; + } + connection.received = ""; + connection.on('data', function(d) { + connection.received += d; + connection.hadData = true; + if (connection.cb) connection.cb(d); + }); + connection.on('close', function(d) { + connection = undefined; + }); + isBusy = true; + connection.write(data, onWritten); + }); + + return result + } + + // ---------------------------------------------------------- + + var puck = { + /// Are we writing debug information? 0 is no, 1 is some, 2 is more, 3 is all. + debug : 1, + /// Should we use flow control? Default is true + flowControl : true, + /// Used internally to write log information - you can replace this with your own function + log : function(level, s) { if (level <= this.debug) console.log(" "+s)}, + /// Called with the current send progress or undefined when done - you can replace this with your own function + writeProgress : function (charsSent, charsTotal) { + //console.log(charsSent + "/" + charsTotal); + }, + /** Connect to a new device - this creates a separate + connection to the one `write` and `eval` use. */ + connect : connect, + /// Write to Puck.js and call back when the data is written. Creates a connection if it doesn't exist + write : write, + /// Evaluate an expression and call cb with the result. Creates a connection if it doesn't exist + eval : function(expr, cb) { + + const response = write('\x10Bluetooth.println(JSON.stringify(' + expr + '))\n', true) + .then(function (d) { + try { + return JSON.parse(d); + } catch (e) { + log(1, "Unable to decode " + JSON.stringify(d) + ", got " + e.toString()); + return Promise.reject(d); + } + }); + + + if (cb) { + return void response.then(cb, (err) => cb(null, err)); + } else { + return response; + } + + }, + /// Write the current time to the Puck + setTime : function(cb) { + var d = new Date(); + var cmd = 'setTime('+(d.getTime()/1000)+');'; + // in 1v93 we have timezones too + cmd += 'if (E.setTimeZone) E.setTimeZone('+d.getTimezoneOffset()/-60+');\n'; + write(cmd, cb); + }, + /// Did `write` and `eval` manage to create a connection? + isConnected : function() { + return connection!==undefined; + }, + /// get the connection used by `write` and `eval` + getConnection : function() { + return connection; + }, + /// Close the connection used by `write` and `eval` + close : function() { + if (connection) + connection.close(); + }, + /** Utility function to fade out everything on the webpage and display + a window saying 'Click to continue'. When clicked it'll disappear and + 'callback' will be called. This is useful because you can't initialise + Web Bluetooth unless you're doing so in response to a user input.*/ + modal : function(callback) { + var e = document.createElement('div'); + e.style = 'position:absolute;top:0px;left:0px;right:0px;bottom:0px;opacity:0.5;z-index:100;background:black;'; + e.innerHTML = '
Click to Continue...
'; + e.onclick = function(evt) { + callback(); + evt.preventDefault(); + document.body.removeChild(e); + }; + document.body.appendChild(e); + } + }; + return puck; +})); + + + + - + + diff --git a/apps/qrcode/qr-scanner-worker.min.js b/apps/qrcode/qr-scanner-worker.min.js new file mode 100644 index 000000000..0d026ac07 --- /dev/null +++ b/apps/qrcode/qr-scanner-worker.min.js @@ -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`%${("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;gk?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()=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;gf)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);6d;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)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=a?d:null}function ea(a,b,c){let d=b.errorCorrectionLevels[c],e=[],f=0;d.ecBlocks.forEach(a=>{for(let b=0;ba+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{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;ea+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{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)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)&&.5c>=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)&&.5a.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;ba.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{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>8)}else for(l=0;lt;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,0a?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;fa||32this.available())throw Error("Cannot read "+a.toString()+" bits");var b=0;if(0>8-c<>b;a-=c;this.bitOffset+=c;8===this.bitOffset&&(this.bitOffset=0,this.byteOffset++)}if(0>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(1a.length&&([b,a]=[a,b]);let c=new Uint8ClampedArray(a.length),d=a.length-b.length;for(var e=0;ea)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{b^=a}),b;b=this.coefficients[0];for(let d=1;d=this.size&&(a=(a^this.primitive)&this.size-1);for(a=0;aa)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 diff --git a/apps/qrcode/qr-scanner-worker.min.js.map b/apps/qrcode/qr-scanner-worker.min.js.map new file mode 100644 index 000000000..dbf29b5cd --- /dev/null +++ b/apps/qrcode/qr-scanner-worker.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"qr-scanner-worker.min.js","sources":["node_modules/jsqr-es6/src/decoder/decodeData/index.ts","node_modules/jsqr-es6/src/decoder/reedsolomon/GenericGF.ts","node_modules/jsqr-es6/src/decoder/reedsolomon/index.ts","node_modules/jsqr-es6/src/decoder/decoder.ts","node_modules/jsqr-es6/src/extractor/index.ts","node_modules/jsqr-es6/src/locator/index.ts","node_modules/jsqr-es6/src/index.ts","node_modules/jsqr-es6/src/binarizer/index.ts","node_modules/jsqr-es6/src/BitMatrix.ts","node_modules/jsqr-es6/src/decoder/decodeData/BitStream.ts","node_modules/jsqr-es6/src/decoder/reedsolomon/GenericGFPoly.ts","node_modules/jsqr-es6/src/decoder/version.ts","src/worker.js"],"sourcesContent":["// tslint:disable:no-bitwise\nimport { BitStream } from \"./BitStream\";\n\nexport interface Chunk {\n type: Mode;\n text: string;\n}\n\nexport interface ByteChunk {\n type: Mode.Byte | Mode.Kanji;\n bytes: number[];\n}\n\nexport interface ECIChunk {\n type: Mode.ECI;\n assignmentNumber: number;\n}\n\nexport interface StructuredAppend {\n type: Mode.StructuredAppend;\n currentSequence: number;\n totalSequence: number;\n parity: number;\n}\n\nexport type Chunks = Array;\n\nexport interface DecodedQR {\n text: string;\n bytes: number[];\n chunks: Chunks;\n version: number;\n}\n\nexport enum Mode {\n Numeric = \"numeric\",\n Alphanumeric = \"alphanumeric\",\n Byte = \"byte\",\n Kanji = \"kanji\",\n ECI = \"eci\",\n StructuredAppend = \"structuredappend\",\n}\n\nenum ModeByte {\n Terminator = 0x0,\n Numeric = 0x1,\n Alphanumeric = 0x2,\n Byte = 0x4,\n Kanji = 0x8,\n ECI = 0x7,\n StructuredAppend = 0x3,\n // FNC1FirstPosition = 0x5,\n // FNC1SecondPosition = 0x9,\n}\n\nfunction decodeNumeric(stream: BitStream, size: number) {\n const bytes: number[] = [];\n let text = \"\";\n\n const characterCountSize = [10, 12, 14][size];\n let length = stream.readBits(characterCountSize);\n // Read digits in groups of 3\n while (length >= 3) {\n const num = stream.readBits(10);\n if (num >= 1000) {\n throw new Error(\"Invalid numeric value above 999\");\n }\n\n const a = Math.floor(num / 100);\n const b = Math.floor(num / 10) % 10;\n const c = num % 10;\n\n bytes.push(48 + a, 48 + b, 48 + c);\n text += a.toString() + b.toString() + c.toString();\n length -= 3;\n }\n\n // If the number of digits aren't a multiple of 3, the remaining digits are special cased.\n if (length === 2) {\n const num = stream.readBits(7);\n if (num >= 100) {\n throw new Error(\"Invalid numeric value above 99\");\n }\n\n const a = Math.floor(num / 10);\n const b = num % 10;\n\n bytes.push(48 + a, 48 + b);\n text += a.toString() + b.toString();\n } else if (length === 1) {\n const num = stream.readBits(4);\n if (num >= 10) {\n throw new Error(\"Invalid numeric value above 9\");\n }\n\n bytes.push(48 + num);\n text += num.toString();\n }\n\n return { bytes, text };\n}\n\nconst AlphanumericCharacterCodes = [\n \"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\",\n \"9\", \"A\", \"B\", \"C\", \"D\", \"E\", \"F\", \"G\", \"H\",\n \"I\", \"J\", \"K\", \"L\", \"M\", \"N\", \"O\", \"P\", \"Q\",\n \"R\", \"S\", \"T\", \"U\", \"V\", \"W\", \"X\", \"Y\", \"Z\",\n \" \", \"$\", \"%\", \"*\", \"+\", \"-\", \".\", \"/\", \":\",\n];\n\nfunction decodeAlphanumeric(stream: BitStream, size: number) {\n const bytes: number[] = [];\n let text = \"\";\n\n const characterCountSize = [9, 11, 13][size];\n let length = stream.readBits(characterCountSize);\n while (length >= 2) {\n const v = stream.readBits(11);\n\n const a = Math.floor(v / 45);\n const b = v % 45;\n\n bytes.push(AlphanumericCharacterCodes[a].charCodeAt(0), AlphanumericCharacterCodes[b].charCodeAt(0));\n text += AlphanumericCharacterCodes[a] + AlphanumericCharacterCodes[b];\n length -= 2;\n }\n\n if (length === 1) {\n const a = stream.readBits(6);\n bytes.push(AlphanumericCharacterCodes[a].charCodeAt(0));\n text += AlphanumericCharacterCodes[a];\n }\n\n return { bytes, text };\n}\n\nfunction decodeByte(stream: BitStream, size: number) {\n const bytes: number[] = [];\n let text = \"\";\n\n const characterCountSize = [8, 16, 16][size];\n const length = stream.readBits(characterCountSize);\n for (let i = 0; i < length; i++) {\n const b = stream.readBits(8);\n bytes.push(b);\n }\n try {\n text += decodeURIComponent(bytes.map(b => `%${(\"0\" + b.toString(16)).substr(-2)}`).join(\"\"));\n } catch {\n // failed to decode\n }\n\n return { bytes, text };\n}\n\nfunction decodeKanji(stream: BitStream, size: number) {\n const bytes: number[] = [];\n\n const characterCountSize = [8, 10, 12][size];\n const length = stream.readBits(characterCountSize);\n for (let i = 0; i < length; i++) {\n const k = stream.readBits(13);\n\n let c = (Math.floor(k / 0xC0) << 8) | (k % 0xC0);\n if (c < 0x1F00) {\n c += 0x8140;\n } else {\n c += 0xC140;\n }\n\n bytes.push(c >> 8, c & 0xFF);\n }\n\n const text = new TextDecoder(\"shift-jis\").decode(Uint8Array.from(bytes));\n return { bytes, text };\n}\n\nexport function decode(data: Uint8ClampedArray, version: number): DecodedQR {\n const stream = new BitStream(data);\n\n // There are 3 'sizes' based on the version. 1-9 is small (0), 10-26 is medium (1) and 27-40 is large (2).\n const size = version <= 9 ? 0 : version <= 26 ? 1 : 2;\n\n const result: DecodedQR = {\n text: \"\",\n bytes: [],\n chunks: [],\n version,\n };\n\n while (stream.available() >= 4) {\n const mode = stream.readBits(4);\n if (mode === ModeByte.Terminator) {\n return result;\n } else if (mode === ModeByte.ECI) {\n if (stream.readBits(1) === 0) {\n result.chunks.push({\n type: Mode.ECI,\n assignmentNumber: stream.readBits(7),\n });\n } else if (stream.readBits(1) === 0) {\n result.chunks.push({\n type: Mode.ECI,\n assignmentNumber: stream.readBits(14),\n });\n } else if (stream.readBits(1) === 0) {\n result.chunks.push({\n type: Mode.ECI,\n assignmentNumber: stream.readBits(21),\n });\n } else {\n // ECI data seems corrupted\n result.chunks.push({\n type: Mode.ECI,\n assignmentNumber: -1,\n });\n }\n } else if (mode === ModeByte.Numeric) {\n const numericResult = decodeNumeric(stream, size);\n result.text += numericResult.text;\n result.bytes.push(...numericResult.bytes);\n result.chunks.push({\n type: Mode.Numeric,\n text: numericResult.text,\n });\n } else if (mode === ModeByte.Alphanumeric) {\n const alphanumericResult = decodeAlphanumeric(stream, size);\n result.text += alphanumericResult.text;\n result.bytes.push(...alphanumericResult.bytes);\n result.chunks.push({\n type: Mode.Alphanumeric,\n text: alphanumericResult.text,\n });\n } else if (mode === ModeByte.Byte) {\n const byteResult = decodeByte(stream, size);\n result.text += byteResult.text;\n result.bytes.push(...byteResult.bytes);\n result.chunks.push({\n type: Mode.Byte,\n bytes: byteResult.bytes,\n text: byteResult.text,\n });\n } else if (mode === ModeByte.Kanji) {\n const kanjiResult = decodeKanji(stream, size);\n result.text += kanjiResult.text;\n result.bytes.push(...kanjiResult.bytes);\n result.chunks.push({\n type: Mode.Kanji,\n bytes: kanjiResult.bytes,\n text: kanjiResult.text,\n });\n } else if (mode === ModeByte.StructuredAppend) {\n result.chunks.push({\n type: Mode.StructuredAppend,\n currentSequence: stream.readBits(4),\n totalSequence: stream.readBits(4),\n parity: stream.readBits(8),\n });\n }\n }\n\n // If there is no data left, or the remaining bits are all 0, then that counts as a termination marker\n if (stream.available() === 0 || stream.readBits(stream.available()) === 0) {\n return result;\n }\n}\n","import GenericGFPoly from \"./GenericGFPoly\";\n\nexport function addOrSubtractGF(a: number, b: number) {\n return a ^ b; // tslint:disable-line:no-bitwise\n}\n\nexport default class GenericGF {\n public primitive: number;\n public size: number;\n public generatorBase: number;\n public zero: GenericGFPoly;\n public one: GenericGFPoly;\n\n private expTable: number[];\n private logTable: number[];\n\n constructor(primitive: number, size: number, genBase: number) {\n this.primitive = primitive;\n this.size = size;\n this.generatorBase = genBase;\n this.expTable = new Array(this.size);\n this.logTable = new Array(this.size);\n\n let x = 1;\n for (let i = 0; i < this.size; i++) {\n this.expTable[i] = x;\n x = x * 2;\n if (x >= this.size) {\n x = (x ^ this.primitive) & (this.size - 1); // tslint:disable-line:no-bitwise\n }\n }\n\n for (let i = 0; i < this.size - 1; i++) {\n this.logTable[this.expTable[i]] = i;\n }\n this.zero = new GenericGFPoly(this, Uint8ClampedArray.from([0]));\n this.one = new GenericGFPoly(this, Uint8ClampedArray.from([1]));\n }\n\n public multiply(a: number, b: number) {\n if (a === 0 || b === 0) {\n return 0;\n }\n return this.expTable[(this.logTable[a] + this.logTable[b]) % (this.size - 1)];\n }\n\n public inverse(a: number) {\n if (a === 0) {\n throw new Error(\"Can't invert 0\");\n }\n return this.expTable[this.size - this.logTable[a] - 1];\n }\n\n public buildMonomial(degree: number, coefficient: number): GenericGFPoly {\n if (degree < 0) {\n throw new Error(\"Invalid monomial degree less than 0\");\n }\n if (coefficient === 0) {\n return this.zero;\n }\n const coefficients = new Uint8ClampedArray(degree + 1);\n coefficients[0] = coefficient;\n return new GenericGFPoly(this, coefficients);\n }\n\n public log(a: number) {\n if (a === 0) {\n throw new Error(\"Can't take log(0)\");\n }\n return this.logTable[a];\n }\n\n public exp(a: number) {\n return this.expTable[a];\n }\n}\n","import GenericGF, { addOrSubtractGF } from \"./GenericGF\";\nimport GenericGFPoly from \"./GenericGFPoly\";\n\nfunction runEuclideanAlgorithm(field: GenericGF, a: GenericGFPoly, b: GenericGFPoly, R: number): GenericGFPoly[] {\n // Assume a's degree is >= b's\n if (a.degree() < b.degree()) {\n [a, b] = [b, a];\n }\n\n let rLast = a;\n let r = b;\n let tLast = field.zero;\n let t = field.one;\n\n // Run Euclidean algorithm until r's degree is less than R/2\n while (r.degree() >= R / 2) {\n const rLastLast = rLast;\n const tLastLast = tLast;\n rLast = r;\n tLast = t;\n\n // Divide rLastLast by rLast, with quotient in q and remainder in r\n if (rLast.isZero()) {\n // Euclidean algorithm already terminated?\n return null;\n }\n r = rLastLast;\n let q = field.zero;\n const denominatorLeadingTerm = rLast.getCoefficient(rLast.degree());\n const dltInverse = field.inverse(denominatorLeadingTerm);\n while (r.degree() >= rLast.degree() && !r.isZero()) {\n const degreeDiff = r.degree() - rLast.degree();\n const scale = field.multiply(r.getCoefficient(r.degree()), dltInverse);\n q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale));\n r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale));\n }\n\n t = q.multiplyPoly(tLast).addOrSubtract(tLastLast);\n\n if (r.degree() >= rLast.degree()) {\n return null;\n }\n }\n\n const sigmaTildeAtZero = t.getCoefficient(0);\n if (sigmaTildeAtZero === 0) {\n return null;\n }\n\n const inverse = field.inverse(sigmaTildeAtZero);\n return [t.multiply(inverse), r.multiply(inverse)];\n}\n\nfunction findErrorLocations(field: GenericGF, errorLocator: GenericGFPoly): number[] {\n // This is a direct application of Chien's search\n const numErrors = errorLocator.degree();\n if (numErrors === 1) {\n return [errorLocator.getCoefficient(1)];\n }\n const result: number[] = new Array(numErrors);\n let errorCount = 0;\n for (let i = 1; i < field.size && errorCount < numErrors; i++) {\n if (errorLocator.evaluateAt(i) === 0) {\n result[errorCount] = field.inverse(i);\n errorCount++;\n }\n }\n if (errorCount !== numErrors) {\n return null;\n }\n return result;\n}\n\nfunction findErrorMagnitudes(field: GenericGF, errorEvaluator: GenericGFPoly, errorLocations: number[]): number[] {\n // This is directly applying Forney's Formula\n const s = errorLocations.length;\n const result: number[] = new Array(s);\n for (let i = 0; i < s; i++) {\n const xiInverse = field.inverse(errorLocations[i]);\n let denominator = 1;\n for (let j = 0; j < s; j++) {\n if (i !== j) {\n denominator = field.multiply(denominator, addOrSubtractGF(1, field.multiply(errorLocations[j], xiInverse)));\n }\n }\n result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse), field.inverse(denominator));\n if (field.generatorBase !== 0) {\n result[i] = field.multiply(result[i], xiInverse);\n }\n }\n return result;\n}\n\nexport function decode(bytes: number[], twoS: number) {\n const outputBytes = new Uint8ClampedArray(bytes.length);\n outputBytes.set(bytes);\n\n const field = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1\n const poly = new GenericGFPoly(field, outputBytes);\n\n const syndromeCoefficients = new Uint8ClampedArray(twoS);\n let error = false;\n for (let s = 0; s < twoS; s++) {\n const evaluation = poly.evaluateAt(field.exp(s + field.generatorBase));\n syndromeCoefficients[syndromeCoefficients.length - 1 - s] = evaluation;\n if (evaluation !== 0) {\n error = true;\n }\n }\n if (!error) {\n return outputBytes;\n }\n\n const syndrome = new GenericGFPoly(field, syndromeCoefficients);\n\n const sigmaOmega = runEuclideanAlgorithm(field, field.buildMonomial(twoS, 1), syndrome, twoS);\n if (sigmaOmega === null) {\n return null;\n }\n\n const errorLocations = findErrorLocations(field, sigmaOmega[0]);\n if (errorLocations == null) {\n return null;\n }\n\n const errorMagnitudes = findErrorMagnitudes(field, sigmaOmega[1], errorLocations);\n for (let i = 0; i < errorLocations.length; i++) {\n const position = outputBytes.length - 1 - field.log(errorLocations[i]);\n if (position < 0) {\n return null;\n }\n outputBytes[position] = addOrSubtractGF(outputBytes[position], errorMagnitudes[i]);\n }\n\n return outputBytes;\n}\n","import { BitMatrix } from \"../BitMatrix\";\nimport { Point } from \"../Point\";\nimport { decode as decodeData, DecodedQR } from \"./decodeData\";\nimport { decode as rsDecode } from \"./reedsolomon\";\nimport { Version, VERSIONS } from \"./version\";\n\n// tslint:disable:no-bitwise\nfunction numBitsDiffering(x: number, y: number) {\n let z = x ^ y;\n let bitCount = 0;\n while (z) {\n bitCount++;\n z &= z - 1;\n }\n return bitCount;\n}\n\nfunction pushBit(bit: any, byte: number) {\n return (byte << 1) | bit;\n}\n// tslint:enable:no-bitwise\n\nconst FORMAT_INFO_TABLE = [\n { bits: 0x5412, formatInfo: { errorCorrectionLevel: 1, dataMask: 0 } },\n { bits: 0x5125, formatInfo: { errorCorrectionLevel: 1, dataMask: 1 } },\n { bits: 0x5E7C, formatInfo: { errorCorrectionLevel: 1, dataMask: 2 } },\n { bits: 0x5B4B, formatInfo: { errorCorrectionLevel: 1, dataMask: 3 } },\n { bits: 0x45F9, formatInfo: { errorCorrectionLevel: 1, dataMask: 4 } },\n { bits: 0x40CE, formatInfo: { errorCorrectionLevel: 1, dataMask: 5 } },\n { bits: 0x4F97, formatInfo: { errorCorrectionLevel: 1, dataMask: 6 } },\n { bits: 0x4AA0, formatInfo: { errorCorrectionLevel: 1, dataMask: 7 } },\n { bits: 0x77C4, formatInfo: { errorCorrectionLevel: 0, dataMask: 0 } },\n { bits: 0x72F3, formatInfo: { errorCorrectionLevel: 0, dataMask: 1 } },\n { bits: 0x7DAA, formatInfo: { errorCorrectionLevel: 0, dataMask: 2 } },\n { bits: 0x789D, formatInfo: { errorCorrectionLevel: 0, dataMask: 3 } },\n { bits: 0x662F, formatInfo: { errorCorrectionLevel: 0, dataMask: 4 } },\n { bits: 0x6318, formatInfo: { errorCorrectionLevel: 0, dataMask: 5 } },\n { bits: 0x6C41, formatInfo: { errorCorrectionLevel: 0, dataMask: 6 } },\n { bits: 0x6976, formatInfo: { errorCorrectionLevel: 0, dataMask: 7 } },\n { bits: 0x1689, formatInfo: { errorCorrectionLevel: 3, dataMask: 0 } },\n { bits: 0x13BE, formatInfo: { errorCorrectionLevel: 3, dataMask: 1 } },\n { bits: 0x1CE7, formatInfo: { errorCorrectionLevel: 3, dataMask: 2 } },\n { bits: 0x19D0, formatInfo: { errorCorrectionLevel: 3, dataMask: 3 } },\n { bits: 0x0762, formatInfo: { errorCorrectionLevel: 3, dataMask: 4 } },\n { bits: 0x0255, formatInfo: { errorCorrectionLevel: 3, dataMask: 5 } },\n { bits: 0x0D0C, formatInfo: { errorCorrectionLevel: 3, dataMask: 6 } },\n { bits: 0x083B, formatInfo: { errorCorrectionLevel: 3, dataMask: 7 } },\n { bits: 0x355F, formatInfo: { errorCorrectionLevel: 2, dataMask: 0 } },\n { bits: 0x3068, formatInfo: { errorCorrectionLevel: 2, dataMask: 1 } },\n { bits: 0x3F31, formatInfo: { errorCorrectionLevel: 2, dataMask: 2 } },\n { bits: 0x3A06, formatInfo: { errorCorrectionLevel: 2, dataMask: 3 } },\n { bits: 0x24B4, formatInfo: { errorCorrectionLevel: 2, dataMask: 4 } },\n { bits: 0x2183, formatInfo: { errorCorrectionLevel: 2, dataMask: 5 } },\n { bits: 0x2EDA, formatInfo: { errorCorrectionLevel: 2, dataMask: 6 } },\n { bits: 0x2BED, formatInfo: { errorCorrectionLevel: 2, dataMask: 7 } },\n];\n\nconst DATA_MASKS = [\n (p: Point) => ((p.y + p.x) % 2) === 0,\n (p: Point) => (p.y % 2) === 0,\n (p: Point) => p.x % 3 === 0,\n (p: Point) => (p.y + p.x) % 3 === 0,\n (p: Point) => (Math.floor(p.y / 2) + Math.floor(p.x / 3)) % 2 === 0,\n (p: Point) => ((p.x * p.y) % 2) + ((p.x * p.y) % 3) === 0,\n (p: Point) => ((((p.y * p.x) % 2) + (p.y * p.x) % 3) % 2) === 0,\n (p: Point) => ((((p.y + p.x) % 2) + (p.y * p.x) % 3) % 2) === 0,\n];\n\ninterface FormatInformation {\n errorCorrectionLevel: number;\n dataMask: number;\n}\n\nfunction buildFunctionPatternMask(version: Version): BitMatrix {\n const dimension = 17 + 4 * version.versionNumber;\n const matrix = BitMatrix.createEmpty(dimension, dimension);\n\n matrix.setRegion(0, 0, 9, 9, true); // Top left finder pattern + separator + format\n matrix.setRegion(dimension - 8, 0, 8, 9, true); // Top right finder pattern + separator + format\n matrix.setRegion(0, dimension - 8, 9, 8, true); // Bottom left finder pattern + separator + format\n\n // Alignment patterns\n for (const x of version.alignmentPatternCenters) {\n for (const y of version.alignmentPatternCenters) {\n if (!(x === 6 && y === 6 || x === 6 && y === dimension - 7 || x === dimension - 7 && y === 6)) {\n matrix.setRegion(x - 2, y - 2, 5, 5, true);\n }\n }\n }\n\n matrix.setRegion(6, 9, 1, dimension - 17, true); // Vertical timing pattern\n matrix.setRegion(9, 6, dimension - 17, 1, true); // Horizontal timing pattern\n\n if (version.versionNumber > 6) {\n matrix.setRegion(dimension - 11, 0, 3, 6, true); // Version info, top right\n matrix.setRegion(0, dimension - 11, 6, 3, true); // Version info, bottom left\n }\n\n return matrix;\n}\n\nfunction readCodewords(matrix: BitMatrix, version: Version, formatInfo: FormatInformation) {\n const dataMask = DATA_MASKS[formatInfo.dataMask];\n const dimension = matrix.height;\n\n const functionPatternMask = buildFunctionPatternMask(version);\n\n const codewords: number[] = [];\n let currentByte = 0;\n let bitsRead = 0;\n\n // Read columns in pairs, from right to left\n let readingUp = true;\n for (let columnIndex = dimension - 1; columnIndex > 0; columnIndex -= 2) {\n if (columnIndex === 6) { // Skip whole column with vertical alignment pattern;\n columnIndex--;\n }\n for (let i = 0; i < dimension; i++) {\n const y = readingUp ? dimension - 1 - i : i;\n for (let columnOffset = 0; columnOffset < 2; columnOffset++) {\n const x = columnIndex - columnOffset;\n if (!functionPatternMask.get(x, y)) {\n bitsRead++;\n let bit = matrix.get(x, y);\n if (dataMask({y, x})) {\n bit = !bit;\n }\n currentByte = pushBit(bit, currentByte);\n if (bitsRead === 8) { // Whole bytes\n codewords.push(currentByte);\n bitsRead = 0;\n currentByte = 0;\n }\n }\n }\n }\n readingUp = !readingUp;\n }\n return codewords;\n}\n\nfunction readVersion(matrix: BitMatrix): Version {\n const dimension = matrix.height;\n\n const provisionalVersion = Math.floor((dimension - 17) / 4);\n if (provisionalVersion <= 6) { // 6 and under dont have version info in the QR code\n return VERSIONS[provisionalVersion - 1];\n }\n\n let topRightVersionBits = 0;\n for (let y = 5; y >= 0; y--) {\n for (let x = dimension - 9; x >= dimension - 11; x--) {\n topRightVersionBits = pushBit(matrix.get(x, y), topRightVersionBits);\n }\n }\n\n let bottomLeftVersionBits = 0;\n for (let x = 5; x >= 0; x--) {\n for (let y = dimension - 9; y >= dimension - 11; y--) {\n bottomLeftVersionBits = pushBit(matrix.get(x, y), bottomLeftVersionBits);\n }\n }\n\n let bestDifference = Infinity;\n let bestVersion: Version;\n for (const version of VERSIONS) {\n if (version.infoBits === topRightVersionBits || version.infoBits === bottomLeftVersionBits) {\n return version;\n }\n\n let difference = numBitsDiffering(topRightVersionBits, version.infoBits);\n if (difference < bestDifference) {\n bestVersion = version;\n bestDifference = difference;\n }\n\n difference = numBitsDiffering(bottomLeftVersionBits, version.infoBits);\n if (difference < bestDifference) {\n bestVersion = version;\n bestDifference = difference;\n }\n }\n // We can tolerate up to 3 bits of error since no two version info codewords will\n // differ in less than 8 bits.\n if (bestDifference <= 3) {\n return bestVersion;\n }\n}\n\nfunction readFormatInformation(matrix: BitMatrix) {\n let topLeftFormatInfoBits = 0;\n for (let x = 0; x <= 8; x++) {\n if (x !== 6) { // Skip timing pattern bit\n topLeftFormatInfoBits = pushBit(matrix.get(x, 8), topLeftFormatInfoBits);\n }\n }\n for (let y = 7; y >= 0; y--) {\n if (y !== 6) { // Skip timing pattern bit\n topLeftFormatInfoBits = pushBit(matrix.get(8, y), topLeftFormatInfoBits);\n }\n }\n\n const dimension = matrix.height;\n let topRightBottomRightFormatInfoBits = 0;\n for (let y = dimension - 1; y >= dimension - 7; y--) { // bottom left\n topRightBottomRightFormatInfoBits = pushBit(matrix.get(8, y), topRightBottomRightFormatInfoBits);\n }\n for (let x = dimension - 8; x < dimension; x++) { // top right\n topRightBottomRightFormatInfoBits = pushBit(matrix.get(x, 8), topRightBottomRightFormatInfoBits);\n }\n\n let bestDifference = Infinity;\n let bestFormatInfo = null;\n for (const {bits, formatInfo} of FORMAT_INFO_TABLE) {\n if (bits === topLeftFormatInfoBits || bits === topRightBottomRightFormatInfoBits) {\n return formatInfo;\n }\n let difference = numBitsDiffering(topLeftFormatInfoBits, bits);\n if (difference < bestDifference) {\n bestFormatInfo = formatInfo;\n bestDifference = difference;\n }\n if (topLeftFormatInfoBits !== topRightBottomRightFormatInfoBits) { // also try the other option\n difference = numBitsDiffering(topRightBottomRightFormatInfoBits, bits);\n if (difference < bestDifference) {\n bestFormatInfo = formatInfo;\n bestDifference = difference;\n }\n }\n }\n // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits differing means we found a match\n if (bestDifference <= 3) {\n return bestFormatInfo;\n }\n return null;\n}\n\nfunction getDataBlocks(codewords: number[], version: Version, ecLevel: number) {\n const ecInfo = version.errorCorrectionLevels[ecLevel];\n const dataBlocks: Array<{\n numDataCodewords: number;\n codewords: number[];\n }> = [];\n\n let totalCodewords = 0;\n ecInfo.ecBlocks.forEach(block => {\n for (let i = 0; i < block.numBlocks; i++) {\n dataBlocks.push({ numDataCodewords: block.dataCodewordsPerBlock, codewords: [] });\n totalCodewords += block.dataCodewordsPerBlock + ecInfo.ecCodewordsPerBlock;\n }\n });\n\n // In some cases the QR code will be malformed enough that we pull off more or less than we should.\n // If we pull off less there's nothing we can do.\n // If we pull off more we can safely truncate\n if (codewords.length < totalCodewords) {\n return null;\n }\n codewords = codewords.slice(0, totalCodewords);\n\n const shortBlockSize = ecInfo.ecBlocks[0].dataCodewordsPerBlock;\n // Pull codewords to fill the blocks up to the minimum size\n for (let i = 0; i < shortBlockSize; i++) {\n for (const dataBlock of dataBlocks) {\n dataBlock.codewords.push(codewords.shift());\n }\n }\n\n // If there are any large blocks, pull codewords to fill the last element of those\n if (ecInfo.ecBlocks.length > 1) {\n const smallBlockCount = ecInfo.ecBlocks[0].numBlocks;\n const largeBlockCount = ecInfo.ecBlocks[1].numBlocks;\n for (let i = 0; i < largeBlockCount; i++) {\n dataBlocks[smallBlockCount + i].codewords.push(codewords.shift());\n }\n }\n\n // Add the rest of the codewords to the blocks. These are the error correction codewords.\n while (codewords.length > 0) {\n for (const dataBlock of dataBlocks) {\n dataBlock.codewords.push(codewords.shift());\n }\n }\n\n return dataBlocks;\n}\n\nfunction decodeMatrix(matrix: BitMatrix) {\n const version = readVersion(matrix);\n if (!version) {\n return null;\n }\n\n const formatInfo = readFormatInformation(matrix);\n if (!formatInfo) {\n return null;\n }\n\n const codewords = readCodewords(matrix, version, formatInfo);\n const dataBlocks = getDataBlocks(codewords, version, formatInfo.errorCorrectionLevel);\n if (!dataBlocks) {\n return null;\n }\n\n // Count total number of data bytes\n const totalBytes = dataBlocks.reduce((a, b) => a + b.numDataCodewords, 0);\n const resultBytes = new Uint8ClampedArray(totalBytes);\n\n let resultIndex = 0;\n for (const dataBlock of dataBlocks) {\n const correctedBytes = rsDecode(dataBlock.codewords, dataBlock.codewords.length - dataBlock.numDataCodewords);\n if (!correctedBytes) {\n return null;\n }\n for (let i = 0; i < dataBlock.numDataCodewords; i++) {\n resultBytes[resultIndex++] = correctedBytes[i];\n }\n }\n\n try {\n return decodeData(resultBytes, version.versionNumber);\n } catch {\n return null;\n }\n}\n\nexport function decode(matrix: BitMatrix): DecodedQR {\n if (matrix == null) {\n return null;\n }\n const result = decodeMatrix(matrix);\n if (result) {\n return result;\n }\n // Decoding didn't work, try mirroring the QR across the topLeft -> bottomRight line.\n for (let x = 0; x < matrix.width; x++) {\n for (let y = x + 1; y < matrix.height; y++) {\n if (matrix.get(x, y) !== matrix.get(y, x)) {\n matrix.set(x, y, !matrix.get(x, y));\n matrix.set(y, x, !matrix.get(y, x));\n }\n }\n }\n return decodeMatrix(matrix);\n}\n","import {BitMatrix} from \"../BitMatrix\";\nimport {Point, QRLocation} from \"../locator\";\n\ninterface PerspectiveTransform {\n a11: number;\n a21: number;\n a31: number;\n a12: number;\n a22: number;\n a32: number;\n a13: number;\n a23: number;\n a33: number;\n}\n\nfunction squareToQuadrilateral(p1: Point, p2: Point, p3: Point, p4: Point): PerspectiveTransform {\n const dx3 = p1.x - p2.x + p3.x - p4.x;\n const dy3 = p1.y - p2.y + p3.y - p4.y;\n if (dx3 === 0 && dy3 === 0) { // Affine\n return {\n a11: p2.x - p1.x,\n a12: p2.y - p1.y,\n a13: 0,\n a21: p3.x - p2.x,\n a22: p3.y - p2.y,\n a23: 0,\n a31: p1.x,\n a32: p1.y,\n a33: 1,\n };\n } else {\n const dx1 = p2.x - p3.x;\n const dx2 = p4.x - p3.x;\n const dy1 = p2.y - p3.y;\n const dy2 = p4.y - p3.y;\n const denominator = dx1 * dy2 - dx2 * dy1;\n const a13 = (dx3 * dy2 - dx2 * dy3) / denominator;\n const a23 = (dx1 * dy3 - dx3 * dy1) / denominator;\n return {\n a11: p2.x - p1.x + a13 * p2.x,\n a12: p2.y - p1.y + a13 * p2.y,\n a13,\n a21: p4.x - p1.x + a23 * p4.x,\n a22: p4.y - p1.y + a23 * p4.y,\n a23,\n a31: p1.x,\n a32: p1.y,\n a33: 1,\n };\n }\n}\n\nfunction quadrilateralToSquare(p1: Point, p2: Point, p3: Point, p4: Point): PerspectiveTransform {\n // Here, the adjoint serves as the inverse:\n const sToQ = squareToQuadrilateral(p1, p2, p3, p4);\n return {\n a11: sToQ.a22 * sToQ.a33 - sToQ.a23 * sToQ.a32,\n a12: sToQ.a13 * sToQ.a32 - sToQ.a12 * sToQ.a33,\n a13: sToQ.a12 * sToQ.a23 - sToQ.a13 * sToQ.a22,\n a21: sToQ.a23 * sToQ.a31 - sToQ.a21 * sToQ.a33,\n a22: sToQ.a11 * sToQ.a33 - sToQ.a13 * sToQ.a31,\n a23: sToQ.a13 * sToQ.a21 - sToQ.a11 * sToQ.a23,\n a31: sToQ.a21 * sToQ.a32 - sToQ.a22 * sToQ.a31,\n a32: sToQ.a12 * sToQ.a31 - sToQ.a11 * sToQ.a32,\n a33: sToQ.a11 * sToQ.a22 - sToQ.a12 * sToQ.a21,\n };\n}\n\nfunction times(a: PerspectiveTransform, b: PerspectiveTransform): PerspectiveTransform {\n return {\n a11: a.a11 * b.a11 + a.a21 * b.a12 + a.a31 * b.a13,\n a12: a.a12 * b.a11 + a.a22 * b.a12 + a.a32 * b.a13,\n a13: a.a13 * b.a11 + a.a23 * b.a12 + a.a33 * b.a13,\n a21: a.a11 * b.a21 + a.a21 * b.a22 + a.a31 * b.a23,\n a22: a.a12 * b.a21 + a.a22 * b.a22 + a.a32 * b.a23,\n a23: a.a13 * b.a21 + a.a23 * b.a22 + a.a33 * b.a23,\n a31: a.a11 * b.a31 + a.a21 * b.a32 + a.a31 * b.a33,\n a32: a.a12 * b.a31 + a.a22 * b.a32 + a.a32 * b.a33,\n a33: a.a13 * b.a31 + a.a23 * b.a32 + a.a33 * b.a33,\n };\n}\n\nexport function extract(image: BitMatrix, location: QRLocation) {\n const qToS = quadrilateralToSquare(\n {x: 3.5, y: 3.5},\n {x: location.dimension - 3.5, y: 3.5},\n {x: location.dimension - 6.5, y: location.dimension - 6.5},\n {x: 3.5, y: location.dimension - 3.5},\n );\n const sToQ = squareToQuadrilateral(location.topLeft, location.topRight, location.alignmentPattern, location.bottomLeft);\n const transform = times(sToQ, qToS);\n\n const matrix = BitMatrix.createEmpty(location.dimension, location.dimension);\n const mappingFunction = (x: number, y: number) => {\n const denominator = transform.a13 * x + transform.a23 * y + transform.a33;\n return {\n x: (transform.a11 * x + transform.a21 * y + transform.a31) / denominator,\n y: (transform.a12 * x + transform.a22 * y + transform.a32) / denominator,\n };\n };\n\n for (let y = 0; y < location.dimension; y++) {\n for (let x = 0; x < location.dimension; x++) {\n const xValue = x + 0.5;\n const yValue = y + 0.5;\n const sourcePixel = mappingFunction(xValue, yValue);\n matrix.set(x, y, image.get(Math.floor(sourcePixel.x), Math.floor(sourcePixel.y)));\n }\n }\n\n return {\n matrix,\n mappingFunction,\n };\n}\n","import { BitMatrix } from \"../BitMatrix\";\n\nconst MAX_FINDERPATTERNS_TO_SEARCH = 5;\nconst MIN_QUAD_RATIO = 0.5;\nconst MAX_QUAD_RATIO = 1.5;\n\nexport interface Point {\n x: number;\n y: number;\n}\n\nexport interface QRLocation {\n topRight: Point;\n bottomLeft: Point;\n topLeft: Point;\n alignmentPattern: Point;\n dimension: number;\n}\n\nconst distance = (a: Point, b: Point) => Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2);\n\nfunction sum(values: number[]) {\n return values.reduce((a, b) => a + b);\n}\n\n// Takes three finder patterns and organizes them into topLeft, topRight, etc\nfunction reorderFinderPatterns(pattern1: Point, pattern2: Point, pattern3: Point) {\n // Find distances between pattern centers\n const oneTwoDistance = distance(pattern1, pattern2);\n const twoThreeDistance = distance(pattern2, pattern3);\n const oneThreeDistance = distance(pattern1, pattern3);\n\n let bottomLeft: Point;\n let topLeft: Point;\n let topRight: Point;\n\n // Assume one closest to other two is B; A and C will just be guesses at first\n if (twoThreeDistance >= oneTwoDistance && twoThreeDistance >= oneThreeDistance) {\n [bottomLeft, topLeft, topRight] = [pattern2, pattern1, pattern3];\n } else if (oneThreeDistance >= twoThreeDistance && oneThreeDistance >= oneTwoDistance) {\n [bottomLeft, topLeft, topRight] = [pattern1, pattern2, pattern3];\n } else {\n [bottomLeft, topLeft, topRight] = [pattern1, pattern3, pattern2];\n }\n\n // Use cross product to figure out whether bottomLeft (A) and topRight (C) are correct or flipped in relation to topLeft (B)\n // This asks whether BC x BA has a positive z component, which is the arrangement we want. If it's negative, then\n // we've got it flipped around and should swap topRight and bottomLeft.\n if (((topRight.x - topLeft.x) * (bottomLeft.y - topLeft.y)) - ((topRight.y - topLeft.y) * (bottomLeft.x - topLeft.x)) < 0) {\n [bottomLeft, topRight] = [topRight, bottomLeft];\n }\n\n return { bottomLeft, topLeft, topRight };\n}\n\n// Computes the dimension (number of modules on a side) of the QR Code based on the position of the finder patterns\nfunction computeDimension(topLeft: Point, topRight: Point, bottomLeft: Point, matrix: BitMatrix) {\n const moduleSize = (\n sum(countBlackWhiteRun(topLeft, bottomLeft, matrix, 5)) / 7 + // Divide by 7 since the ratio is 1:1:3:1:1\n sum(countBlackWhiteRun(topLeft, topRight, matrix, 5)) / 7 +\n sum(countBlackWhiteRun(bottomLeft, topLeft, matrix, 5)) / 7 +\n sum(countBlackWhiteRun(topRight, topLeft, matrix, 5)) / 7\n ) / 4;\n\n if (moduleSize < 1) {\n throw new Error(\"Invalid module size\");\n }\n\n const topDimension = Math.round(distance(topLeft, topRight) / moduleSize);\n const sideDimension = Math.round(distance(topLeft, bottomLeft) / moduleSize);\n let dimension = Math.floor((topDimension + sideDimension) / 2) + 7;\n switch (dimension % 4) {\n case 0:\n dimension++;\n break;\n case 2:\n dimension--;\n break;\n }\n return { dimension, moduleSize };\n}\n\n// Takes an origin point and an end point and counts the sizes of the black white run from the origin towards the end point.\n// Returns an array of elements, representing the pixel size of the black white run.\n// Uses a variant of http://en.wikipedia.org/wiki/Bresenham's_line_algorithm\nfunction countBlackWhiteRunTowardsPoint(origin: Point, end: Point, matrix: BitMatrix, length: number) {\n const switchPoints: Point[] = [{x: Math.floor(origin.x), y: Math.floor(origin.y)}];\n const steep = Math.abs(end.y - origin.y) > Math.abs(end.x - origin.x);\n\n let fromX: number;\n let fromY: number;\n let toX: number;\n let toY: number;\n if (steep) {\n fromX = Math.floor(origin.y);\n fromY = Math.floor(origin.x);\n toX = Math.floor(end.y);\n toY = Math.floor(end.x);\n } else {\n fromX = Math.floor(origin.x);\n fromY = Math.floor(origin.y);\n toX = Math.floor(end.x);\n toY = Math.floor(end.y);\n }\n\n const dx = Math.abs(toX - fromX);\n const dy = Math.abs(toY - fromY);\n let error = Math.floor(-dx / 2);\n const xStep = fromX < toX ? 1 : -1;\n const yStep = fromY < toY ? 1 : -1;\n\n let currentPixel = true;\n // Loop up until x == toX, but not beyond\n for (let x = fromX, y = fromY; x !== toX + xStep; x += xStep) {\n // Does current pixel mean we have moved white to black or vice versa?\n // Scanning black in state 0,2 and white in state 1, so if we find the wrong\n // color, advance to next state or end if we are in state 2 already\n const realX = steep ? y : x;\n const realY = steep ? x : y;\n if (matrix.get(realX, realY) !== currentPixel) {\n currentPixel = !currentPixel;\n switchPoints.push({x: realX, y: realY});\n if (switchPoints.length === length + 1) {\n break;\n }\n }\n error += dy;\n if (error > 0) {\n if (y === toY) {\n break;\n }\n y += yStep;\n error -= dx;\n }\n }\n const distances: number[] = [];\n for (let i = 0; i < length; i++) {\n if (switchPoints[i] && switchPoints[i + 1]) {\n distances.push(distance(switchPoints[i], switchPoints[i + 1]));\n } else {\n distances.push(0);\n }\n }\n return distances;\n}\n\n// Takes an origin point and an end point and counts the sizes of the black white run in the origin point\n// along the line that intersects with the end point. Returns an array of elements, representing the pixel sizes\n// of the black white run. Takes a length which represents the number of switches from black to white to look for.\nfunction countBlackWhiteRun(origin: Point, end: Point, matrix: BitMatrix, length: number) {\n const rise = end.y - origin.y;\n const run = end.x - origin.x;\n\n const towardsEnd = countBlackWhiteRunTowardsPoint(origin, end, matrix, Math.ceil(length / 2));\n const awayFromEnd = countBlackWhiteRunTowardsPoint(origin, {x: origin.x - run, y: origin.y - rise}, matrix, Math.ceil(length / 2));\n\n const middleValue = towardsEnd.shift() + awayFromEnd.shift() - 1; // Substract one so we don't double count a pixel\n return awayFromEnd.concat(middleValue).concat(...towardsEnd);\n}\n\n// Takes in a black white run and an array of expected ratios. Returns the average size of the run as well as the \"error\" -\n// that is the amount the run diverges from the expected ratio\nfunction scoreBlackWhiteRun(sequence: number[], ratios: number[]) {\n const averageSize = sum(sequence) / sum(ratios);\n let error = 0;\n ratios.forEach((ratio, i) => {\n error += (sequence[i] - ratio * averageSize) ** 2;\n });\n\n return { averageSize, error };\n}\n\n// Takes an X,Y point and an array of sizes and scores the point against those ratios.\n// For example for a finder pattern takes the ratio list of 1:1:3:1:1 and checks horizontal, vertical and diagonal ratios\n// against that.\nfunction scorePattern(point: Point, ratios: number[], matrix: BitMatrix) {\n try {\n const horizontalRun = countBlackWhiteRun(point, {x: -1, y: point.y}, matrix, ratios.length);\n const verticalRun = countBlackWhiteRun(point, {x: point.x, y: -1}, matrix, ratios.length);\n\n const topLeftPoint = {\n x: Math.max(0, point.x - point.y) - 1,\n y: Math.max(0, point.y - point.x) - 1,\n };\n const topLeftBottomRightRun = countBlackWhiteRun(point, topLeftPoint, matrix, ratios.length);\n\n const bottomLeftPoint = {\n x: Math.min(matrix.width, point.x + point.y) + 1,\n y: Math.min(matrix.height, point.y + point.x) + 1,\n };\n const bottomLeftTopRightRun = countBlackWhiteRun(point, bottomLeftPoint, matrix, ratios.length);\n\n const horzError = scoreBlackWhiteRun(horizontalRun, ratios);\n const vertError = scoreBlackWhiteRun(verticalRun, ratios);\n const diagDownError = scoreBlackWhiteRun(topLeftBottomRightRun, ratios);\n const diagUpError = scoreBlackWhiteRun(bottomLeftTopRightRun, ratios);\n\n const ratioError = Math.sqrt(horzError.error * horzError.error +\n vertError.error * vertError.error +\n diagDownError.error * diagDownError.error +\n diagUpError.error * diagUpError.error);\n\n const avgSize = (horzError.averageSize + vertError.averageSize + diagDownError.averageSize + diagUpError.averageSize) / 4;\n\n const sizeError = ((horzError.averageSize - avgSize) ** 2 +\n (vertError.averageSize - avgSize) ** 2 +\n (diagDownError.averageSize - avgSize) ** 2 +\n (diagUpError.averageSize - avgSize) ** 2) / avgSize;\n return ratioError + sizeError;\n } catch {\n return Infinity;\n }\n}\n\nfunction recenterLocation(matrix: BitMatrix, p: Point): Point {\n let leftX = Math.round(p.x);\n while (matrix.get(leftX, Math.round(p.y))) {\n leftX--;\n }\n let rightX = Math.round(p.x);\n while (matrix.get(rightX, Math.round(p.y))) {\n rightX++;\n }\n const x = (leftX + rightX) / 2;\n\n let topY = Math.round(p.y);\n while (matrix.get(Math.round(x), topY)) {\n topY--;\n }\n let bottomY = Math.round(p.y);\n while (matrix.get(Math.round(x), bottomY)) {\n bottomY++;\n }\n const y = (topY + bottomY) / 2;\n\n return { x, y };\n}\n\ninterface Quad {\n top: {\n startX: number;\n endX: number;\n y: number;\n };\n bottom: {\n startX: number;\n endX: number;\n y: number;\n };\n}\n\nexport function locate(matrix: BitMatrix): QRLocation[] {\n const finderPatternQuads: Quad[] = [];\n let activeFinderPatternQuads: Quad[] = [];\n const alignmentPatternQuads: Quad[] = [];\n let activeAlignmentPatternQuads: Quad[] = [];\n\n for (let y = 0; y <= matrix.height; y++) {\n let length = 0;\n let lastBit = false;\n let scans = [0, 0, 0, 0, 0];\n\n for (let x = -1; x <= matrix.width; x++) {\n const v = matrix.get(x, y);\n if (v === lastBit) {\n length++;\n } else {\n scans = [scans[1], scans[2], scans[3], scans[4], length];\n length = 1;\n lastBit = v;\n\n // Do the last 5 color changes ~ match the expected ratio for a finder pattern? 1:1:3:1:1 of b:w:b:w:b\n const averageFinderPatternBlocksize = sum(scans) / 7;\n const validFinderPattern =\n Math.abs(scans[0] - averageFinderPatternBlocksize) < averageFinderPatternBlocksize &&\n Math.abs(scans[1] - averageFinderPatternBlocksize) < averageFinderPatternBlocksize &&\n Math.abs(scans[2] - 3 * averageFinderPatternBlocksize) < 3 * averageFinderPatternBlocksize &&\n Math.abs(scans[3] - averageFinderPatternBlocksize) < averageFinderPatternBlocksize &&\n Math.abs(scans[4] - averageFinderPatternBlocksize) < averageFinderPatternBlocksize &&\n !v; // And make sure the current pixel is white since finder patterns are bordered in white\n\n // Do the last 3 color changes ~ match the expected ratio for an alignment pattern? 1:1:1 of w:b:w\n const averageAlignmentPatternBlocksize = sum(scans.slice(-3)) / 3;\n const validAlignmentPattern =\n Math.abs(scans[2] - averageAlignmentPatternBlocksize) < averageAlignmentPatternBlocksize &&\n Math.abs(scans[3] - averageAlignmentPatternBlocksize) < averageAlignmentPatternBlocksize &&\n Math.abs(scans[4] - averageAlignmentPatternBlocksize) < averageAlignmentPatternBlocksize &&\n v; // Is the current pixel black since alignment patterns are bordered in black\n\n if (validFinderPattern) {\n // Compute the start and end x values of the large center black square\n const endX = x - scans[3] - scans[4];\n const startX = endX - scans[2];\n\n const line = { startX, endX, y };\n // Is there a quad directly above the current spot? If so, extend it with the new line. Otherwise, create a new quad with\n // that line as the starting point.\n const matchingQuads = activeFinderPatternQuads.filter(q =>\n (startX >= q.bottom.startX && startX <= q.bottom.endX) ||\n (endX >= q.bottom.startX && startX <= q.bottom.endX) ||\n (startX <= q.bottom.startX && endX >= q.bottom.endX && (\n (scans[2] / (q.bottom.endX - q.bottom.startX)) < MAX_QUAD_RATIO &&\n (scans[2] / (q.bottom.endX - q.bottom.startX)) > MIN_QUAD_RATIO\n )),\n );\n if (matchingQuads.length > 0) {\n matchingQuads[0].bottom = line;\n } else {\n activeFinderPatternQuads.push({ top: line, bottom: line });\n }\n }\n if (validAlignmentPattern) {\n // Compute the start and end x values of the center black square\n const endX = x - scans[4];\n const startX = endX - scans[3];\n\n const line = { startX, y, endX };\n // Is there a quad directly above the current spot? If so, extend it with the new line. Otherwise, create a new quad with\n // that line as the starting point.\n const matchingQuads = activeAlignmentPatternQuads.filter(q =>\n (startX >= q.bottom.startX && startX <= q.bottom.endX) ||\n (endX >= q.bottom.startX && startX <= q.bottom.endX) ||\n (startX <= q.bottom.startX && endX >= q.bottom.endX && (\n (scans[2] / (q.bottom.endX - q.bottom.startX)) < MAX_QUAD_RATIO &&\n (scans[2] / (q.bottom.endX - q.bottom.startX)) > MIN_QUAD_RATIO\n )),\n );\n if (matchingQuads.length > 0) {\n matchingQuads[0].bottom = line;\n } else {\n activeAlignmentPatternQuads.push({ top: line, bottom: line });\n }\n }\n }\n }\n finderPatternQuads.push(...activeFinderPatternQuads.filter(q => q.bottom.y !== y && q.bottom.y - q.top.y >= 2));\n activeFinderPatternQuads = activeFinderPatternQuads.filter(q => q.bottom.y === y);\n\n alignmentPatternQuads.push(...activeAlignmentPatternQuads.filter(q => q.bottom.y !== y));\n activeAlignmentPatternQuads = activeAlignmentPatternQuads.filter(q => q.bottom.y === y);\n\n }\n\n finderPatternQuads.push(...activeFinderPatternQuads.filter(q => q.bottom.y - q.top.y >= 2));\n alignmentPatternQuads.push(...activeAlignmentPatternQuads);\n\n // Refactored from cozmo/jsQR to (hopefully) circumvent an issue in Safari 13+ on both Mac and iOS (also including\n // iOS Chrome and other Safari iOS derivatives). Safari was very occasionally and apparently not deterministically\n // throwing a \"RangeError: Array size is not a small enough positive integer.\" exception seemingly within the second\n // .map of the original code (here the second for-loop). This second .map contained a nested .map call over the same\n // array instance which was the chained result from previous calls to .map, .filter and .sort which potentially caused\n // this bug in Safari?\n // Also see https://github.com/cozmo/jsQR/issues/157 and https://bugs.webkit.org/show_bug.cgi?id=211619#c3\n const scoredFinderPatternPositions: Array = [];\n for (const quad of finderPatternQuads) {\n if (quad.bottom.y - quad.top.y < 2) {\n // All quads must be at least 2px tall since the center square is larger than a block\n continue;\n }\n\n // calculate quad center\n const x = (quad.top.startX + quad.top.endX + quad.bottom.startX + quad.bottom.endX) / 4;\n const y = (quad.top.y + quad.bottom.y + 1) / 2;\n if (!matrix.get(Math.round(x), Math.round(y))) {\n continue;\n }\n\n const lengths = [quad.top.endX - quad.top.startX, quad.bottom.endX - quad.bottom.startX, quad.bottom.y - quad.top.y + 1];\n const size = sum(lengths) / lengths.length;\n // Initial scoring of finder pattern quads by looking at their ratios, not taking into account position\n const score = scorePattern({x: Math.round(x), y: Math.round(y)}, [1, 1, 3, 1, 1], matrix);\n scoredFinderPatternPositions.push({ score, x, y, size });\n }\n if (scoredFinderPatternPositions.length < 3) {\n // A QR code has 3 finder patterns, therefore we need at least 3 candidates.\n return null;\n }\n scoredFinderPatternPositions.sort((a, b) => a.score - b.score);\n\n // Now take the top finder pattern options and try to find 2 other options with a similar size.\n const finderPatternGroups: Array<{ points: [Point, Point, Point], score: number }> = [];\n for (let i = 0; i < Math.min(scoredFinderPatternPositions.length, MAX_FINDERPATTERNS_TO_SEARCH); ++i) {\n const point = scoredFinderPatternPositions[i];\n const otherPoints: typeof scoredFinderPatternPositions = [];\n\n for (const otherPoint of scoredFinderPatternPositions) {\n if (otherPoint === point) {\n continue;\n }\n otherPoints.push({\n ...otherPoint,\n score: otherPoint.score + ((otherPoint.size - point.size) ** 2) / point.size, // score similarity of sizes\n });\n }\n otherPoints.sort((a, b) => a.score - b.score);\n\n finderPatternGroups.push({\n points: [point, otherPoints[0], otherPoints[1]], // note that otherPoints.length >= 2 as scoredFinderPatternPositions.length >= 3\n score: point.score + otherPoints[0].score + otherPoints[1].score, // total combined score of the three points in the group\n });\n }\n finderPatternGroups.sort((a, b) => a.score - b.score);\n const bestFinderPatternGroup = finderPatternGroups[0];\n\n const { topRight, topLeft, bottomLeft } = reorderFinderPatterns(...bestFinderPatternGroup.points);\n const alignment = findAlignmentPattern(matrix, alignmentPatternQuads, topRight, topLeft, bottomLeft);\n const result: QRLocation[] = [];\n if (alignment) {\n result.push({\n alignmentPattern: { x: alignment.alignmentPattern.x, y: alignment.alignmentPattern.y },\n bottomLeft: {x: bottomLeft.x, y: bottomLeft.y },\n dimension: alignment.dimension,\n topLeft: {x: topLeft.x, y: topLeft.y },\n topRight: {x: topRight.x, y: topRight.y },\n });\n }\n\n // We normally use the center of the quads as the location of the tracking points, which is optimal for most cases and will account\n // for a skew in the image. However, In some cases, a slight skew might not be real and instead be caused by image compression\n // errors and/or low resolution. For those cases, we'd be better off centering the point exactly in the middle of the black area. We\n // compute and return the location data for the naively centered points as it is little additional work and allows for multiple\n // attempts at decoding harder images.\n const midTopRight = recenterLocation(matrix, topRight);\n const midTopLeft = recenterLocation(matrix, topLeft);\n const midBottomLeft = recenterLocation(matrix, bottomLeft);\n const centeredAlignment = findAlignmentPattern(matrix, alignmentPatternQuads, midTopRight, midTopLeft, midBottomLeft);\n if (centeredAlignment) {\n result.push({\n alignmentPattern: { x: centeredAlignment.alignmentPattern.x, y: centeredAlignment.alignmentPattern.y },\n bottomLeft: { x: midBottomLeft.x, y: midBottomLeft. y },\n topLeft: { x: midTopLeft.x, y: midTopLeft. y },\n topRight: { x: midTopRight.x, y: midTopRight. y },\n dimension: centeredAlignment.dimension,\n });\n }\n\n if (result.length === 0) {\n return null;\n }\n\n return result;\n}\n\nfunction findAlignmentPattern(matrix: BitMatrix, alignmentPatternQuads: Quad[], topRight: Point, topLeft: Point, bottomLeft: Point) {\n // Now that we've found the three finder patterns we can determine the blockSize and the size of the QR code.\n // We'll use these to help find the alignment pattern but also later when we do the extraction.\n let dimension: number;\n let moduleSize: number;\n try {\n ({ dimension, moduleSize } = computeDimension(topLeft, topRight, bottomLeft, matrix));\n } catch (e) {\n return null;\n }\n\n // Now find the alignment pattern\n const bottomRightFinderPattern = { // Best guess at where a bottomRight finder pattern would be\n x: topRight.x - topLeft.x + bottomLeft.x,\n y: topRight.y - topLeft.y + bottomLeft.y,\n };\n const modulesBetweenFinderPatterns = ((distance(topLeft, bottomLeft) + distance(topLeft, topRight)) / 2 / moduleSize);\n const correctionToTopLeft = 1 - (3 / modulesBetweenFinderPatterns);\n const expectedAlignmentPattern = {\n x: topLeft.x + correctionToTopLeft * (bottomRightFinderPattern.x - topLeft.x),\n y: topLeft.y + correctionToTopLeft * (bottomRightFinderPattern.y - topLeft.y),\n };\n\n const alignmentPatterns = alignmentPatternQuads\n .map(q => {\n const x = (q.top.startX + q.top.endX + q.bottom.startX + q.bottom.endX) / 4;\n const y = (q.top.y + q.bottom.y + 1) / 2;\n if (!matrix.get(Math.floor(x), Math.floor(y))) {\n return;\n }\n\n const sizeScore = scorePattern({x: Math.floor(x), y: Math.floor(y)}, [1, 1, 1], matrix);\n const score = sizeScore + distance({x, y}, expectedAlignmentPattern);\n return { x, y, score };\n })\n .filter(v => !!v)\n .sort((a, b) => a.score - b.score);\n\n // If there are less than 15 modules between finder patterns it's a version 1 QR code and as such has no alignmemnt pattern\n // so we can only use our best guess.\n const alignmentPattern = modulesBetweenFinderPatterns >= 15 && alignmentPatterns.length ? alignmentPatterns[0] : expectedAlignmentPattern;\n\n return { alignmentPattern, dimension };\n}\n","import {binarize} from \"./binarizer\";\nimport {BitMatrix} from \"./BitMatrix\";\nimport {Chunks} from \"./decoder/decodeData\";\nimport {decode} from \"./decoder/decoder\";\nimport { Version } from \"./decoder/version\";\nimport {extract} from \"./extractor\";\nimport {locate, Point} from \"./locator\";\n\nexport interface QRCode {\n binaryData: number[];\n data: string;\n chunks: Chunks;\n version: number;\n location: {\n topRightCorner: Point;\n topLeftCorner: Point;\n bottomRightCorner: Point;\n bottomLeftCorner: Point;\n\n topRightFinderPattern: Point;\n topLeftFinderPattern: Point;\n bottomLeftFinderPattern: Point;\n\n bottomRightAlignmentPattern?: Point;\n };\n matrix: BitMatrix;\n}\n\nfunction scan(matrix: BitMatrix): QRCode | null {\n const locations = locate(matrix);\n if (!locations) {\n return null;\n }\n\n for (const location of locations) {\n const extracted = extract(matrix, location);\n const decoded = decode(extracted.matrix);\n if (decoded) {\n return {\n binaryData: decoded.bytes,\n data: decoded.text,\n chunks: decoded.chunks,\n version: decoded.version,\n location: {\n topRightCorner: extracted.mappingFunction(location.dimension, 0),\n topLeftCorner: extracted.mappingFunction(0, 0),\n bottomRightCorner: extracted.mappingFunction(location.dimension, location.dimension),\n bottomLeftCorner: extracted.mappingFunction(0, location.dimension),\n\n topRightFinderPattern: location.topRight,\n topLeftFinderPattern: location.topLeft,\n bottomLeftFinderPattern: location.bottomLeft,\n\n bottomRightAlignmentPattern: location.alignmentPattern,\n },\n matrix: extracted.matrix,\n };\n }\n }\n return null;\n}\n\nexport interface Options {\n inversionAttempts?: \"dontInvert\" | \"onlyInvert\" | \"attemptBoth\" | \"invertFirst\";\n greyScaleWeights?: GreyscaleWeights;\n canOverwriteImage?: boolean;\n}\n\nexport interface GreyscaleWeights {\n red: number;\n green: number;\n blue: number;\n useIntegerApproximation?: boolean;\n}\n\nconst defaultOptions: Options = {\n inversionAttempts: \"attemptBoth\",\n greyScaleWeights: {\n red: 0.2126,\n green: 0.7152,\n blue: 0.0722,\n useIntegerApproximation: false,\n },\n canOverwriteImage: true,\n};\n\nfunction mergeObject(target: any, src: any) {\n Object.keys(src).forEach(opt => { // Sad implementation of Object.assign since we target es5 not es6\n target[opt] = src[opt];\n });\n}\n\nfunction jsQR(data: Uint8ClampedArray, width: number, height: number, providedOptions: Options = {}): QRCode | null {\n const options = Object.create(null);\n mergeObject(options, defaultOptions);\n mergeObject(options, providedOptions);\n\n const tryInvertedFirst = options.inversionAttempts === \"onlyInvert\" || options.inversionAttempts === \"invertFirst\";\n const shouldInvert = options.inversionAttempts === \"attemptBoth\" || tryInvertedFirst;\n const {binarized, inverted} = binarize(data, width, height, shouldInvert, options.greyScaleWeights,\n options.canOverwriteImage);\n let result = scan(tryInvertedFirst ? inverted : binarized);\n if (!result && (options.inversionAttempts === \"attemptBoth\" || options.inversionAttempts === \"invertFirst\")) {\n result = scan(tryInvertedFirst ? binarized : inverted);\n }\n return result;\n}\n\n(jsQR as any).default = jsQR;\nexport default jsQR;\n","import {BitMatrix} from \"../BitMatrix\";\nimport {GreyscaleWeights} from \"../index\";\n\nconst REGION_SIZE = 8;\nconst MIN_DYNAMIC_RANGE = 24;\n\nfunction numBetween(value: number, min: number, max: number): number {\n return value < min ? min : value > max ? max : value;\n}\n\n// Like BitMatrix but accepts arbitry Uint8 values\nclass Matrix {\n private data: Uint8ClampedArray;\n private width: number;\n constructor(width: number, height: number, buffer?: Uint8ClampedArray) {\n this.width = width;\n const bufferSize = width * height;\n if (buffer && buffer.length !== bufferSize) {\n throw new Error(\"Wrong buffer size\");\n }\n this.data = buffer || new Uint8ClampedArray(bufferSize);\n }\n public get(x: number, y: number) {\n return this.data[y * this.width + x];\n }\n public set(x: number, y: number, value: number) {\n this.data[y * this.width + x] = value;\n }\n}\n\nexport function binarize(data: Uint8ClampedArray, width: number, height: number, returnInverted: boolean,\n greyscaleWeights: GreyscaleWeights, canOverwriteImage: boolean) {\n const pixelCount = width * height;\n if (data.length !== pixelCount * 4) {\n throw new Error(\"Malformed data passed to binarizer.\");\n }\n // assign the greyscale and binary image within the rgba buffer as the rgba image will not be needed after conversion\n let bufferOffset = 0;\n // Convert image to greyscale\n let greyscaleBuffer: Uint8ClampedArray;\n if (canOverwriteImage) {\n greyscaleBuffer = new Uint8ClampedArray(data.buffer, bufferOffset, pixelCount);\n bufferOffset += pixelCount;\n }\n const greyscalePixels = new Matrix(width, height, greyscaleBuffer);\n if (greyscaleWeights.useIntegerApproximation) {\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const pixelPosition = (y * width + x) * 4;\n const r = data[pixelPosition];\n const g = data[pixelPosition + 1];\n const b = data[pixelPosition + 2];\n greyscalePixels.set(x, y,\n // tslint:disable-next-line no-bitwise\n (greyscaleWeights.red * r + greyscaleWeights.green * g + greyscaleWeights.blue * b + 128) >> 8);\n }\n }\n } else {\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const pixelPosition = (y * width + x) * 4;\n const r = data[pixelPosition];\n const g = data[pixelPosition + 1];\n const b = data[pixelPosition + 2];\n greyscalePixels.set(x, y,\n greyscaleWeights.red * r + greyscaleWeights.green * g + greyscaleWeights.blue * b);\n }\n }\n }\n const horizontalRegionCount = Math.ceil(width / REGION_SIZE);\n const verticalRegionCount = Math.ceil(height / REGION_SIZE);\n const blackPointsCount = horizontalRegionCount * verticalRegionCount;\n\n let blackPointsBuffer: Uint8ClampedArray;\n if (canOverwriteImage) {\n blackPointsBuffer = new Uint8ClampedArray(data.buffer, bufferOffset, blackPointsCount);\n bufferOffset += blackPointsCount;\n }\n const blackPoints = new Matrix(horizontalRegionCount, verticalRegionCount, blackPointsBuffer);\n for (let verticalRegion = 0; verticalRegion < verticalRegionCount; verticalRegion++) {\n for (let hortizontalRegion = 0; hortizontalRegion < horizontalRegionCount; hortizontalRegion++) {\n let min = Infinity;\n let max = 0;\n for (let y = 0; y < REGION_SIZE; y++) {\n for (let x = 0; x < REGION_SIZE; x++) {\n const pixelLumosity =\n greyscalePixels.get(hortizontalRegion * REGION_SIZE + x, verticalRegion * REGION_SIZE + y);\n min = Math.min(min, pixelLumosity);\n max = Math.max(max, pixelLumosity);\n }\n }\n // We could also compute the real average of all pixels but following the assumption that the qr code consists\n // of bright and dark pixels and essentially not much in between, by (min + max)/2 we make the cut really between\n // those two classes. If using the average over all pixel in a block of mostly bright pixels and few dark pixels,\n // the avg would tend to the bright side and darker bright pixels could be interpreted as dark.\n let average = (min + max) / 2;\n // Small bias towards black by moving the threshold up. We do this, as in the finder patterns white holes tend\n // to appear which makes them undetectable.\n const blackBias = 1.11;\n average = Math.min(255, average * blackBias);\n if (max - min <= MIN_DYNAMIC_RANGE) {\n // If variation within the block is low, assume this is a block with only light or only\n // dark pixels. In that case we do not want to use the average, as it would divide this\n // low contrast area into black and white pixels, essentially creating data out of noise.\n //\n // Default the blackpoint for these blocks to be half the min - effectively white them out\n average = min / 2;\n\n if (verticalRegion > 0 && hortizontalRegion > 0) {\n // Correct the \"white background\" assumption for blocks that have neighbors by comparing\n // the pixels in this block to the previously calculated black points. This is based on\n // the fact that dark barcode symbology is always surrounded by some amount of light\n // background for which reasonable black point estimates were made. The bp estimated at\n // the boundaries is used for the interior.\n\n // The (min < bp) is arbitrary but works better than other heuristics that were tried.\n const averageNeighborBlackPoint = (\n blackPoints.get(hortizontalRegion, verticalRegion - 1) +\n (2 * blackPoints.get(hortizontalRegion - 1, verticalRegion)) +\n blackPoints.get(hortizontalRegion - 1, verticalRegion - 1)\n ) / 4;\n if (min < averageNeighborBlackPoint) {\n average = averageNeighborBlackPoint; // no need to apply black bias as already applied to neighbors\n }\n }\n }\n blackPoints.set(hortizontalRegion, verticalRegion, average);\n }\n }\n\n let binarized: BitMatrix;\n if (canOverwriteImage) {\n const binarizedBuffer = new Uint8ClampedArray(data.buffer, bufferOffset, pixelCount);\n bufferOffset += pixelCount;\n binarized = new BitMatrix(binarizedBuffer, width);\n } else {\n binarized = BitMatrix.createEmpty(width, height);\n }\n\n let inverted: BitMatrix = null;\n if (returnInverted) {\n if (canOverwriteImage) {\n const invertedBuffer = new Uint8ClampedArray(data.buffer, bufferOffset, pixelCount);\n inverted = new BitMatrix(invertedBuffer, width);\n } else {\n inverted = BitMatrix.createEmpty(width, height);\n }\n }\n\n for (let verticalRegion = 0; verticalRegion < verticalRegionCount; verticalRegion++) {\n for (let hortizontalRegion = 0; hortizontalRegion < horizontalRegionCount; hortizontalRegion++) {\n const left = numBetween(hortizontalRegion, 2, horizontalRegionCount - 3);\n const top = numBetween(verticalRegion, 2, verticalRegionCount - 3);\n let sum = 0;\n for (let xRegion = -2; xRegion <= 2; xRegion++) {\n for (let yRegion = -2; yRegion <= 2; yRegion++) {\n sum += blackPoints.get(left + xRegion, top + yRegion);\n }\n }\n const threshold = sum / 25;\n for (let xRegion = 0; xRegion < REGION_SIZE; xRegion++) {\n for (let yRegion = 0; yRegion < REGION_SIZE; yRegion++) {\n const x = hortizontalRegion * REGION_SIZE + xRegion;\n const y = verticalRegion * REGION_SIZE + yRegion;\n const lum = greyscalePixels.get(x, y);\n binarized.set(x, y, lum <= threshold);\n if (returnInverted) {\n inverted.set(x, y, !(lum <= threshold));\n }\n }\n }\n }\n }\n if (returnInverted) {\n return { binarized, inverted };\n }\n return { binarized };\n}\n","export class BitMatrix {\n public static createEmpty(width: number, height: number) {\n return new BitMatrix(new Uint8ClampedArray(width * height), width);\n }\n\n public width: number;\n public height: number;\n private data: Uint8ClampedArray;\n\n constructor(data: Uint8ClampedArray, width: number) {\n this.width = width;\n this.height = data.length / width;\n this.data = data;\n }\n\n public get(x: number, y: number): boolean {\n if (x < 0 || x >= this.width || y < 0 || y >= this.height) {\n return false;\n }\n return !!this.data[y * this.width + x];\n }\n\n public set(x: number, y: number, v: boolean) {\n this.data[y * this.width + x] = v ? 1 : 0;\n }\n\n public setRegion(left: number, top: number, width: number, height: number, v: boolean) {\n for (let y = top; y < top + height; y++) {\n for (let x = left; x < left + width; x++) {\n this.set(x, y, !!v);\n }\n }\n }\n}\n","// tslint:disable:no-bitwise\n\nexport class BitStream {\n private bytes: Uint8ClampedArray;\n private byteOffset: number = 0;\n private bitOffset: number = 0;\n\n constructor(bytes: Uint8ClampedArray) {\n this.bytes = bytes;\n }\n\n public readBits(numBits: number): number {\n if (numBits < 1 || numBits > 32 || numBits > this.available()) {\n throw new Error(\"Cannot read \" + numBits.toString() + \" bits\");\n }\n\n let result = 0;\n // First, read remainder from current byte\n if (this.bitOffset > 0) {\n const bitsLeft = 8 - this.bitOffset;\n const toRead = numBits < bitsLeft ? numBits : bitsLeft;\n const bitsToNotRead = bitsLeft - toRead;\n const mask = (0xFF >> (8 - toRead)) << bitsToNotRead;\n result = (this.bytes[this.byteOffset] & mask) >> bitsToNotRead;\n numBits -= toRead;\n this.bitOffset += toRead;\n if (this.bitOffset === 8) {\n this.bitOffset = 0;\n this.byteOffset++;\n }\n }\n\n // Next read whole bytes\n if (numBits > 0) {\n while (numBits >= 8) {\n result = (result << 8) | (this.bytes[this.byteOffset] & 0xFF);\n this.byteOffset++;\n numBits -= 8;\n }\n\n // Finally read a partial byte\n if (numBits > 0) {\n const bitsToNotRead = 8 - numBits;\n const mask = (0xFF >> bitsToNotRead) << bitsToNotRead;\n result = (result << numBits) | ((this.bytes[this.byteOffset] & mask) >> bitsToNotRead);\n this.bitOffset += numBits;\n }\n }\n return result;\n }\n\n public available(): number {\n return 8 * (this.bytes.length - this.byteOffset) - this.bitOffset;\n }\n}\n","import GenericGF, { addOrSubtractGF } from \"./GenericGF\";\n\nexport default class GenericGFPoly {\n private field: GenericGF;\n private coefficients: Uint8ClampedArray;\n\n constructor(field: GenericGF, coefficients: Uint8ClampedArray) {\n if (coefficients.length === 0) {\n throw new Error(\"No coefficients.\");\n }\n this.field = field;\n const coefficientsLength = coefficients.length;\n if (coefficientsLength > 1 && coefficients[0] === 0) {\n // Leading term must be non-zero for anything except the constant polynomial \"0\"\n let firstNonZero = 1;\n while (firstNonZero < coefficientsLength && coefficients[firstNonZero] === 0) {\n firstNonZero++;\n }\n if (firstNonZero === coefficientsLength) {\n this.coefficients = field.zero.coefficients;\n } else {\n this.coefficients = new Uint8ClampedArray(coefficientsLength - firstNonZero);\n for (let i = 0; i < this.coefficients.length; i++) {\n this.coefficients[i] = coefficients[firstNonZero + i];\n }\n }\n } else {\n this.coefficients = coefficients;\n }\n }\n\n public degree() {\n return this.coefficients.length - 1;\n }\n\n public isZero() {\n return this.coefficients[0] === 0;\n }\n\n public getCoefficient(degree: number) {\n return this.coefficients[this.coefficients.length - 1 - degree];\n }\n\n public addOrSubtract(other: GenericGFPoly) {\n if (this.isZero()) {\n return other;\n }\n if (other.isZero()) {\n return this;\n }\n\n let smallerCoefficients = this.coefficients;\n let largerCoefficients = other.coefficients;\n if (smallerCoefficients.length > largerCoefficients.length) {\n [smallerCoefficients, largerCoefficients] = [largerCoefficients, smallerCoefficients];\n }\n const sumDiff = new Uint8ClampedArray(largerCoefficients.length);\n const lengthDiff = largerCoefficients.length - smallerCoefficients.length;\n for (let i = 0; i < lengthDiff; i++) {\n sumDiff[i] = largerCoefficients[i];\n }\n\n for (let i = lengthDiff; i < largerCoefficients.length; i++) {\n sumDiff[i] = addOrSubtractGF(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);\n }\n\n return new GenericGFPoly(this.field, sumDiff);\n }\n\n public multiply(scalar: number) {\n if (scalar === 0) {\n return this.field.zero;\n }\n if (scalar === 1) {\n return this;\n }\n const size = this.coefficients.length;\n const product = new Uint8ClampedArray(size);\n for (let i = 0; i < size; i++) {\n product[i] = this.field.multiply(this.coefficients[i], scalar);\n }\n\n return new GenericGFPoly(this.field, product);\n }\n\n public multiplyPoly(other: GenericGFPoly): GenericGFPoly {\n if (this.isZero() || other.isZero()) {\n return this.field.zero;\n }\n const aCoefficients = this.coefficients;\n const aLength = aCoefficients.length;\n const bCoefficients = other.coefficients;\n const bLength = bCoefficients.length;\n const product = new Uint8ClampedArray(aLength + bLength - 1);\n for (let i = 0; i < aLength; i++) {\n const aCoeff = aCoefficients[i];\n for (let j = 0; j < bLength; j++) {\n product[i + j] = addOrSubtractGF(product[i + j],\n this.field.multiply(aCoeff, bCoefficients[j]));\n }\n }\n return new GenericGFPoly(this.field, product);\n }\n\n public multiplyByMonomial(degree: number, coefficient: number) {\n if (degree < 0) {\n throw new Error(\"Invalid degree less than 0\");\n }\n if (coefficient === 0) {\n return this.field.zero;\n }\n const size = this.coefficients.length;\n const product = new Uint8ClampedArray(size + degree);\n for (let i = 0; i < size; i++) {\n product[i] = this.field.multiply(this.coefficients[i], coefficient);\n }\n return new GenericGFPoly(this.field, product);\n }\n\n public evaluateAt(a: number) {\n let result = 0;\n if (a === 0) {\n // Just return the x^0 coefficient\n return this.getCoefficient(0);\n }\n const size = this.coefficients.length;\n if (a === 1) {\n // Just the sum of the coefficients\n this.coefficients.forEach((coefficient) => {\n result = addOrSubtractGF(result, coefficient);\n });\n return result;\n }\n result = this.coefficients[0];\n for (let i = 1; i < size; i++) {\n result = addOrSubtractGF(this.field.multiply(a, result), this.coefficients[i]);\n }\n return result;\n }\n}\n","export interface Version {\n infoBits: number;\n versionNumber: number;\n alignmentPatternCenters: number[];\n errorCorrectionLevels: Array<{\n ecCodewordsPerBlock: number;\n ecBlocks: Array<{\n numBlocks: number;\n dataCodewordsPerBlock: number;\n }>\n }>;\n}\n\nexport const VERSIONS: Version[] = [\n {\n infoBits: null,\n versionNumber: 1,\n alignmentPatternCenters: [],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 7,\n ecBlocks: [{ numBlocks: 1, dataCodewordsPerBlock: 19 }],\n },\n {\n ecCodewordsPerBlock: 10,\n ecBlocks: [{ numBlocks: 1, dataCodewordsPerBlock: 16 }],\n },\n {\n ecCodewordsPerBlock: 13,\n ecBlocks: [{ numBlocks: 1, dataCodewordsPerBlock: 13 }],\n },\n {\n ecCodewordsPerBlock: 17,\n ecBlocks: [{ numBlocks: 1, dataCodewordsPerBlock: 9 }],\n },\n ],\n },\n {\n infoBits: null,\n versionNumber: 2,\n alignmentPatternCenters: [6, 18],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 10,\n ecBlocks: [{ numBlocks: 1, dataCodewordsPerBlock: 34 }],\n },\n {\n ecCodewordsPerBlock: 16,\n ecBlocks: [{ numBlocks: 1, dataCodewordsPerBlock: 28 }],\n },\n {\n ecCodewordsPerBlock: 22,\n ecBlocks: [{ numBlocks: 1, dataCodewordsPerBlock: 22 }],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [{ numBlocks: 1, dataCodewordsPerBlock: 16 }],\n },\n ],\n },\n {\n infoBits: null,\n versionNumber: 3,\n alignmentPatternCenters: [6, 22],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 15,\n ecBlocks: [{ numBlocks: 1, dataCodewordsPerBlock: 55 }],\n },\n {\n ecCodewordsPerBlock: 26,\n ecBlocks: [{ numBlocks: 1, dataCodewordsPerBlock: 44 }],\n },\n {\n ecCodewordsPerBlock: 18,\n ecBlocks: [{ numBlocks: 2, dataCodewordsPerBlock: 17 }],\n },\n {\n ecCodewordsPerBlock: 22,\n ecBlocks: [{ numBlocks: 2, dataCodewordsPerBlock: 13 }],\n },\n ],\n },\n {\n infoBits: null,\n versionNumber: 4,\n alignmentPatternCenters: [6, 26],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 20,\n ecBlocks: [{ numBlocks: 1, dataCodewordsPerBlock: 80 }],\n },\n {\n ecCodewordsPerBlock: 18,\n ecBlocks: [{ numBlocks: 2, dataCodewordsPerBlock: 32 }],\n },\n {\n ecCodewordsPerBlock: 26,\n ecBlocks: [{ numBlocks: 2, dataCodewordsPerBlock: 24 }],\n },\n {\n ecCodewordsPerBlock: 16,\n ecBlocks: [{ numBlocks: 4, dataCodewordsPerBlock: 9 }],\n },\n ],\n },\n {\n infoBits: null,\n versionNumber: 5,\n alignmentPatternCenters: [6, 30],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 26,\n ecBlocks: [{ numBlocks: 1, dataCodewordsPerBlock: 108 }],\n },\n {\n ecCodewordsPerBlock: 24,\n ecBlocks: [{ numBlocks: 2, dataCodewordsPerBlock: 43 }],\n },\n {\n ecCodewordsPerBlock: 18,\n ecBlocks: [\n { numBlocks: 2, dataCodewordsPerBlock: 15 },\n { numBlocks: 2, dataCodewordsPerBlock: 16 },\n ],\n },\n {\n ecCodewordsPerBlock: 22,\n ecBlocks: [\n { numBlocks: 2, dataCodewordsPerBlock: 11 },\n { numBlocks: 2, dataCodewordsPerBlock: 12 },\n ],\n },\n ],\n },\n {\n infoBits: null,\n versionNumber: 6,\n alignmentPatternCenters: [6, 34],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 18,\n ecBlocks: [{ numBlocks: 2, dataCodewordsPerBlock: 68 }],\n },\n {\n ecCodewordsPerBlock: 16,\n ecBlocks: [{ numBlocks: 4, dataCodewordsPerBlock: 27 }],\n },\n {\n ecCodewordsPerBlock: 24,\n ecBlocks: [{ numBlocks: 4, dataCodewordsPerBlock: 19 }],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [{ numBlocks: 4, dataCodewordsPerBlock: 15 }],\n },\n ],\n },\n {\n infoBits: 0x07C94,\n versionNumber: 7,\n alignmentPatternCenters: [6, 22, 38],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 20,\n ecBlocks: [{ numBlocks: 2, dataCodewordsPerBlock: 78 }],\n },\n {\n ecCodewordsPerBlock: 18,\n ecBlocks: [{ numBlocks: 4, dataCodewordsPerBlock: 31 }],\n },\n {\n ecCodewordsPerBlock: 18,\n ecBlocks: [\n { numBlocks: 2, dataCodewordsPerBlock: 14 },\n { numBlocks: 4, dataCodewordsPerBlock: 15 },\n ],\n },\n {\n ecCodewordsPerBlock: 26,\n ecBlocks: [\n { numBlocks: 4, dataCodewordsPerBlock: 13 },\n { numBlocks: 1, dataCodewordsPerBlock: 14 },\n ],\n },\n ],\n },\n {\n infoBits: 0x085BC,\n versionNumber: 8,\n alignmentPatternCenters: [6, 24, 42],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 24,\n ecBlocks: [{ numBlocks: 2, dataCodewordsPerBlock: 97 }],\n },\n {\n ecCodewordsPerBlock: 22,\n ecBlocks: [\n { numBlocks: 2, dataCodewordsPerBlock: 38 },\n { numBlocks: 2, dataCodewordsPerBlock: 39 },\n ],\n },\n {\n ecCodewordsPerBlock: 22,\n ecBlocks: [\n { numBlocks: 4, dataCodewordsPerBlock: 18 },\n { numBlocks: 2, dataCodewordsPerBlock: 19 },\n ],\n },\n {\n ecCodewordsPerBlock: 26,\n ecBlocks: [\n { numBlocks: 4, dataCodewordsPerBlock: 14 },\n { numBlocks: 2, dataCodewordsPerBlock: 15 },\n ],\n },\n ],\n },\n {\n infoBits: 0x09A99,\n versionNumber: 9,\n alignmentPatternCenters: [6, 26, 46],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [{ numBlocks: 2, dataCodewordsPerBlock: 116 }],\n },\n {\n ecCodewordsPerBlock: 22,\n ecBlocks: [\n { numBlocks: 3, dataCodewordsPerBlock: 36 },\n { numBlocks: 2, dataCodewordsPerBlock: 37 },\n ],\n },\n {\n ecCodewordsPerBlock: 20,\n ecBlocks: [\n { numBlocks: 4, dataCodewordsPerBlock: 16 },\n { numBlocks: 4, dataCodewordsPerBlock: 17 },\n ],\n },\n {\n ecCodewordsPerBlock: 24,\n ecBlocks: [\n { numBlocks: 4, dataCodewordsPerBlock: 12 },\n { numBlocks: 4, dataCodewordsPerBlock: 13 },\n ],\n },\n ],\n },\n {\n infoBits: 0x0A4D3,\n versionNumber: 10,\n alignmentPatternCenters: [6, 28, 50],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 18,\n ecBlocks: [\n { numBlocks: 2, dataCodewordsPerBlock: 68 },\n { numBlocks: 2, dataCodewordsPerBlock: 69 },\n ],\n },\n {\n ecCodewordsPerBlock: 26,\n ecBlocks: [\n { numBlocks: 4, dataCodewordsPerBlock: 43 },\n { numBlocks: 1, dataCodewordsPerBlock: 44 },\n ],\n },\n {\n ecCodewordsPerBlock: 24,\n ecBlocks: [\n { numBlocks: 6, dataCodewordsPerBlock: 19 },\n { numBlocks: 2, dataCodewordsPerBlock: 20 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 6, dataCodewordsPerBlock: 15 },\n { numBlocks: 2, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x0BBF6,\n versionNumber: 11,\n alignmentPatternCenters: [6, 30, 54],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 20,\n ecBlocks: [{ numBlocks: 4, dataCodewordsPerBlock: 81 }],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 1, dataCodewordsPerBlock: 50 },\n { numBlocks: 4, dataCodewordsPerBlock: 51 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 4, dataCodewordsPerBlock: 22 },\n { numBlocks: 4, dataCodewordsPerBlock: 23 },\n ],\n },\n {\n ecCodewordsPerBlock: 24,\n ecBlocks: [\n { numBlocks: 3, dataCodewordsPerBlock: 12 },\n { numBlocks: 8, dataCodewordsPerBlock: 13 },\n ],\n },\n ],\n },\n {\n infoBits: 0x0C762,\n versionNumber: 12,\n alignmentPatternCenters: [6, 32, 58],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 24,\n ecBlocks: [\n { numBlocks: 2, dataCodewordsPerBlock: 92 },\n { numBlocks: 2, dataCodewordsPerBlock: 93 },\n ],\n },\n {\n ecCodewordsPerBlock: 22,\n ecBlocks: [\n { numBlocks: 6, dataCodewordsPerBlock: 36 },\n { numBlocks: 2, dataCodewordsPerBlock: 37 },\n ],\n },\n {\n ecCodewordsPerBlock: 26,\n ecBlocks: [\n { numBlocks: 4, dataCodewordsPerBlock: 20 },\n { numBlocks: 6, dataCodewordsPerBlock: 21 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 7, dataCodewordsPerBlock: 14 },\n { numBlocks: 4, dataCodewordsPerBlock: 15 },\n ],\n },\n ],\n },\n {\n infoBits: 0x0D847,\n versionNumber: 13,\n alignmentPatternCenters: [6, 34, 62],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 26,\n ecBlocks: [{ numBlocks: 4, dataCodewordsPerBlock: 107 }],\n },\n {\n ecCodewordsPerBlock: 22,\n ecBlocks: [\n { numBlocks: 8, dataCodewordsPerBlock: 37 },\n { numBlocks: 1, dataCodewordsPerBlock: 38 },\n ],\n },\n {\n ecCodewordsPerBlock: 24,\n ecBlocks: [\n { numBlocks: 8, dataCodewordsPerBlock: 20 },\n { numBlocks: 4, dataCodewordsPerBlock: 21 },\n ],\n },\n {\n ecCodewordsPerBlock: 22,\n ecBlocks: [\n { numBlocks: 12, dataCodewordsPerBlock: 11 },\n { numBlocks: 4, dataCodewordsPerBlock: 12 },\n ],\n },\n ],\n },\n {\n infoBits: 0x0E60D,\n versionNumber: 14,\n alignmentPatternCenters: [6, 26, 46, 66],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 3, dataCodewordsPerBlock: 115 },\n { numBlocks: 1, dataCodewordsPerBlock: 116 },\n ],\n },\n {\n ecCodewordsPerBlock: 24,\n ecBlocks: [\n { numBlocks: 4, dataCodewordsPerBlock: 40 },\n { numBlocks: 5, dataCodewordsPerBlock: 41 },\n ],\n },\n {\n ecCodewordsPerBlock: 20,\n ecBlocks: [\n { numBlocks: 11, dataCodewordsPerBlock: 16 },\n { numBlocks: 5, dataCodewordsPerBlock: 17 },\n ],\n },\n {\n ecCodewordsPerBlock: 24,\n ecBlocks: [\n { numBlocks: 11, dataCodewordsPerBlock: 12 },\n { numBlocks: 5, dataCodewordsPerBlock: 13 },\n ],\n },\n ],\n },\n {\n infoBits: 0x0F928,\n versionNumber: 15,\n alignmentPatternCenters: [6, 26, 48, 70],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 22,\n ecBlocks: [\n { numBlocks: 5, dataCodewordsPerBlock: 87 },\n { numBlocks: 1, dataCodewordsPerBlock: 88 },\n ],\n },\n {\n ecCodewordsPerBlock: 24,\n ecBlocks: [\n { numBlocks: 5, dataCodewordsPerBlock: 41 },\n { numBlocks: 5, dataCodewordsPerBlock: 42 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 5, dataCodewordsPerBlock: 24 },\n { numBlocks: 7, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 24,\n ecBlocks: [\n { numBlocks: 11, dataCodewordsPerBlock: 12 },\n { numBlocks: 7, dataCodewordsPerBlock: 13 },\n ],\n },\n ],\n },\n {\n infoBits: 0x10B78,\n versionNumber: 16,\n alignmentPatternCenters: [6, 26, 50, 74],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 24,\n ecBlocks: [\n { numBlocks: 5, dataCodewordsPerBlock: 98 },\n { numBlocks: 1, dataCodewordsPerBlock: 99 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 7, dataCodewordsPerBlock: 45 },\n { numBlocks: 3, dataCodewordsPerBlock: 46 },\n ],\n },\n {\n ecCodewordsPerBlock: 24,\n ecBlocks: [\n { numBlocks: 15, dataCodewordsPerBlock: 19 },\n { numBlocks: 2, dataCodewordsPerBlock: 20 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 3, dataCodewordsPerBlock: 15 },\n { numBlocks: 13, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x1145D,\n versionNumber: 17,\n alignmentPatternCenters: [6, 30, 54, 78],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 1, dataCodewordsPerBlock: 107 },\n { numBlocks: 5, dataCodewordsPerBlock: 108 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 10, dataCodewordsPerBlock: 46 },\n { numBlocks: 1, dataCodewordsPerBlock: 47 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 1, dataCodewordsPerBlock: 22 },\n { numBlocks: 15, dataCodewordsPerBlock: 23 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 2, dataCodewordsPerBlock: 14 },\n { numBlocks: 17, dataCodewordsPerBlock: 15 },\n ],\n },\n ],\n },\n {\n infoBits: 0x12A17,\n versionNumber: 18,\n alignmentPatternCenters: [6, 30, 56, 82],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 5, dataCodewordsPerBlock: 120 },\n { numBlocks: 1, dataCodewordsPerBlock: 121 },\n ],\n },\n {\n ecCodewordsPerBlock: 26,\n ecBlocks: [\n { numBlocks: 9, dataCodewordsPerBlock: 43 },\n { numBlocks: 4, dataCodewordsPerBlock: 44 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 17, dataCodewordsPerBlock: 22 },\n { numBlocks: 1, dataCodewordsPerBlock: 23 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 2, dataCodewordsPerBlock: 14 },\n { numBlocks: 19, dataCodewordsPerBlock: 15 },\n ],\n },\n ],\n },\n {\n infoBits: 0x13532,\n versionNumber: 19,\n alignmentPatternCenters: [6, 30, 58, 86],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 3, dataCodewordsPerBlock: 113 },\n { numBlocks: 4, dataCodewordsPerBlock: 114 },\n ],\n },\n {\n ecCodewordsPerBlock: 26,\n ecBlocks: [\n { numBlocks: 3, dataCodewordsPerBlock: 44 },\n { numBlocks: 11, dataCodewordsPerBlock: 45 },\n ],\n },\n {\n ecCodewordsPerBlock: 26,\n ecBlocks: [\n { numBlocks: 17, dataCodewordsPerBlock: 21 },\n { numBlocks: 4, dataCodewordsPerBlock: 22 },\n ],\n },\n {\n ecCodewordsPerBlock: 26,\n ecBlocks: [\n { numBlocks: 9, dataCodewordsPerBlock: 13 },\n { numBlocks: 16, dataCodewordsPerBlock: 14 },\n ],\n },\n ],\n },\n {\n infoBits: 0x149A6,\n versionNumber: 20,\n alignmentPatternCenters: [6, 34, 62, 90],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 3, dataCodewordsPerBlock: 107 },\n { numBlocks: 5, dataCodewordsPerBlock: 108 },\n ],\n },\n {\n ecCodewordsPerBlock: 26,\n ecBlocks: [\n { numBlocks: 3, dataCodewordsPerBlock: 41 },\n { numBlocks: 13, dataCodewordsPerBlock: 42 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 15, dataCodewordsPerBlock: 24 },\n { numBlocks: 5, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 15, dataCodewordsPerBlock: 15 },\n { numBlocks: 10, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x15683,\n versionNumber: 21,\n alignmentPatternCenters: [6, 28, 50, 72, 94],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 4, dataCodewordsPerBlock: 116 },\n { numBlocks: 4, dataCodewordsPerBlock: 117 },\n ],\n },\n {\n ecCodewordsPerBlock: 26,\n ecBlocks: [{ numBlocks: 17, dataCodewordsPerBlock: 42 }],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 17, dataCodewordsPerBlock: 22 },\n { numBlocks: 6, dataCodewordsPerBlock: 23 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 19, dataCodewordsPerBlock: 16 },\n { numBlocks: 6, dataCodewordsPerBlock: 17 },\n ],\n },\n ],\n },\n {\n infoBits: 0x168C9,\n versionNumber: 22,\n alignmentPatternCenters: [6, 26, 50, 74, 98],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 2, dataCodewordsPerBlock: 111 },\n { numBlocks: 7, dataCodewordsPerBlock: 112 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [{ numBlocks: 17, dataCodewordsPerBlock: 46 }],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 7, dataCodewordsPerBlock: 24 },\n { numBlocks: 16, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 24,\n ecBlocks: [{ numBlocks: 34, dataCodewordsPerBlock: 13 }],\n },\n ],\n },\n {\n infoBits: 0x177EC,\n versionNumber: 23,\n alignmentPatternCenters: [6, 30, 54, 74, 102],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 4, dataCodewordsPerBlock: 121 },\n { numBlocks: 5, dataCodewordsPerBlock: 122 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 4, dataCodewordsPerBlock: 47 },\n { numBlocks: 14, dataCodewordsPerBlock: 48 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 11, dataCodewordsPerBlock: 24 },\n { numBlocks: 14, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 16, dataCodewordsPerBlock: 15 },\n { numBlocks: 14, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x18EC4,\n versionNumber: 24,\n alignmentPatternCenters: [6, 28, 54, 80, 106],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 6, dataCodewordsPerBlock: 117 },\n { numBlocks: 4, dataCodewordsPerBlock: 118 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 6, dataCodewordsPerBlock: 45 },\n { numBlocks: 14, dataCodewordsPerBlock: 46 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 11, dataCodewordsPerBlock: 24 },\n { numBlocks: 16, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 30, dataCodewordsPerBlock: 16 },\n { numBlocks: 2, dataCodewordsPerBlock: 17 },\n ],\n },\n ],\n },\n {\n infoBits: 0x191E1,\n versionNumber: 25,\n alignmentPatternCenters: [6, 32, 58, 84, 110],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 26,\n ecBlocks: [\n { numBlocks: 8, dataCodewordsPerBlock: 106 },\n { numBlocks: 4, dataCodewordsPerBlock: 107 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 8, dataCodewordsPerBlock: 47 },\n { numBlocks: 13, dataCodewordsPerBlock: 48 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 7, dataCodewordsPerBlock: 24 },\n { numBlocks: 22, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 22, dataCodewordsPerBlock: 15 },\n { numBlocks: 13, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x1AFAB,\n versionNumber: 26,\n alignmentPatternCenters: [6, 30, 58, 86, 114],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 10, dataCodewordsPerBlock: 114 },\n { numBlocks: 2, dataCodewordsPerBlock: 115 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 19, dataCodewordsPerBlock: 46 },\n { numBlocks: 4, dataCodewordsPerBlock: 47 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 28, dataCodewordsPerBlock: 22 },\n { numBlocks: 6, dataCodewordsPerBlock: 23 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 33, dataCodewordsPerBlock: 16 },\n { numBlocks: 4, dataCodewordsPerBlock: 17 },\n ],\n },\n ],\n },\n {\n infoBits: 0x1B08E,\n versionNumber: 27,\n alignmentPatternCenters: [6, 34, 62, 90, 118],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 8, dataCodewordsPerBlock: 122 },\n { numBlocks: 4, dataCodewordsPerBlock: 123 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 22, dataCodewordsPerBlock: 45 },\n { numBlocks: 3, dataCodewordsPerBlock: 46 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 8, dataCodewordsPerBlock: 23 },\n { numBlocks: 26, dataCodewordsPerBlock: 24 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 12, dataCodewordsPerBlock: 15 },\n { numBlocks: 28, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x1CC1A,\n versionNumber: 28,\n alignmentPatternCenters: [6, 26, 50, 74, 98, 122],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 3, dataCodewordsPerBlock: 117 },\n { numBlocks: 10, dataCodewordsPerBlock: 118 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 3, dataCodewordsPerBlock: 45 },\n { numBlocks: 23, dataCodewordsPerBlock: 46 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 4, dataCodewordsPerBlock: 24 },\n { numBlocks: 31, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 11, dataCodewordsPerBlock: 15 },\n { numBlocks: 31, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x1D33F,\n versionNumber: 29,\n alignmentPatternCenters: [6, 30, 54, 78, 102, 126],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 7, dataCodewordsPerBlock: 116 },\n { numBlocks: 7, dataCodewordsPerBlock: 117 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 21, dataCodewordsPerBlock: 45 },\n { numBlocks: 7, dataCodewordsPerBlock: 46 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 1, dataCodewordsPerBlock: 23 },\n { numBlocks: 37, dataCodewordsPerBlock: 24 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 19, dataCodewordsPerBlock: 15 },\n { numBlocks: 26, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x1ED75,\n versionNumber: 30,\n alignmentPatternCenters: [6, 26, 52, 78, 104, 130],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 5, dataCodewordsPerBlock: 115 },\n { numBlocks: 10, dataCodewordsPerBlock: 116 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 19, dataCodewordsPerBlock: 47 },\n { numBlocks: 10, dataCodewordsPerBlock: 48 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 15, dataCodewordsPerBlock: 24 },\n { numBlocks: 25, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 23, dataCodewordsPerBlock: 15 },\n { numBlocks: 25, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x1F250,\n versionNumber: 31,\n alignmentPatternCenters: [6, 30, 56, 82, 108, 134],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 13, dataCodewordsPerBlock: 115 },\n { numBlocks: 3, dataCodewordsPerBlock: 116 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 2, dataCodewordsPerBlock: 46 },\n { numBlocks: 29, dataCodewordsPerBlock: 47 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 42, dataCodewordsPerBlock: 24 },\n { numBlocks: 1, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 23, dataCodewordsPerBlock: 15 },\n { numBlocks: 28, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x209D5,\n versionNumber: 32,\n alignmentPatternCenters: [6, 34, 60, 86, 112, 138],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [{ numBlocks: 17, dataCodewordsPerBlock: 115 }],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 10, dataCodewordsPerBlock: 46 },\n { numBlocks: 23, dataCodewordsPerBlock: 47 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 10, dataCodewordsPerBlock: 24 },\n { numBlocks: 35, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 19, dataCodewordsPerBlock: 15 },\n { numBlocks: 35, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x216F0,\n versionNumber: 33,\n alignmentPatternCenters: [6, 30, 58, 86, 114, 142],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 17, dataCodewordsPerBlock: 115 },\n { numBlocks: 1, dataCodewordsPerBlock: 116 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 14, dataCodewordsPerBlock: 46 },\n { numBlocks: 21, dataCodewordsPerBlock: 47 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 29, dataCodewordsPerBlock: 24 },\n { numBlocks: 19, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 11, dataCodewordsPerBlock: 15 },\n { numBlocks: 46, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x228BA,\n versionNumber: 34,\n alignmentPatternCenters: [6, 34, 62, 90, 118, 146],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 13, dataCodewordsPerBlock: 115 },\n { numBlocks: 6, dataCodewordsPerBlock: 116 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 14, dataCodewordsPerBlock: 46 },\n { numBlocks: 23, dataCodewordsPerBlock: 47 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 44, dataCodewordsPerBlock: 24 },\n { numBlocks: 7, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 59, dataCodewordsPerBlock: 16 },\n { numBlocks: 1, dataCodewordsPerBlock: 17 },\n ],\n },\n ],\n },\n {\n infoBits: 0x2379F,\n versionNumber: 35,\n alignmentPatternCenters: [6, 30, 54, 78, 102, 126, 150],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 12, dataCodewordsPerBlock: 121 },\n { numBlocks: 7, dataCodewordsPerBlock: 122 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 12, dataCodewordsPerBlock: 47 },\n { numBlocks: 26, dataCodewordsPerBlock: 48 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 39, dataCodewordsPerBlock: 24 },\n { numBlocks: 14, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 22, dataCodewordsPerBlock: 15 },\n { numBlocks: 41, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x24B0B,\n versionNumber: 36,\n alignmentPatternCenters: [ 6, 24, 50, 76, 102, 128, 154 ],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 6, dataCodewordsPerBlock: 121 },\n { numBlocks: 14, dataCodewordsPerBlock: 122 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 6, dataCodewordsPerBlock: 47 },\n { numBlocks: 34, dataCodewordsPerBlock: 48 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 46, dataCodewordsPerBlock: 24 },\n { numBlocks: 10, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 2, dataCodewordsPerBlock: 15 },\n { numBlocks: 64, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x2542E,\n versionNumber: 37,\n alignmentPatternCenters: [ 6, 28, 54, 80, 106, 132, 158 ],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 17, dataCodewordsPerBlock: 122 },\n { numBlocks: 4, dataCodewordsPerBlock: 123 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 29, dataCodewordsPerBlock: 46 },\n { numBlocks: 14, dataCodewordsPerBlock: 47 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 49, dataCodewordsPerBlock: 24 },\n { numBlocks: 10, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 24, dataCodewordsPerBlock: 15 },\n { numBlocks: 46, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x26A64,\n versionNumber: 38,\n alignmentPatternCenters: [ 6, 32, 58, 84, 110, 136, 162 ],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 4, dataCodewordsPerBlock: 122 },\n { numBlocks: 18, dataCodewordsPerBlock: 123 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 13, dataCodewordsPerBlock: 46 },\n { numBlocks: 32, dataCodewordsPerBlock: 47 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 48, dataCodewordsPerBlock: 24 },\n { numBlocks: 14, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 42, dataCodewordsPerBlock: 15 },\n { numBlocks: 32, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x27541,\n versionNumber: 39,\n alignmentPatternCenters: [ 6, 26, 54, 82, 110, 138, 166 ],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 20, dataCodewordsPerBlock: 117 },\n { numBlocks: 4, dataCodewordsPerBlock: 118 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 40, dataCodewordsPerBlock: 47 },\n { numBlocks: 7, dataCodewordsPerBlock: 48 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 43, dataCodewordsPerBlock: 24 },\n { numBlocks: 22, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 10, dataCodewordsPerBlock: 15 },\n { numBlocks: 67, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n {\n infoBits: 0x28C69,\n versionNumber: 40,\n alignmentPatternCenters: [ 6, 30, 58, 86, 114, 142, 170 ],\n errorCorrectionLevels: [\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 19, dataCodewordsPerBlock: 118 },\n { numBlocks: 6, dataCodewordsPerBlock: 119 },\n ],\n },\n {\n ecCodewordsPerBlock: 28,\n ecBlocks: [\n { numBlocks: 18, dataCodewordsPerBlock: 47 },\n { numBlocks: 31, dataCodewordsPerBlock: 48 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 34, dataCodewordsPerBlock: 24 },\n { numBlocks: 34, dataCodewordsPerBlock: 25 },\n ],\n },\n {\n ecCodewordsPerBlock: 30,\n ecBlocks: [\n { numBlocks: 20, dataCodewordsPerBlock: 15 },\n { numBlocks: 61, dataCodewordsPerBlock: 16 },\n ],\n },\n ],\n },\n];\n","import jsQR from '../node_modules/jsqr-es6/dist/jsQR.js';\n\nlet inversionAttempts = 'dontInvert';\nlet grayscaleWeights = {\n // weights for quick luma integer approximation (https://en.wikipedia.org/wiki/YUV#Full_swing_for_BT.601)\n red: 77,\n green: 150,\n blue: 29,\n useIntegerApproximation: true,\n};\n\nself.onmessage = event => {\n const type = event['data']['type'];\n const data = event['data']['data'];\n\n switch (type) {\n case 'decode':\n decode(data);\n break;\n case 'grayscaleWeights':\n setGrayscaleWeights(data);\n break;\n case 'inversionMode':\n setInversionMode(data);\n break;\n case 'close':\n // close after earlier messages in the event loop finished processing\n self.close();\n break;\n }\n};\n\nfunction decode(data) {\n const rgbaData = data['data'];\n const width = data['width'];\n const height = data['height'];\n const result = jsQR(rgbaData, width, height, {\n inversionAttempts: inversionAttempts,\n greyScaleWeights: grayscaleWeights,\n });\n self.postMessage({\n type: 'qrResult',\n data: result? result.data : null,\n });\n}\n\nfunction setGrayscaleWeights(data) {\n // update grayscaleWeights in a closure compiler compatible fashion\n grayscaleWeights.red = data['red'];\n grayscaleWeights.green = data['green'];\n grayscaleWeights.blue = data['blue'];\n grayscaleWeights.useIntegerApproximation = data['useIntegerApproximation'];\n}\n\nfunction setInversionMode(inversionMode) {\n switch (inversionMode) {\n case 'original':\n inversionAttempts = 'dontInvert';\n break;\n case 'invert':\n inversionAttempts = 'onlyInvert';\n break;\n case 'both':\n inversionAttempts = 'attemptBoth';\n break;\n default:\n throw new Error('Invalid inversion mode');\n }\n}\n"],"names":["decodeByte","stream","size","text","i","length","bytes","push","b","decodeURIComponent","map","substr","toString","join","decode","data","version","chunks","available","mode","ModeByte","Terminator","result","ECI","readBits","type","Mode","assignmentNumber","Numeric","num","Error","a","c","numericResult","Alphanumeric","AlphanumericCharacterCodes","charCodeAt","alphanumericResult","Byte","byteResult","Kanji","Math","floor","k","kanjiResult","StructuredAppend","currentSequence","totalSequence","parity","addOrSubtractGF","runEuclideanAlgorithm","field","R","degree","tLast","zero","t","one","r","rLast","isZero","rLastLast","q","dltInverse","addOrSubtract","buildMonomial","degreeDiff","scale","multiplyByMonomial","multiplyPoly","tLastLast","sigmaTildeAtZero","multiply","inverse","twoS","outputBytes","set","error","s","syndromeCoefficients","evaluation","syndrome","sigmaOmega","numErrors","errorLocator","getCoefficient","errorCount","evaluateAt","errorLocations","denominator","j","xiInverse","errorEvaluator","generatorBase","position","numBitsDiffering","x","y","z","bitCount","pushBit","bit","byte","readCodewords","matrix","formatInfo","dimension","setRegion","versionNumber","bitsRead","currentByte","readingUp","columnIndex","columnOffset","get","dataMask","codewords","readVersion","provisionalVersion","VERSIONS","topRightVersionBits","bottomLeftVersionBits","bestDifference","Infinity","bestVersion","infoBits","difference","readFormatInformation","topLeftFormatInfoBits","topRightBottomRightFormatInfoBits","bestFormatInfo","bits","getDataBlocks","ecLevel","totalCodewords","ecInfo","ecBlocks","forEach","block","numBlocks","dataBlocks","numDataCodewords","dataCodewordsPerBlock","ecCodewordsPerBlock","slice","shortBlockSize","dataBlock","shift","largeBlockCount","smallBlockCount","decodeMatrix","errorCorrectionLevel","resultIndex","correctedBytes","resultBytes","decodeData","squareToQuadrilateral","p1","p2","p3","p4","dx3","dy3","a11","a12","a13","a21","a22","a23","a31","a32","a33","quadrilateralToSquare","sToQ","extract","image","location","topRight","alignmentPattern","bottomLeft","qToS","sourcePixel","mappingFunction","sum","values","reduce","reorderFinderPatterns","pattern1","pattern2","pattern3","topLeft","twoThreeDistance","oneTwoDistance","oneThreeDistance","computeDimension","countBlackWhiteRun","moduleSize","topDimension","sideDimension","countBlackWhiteRunTowardsPoint","origin","end","steep","fromX","fromY","toX","toY","dx","currentPixel","xStep","realX","realY","switchPoints","dy","yStep","distances","distance","ceil","awayFromEnd","concat","middleValue","towardsEnd","scoreBlackWhiteRun","sequence","ratios","ratio","pow","averageSize","scorePattern","point","max","min","width","height","vertError","diagDownError","diagUpError","avgSize","recenterLocation","p","leftX","round","rightX","topY","bottomY","locate","activeFinderPatternQuads","activeAlignmentPatternQuads","lastBit","scans","v","abs","averageFinderPatternBlocksize","averageAlignmentPatternBlocksize","validFinderPattern","startX","endX","bottom","matchingQuads","line","top","validAlignmentPattern","finderPatternQuads","filter","alignmentPatternQuads","quad","scoredFinderPatternPositions","score","sort","otherPoint","otherPoints","finderPatternGroups","points","alignment","midTopRight","midTopLeft","midBottomLeft","centeredAlignment","findAlignmentPattern","e","correctionToTopLeft","sizeScore","expectedAlignmentPattern","scan","locations","decoded","binaryData","topRightCorner","extracted","topLeftCorner","bottomRightCorner","bottomLeftCorner","topRightFinderPattern","topLeftFinderPattern","bottomLeftFinderPattern","bottomRightAlignmentPattern","mergeObject","target","src","Object","keys","opt","jsQR","providedOptions","options","defaultOptions","shouldInvert","greyScaleWeights","canOverwriteImage","pixelCount","bufferOffset","greyscaleBuffer","Uint8ClampedArray","buffer","greyscaleWeights","useIntegerApproximation","greyscalePixels","red","green","blue","blackPointsBuffer","blackPointsCount","verticalRegionCount","verticalRegion","hortizontalRegion","horizontalRegionCount","pixelLumosity","average","blackPoints","averageNeighborBlackPoint","binarized","BitMatrix","binarizedBuffer","createEmpty","inverted","returnInverted","invertedBuffer","xRegion","yRegion","left","lum","threshold","tryInvertedFirst","inversionAttempts","Matrix","bufferSize","value","BitStream","bitOffset","byteOffset","numBits","bitsToNotRead","toRead","GenericGFPoly","coefficients","coefficientsLength","firstNonZero","other","smallerCoefficients","largerCoefficients","lengthDiff","sumDiff","scalar","product","aLength","bLength","aCoeff","bCoefficients","coefficient","GenericGF","primitive","genBase","expTable","Array","logTable","from","alignmentPatternCenters","errorCorrectionLevels","default","grayscaleWeights","self","onmessage","event","self.onmessage","postMessage","close"],"mappings":"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"} \ No newline at end of file diff --git a/apps/qrcode/qr-scanner.umd.min.js b/apps/qrcode/qr-scanner.umd.min.js new file mode 100644 index 000000000..70d61f6f1 --- /dev/null +++ b/apps/qrcode/qr-scanner.umd.min.js @@ -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 diff --git a/apps/qrcode/qr-scanner.umd.min.js.map b/apps/qrcode/qr-scanner.umd.min.js.map new file mode 100644 index 000000000..c4f086333 --- /dev/null +++ b/apps/qrcode/qr-scanner.umd.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"qr-scanner.umd.min.js","sources":["src/qr-scanner.js"],"sourcesContent":["export default class QrScanner {\n /* async */\n static hasCamera() {\n return QrScanner.listCameras(false)\n .then(cameras => !!cameras.length)\n .catch(() => false);\n }\n\n /* async */\n static listCameras(requestLabels = false) {\n if (!navigator.mediaDevices) return Promise.resolve([]);\n\n // Note that enumerateDevices can always be called and does not prompt the user for permission.\n // However, enumerateDevices only includes device labels if served via https and an active media stream exists\n // or permission to access the camera was given. Therefore, ask for camera permission by opening a stream, if\n // labels were requested.\n let openedStream = null;\n return (requestLabels\n ? navigator.mediaDevices.getUserMedia({ audio: false, video: true })\n .then(stream => openedStream = stream)\n // Fail gracefully, especially if the device has no camera or on mobile when the camera is already in\n // use and some browsers disallow a second stream.\n .catch(() => {})\n : Promise.resolve()\n )\n .then(() => navigator.mediaDevices.enumerateDevices())\n .then(devices => devices.filter(device => device.kind === 'videoinput').map((device, i) => ({\n id: device.deviceId,\n label: device.label || (i === 0 ? 'Default Camera' : `Camera ${i + 1}`),\n })))\n .finally(() => {\n // close the stream we just opened for getting camera access for listing the device labels\n if (!openedStream) return;\n for (const track of openedStream.getTracks()) {\n track.stop();\n openedStream.removeTrack(track);\n }\n });\n }\n\n constructor(\n video,\n onDecode,\n canvasSizeOrOnDecodeError = this._onDecodeError,\n canvasSizeOrCalculateScanRegion = this._calculateScanRegion,\n preferredCamera = 'environment'\n ) {\n this.$video = video;\n this.$canvas = document.createElement('canvas');\n this._onDecode = onDecode;\n this._legacyCanvasSize = QrScanner.DEFAULT_CANVAS_SIZE;\n this._preferredCamera = preferredCamera;\n this._active = false;\n this._paused = false;\n this._flashOn = false;\n\n if (typeof canvasSizeOrOnDecodeError === 'number') {\n // legacy function signature where the third argument is the canvas size\n this._legacyCanvasSize = canvasSizeOrOnDecodeError;\n console.warn('You\\'re using a deprecated version of the QrScanner constructor which will be removed in '\n + 'the future');\n } else {\n this._onDecodeError = canvasSizeOrOnDecodeError;\n }\n\n if (typeof canvasSizeOrCalculateScanRegion === 'number') {\n // legacy function signature where the fourth argument is the canvas size\n this._legacyCanvasSize = canvasSizeOrCalculateScanRegion;\n console.warn('You\\'re using a deprecated version of the QrScanner constructor which will be removed in '\n + 'the future');\n } else {\n this._calculateScanRegion = canvasSizeOrCalculateScanRegion;\n }\n\n this._scanRegion = this._calculateScanRegion(video);\n\n this._onPlay = this._onPlay.bind(this);\n this._onLoadedMetaData = this._onLoadedMetaData.bind(this);\n this._onVisibilityChange = this._onVisibilityChange.bind(this);\n\n video.disablePictureInPicture = true;\n // Allow inline playback on iPhone instead of requiring full screen playback,\n // see https://webkit.org/blog/6784/new-video-policies-for-ios/\n video.playsInline = true;\n // Allow play() on iPhone without requiring a user gesture. Should not really be needed as camera stream\n // includes no audio, but just to be safe.\n video.muted = true;\n\n // Avoid Safari stopping the video stream on a hidden video.\n // See https://github.com/cozmo/jsQR/issues/185\n let shouldHideVideo = false;\n if (video.hidden) {\n video.hidden = false;\n shouldHideVideo = true;\n }\n if (!document.body.contains(video)) {\n document.body.appendChild(video);\n shouldHideVideo = true;\n }\n requestAnimationFrame(() => {\n // Checking in requestAnimationFrame which should avoid a potential additional re-flow for getComputedStyle.\n const computedStyle = window.getComputedStyle(video);\n if (computedStyle.display === 'none') {\n video.style.setProperty('display', 'block', 'important');\n shouldHideVideo = true;\n }\n if (computedStyle.visibility !== 'visible') {\n video.style.setProperty('visibility', 'visible', 'important');\n shouldHideVideo = true;\n }\n if (shouldHideVideo) {\n // Hide the video in a way that doesn't cause Safari to stop the playback.\n console.warn('QrScanner has overwritten the video hiding style to avoid Safari stopping the playback.');\n video.style.opacity = 0;\n video.style.width = 0;\n video.style.height = 0;\n }\n });\n\n video.addEventListener('play', this._onPlay);\n video.addEventListener('loadedmetadata', this._onLoadedMetaData);\n document.addEventListener('visibilitychange', this._onVisibilityChange);\n\n this._qrEnginePromise = QrScanner.createQrEngine();\n }\n\n /* async */\n hasFlash() {\n let openedStream = null;\n return (this.$video.srcObject\n ? Promise.resolve(this.$video.srcObject.getVideoTracks()[0])\n : this._getCameraStream().then(({ stream }) => {\n console.warn('Call hasFlash after successfully starting the scanner to avoid creating '\n + 'a temporary video stream');\n openedStream = stream;\n return stream.getVideoTracks()[0];\n })\n )\n .then((track) => 'torch' in track.getSettings())\n .catch(() => false)\n .finally(() => {\n // close the stream we just opened for detecting whether it supports flash\n if (!openedStream) return;\n for (const track of openedStream.getTracks()) {\n track.stop();\n openedStream.removeTrack(track);\n }\n });\n }\n\n isFlashOn() {\n return this._flashOn;\n }\n\n /* async */\n toggleFlash() {\n if (this._flashOn) {\n return this.turnFlashOff();\n } else {\n return this.turnFlashOn();\n }\n }\n\n /* async */\n turnFlashOn() {\n if (this._flashOn) return Promise.resolve();\n this._flashOn = true;\n if (!this._active || this._paused) return Promise.resolve(); // flash will be turned on later on .start()\n return this.hasFlash().then((hasFlash) => {\n if (!hasFlash) return Promise.reject('No flash available');\n // Note that the video track is guaranteed to exist at this point\n return this.$video.srcObject.getVideoTracks()[0].applyConstraints({\n advanced: [{ torch: true }],\n });\n }).catch(() => {\n this._flashOn = false;\n throw e;\n });\n }\n\n /* async */\n turnFlashOff() {\n if (!this._flashOn) return;\n // applyConstraints with torch: false does not work to turn the flashlight off, as a stream's torch stays\n // continuously on, see https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#torch. Therefore,\n // we have to stop the stream to turn the flashlight off.\n this._flashOn = false;\n return this._restartVideoStream();\n }\n\n destroy() {\n this.$video.removeEventListener('loadedmetadata', this._onLoadedMetaData);\n this.$video.removeEventListener('play', this._onPlay);\n document.removeEventListener('visibilitychange', this._onVisibilityChange);\n\n this.stop();\n QrScanner._postWorkerMessage(this._qrEnginePromise, 'close');\n }\n\n /* async */\n start() {\n if (this._active && !this._paused) {\n return Promise.resolve();\n }\n if (window.location.protocol !== 'https:') {\n // warn but try starting the camera anyways\n console.warn('The camera stream is only accessible if the page is transferred via https.');\n }\n this._active = true;\n if (document.hidden) {\n // camera will be started as soon as tab is in foreground\n return Promise.resolve();\n }\n this._paused = false;\n if (this.$video.srcObject) {\n // camera stream already/still set\n this.$video.play();\n return Promise.resolve();\n }\n\n return this._getCameraStream()\n .then(({ stream, facingMode }) => {\n this.$video.srcObject = stream;\n this.$video.play();\n this._setVideoMirror(facingMode);\n\n // Restart the flash if it was previously on\n if (this._flashOn) {\n this._flashOn = false; // force turnFlashOn to restart the flash\n this.turnFlashOn().catch(() => {});\n }\n })\n .catch(e => {\n this._active = false;\n throw e;\n });\n }\n\n stop() {\n this.pause();\n this._active = false;\n }\n\n /* async */\n pause(stopStreamImmediately = false) {\n this._paused = true;\n if (!this._active) {\n return Promise.resolve(true);\n }\n this.$video.pause();\n\n const stopStream = () => {\n const tracks = this.$video.srcObject ? this.$video.srcObject.getTracks() : [];\n for (const track of tracks) {\n track.stop(); // note that this will also automatically turn the flashlight off\n this.$video.srcObject.removeTrack(track);\n }\n this.$video.srcObject = null;\n };\n\n if (stopStreamImmediately) {\n stopStream();\n return Promise.resolve(true);\n }\n\n return new Promise((resolve) => setTimeout(resolve, 300))\n .then(() => {\n if (!this._paused) return false;\n stopStream();\n return true;\n });\n }\n\n /* async */\n setCamera(facingModeOrDeviceId) {\n if (facingModeOrDeviceId === this._preferredCamera) return Promise.resolve();\n this._preferredCamera = facingModeOrDeviceId;\n // Restart the scanner with the new camera which will also update the video mirror and the scan region.\n return this._restartVideoStream();\n }\n\n /* async */\n static scanImage(imageOrFileOrUrl, scanRegion=null, qrEngine=null, canvas=null, disallowCanvasResizing=false,\n alsoTryWithoutScanRegion=false) {\n const gotExternalWorker = qrEngine instanceof Worker;\n\n let promise = Promise.all([\n qrEngine || QrScanner.createQrEngine(),\n QrScanner._loadImage(imageOrFileOrUrl),\n ]).then(([engine, image]) => {\n qrEngine = engine;\n let canvasContext;\n [canvas, canvasContext] = this._drawToCanvas(image, scanRegion, canvas, disallowCanvasResizing);\n\n if (qrEngine instanceof Worker) {\n if (!gotExternalWorker) {\n // Enable scanning of inverted color qr codes. Not using _postWorkerMessage as it's async\n qrEngine.postMessage({ type: 'inversionMode', data: 'both' });\n }\n return new Promise((resolve, reject) => {\n let timeout, onMessage, onError;\n onMessage = event => {\n if (event.data.type !== 'qrResult') {\n return;\n }\n qrEngine.removeEventListener('message', onMessage);\n qrEngine.removeEventListener('error', onError);\n clearTimeout(timeout);\n if (event.data.data !== null) {\n resolve(event.data.data);\n } else {\n reject(QrScanner.NO_QR_CODE_FOUND);\n }\n };\n onError = (e) => {\n qrEngine.removeEventListener('message', onMessage);\n qrEngine.removeEventListener('error', onError);\n clearTimeout(timeout);\n const errorMessage = !e ? 'Unknown Error' : (e.message || e);\n reject('Scanner error: ' + errorMessage);\n };\n qrEngine.addEventListener('message', onMessage);\n qrEngine.addEventListener('error', onError);\n timeout = setTimeout(() => onError('timeout'), 10000);\n const imageData = canvasContext.getImageData(0, 0, canvas.width, canvas.height);\n qrEngine.postMessage({\n type: 'decode',\n data: imageData\n }, [imageData.data.buffer]);\n });\n } else {\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => reject('Scanner error: timeout'), 10000);\n qrEngine.detect(canvas).then(scanResults => {\n if (!scanResults.length) {\n reject(QrScanner.NO_QR_CODE_FOUND);\n } else {\n resolve(scanResults[0].rawValue);\n }\n }).catch((e) => reject('Scanner error: ' + (e.message || e))).finally(() => clearTimeout(timeout));\n });\n }\n });\n\n if (scanRegion && alsoTryWithoutScanRegion) {\n promise = promise.catch(() =>\n QrScanner.scanImage(imageOrFileOrUrl, null, qrEngine, canvas, disallowCanvasResizing));\n }\n\n promise = promise.finally(() => {\n if (gotExternalWorker) return;\n QrScanner._postWorkerMessage(qrEngine, 'close');\n });\n\n return promise;\n }\n\n setGrayscaleWeights(red, green, blue, useIntegerApproximation = true) {\n // Note that for the native BarcodeDecoder, this is a no-op. However, the native implementations work also\n // well with colored qr codes.\n QrScanner._postWorkerMessage(\n this._qrEnginePromise,\n 'grayscaleWeights',\n { red, green, blue, useIntegerApproximation }\n );\n }\n\n setInversionMode(inversionMode) {\n // Note that for the native BarcodeDecoder, this is a no-op. However, the native implementations scan normal\n // and inverted qr codes by default\n QrScanner._postWorkerMessage(this._qrEnginePromise, 'inversionMode', inversionMode);\n }\n\n /* async */\n static createQrEngine(workerPath = QrScanner.WORKER_PATH) {\n return ('BarcodeDetector' in window && BarcodeDetector.getSupportedFormats\n ? BarcodeDetector.getSupportedFormats()\n : Promise.resolve([])\n )\n .then((supportedFormats) => supportedFormats.indexOf('qr_code') !== -1\n ? new BarcodeDetector({ formats: ['qr_code'] })\n : new Worker(workerPath)\n );\n }\n\n _onPlay() {\n this._scanRegion = this._calculateScanRegion(this.$video);\n this._scanFrame();\n }\n\n _onLoadedMetaData() {\n this._scanRegion = this._calculateScanRegion(this.$video);\n }\n\n _onVisibilityChange() {\n if (document.hidden) {\n this.pause();\n } else if (this._active) {\n this.start();\n }\n }\n\n _calculateScanRegion(video) {\n // Default scan region calculation. Note that this can be overwritten in the constructor.\n const smallestDimension = Math.min(video.videoWidth, video.videoHeight);\n const scanRegionSize = Math.round(2 / 3 * smallestDimension);\n return {\n x: Math.round((video.videoWidth - scanRegionSize) / 2),\n y: Math.round((video.videoHeight - scanRegionSize) / 2),\n width: scanRegionSize,\n height: scanRegionSize,\n downScaledWidth: this._legacyCanvasSize,\n downScaledHeight: this._legacyCanvasSize,\n };\n }\n\n _scanFrame() {\n if (!this._active || this.$video.paused || this.$video.ended) return false;\n // using requestAnimationFrame to avoid scanning if tab is in background\n requestAnimationFrame(() => {\n if (this.$video.readyState <= 1) {\n // Skip scans until the video is ready as drawImage() only works correctly on a video with readyState\n // > 1, see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage#Notes.\n // This also avoids false positives for videos paused after a successful scan which remains visible on\n // the canvas until the video is started again and ready.\n this._scanFrame();\n return;\n }\n this._qrEnginePromise\n .then((qrEngine) => QrScanner.scanImage(this.$video, this._scanRegion, qrEngine, this.$canvas))\n .then(this._onDecode, (error) => {\n if (!this._active) return;\n const errorMessage = error.message || error;\n if (errorMessage.indexOf('service unavailable') !== -1) {\n // When the native BarcodeDetector crashed, create a new one\n this._qrEnginePromise = QrScanner.createQrEngine();\n }\n this._onDecodeError(error);\n })\n .then(() => this._scanFrame());\n });\n }\n\n _onDecodeError(error) {\n // default error handler; can be overwritten in the constructor\n if (error === QrScanner.NO_QR_CODE_FOUND) return;\n console.log(error);\n }\n\n /* async */\n _getCameraStream() {\n if (!navigator.mediaDevices) {\n return Promise.reject('Camera not found.');\n }\n\n const preferenceType = this._preferredCamera === 'environment' || this._preferredCamera === 'user'\n ? 'facingMode'\n : 'deviceId';\n const constraintsWithoutCamera = [{\n width: { min: 1024 }\n }, {\n width: { min: 768 }\n }, {}];\n const constraintsWithCamera = constraintsWithoutCamera.map((constraint) => Object.assign({}, constraint, {\n [preferenceType]: { exact: this._preferredCamera },\n }));\n\n // First try constraints with camera, then without camera. Using reduceRight as the Promise is build in a\n // bottom up fashion.\n return [...constraintsWithCamera, ...constraintsWithoutCamera].reduceRight((fallback, constraint) =>\n () => navigator.mediaDevices.getUserMedia({ video: constraint, audio: false })\n .then((stream) => ({\n stream,\n // Try to determine the facing mode from the stream, otherwise use a guess or 'environment' as\n // default. Note that the guess is not always accurate as Safari returns cameras of different facing\n // mode, even for exact facingMode constraints.\n facingMode: this._getFacingMode(stream)\n || (constraint.facingMode\n ? this._preferredCamera // _preferredCamera is a facing mode and we are able to fulfill it\n : (this._preferredCamera === 'environment'\n ? 'user' // switch as _preferredCamera was environment but we are not able to fulfill it\n : 'environment' // switch from unfulfilled user facingMode or default to environment\n )\n ),\n }))\n .catch(fallback),\n () => Promise.reject('Camera not found.')\n )();\n }\n\n /* async */\n _restartVideoStream() {\n // Note that we always pause the stream and not only if !this._paused as even if this._paused === true, the\n // stream might still be running, as it's by default only stopped after a delay of 300ms.\n const wasPaused = this._paused;\n return this.pause(true).then((paused) => {\n if (!paused || wasPaused || !this._active) return;\n return this.start();\n });\n }\n\n _setVideoMirror(facingMode) {\n // in user facing mode mirror the video to make it easier for the user to position the QR code\n const scaleFactor = facingMode==='user'? -1 : 1;\n this.$video.style.transform = 'scaleX(' + scaleFactor + ')';\n }\n\n _getFacingMode(videoStream) {\n const videoTrack = videoStream.getVideoTracks()[0];\n if (!videoTrack) return null; // unknown\n // inspired by https://github.com/JodusNodus/react-qr-reader/blob/master/src/getDeviceId.js#L13\n return /rear|back|environment/i.test(videoTrack.label)\n ? 'environment'\n : /front|user|face/i.test(videoTrack.label)\n ? 'user'\n : null; // unknown\n }\n\n static _drawToCanvas(image, scanRegion=null, canvas=null, disallowCanvasResizing=false) {\n canvas = canvas || document.createElement('canvas');\n const scanRegionX = scanRegion && scanRegion.x? scanRegion.x : 0;\n const scanRegionY = scanRegion && scanRegion.y? scanRegion.y : 0;\n const scanRegionWidth = scanRegion && scanRegion.width? scanRegion.width : image.width || image.videoWidth;\n const scanRegionHeight = scanRegion && scanRegion.height? scanRegion.height : image.height || image.videoHeight;\n\n if (!disallowCanvasResizing) {\n const canvasWidth = scanRegion && scanRegion.downScaledWidth\n ? scanRegion.downScaledWidth\n : scanRegionWidth;\n const canvasHeight = scanRegion && scanRegion.downScaledHeight\n ? scanRegion.downScaledHeight\n : scanRegionHeight;\n // Setting the canvas width or height clears the canvas, even if the values didn't change, therefore only\n // set them if they actually changed.\n if (canvas.width !== canvasWidth) {\n canvas.width = canvasWidth;\n }\n if (canvas.height !== canvasHeight) {\n canvas.height = canvasHeight;\n }\n }\n\n const context = canvas.getContext('2d', { alpha: false });\n context.imageSmoothingEnabled = false; // gives less blurry images\n context.drawImage(\n image,\n scanRegionX, scanRegionY, scanRegionWidth, scanRegionHeight,\n 0, 0, canvas.width, canvas.height\n );\n return [canvas, context];\n }\n\n /* async */\n static _loadImage(imageOrFileOrBlobOrUrl) {\n if (imageOrFileOrBlobOrUrl instanceof HTMLCanvasElement || imageOrFileOrBlobOrUrl instanceof HTMLVideoElement\n || window.ImageBitmap && imageOrFileOrBlobOrUrl instanceof window.ImageBitmap\n || window.OffscreenCanvas && imageOrFileOrBlobOrUrl instanceof window.OffscreenCanvas) {\n return Promise.resolve(imageOrFileOrBlobOrUrl);\n } else if (imageOrFileOrBlobOrUrl instanceof Image) {\n return QrScanner._awaitImageLoad(imageOrFileOrBlobOrUrl).then(() => imageOrFileOrBlobOrUrl);\n } else if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob\n || imageOrFileOrBlobOrUrl instanceof URL || typeof(imageOrFileOrBlobOrUrl)==='string') {\n const image = new Image();\n if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob) {\n image.src = URL.createObjectURL(imageOrFileOrBlobOrUrl);\n } else {\n image.src = imageOrFileOrBlobOrUrl;\n }\n return QrScanner._awaitImageLoad(image).then(() => {\n if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob) {\n URL.revokeObjectURL(image.src);\n }\n return image;\n });\n } else {\n return Promise.reject('Unsupported image type.');\n }\n }\n\n /* async */\n static _awaitImageLoad(image) {\n return new Promise((resolve, reject) => {\n if (image.complete && image.naturalWidth!==0) {\n // already loaded\n resolve();\n } else {\n let onLoad, onError;\n onLoad = () => {\n image.removeEventListener('load', onLoad);\n image.removeEventListener('error', onError);\n resolve();\n };\n onError = () => {\n image.removeEventListener('load', onLoad);\n image.removeEventListener('error', onError);\n reject('Image load error');\n };\n image.addEventListener('load', onLoad);\n image.addEventListener('error', onError);\n }\n });\n }\n\n /* async */\n static _postWorkerMessage(qrEngineOrQrEnginePromise, type, data) {\n return Promise.resolve(qrEngineOrQrEnginePromise).then((qrEngine) => {\n if (!(qrEngine instanceof Worker)) return;\n qrEngine.postMessage({ type, data });\n });\n }\n}\nQrScanner.DEFAULT_CANVAS_SIZE = 400;\nQrScanner.NO_QR_CODE_FOUND = 'No QR code found';\nQrScanner.WORKER_PATH = 'qr-scanner-worker.min.js';\n"],"names":["QrScanner","listCameras","then","cameras","length","catch","requestLabels","navigator","mediaDevices","Promise","resolve","openedStream","getUserMedia","audio","video","stream","enumerateDevices","devices","filter","device","kind","map","i","id","deviceId","label","finally","track","stop","removeTrack","onDecode","canvasSizeOrOnDecodeError","_onDecodeError","canvasSizeOrCalculateScanRegion","_calculateScanRegion","preferredCamera","$video","$canvas","document","createElement","_onDecode","_legacyCanvasSize","DEFAULT_CANVAS_SIZE","_preferredCamera","_flashOn","_paused","_active","console","warn","_scanRegion","_onPlay","bind","_onLoadedMetaData","_onVisibilityChange","disablePictureInPicture","playsInline","muted","shouldHideVideo","hidden","body","contains","appendChild","requestAnimationFrame","computedStyle","display","style","setProperty","visibility","opacity","width","height","addEventListener","_qrEnginePromise","createQrEngine","srcObject","getVideoTracks","_getCameraStream","getSettings","turnFlashOff","turnFlashOn","hasFlash","applyConstraints","advanced","torch","reject","e","_restartVideoStream","removeEventListener","_postWorkerMessage","window","location","protocol","play","facingMode","_setVideoMirror","pause","stopStreamImmediately","tracks","getTracks","stopStream","setTimeout","facingModeOrDeviceId","imageOrFileOrUrl","scanRegion","qrEngine","canvas","disallowCanvasResizing","alsoTryWithoutScanRegion","promise","all","_loadImage","engine","image","canvasContext","_drawToCanvas","Worker","gotExternalWorker","postMessage","type","data","timeout","onMessage","onError","event","clearTimeout","NO_QR_CODE_FOUND","imageData","buffer","detect","scanResults","rawValue","message","scanImage","red","green","blue","useIntegerApproximation","inversionMode","workerPath","WORKER_PATH","BarcodeDetector","getSupportedFormats","supportedFormats","indexOf","formats","_scanFrame","start","videoHeight","x","Math","round","videoWidth","scanRegionSize","y","downScaledWidth","downScaledHeight","paused","ended","readyState","error","log","min","constraint","preferenceType","exact","constraintsWithoutCamera","reduceRight","fallback","_getFacingMode","wasPaused","transform","videoStream","test","videoTrack","scanRegionWidth","scanRegionHeight","canvasWidth","canvasHeight","alpha","context","imageSmoothingEnabled","drawImage","scanRegionX","scanRegionY","imageOrFileOrBlobOrUrl","HTMLCanvasElement","HTMLVideoElement","ImageBitmap","OffscreenCanvas","Image","_awaitImageLoad","File","Blob","URL","src","createObjectURL","revokeObjectURL","complete","naturalWidth","onLoad","qrEngineOrQrEnginePromise"],"mappings":"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"} \ No newline at end of file diff --git a/apps/rebble/ChangeLog b/apps/rebble/ChangeLog new file mode 100644 index 000000000..16e65d4f9 --- /dev/null +++ b/apps/rebble/ChangeLog @@ -0,0 +1,3 @@ +0.01: First release +0.02: Fix typo to Purple +0.03: Added dependancy on Pedometer Widget diff --git a/apps/rebble/README.md b/apps/rebble/README.md new file mode 100644 index 000000000..712fa4e9b --- /dev/null +++ b/apps/rebble/README.md @@ -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 + +![](screenshot_rebble.png) +![](screenshot_rebble2.png) +![](screenshot_rebble3.png) +![](screenshot_rebble4.png) + +## 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/) diff --git a/apps/rebble/rebble.app.js b/apps/rebble/rebble.app.js new file mode 100644 index 000000000..d186ea8ec --- /dev/null +++ b/apps/rebble/rebble.app.js @@ -0,0 +1,272 @@ +var SunCalc = require("https://raw.githubusercontent.com/mourner/suncalc/master/suncalc.js"); +const SETTINGS_FILE = "rebble.json"; +const LOCATION_FILE = "mylocation.json"; +let settings; +let location; + +Graphics.prototype.setFontLECO1976Regular22 = function(scale) { + // Actual height 22 (21 - 0) + g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/nA/+cD/5wP/nAAAAAAAAPwAA/gAD+AAPwAAAAAD+AAP4AA/gAAAAAAAAAAAAAcOAP//A//8D//wP//AHDgAcOAP//A//8D//wP//AHDgAAAAAAAAH/jgf+OB/44H/jj8OP/w4//Dj/8OPxw/4HD/gcP+Bw/4AAAAAAAP+AA/8AD/wQOHHA4c8D//wP/8A//gAD4AAfAAH/8A//wP//A84cDjhwIP/AA/8AB/wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8ABwAAAAAAAAD8AAP4AA/gAD8AAAAAAAAAAAEAAD+AB//A///v/D//gB/wABwAAAAAADgAA/wAf/4P8///wf/4AP8AAOAAAAAAAAAyAAHcAAPwAD/gAP/AA/8AA/AAH8AAMwAAAAAAAAAAAAADgAAOAAA4AAf8AD/wAP/AA/8AAOAAA4AADgAAAAAAAAAAD8AAfwAB/AAD8AAAAAAAADgAAOAAA4AADgAAOAAA4AADgAAAAAAAAAADgAAOAAA4AADgAAAAAAAAABwAB/AA/8A//gP/gA/wADwAAIAAAAAAD//wP//A//8D//wOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA4AcDgBwOAHA//8D//wP//A//8AABwAAHAAAcAAAAAAAA+f8D5/wPn/A+f8DhxwOHHA4ccDhxwP/HA/8cD/xwP/HAAAAAAAAOAHA4AcDhxwOHHA4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/wAP/AA/8AD/wAAHAAAcAABwAAHAA//8D//wP//A//8AAAAAAAA/98D/3wP/fA/98DhxwOHHA4ccDhxwOH/A4f8Dh/wOH/AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccDh/wOH/A4f8Dh/wAAAAAAAD4AAPgAA+AADgAAOAAA4AADgAAP//A//8D//wP//AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA//8D//wP//A//8AAAAAAAAOA4A4DgDgOAOA4AAAAAAAAOA/A4H8DgfwOA/AAAAAAAAB4AAPwAA/AAD8AAf4ABzgAPPAA8cAHh4AAAAAAAAAAAAHHAAccABxwAHHAAccABxwAHHAAccABxwAHHAAAAAAAAAOHAA4cADzwAPPAAf4AB/gAD8AAPwAAeAAB4AAAAAAAAA+AAD4AAPgAA+ecDh9wOH3A4fcDhwAP/AA/8AD/wAP/AAAAAAAAAP//4///j//+P//44ADjn/OOf845/zjnHOP8c4//zj//OP/84AAAAAAAP//A//8D//wP//A4cADhwAOHAA4cAD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA//8D//wP9/A/j8AAAAAAAA//8D//wP//A//8DgBwOAHA4AcDgBwOAHA4AcDgBwOAHAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA8A8D//wH/+AP/wAf+AAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4ccDhxwOAHA4AcAAAAAAAA//8D//wP//A//8DhwAOHAA4cADhwAOHAA4cADgAAOAAAAAAD//wP//A//8D//wOAHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA//8D//wP//A//8ABwAAHAAAcAABwAP//A//8D//wP//AAAAAAAAP//A//8D//wP//AAAAAAAAOAHA4AcDgBwOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA//8D//wP//A//8AHwAA/AAP8AB/wAPn/A8f8DB/wIH/AAAAAAAAP//A//8D//wP//AAAcAABwAAHAAAcAABwAAHAAAAAAAAP//A//8D//wP//Af8AAP+AAH/AAD8AAHwAD/AB/wAf8AP+AA//8D//wP//AAAAAAAAP//A//8D//wP//AfwAAfwAAfwAAfwAAfwP//A//8D//wAAAAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHAA4cADhwAOHAA/8AD/wAP/AA/8AAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//+P//4///j//+AAA4AADgAAAP//A//8D//wP//A4eADh+AOH8A4f4D/3wP/HA/8MD/wQAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA4AADgAAOAAA//8D//wP//A//8DgAAOAAA4AADgAAAAAA//8D//wP//A//8AABwAAHAAAcAABwP//A//8D//wP//AAAADAAAPgAA/wAD/4AB/8AA/8AAfwAB/AA/8Af+AP/AA/wAD4AAMAAA4AAD+AAP/gA//8AH/wAB/AAf8Af/wP/4A/4AD/gAP/4AH/8AB/wAB/AB/8D//wP/gA/gADgAAIABA4AcDwDwPw/Afn4Af+AA/wAD/AA//AH5+A/D8DwDwOAHAgAEAAAAP/AA/8AD/wAP/AAAf8AB/wAH/AAf8D/wAP/AA/8AD/wAAAAAAAADh/wOH/A4f8Dh/wOHHA4ccDhxwOHHA/8cD/xwP/HA/8cAAAAAAAAf//9///3///f//9wAA3AADcAAMAAAOAAA/gAD/wAH/8AB/8AA/wAAPAAAEAAAAHAADcAANwAB3///f//9///wAA"), 32, atob("BwYLDg4UDwYJCQwMBgkGCQ4MDg4ODg4NDg4GBgwMDA4PDg4ODg4NDg4GDQ4MEg8ODQ8ODgwODhQODg4ICQg="), 22+(scale<<8)+(1<<16)); +} + +Graphics.prototype.setFontKdamThmor = function(scale) { + // Actual height 72 (71 - 0) + g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAH+AAAAAAAAAAAAAP/AAAAAAAAAAAAAf/gAAAAAAAAAAAA//gAAAAAAAAAAAA//wAAAAAAAAAAAA//wAAAAAAAAAAAA//wAAAAAAAAAAAA//wAAAAAAAAAAAA//gAAAAAAAAAAAAf/gAAAAAAAAAAAAf/AAAAAAAAAAAAAP+AAAAAAAAAAAAAD8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfAAAAAAAAAAAAAB/AAAAAAAAAAAAAP/AAAAAAAAAAAAA//AAAAAAAAAAAAH/+AAAAAAAAAAAAf/+AAAAAAAAAAAD//+AAAAAAAAAAAf//8AAAAAAAAAAB///4AAAAAAAAAAP///wAAAAAAAAAA////AAAAAAAAAAH///4AAAAAAAAAAf///gAAAAAAAAAD///8AAAAAAAAAAf///wAAAAAAAAAB///+AAAAAAAAAAP///4AAAAAAAAAA////AAAAAAAAAAH///4AAAAAAAAAAf///gAAAAAAAAAD///8AAAAAAAAAAP///wAAAAAAAAAB///+AAAAAAAAAAP///4AAAAAAAAAA////AAAAAAAAAAD///4AAAAAAAAAAP///gAAAAAAAAAAf//8AAAAAAAAAAA///wAAAAAAAAAAA//+AAAAAAAAAAAA//4AAAAAAAAAAAA//AAAAAAAAAAAAA/4AAAAAAAAAAAAA/gAAAAAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf//AAAAAAAAAAAP///+AAAAAAAAAD/////wAAAAAAAAP/////+AAAAAAAA///////gAAAAAAD///////4AAAAAAH///////8AAAAAAf///////+AAAAAA/////////gAAAAB/////////wAAAAB/////////wAAAAD/////////4AAAAH///AAA///8AAAAH//wAAAB//8AAAAP/+AAAAAP/+AAAAP/4AAAAAD/+AAAAf/gAAAAAA//AAAAf/AAAAAAAf/AAAAf+AAAAAAAP/AAAA/+AAAAAAAP/gAAA/8AAAAAAAH/gAAA/8AAAAAAAH/gAAA/8AAAAAAAH/gAAA/8AAAAAAAH/gAAA/8AAAAAAAH/gAAA/8AAAAAAAH/gAAA/8AAAAAAAH/gAAA/8AAAAAAAH/gAAA/+AAAAAAAP/gAAAf+AAAAAAAP/AAAAf/AAAAAAAf/AAAAf/gAAAAAA//AAAAP/4AAAAAD/+AAAAP/+AAAAAP/+AAAAH//gAAAA//8AAAAH///AAAf//8AAAAD/////////4AAAAD/////////wAAAAB/////////wAAAAA/////////gAAAAAf////////AAAAAAH///////8AAAAAAD///////4AAAAAAA///////gAAAAAAAP/////+AAAAAAAAD/////4AAAAAAAAAf////AAAAAAAAAAA///gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAADwAAAAAAAAAAAAAD4AAAAAAAAAAAAAH8AAAAAAAAAAAAAP+AAAAAAAAAAAAAf/AAAAAH/AAAAAA//AAAAAH/AAAAAB//AAAAAH/AAAAAB//AAAAAH/AAAAAD/+AAAAAH/AAAAAH/8AAAAAH/AAAAAP/4AAAAAH/AAAAAf/wAAAAAH/AAAAA//gAAAAAH/AAAAB//gAAAAAH/AAAAB//AAAAAAH/AAAAD/+AAAAAAH/AAAAH/8AAAAAAH/AAAAP//////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAAAAAAAAAAH/AAAAAAAAAAAAAH/AAAAAAAAAAAAAH/AAAAAAAAAAAAAH/AAAAAAAAAAAAAH/AAAAAAAAAAAAAH/AAAAAAAAAAAAAH/AAAAAAAAAAAAAH/AAAAAAAAAAAAAH/AAAAAAAAAAAAAH/AAAAAAAAAAAAAH/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAAAHgAAAAAD/AAAAAA/wAAAAAH/AAAAAD/wAAAAAP/AAAAAH/wAAAAAf/AAAAAP/wAAAAA//AAAAA//wAAAAB//AAAAB//wAAAAD//AAAAB//wAAAAH//AAAAD//wAAAAP//AAAAH//wAAAAf//AAAAH//gAAAA///AAAAP/+AAAAB///AAAAP/4AAAAD///AAAAf/gAAAAH///AAAAf/AAAAAP///AAAAf+AAAAAf///AAAAf+AAAAA//v/AAAA/8AAAAB//P/AAAA/8AAAAD/+P/AAAA/8AAAAH/8P/AAAA/8AAAAP/4P/AAAA/8AAAAf/wf/AAAA/8AAAA//gf/AAAA/8AAAD//Af/AAAA/8AAAH/+Af/AAAA/+AAAP/8Af/AAAA/+AAAf/4Af/AAAAf/AAB//wAf/AAAAf/gAD//gAf/AAAAf/wAf//AAf/AAAAf/+D//+AAf/AAAAP/////8AAf/AAAAP/////4AAf/AAAAH/////wAAf/AAAAH/////gAAf/AAAAD/////AAAf/AAAAB////8AAAf/AAAAA////4AAAf/AAAAAf///wAAAf/AAAAAP///AAAAf/AAAAAD//8AAAAf/AAAAAA//gAAAAP/AAAAAABwAAAAAH/AAAAAAAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAfAAAAAAAAHgAAAA/wAAAAAAA/wAAAA/8AAAAAAD/wAAAB/+AAAAAAH/wAAAB//AAAAAAP/wAAAB//gAAAAA//wAAAB//wAAAAB//wAAAB//4AAAAB//wAAAB//8AAAAD//wAAAA//8AAAAH//wAAAAf/+AAAAH//gAAAAH/+AAAAP/+AAAAAB//AAAAP/4AAAAAA//AAAAf/gAAAAAAf/AAAAf/AAAAAAAf/AAAAf/AAAAAAAP/gAAAf+AAAAAAAP/gAAA/+AAD/gAAH/gAAA/8AAD/gAAH/gAAA/8AAD/gAAH/gAAA/8AAD/gAAH/gAAA/8AAD/gAAH/gAAA/8AAD/gAAH/gAAA/8AAD/gAAH/gAAA/8AAH/wAAH/gAAA/8AAH/wAAP/gAAA/+AAH/wAAP/AAAAf/AAP/4AAf/AAAAf/AAf/4AA//AAAAf/wA//8AB//AAAAf/+H//+AD/+AAAAP//////4f/+AAAAP////v////8AAAAH////v////8AAAAH////H////4AAAAD////H////wAAAAB///+D////wAAAAA///8D////gAAAAAf//4B////AAAAAAP//wA///+AAAAAAD//AAf//4AAAAAAAf4AAH//gAAAAAAAAAAAB/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8AAAAAAAAAAAAAD/gAAAAAAAAAAAAP/wAAAAAAAAAAAAf/wAAAAAAAAAAAA//wAAAAAAAAAAAD//wAAAAAAAAAAAH//wAAAAAAAAAAAP//wAAAAAAAAAAA///wAAAAAAAAAAB///wAAAAAAAAAAD///wAAAAAAAAAAP///wAAAAAAAAAAf///wAAAAAAAAAA//9/wAAAAAAAAAD//x/wAAAAAAAAAH//h/wAAAAAAAAAP/+B/wAAAAAAAAA//8B/wAAAAAAAAB//4B/wAAAAAAAAD//gB/wAAAAAAAAP//AB/wAAAAAAAAf/+AB/wAAAAAAAA//4AB/wAAAAAAAD//wAB/wAAAAAAAH//AAB/wAAAAAAAP/+AAB/wAAAAAAA//8AAB/wAAAAAAB//wAAB/wAAAAAAD//gAAB/wAAAAAAP/+AAAB/wAAAAAAf/8AAAB/wAAAAAAf/5////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAf//////////AAAAAAAAAAB/wAAAAAAAAAAAAB/wAAAAAAAAAAAAB/wAAAAAAAAAAAAB/wAAAAAAAAAAAAB/wAAAAAAAAAAAAB/wAAAAAAAAAAAAB/wAAAAAAAAAAAAB/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAAAAAAAAAAPgAAAAAAAAAAAAA/wAAAAAAAAD4AAB/4AAAAAAAD/4AAB/4AAAAAAD//4AAB/8AAAAAD///8AAB/8AAAAD////8AAB/+AAAAf////8AAA/+AAAAf////+AAA/+AAAAf////+AAAf/AAAAf////8AAAf/AAAAf////8AAAP/AAAAf////8AAAP/AAAAf//4/4AAAH/gAAAf/8A/4AAAH/gAAAf+AA/4AAAH/gAAAf+AA/4AAAH/gAAAf+AA/4AAAH/gAAAf+AA/4AAAH/gAAAf+AA/4AAAH/gAAAf+AB/4AAAH/gAAAf+AB/4AAAH/gAAAf+AA/8AAAP/gAAAf+AA/8AAAP/AAAAf+AA/8AAAP/AAAAf+AA/+AAAf/AAAAf+AA/+AAA//AAAAf+AA//AAB/+AAAAf+AAf/gAD/+AAAAf+AAf/4Af/8AAAAf+AAf/////8AAAAf+AAP/////4AAAAf+AAP/////wAAAAf+AAH/////wAAAAf+AAD/////gAAAAf+AAD/////AAAAAf+AAB////+AAAAAf8AAA////8AAAAAf4AAAP///wAAAAAfgAAAH///AAAAAAAAAAAA//8AAAAAAAAAAAAH/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/4AAAAAAAAAAAB///AAAAAAAAAAAP///wAAAAAAAAAA////8AAAAAAAAAB////+AAAAAAAAAH/////AAAAAAAAAP/////gAAAAAAAA//////wAAAAAAAB//////4AAAAAAAH//////8AAAAAAAP//////8AAAAAAAf//+AP/+AAAAAAB///4AB/+AAAAAAD///wAA/+AAAAAAH///gAAf/AAAAAAf///AAAP/AAAAAA///+AAAP/AAAAAB///+AAAH/gAAAAH//3+AAAH/gAAAAP//v8AAAH/gAAAAf//P8AAAH/gAAAB//+P8AAAH/gAAAD//4f8AAAH/gAAAH//wf8AAAH/gAAAP//gf8AAAH/gAAAf//Af8AAAH/gAAAf/8Af+AAAH/gAAAf/4Af+AAAP/AAAAf/wAf+AAAP/AAAAf/gAf/AAAf/AAAAf/AAP/gAA//AAAAf8AAP/wAB/+AAAAf4AAP/4AD/+AAAAfwAAP//Af/8AAAAfgAAH/////8AAAAeAAAH/////4AAAAcAAAD/////4AAAAYAAAD/////wAAAAQAAAB/////gAAAAAAAAA/////AAAAAAAAAAf///+AAAAAAAAAAP///8AAAAAAAAAAD///wAAAAAAAAAAB///AAAAAAAAAAAAP/8AAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf8AAAAAAAAAAAAAf+AAAAAAAAAAAAAf+AAAAAAAAAAAAAf+AAAAAAAAAAAAAf+AAAAAAAAAAAAAf+AAAAAAAABAAAAf+AAAAAAAAHAAAAf+AAAAAAAAfAAAAf+AAAAAAAB/AAAAf+AAAAAAAH/AAAAf+AAAAAAAf/AAAAf+AAAAAAB//AAAAf+AAAAAAH//AAAAf+AAAAAAf//AAAAf+AAAAAB///AAAAf+AAAAAH///AAAAf+AAAAAf///AAAAf+AAAAB///+AAAAf+AAAAH///8AAAAf+AAAAf///wAAAAf+AAAA////AAAAAf+AAAD///8AAAAAf+AAAP///wAAAAAf+AAA////AAAAAAf+AAD///8AAAAAAf+AAP///wAAAAAAf+AA////AAAAAAAf+AD///8AAAAAAAf+AP///wAAAAAAAf+A////AAAAAAAAf+D///8AAAAAAAAf+P///wAAAAAAAAf+f//+AAAAAAAAAf////4AAAAAAAAAf////gAAAAAAAAAf///+AAAAAAAAAAf///4AAAAAAAAAAf///gAAAAAAAAAAf//+AAAAAAAAAAAf//4AAAAAAAAAAAf//gAAAAAAAAAAAf/+AAAAAAAAAAAAf/4AAAAAAAAAAAAf/gAAAAAAAAAAAAf+AAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP4AAAAAAAAAAAAB//gAAAAAAADwAAH//4AAAAAAA//AAf//8AAAAAAD//wA////AAAAAAP//4B////gAAAAAf//+B////wAAAAA////D////wAAAAB////H////4AAAAD////n////8AAAAH/////////8AAAAH/////////+AAAAP//////wP/+AAAAP//////AB/+AAAAf/wD//8AA//AAAAf/AA//4AAf/AAAAf+AAf/4AAP/AAAAf8AAP/wAAH/AAAA/8AAH/wAAH/gAAA/4AAH/gAAH/gAAA/4AAH/gAAD/gAAA/4AAD/gAAD/gAAA/4AAD/gAAD/gAAA/4AAD/gAAD/gAAA/4AAD/gAAD/gAAA/4AAD/gAAD/gAAA/4AAH/gAAH/gAAA/8AAH/wAAH/gAAAf8AAP/wAAH/gAAAf+AAP/wAAP/AAAAf/AAf/4AAf/AAAAf/wB//8AAf/AAAAP/////+AB//AAAAP//////wH/+AAAAH/////////+AAAAH/////////8AAAAD////n////8AAAAB////H////4AAAAA////D////wAAAAAf//+B////wAAAAAP//8B////gAAAAAH//wA////AAAAAAB//AAf//8AAAAAAAH4AAH//4AAAAAAAAAAAB//gAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/8AAAAAAAAAAAAf//AAAAAAAAAAAB///wAAAAAAAAAAH///4AAAAAAAAAAP///+AAAAAAAAAAf////AAAAAAAAAA/////AAAADAAAAB/////gAAAHAAAAD/////wAAAPAAAAH/////wAAAfAAAAH/////4AAB/AAAAP//A//4AAD/AAAAP/4AH/8AAH/AAAAf/gAD/8AAP/AAAAf/AAB/8AA//AAAAf+AAA/8AB//AAAAf+AAA/8AD//AAAA/8AAAf8AH//AAAA/8AAAf8Af//AAAA/8AAAf8A//+AAAA/8AAAf8B//+AAAA/8AAAf8D//8AAAA/8AAAf8P//wAAAA/8AAAf8f//gAAAA/8AAAf4//+AAAAA/8AAAf5//8AAAAA/8AAAf3//4AAAAAf+AAA////gAAAAAf+AAB////AAAAAAf/AAB///8AAAAAAf/gAD///4AAAAAAP/4AP///gAAAAAAP//B////AAAAAAAH//////+AAAAAAAH//////4AAAAAAAD//////wAAAAAAAB//////AAAAAAAAA/////+AAAAAAAAAf////4AAAAAAAAAP////wAAAAAAAAAH////AAAAAAAAAAB///8AAAAAAAAAAAf//gAAAAAAAAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAD8AAAAAAAA/4AAAP+AAAAAAAB/8AAAf/AAAAAAAB/+AAAf/gAAAAAAD/+AAA//gAAAAAAD//AAA//wAAAAAAD//AAA//wAAAAAAD//AAA//wAAAAAAD//AAA//wAAAAAAD/+AAA//gAAAAAAB/+AAAf/gAAAAAAA/8AAAP/AAAAAAAAf4AAAH+AAAAAAAAHgAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("FCM0NDQ0NDQ0NDQ0GA=="), 90+(scale<<8)+(1<<16)); +} + +var boot_img = require("heatshrink").decompress(atob("oFAwkEogA/AH4A/AH4A/AH4A/AE8AAAoeXoAfeDQUBmcyD7A+Dh///8QD649CiAfaHwUvD4sEHy0DDYIfEICg+Cn4fHICY+DD4nxcgojOHwgfEIAYfRCIQaDD4ZAFD5r7DH4//kAfRCIZ/GAAnwD5p9DX44fTHgYSBf4ofVDAQEBl4fFUAgfOXoQzBgIfFBAIfPP4RAEAoYAB+cRiK/SG4h/WIBAfXIA7CBAAswD55AHn6fUIBMCD65AHl4gCmcziAfQQJqfQQJpiDgk0IDXxQLRAEECaBM+QgRYRYgUIA0CD4ggSQJiDCiAKBICszAAswD55AHABKBVD7BAFABIqBD5pAFABPxD55AOD6BADiIAJQAyxLABwf/gaAPAH4A/AH4ARA==")); +var sunrise_img = require("heatshrink").decompress(atob("oFAwkEogA/AH4A/AH4A/AH4ACp5A/AH4A/AH4AIoEAggfcgAABD/4f/D/4f/CiNPmgfUoYIHoEAggfSoEQgYJGmAUJD5QJBgQ/IIBBKJChiVSCYR1LBZAzTICQyNICAxOICAwPD40xA4UTc5xAFiAuDiAWCAAMBc5hgHDxAgFeCKEDh//AAPwdiKDHh9PD4X0EAX0DyQ+BHoYgFh4+UDwofB/68OAAlBHw6CEQKITBDxAABMCReHUQhgSLxRgDDx9CD4g8DD4sUbqEUH5SABUB4fBDxYfKkQAFkEAiQJGAAcjgECBQ6qBAH4A9Y5wA/AH4Aw")); +var sunset_img = require("heatshrink").decompress(atob("oFAwkEogA/AH4A/AH4A/AH4A/AH4A/AH4AMoEAggfcgAABD/4f/D/4f/CqU0D6lDBA9AgEED6VAiEDBI0wChIfKBIMCH5BAIJRIUMSqQTCOpYLIGaZASGRpAQGJxAQGB4fGmIHCibnOIAsQFwcQCwQABgLnMMA4eIEArwRQgY0DAwwARC44gC+geSORJ8PHw4KTABFBGhRAT+AzLgEPLzZgUKRhgBDx9CD50UbqARMUCBROD5MiAAsggESBIwADkcAgQKHVQIA/AHrHOAH4A/AGA")); + +var drawCount = 0; +var sideBar = 0; +var sunRise = "00:00"; +var sunSet = "00:00"; + +function log_debug(o) { + //console.log(o); +} + +// requires the myLocation app +function loadLocation() { + location = require("Storage").readJSON(LOCATION_FILE,1)||{"lat":51.5072,"lon":0.1276,"location":"London"}; +} + +function loadSettings() { + settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'bg': '#0f0', 'color': 'Green'}; +} + +function extractTime(d){ + var h = d.getHours(), m = d.getMinutes(); + return(("0"+h).substr(-2) + ":" + ("0"+m).substr(-2)); +} + +function updateSunRiseSunSet(lat, lon){ + // get today's sunlight times for lat/lon + var times = SunCalc.getTimes(new Date(), lat, lon); + + // format sunrise time from the Date object + sunRise = extractTime(times.sunrise); + sunSet = extractTime(times.sunset); +} + +// wrapper, makes it easier if we want to switch to a different font later +function setSmallFont() { + g.setFont('Vector', 20); +} + +// set the text color of the sidebar elements that dont change with the Theme +function setTextColor() { + // day and steps + if (settings.color == 'Blue' || settings.color == 'Red') { + g.setColor('#fff'); // white on blue or red best contrast + } else { + g.setColor('#000'); // otherwise black regardless of theme + } +} + +const h = g.getHeight(); +const w = g.getWidth(); +const ha = 2*h/5 - 8; +const h2 = 3*h/5 - 10; +const h3 = 7*h/8; +const w2 = 9*w/14; +const w3 = w2 + ((w - w2)/2); // centre line of the sidebar +const ws = w - w2; // sidebar width +const wb = 40; // battery width + +function draw() { + log_debug("draw()"); + let date = new Date(); + let da = date.toString().split(" "); + let hh = da[4].substr(0,2); + let mm = da[4].substr(3,2); + //const t = 6; + + if (drawCount % 60 == 0) + updateSunRiseSunSet(location.lat, location.lon); + + g.reset(); + g.setColor(g.theme.bg); + g.fillRect(0, 0, w2, h); + g.setColor(settings.bg); + g.fillRect(w2, 0, w, h); + + // time + g.setColor(g.theme.fg); + g.setFontKdamThmor(); + g.setFontAlign(0, -1); + g.drawString(hh, w2/2, 10 + 0); + g.drawString(mm, w2/2, 10 + h/2); + + switch(sideBar) { + case 0: + drawSideBar1(); + break; + case 1: + drawSideBar2(); + break; + case 2: + drawSideBar3(); + break; + } + + drawCount++; + queueDraw(); +} + +function drawSideBar1() { + let date = new Date(); + let da = date.toString().split(" "); + + drawBattery(w2 + (w-w2-wb)/2, h/10, wb, 17); + + setTextColor(); + g.setFont('Vector', 20); + g.setFontAlign(0, -1); + g.drawString(E.getBattery() + '%', w3, (h/10) + 17 + 7); + + drawDateAndCalendar(w3, h/2, da[0], da[2], da[1]); +} + +function drawSideBar2() { + drawBattery(w2 + (w-w2-wb)/2, h/10, wb, 17); + + setTextColor(); + g.setFont('Vector', 20); + g.setFontAlign(0, -1); + g.drawString(E.getBattery() + '%', w3, (h/10) + 17 + 7); + + // steps + g.drawImage(boot_img, w2 + (ws - 64)/2, h/2, { scale: 1 }); + setSmallFont(); + g.setFontAlign(0, -1); + g.drawString(formatSteps(), w3, 7*h/8); +} + +// sunrise, sunset times +function drawSideBar3() { + g.setColor('#fff'); // sunrise white + g.drawImage(sunrise_img, w2 + (ws - 64)/2, 0, { scale: 1 }); + setTextColor(); + setSmallFont(); + g.setFontAlign(0, -1); + g.drawString(sunRise, w3, 64); + + g.setColor('#000'); // sunset black + g.drawImage(sunset_img, w2 + (ws - 64)/2, h/2, { scale: 1 }); + setTextColor(); + setSmallFont(); + g.setFontAlign(0, -1); + g.drawString(sunSet, w3, (h/2) + 64); +} + +function drawDateAndCalendar(x,y,dy,dd,mm) { + // day + setTextColor(); + setSmallFont(); + g.setFontAlign(0, -1); + g.drawString(dy.toUpperCase(), x, y); + + drawCalendar(x - (w/10), y + 28, w/5, 3, dd); + + // month + setTextColor(); + setSmallFont(); + g.setFontAlign(0, -1); + g.drawString(mm.toUpperCase(), x, y + 70); +} + +// at x,y width:wi thicknes:th +function drawCalendar(x,y,wi,th,str) { + g.setColor(g.theme.fg); + g.fillRect(x, y, x + wi, y + wi); + g.setColor(g.theme.bg); + g.fillRect(x + th, y + th, x + wi - th, y + wi - th); + g.setColor(g.theme.fg); + + let hook_t = 6; + // first calendar hook, one third in + g.fillRect(x + (wi/3) - (th/2), y - hook_t, x + wi/3 + th - (th/2), y + hook_t); + // second calendar hook, two thirds in + g.fillRect(x + (2*wi/3) -(th/2), y - hook_t, x + 2*wi/3 + th - (th/2), y + hook_t); + + setSmallFont(); + g.setFontAlign(0, 0); + g.drawString(str, x + wi/2 + th/2, y + wi/2 + th/2); +} + +function drawBattery(x,y,wi,hi) { + g.reset(); + g.setColor(g.theme.fg); + g.fillRect(x,y+2,x+wi-4,y+2+hi); // outer + g.clearRect(x+2,y+2+2,x+wi-4-2,y+2+hi-2); // centre + g.setColor(g.theme.fg); + g.fillRect(x+wi-3,y+2+(((hi - 1)/2)-1),x+wi-2,y+2+(((hi - 1)/2)-1)+4); // contact + g.fillRect(x+3, y+5, x +4 + E.getBattery()*(wi-12)/100, y+hi-1); // the level +} + +function getSteps() { + if (WIDGETS.wpedom !== undefined) { + return WIDGETS.wpedom.getSteps(); + } + return '????'; +} + +// format steps so they fit in the place +function formatSteps() { + var s = getSteps(); + + if ( s == '????') { + return s; + } else if (s < 1000) { + return s + ''; + } else if (s < 10000) { + return '' + (s/1000).toFixed(1) + 'K'; + } + return Math.floor(s / 1000) + 'K'; +} + +function nextSidebar() { + if (++sideBar > 2) sideBar = 0; + log_debug("next: " + sideBar); +} + +function prevSidebar() { + if (--sideBar < 0) sideBar = 2; + log_debug("prev: " + sideBar); +} + +Bangle.setUI("clockupdown", btn=> { + if (btn<0) prevSidebar(); + if (btn>0) nextSidebar(); + draw(); +}); + + +// 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; + nextSidebar(); + draw(); + }, 60000 - (Date.now() % 60000)); +} + + +log_debug("starting.."); +g.clear(); +Bangle.loadWidgets(); +/* + * we are not drawing the widgets as we are taking over the whole screen + * so we will blank out the draw() functions of each widget and change the + * area to the top bar doesn't get cleared. + */ +for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";} +loadSettings(); +loadLocation(); +draw(); // queues the next draw for a minutes time diff --git a/apps/rebble/rebble.icon.js b/apps/rebble/rebble.icon.js new file mode 100644 index 000000000..4c898974e --- /dev/null +++ b/apps/rebble/rebble.icon.js @@ -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")) diff --git a/apps/rebble/rebble.png b/apps/rebble/rebble.png new file mode 100644 index 000000000..69653015c Binary files /dev/null and b/apps/rebble/rebble.png differ diff --git a/apps/rebble/rebble.settings.js b/apps/rebble/rebble.settings.js new file mode 100644 index 000000000..db3bab878 --- /dev/null +++ b/apps/rebble/rebble.settings.js @@ -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(); + }, + } + }); +}) diff --git a/apps/rebble/screenshot_rebble.png b/apps/rebble/screenshot_rebble.png new file mode 100644 index 000000000..3cbd77d8b Binary files /dev/null and b/apps/rebble/screenshot_rebble.png differ diff --git a/apps/rebble/screenshot_rebble2.png b/apps/rebble/screenshot_rebble2.png new file mode 100644 index 000000000..186b8f21c Binary files /dev/null and b/apps/rebble/screenshot_rebble2.png differ diff --git a/apps/rebble/screenshot_rebble3.png b/apps/rebble/screenshot_rebble3.png new file mode 100644 index 000000000..dca65c0fc Binary files /dev/null and b/apps/rebble/screenshot_rebble3.png differ diff --git a/apps/rebble/screenshot_rebble4.png b/apps/rebble/screenshot_rebble4.png new file mode 100644 index 000000000..b52be8be5 Binary files /dev/null and b/apps/rebble/screenshot_rebble4.png differ diff --git a/apps/recorder/ChangeLog b/apps/recorder/ChangeLog index 40240de64..bedc63141 100644 --- a/apps/recorder/ChangeLog +++ b/apps/recorder/ChangeLog @@ -3,3 +3,4 @@ Fix interface.html 0.03: Fix theme and maps/graphing if no GPS 0.04: Multiple bugfixes +0.05: Add recording for coresensor diff --git a/apps/recorder/README.md b/apps/recorder/README.md index ba53a99f2..4a4561f1c 100644 --- a/apps/recorder/README.md +++ b/apps/recorder/README.md @@ -17,6 +17,7 @@ You can record * **GPS** GPS Latitude, Longitude and Altitude * **Steps** Steps counted by the step counter * **HR** Heart rate +* **Core** CoreTemp body temperature **Note:** It is possible for other apps to record information using this app as well. They need to define a `foobar.recorder.js` file - see the `getRecorders` diff --git a/apps/recorder/interface.html b/apps/recorder/interface.html index ad0de4887..0535b2d51 100644 --- a/apps/recorder/interface.html +++ b/apps/recorder/interface.html @@ -18,8 +18,15 @@ ${track[0].Heartrate!==undefined ? ` - Step Count`:``} - + Step Count + `:``} +${track[0].Core!==undefined ? ` + Core Temp + `:``} +${track[0].Skin!==undefined ? ` + Skin Temp + `:``} + Tracks @@ -37,6 +44,12 @@ ${track.map(pt=>` ${0|pt.Heartrate}\n`).join("") ${track[0].Steps!==undefined ? ` ${track.map(pt=>` ${0|pt.Steps}\n`).join("")} `:``} +${track[0].Core!==undefined ? ` +${track.map(pt=>` ${0|pt.Core}\n`).join("")} + `:``} +${track[0].Skin!==undefined ? ` +${track.map(pt=>` ${0|pt.Skin}\n`).join("")} + `:``} @@ -59,8 +72,7 @@ ${track.map(pt=>` ${0|pt.Steps}\n`).join("")} function saveGPX(track, title) { var gpx = ` - - + diff --git a/apps/recorder/widget.js b/apps/recorder/widget.js index 09893bbb7..4d8cdddb1 100644 --- a/apps/recorder/widget.js +++ b/apps/recorder/widget.js @@ -48,7 +48,7 @@ Bangle.removeListener('GPS', onGPS); Bangle.setGPSPower(0,"recorder"); }, - draw : (x,y) => g.setColor(hasFix?"#f00":"#888").drawImage(atob("DAyBAAACADgDuBOAeA4AzAHADgAAAA=="),x,y) + draw : (x,y) => g.setColor(hasFix?"#0ff":"#888").drawImage(atob("DAyBAAACADgDuBOAeA4AzAHADgAAAA=="),x,y) }; }, hrm:function() { @@ -82,6 +82,33 @@ draw : (x,y) => g.setColor(hasBPM?"#f00":"#888").drawImage(atob("DAyBAAAAAD/H/n/n/j/D/B+AYAAAAA=="),x,y) }; }, + + temp:function() { + var core = 0, skin = 0; + var hasCore = false; + function onCore(c) { + core=c.core; + skin=c.skin; + hasCore = true; + } + return { + name : "Core", + fields : ["Core","Skin"], + getValues : () => { + var r = [core,skin]; + return r; + }, + start : () => { + hasCore = false; + Bangle.on('CoreTemp', onCore); + }, + stop : () => { + hasCore = false; + Bangle.removeListener('CoreTemp', onCore); + }, + draw : (x,y) => g.setColor(hasCore?"#0f0":"#888").drawImage(atob("DAyBAAHh0js3EuDMA8A8AWBnDj9A8A=="),x,y) + }; + }, steps:function() { var lastSteps = 0; return { diff --git a/apps/rtorch/ChangeLog b/apps/rtorch/ChangeLog index 06f10fe08..13cbb6e72 100644 --- a/apps/rtorch/ChangeLog +++ b/apps/rtorch/ChangeLog @@ -1 +1,2 @@ 0.01: Cloning torch and making it red :D +0.02: Modify for setUI and Bangle 2 diff --git a/apps/rtorch/app.js b/apps/rtorch/app.js index 4f6b1d6f7..03a50ee10 100644 --- a/apps/rtorch/app.js +++ b/apps/rtorch/app.js @@ -2,21 +2,38 @@ Bangle.setLCDPower(1); Bangle.setLCDTimeout(0); g.reset(); c = 1; + function setColor(delta){ c+=delta; c = Math.max(c,0); c = Math.min(c,2); if (c<1){ g.setColor(c,0,0); + Bangle.setLCDBrightness(c >= 0.1 ? c : 0.1); }else{ g.setColor(1,c-1,c-1); + Bangle.setLCDBrightness(1); } g.fillRect(0,0,g.getWidth(),g.getHeight()); } -setColor(0) -// BTN1 light up toward white -// BTN3 light down to red -// BTN2 to reset -setWatch(()=>setColor(0.1), BTN1, { repeat:true, edge:"rising", debounce: 50 }); -setWatch(()=>load(), BTN2); -setWatch(()=>setColor(-0.1), BTN3, { repeat:true, edge:"rising", debounce: 50 }); + +function updownHandler(direction){ + if (direction == undefined){ + c=1; + setColor(0); + } else { + setColor(-direction * 0.1); + } +} + +setColor(0); + +// Bangle 1: +// BTN1: light up toward white +// BTN3: light down to red +// BTN2: reset +// Bangle 2: +// Swipe up: light up toward white +// Swipe down: light down to red +// BTN1: reset +Bangle.setUI("updown", updownHandler); diff --git a/apps/scribble/add_to_apps.json b/apps/scribble/add_to_apps.json deleted file mode 100644 index 054f35d55..000000000 --- a/apps/scribble/add_to_apps.json +++ /dev/null @@ -1,14 +0,0 @@ -{ "id": "scribble", - "name": "Scribble", - "shortName":"Scribble", - "version":"0.01", - "description": "A keyboard on your wrist!", - "icon": "app.png", - "tags": "keyboard, text, scribble", - "supports" : ["BANGLEJS2"], - "readme": "README.md", - "storage": [ - {"name":"scribble.app.js","url":"app.js"}, - {"name":"scribble.img","url":"app-icon.js","evaluate":true} - ] -} diff --git a/apps/sensible/ChangeLog b/apps/sensible/ChangeLog index baa93f297..33e44b70c 100644 --- a/apps/sensible/ChangeLog +++ b/apps/sensible/ChangeLog @@ -1,3 +1,5 @@ 0.01: New App! 0.02: Corrected variable initialisation 0.03: Advertise app name, added screenshots +0.04: Advertise bar, GPS, HRM and mag services +0.05: Refactored for efficiency, corrected sensor value inaccuracies \ No newline at end of file diff --git a/apps/sensible/README.md b/apps/sensible/README.md index f79b61aea..fcff3b0f9 100644 --- a/apps/sensible/README.md +++ b/apps/sensible/README.md @@ -17,7 +17,7 @@ Currently implements: - Heart Rate Monitor - Magnetometer -in the menu display but NOT YET in Bluetooth Low Energy advertising (which will be implemented in a subsequent version). +in the menu display, and broadcasts all sensor data readings _except_ acceleration in Bluetooth Low Energy advertising packets as GATT characteristic services. ## Controls diff --git a/apps/sensible/sensible.js b/apps/sensible/sensible.js index 45852adab..73c348556 100644 --- a/apps/sensible/sensible.js +++ b/apps/sensible/sensible.js @@ -7,6 +7,9 @@ // Non-user-configurable constants const APP_ID = 'sensible'; const ESPRUINO_COMPANY_CODE = 0x0590; +const APP_ADVERTISING_DATA = [ 0x12, 0xff, 0x90, 0x05, 0x7b, 0x6e, 0x61, 0x6d, + 0x65, 0x3a, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x62, + 0x6c, 0x65, 0x7d ]; // Global variables @@ -20,6 +23,12 @@ let isBarEnabled = true; let isGpsEnabled = true; let isHrmEnabled = true; let isMagEnabled = true; +let isNewAccData = false; +let isNewBarData = false; +let isNewGpsData = false; +let isNewHrmData = false; +let isNewMagData = false; + // Menus @@ -91,22 +100,100 @@ let magMenu = { }; -// Transmit the app name under the Espruino company code to facilitate discovery -function transmitAppName() { - let options = { - showName: false, - manufacturer: ESPRUINO_COMPANY_CODE, - manufacturerData: JSON.stringify({ name: APP_ID }), - interval: 2000 +// Check for new sensor data and update the advertising sequence +function transmitUpdatedSensorData() { + let data = [ APP_ADVERTISING_DATA ]; // Always advertise at least app name + + if(isNewBarData) { + data.push(encodeBarServiceData()); + isNewBarData = false; } - NRF.setAdvertising({}, options); + if(isNewGpsData && gps.lat && gps.lon) { + data.push(encodeGpsServiceData()); + isNewGpsData = false; + } + + if(isNewHrmData) { + data.push({ 0x2a37: [ 0, hrm.bpm ] }); + isNewHrmData = false; + } + + if(isNewMagData) { + data.push(encodeMagServiceData()); + isNewMagData = false; + } + + let interval = 1000 / data.length; + NRF.setAdvertising(data, { showName: false, interval: interval }); +} + + +// Encode the bar service data to fit in a Bluetooth PDU +function encodeBarServiceData() { + let t = toByteArray(Math.round(bar.temperature * 100), 2, true); + let p = toByteArray(Math.round(bar.pressure * 1000), 4, false); + let e = toByteArray(Math.round(bar.altitude * 100), 3, true); + + return [ + 0x02, 0x01, 0x06, // Flags + 0x05, 0x16, 0x6e, 0x2a, t[0], t[1], // Temperature + 0x07, 0x16, 0x6d, 0x2a, p[0], p[1], p[2], p[3], // Pressure + 0x06, 0x16, 0x6c, 0x2a, e[0], e[1], e[2] // Elevation + ]; +} + + +// Encode the GPS service data using the Location and Speed characteristic +function encodeGpsServiceData() { + let s = toByteArray(Math.round(1000 * gps.speed / 36), 2, false); + let lat = toByteArray(Math.round(gps.lat * 10000000), 4, true); + let lon = toByteArray(Math.round(gps.lon * 10000000), 4, true); + let e = toByteArray(Math.round(gps.alt * 100), 3, true); + let h = toByteArray(Math.round(gps.course * 100), 2, false); + + return [ + 0x02, 0x01, 0x06, // Flags + 0x14, 0x16, 0x67, 0x2a, 0x9d, 0x02, s[0], s[1], lat[0], lat[1], lat[2], + lat[3], lon[0], lon[1], lon[2], lon[3], e[0], e[1], e[2], h[0], h[1] + // Location and Speed + ]; +} + + +// Encode the mag service data using the magnetic flux density 3D characteristic +function encodeMagServiceData() { + let x = toByteArray(mag.x, 2, true); + let y = toByteArray(mag.y, 2, true); + let z = toByteArray(mag.z, 2, true); + + return [ + 0x02, 0x01, 0x06, // Flags + 0x09, 0x16, 0xa1, 0x2a, x[0], x[1], y[0], y[1], z[0], z[1] // Mag 3D + ]; +} + + +// Convert the given value to a little endian byte array +function toByteArray(value, numberOfBytes, isSigned) { + let byteArray = new Array(numberOfBytes); + + if(isSigned && (value < 0)) { + value += 1 << (numberOfBytes * 8); + } + + for(let index = 0; index < numberOfBytes; index++) { + byteArray[index] = (value >> (index * 8)) & 0xff; + } + + return byteArray; } // Update acceleration Bangle.on('accel', function(newAcc) { acc = newAcc; + isNewAccData = true; if(isAccMenu) { accMenu.x.value = acc.x.toFixed(2); @@ -119,6 +206,7 @@ Bangle.on('accel', function(newAcc) { // Update barometer Bangle.on('pressure', function(newBar) { bar = newBar; + isNewBarData = true; if(isBarMenu) { barMenu.Altitude.value = bar.altitude.toFixed(1) + 'm'; @@ -131,6 +219,7 @@ Bangle.on('pressure', function(newBar) { // Update GPS Bangle.on('GPS', function(newGps) { gps = newGps; + isNewGpsData = true; if(isGpsMenu) { gpsMenu.Lat.value = gps.lat.toFixed(4); @@ -145,6 +234,7 @@ Bangle.on('GPS', function(newGps) { // Update heart rate monitor Bangle.on('HRM', function(newHrm) { hrm = newHrm; + isNewHrmData = true; if(isHrmMenu) { hrmMenu.BPM.value = hrm.bpm; @@ -156,6 +246,7 @@ Bangle.on('HRM', function(newHrm) { // Update magnetometer Bangle.on('mag', function(newMag) { mag = newMag; + isNewMagData = true; if(isMagMenu) { magMenu.x.value = mag.x; @@ -169,9 +260,9 @@ Bangle.on('mag', function(newMag) { // On start: enable sensors and display main menu g.clear(); -transmitAppName(); Bangle.setBarometerPower(isBarEnabled, APP_ID); Bangle.setGPSPower(isGpsEnabled, APP_ID); Bangle.setHRMPower(isHrmEnabled, APP_ID); Bangle.setCompassPower(isMagEnabled, APP_ID); -E.showMenu(mainMenu); \ No newline at end of file +E.showMenu(mainMenu); +setInterval(transmitUpdatedSensorData, 1000); \ No newline at end of file diff --git a/apps/setting/ChangeLog b/apps/setting/ChangeLog index c676e3828..a2245a02d 100644 --- a/apps/setting/ChangeLog +++ b/apps/setting/ChangeLog @@ -39,3 +39,6 @@ 0.34: Remove Quiet Mode LCD settings: now handled by Quiet Mode Schedule app 0.35: Change App/Widget settings to 'App Settings' so it fits on Bangle screen 0.36: Added 'Utils' menu with helpful utilities for restoring Bangle.js +0.37: Going into passkey menu now saves settings with passkey +0.38: Restructed menus as per forum discussion +0.39: Fix misbehaving debug info option diff --git a/apps/setting/settings.js b/apps/setting/settings.js index 8f95eb3bb..1208018ed 100644 --- a/apps/setting/settings.js +++ b/apps/setting/settings.js @@ -58,9 +58,40 @@ 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() { + + const mainmenu = { + '': { 'title': 'Settings' }, + '< Back': ()=>load(), + /*LANG*/'Apps': ()=>showAppSettingsMenu(), + /*LANG*/'Bluetooth': ()=>showBLEMenu(), + /*LANG*/'System': ()=>showSystemMenu(), + /*LANG*/'Alerts': ()=>showAlertsMenu(), + /*LANG*/'Utils': ()=>showUtilMenu(), + /*LANG*/'Turn Off': ()=>{ if (Bangle.softOff) Bangle.softOff(); else Bangle.off() } + }; + + return E.showMenu(mainmenu); +} + +function showSystemMenu() { + + const mainmenu = { + '': { 'title': 'System' }, + '< Back': ()=>showMainMenu(), + /*LANG*/'Theme': ()=>showThemeMenu(), + /*LANG*/'LCD': ()=>showLCDMenu(), + /*LANG*/'Locale': ()=>showLocaleMenu(), + /*LANG*/'Select Clock': ()=>showClockMenu(), + /*LANG*/'Set Time': ()=>showSetTimeMenu() + }; + + return E.showMenu(mainmenu); +} + +function showAlertsMenu() { var beepMenuItem; if (BANGLEJS2) { beepMenuItem = { @@ -77,7 +108,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, @@ -91,14 +122,11 @@ function showMainMenu() { }; } - const mainmenu = { - '': { 'title': 'Settings' }, - '< Back': ()=>load(), - 'App Settings': ()=>showAppSettingsMenu(), - 'BLE': ()=>showBLEMenu(), - 'Beep': beepMenuItem, - 'Vibration': { + '': { 'title': 'Alerts' }, + '< Back': ()=>showMainMenu(), + /*LANG*/'Beep': beepMenuItem, + /*LANG*/'Vibration': { value: settings.vibrate, format: boolFormat, onchange: () => { @@ -110,7 +138,7 @@ function showMainMenu() { } } }, - "Quiet Mode": { + /*LANG*/"Quiet Mode": { value: settings.quiet|0, format: v => ["Off", "Alarms", "Silent"][v%3], onchange: v => { @@ -119,23 +147,18 @@ function showMainMenu() { updateOptions(); 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() }, + } }; return E.showMenu(mainmenu); } + function showBLEMenu() { var hidV = [false, "kbmedia", "kb", "joy"]; var hidN = ["Off", "Kbrd & Media", "Kbrd","Joystick"]; E.showMenu({ + '': { 'title': 'Bluetooth' }, '< Back': ()=>showMainMenu(), 'Make Connectable': ()=>makeConnectable(), 'BLE': { @@ -190,7 +213,7 @@ function showThemeMenu() { } var m = E.showMenu({ '':{title:'Theme'}, - '< Back': ()=>showMainMenu(), + '< Back': ()=>showSystemMenu(), 'Dark BW': ()=>{ upd({ fg:cl("#fff"), bg:cl("#000"), @@ -276,8 +299,10 @@ function showPasskeyMenu() { showBLEMenu(); } }; - if (!settings.passkey || settings.passkey.length!=6) + if (!settings.passkey || settings.passkey.length!=6) { settings.passkey = "123456"; + updateSettings(); + } for (var i=0;i<6;i++) (function(i){ menu[`Digit ${i+1}`] = { value : 0|settings.passkey[i], @@ -333,7 +358,7 @@ function showWhitelistMenu() { function showLCDMenu() { const lcdMenu = { '': { 'title': 'LCD' }, - '< Back': ()=>showMainMenu(), + '< Back': ()=>showSystemMenu(), 'LCD Brightness': { value: settings.brightness, min: 0.1, @@ -445,7 +470,7 @@ function showLCDMenu() { function showLocaleMenu() { const localemenu = { '': { 'title': 'Locale' }, - '< Back': ()=>showMainMenu(), + '< Back': ()=>showSystemMenu(), 'Time Zone': { value: settings.timezone, min: -11, @@ -474,6 +499,8 @@ function showUtilMenu() { '< Back': ()=>showMainMenu(), 'Debug Info': { value: E.clip(0|settings.log,0,2), + min: 0, + max: 2, format: v => ["Hide","Show","Log"][E.clip(0|v,0,2)], onchange: v => { settings.log = v; @@ -549,7 +576,7 @@ function showClockMenu() { '': { 'title': 'Select Clock', }, - '< Back': ()=>showMainMenu(), + '< Back': ()=>showSystemMenu(), }; clockApps.forEach((app, index) => { var label = app.name; @@ -576,7 +603,7 @@ function showSetTimeMenu() { '': { 'title': 'Set Time' }, '< Back': function () { setTime(d.getTime() / 1000); - showMainMenu(); + showSystemMenu(); }, 'Hour': { value: d.getHours(), diff --git a/apps/showimg/ChangeLog b/apps/showimg/ChangeLog new file mode 100644 index 000000000..296bc78d0 --- /dev/null +++ b/apps/showimg/ChangeLog @@ -0,0 +1,2 @@ +0.1: Initial release +0.2: Fixed launcher image diff --git a/apps/showimg/README.md b/apps/showimg/README.md new file mode 100644 index 000000000..0624fd962 --- /dev/null +++ b/apps/showimg/README.md @@ -0,0 +1,3 @@ +Displays an image. I use this app to show my vaccination certificate. +The image is read from the file "showimg.user.img". +Returns to watch face after 60s/button push. diff --git a/apps/showimg/app-icon.js b/apps/showimg/app-icon.js new file mode 100644 index 000000000..abb1eb434 --- /dev/null +++ b/apps/showimg/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkBIf4Aah//BRQAMDowUNC5AARC4YKKL5gTC+B3TCpAyIC5oNBEA4XNJwS4GC55pHC8TEHC57QHC4wSEC5YpEC6YwEC5oEEC5x3DC6ZHbC7PwcYxfNAYYXPJA4XQDAwKEBYQXJIoReHC5gMFAAojBC5QUIC5Y5JMgYXIUQYJFPggXMAwICCBAYXMCAQJDDwQUCC5QOCUwQdEC5QqFDghNFC5wrEC5gQDPgoTCDYYXFMAgXaCQoXJEwZ4FLQbhFC4imDAAglFC5QAGBgYXKIoYWIC5YYFG4ZkDC4YjCYYwAJC4gASC6THFH5pqGAAY")) diff --git a/apps/showimg/app.js b/apps/showimg/app.js new file mode 100644 index 000000000..e00385bd7 --- /dev/null +++ b/apps/showimg/app.js @@ -0,0 +1,16 @@ +g.reset(); +g.clear(); +g.drawImage(require("Storage").read("showimg.user.img"),0,0); +drawTimeout = setTimeout(function() { + load(); +}, 60000); +setWatch(function() { + load(); +}, BTN, { repeat:false, edge:'falling' }); +var savedOptions=Bangle.getOptions(); +Bangle.setLCDBrightness(1); +var newOptions={ + lockTimeout:60000, + backlightTimeout:60000 +}; +Bangle.setOptions(newOptions); diff --git a/apps/showimg/app.png b/apps/showimg/app.png new file mode 100644 index 000000000..306db9b42 Binary files /dev/null and b/apps/showimg/app.png differ diff --git a/apps/simple_clock/README.md b/apps/simple_clock/README.md new file mode 100644 index 000000000..d44c495c6 --- /dev/null +++ b/apps/simple_clock/README.md @@ -0,0 +1,14 @@ +# Simple Analog Clock # + +This app displays a simple, yet stylish, analog clock. It considers the +currently configured "theme" (and may therefore look different than shown in +the screenshot on your watch depending on which theme you prefer). + +![](app-screenshot.png) + +This clock also acts as an example for the building blocks found in the author's +[GitHub repository](https://github.com/rozek/banglejs-2-activities) + +## License ## + +[MIT License](LICENSE) diff --git a/apps/simple_clock/app-icon.js b/apps/simple_clock/app-icon.js new file mode 100644 index 000000000..9bc0ebfe1 --- /dev/null +++ b/apps/simple_clock/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwcBIf4AOgPAjgROh/A/+AEZ8DCKH8Gp/4Gp0QCKAARgQRigFACMUICMT7SEcUAkAvK/EAv//BpH8eoYOBAQP//0ECIrvDCIQABj4TB8AREj4RCgIyFn4RJh5HBCJQ1DAA0/UKBuJQZIRgL4wRL4ARhAH4AIg4RQdIwRcnAjiLKIA/ACI=")) \ No newline at end of file diff --git a/apps/simple_clock/app-icon.png b/apps/simple_clock/app-icon.png new file mode 100644 index 000000000..b1dcc461e Binary files /dev/null and b/apps/simple_clock/app-icon.png differ diff --git a/apps/simple_clock/app-screenshot.png b/apps/simple_clock/app-screenshot.png new file mode 100644 index 000000000..ec99c9fc6 Binary files /dev/null and b/apps/simple_clock/app-screenshot.png differ diff --git a/apps/simple_clock/app.js b/apps/simple_clock/app.js new file mode 100644 index 000000000..3c1843cb0 --- /dev/null +++ b/apps/simple_clock/app.js @@ -0,0 +1,230 @@ + let ScreenWidth = g.getWidth(), CenterX = ScreenWidth/2; + let ScreenHeight = g.getHeight(), CenterY = ScreenHeight/2; + + let outerRadius = Math.min(CenterX,CenterY) * 0.9; + + Bangle.loadWidgets(); + +/**** updateClockFaceSize ****/ + + function updateClockFaceSize () { + CenterX = ScreenWidth/2; + CenterY = ScreenHeight/2; + + outerRadius = Math.min(CenterX,CenterY) * 0.9; + + if (global.WIDGETS == null) { return; } + + let WidgetLayouts = { + tl:{ x:0, y:0, Direction:0 }, + tr:{ x:ScreenWidth-1, y:0, Direction:1 }, + bl:{ x:0, y:ScreenHeight-24, Direction:0 }, + br:{ x:ScreenWidth-1, y:ScreenHeight-24, Direction:1 } + }; + + for (let Widget of WIDGETS) { + let WidgetLayout = WidgetLayouts[Widget.area]; // reference, not copy! + if (WidgetLayout == null) { continue; } + + Widget.x = WidgetLayout.x - WidgetLayout.Direction * Widget.width; + Widget.y = WidgetLayout.y; + + WidgetLayout.x += Widget.width * (1-2*WidgetLayout.Direction); + } + + let x,y, dx,dy; + let cx = CenterX, cy = CenterY, r = outerRadius, r2 = r*r; + + x = WidgetLayouts.tl.x; y = WidgetLayouts.tl.y+24; dx = x - cx; dy = y - cy; + if (dx*dx + dy*dy < r2) { + cy = CenterY + 12; dy = y - cy; r2 = dx*dx + dy*dy; r = Math.sqrt(r2); + } + + x = WidgetLayouts.tr.x; y = WidgetLayouts.tr.y+24; dx = x - cx; dy = y - cy; + if (dx*dx + dy*dy < r2) { + cy = CenterY + 12; dy = y - cy; r2 = dx*dx + dy*dy; r = Math.sqrt(r2); + } + + x = WidgetLayouts.bl.x; y = WidgetLayouts.bl.y; dx = x - cx; dy = y - cy; + if (dx*dx + dy*dy < r2) { + cy = CenterY - 12; dy = y - cy; r2 = dx*dx + dy*dy; r = Math.sqrt(r2); + } + + x = WidgetLayouts.br.x; y = WidgetLayouts.br.y; dx = x - cx; dy = y - cy; + if (dx*dx + dy*dy < r2) { + cy = CenterY - 12; dy = y - cy; r2 = dx*dx + dy*dy; r = Math.sqrt(r2); + } + + CenterX = cx; CenterY = cy; outerRadius = r * 0.9; + } + + updateClockFaceSize(); + +/**** custom version of Bangle.drawWidgets (does not clear the widget areas) ****/ + + Bangle.drawWidgets = function () { + var w = g.getWidth(), h = g.getHeight(); + + var pos = { + tl:{x:0, y:0, r:0, c:0}, // if r==1, we're right->left + tr:{x:w-1, y:0, r:1, c:0}, + bl:{x:0, y:h-24, r:0, c:0}, + br:{x:w-1, y:h-24, r:1, c:0} + }; + + if (global.WIDGETS) { + for (var wd of WIDGETS) { + var p = pos[wd.area]; + if (!p) continue; + + wd.x = p.x - p.r*wd.width; + wd.y = p.y; + + p.x += wd.width*(1-2*p.r); + p.c++; + } + + g.reset(); // also loads the current theme + + if (pos.tl.c || pos.tr.c) { + g.setClipRect(0,h-24,w-1,h-1); + g.reset(); // also (re)loads the current theme + } + + if (pos.bl.c || pos.br.c) { + g.setClipRect(0,h-24,w-1,h-1); + g.reset(); // also (re)loads the current theme + } + + try { + for (wd of WIDGETS) { + g.clearRect(wd.x,wd.y, wd.x+wd.width-1,23); + wd.draw(wd); + } + } catch (e) { print(e); } + } + }; + + let HourHandLength = outerRadius * 0.5; + let HourHandWidth = 2*3, halfHourHandWidth = HourHandWidth/2; + + let MinuteHandLength = outerRadius * 0.7; + let MinuteHandWidth = 2*2, halfMinuteHandWidth = MinuteHandWidth/2; + + let SecondHandLength = outerRadius * 0.9; + let SecondHandOffset = 6; + + let twoPi = 2*Math.PI; + let Pi = Math.PI; + let halfPi = Math.PI/2; + + let sin = Math.sin, cos = Math.cos; + + let HourHandPolygon = [ + -halfHourHandWidth,halfHourHandWidth, + -halfHourHandWidth,halfHourHandWidth-HourHandLength, + halfHourHandWidth,halfHourHandWidth-HourHandLength, + halfHourHandWidth,halfHourHandWidth, + ]; + + let MinuteHandPolygon = [ + -halfMinuteHandWidth,halfMinuteHandWidth, + -halfMinuteHandWidth,halfMinuteHandWidth-MinuteHandLength, + halfMinuteHandWidth,halfMinuteHandWidth-MinuteHandLength, + halfMinuteHandWidth,halfMinuteHandWidth, + ]; + +/**** drawClockFace ****/ + + function drawClockFace () { + g.setColor(g.theme.fg); + g.setFont('Vector', 22); + + g.setFontAlign(0,-1); + g.drawString('12', CenterX,CenterY-outerRadius); + + g.setFontAlign(1,0); + g.drawString('3', CenterX+outerRadius,CenterY); + + g.setFontAlign(0,1); + g.drawString('6', CenterX,CenterY+outerRadius); + + g.setFontAlign(-1,0); + g.drawString('9', CenterX-outerRadius,CenterY); + } + +/**** transforme polygon ****/ + + let transformedPolygon = new Array(HourHandPolygon.length); + + function transformPolygon (originalPolygon, OriginX,OriginY, Phi) { + let sPhi = sin(Phi), cPhi = cos(Phi), x,y; + + for (let i = 0, l = originalPolygon.length; i < l; i+=2) { + x = originalPolygon[i]; + y = originalPolygon[i+1]; + + transformedPolygon[i] = OriginX + x*cPhi + y*sPhi; + transformedPolygon[i+1] = OriginY + x*sPhi - y*cPhi; + } + } + +/**** draw clock hands ****/ + + function drawClockHands () { + let now = new Date(); + + let Hours = now.getHours() % 12; + let Minutes = now.getMinutes(); + let Seconds = now.getSeconds(); + + let HoursAngle = (Hours+(Minutes/60))/12 * twoPi - Pi; + let MinutesAngle = (Minutes/60) * twoPi - Pi; + let SecondsAngle = (Seconds/60) * twoPi - Pi; + + g.setColor(g.theme.fg); + + transformPolygon(HourHandPolygon, CenterX,CenterY, HoursAngle); + g.fillPoly(transformedPolygon); + + transformPolygon(MinuteHandPolygon, CenterX,CenterY, MinutesAngle); + g.fillPoly(transformedPolygon); + + let sPhi = Math.sin(SecondsAngle), cPhi = Math.cos(SecondsAngle); + + g.setColor(g.theme.fg2); + g.drawLine( + CenterX + SecondHandOffset*sPhi, + CenterY - SecondHandOffset*cPhi, + CenterX - SecondHandLength*sPhi, + CenterY + SecondHandLength*cPhi + ); + } + +/**** refreshDisplay ****/ + + let Timer; + function refreshDisplay () { + g.clear(true); // also loads current theme + + Bangle.drawWidgets(); + + drawClockFace(); + drawClockHands(); + + let Pause = 1000 - (Date.now() % 1000); + Timer = setTimeout(refreshDisplay,Pause); + } + + setTimeout(refreshDisplay, 500); // enqueue first draw request + + Bangle.on('lcdPower', (on) => { + if (on) { + if (Timer != null) { clearTimeout(Timer); Timer = undefined; } + refreshDisplay(); + } + }); + + Bangle.loadWidgets(); + + Bangle.setUI('clock'); diff --git a/apps/slash/ChangeLog b/apps/slash/ChangeLog new file mode 100644 index 000000000..f3fae1785 --- /dev/null +++ b/apps/slash/ChangeLog @@ -0,0 +1 @@ +0.01: First version for upload diff --git a/apps/slash/README.md b/apps/slash/README.md new file mode 100644 index 000000000..9bef104cc --- /dev/null +++ b/apps/slash/README.md @@ -0,0 +1,11 @@ +# Slash Watch + +![](screenshot.png) + +Slash Watch, a recreation of the Slash watch face for Pebble watches by Nikki. +Simple watchface with a slash through the hours and minutes. Date shown at the bottom. Theme will change (light/dark) based on watch theme. + +This watch face was made using Espruino documentation, Espruino forum threads, the 93 Dub watch face, the barclock watch face, and the waveclk app. + +Contributors: +* Ray Holder (93 Dub watchface helped create this one) diff --git a/apps/slash/app-icon.js b/apps/slash/app-icon.js new file mode 100644 index 000000000..a0737974a --- /dev/null +++ b/apps/slash/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwgEBgUiACUgC4IWTAAIuVGAQWVAH4A/AH4AmmQWVl//C6s//4wPkfzAof/F4U/DRgpFC4Uj/4hEFw4RBC4QTDBIouJEoYrEBQouLRwXyBZAuKAwQXCGBQiHPgowJEI6mDGBYXXHIy6GPBQhFRwJ9GVBAiFUwjYNEYiOFa5YkFC4guMFYqOEPgwwIBoSmFn4uLJYopMABKOEACUjCyoA/AH4A/AE0CCysggAXVgEAGCguB")) diff --git a/apps/slash/app.js b/apps/slash/app.js new file mode 100644 index 000000000..f548bcaf7 --- /dev/null +++ b/apps/slash/app.js @@ -0,0 +1,109 @@ +// Get 12 hour status, from barclock +const is12Hour = (require("Storage").readJSON("setting.json", 1) || {})["12hour"]; + +// Used from waveclk to schedule updates every minute +var drawTimeout; + +// Schedule a draw for the next minute +function queueDraw() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + draw(); + }, 60000 - (Date.now() % 60000)); +} + +// From forum conversation 348275 +function fillLine(x1, y1, x2, y2, lineWidth) { + var dx, dy, d; + if (!lineWidth) { + g.drawLine(x1, y1, x2, y2); + } else { + lineWidth = (lineWidth - 1) / 2; + dx = x2 - x1; + dy = y2 - y1; + d = Math.sqrt(dx * dx + dy * dy); + dx = Math.round(dx * lineWidth / d, 0); + dy = Math.round(dy * lineWidth / d, 0); + g.fillPoly([x1 + dx, y1 - dy, x1 - dx, y1 + dy, x2 - dx, y2 + dy, x2 + dx, y2 - dy], true); + } +} + +// Mainly to convert day number to day of the week +function convertDate(date) { + var dayNum = date.getDay(); + var month = date.getMonth(); + var dayOfMonth = date.getDate(); + var dayChar; + + month += 1; + + switch (dayNum) { + case 0 : dayChar = "Sun"; break; + case 1 : dayChar = "Mon"; break; + case 2 : dayChar = "Tue"; break; + case 3 : dayChar = "Wed"; break; + case 4 : dayChar = "Thur"; break; + case 5 : dayChar = "Fri"; break; + case 6 : dayChar = "Sat"; break; + } + + return dayChar + " " + month + "/" + dayOfMonth; +} + +function draw() { + var d = new Date(); + var h = d.getHours(), m = d.getMinutes(); + var minutes = ("0"+m).substr(-2); + g.reset(); + + // Convert to 12hr time mode + if (is12Hour && h > 12) { + h = h - 12; + if (h < 10) { + h = "0" + h; + } + } else if (h < 12) { + h = "0" + h; + } else if (h == 0) { + h = 12; + } + + var hour = (" "+h).substr(-2); + + // Draw the time, vector font + g.setFont("Vector", 50); + g.setFontAlign(1,1); // Align right bottom + g.drawString(hour, 85, 80, true); + g.drawString(minutes, 155, 140, true); + + // Draw slash, width 6 + fillLine(57, 120, 112, 40, 6); + + // Convert date then draw + g.setFont("Vector", 20); + g.setFontAlign(0,1); // Align center bottom + var convertedDate = convertDate(d); + g.drawString(convertedDate, g.getWidth()/2, 170, true); + + Bangle.drawWidgets(); + queueDraw(); +} + +// Clear screen and draw +g.clear(); +draw(); + +// From waveclk +Bangle.on('lcdPower',on=>{ + if (on) { + draw(); // Draw immediately, queue redraw + } else { // Stop draw timer + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + } +}); + +Bangle.setUI("clock"); +Bangle.loadWidgets(); +Bangle.drawWidgets(); diff --git a/apps/slash/screenshot.png b/apps/slash/screenshot.png new file mode 100644 index 000000000..41008d76c Binary files /dev/null and b/apps/slash/screenshot.png differ diff --git a/apps/slash/slash.png b/apps/slash/slash.png new file mode 100644 index 000000000..cbfc8693d Binary files /dev/null and b/apps/slash/slash.png differ diff --git a/apps/slimehunt/ChangeLog b/apps/slimehunt/ChangeLog new file mode 100644 index 000000000..eb0cc5918 --- /dev/null +++ b/apps/slimehunt/ChangeLog @@ -0,0 +1,2 @@ +0.01: Public version is a go! +0.02: Fixed bug where Critial Up wasn't letting player attack. diff --git a/apps/slimehunt/README.md b/apps/slimehunt/README.md new file mode 100644 index 000000000..84d8f2b16 --- /dev/null +++ b/apps/slimehunt/README.md @@ -0,0 +1,77 @@ +<><><><>-SLIME HUNT-<><><><> + +Slime Hunt is a RPG turn-based style combat game +where you fight slimes until your HP runs out. + +The main goal is to beat your personal highscore! + +============================ + +During each fight the player has 3 options, + +BTN1) FIGHT +- Attacks the slime, dealing 1 hp worth of damage. + +BTN2) DEFEND +- Defends against the slime, blocking 3 damage from the next slime attack. + +BTN3) RUN +- Find a new slime to fight against. (This could change in the future!) + +============================ + +There are currently 5 types of slime each with unique behavior. + +<><>-BEHAVIORS-<><> + +1. NEUTRAL +- Slime deals 0-1 damage on it's next attack. + +2. ANGRY +- Slime deals 3-5 damage on it's next attack. + +3. ERACTIC +- Slime deals 0-5 damage on it's next attack. + +<><>-ITEMS-<><> + +1. Attack Up +- +1 damage next battle. + +2. Defence Up +- +1 defence next battle, stacks with block. +Setting defence to 4 when using DEFEND, and 1 otherwise. + +3. HP Up +- +3 HP. + +4. Block Up +- +2 block on DEFEND next battle, +setting Defence to 5 when using DEFEND command. + +5. Critical Up +- 20% chance to crit next battle on each attack, +instantly defeating the Slime. + +*****Using the RUN command causes you to lose your item!***** + +<><>-SLIMES-<><> + +1. GREEN SLIME +- Is always neutral. | 0% chance of item. + +2. RED SLIME +- Can be either neutral or angry. | 10% chance of item. + +3. GRAY SLIME +- Can be neutral, angry or eratic. | 20% chance of item. + +4. YELLOW SLIME +- Is always eratic. | 50% chance of item. + +5. PURPLE SLIME +- Is always angry. | 100% chance of item. + +============================ + +Created by Colton LaChance! diff --git a/apps/slimehunt/app-icon.js b/apps/slimehunt/app-icon.js new file mode 100644 index 000000000..36486cb8a --- /dev/null +++ b/apps/slimehunt/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+If4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH9bxgAM1gtsGTwtTGDVhFSPX64wZLqgwFF6iMVGAhgUF6owBMCzsWAAthL1AAGF/4vxrdhADVbeCQA/AH4A/AH4A/AH4A/AH4A/AH4A/AH4A/AH4AFA")) diff --git a/apps/slimehunt/app.js b/apps/slimehunt/app.js new file mode 100644 index 000000000..547ea7356 --- /dev/null +++ b/apps/slimehunt/app.js @@ -0,0 +1,477 @@ +//Create constants------------------------------------------------------------------ + +//Slimes +const GREEN_SLIME = 1; //Normal slime, is always neutral. | 0% Item chance +const PINK_SLIME = 2; //Can get angry. | 10% Item chance +const GRAY_SLIME = 3; //Can be neutral, angry or erratic. | 20% Item chance +const YELLOW_SLIME = 4; //Is always erratic. | 50% Item chance +const PURPLE_SLIME = 5; //Is always angry. | 100% Item chance + +//Items +const ITEM_ATK_UP = 1; //Raises damage dealt by +1 for next battle +const ITEM_DEF_UP = 2; //Reduces all damage by +1 for next battle +const ITEM_HP_UP = 3; //Increases HP by 3 +const ITEM_BLOCK_UP = 4; //Raises defence when defending by from 3 to 5 for next battle +const ITEM_CRIT_UP = 5; //Gives attack a 20% chance to instantly KO slime for next battle + +//Base stats +const BASE_ATK = 1; +const BASE_DEF = 0; +const BASE_BLOCK = 3; +const BASE_CRIT = 0; + +//Initialize variables------------------------------------------------------------------ +var playerHP = 20; +var slimeHP = 3; +var slimeType = GREEN_SLIME; +var turn = 0; +var screenWidth = g.getWidth(); +var screenHeight = g.getHeight(); +var slimeState = 0; +var showBattleResult = false; +var dmgDealt = 0; +var playerDefence = 0; +var playerItem = 0; +var critChance = 0; + +//Stats (Modifiers) +var statAtk = 1; +var statDef = 0; +var statBlock = 3; +var statCrit = 0; + +//Item vars +var itemName = ""; +var itemDesc = ""; +var itemChance = 0; + +var refreshInterval; +var waitTime = 0; + +var highscore = 0; +var score = 0; + +var themeNote = 0; + +//Load files------------------------------------------------------------------ +var file = require("Storage").open("highscore.txt", "r"); +highscore = file.readLine(); +if (highscore == undefined) highscore = 0; + +var greenSlime = require("Storage").read("slime.img"); +var pinkSlime = require("Storage").read("slimered.img"); +var graySlime = require("Storage").read("slimegray.img"); +var yellowSlime = require("Storage").read("slimeyellow.img"); +var purpleSlime = require("Storage").read("slimepurple.img"); + +//UI Stuff------------------------------------------------------------------ +function drawOpeningUI() { + g.clear(); + g.setFont("Vector", screenWidth / 15); + g.setFontAlign(0, 0); // center font + g.drawString("SLIME HUNT", screenWidth / 2, screenHeight * 0.1); + g.drawString("-SCORE TO BEAT-", screenWidth / 2, screenHeight * 0.3); + g.drawString("<><><> " + highscore + " <><><>", screenWidth / 2, screenHeight * 0.45); + g.setFont("Vector", screenWidth / 20); + g.drawString("A Slime approches...", screenWidth / 2, screenHeight * 0.6); + wait(8, waitForBattle); +} + +function drawSlime() { + switch (slimeType) { + case GREEN_SLIME: + g.drawImage(greenSlime, screenWidth / 2, screenHeight / 2, { + scale: 4, + rotate: 0 + }); + break; + case PINK_SLIME: + g.drawImage(pinkSlime, screenWidth / 2, screenHeight / 2, { + scale: 4, + rotate: 0 + }); + break; + case GRAY_SLIME: + g.drawImage(graySlime, screenWidth / 2, screenHeight / 2, { + scale: 4, + rotate: 0 + }); + break; + case YELLOW_SLIME: + g.drawImage(yellowSlime, screenWidth / 2, screenHeight / 2, { + scale: 4, + rotate: 0 + }); + break; + case PURPLE_SLIME: + g.drawImage(purpleSlime, screenWidth / 2, screenHeight / 2, { + scale: 4, + rotate: 0 + }); + break; + } +} + +function drawBattleUI() { + g.clear(); + g.setFont("Vector", screenWidth / 8); + g.setFontAlign(0, 0); // center font + g.drawString("SLIME HP: " + slimeHP, screenWidth / 2, screenHeight * 0.1); + g.setFont("Vector", screenWidth / 20); + if (!showBattleResult) { + switch (slimeState) { + case 0: + g.drawString("The slime seems neutral...", screenWidth / 2, screenHeight * 0.25); + break; + case 1: + g.drawString("The slime seems angry...", screenWidth / 2, screenHeight * 0.25); + break; + case 2: + g.drawString("The slime seems eratic...", screenWidth / 2, screenHeight * 0.25); + break; + } + } else { + var brString = (turn == 0 ? "The Slime loses " : "You lose "); + g.drawString(brString + dmgDealt + "HP!", screenWidth / 2, screenHeight * 0.25); + } + drawSlime(); + g.drawLine(0, screenHeight * 0.72, screenWidth, screenHeight * 0.72); + if (turn == 0) { + g.setFont("Vector", screenWidth / 15); + g.drawString("Your HP is " + playerHP + ".", screenWidth / 2, screenHeight * 0.8); + g.setFont("Vector", screenWidth / 20); + g.drawString("(B1) FIGHT\t|\t(B2) DEFEND\t|\t(B3) RUN", screenWidth / 2, screenHeight * 0.9); + } +} + +//Win / lose functions------------------------------------------------------------------ +function win() { + wait(5, winTheme); + calcScore(slimeType); + showBattleResult = false; + g.clear(); + g.setFont("Vector", screenWidth / 8); + g.setFontAlign(0, 0); // center font + g.drawString("YOU WON!", screenWidth / 2, screenHeight * 0.1); + g.drawLine(0, screenHeight * 0.2, screenWidth, screenHeight * 0.2); + g.setFont("Vector", screenWidth / 12); + g.drawString((playerItem == 0 ? "No Item." : "GOT ITEM!"), screenWidth / 2, screenHeight * 0.27); + g.setFont("Vector", screenWidth / 15); + g.drawString((playerItem == 0 ? "" : "<><> " + itemName + " <><>"), screenWidth / 2, screenHeight * 0.40); + g.setFont("Vector", screenWidth / 20); + g.drawString((playerItem == 0 ? "" : itemDesc), screenWidth / 2, screenHeight * 0.52); + g.drawLine(0, screenHeight * 0.6, screenWidth, screenHeight * 0.6); + g.drawString("Your score is << " + score + " >>", screenWidth / 2, screenHeight * 0.75); + g.drawString("Press (B3) to find another slime!", screenWidth / 2, screenHeight * 0.9); + turn = 0; + setWatch(run, BTN3); +} + +function lose() { + wait(5, loseTheme); + playerHP = 20; + showBattleResult = false; + g.clear(); + g.setFont("Vector", screenWidth / 8); + g.setFontAlign(0, 0); // center font + g.drawString("You lose...", screenWidth / 2, screenHeight * 0.1); + g.drawLine(0, screenHeight * 0.2, screenWidth, screenHeight * 0.2); + g.setFont("Vector", screenWidth / 12); + g.drawString((score > highscore ? "-NEW HIGHSCORE-" : "-SCORE TO BEAT-"), screenWidth / 2, screenHeight * 0.27); + g.setFont("Vector", screenWidth / 15); + g.drawString((score > highscore ? "<><> " + score + " <><>" : "<><> " + highscore + " <><>"), screenWidth / 2, screenHeight * 0.43); + g.drawLine(0, screenHeight * 0.6, screenWidth, screenHeight * 0.6); + g.setFont("Vector", screenWidth / 20); + g.drawString("Your score is << " + score + " >>", screenWidth / 2, screenHeight * 0.75); + g.drawString("Press (B3) to try again...", screenWidth / 2, screenHeight * 0.9); + score = 0; + turn = 0; + setWatch(run, BTN3); +} + +//Battle Stuff------------------------------------------------------------------ +function nextTurn() { + turn = (turn == 0 ? 1 : 0); +} + +function slimeFight() { + Bangle.beep(100, 500); + switch (slimeState) { + case 0: + dmgDealt = Math.floor(Math.random() * 2); + break; + case 1: + dmgDealt = Math.floor(Math.random() * 3) + 3; + break; + case 2: + dmgDealt = Math.floor(Math.random() * 6); + break; + } + dmgDealt = Math.max(0, dmgDealt - playerDefence); + playerHP -= dmgDealt; + slimeAI(); +} + +function fight() { + if (turn == 0 && waitTime <= 0) { + Bangle.beep(100, 1000); + dmgDealt = statAtk; + playerDefence = statDef; + if (statCrit == 0) { + slimeHP -= dmgDealt; + }else{ + critChance = Math.floor(Math.random() * 100); + if (critChance >= 100-statCrit) { + slimeHP = 0; + dmgDealt = 99; + }else{ + slimeHP -= dmgDealt; + } + critChance = 0; + } + showBattleResult = true; + drawBattleUI(); + wait(5, waitForTurn); + } +} + +function defend() { + if (turn == 0 && waitTime <= 0) { + dmgDealt = 0; + playerDefence = statBlock + statDef; + showBattleResult = true; + drawBattleUI(); + wait(5, waitForTurn); + } +} + +function run() { + if (turn == 0 && waitTime <= 0) { + showBattleResult = false; + Bangle.beep(200, 4000); + wait(3, waitForBattle); + } +} + + +function newBattle() { + showBattleResult = false; + slimeType = Math.floor(Math.random() * 5) + 1; + useItem(); //Use item at start of new battle + switch (slimeType) { + case GREEN_SLIME: + slimeHP = 3; + break; + case PINK_SLIME: + slimeHP = 3; + break; + case GRAY_SLIME: + slimeHP = 5; + break; + case YELLOW_SLIME: + slimeHP = 5; + break; + case PURPLE_SLIME: + slimeHP = 5; + break; + } + turn = 0; + battle(); + slimeAI(); + drawBattleUI(); +} + +function battle() { + setWatch(fight, BTN1); + setWatch(defend, BTN2); + setWatch(run, BTN3); +} + +function slimeAI() { + switch (slimeType) { + case GREEN_SLIME: + slimeState = 0; + break; + case PINK_SLIME: + slimeState = Math.floor(Math.random() * 2); + break; + case GRAY_SLIME: + slimeState = Math.floor(Math.random() * 3); + break; + case YELLOW_SLIME: + slimeState = 2; + break; + case PURPLE_SLIME: + slimeState = 1; + break; + } +} +//Items------------------------------------------------------------------ +function getItem() { + playerItem = Math.floor(Math.random() * 5) + 1; + switch (playerItem) { + case ITEM_ATK_UP: + itemName = "Attack Up"; + itemDesc = "+1 damage next battle."; + break; + case ITEM_DEF_UP: + itemName = "Defence Up"; + itemDesc = "+1 defence next battle."; + break; + case ITEM_HP_UP: + itemName = "HP Up"; + itemDesc = "+3 HP."; + break; + case ITEM_BLOCK_UP: + itemName = "Block Up"; + itemDesc = "+2 block on DEFEND next battle."; + break; + case ITEM_CRIT_UP: + itemName = "Critical Up"; + itemDesc = "20% chance to crit next battle."; + break; + } +} + +function useItem() { + statAtk = BASE_ATK; + statDef = BASE_DEF; + statBlock = BASE_BLOCK; + statCrit = BASE_CRIT; + switch (playerItem) { + case ITEM_ATK_UP: + statAtk = 2; + break; + case ITEM_DEF_UP: + statDef = 1; + break; + case ITEM_HP_UP: + playerHP += 3; + break; + case ITEM_BLOCK_UP: + statBlock = 5; + break; + case ITEM_CRIT_UP: + statCrit = 20; + break; + } + playerItem = 0; +} + +//Timed transitions------------------------------------------------------------------ +function wait(duration, waitFunc) { + waitTime = duration; + if (!refreshInterval) + refreshInterval = setInterval(waitFunc, 500); +} + +function waitForTurn() { + waitTime--; + if (waitTime <= 0) { + clearInterval(refreshInterval); + refreshInterval = undefined; + nextTurn(); + if (playerHP > 0 && slimeHP > 0) { + if (turn == 1) { + slimeFight(); + wait(5, waitForTurn); + } else { + showBattleResult = false; + battle(); + } + drawBattleUI(); + } else { + if (playerHP <= 0) { + lose(); + } + if (slimeHP <= 0) { + win(); + } + } + } + Bangle.setLCDPower(1); +} + +function waitForBattle() { + waitTime--; + Bangle.beep(100, 1000); + if (waitTime <= 0) { + clearInterval(refreshInterval); + refreshInterval = undefined; + showBattleResult = false; + newBattle(); + } + Bangle.setLCDPower(1); +} + +function winTheme() { + waitTime--; + Bangle.beep(200, 100 * themeNote); + themeNote++; + if (waitTime <= 0) { + themeNote = 0; + clearInterval(refreshInterval); + refreshInterval = undefined; + setWatch(run, BTN3); + } + Bangle.setLCDPower(1); +} + +function loseTheme() { + waitTime--; + Bangle.beep(200, 600 - (100 * themeNote)); + themeNote++; + if (waitTime <= 0) { + themeNote = 0; + clearInterval(refreshInterval); + refreshInterval = undefined; + setWatch(run, BTN3); + } + Bangle.setLCDPower(1); +} + +//Calculations------------------------------------------------------------------ +function calcScore(slimeType) { + switch (slimeType) { + case GREEN_SLIME: + score += 1; + //No items + break; + case PINK_SLIME: + score += 2; + itemChance = Math.floor(Math.random() * 100); + if (itemChance >= 100 - 10) { //100 - ITEM CHANCE % + getItem(); + } + break; + case GRAY_SLIME: + score += 3; + itemChance = Math.floor(Math.random() * 100); + if (itemChance >= 100 - 25) { //100 - ITEM CHANCE % + getItem(); + } + break; + case YELLOW_SLIME: + score += 5; + itemChance = Math.floor(Math.random() * 100); + if (itemChance >= 100 - 50) { //100 - ITEM CHANCE % + getItem(); + } + break; + case PURPLE_SLIME: + score += 10; + getItem(); + break; + } + if (score > highscore) { + file.erase(); + file = require("Storage").open("highscore.txt", "w"); + file.write(score); + } +} + + +//------------------------------------GAME STARTS HERE ----------------------------------------------- + +//Load opening UI +drawOpeningUI(); diff --git a/apps/slimehunt/app.png b/apps/slimehunt/app.png new file mode 100644 index 000000000..208e68f46 Binary files /dev/null and b/apps/slimehunt/app.png differ diff --git a/apps/snaky/README.md b/apps/snaky/README.md new file mode 100644 index 000000000..03ef3be2b --- /dev/null +++ b/apps/snaky/README.md @@ -0,0 +1,7 @@ +# Snaky + +Eat apples and don't bite your tail. + +## Controls +Use the touch screen, drag up, down, right or left. + diff --git a/apps/snaky/snaky-icon.js b/apps/snaky/snaky-icon.js new file mode 100644 index 000000000..85e81eadf --- /dev/null +++ b/apps/snaky/snaky-icon.js @@ -0,0 +1 @@ +E.toArrayBuffer(atob("MDCEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC7u7u7u7AAAJmZmZmZmZmZkAAAAAAAAAu7u7u7u7uwAJmZmZmZmZmZkAAAAAAAALuyu7u7u7uwAJmZmZmZmZmZkAAAAAAAALIiIru7IiKwAJmZmZmZmZmZkAAAAAAAALISIru7IiK7AJmZmZmZmZmZkAAAAAAAALsiK7u7IjK7AJmZmZmZmZmZkAAAAAAAALu7u7u7u7u7AJmZmZmZmZmZkAAAAAAAALu7u7u7u7u7AAAAAAAJmZmZkAAAAAAAALsru7u7siu7AAAAAAAAmZmZkAAAAAAAALuxEiIiIruwAAAAAAAJmZmZkAAAAAAAAAu7IiIiu7uwAAAAAAAJmZmZkAAAAAAAAAu7u7u7u7sAAAAAAAAJmZmZkAAAAAAAAAALuqqqqwAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAAmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmZmZmZmZmZmZmZmZkAAAAAAAAAAAmZmZmZmZmZmZmZmZmZmZkAAAAAAAAAAAmZmZmZmZmZmZmZmZmZmZkAAAAAAAAAAAmZmZmZmZmZmZmZmZmZmZkAAAAAAAAAAAmZmZmZmZmZmZmZmZmZmZkAAAAAAAAAAAmZmZmZmZmZmZmZmZmZmZkAAAAAAAAAAAmZmZmZmZmZmZmZmZmZmZkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==")) diff --git a/apps/snaky/snaky.js b/apps/snaky/snaky.js new file mode 100644 index 000000000..7ce032fcf --- /dev/null +++ b/apps/snaky/snaky.js @@ -0,0 +1,185 @@ +//Bangle.setLCDMode("176x176"); +Bangle.setLCDTimeout(0); + +const H = g.getWidth(); +const W = g.getHeight(); +let running = true; +let score = 0; +let d; +const gridSize = 29; +const tileSize = 6; +let nextX = 0; +let nextY = 0; +const defaultTailSize = 3; +let tailSize = defaultTailSize; +const snakeTrail = []; +const snake = { x: 10, y: 10 }; +const apple = { x: Math.floor(Math.random() * gridSize), y: Math.floor(Math.random() * gridSize) }; + +function drawBackground(){ + g.setColor("#000000"); + g.fillRect(0, 0, H, W); +} + +function drawBackgroundSuccess(){ + g.setColor("#00FFFF"); + g.fillRect(0, 0, H, W); +} + +function drawApple(){ + g.setColor("#FF0000"); + g.fillCircle((apple.x * tileSize) + tileSize/2, (apple.y * tileSize) + tileSize/2, tileSize/2); +} + +function drawSnake(){ + g.setColor("#008000"); + for (let i = 0; i < snakeTrail.length; i++) { + g.fillRect(snakeTrail[i].x * tileSize, snakeTrail[i].y * tileSize, snakeTrail[i].x * tileSize + tileSize, snakeTrail[i].y * tileSize + tileSize); + + //snake bites it's tail + if (snakeTrail[i].x === snake.x && snakeTrail[i].y === snake.y && tailSize > defaultTailSize) { + Bangle.buzz(1000); + gameOver(); + } + } + g.setColor("#FFFFFF"); + g.fillRect(snake.x*tileSize, snake.y*tileSize, snake.x*tileSize+ tileSize, snake.y*tileSize + tileSize); + + g.setColor("#0000ff"); + g.fillRect((snake.x*tileSize)+1, (snake.y*tileSize)+2, (snake.x*tileSize)+2, (snake.y*tileSize)+4); + + g.setColor("#0000ff"); + g.fillRect((snake.x*tileSize)+tileSize-1, (snake.y*tileSize)+2, (snake.x*tileSize)+tileSize-2, (snake.y*tileSize)+4); + +} + +function drawScore(){ + g.setColor("#555555"); + g.setFont("Vector20"); + g.setFontAlign(0, 0); + g.drawString("Score:" + score, W / 2, 10); +} + +function gameStart() { + running = true; + score = 0; +} + +function gameOver() { + g.clear(); + g.setColor("#000000"); + g.setFont("Vector12"); + g.drawString("GAME OVER!", W / 2, H / 2 - 20); + g.drawString("Score: " + score, W / 2, H / 2 - 10); + g.drawString("Tap to Restart", W / 2, H / 2 + 10); + running = false; + tailSize = defaultTailSize; +} + +function draw() { + if (!running) { + return; + } + + g.clear(); + + // move snake in next pos + snake.x += nextX; + snake.y += nextY; + + // snake over game world + if (snake.x < 0) { + snake.x = gridSize - 1; + } + if (snake.x > gridSize - 1) { + snake.x = 0; + } + + if (snake.y < 0) { + snake.y = gridSize - 1; + } + if (snake.y > gridSize - 1) { + snake.y = 0; + } + + //snake bite apple + if (snake.x === apple.x && snake.y === apple.y) { + Bangle.beep(20); + drawBackgroundSuccess(); + tailSize++; + score++; + + apple.x = Math.floor(Math.random() * gridSize); + apple.y = Math.floor(Math.random() * gridSize); + drawApple(); + } + + drawBackground(); + drawApple(); + drawSnake(); + drawScore(); + + //set snake trail + snakeTrail.push({ x: snake.x, y: snake.y }); + while (snakeTrail.length > tailSize) { + snakeTrail.shift(); + } + + g.flip(); +} + +let dDiff = 10; + +Bangle.on('drag', function(a) { + + if (a.dx > dDiff ) { // right + if (d !== 'l') + { + nextX = 1; + nextY = 0; + d = 'r'; + } + } + + if (a.dx < -dDiff ) { // left + if (d !== 'r') + { + nextX = -1; + nextY = 0; + d = 'l'; + } + } + + if (a.dy < -dDiff) { // Up + if (d !== 'd') { + nextX = 0; + nextY = -1; + d = 'u'; + } + } + + + if (a.dy > dDiff) { // Down + if (d !== 'u') + { + nextX = 0; + nextY = 1; + d = 'd'; + } + } + +}); + + + + +Bangle.on('touch', button => { + if (!running) { + gameStart(); + } +}); + + +// render X times per second +const x = 5; +setInterval(draw, 1000 / x); diff --git a/apps/snaky/snaky.png b/apps/snaky/snaky.png new file mode 100644 index 000000000..388c32126 Binary files /dev/null and b/apps/snaky/snaky.png differ diff --git a/apps/snek/ChangeLog b/apps/snek/ChangeLog new file mode 100644 index 000000000..7c93db451 --- /dev/null +++ b/apps/snek/ChangeLog @@ -0,0 +1,2 @@ +0.01: First release +0.02: Fixed snek.png and snek.icon.js to 64x64 to display in launcher, added screenshots, updated apps.json diff --git a/apps/snek/screenshot_snek.png b/apps/snek/screenshot_snek.png new file mode 100644 index 000000000..01a15bb27 Binary files /dev/null and b/apps/snek/screenshot_snek.png differ diff --git a/apps/snek/screenshot_snek2.png b/apps/snek/screenshot_snek2.png new file mode 100644 index 000000000..d2cb938bc Binary files /dev/null and b/apps/snek/screenshot_snek2.png differ diff --git a/apps/snek/snek-icon.js b/apps/snek/snek-icon.js deleted file mode 100644 index c919c429e..000000000 --- a/apps/snek/snek-icon.js +++ /dev/null @@ -1 +0,0 @@ -require("heatshrink").decompress(atob("pFIwkB+MRAEH/EUIA7i93u9xEUIABEf4AC+93/4CBETpDBv//gEHEf6MB9wkB/8HSDl3vwjCAAfnErIjiEQYdBAAXuAoSNXEYIdDAAoj4j/3DpN3v6NWAA3/fDYjgRgIjLu9xESj2BAAN/SQwLBEe4XDdwghDBQQjSCgN+C5D9FEebTDEZJEWEQSDVEdZpDZYPnETYhDAAhpeEbzREI0rTbEdXuETb4Bvz1BAYIj/EYxrg9yQDv/3JoS9WEcoaGAAQtBOwYABEaSMBWoYeFJKgjjiIUD9ySEEjwAJFogj0SQgAFBQ4jRABcfXoQj/TowjCOgIkeEf7lHvz+CEb93Ef4jHR8Rr/fY4jCuIifAAQj/EIojbeohGeEcQhHfLRFDeoIicEcQbDv3uEYiNXIgnu87SgEcf/DKnxBJEf/4ACESf/EcYA==")) \ No newline at end of file diff --git a/apps/snek/snek.icon.js b/apps/snek/snek.icon.js new file mode 100644 index 000000000..b820ffcf7 --- /dev/null +++ b/apps/snek/snek.icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("oFA4X/AAOJksvr2rmokYgWqB7sq/2AB5krgYPMgW8ioPc1X9i/oLplVqv+1BdK1OV//q9QPMv4PL1eqy/q1SRK3tVu+AgWCFxP96t+Vhn9qoPLgWr/+//wFBSBEq3/qlW+JwJ/I3eXDQIOBB5OrB5sC3xMD1WAH4+r6xsOtSpKLoYPN1fV1bpKTYf+RJAeDytXFxoPOdQYPNPpkCy1VtQPc6wvO62Vu+CbhfVN4P//+q//uMgwPH9QPH3tqqtpqoABv4wHfoOpBoP/6tVUg7uBFwIvB3xlIB4v+OpJsC1WA1fVQpiGCB52+uzlMB58A31XB5sqy4PNlYPfH50rywPN3++BxgPPgW9V5kCZ4L/HBwmq/tX1APM/4PMBwNVvxuKgW/tP/HxUq1X+1eqFxQPRAAKsLB4KqNAFY=")) diff --git a/apps/snek/snek.png b/apps/snek/snek.png index 8cf8d05e3..6e31cfcbb 100644 Binary files a/apps/snek/snek.png and b/apps/snek/snek.png differ diff --git a/apps/speedalt2/ChangeLog b/apps/speedalt2/ChangeLog index 91f01988e..fa2e32f5b 100644 --- a/apps/speedalt2/ChangeLog +++ b/apps/speedalt2/ChangeLog @@ -1,2 +1,4 @@ 0.01: Initial import. 0.07: Add swipe to change screens. +1.06: Misc memory and screen optimisations. +1.10: ... diff --git a/apps/speedalt2/app.js b/apps/speedalt2/app.js index 0db9629c7..fd53cf7c3 100644 --- a/apps/speedalt2/app.js +++ b/apps/speedalt2/app.js @@ -1,11 +1,9 @@ /* Speed and Altitude [speedalt2] Mike Bennett mike[at]kereru.com -0.01 : Initial -0.06 : Add Posn screen -0.07 : Add swipe to change screens same as BTN3 +1.10 : add inverted colours */ -var v = '1.05'; +var v = '1.10'; /*kalmanjs, Wouter Bulten, MIT, https://github.com/wouterbulten/kalmanjs */ var KalmanFilter = (function () { @@ -173,6 +171,15 @@ var KalmanFilter = (function () { var buf = Graphics.createArrayBuffer(240,160,2,{msb:true}); +let LED = // LED as minimal and only definition (as instance / singleton) +{ isOn: false // status on / off, not needed if you don't need to ask for it +, set: function(v) { // turn on w/ no arg or truey, else off + g.setColor((this.isOn=(v===undefined||!!v))?1:0,0,0).fillCircle(40,10,10); } +, reset: function() { this.set(false); } // turn off +, write: function(v) { this.set(v); } // turn on w/ no arg or truey, else off +, toggle: function() { this.set( ! this.isOn); } // toggle the LED +}, LED1 = LED; // LED1 as 'synonym' for LED + // Load fonts //require("Font7x11Numeric7Seg").add(Graphics); @@ -183,17 +190,16 @@ var canDraw = 1; var time = ''; // Last time string displayed. Re displayed in background colour to remove before drawing new time. var tmrLP; // Timer for delay in switching to low power after screen turns off -var max = {}; -max.spd = 0; -max.alt = 0; -max.n = 0; // counter. Only start comparing for max after a certain number of fixes to allow kalman filter to have smoohed the data. +var maxSpd = 0; +var maxAlt = 0; +var maxN = 0; // counter. Only start comparing for max after a certain number of fixes to allow kalman filter to have smoohed the data. var emulator = (process.env.BOARD=="EMSCRIPTEN")?1:0; // 1 = running in emulator. Supplies test values; var wp = {}; // Waypoint to use for distance from cur position. -function nxtWp(inc){ - cfg.wp+=inc; +function nxtWp(){ + cfg.wp++; loadWp(); } @@ -227,7 +233,8 @@ function drawScrn(dat) { if (!canDraw) return; buf.clear(); - + buf.setBgColor(0); + var n; n = dat.val.toString(); @@ -252,29 +259,21 @@ function drawScrn(dat) { buf.setFontVector(35); buf.drawString(dat.unit,5,164); - if ( dat.max ) drawMax(); // MAX display indicator - if ( dat.wp ) drawWP(); // Waypoint name - - //Sats - if ( dat.sat ) { - if ( dat.age > 10 ) { - if ( dat.age > 90 ) dat.age = '>90'; - drawSats('Age:'+dat.age); - } - else drawSats('Sats:'+dat.sats); - } - + drawMax(dat.max); // MAX display indicator + drawWP(dat.wp); // Waypoint name + drawSats(dat.sats); + g.reset(); g.drawImage(img,0,40); - if ( pwrSav ) LED1.reset(); - else LED1.set(); + LED1.write(!pwrSav); } function drawPosn(dat) { if (!canDraw) return; buf.clear(); + buf.setBgColor(0); var x, y; x=210; @@ -293,20 +292,12 @@ function drawPosn(dat) { buf.drawString(dat.ew,x,y+70); - //Sats - if ( dat.sat ) { - if ( dat.age > 10 ) { - if ( dat.age > 90 ) dat.age = '>90'; - drawSats('Age:'+dat.age); - } - else drawSats('Sats:'+dat.sats); - } + drawSats(dat.sats); g.reset(); g.drawImage(img,0,40); - if ( pwrSav ) LED1.reset(); - else LED1.set(); + LED1.write(!pwrSav); } @@ -314,6 +305,8 @@ function drawClock() { if (!canDraw) return; buf.clear(); + buf.setBgColor(0); + var x, y; x=185; y=0; @@ -329,19 +322,14 @@ function drawClock() { g.reset(); g.drawImage(img,0,40); - if ( pwrSav ) LED1.reset(); - else LED1.set(); + LED1.write(!pwrSav); } -function drawWP() { - var nm = wp.name; - if ( nm == undefined || nm == 'NONE' || cfg.modeA ==1 ) nm = ''; - buf.setColor(2); - +function drawWP(wp) { + buf.setColor(3); buf.setFontAlign(0,1); //left, bottom buf.setFontVector(48); - buf.drawString(nm.substring(0,8),120,140); - + buf.drawString(wp,120,140); } function drawSats(sats) { @@ -351,16 +339,15 @@ function drawSats(sats) { buf.drawString(sats,240,160); } -function drawMax() { +function drawMax(max) { buf.setFontVector(30); buf.setColor(2); buf.setFontAlign(0,1); //centre, bottom - buf.drawString('MAX',120,164); + buf.drawString(max,120,164); } function onGPS(fix) { - - if ( emulator ) { +if ( emulator ) { fix.fix = 1; fix.speed = 10 + (Math.random()*5); fix.alt = 354 + (Math.random()*50); @@ -382,10 +369,15 @@ function onGPS(fix) { var ns = ''; var ew = ''; var lon = '---.--'; + var sats = '---'; + // Waypoint name + var wpName = wp.name; + if ( wpName == undefined || wpName == 'NONE' ) wpName = ''; + wpName = wpName.substring(0,8); if (fix.fix) lf = fix; - + if (lf.fix) { // Smooth data @@ -393,10 +385,9 @@ function onGPS(fix) { if ( cfg.spdFilt ) lf.speed = spdFilter.filter(lf.speed); if ( cfg.altFilt ) lf.alt = altFilter.filter(lf.alt); lf.smoothed = 1; - if ( max.n <= 15 ) max.n++; + if ( maxN <= 15 ) maxN++; } - // Speed if ( cfg.spd == 0 ) { m = require("locale").speed(lf.speed).match(/([0-9,\.]+)(.*)/); // regex splits numbers from units @@ -407,18 +398,19 @@ function onGPS(fix) { if ( sp < 10 ) sp = sp.toFixed(1); else sp = Math.round(sp); + if (isNaN(sp)) sp = '---'; - if (parseFloat(sp) > parseFloat(max.spd) && max.n > 15 ) max.spd = sp; + if (parseFloat(sp) > parseFloat(maxSpd) && maxN > 15 ) maxSpd = sp; // Altitude al = lf.alt; al = Math.round(parseFloat(al)/parseFloat(cfg.alt)); - - if (parseFloat(al) > parseFloat(max.alt) && max.n > 15 ) max.alt = al; + if (parseFloat(al) > parseFloat(maxAlt) && maxN > 15 ) maxAlt = al; + if (isNaN(al)) al = '---'; // Distance to waypoint di = distance(lf,wp); - if (isNaN(di)) di = 0; + if (isNaN(di)) di = '--------'; // Age of last fix (secs) age = Math.max(0,Math.round(getTime())-(lf.time.getTime()/1000)); @@ -431,6 +423,13 @@ function onGPS(fix) { ew = 'E'; if ( lf.lon < 0 ) ew = 'W'; lon = Math.abs(lf.lon.toFixed(2)); + + // Sats + if ( age > 10 ) { + sats = 'Age:'+Math.round(age); + if ( age > 90 ) sats = 'Age:>90'; + } + else sats = 'Sats:'+lf.satellites; } @@ -438,23 +437,21 @@ function onGPS(fix) { // Speed if ( showMax ) drawScrn({ - val:max.spd, + val:maxSpd, unit:cfg.spd_unit, - sats:lf.satellites, + sats:sats, age:age, - max:true, - wp:false, - sat:true + max:'MAX', + wp:'' }); // Speed maximums else drawScrn({ val:sp, unit:cfg.spd_unit, - sats:lf.satellites, + sats:sats, age:age, - max:false, - wp:false, - sat:true + max:'SPD', + wp:'' }); } @@ -462,23 +459,21 @@ function onGPS(fix) { // Alt if ( showMax ) drawScrn({ - val:max.alt, + val:maxAlt, unit:cfg.alt_unit, - sats:lf.satellites, + sats:sats, age:age, - max:true, - wp:false, - sat:true + max:'MAX', + wp:'' }); // Alt maximums else drawScrn({ val:al, unit:cfg.alt_unit, - sats:lf.satellites, + sats:sats, age:age, - max:false, - wp:false, - sat:true + max:'ALT', + wp:'' }); } @@ -487,24 +482,22 @@ function onGPS(fix) { drawScrn({ val:di, unit:cfg.dist_unit, - sats:lf.satellites, + sats:sats, age:age, - max:false, - wp:true, - sat:true + max:'DST', + wp:wpName }); } if ( cfg.modeA == 3 ) { // Position - drawPosn({ - sats:lf.satellites, + drawPosn({ + sats:sats, age:age, lat:lat, lon:lon, ns:ns, - ew:ew, - sat:true + ew:ew }); } @@ -534,9 +527,9 @@ function nextFunc(dur) { if ( cfg.modeA == 0 || cfg.modeA == 1 ) { // Spd+Alt mode - Switch between fix and MAX if ( dur < 2 ) showMax = !showMax; // Short press toggle fix/max display - else { max.spd = 0; max.alt = 0; } // Long press resets max values. + else { maxSpd = 0; maxAlt = 0; } // Long press resets max values. } - else if ( cfg.modeA == 2) nxtWp(1); // Dist mode - Select next waypoint + else if ( cfg.modeA == 2) nxtWp(); // Dist mode - Select next waypoint onGPS(lf); } @@ -545,7 +538,7 @@ function updateClock() { if (!canDraw) return; if ( cfg.modeA != 4 ) return; drawClock(); - if ( emulator ) {max.spd++;max.alt++;} + if ( emulator ) {maxSpd++;maxAlt++;} } function startDraw(){ @@ -585,7 +578,6 @@ function setButtons(){ setWatch(function(e){ pwrSav=!pwrSav; if ( pwrSav ) { - LED1.reset(); var s = require('Storage').readJSON('setting.json',1)||{}; var t = s.timeout||10; Bangle.setLCDTimeout(t); @@ -593,8 +585,8 @@ function setButtons(){ else { Bangle.setLCDTimeout(0); // Bangle.setLCDPower(1); - LED1.set(); } + LED1.write(!pwrSav); }, BTN2, {repeat:true,edge:"falling"}); // BTN3 - next screen @@ -690,7 +682,8 @@ var img = { }; if ( cfg.colour == 1 ) img.palette = new Uint16Array([0,0xFFFF,0xFFF6,0xDFFF]); -if ( cfg.colour == 2 ) img.palette = new Uint16Array([0,0xFF800,0xFAE0,0xF813]); +if ( cfg.colour == 2 ) img.palette = new Uint16Array([0,0xF800,0xFAE0,0xF813]); +if ( cfg.colour == 3 ) img.palette = new Uint16Array([0xFFFF,0x007F,0x0054,0x0054]); var SCREENACCESS = { withApp:true, diff --git a/apps/speedalt2/settings.js b/apps/speedalt2/settings.js index 96174a89b..fe30d88df 100644 --- a/apps/speedalt2/settings.js +++ b/apps/speedalt2/settings.js @@ -65,7 +65,8 @@ '< Back': function() { E.showMenu(appMenu); }, 'Default' : function() { setColour(0); }, 'Hi Contrast' : function() { setColour(1); }, - 'Night' : function() { setColour(2); } + 'Night' : function() { setColour(2); }, + 'Inverted' : function() { setColour(3); } }; const kalMenu = { diff --git a/apps/stopwatch/README.md b/apps/stopwatch/README.md index 30a9306d1..ceeafaefc 100644 --- a/apps/stopwatch/README.md +++ b/apps/stopwatch/README.md @@ -31,3 +31,6 @@ Which one is which ? ![](A.jpg) ![](B.jpg) + + +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/) diff --git a/apps/supf/ChangeLog b/apps/supf/ChangeLog new file mode 100644 index 000000000..55ebb3e4b --- /dev/null +++ b/apps/supf/ChangeLog @@ -0,0 +1 @@ +0.01: New App diff --git a/apps/supf/README.md b/apps/supf/README.md new file mode 100644 index 000000000..258a69d4b --- /dev/null +++ b/apps/supf/README.md @@ -0,0 +1,4 @@ +# Simple Clock with Date +Simple Clock with seconds and date in custom language. Install 'Languages' to get localized names. + +![](screenshot_supf.png) diff --git a/apps/supf/app.js b/apps/supf/app.js new file mode 100644 index 000000000..d7a30aed9 --- /dev/null +++ b/apps/supf/app.js @@ -0,0 +1,56 @@ +require("Font7x11Numeric7Seg").add(Graphics); + +function draw() { + var d = new Date(); + var size = Math.floor(g.getWidth()/(7*6)); + var x = (g.getWidth()/2) - size*6, + y = (g.getHeight()/2) - size*7 - 0; + // y variable for ':' + var y_dop = 70 - 0; + g.reset().clearRect(0,y,g.getWidth(),y+size*28); + // draw hours in 24h format + g.setFont("7x11Numeric7Seg",size).setFontAlign(1,-1); + if (d.getHours().toString.length < 2) { + g.drawString('0'+d.getHours(), 58, y); + } + else { + g.drawString(d.getHours(), 58, y); + } + g.setFont("7x11Numeric7Seg",size/2).setFontAlign(1,-1); + g.drawString(":",64,y_dop); + g.setFont("7x11Numeric7Seg",size).setFontAlign(1,-1); + // draw minutes + g.drawString(("0"+d.getMinutes()).substr(-2),118,y); + g.setFont("7x11Numeric7Seg",size/2).setFontAlign(1,-1); + g.drawString(":",124,y_dop); + // draw seconds + g.setFont("7x11Numeric7Seg",size).setFontAlign(1,-1); + g.drawString(("0"+d.getSeconds()).substr(-2),178,y); + // date + g.setFont("6x8",size/2).setFontAlign(0,-1); + // draw name of day + g.drawString(require('locale').dow(new Date()),g.getWidth()/2, y + size*16); + // draw date and name of month + g.drawString(d.getDate()+' '+require('locale').month(new Date()),g.getWidth()/2, y + size*20); + // draw year + g.drawString((d.getFullYear()),g.getWidth()/2, y + size*24); + +} +// Only update when display turns on +if (process.env.BOARD!="SMAQ3") // hack for Q3 which is always-on +Bangle.on('lcdPower', function(on) { + if (secondInterval) + clearInterval(secondInterval); + secondInterval = undefined; + if (on) + secondInterval = setInterval(draw, 1000); + draw(); +}); + +g.clear(); +var secondInterval = setInterval(draw, 1000); +draw(); +// Show launcher when button pressed +Bangle.setUI("clock"); +Bangle.loadWidgets(); +Bangle.drawWidgets(); diff --git a/apps/supf/icon.js b/apps/supf/icon.js new file mode 100644 index 000000000..9b880b5e9 --- /dev/null +++ b/apps/supf/icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4UBAoP/AAXnnNVAH4ACoEABZJXBgoLUGaIAIgILLbf4AUnWqweq1gEB4QEBBZ0OwEA9k7h3C2ALGlk4BZAeBBZAvCBZOv/gLJ2EABZOggE7hQLFL5U+1XDBYYEB3jB/AClABRMBqoAXEhAiCBZdQBY8FHTAAj")) diff --git a/apps/supf/icon.png b/apps/supf/icon.png new file mode 100644 index 000000000..2ca4d6140 Binary files /dev/null and b/apps/supf/icon.png differ diff --git a/apps/supf/screenshot_supf.png b/apps/supf/screenshot_supf.png new file mode 100644 index 000000000..18e391f28 Binary files /dev/null and b/apps/supf/screenshot_supf.png differ diff --git a/apps/swp2clk/ChangeLog b/apps/swp2clk/ChangeLog new file mode 100644 index 000000000..ea6473980 --- /dev/null +++ b/apps/swp2clk/ChangeLog @@ -0,0 +1 @@ +0.01: Initial creation of "Swipe back to the Clock" App. Let's you swipe from left to right on any app to return back to the clock face. diff --git a/apps/swp2clk/README.md b/apps/swp2clk/README.md new file mode 100644 index 000000000..877a13ecc --- /dev/null +++ b/apps/swp2clk/README.md @@ -0,0 +1,22 @@ +# Swipe back to the Clock + +Let's you swipe from left to right on any app to return back to the clock face. + +## Configurable Modes: + +The swipe modes can be configured in the settings app, under "Swipe to Clock". + +- Always Off: Deactivated for all apps (Default) +- White List: Only activate for chosen apps, otherwise deactivated for all apps. +- Black List: Only disabled for chosen apps, otherwise activated for all apps. +- Always On: Active for all apps (Not actually recommended! E.g. Games need to be able to deal with swipe gestures) + +## Motivation: + +The goal is to further support touch-only usage of the BangleJS 2 watch. You can use the [Swiper Clock Launch](https://banglejs.com/apps/#swiper%20clock%20launch) or the [Pattern Launcher](https://banglejs.com/apps/#pattern%20launcher) to further enable touch-only support of the watch. + +## Credits: + +Initial creation: [crazysaem](https://github.com/crazysaem) + +Inspired by: [Swiper Clock Launch](https://banglejs.com/apps/#swiper%20clock%20launch) \ No newline at end of file diff --git a/apps/swp2clk/app.png b/apps/swp2clk/app.png new file mode 100644 index 000000000..b964520e6 Binary files /dev/null and b/apps/swp2clk/app.png differ diff --git a/apps/swp2clk/boot.js b/apps/swp2clk/boot.js new file mode 100644 index 000000000..bb8e792c4 --- /dev/null +++ b/apps/swp2clk/boot.js @@ -0,0 +1,109 @@ +/** + * How does this work? + * + * Every *boot.js file is executed everytime any app is loaded, including this one. + * We just need to figure out which app is currently loaded, in case we are in the white list / black list mode, + * to figure out if the swipe handler should be attached or not. + * It does not seem to be the case that this can be done easily, but we can work around it. + * It seems that every app is loaded via the global "load" function, which takes a fileName as it's first parameter to load any app + * or the default clock when the fileName is undefined. + * To be able to use this for us, we wrap the global "load" function, and determine before loading the next app, + * whether the swipe handler should be added or not, since we now know which app will be loaded. + * Note: We cannot add the swipe handler inside the wrapped "load" function, because once the "load" function is complete + * everything is cleaned up. That's why we merely save a flag, whether the swipe handler should be attached or not, + * which is evaluated once this file is executed again, which will be right after the load function completes + * (since every *boot.js file is executed everytime any app is loaded). + */ + +(function () { + var DEBUG = false; + var FILE = "swp2clk.data.json"; + + var main = () => { + var settings = readSettings(); + + if (settings.addSwipeHandler) { + var swipeHandler = (dir) => { + log("swipe"); + log(dir); + if (dir === 1) { + load(); + } + }; + Bangle.on("swipe", swipeHandler); + } + + var global_load = global.load; + global.load = (fileName) => { + log("loading filename!"); + log(fileName); + var settings = readSettings(); + + if (fileName) { + // "Off" + if (settings.mode === 0) { + settings.addSwipeHandler = false; + } + + // "White List" + if (settings.mode === 1) { + if (settings.whiteList.indexOf(fileName) >= 0) { + settings.addSwipeHandler = true; + } else { + settings.addSwipeHandler = false; + } + } + + // "Black List" + if (settings.mode === 2) { + if (settings.blackList.indexOf(fileName) >= 0) { + settings.addSwipeHandler = false; + } else { + settings.addSwipeHandler = true; + } + } + + // "Always" + if (settings.mode === 3) { + settings.addSwipeHandler = true; + } + } else { + // Clock will load + settings.addSwipeHandler = false; + } + + writeSettings(settings); + global_load(fileName); + }; + }; + + // lib functions + + var log = (message) => { + if (DEBUG) { + console.log(JSON.stringify(message)); + } + }; + + var readSettings = () => { + log("reading settings"); + var settings = require("Storage").readJSON(FILE, 1) || { + mode: 0, + whiteList: [], + blackList: [], + addSwipeHandler: false, + }; + log(settings); + return settings; + }; + + var writeSettings = (settings) => { + log("writing settings"); + log(settings); + require("Storage").writeJSON(FILE, settings); + }; + + // start main function + + main(); +})(); diff --git a/apps/swp2clk/settings.js b/apps/swp2clk/settings.js new file mode 100644 index 000000000..a97b51fab --- /dev/null +++ b/apps/swp2clk/settings.js @@ -0,0 +1,181 @@ +(function (back) { + var DEBUG = false; + var FILE = "swp2clk.data.json"; + + var settings = {}; + + var showMainMenu = () => { + log("Loading main menu"); + + E.showMenu({ + "": { title: "Swipe to Clock" }, + "< Back": () => back(), + Mode: { + value: settings.mode, + min: 0, + max: 3, + format: (value) => ["Off", "White List", "Black List", "Always"][value], + onchange: (value) => { + settings.mode = value; + writeSettings(settings); + }, + }, + "White List": () => showWhiteListMenu(), + "Black List": () => showBlackListMenu(), + }); + }; + + var showWhiteListMenu = () => { + var appList = getAppList(); + + var whiteListMenu = { + "": { title: "White List" }, + "< Back": () => showMainMenu(), + "_Add App_": () => { + var addAppMenu = { + "": { title: "Add app to WL" }, + "< Back": () => showWhiteListMenu(), + }; + + appList.forEach((app) => { + if (settings.whiteList.indexOf(app.src) < 0) { + addAppMenu[app.name] = () => { + settings.whiteList.push(app.src); + writeSettings(settings); + showWhiteListMenu(); + }; + } + }); + + E.showMenu(addAppMenu); + }, + }; + + appList.forEach((app) => { + if (settings.whiteList.indexOf(app.src) >= 0) { + whiteListMenu[app.name] = () => { + E.showPrompt("Delete from WL?", { + title: "Delete from WL?", + buttons: { Yes: true, No: false }, + }).then(function (flag) { + if (flag) { + settings.whiteList.splice(index, 1); + writeSettings(settings); + } + + showWhiteListMenu(); + }); + }; + } + }); + + log("Loading white list menu"); + E.showMenu(whiteListMenu); + }; + + var showBlackListMenu = () => { + var appList = getAppList(); + + var blackListMenu = { + "": { title: "Black List" }, + "< Back": () => showMainMenu(), + "_Add App_": () => { + var addAppMenu = { + "": { title: "Add app to BL" }, + "< Back": () => showBlackListMenu(), + }; + + appList.forEach((app) => { + if (settings.blackList.indexOf(app.src) < 0) { + addAppMenu[app.name] = () => { + settings.blackList.push(app.src); + writeSettings(settings); + showBlackListMenu(); + }; + } + }); + + E.showMenu(addAppMenu); + }, + }; + + appList.forEach((app) => { + if (settings.blackList.indexOf(app.src) >= 0) { + blackListMenu[app.name] = () => { + E.showPrompt("Delete from BL?", { + title: "Delete from BL?", + buttons: { Yes: true, No: false }, + }).then(function (flag) { + if (flag) { + settings.blackList.splice(index, 1); + writeSettings(settings); + } + + showBlackListMenu(); + }); + }; + } + }); + + log("Loading black list menu"); + E.showMenu(blackListMenu); + }; + + // lib functions + + var log = (message) => { + if (DEBUG) { + console.log(JSON.stringify(message)); + } + }; + + var readSettings = () => { + log("reading settings"); + var settings = require("Storage").readJSON(FILE, 1) || { + mode: 0, + whiteList: [], + blackList: [], + addSwipeHandler: false, + }; + log(settings); + return settings; + }; + + var writeSettings = (settings) => { + log("writing settings"); + log(settings); + require("Storage").writeJSON(FILE, settings); + }; + + var getAppList = () => { + var appList = storage + .list(/\.info$/) + .map((appInfoFileName) => { + var appInfo = storage.readJSON(appInfoFileName, 1); + return ( + appInfo && { + name: appInfo.name, + // type: appInfo.type, + // icon: appInfo.icon, + sortorder: appInfo.sortorder, + src: appInfo.src, + } + ); + }) + .filter((app) => app && !!app.src); + appList.sort((a, b) => { + var n = (0 | a.sortorder) - (0 | b.sortorder); + if (n) return n; // do sortorder first + if (a.name < b.name) return -1; + if (a.name > b.name) return 1; + return 0; + }); + + return appList; + }; + + // start main function + + settings = readSettings(); + showMainMenu(); +}); diff --git a/apps/tapelauncher/icon.js b/apps/tapelauncher/icon.js index bf323e5bf..25ca0a4c6 100644 --- a/apps/tapelauncher/icon.js +++ b/apps/tapelauncher/icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("AH4A/ACXd7vQC6vUpoBBDaQXEDaQXIDZwXMAIQZHC4R6BAAIZJDAwXIDY4XHAAodJ7oXMDpQXSAAiRHhoWN7zFLDY/e9ve9zeMhvQCIIBFC5ARIC5oVNC5EOCpwABC4vuCZYXPCIwXOCJAAFC5gAJ8AXFCpwuHgDjCFqQXC6lN6gbFf5gXEAInd6AXVDYndhoXKBoIbMC5QZLC44AFDpIXNDpQXdhoYMAAbwIC6oZQbxhOKC5gbKC6BUGC6oA/AHgA==")) +require("heatshrink").decompress(atob("mEw4UA///sH8ov+8GyJf4AIgt8BZV9voNIBYQNIBYgNGBYwMEBYNVqoMEoALGBoYLDBQILCAQVQBYoOEBZIABBYUAgILGsBiEBodWy2gN4soywACBYcI1QJDBYoJFBYkCBQ2qBYUKBIoLHBAQLHBAYACBYwAEwALBgwKG1S/DC4wWCa4Y3Efa19mALKvrLDfY7XGBwjvVBYjuHfYgLLBg4LEAAMVBZQNEBZBPCBZQA+A")) diff --git a/apps/teatimer/README.md b/apps/teatimer/README.md new file mode 100644 index 000000000..71bec3ea8 --- /dev/null +++ b/apps/teatimer/README.md @@ -0,0 +1,45 @@ +# Tea Timer app + +A simple timer. You can easyly set up the time. The initial time is 2:30 + +On the first screen, you can +- tap to get help +- swipe up/down to change the timer by +/- one minute +- swipe left/right to change the time by +/- 15 seconds +- press Btn1 to start + +Press Btn1 again to stop the timer +- when time is up, your Bangle will buzz for 15 seconds +- and it will count up to 60 seconds and stop after that + +## Images +_1. Startscreen_ + +![](TeatimerStart.jpg) +Current time is displayed below the Title. Initial time is 2:30. + +_2. Help Screen_ + +![](TeatimerHelp.jpg) + +_3. Tea Timer running_ + +![](TeatimerRun.jpg) +Remainig time is shown in big font size. Above the initial time is shown. + +_4. When time is up_ + +![](TeatimerUp.jpg) +When time is up, the watch will buzz for 15 seconds. It will count up to 60 seconds. + +## Requests + +Please mail any issues to thomas.fehling@mailbox.org + +## Creator + +Thomas Fehling + +## Attributions + +Icons used in this app are from https://icons8.com diff --git a/apps/teatimer/TeatimerHelp.jpg b/apps/teatimer/TeatimerHelp.jpg new file mode 100644 index 000000000..e22960c66 Binary files /dev/null and b/apps/teatimer/TeatimerHelp.jpg differ diff --git a/apps/teatimer/TeatimerRun.jpg b/apps/teatimer/TeatimerRun.jpg new file mode 100644 index 000000000..a442d12a5 Binary files /dev/null and b/apps/teatimer/TeatimerRun.jpg differ diff --git a/apps/teatimer/TeatimerStart.jpg b/apps/teatimer/TeatimerStart.jpg new file mode 100644 index 000000000..4fa8f2fc4 Binary files /dev/null and b/apps/teatimer/TeatimerStart.jpg differ diff --git a/apps/teatimer/TeatimerUp.jpg b/apps/teatimer/TeatimerUp.jpg new file mode 100644 index 000000000..80b8c3c8a Binary files /dev/null and b/apps/teatimer/TeatimerUp.jpg differ diff --git a/apps/teatimer/app-icon.js b/apps/teatimer/app-icon.js new file mode 100644 index 000000000..ae91f88d7 --- /dev/null +++ b/apps/teatimer/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+AAONjQAoxoutGAYusGAQutABs4qwAUnAwYik4vQAQnEUMTcxqoAOm6Sdm9PAB0TF7sTF90awIAOFz0bxIAOjYv/F/2s1eq5wAI1Wr1gvg4/GABXH1Yvg5wvL5xffjWIMBfH1lkFzwwC1YvJ1eIF0CRC5CNJF0KRK5CNiGBS8BRsQwJ468jAA2JGAJdpF4et1ms1uJF9YADF1GIxIAD1qQoFwgwpLwoADF1jABxJgjLpGt2YwBjYugjaMIF4IwBsgvgsgvMxIwfLxIwC66RhF/ezF9q/B6/XF9xfrLwYvhjWBABGsAAOmwWBFz0Tp4AOiYvdm4vumNVAB0xFrcUnF6ACE4iguYnFWACk4ebwAcxouu/wwsFwIABGFQuCA==")) diff --git a/apps/teatimer/app.js b/apps/teatimer/app.js new file mode 100644 index 000000000..dd7afdadb --- /dev/null +++ b/apps/teatimer/app.js @@ -0,0 +1,233 @@ +// Tea Timer +// Button press stops timer, next press restarts timer +let drag; +var counter = 0; +var counterStart = 150; // 150 seconds +var counterInterval; +const states = { + init: 1, // unused + help: 2, // show help text + start: 4, // show/change initial counter + count: 8, // count down + countUp: 16, // count up after timer finished + stop: 32 // timer stopped +}; +var state = states.start; +E.setTimeZone(1); + +// Title showing current time +function appTitle() { + return "Tea Timer " + currentTime(); +} + +function currentTime() { + min = Date().getMinutes(); + if (min < 10) min = "0" + min; + return Date().getHours() + ":" + min; +} + +function timeFormated(sec) { + var min = Math.floor(sec / 60); + sec = sec % 60; + if (sec < 10) sec = "0" + sec; + return min + ":" + sec; +} + +// initialize timer and show timer value => state: start +function initTimer() { + counter = counterStart; + setState(states.start); + showCounter(true); +} + +// timer value (counter) can be changed in state start +function changeCounter(diff) { + if (state == states.start) { + if (counter + diff > 0) { + counter = counter + diff; + showCounter(true); + } + } +} + +// start or restart timer => state: count +function startTimer() { + counterStart = counter; + setState(states.count); + countDown(); + if (!counterInterval) + counterInterval = setInterval(countDown, 1000); +} + +/* show current counter value at start and while count down + Show + - Title with current time + - initial timer value + - remaining time + - hint for help in state start +*/ +function showCounter(withHint) { + //g.clear(); + E.showMessage("", appTitle()); + g.setFontAlign(0,0); // center font + // draw the current counter value + g.setBgColor(-1).setColor(0,0,1); // blue + g.setFont("Vector",20); // vector font, 20px + g.drawString("Timer: " + timeFormated(counterStart),80,55); + g.setFont("Vector",60); // vector font, 60px + g.drawString(timeFormated(counter),83,100); + if (withHint) { + g.setFont("Vector",20); // vector font, 80px + g.drawString("Tap for help",80,150); + } +} + +// count down and update every second +// when time is up, start counting up +function countDown() { + counter--; + // Out of time + if (counter<=0) { + outOfTime(); + countUp(); + counterInterval = setInterval(countUp, 1000); + return; + } + showCounter(false); +} + +// +function outOfTime() { + E.showMessage("Time is up!",appTitle()); + setState(states.countUp); + resetTimer(); + Bangle.buzz(); + Bangle.buzz(); +} + +/* this counts up (one minute), after time is up + Show + - Title with current time + - initial timer value + - "Time is up!" + - time since timer finished +*/ +function countUp() { + // buzz for 15 seconds + counter++; + if (counter <=15) { + Bangle.buzz(); + } + // stop counting up after 60 seconds + if (counter > 60) { + outOfTime(); + return; + } + g.clear(); + E.showMessage("", appTitle()); + g.setFontAlign(0,0); // center font + g.setBgColor(-1).setColor(0,0,1); // blue + g.setFont("Vector",20); // vector font, 20px + g.drawString("Timer: " + timeFormated(counterStart),80,55); + g.setFont("Vector",30); // vector font, 80px + g.setBgColor(-1).setColor(1,0,0); // red + g.drawString("Time is up!",85,85); + g.setFont("Vector",40); // vector font, 80px + // draw the current counter value + g.drawString(timeFormated(counter),80,130); +} + +// reset when interupted by user oder 60 seconds after timer finished +function resetTimer() { + clearInterval(); + counterInterval = undefined; +} + +// timer is stopped by user => state: stop +function stopTimer() { + resetTimer(); + E.showMessage("Timer stopped!", appTitle()); + setState(states.stop); +} + +// timer is stopped by user while counting up => state: start +function stopTimer2() { + resetTimer(); + initTimer(); +} + + +function setState(st) { + state = st; +} + +function buttonPressed() { + switch(state) { + case states.init: + initTimer(); + break; + case states.help: + initTimer(); + break; + case states.start: + startTimer(); + break; + case states.count: + stopTimer(); + break; + case states.countUp: + stopTimer2(); + break; + case states.stop: + initTimer(); + break; + default: + initTimer(); + break; + } +} + +/* Change initial counter value by swiping + swipe up: +1 minute + swipe down: -1 minute + swipe right: +15 seconds + swipe left: -15 seconds */ +function initDragEvents() { + Bangle.on("drag", e => { + if (state == states.start) { + if (!drag) { // start dragging + drag = {x: e.x, y: e.y}; + } else if (!e.b) { // released + const dx = e.x-drag.x, dy = e.y-drag.y; + drag = null; + if (Math.abs(dx)>Math.abs(dy)+10) { + // horizontal + changeCounter(dx>0 ? 15 : -15); + } else if (Math.abs(dy)>Math.abs(dx)+10) { + // vertical + changeCounter(dy>0 ? -60 : 60); + } + } + } +}); +} + +// show help text while in start state (see initDragEvents()) +function showHelp() { + if (state == states.start) { + state = states.help; + E.showMessage("Swipe up/down\n+/- one minute\n\nSwipe left/right\n+/- 15 seconds\n\nPress Btn1 to start","Tea timer help"); + } + // return to start + else if (state == states.help) { + initTimer(); + } +} + +// drag events in start state (to change counter value) +initDragEvents(); +// Show help test in start state +Bangle.on('touch', function(button, xy) { showHelp(); }); +// event handling for button1 +setWatch(buttonPressed, BTN1, {repeat: true}); +initTimer(); diff --git a/apps/teatimer/teatimer.png b/apps/teatimer/teatimer.png new file mode 100644 index 000000000..29ca58f0e Binary files /dev/null and b/apps/teatimer/teatimer.png differ diff --git a/apps/themesetter/README.md b/apps/themesetter/README.md new file mode 100644 index 000000000..518d05c01 --- /dev/null +++ b/apps/themesetter/README.md @@ -0,0 +1,22 @@ +# Theme Setter # + +This little tool allows you to configure the global theme of all Bangle.js apps +(provided that they do not override global settings) in a more comfortable way +than through the settings menu. + +![](ThemeSetter-MainScreen.png) +![](ThemeSetter-DetailSelectionScreen.png) +![](ThemeSetter-ColorSelectionScreen.png) +![](ThemeSetter-ThemePreviewScreen.png) + +This app also acts as an example for a non-trivial Bangle.js application +using the "layout" library, custom controls and generic event dispatching. +See [GitHub](https://github.com/rozek/banglejs-2-activities) for details. + +## License ## + +[MIT License](LICENSE) + +## Credits ## + +The icon for this app was taken from [icons8.com](https://icons8.com/). \ No newline at end of file diff --git a/apps/themesetter/ThemeSetter-ColorSelectionScreen.png b/apps/themesetter/ThemeSetter-ColorSelectionScreen.png new file mode 100644 index 000000000..3ed4d857e Binary files /dev/null and b/apps/themesetter/ThemeSetter-ColorSelectionScreen.png differ diff --git a/apps/themesetter/ThemeSetter-DetailSelectionScreen.png b/apps/themesetter/ThemeSetter-DetailSelectionScreen.png new file mode 100644 index 000000000..79a983652 Binary files /dev/null and b/apps/themesetter/ThemeSetter-DetailSelectionScreen.png differ diff --git a/apps/themesetter/ThemeSetter-MainScreen.png b/apps/themesetter/ThemeSetter-MainScreen.png new file mode 100644 index 000000000..3a0fc215c Binary files /dev/null and b/apps/themesetter/ThemeSetter-MainScreen.png differ diff --git a/apps/themesetter/ThemeSetter-ThemePreviewScreen.png b/apps/themesetter/ThemeSetter-ThemePreviewScreen.png new file mode 100644 index 000000000..1d21935b2 Binary files /dev/null and b/apps/themesetter/ThemeSetter-ThemePreviewScreen.png differ diff --git a/apps/themesetter/app-icon.js b/apps/themesetter/app-icon.js new file mode 100644 index 000000000..31fc30449 --- /dev/null +++ b/apps/themesetter/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwgP/ACHgDAQWBApfjCoXxAqHwg4FP+PHApY7EApheEAq3+g4FD/EPAofAj4QDgAQECwgQQ8E/Cwg+EAvYAhA==")) \ No newline at end of file diff --git a/apps/themesetter/app-icon.png b/apps/themesetter/app-icon.png new file mode 100644 index 000000000..078920a27 Binary files /dev/null and b/apps/themesetter/app-icon.png differ diff --git a/apps/themesetter/app-screenshot.png b/apps/themesetter/app-screenshot.png new file mode 100644 index 000000000..e7bf5a6a2 Binary files /dev/null and b/apps/themesetter/app-screenshot.png differ diff --git a/apps/themesetter/app.js b/apps/themesetter/app.js new file mode 100644 index 000000000..aa3c608cf --- /dev/null +++ b/apps/themesetter/app.js @@ -0,0 +1,498 @@ + let Layout = require('Layout'); + + let ScreenWidth = g.getWidth(), halfWidth = ScreenWidth/2; + let ScreenHeight = g.getHeight(); + + let normalizedColorSet = { + black:g.toColor(0,0,0), white: g.toColor(1,1,1), + red: g.toColor(1,0,0), yellow: g.toColor(1,1,0), + green:g.toColor(0,1,0), magenta:g.toColor(1,0,1), + blue: g.toColor(0,0,1), cyan: g.toColor(0,1,1) + }; + + let activeTheme = g.theme; // currently active theme + let pendingTheme = Object.assign({},activeTheme); + let chosenDetail = null; // one of 'fg','bg','fg2','bg2','fgH','bgH' + +/**** Label ****/ + + function Label (Text, Options) { + function renderLabel (Details) { + let halfWidth = Details.w/2, xAlignment = Details.halign || 0; + let halfHeight = Details.h/2, yAlignment = Details.valign || 0; + let Padding = Details.pad || 0; + + g.setColor(Details.col || g.theme.fg || '#000000'); + + if (Details.font != null) { g.setFont(Details.font); } + g.setFontAlign(xAlignment,yAlignment); + + let x = Details.x + halfWidth + xAlignment*(halfWidth+Padding); + let y = Details.y + halfHeight + yAlignment*(halfHeight+Padding); + + g.drawString(Details.label, x,y); + if (Details.bold) { + g.drawString(Details.label, x+1,y); + g.drawString(Details.label, x,y+1); + g.drawString(Details.label, x+1,y+1); + } + } + + let Result = Object.assign(( + Options == null ? {} : Object.assign({}, Options.common || {}, Options) + ), { + type:'custom', render:renderLabel, label:Text || '' + }); + let TextMetrics; + if (! Result.width || ! Result.height) { + if (Result.font != null) { g.setFont(Result.font); } + TextMetrics = g.stringMetrics(Result.label); + } + + Result.width = Result.width || TextMetrics.width + 2*(Result.pad || 0); + Result.height = Result.height || TextMetrics.height + 2*(Result.pad || 0); + return Result; + } + + if (g.drawRoundedRect == null) { + g.drawRoundedRect = function drawRoundedRect (x1,y1, x2,y2, r) { + let x,y; + if (x1 > x2) { x = x1; x1 = x2; x2 = x; } + if (y1 > y2) { y = y1; y1 = y2; y2 = y; } + + r = Math.min(r || 0, (x2-x1)/2, (y2-y1)/2); + + let cx1 = x1+r, cx2 = x2-r; + let cy1 = y1+r, cy2 = y2-r; + + this.drawLine(cx1,y1, cx2,y1); + this.drawLine(cx1,y2, cx2,y2); + this.drawLine(x1,cy1, x1,cy2); + this.drawLine(x2,cy1, x2,cy2); + + x = r; y = 0; + + let dx,dy, Error = 0; + while (y <= x) { + dy = 1 + 2*y; y++; Error -= dy; + if (Error < 0) { + dx = 1 - 2*x; x--; Error -= dx; + } + + this.setPixel(cx1 - x, cy1 - y); this.setPixel(cx1 - y, cy1 - x); + this.setPixel(cx2 + x, cy1 - y); this.setPixel(cx2 + y, cy1 - x); + this.setPixel(cx2 + x, cy2 + y); this.setPixel(cx2 + y, cy2 + x); + this.setPixel(cx1 - x, cy2 + y); this.setPixel(cx1 - y, cy2 + x); + } + }; + } + + +/**** Button ****/ + + function Button (Text, Options) { + function renderButton (Details) { + let x = Details.x, Width = Details.w, halfWidth = Width/2; + let y = Details.y, Height = Details.h, halfHeight = Height/2; + let Padding = Details.pad || 0; + + g.setColor(Details.col || g.theme.fg || '#000000'); + + if (Details.font != null) { g.setFont(Details.font); } + g.setFontAlign(0,0); + + g.drawRoundedRect(x+Padding,y+Padding, x+Width-Padding-1,y+Height-Padding-1,8); + g.drawString(Details.label, x+halfWidth,y+halfHeight); + g.drawString(Details.label, x+halfWidth+1,y+halfHeight); + g.drawString(Details.label, x+halfWidth,y+halfHeight+1); + g.drawString(Details.label, x+halfWidth+1,y+halfHeight+1); + } + + let Result = Object.assign(( + Options == null ? {} : Object.assign({}, Options.common || {}, Options) + ), { + type:'custom', render:renderButton, label:Text || 'Tap' + }); + let TextMetrics; + if (! Result.width || ! Result.height) { + if (Options.font != null) { g.setFont(Options.font); } + TextMetrics = g.stringMetrics(Result.label); + } + + Result.width = Result.width || TextMetrics.width + 2*10 + 2*(Result.pad || 0); + Result.height = Result.height || TextMetrics.height + 2*5 + 2*(Result.pad || 0); + return Result; + } + +/**** ColorDemo ****/ + + function ColorDemo (Text, Options) { + function renderDemo (Details) { + let x = Details.x, Width = Details.w, halfWidth = Width/2; + let y = Details.y, Height = Details.h, halfHeight = Height/2; + let Padding = Details.pad || 0; + + if (Details.font != null) { g.setFont(Details.font); } + g.setFontAlign(0,0); + + g.setColor(Details.bg); // do not use "bgCol"! + g.fillRect(x+Padding, y+Padding, x+Width-Padding, y+Height-Padding); + + g.setColor(Details.fg); + g.drawString(Details.label, x+halfWidth,y+halfHeight); + } + + let Result = Object.assign(( + Options == null ? {} : Object.assign({}, Options.common || {}, Options) + ), { + type:'custom', render:renderDemo, label:Text || 'Test' + }); + let TextMetrics; + if (! Result.width || ! Result.height) { + if (Result.font != null) { g.setFont(Result.font); } + TextMetrics = g.stringMetrics(Result.label); + } + + Result.width = Result.width || TextMetrics.width + 2*2 + 2*(Result.pad || 0); + Result.height = Result.height || TextMetrics.height + 2*2 + 2*(Result.pad || 0); + return Result; + } + + +/**** ColorView ****/ + + function ColorView (Color, Options) { + function renderColorView (Details) { + let x = Details.x, Width = Details.w; + let y = Details.y, Height = Details.h; + let Padding = Details.pad || 0; + + g.setColor('#000000'); + g.drawRect(x+Padding,y+Padding, x+Width-Padding-1,y+Height-Padding-1); + + g.setColor(Details.col); + g.fillRect(x+Padding+2, y+Padding+2, x+Width-Padding-3, y+Height-Padding-3); + } + + let Result = Object.assign(( + Options == null ? {} : Object.assign({}, Options.common || {}, Options) + ), { + type:'custom', render:renderColorView, col:Color + }); + Result.width = Math.max(10, Result.width || 10) + 2*(Result.pad || 0); + Result.height = Math.max(10, Result.height || 10) + 2*(Result.pad || 0); + return Result; + } + + +/**** ColorSelectionView ****/ + + function ColorSelectionView (Color, Options) { + function renderColorView (Details) { + let x = Details.x, Width = Details.w; + let y = Details.y, Height = Details.h; + let Padding = Details.pad || 0; + + if (Details.selected) { + g.setColor(Details.selected ? '#FF0000' : '#000000'); + g.fillRect(x+Padding,y+Padding, x+Width-Padding-1,y+Height-Padding-1); + + g.setColor('#FFFFFF'); + g.drawRect(x+Padding+4,y+Padding+4, x+Width-Padding-5,y+Height-Padding-5); + } else { + g.setColor('#000000'); + g.drawRect(x+Padding+3,y+Padding+3, x+Width-Padding-4,y+Height-Padding-4); + } + + g.setColor(Details.col); + g.fillRect(x+Padding+5, y+Padding+5, x+Width-Padding-6, y+Height-Padding-6); + } + + let Result = Object.assign(( + Options == null ? {} : Object.assign({}, Options.common || {}, Options) + ), { + type:'custom', render:renderColorView, col:Color + }); + Result.width = Math.max(10, Result.width || 10) + 2*(Result.pad || 0); + Result.height = Math.max(10, Result.height || 10) + 2*(Result.pad || 0); + return Result; + } + + +/**** EventConsumerAtPoint ****/ + + function EventConsumerAtPoint (HandlerName, x,y) { + let Layout = (activeLayout || {}).l; + if (Layout == null) { return; } + + function ConsumerIn (Control) { + if ( + (x < Control.x) || (x >= Control.x + Control.w) || + (y < Control.y) || (y >= Control.y + Control.h) + ) { return undefined; } + + if (typeof Control[HandlerName] === 'function') { return Control; } + + if (Control.c != null) { + let ControlList = Control.c; + for (let i = 0, l = ControlList.length; i < l; i++) { + let Consumer = ConsumerIn(ControlList[i]); + if (Consumer != null) { return Consumer; } + } + } + + return undefined; + } + + return ConsumerIn(Layout); + } + +/**** dispatchTouchEvent ****/ + + function dispatchTouchEvent () { + function handleTouchEvent (Button, xy) { + let Control = EventConsumerAtPoint('onTouch', xy.x,xy.y); + if (Control != null) { + Control.onTouch(Control, Button, xy); + } + } + Bangle.on('touch',handleTouchEvent); + } + dispatchTouchEvent(); + +/**** dispatchStrokeEvent ****/ + + function dispatchStrokeEvent () { + function handleStrokeEvent (Coordinates) { + let Control = EventConsumerAtPoint('onStroke', Coordinates.xy[0],Coordinates.xy[1]); + if (Control != null) { + Control.onStroke(Control, Coordinates); + } + } + Bangle.on('stroke',handleStrokeEvent); + } + dispatchStrokeEvent(); + + let ScreenSet = {}; + + g.setFont12x20(); // does not seem to be respected in layout! + let leftColumnWidth = Math.max( + g.stringWidth('Normal '), g.stringWidth('Accented '), g.stringWidth('Hilighted ') + ); + + let StdFont = { font:'12x20' }; + let legible = Object.assign({ col:'#000000', bgCol:'#FFFFFF' }, StdFont); + let leftAligned = Object.assign({ halign:-1, valign:0 }, legible); + let MainLabel = Object.assign({ pad:4, width:leftColumnWidth }, leftAligned); + let halfWidthButton = Object.assign({ pad:4, width:halfWidth }, legible); + + ScreenSet['MainScreen'] = new Layout({ + type:'v', c:[ + Label('Current Theme', { common:legible, pad:8, bold:true, filly:1 }), + { type:'h', c:[ + Label('Normal', { common:MainLabel }), + ColorDemo(' Demo ',{ common:StdFont, pad:2, id:'NormalDemo' }), + ] }, + { type:'h', c:[ + Label('Accented', { common:MainLabel }), + ColorDemo(' Demo ',{ common:StdFont, pad:2, id:'AccentedDemo' }), + ] }, + { type:'h', c:[ + Label('Hilighted', { common:MainLabel }), + ColorDemo(' Demo ',{ common:StdFont, pad:2, id:'HilitedDemo' }), + ] }, + { height:4 }, + { type:'h', c:[ + Button('Exit', { common:halfWidthButton, onTouch:() => load() }), + Button('Config', { common:halfWidthButton, onTouch:() => gotoScreen('DetailSelectionScreen') }) + ], filly:1 } + ] + }); + + let LabelWidth = Math.max( + g.stringWidth('Fg '), g.stringWidth('Fg2 '), g.stringWidth('FgH '), + g.stringWidth('Bg '), g.stringWidth('Bg2 '), g.stringWidth('BgH ') + ); + let LabelHeight = g.stringMetrics('FgH').height; + + let DetailLabel = Object.assign({ pad:4, width:LabelWidth }, leftAligned); + let DetailView = { width:30, height:LabelHeight, pad:2 }; + + ScreenSet['DetailSelectionScreen'] = new Layout({ + type:'v', c:[ + Label('Configure Detail', { font:'12x20', pad:8, col:'#000000', bgCol:'#FFFFFF', bold:true, filly:1 }), + { type:'h', c:[ + Label('fg', { common:DetailLabel, onTouch:() => configureDetail('fg') }), + ColorView(0, { common:DetailView, onTouch:() => configureDetail('fg'), id:'fgView' }), + { width:20 }, + Label('bg', { common:DetailLabel, onTouch:() => configureDetail('bg') }), + ColorView(0, { common:DetailView, onTouch:() => configureDetail('bg'), id:'bgView' }), + ] }, + { type:'h', c:[ + Label('fg2', { common:DetailLabel, onTouch:() => configureDetail('fg2') }), + ColorView(0, { common:DetailView, onTouch:() => configureDetail('fg2'), id:'fg2View' }), + { width:20 }, + Label('bg2', { common:DetailLabel, onTouch:() => configureDetail('bg2') }), + ColorView(0, { common:DetailView, onTouch:() => configureDetail('bg2'), id:'bg2View' }), + ] }, + { type:'h', c:[ + Label('fgH', { common:DetailLabel, onTouch:() => configureDetail('fgH') }), + ColorView(0, { common:DetailView, onTouch:() => configureDetail('fgH'), id:'fgHView' }), + { width:20 }, + Label('bgH', { common:DetailLabel, onTouch:() => configureDetail('bgH') }), + ColorView(0, { common:DetailView, onTouch:() => configureDetail('bgH'), id:'bgHView' }), + ] }, + { type:'h', c:[ + Button('Save', { common:halfWidthButton, onTouch:() => { applyChanges(); gotoScreen('MainScreen'); } }), + Button('Cancel', { common:halfWidthButton, onTouch:() => gotoScreen('MainScreen') }) + ], filly:1 }, + ] + }); + + let StdSelectionView = { width:40, height:40, pad:2 }; + + ScreenSet['ColorSelectionScreen'] = new Layout({ + type:'v', c:[ + Label('Choose Color', { font:'12x20', pad:8, col:'#000000', bgCol:'#FFFFFF', bold:true, filly:1 }), + { type:'h', c:[ + ColorSelectionView('#000000',{ common:StdSelectionView, id:'black', + onTouch:() => selectColor(0,0,0) }), + ColorSelectionView('#FF0000',{ common:StdSelectionView, id:'red', + onTouch:() => selectColor(1,0,0) }), + ColorSelectionView('#00FF00',{ common:StdSelectionView, id:'green', + onTouch:() => selectColor(0,1,0) }), + ColorSelectionView('#0000FF',{ common:StdSelectionView, id:'blue', + onTouch:() => selectColor(0,0,1) }), + ] }, + { type:'h', c:[ + ColorSelectionView('#FFFFFF',{ common:StdSelectionView, id:'white', + onTouch:() => selectColor(1,1,1) }), + ColorSelectionView('#FFFF00',{ common:StdSelectionView, id:'yellow', + onTouch:() => selectColor(1,1,0) }), + ColorSelectionView('#FF00FF',{ common:StdSelectionView, id:'magenta', + onTouch:() => selectColor(1,0,1) }), + ColorSelectionView('#00FFFF',{ common:StdSelectionView, id:'cyan', + onTouch:() => selectColor(0,1,1) }), + ] }, + { height:4 }, + { type:'h', c:[ + Button('Back', { common:halfWidthButton, onTouch:() => gotoScreen('DetailSelectionScreen') }), + Button('Preview', { common:halfWidthButton, onTouch:() => gotoScreen('ThemePreviewScreen') }) + ], filly:1 }, + ] + }); + + ScreenSet['ThemePreviewScreen'] = new Layout({ + type:'v', c:[ + Label('Theme Preview', { common:legible, bold:true, filly:1 }), + { type:'h', c:[ + Label('Normal', { common:MainLabel }), + ColorDemo(' Test ',{ common:StdFont, pad:2, id:'NormalTest' }), + ] }, + { type:'h', c:[ + Label('Accented', { common:MainLabel }), + ColorDemo(' Test ',{ common:StdFont, pad:2, id:'AccentedTest' }), + ] }, + { type:'h', c:[ + Label('Hilighted', { common:MainLabel }), + ColorDemo(' Test ',{ common:StdFont, pad:2, id:'HilitedTest' }), + ] }, + { height:4 }, + { type:'h', c:[ + Button('Back', { common:legible, pad:4, onTouch:() => gotoScreen('ColorSelectionScreen') }) + ], filly:1 } + ] + }); + + +/**** applyChanges ****/ + + function applyChanges () { + let pendingBg = pendingTheme.bg; + let R = ((pendingBg >> 11) & 0b11111) / 0b11111; + let G = ((pendingBg >> 5) & 0b111111) / 0b111111; + let B = (pendingBg & 0b11111) / 0b11111; + pendingTheme.dark = (0.2126*R + 0.7152*G + 0.0722*B < 0.5); + + activeTheme = Object.assign(activeTheme,pendingTheme); + + let globalSettings = Object.assign( + require('Storage').readJSON('setting.json', true) || {}, + { theme:activeTheme } + ); + require('Storage').writeJSON('setting.json', globalSettings); + } + +/**** configureDetail ****/ + + function configureDetail (Detail) { + chosenDetail = Detail; + gotoScreen('ColorSelectionScreen'); + } + +/**** updateColorSelection ****/ + + function updateColorSelection () { + let selectedColor = pendingTheme[chosenDetail]; + + for (let Key in normalizedColorSet) { + if (normalizedColorSet.hasOwnProperty(Key)) { + activeLayout[Key].selected = (selectedColor === normalizedColorSet[Key]); + } + } + } + +/**** selectColor ****/ + + function selectColor (R,G,B) { + let selectedColor = g.toColor(R,G,B); + pendingTheme[chosenDetail] = selectedColor; + + updateColorSelection(); + g.clear(); + activeLayout.render(); + } + +/**** gotoScreen ****/ + + let activeLayout; + + function gotoScreen (ScreenName) { + activeLayout = ScreenSet[ScreenName]; + + switch (ScreenName) { + case 'MainScreen': + activeLayout['NormalDemo'].fg = activeTheme.fg; + activeLayout['NormalDemo'].bg = activeTheme.bg; + activeLayout['AccentedDemo'].fg = activeTheme.fg2; + activeLayout['AccentedDemo'].bg = activeTheme.bg2; + activeLayout['HilitedDemo'].fg = activeTheme.fgH; + activeLayout['HilitedDemo'].bg = activeTheme.bgH; + break; + case 'DetailSelectionScreen': + activeLayout['fgView'].col = pendingTheme.fg; + activeLayout['bgView'].col = pendingTheme.bg; + activeLayout['fg2View'].col = pendingTheme.fg2; + activeLayout['bg2View'].col = pendingTheme.bg2; + activeLayout['fgHView'].col = pendingTheme.fgH; + activeLayout['bgHView'].col = pendingTheme.bgH; + break; + case 'ColorSelectionScreen': + updateColorSelection(); + break; + case 'ThemePreviewScreen': + activeLayout['NormalTest'].fg = pendingTheme.fg; + activeLayout['NormalTest'].bg = pendingTheme.bg; + activeLayout['AccentedTest'].fg = pendingTheme.fg2; + activeLayout['AccentedTest'].bg = pendingTheme.bg2; + activeLayout['HilitedTest'].fg = pendingTheme.fgH; + activeLayout['HilitedTest'].bg = pendingTheme.bgH; + } + + g.setColor('#000000'); g.setBgColor('#FFFFFF'); // assert legibility + g.clear(); + + activeLayout.render(); + } + gotoScreen('MainScreen'); + diff --git a/apps/thermom/ChangeLog b/apps/thermom/ChangeLog index 6ab6ba8e5..6d3a966e3 100644 --- a/apps/thermom/ChangeLog +++ b/apps/thermom/ChangeLog @@ -1,2 +1,6 @@ 0.02: New App! 0.03: Improved messages and added Celsius sign +0.04: Make temperature value readable on smaller screens +0.05: Use temperature from current locale + Update every 10s, average last 5 readings + Changes based on #1092 diff --git a/apps/thermom/app.js b/apps/thermom/app.js index 7eae9b3d4..0e45ed3e7 100644 --- a/apps/thermom/app.js +++ b/apps/thermom/app.js @@ -1,13 +1,27 @@ +// history of temperature readings +var history = []; + + +// When we get temperature... function onTemperature(p) { - g.reset(1).clearRect(0,24,g.getWidth(),g.getHeight()); + var rect = Bangle.appRect; + g.reset(1).clearRect(rect.x, rect.y, rect.x2, rect.y2); g.setFont("6x8",2).setFontAlign(0,0); - var x = g.getWidth()/2; - var y = g.getHeight()/2 + 10; + var x = (rect.x+rect.x2)/2; + var y = (rect.y+rect.y2)/2 + 10; g.drawString("Temperature:", x, y - 45); - g.setFontVector(70).setFontAlign(0,0); - g.drawString(p.temperature.toFixed(1) + " °C", x, y); + g.setFontVector(g.getWidth() > 200 ? 70 : 50).setFontAlign(0,0); + + // Average the last 5 temperature readings + while (history.length>4) history.shift(); + history.push(p.temperature); + var avrTemp = E.sum(history) / history.length; + // Draw the temperature + var t = require('locale').temp(avrTemp).replace("'","°"); + g.drawString(t, x, y); } +// Gets the temperature in the most accurate way (pressure sensor or inbuilt thermistor) function drawTemperature() { if (Bangle.getPressure) { Bangle.getPressure().then(onTemperature); @@ -18,11 +32,10 @@ function drawTemperature() { } } - setInterval(function() { drawTemperature(); -}, 20000); -drawTemperature(); +}, 10000); E.showMessage("Reading temperature..."); +drawTemperature(); Bangle.loadWidgets(); Bangle.drawWidgets(); diff --git a/apps/thermom/screenshot.png b/apps/thermom/screenshot.png new file mode 100644 index 000000000..a12bbef1c Binary files /dev/null and b/apps/thermom/screenshot.png differ diff --git a/apps/tinydraw/ChangeLog b/apps/tinydraw/ChangeLog new file mode 100644 index 000000000..af7f83942 --- /dev/null +++ b/apps/tinydraw/ChangeLog @@ -0,0 +1 @@ +0.01: Initial release diff --git a/apps/tinydraw/README.md b/apps/tinydraw/README.md new file mode 100644 index 000000000..a4acd9a72 --- /dev/null +++ b/apps/tinydraw/README.md @@ -0,0 +1,14 @@ +TinyDraw +======== + +This is a simple drawing application to make sketches +using different brushes and colors for your BangleJS2 watch! + +* Brush types: dot, brush, circle, square + +It is my first BangleJS application, I plan +to continue improving this app over time, but +if you want to contribute or provide feedback +don't hesitate to contact me! + +--pancake diff --git a/apps/tinydraw/app-icon.js b/apps/tinydraw/app-icon.js new file mode 100644 index 000000000..1f0eaae27 --- /dev/null +++ b/apps/tinydraw/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwhC/ACEF7vd6oXTroXB7tQC6QWC7vQC6Xf//9C6n4xGPC4VM5nMoAXNxH/xH96EECwPM4gXNx///AXBhgXC5gXRqAXOt3u91gC4S/BC5sGCwPu8wXCuAXOhwXC9wXcR5oXJX5oXHxGIuF3DYQXRDIWHC5SPH/7yBAIN3u6/QC4JME+AXIg5WCC44CBJRN4KwQXHAgOAC5BWCC6gOCC6xUBC6cIUIQbCAAwXJvAMBQ4QXRg6fEC6SQBLQQXRJAYAJC5UIFpIXMMAQXUVIYXVGBQXLh4XKw4XKgCpCAA34F5apK/FwC5ZIJxAWLSJP4LxhhJxBGMMIeIJQX4xH3Cxz0CxAACu4WQAH4A/AAwA==")) diff --git a/apps/tinydraw/app.js b/apps/tinydraw/app.js new file mode 100644 index 000000000..e4c612219 --- /dev/null +++ b/apps/tinydraw/app.js @@ -0,0 +1,159 @@ +(function () { + var pen = 'circle'; + var discard = null; + var kule = [255, 255, 255]; + var oldLock = false; + + setInterval(() => { + if (Bangle.isLocked()) { + if (oldLock) { + return; + } + g.setColor('#fff'); + g.fillRect(0, 0, g.getWidth(), 20); + g.setFont('6x8', 2); + g.setColor('#000'); + g.drawString('PLEASE UNLOCK', 10, 2); + oldLock = true; + } else { + oldLock = false; + drawUtil(); + } + }, 1000); + + function nextColor () { + kule[0] = Math.random(); + kule[1] = Math.random(); + kule[2] = Math.random(); + } + + function nextPen () { + switch (pen) { + case 'circle': pen = 'pixel'; break; + case 'pixel': pen = 'crayon'; break; + case 'crayon': pen = 'square'; break; + case 'square': pen = 'circle'; break; + default: pen = 'pixel'; break; + } + console.log('set time'); + drawUtil(); + + discard = setTimeout(function () { console.log('timeout'); discard = null; }, 500); + } + + function drawUtil () { + if (Bangle.isLocked()) { + // do something to tell the user to unlock the screen + } + // titlebar + g.setColor(kule[0], kule[1], kule[2]); + g.fillRect(0, 0, g.getWidth(), 20); + // clear button + g.setColor('#000'); // black + g.fillCircle(10, 10, 8, 8); + g.setColor('#fff'); + g.drawLine(8, 8, 13, 13); + g.drawLine(13, 8, 8, 13); + // tool button + g.setColor('#fff'); + g.fillCircle(g.getWidth() - 10, 10, 8); + g.setColor('#000'); + + var w = g.getWidth(); + switch (pen) { + case 'circle': + g.fillCircle(w - 10, 10, 5); + break; + case 'square': + g.fillRect(w - 5, 5, w - 15, 15); + break; + case 'pixel': + g.setPixel(10, 10); + g.fillCircle(w - 10, 10, 2); + break; + case 'crayon': + var tap = { x: 10, y: 15, dy: -5, dx: 5 }; + g.drawLine(w - tap.x, tap.y, w - tap.x + tap.dx, tap.y + tap.dy); + g.drawLine(w - tap.x + 1, tap.y + 2, w - tap.x + tap.dx, tap.y + tap.dy - 2); + g.drawLine(w - tap.x + 2, tap.y + 2, w - tap.x + tap.dx, tap.y + tap.dy + 2); + break; + } + } + var tapTimer = null; + Bangle.on('drag', function (tap) { + if (tap.b === 0) { + if (tapTimer !== null) { + clearTimeout(tapTimer); + tapTimer = null; + } + } + // tap and hold the clear button + if (tap.x < 32 && tap.y < 32) { + if (tap.b === 1) { + if (tapTimer === null) { + tapTimer = setTimeout(function () { + g.clear(); + drawUtil(); + tapTimer = null; + }, 800); + } + if (discard) { + clearTimeout(discard); discard = null; + return; + } + } + return; + } + if (tap.x > g.getWidth() - 32 && tap.y < 32) { + if (tap.b === 1) { + if (tapTimer === null) { + tapTimer = setTimeout(function () { + g.clear(); + drawUtil(); + tapTimer = null; + }, 800); + } + if (discard) { + clearTimeout(discard); + discard = null; + return; + } + nextPen(); + } + drawUtil(); + return; + } else if (tap.y < 32) { + nextColor(); + drawUtil(); + return; + } + + g.setColor(kule[0], kule[1], kule[2]); + + switch (pen) { + case 'pixel': + g.setPixel(tap.x, tap.y); + g.drawLine(tap.x, tap.y, tap.x + tap.dx, tap.y + tap.dy); + break; + case 'crayon': + g.drawLine(tap.x, tap.y, tap.x + tap.dx, tap.y + tap.dy); + g.drawLine(tap.x + 1, tap.y + 2, tap.x + tap.dx, tap.y + tap.dy - 2); + g.drawLine(tap.x + 2, tap.y + 2, tap.x + tap.dx, tap.y + tap.dy + 2); + break; + case 'circle': + var XS = tap.dx / 10; + var YS = tap.dy / 10; + for (i = 0; i < 10; i++) { + g.fillCircle(tap.x + (i * XS), tap.y + (i * YS), 4, 4); + } + break; + case 'square': + g.fillRect(tap.x - 10, tap.y - 10, tap.x + 10, tap.y + 10); + break; + } + drawUtil(); + }); + + g.clear(); + drawUtil(); +})(); diff --git a/apps/tinydraw/app.png b/apps/tinydraw/app.png new file mode 100644 index 000000000..01eda0a60 Binary files /dev/null and b/apps/tinydraw/app.png differ diff --git a/apps/tinydraw/screenshot.png b/apps/tinydraw/screenshot.png new file mode 100644 index 000000000..27d5cc56c Binary files /dev/null and b/apps/tinydraw/screenshot.png differ diff --git a/apps/toucher/app.js b/apps/toucher/app.js index 8ac198f52..aab50fbda 100644 --- a/apps/toucher/app.js +++ b/apps/toucher/app.js @@ -293,9 +293,9 @@ Bangle.on('swipe', dir => { else next(); }); -// close launcher when lcd is off -Bangle.on('lcdPower', on => { - if(!on) return load(); +// close launcher when screen is locked +Bangle.on('lock', on => { + if(on) return load(); }); if (process.env.HWVERSION == 1) { diff --git a/apps/touchtimer/0_dark_timer_edit.png b/apps/touchtimer/0_dark_timer_edit.png new file mode 100644 index 000000000..2160ef38d Binary files /dev/null and b/apps/touchtimer/0_dark_timer_edit.png differ diff --git a/apps/touchtimer/0_light_timer_edit.png b/apps/touchtimer/0_light_timer_edit.png new file mode 100644 index 000000000..361223af9 Binary files /dev/null and b/apps/touchtimer/0_light_timer_edit.png differ diff --git a/apps/touchtimer/1_dark_timer_ready.png b/apps/touchtimer/1_dark_timer_ready.png new file mode 100644 index 000000000..236aef7f9 Binary files /dev/null and b/apps/touchtimer/1_dark_timer_ready.png differ diff --git a/apps/touchtimer/1_light_timer_ready.png b/apps/touchtimer/1_light_timer_ready.png new file mode 100644 index 000000000..5e2ca3c9d Binary files /dev/null and b/apps/touchtimer/1_light_timer_ready.png differ diff --git a/apps/touchtimer/2_dark_timer_running.png b/apps/touchtimer/2_dark_timer_running.png new file mode 100644 index 000000000..4de00cc61 Binary files /dev/null and b/apps/touchtimer/2_dark_timer_running.png differ diff --git a/apps/touchtimer/2_light_timer_running.png b/apps/touchtimer/2_light_timer_running.png new file mode 100644 index 000000000..2a0d314f4 Binary files /dev/null and b/apps/touchtimer/2_light_timer_running.png differ diff --git a/apps/touchtimer/3_dark_timer_finished.png b/apps/touchtimer/3_dark_timer_finished.png new file mode 100644 index 000000000..c6dd77b82 Binary files /dev/null and b/apps/touchtimer/3_dark_timer_finished.png differ diff --git a/apps/touchtimer/3_light_timer_finished.png b/apps/touchtimer/3_light_timer_finished.png new file mode 100644 index 000000000..18ee015e3 Binary files /dev/null and b/apps/touchtimer/3_light_timer_finished.png differ diff --git a/apps/touchtimer/ChangeLog b/apps/touchtimer/ChangeLog new file mode 100644 index 000000000..01904c6ea --- /dev/null +++ b/apps/touchtimer/ChangeLog @@ -0,0 +1,2 @@ +0.01: Initial creation of the touch timer app +0.02: Add settings menu \ No newline at end of file diff --git a/apps/touchtimer/README.md b/apps/touchtimer/README.md new file mode 100644 index 000000000..39afba8e5 --- /dev/null +++ b/apps/touchtimer/README.md @@ -0,0 +1,29 @@ +# Touch Timer + +Quickly and easily create a timer with touch-only input. The time can be easily set with a number pad. + +## How to + +- First input the timer time via the input buttons +- If you need to correct the time, press "<-". +- If the timer time is correct, press "OK". +- If you have accidentially pressed "OK", press "STOP" to go cancel. +- Press "START" to start the timer, if the time is correct. +- The timer will run the time until 0. Once it hits zero the watch will buzz for 1 second every 1 seconds for a total of 3 times, or until you press "STOP" +- -> The number of buzzes, the buzz duration, and the pause between buzzes is configurable in the settings app + +## Screenshots + +### Light Theme + +![](0_light_timer_edit.png) +![](1_light_timer_ready.png) +![](2_light_timer_running.png) +![](3_light_timer_finished.png) + +### Dark Theme + +![](0_dark_timer_edit.png) +![](1_dark_timer_ready.png) +![](2_dark_timer_running.png) +![](3_dark_timer_finished.png) \ No newline at end of file diff --git a/apps/touchtimer/app-icon.js b/apps/touchtimer/app-icon.js new file mode 100644 index 000000000..d58446bcc --- /dev/null +++ b/apps/touchtimer/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwkE/4A3mUQIAMRkYWQkBaFiQWQgMjn8zGYUDCxkxFA3zD4MfCxXygECMAURiReCDAM/IpUBFIJ2CAAIeB+ZJKBYI8BCwMBiABBDARSBC5EwFwMwEwUwh5FCEIJhJiEfGIIXC+IQBSwQeBNYR1Gn4xB+MDDYITBiEzFoIOCC4vwEAIxBAwQzBAoQtCBgaNEh4iEAwMwRQXxHgRnBLwsvFQJdCFoIGBl55DH4QAEEIK/BC4KjBC4RECiED+RnBXooxCn4uBKwPwgIiB+fxgQQCRwgeBLwRbBkAXBh5yCBwoACEAoVBC4fwJ4I+DC5EjJQQXDBYP/kJWDC4qmBBYYXFfIQXKiQvUL6AXGR5LzBR4YXIBAS/BC4UCeAQOFC4rvDN4LvCFYMgd4IXJmEABgMxC4bWBiADDC45EBZIRHBMYINCBQQXIIgIkB//wgIFDmBKBC5QNB+UDboU/kEzgCRBC5QTBNwUxLoZRDC5J5EmAqBkEAiYMCC5XzFIMRkECAgILDC5YYDAAUBIoQXNDAMhiMRkYJEC5oAKC7qKBACDfCK4IWRPwjqBkczAB0yGAcQGgYAOmByCfAYAP+MBC4QWR//yC4ciACMhC4YATC4T9BACUSLiQAdA=")) \ No newline at end of file diff --git a/apps/touchtimer/app.js b/apps/touchtimer/app.js new file mode 100644 index 000000000..ffa1af80a --- /dev/null +++ b/apps/touchtimer/app.js @@ -0,0 +1,456 @@ +var DEBUG = false; +var FILE = "touchtimer.data.json"; + +var main = () => { + var settings = readSettings(); + + var button1 = new Button({ x1: 1, y1: 35, x2: 58, y2: 70 }, 1); + var button2 = new Button({ x1: 60, y1: 35, x2: 116, y2: 70 }, 2); + var button3 = new Button({ x1: 118, y1: 35, x2: 174, y2: 70 }, 3); + + var button4 = new Button({ x1: 1, y1: 72, x2: 58, y2: 105 }, 4); + var button5 = new Button({ x1: 60, y1: 72, x2: 116, y2: 105 }, 5); + var button6 = new Button({ x1: 118, y1: 72, x2: 174, y2: 105 }, 6); + + var button7 = new Button({ x1: 1, y1: 107, x2: 58, y2: 140 }, 7); + var button8 = new Button({ x1: 60, y1: 107, x2: 116, y2: 140 }, 8); + var button9 = new Button({ x1: 118, y1: 107, x2: 174, y2: 140 }, 9); + + var buttonOK = new Button({ x1: 1, y1: 142, x2: 58, y2: 174 }, "OK"); + var button0 = new Button({ x1: 60, y1: 142, x2: 116, y2: 174 }, 0); + var buttonDelete = new Button({ x1: 118, y1: 142, x2: 174, y2: 174 }, "<-"); + + var timerNumberButtons = [ + button1, + button2, + button3, + button4, + button5, + button6, + button7, + button8, + button9, + button0, + ]; + + var timerInputButtons = [ + button1, + button2, + button3, + button4, + button5, + button6, + button7, + button8, + button9, + buttonOK, + button0, + buttonDelete, + ]; + + var buttonStartPause = new Button( + { x1: 1, y1: 35, x2: 174, y2: 105 }, + "START" + ); + var buttonStop = new Button({ x1: 1, y1: 107, x2: 174, y2: 174 }, "STOP"); + + var timerRunningButtons = [buttonStartPause, buttonStop]; + + var timerEdit = new TimerEdit(); + timerNumberButtons.forEach((numberButton) => { + numberButton.setOnClick((number) => { + log("number button clicked"); + log(number); + timerEdit.appendNumber(number); + timerEdit.draw(); + }); + }); + + buttonDelete.setOnClick(() => { + log("delete button clicked"); + timerEdit.removeNumber(); + timerEdit.draw(); + }); + + buttonOK.setOnClick(() => { + if (timerEdit.timeStr.length === 0) { + return; + } + + g.clear(); + timerEdit.draw(); + + timerInputButtons.forEach((button) => button.disable()); + + timerRunningButtons.forEach((button) => { + button.enable(); + button.draw(); + }); + }); + + var timerIntervalId = undefined; + var buzzIntervalId = undefined; + var timerCountDown = undefined; + buttonStartPause.setOnClick(() => { + if (buttonStartPause.value === "PAUSE") { + if (timerCountDown) { + timerCountDown.pause(); + } + + buttonStartPause.value = "START"; + buttonStartPause.draw(); + + if (timerIntervalId) { + clearInterval(timerIntervalId); + timerIntervalId = undefined; + } + + if (buzzIntervalId) { + clearInterval(buzzIntervalId); + buzzIntervalId = undefined; + } + + return; + } + + if (buttonStartPause.value === "START") { + if (!timerCountDown) { + timerCountDown = new TimerCountDown(timerEdit.timeStr); + } else { + timerCountDown.unpause(); + } + + buttonStartPause.value = "PAUSE"; + buttonStartPause.draw(); + + timerIntervalId = setInterval(() => { + timerCountDown.draw(); + + if (timerCountDown.isFinished()) { + buttonStartPause.value = "FINISHED!"; + buttonStartPause.draw(); + + if (timerIntervalId) { + clearInterval(timerIntervalId); + timerIntervalId = undefined; + } + + var buzzCount = 1; + Bangle.buzz(settings.buzzDuration * 1000, 1); + buzzIntervalId = setInterval(() => { + if (buzzCount >= settings.buzzCount) { + clearInterval(buzzIntervalId); + buzzIntervalId = undefined; + return; + } else { + Bangle.buzz(settings.buzzDuration * 1000, 1); + buzzCount++; + } + }, settings.buzzDuration * 1000 + settings.pauseBetween * 1000); + } + }, 1000); + + return; + } + }); + + buttonStop.setOnClick(() => { + if (timerCountDown) { + timerCountDown = undefined; + } + + if (timerIntervalId) { + clearInterval(timerIntervalId); + timerIntervalId = undefined; + } + + if (buzzIntervalId) { + clearInterval(buzzIntervalId); + buzzIntervalId = undefined; + } + + buttonStartPause.value = "START"; + buttonStartPause.draw(); + + g.clear(); + timerEdit.reset(); + timerEdit.draw(); + + timerRunningButtons.forEach((button) => button.disable()); + + timerInputButtons.forEach((button) => { + button.enable(); + button.draw(); + }); + }); + + // initalize + g.clear(); + timerEdit.draw(); + timerInputButtons.forEach((button) => { + button.enable(); + button.draw(); + }); +}; + +// lib functions + +var log = (message) => { + if (DEBUG) { + console.log(JSON.stringify(message)); + } +}; + +var touchHandlers = []; + +Bangle.on("touch", (_button, xy) => { + log("touch"); + log(xy); + + var x = Math.min(Math.max(xy.x, 1), 174); + var y = Math.min(Math.max(xy.y, 1), 174); + + touchHandlers.forEach((touchHandler) => { + touchHandler(x, y); + }); +}); + +var BUTTON_BORDER_WITH = 2; + +class Button { + constructor(position, value) { + this.position = position; + this.value = value; + + this.touchHandler = undefined; + this.highlightTimeoutId = undefined; + } + + draw(highlight) { + g.setColor(g.theme.fg); + g.fillRect( + this.position.x1, + this.position.y1, + this.position.x2, + this.position.y2 + ); + + if (highlight) { + g.setColor(g.theme.bgH); + } else { + g.setColor(g.theme.bg); + } + g.fillRect( + this.position.x1 + BUTTON_BORDER_WITH, + this.position.y1 + BUTTON_BORDER_WITH, + this.position.x2 - BUTTON_BORDER_WITH, + this.position.y2 - BUTTON_BORDER_WITH + ); + + g.setColor(g.theme.fg); + g.setFontAlign(0, 0); + g.setFont("Vector", 35); + g.drawString( + this.value, + this.position.x1 + (this.position.x2 - this.position.x1) / 2 + 2, + this.position.y1 + (this.position.y2 - this.position.y1) / 2 + 2 + ); + } + + setOnClick(callback) { + this.touchHandler = (x, y) => { + if ( + x >= this.position.x1 && + x <= this.position.x2 && + y >= this.position.y1 && + y <= this.position.y2 + ) { + this.draw(true); + this.highlightTimeoutId = setTimeout(() => { + this.draw(); + this.highlightTimeoutId = undefined; + }, 100); + setTimeout(() => callback(this.value), 25); + } + }; + } + + disable() { + log("disable button"); + log(this.value); + var touchHandlerIndex = touchHandlers.indexOf(this.touchHandler); + if (touchHandlerIndex > -1) { + log("clearing touch handler"); + touchHandlers.splice(touchHandlerIndex, 1); + } + + if (this.highlightTimeoutId) { + log("clearing higlight timeout"); + clearTimeout(this.highlightTimeoutId); + this.highlightTimeoutId = undefined; + } + } + + enable() { + if (this.touchHandler) { + touchHandlers.push(this.touchHandler); + } + } +} + +class TimerEdit { + constructor() { + this.timeStr = ""; + } + + appendNumber(number) { + if (number === 0 && this.timeStr.length === 0) { + return; + } + + if (this.timeStr.length <= 6) { + this.timeStr = this.timeStr + number; + } + } + + removeNumber() { + if (this.timeStr.length > 0) { + this.timeStr = this.timeStr.slice(0, -1); + } + } + + reset() { + this.timeStr = ""; + } + + draw() { + log("drawing timer edit"); + var timeStrPadded = this.timeStr.padStart(6, "0"); + var timeStrDisplay = + "" + + timeStrPadded.slice(0, 2) + + "h " + + timeStrPadded.slice(2, 4) + + "m " + + timeStrPadded.slice(4, 6) + + "s"; + log(timeStrPadded); + log(timeStrDisplay); + + g.clearRect(0, 0, 176, 34); + g.setColor(g.theme.fg); + g.setFontAlign(-1, -1); + g.setFont("Vector:26x40"); + g.drawString(timeStrDisplay, 2, 0); + } +} + +class TimerCountDown { + constructor(timeStr) { + log("creating timer"); + this.timeStr = timeStr; + log(this.timeStr); + this.start = Math.floor(Date.now() / 1000); + log(this.start); + this.pausedTime = undefined; + } + + getAdjustedTime() { + var elapsedTime = Math.floor(Date.now() / 1000) - this.start; + + var timeStrPadded = this.timeStr.padStart(6, "0"); + var timeStrHours = parseInt(timeStrPadded.slice(0, 2), 10); + var timeStrMinutes = parseInt(timeStrPadded.slice(2, 4), 10); + var timeStrSeconds = parseInt(timeStrPadded.slice(4, 6), 10); + + var hours = timeStrHours; + var minutes = timeStrMinutes; + var seconds = timeStrSeconds - elapsedTime; + + if (seconds < 0) { + var neededMinutes = Math.ceil(Math.abs(seconds) / 60); + + seconds = seconds + neededMinutes * 60; + minutes = minutes - neededMinutes; + + if (minutes < 0) { + var neededHours = Math.ceil(Math.abs(minutes) / 60); + + minutes = minutes + neededHours * 60; + hours = hours - neededHours; + } + } + + if (hours < 0 || minutes < 0 || seconds < 0) { + hours = 0; + minutes = 0; + seconds = 0; + } + + return { hours: hours, minutes: minutes, seconds: seconds }; + } + + pause() { + this.pausedTime = Math.floor(Date.now() / 1000); + } + + unpause() { + if (this.pausedTime) { + this.start += Math.floor(Date.now() / 1000) - this.pausedTime; + } + + this.pausedTime = undefined; + } + + draw() { + log("drawing timer count down"); + var adjustedTime = this.getAdjustedTime(); + var hours = adjustedTime.hours; + var minutes = adjustedTime.minutes; + var seconds = adjustedTime.seconds; + + var timeStrDisplay = + "" + + hours.toString().padStart(2, "0") + + "h " + + minutes.toString().padStart(2, "0") + + "m " + + seconds.toString().padStart(2, "0") + + "s"; + log(timeStrDisplay); + + g.clearRect(0, 0, 176, 34); + g.setColor(g.theme.fg); + g.setFontAlign(-1, -1); + g.setFont("Vector:26x40"); + g.drawString(timeStrDisplay, 2, 0); + } + + isFinished() { + var adjustedTime = this.getAdjustedTime(); + var hours = adjustedTime.hours; + var minutes = adjustedTime.minutes; + var seconds = adjustedTime.seconds; + + if (hours <= 0 && minutes <= 0 && seconds <= 0) { + return true; + } else { + return false; + } + } +} + +var readSettings = () => { + log("reading settings"); + var settings = require("Storage").readJSON(FILE, 1) || { + buzzCount: 3, + buzzDuration: 1, + pauseBetween: 1, + }; + log(settings); + return settings; +}; + +// start main function + +main(); diff --git a/apps/touchtimer/app.png b/apps/touchtimer/app.png new file mode 100644 index 000000000..8ccdb17f0 Binary files /dev/null and b/apps/touchtimer/app.png differ diff --git a/apps/touchtimer/settings.js b/apps/touchtimer/settings.js new file mode 100644 index 000000000..885670f57 --- /dev/null +++ b/apps/touchtimer/settings.js @@ -0,0 +1,77 @@ +(function (back) { + var DEBUG = false; + var FILE = "touchtimer.data.json"; + + var settings = {}; + + var showMainMenu = () => { + log("Loading main menu"); + + E.showMenu({ + "": { title: "Touch Timer" }, + "< Back": () => back(), + "Buzz Count": { + value: settings.buzzCount, + min: 1, + max: 3, + step: 1, + onchange: (value) => { + settings.buzzCount = value; + writeSettings(settings); + }, + }, + "Buzz Duration": { + value: settings.buzzDuration, + min: 1, + max: 10, + step: 0.5, + format: (value) => value + "s", + onchange: (value) => { + settings.buzzDuration = value; + writeSettings(settings); + }, + }, + "Pause Between": { + value: settings.pauseBetween, + min: 1, + max: 5, + step: 1, + format: (value) => value + "s", + onchange: (value) => { + settings.pauseBetween = value; + writeSettings(settings); + }, + }, + }); + }; + + // lib functions + + var log = (message) => { + if (DEBUG) { + console.log(JSON.stringify(message)); + } + }; + + var readSettings = () => { + log("reading settings"); + var settings = require("Storage").readJSON(FILE, 1) || { + buzzCount: 3, + buzzDuration: 1, + pauseBetween: 1, + }; + log(settings); + return settings; + }; + + var writeSettings = (settings) => { + log("writing settings"); + log(settings); + require("Storage").writeJSON(FILE, settings); + }; + + // start main function + + settings = readSettings(); + showMainMenu(); +}); diff --git a/apps/vectorclock/ChangeLog b/apps/vectorclock/ChangeLog new file mode 100644 index 000000000..8addc7170 --- /dev/null +++ b/apps/vectorclock/ChangeLog @@ -0,0 +1,3 @@ +0.01: New watch face +0.02: Use Bangle.setUI for button/launcher handling +0.03: Bangle.js 2 support diff --git a/apps/vectorclock/Changelog b/apps/vectorclock/Changelog deleted file mode 100644 index 43190331b..000000000 --- a/apps/vectorclock/Changelog +++ /dev/null @@ -1,2 +0,0 @@ -0.1: New watch face -0.2: Use Bangle.setUI for button/launcher handling diff --git a/apps/vectorclock/app.js b/apps/vectorclock/app.js index a98c9f97b..78c17b3d4 100644 --- a/apps/vectorclock/app.js +++ b/apps/vectorclock/app.js @@ -5,9 +5,10 @@ function padNum(n, l) { return ("0".repeat(l)+n).substr(-l); } -let rects = {}; -let rectsToClear = {}; -let commands = []; +var rects = {}; +var rectsToClear = {}; +var commands = []; +var showSeconds = true; function pushCommand(command) { let hash = E.CRC32(E.toJS(arguments)); @@ -20,17 +21,20 @@ function executeCommands() { "ram"; for (let hash in rectsToClear) delete rects[hash]; for (let r of rectsToClear) if (r) g.clearRect(r.x1, r.y1, r.x2, r.y2); - g.getModified(true); - for (let c of commands) { - c.command(); - rects[c.hash] = g.getModified(true); - } + for (let c of commands) rects[c.hash] = c.command(); rectsToClear = Object.assign({}, rects); commands = []; } function drawVectorText(text, size, x, y, alignX, alignY) { g.setFont("Vector", size).setFontAlign(alignX, alignY).drawString(text, x, y); + var m = g.stringMetrics(text); + return { + x1: x - m.width * (alignX / 2 + 0.5), + y1: y - m.height * (alignY / 2 + 0.5), + x2: x - m.width * (alignX / 2 - 0.5), + y2: y - m.height * (alignY / 2 - 0.5) + }; } function draw() { @@ -43,11 +47,12 @@ function draw() { let secondsText = padNum(d.getSeconds(), 2); let dowText = locale.dow(d); let dateText = locale.date(d, true); + let width = g.getWidth() - 2; g.setFont("Vector", 256); - let timeFontSize = g.getWidth() / ((g.stringWidth(timeText) / 256) + (Math.max(g.stringWidth(meridian), g.stringWidth(secondsText)) / 512 * 9 / 10)); - let dowFontSize = g.getWidth() / (g.stringWidth(dowText) / 256); - let dateFontSize = g.getWidth() / (g.stringWidth(dateText) / 256); + let timeFontSize = width / ((g.stringWidth(timeText) / 256) + (Math.max(g.stringWidth(meridian), g.stringWidth(secondsText)) / 512 * 9 / 10)); + let dowFontSize = width / (g.stringWidth(dowText) / 256); + let dateFontSize = width / (g.stringWidth(dateText) / 256); let timeHeight = g.setFont("Vector", timeFontSize).getFontHeight() * 9 / 10; let dowHeight = g.setFont("Vector", dowFontSize).getFontHeight(); @@ -56,26 +61,28 @@ function draw() { let remainingHeight = g.getHeight() - 24 - timeHeight - dowHeight - dateHeight; let spacer = remainingHeight / 4; + let x = 2; let y = 24 + spacer; - pushCommand(drawVectorText, timeText, timeFontSize, 0, y, -1, -1); - pushCommand(drawVectorText, meridian, timeFontSize*9/20, g.getWidth(), y, 1, -1); - pushCommand(drawVectorText, secondsText, timeFontSize*9/20, g.getWidth(), y + timeHeight, 1, 1); + pushCommand(drawVectorText, timeText, timeFontSize, x, y, -1, -1); + pushCommand(drawVectorText, meridian, timeFontSize*9/20, x + width, y, 1, -1); + if (showSeconds) pushCommand(drawVectorText, secondsText, timeFontSize*9/20, x + width, y + timeHeight, 1, 1); y += timeHeight + spacer; - pushCommand(drawVectorText, dowText, dowFontSize, g.getWidth()/2, y, 0, -1); + pushCommand(drawVectorText, dowText, dowFontSize, x + width/2, y, 0, -1); y += dowHeight + spacer; - pushCommand(drawVectorText, dateText, dateFontSize, g.getWidth()/2, y, 0, -1); + pushCommand(drawVectorText, dateText, dateFontSize, x + width/2, y, 0, -1); executeCommands(); } -let timeout; +var timeout; function tick() { draw(); - timeout = setTimeout(tick, 1000 - getTime() % 1 * 1000); + var period = showSeconds ? 1000 : 60 * 1000; + timeout = setTimeout(tick, period - getTime() * 1000 % period); } Bangle.on('lcdPower', function(on) { @@ -84,6 +91,13 @@ Bangle.on('lcdPower', function(on) { if (on) tick(); }); +Bangle.on('lock', function(locked) { + if (timeout) clearTimeout(timeout); + timeout = null; + showSeconds = !locked; + tick(); +}); + g.clear(); tick(); Bangle.loadWidgets(); diff --git a/apps/vectorclock/bangle2-vector-clock-screenshot.png b/apps/vectorclock/bangle2-vector-clock-screenshot.png new file mode 100644 index 000000000..30d40c864 Binary files /dev/null and b/apps/vectorclock/bangle2-vector-clock-screenshot.png differ diff --git a/apps/weather/ChangeLog b/apps/weather/ChangeLog index 95f943e6d..910cd4658 100644 --- a/apps/weather/ChangeLog +++ b/apps/weather/ChangeLog @@ -9,3 +9,5 @@ 0.10: Use new Layout library 0.11: Bangle.js 2 support 0.12: Allow hiding the widget +0.13: Tweak Bangle.js 2 light theme colors +0.14: Use weather condition code for icon selection diff --git a/apps/weather/app.js b/apps/weather/app.js index 8c8526fbd..efd9b0209 100644 --- a/apps/weather/app.js +++ b/apps/weather/app.js @@ -9,7 +9,7 @@ var layout = new Layout({type:"v", bgCol: g.theme.bg, c: [ {filly: 1}, {type: "h", filly: 0, c: [ {type: "custom", width: g.getWidth()/2, height: g.getWidth()/2, valign: -1, txt: "unknown", id: "icon", - render: l => weather.drawIcon(l.txt, l.x+l.w/2, l.y+l.h/2, l.w/2-5)}, + render: l => weather.drawIcon(l, l.x+l.w/2, l.y+l.h/2, l.w/2-5)}, {type: "v", fillx: 1, c: [ {type: "h", pad: 2, c: [ {type: "txt", font: "18%", id: "temp", label: "000"}, @@ -47,6 +47,7 @@ function formatDuration(millis) { function draw() { layout.icon.txt = current.txt; + layout.icon.code = current.code; const temp = locale.temp(current.temp-273.15).match(/^(\D*\d*)(.*)$/); layout.temp.label = temp[1]; layout.tempUnit.label = temp[2]; diff --git a/apps/weather/lib.js b/apps/weather/lib.js index 76ed2aaa4..8afdfe6df 100644 --- a/apps/weather/lib.js +++ b/apps/weather/lib.js @@ -16,7 +16,7 @@ function scheduleExpiry(json) { function update(weatherEvent) { let json = storage.readJSON('weather.json')||{}; - + if (weatherEvent) { let weather = weatherEvent.clone(); delete weather.t; @@ -54,14 +54,62 @@ exports.get = function() { scheduleExpiry(storage.readJSON('weather.json')||{}); exports.drawIcon = function(cond, x, y, r) { + var palette; + + if (B2) { + if (g.theme.dark) { + palette = { + sun: '#FF0', + cloud: '#FFF', + bgCloud: '#777', // dithers on B2, but that's ok + rain: '#0FF', + lightning: '#FF0', + snow: '#FFF', + mist: '#FFF' + }; + } else { + palette = { + sun: '#FF0', + cloud: '#777', // dithers on B2, but that's ok + bgCloud: '#000', + rain: '#00F', + lightning: '#FF0', + snow: '#0FF', + mist: '#0FF' + }; + } + } else { + if (g.theme.dark) { + palette = { + sun: '#FE0', + cloud: '#BBB', + bgCloud: '#777', + rain: '#0CF', + lightning: '#FE0', + snow: '#FFF', + mist: '#FFF' + }; + } else { + palette = { + sun: '#FC0', + cloud: '#000', + bgCloud: '#777', + rain: '#07F', + lightning: '#FC0', + snow: '#CCC', + mist: '#CCC' + }; + } + } + function drawSun(x, y, r) { - g.setColor(B2 ? '#FF0' : (g.theme.dark ? "#FE0" : "#FC0")); + g.setColor(palette.sun); g.fillCircle(x, y, r); } function drawCloud(x, y, r, c) { const u = r/12; - if (c==null) c = B2 ? '#FFF': (g.theme.dark ? "#BBB" : "#AAA"); + if (c==null) c = palette.cloud; g.setColor(c); g.fillCircle(x-8*u, y+3*u, 4*u); g.fillCircle(x-4*u, y-2*u, 5*u); @@ -78,7 +126,7 @@ exports.drawIcon = function(cond, x, y, r) { } function drawBrokenClouds(x, y, r) { - drawCloud(x+1/8*r, y-1/8*r, 7/8*r, "#777"); // dithers on B2, but that's ok + drawCloud(x+1/8*r, y-1/8*r, 7/8*r, palette.bgCloud); drawCloud(x-1/8*r, y+1/8*r, 7/8*r); } @@ -88,7 +136,7 @@ exports.drawIcon = function(cond, x, y, r) { } function drawRainLines(x, y, r) { - g.setColor(B2 ? '#0FF' : (g.theme.dark ? "#0CF" : "#07F")); + g.setColor(palette.rain); const y1 = y+1/2*r; const y2 = y+1*r; const poly = g.fillPolyAA ? p => g.fillPolyAA(p) : p => g.fillPoly(p); @@ -124,7 +172,7 @@ exports.drawIcon = function(cond, x, y, r) { function drawThunderstorm(x, y, r) { function drawLightning(x, y, r) { - g.setColor(B2 ? '#FF0' : (g.theme.dark ? "#FE0" : "#FC0")); + g.setColor(palette.lightning); g.fillPoly([ x-2/6*r, y-r, x-4/6*r, y+1/6*r, @@ -152,7 +200,7 @@ exports.drawIcon = function(cond, x, y, r) { } } - g.setColor(B2 ? '#FFF' : (g.theme.dark ? "#FFF" : "#CCC")); + g.setColor(palette.snow); const w = 1/12*r; for(let i = 0; i<=6; ++i) { const points = [ @@ -187,7 +235,7 @@ exports.drawIcon = function(cond, x, y, r) { [-0.2, 0.3], ]; - g.setColor(B2 ? '#FFF' : (g.theme.dark ? "#FFF" : "#CCC")); + g.setColor(palette.mist); for(let i = 0; i<5; ++i) { g.fillRect(x+layers[i][0]*r, y+(0.4*i-0.9)*r, x+layers[i][1]*r, y+(0.4*i-0.7)*r-1); @@ -197,7 +245,7 @@ exports.drawIcon = function(cond, x, y, r) { } function drawUnknown(x, y, r) { - drawCloud(x, y, r, "#777"); // dithers on B2, but that's ok + drawCloud(x, y, r, palette.bgCloud); g.setColor(g.theme.fg).setFontAlign(0, 0).setFont('Vector', r*2).drawString("?", x+r/10, y+r/6); } @@ -232,5 +280,44 @@ exports.drawIcon = function(cond, x, y, r) { return drawUnknown; } - chooseIcon(cond)(x, y, r); + /* + * Choose weather icon to display based on weather conditition code + * https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2 + */ + function chooseIconByCode(code) { + const codeGroup = Math.round(code / 100); + switch (codeGroup) { + case 2: return drawThunderstorm; + case 3: return drawRain; + case 5: + switch (code) { + case 511: return drawSnow; + case 520: return drawShowerRain; + case 521: return drawShowerRain; + case 522: return drawShowerRain; + case 531: return drawShowerRain; + default: return drawRain; + } + break; + case 6: return drawSnow; + case 7: return drawMist; + case 8: + switch (code) { + case 800: return drawSun; + case 801: return drawFewClouds; + case 802: return drawCloud; + default: return drawBrokenClouds; + } + break; + default: return drawUnknown; + } + } + + if (cond.code && cond.code > 0) { + chooseIconByCode(cond.code)(x, y, r); + } else { + chooseIcon(cond.txt)(x, y, r); + } + + }; diff --git a/apps/weatherClock/ChangeLog b/apps/weatherClock/ChangeLog index 600f93be2..a6a12c297 100644 --- a/apps/weatherClock/ChangeLog +++ b/apps/weatherClock/ChangeLog @@ -1,3 +1,5 @@ 0.01: New App! 0.02: Minor layout format tweak so it uses less memory and draws ok on Bangle.js 1 (#1012) 0.03: Minor layout extra spaces. +0.04: Layout now compatible with Bangle.js 2 +0.05: Use weather condition code for icon selection diff --git a/apps/weatherClock/app.js b/apps/weatherClock/app.js index 9dd49427f..1a7f53f05 100644 --- a/apps/weatherClock/app.js +++ b/apps/weatherClock/app.js @@ -53,6 +53,29 @@ function chooseIcon(condition) { return cloudIcon; } +/* +* Choose weather icon to display based on weather conditition code +* https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2 +*/ +function chooseIconByCode(code) { + const codeGroup = Math.round(code / 100); + switch (codeGroup) { + case 2: return stormIcon; + case 3: return rainIcon; + case 5: return rainIcon; + case 6: return snowIcon; + case 7: return cloudIcon; + case 8: + switch (code) { + case 800: return sunIcon; + case 801: return partSunIcon; + default: return cloudIcon; + } + break; + default: return cloudIcon; + } +} + /** Get weather stored in json file by weather app. */ @@ -73,12 +96,10 @@ var clockLayout = new Layout( { {type: "img", filly: 1, id: "weatherIcon", src: sunIcon}, {type: "v", fillx:1, c: [ {type: "h", c: [ - {type: "txt", font: "10%", id: "temp", label: "000"}, - {type: "txt", font: "10%", id: "tempUnit", label: "°C"}, + {type: "txt", font: "10%", id: "temp", label: "000 °C"}, ]}, {type: "h", c: [ - {type: "txt", font: "10%", id: "wind", label: "00"}, - {type: "txt", font: "10%", id: "windUnit", label: "km/h"}, + {type: "txt", font: "10%", id: "wind", label: "00 km/h"}, ]} ] }, @@ -106,18 +127,19 @@ function draw() { if(weatherJson && weatherJson.weather){ var currentWeather = weatherJson.weather; const temp = locale.temp(currentWeather.temp-273.15).match(/^(\D*\d*)(.*)$/); - clockLayout.temp.label = temp[1]; - clockLayout.tempUnit.label = temp[2]; - clockLayout.weatherIcon.src = chooseIcon(currentWeather.txt); + clockLayout.temp.label = temp[1] + " " + temp[2]; + const code = currentWeather.code || -1; + if (code > 0) { + clockLayout.weatherIcon.src = chooseIconByCode(code); + } else { + clockLayout.weatherIcon.src = chooseIcon(currentWeather.txt); + } const wind = locale.speed(currentWeather.wind).match(/^(\D*\d*)(.*)$/); - clockLayout.wind.label = wind[1] + " ".repeat(wind[2].length-1); - clockLayout.windUnit.label = wind[2] + " " + (currentWeather.wrose||'').toUpperCase(); + clockLayout.wind.label = wind[1] + " " + wind[2] + " " + (currentWeather.wrose||'').toUpperCase(); } else{ clockLayout.temp.label = "Err"; - clockLayout.tempUnit.label = ""; clockLayout.wind.label = "No Data"; - clockLayout.windUnit.label = ""; clockLayout.weatherIcon.src = errIcon; } clockLayout.clear(); diff --git a/apps/wid_a_battery_widget/ChangeLog b/apps/wid_a_battery_widget/ChangeLog index 9b0649c27..b04824ae8 100644 --- a/apps/wid_a_battery_widget/ChangeLog +++ b/apps/wid_a_battery_widget/ChangeLog @@ -1,2 +1,3 @@ 1.00: Release for Bangle 2 (2021/11/18) 1.01: Internal id update to wid_* as per Gordon's request (2021/11/21) +1.02: Support dark themes \ No newline at end of file diff --git a/apps/wid_a_battery_widget/widget.js b/apps/wid_a_battery_widget/widget.js index 9fb06e320..8ab644ab3 100644 --- a/apps/wid_a_battery_widget/widget.js +++ b/apps/wid_a_battery_widget/widget.js @@ -1,9 +1,9 @@ (function(){ let COLORS = { - 'white': "#fff", - 'black': "#000", + 'white': g.theme.dark ? "#000" : "#fff", + 'black': g.theme.dark ? "#fff" : "#000", 'charging': "#08f", - 'high': "#000", + 'high': g.theme.dark ? "#fff" : "#000", 'low': "#f00", }; diff --git a/apps/widbt/ChangeLog b/apps/widbt/ChangeLog index 7aa96ce5c..4c2132122 100644 --- a/apps/widbt/ChangeLog +++ b/apps/widbt/ChangeLog @@ -4,3 +4,4 @@ 0.05: Make Bluetooth widget thinner, and when on a bright theme use light grey for disabled color 0.06: Tweaking colors for dark/light themes and low bpp screens 0.07: Memory usage improvements +0.08: Disable LCD on, on bluetooth status change diff --git a/apps/widbt/widget.js b/apps/widbt/widget.js index 88be3d5c9..c7ef8c0ad 100644 --- a/apps/widbt/widget.js +++ b/apps/widbt/widget.js @@ -7,7 +7,6 @@ WIDGETS["bluetooth"]={area:"tr",width:15,draw:function() { g.drawImage(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="),2+this.x,2+this.y); },changed:function() { WIDGETS["bluetooth"].draw(); - Bangle.setLCDPower(1); // turn screen on }}; NRF.on('connect',WIDGETS["bluetooth"].changed); NRF.on('disconnect',WIDGETS["bluetooth"].changed); diff --git a/apps/widclkbttm/Changelog b/apps/widclkbttm/ChangeLog similarity index 100% rename from apps/widclkbttm/Changelog rename to apps/widclkbttm/ChangeLog diff --git a/apps/widpedom/ChangeLog b/apps/widpedom/ChangeLog index 2f36c7647..c033ea505 100644 --- a/apps/widpedom/ChangeLog +++ b/apps/widpedom/ChangeLog @@ -19,3 +19,4 @@ Stop goal drawing outside widget area Fix issue with widget overwrite in large font mode Memory usage enhancements +0.20: Fix issue where step count would randomly reset diff --git a/apps/widpedom/widget.js b/apps/widpedom/widget.js index 3c861cf54..0ec0780c9 100644 --- a/apps/widpedom/widget.js +++ b/apps/widpedom/widget.js @@ -53,7 +53,7 @@ E.on('kill', () => { if (!settings) { loadSettings() } let d = { - lastUpdate : lastUpdate.toISOString(), + lastUpdate : lastUpdate.valueOf(), stepsToday : stp_today, settings : settings, }; diff --git a/apps/widviztime/README.md b/apps/widviztime/README.md new file mode 100644 index 000000000..73e24b658 --- /dev/null +++ b/apps/widviztime/README.md @@ -0,0 +1,8 @@ +# Widget Autohide Widget +This widget is forked from the "Widget Visibility Widget" +It should make widgets completely hidden (except for 4 seconds after the watch is unlocked) + +Additional features that I want to implement: +- Only show widgets when in app launcher +- Make timeout adjustable +- Disable widgets completely diff --git a/apps/widviztime/changelog b/apps/widviztime/changelog new file mode 100644 index 000000000..287061d0c --- /dev/null +++ b/apps/widviztime/changelog @@ -0,0 +1 @@ +0.01: New Widget, forked from widviz diff --git a/apps/widviztime/eye.png b/apps/widviztime/eye.png new file mode 100644 index 000000000..9aec8ce89 Binary files /dev/null and b/apps/widviztime/eye.png differ diff --git a/apps/widviztime/widget.js b/apps/widviztime/widget.js new file mode 100644 index 000000000..5e81af611 --- /dev/null +++ b/apps/widviztime/widget.js @@ -0,0 +1,53 @@ +(() => { + + var saved = null; + + + function hide() { + if (!Bangle.isLCDOn() || saved) return; + saved = []; + for (var wd of WIDGETS) { + saved.push({ + d: wd.draw, + a: wd.area + }); + wd.draw = () => {}; + wd.area = ""; + } + g.setColor(0, 0, 0); + g.fillRect(0, 0, g.getWidth(), 23); + } + + function reveal() { + if (!Bangle.isLCDOn() || !saved) return; + for (var wd of WIDGETS) { + var o = saved.shift(); + wd.draw = o.d; + wd.area = o.a; + } + Bangle.drawWidgets(); + saved = null; + } + + function draw() { + g.setColor(0x07ff); + g.drawImage(atob("GBgBAAAAAAAAAAAAAAAAAH4AAf+AB4HgDgBwHDw4OH4cMOcMYMMGYMMGMOcMOH4cHDw4DgBwB4HgAf+AAH4AAAAAAAAAAAAAAAAA"), this.x, this.y); + } + + WIDGETS.viz = { + area: "tl", + width: 24, + draw: draw + }; + + + + Bangle.on('lock', (locked) => { + if (!locked) { + reveal(); + setTimeout(function() { + hide(); + }, 4000); + } + }); +})(); diff --git a/bin/language_scan.js b/bin/language_scan.js new file mode 100755 index 000000000..2a92fded1 --- /dev/null +++ b/bin/language_scan.js @@ -0,0 +1,69 @@ +#!/usr/bin/nodejs +/* Scans for strings that may be in English in each app, and +outputs a list of strings that have been found. + +Early work towards internationalisation. +See https://github.com/espruino/BangleApps/issues/136 +*/ + +var BASEDIR = __dirname+"/../"; +Espruino = require(BASEDIR+"core/lib/espruinotools.js"); +var fs = require("fs"); + +var APPSDIR = BASEDIR+"apps/"; +function ERROR(s) { + console.error("ERROR: "+s); + process.exit(1); +} +function WARN(s) { + console.log("Warning: "+s); +} + +var appsFile, apps; +try { + appsFile = fs.readFileSync(BASEDIR+"apps.json").toString(); +} catch (e) { + ERROR("apps.json not found"); +} +try{ + apps = JSON.parse(appsFile); +} catch (e) { + ERROR("apps.json not valid JSON"); +} + +// Given a string value, work out if it's obviously not a text string +function isNotString(s) { + if (s.length<2) return true; // too short + if (s.length>40) return true; // too long + if (s[0]=="#") return true; // a color + if (s.endsWith(".json") || s.endsWith(".img")) return true; // a filename + if (s.endsWith("=")) return true; // probably base64 + if (s.startsWith("BTN")) return true; // button name + return false; +} + +var textStrings = []; + +console.log("Scanning..."); +apps.forEach((app,appIdx) => { + var appDir = APPSDIR+app.id+"/"; + app.storage.forEach((file) => { + if (!file.url || !file.name.endsWith(".js")) return; + var fileContents = fs.readFileSync(appDir+file.url).toString(); + var lex = Espruino.Core.Utils.getLexer(fileContents); + var tok = lex.next(); + while (tok!==undefined) { + if (tok.type=="STRING") { + if (!isNotString(tok.value)) { + //console.log(tok.str); + if (!textStrings.includes(tok.value)) + textStrings.push(tok.value); + } + } + tok = lex.next(); + } + }); +}); +console.log("Done"); +textStrings.sort(); +console.log(textStrings.join("\n")); diff --git a/bin/sanitycheck.js b/bin/sanitycheck.js index 572364224..e50256fb6 100755 --- a/bin/sanitycheck.js +++ b/bin/sanitycheck.js @@ -3,6 +3,7 @@ */ var fs = require("fs"); +var heatshrink = require("../core/lib/heatshrink"); var acorn; try { acorn = require("acorn"); @@ -59,6 +60,7 @@ const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite', 'suppo const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate']; const FORBIDDEN_FILE_NAME_CHARS = /[,;]/; // used as separators in appid.info const VALID_DUPLICATES = [ '.tfmodel', '.tfnames' ]; +const GRANDFATHERED_ICONS = ["s7clk", "snek", "astral", "alpinenav", "slomoclock", "arrow", "pebble", "rebble"]; function globToRegex(pattern) { const ESCAPE = '.*+-?^${}()|[]\\'; @@ -74,8 +76,11 @@ function globToRegex(pattern) { const isGlob = f => /[?*]/.test(f) // All storage+data files in all apps: {app:,[file: | data:]} let allFiles = []; +let existingApps = []; apps.forEach((app,appIdx) => { if (!app.id) ERROR(`App ${appIdx} has no id`); + if (existingApps.includes(app.id)) ERROR(`Duplicate app '${app.id}'`); + existingApps.push(app.id); //console.log(`Checking ${app.id}...`); var appDir = APPSDIR+app.id+"/"; if (!fs.existsSync(APPSDIR+app.id)) ERROR(`App ${app.id} has no directory`); @@ -175,6 +180,23 @@ apps.forEach((app,appIdx) => { for (const key in file) { if (!STORAGE_KEYS.includes(key)) ERROR(`App ${app.id} file ${file.name} has unknown key ${key}`); } + // warn if JS icon is the wrong size + if (file.name == app.id+".img") { + let icon; + let match = fileContents.match(/E\.toArrayBuffer\(atob\(\"([^"]*)\"\)\)/); + if (match) icon = Buffer.from(match[1], 'base64'); + else { + match = fileContents.match(/require\(\"heatshrink\"\)\.decompress\(\s*atob\(\s*\"([^"]*)\"\s*\)\s*\)/); + if (match) icon = heatshrink.decompress(Buffer.from(match[1], 'base64')); + else ERROR(`JS icon ${file.name} does not match the pattern 'require("heatshrink").decompress(atob("..."))'`); + } + if (match) { + if (icon[0] > 48 || icon[0] < 24 || icon[1] > 48 || icon[1] < 24) { + if (GRANDFATHERED_ICONS.includes(app.id)) WARN(`JS icon ${file.name} should be 48x48px (or slightly under) but is instead ${icon[0]}x${icon[1]}px`); + else ERROR(`JS icon ${file.name} should be 48x48px (or slightly under) but is instead ${icon[0]}x${icon[1]}px`); + } + } + } }); let dataNames = []; (app.data||[]).forEach((data)=>{ diff --git a/core b/core index 23854083e..5a5957714 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 23854083e0c3f83c649073a2d85e8079efc471d3 +Subproject commit 5a5957714d4aa04413329f57c03e6de0cfb74caf diff --git a/lang/de_DE.json b/lang/de_DE.json new file mode 100644 index 000000000..80d0e74bb --- /dev/null +++ b/lang/de_DE.json @@ -0,0 +1,23 @@ +{ + "//":"German language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarm" : "Wecker", + "Hours" : "Stunden", + "Minutes" : "Minuten", + "Enabled" : "Aktiviert", + "Settings" : "Einstellungen", + "Save" : "Speichern", + "Back" : "Zurück", + "Repeat" : "Wiederholen", + "Delete" : "Löschen", + "Sleep" : "Schlummern", + "Alarms" : "Wecker", + "New Alarm" : "Neuer Wecker", + "ALARM!" : "ALARM!" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "Wdh." + } +} diff --git a/lang/en_GB.json b/lang/en_GB.json new file mode 100644 index 000000000..e85fe8029 --- /dev/null +++ b/lang/en_GB.json @@ -0,0 +1,9 @@ +{ + "//":"British English language translations - the default strings in apps are all english anyway, so no need to have translations for most things", + "GLOBAL": { + "//":"Translations that apply for all apps", + }, + "alarm": { + "//":"App-specific overrides", + } +} diff --git a/lang/es_ES.json b/lang/es_ES.json new file mode 100644 index 000000000..0671c4ab8 --- /dev/null +++ b/lang/es_ES.json @@ -0,0 +1,21 @@ +{ + "//":"Spanish language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarms" : "Alarmas", + "Hours" : "Horas", + "Minutes" : "Minutos", + "Enabled" : "Activados", + "New Alarm" : "Alarma nueva", + "Save" : "Grabar", + "Back" : "Atrás", + "Repeat" : "Repetición", + "Delete" : "Borrar", + "ALARM!" : "ALARM", + "Sleep" : "Dormir" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "rep." + } +} diff --git a/lang/fi_FI.json b/lang/fi_FI.json new file mode 100644 index 000000000..eb1d826d8 --- /dev/null +++ b/lang/fi_FI.json @@ -0,0 +1,21 @@ +{ + "//":"Finnish language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarms" : "Hälytykset", + "Hours" : "Tunnit", + "Minutes" : "Minuutit", + "Enabled" : "Aktivoitu", + "New Alarm" : "Uusi hälytys", + "Save" : "Tallenna", + "Back" : "Paluu", + "Repeat" : "Toista", + "Delete" : "Poista", + "ALARM!" : "ALARM", + "Sleep" : "Nukkuminen" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "toistaa" + } +} diff --git a/lang/fr_FR.json b/lang/fr_FR.json new file mode 100644 index 000000000..209574424 --- /dev/null +++ b/lang/fr_FR.json @@ -0,0 +1,21 @@ +{ + "//":"French language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarms" : "Réveils", + "Hours" : "Heures", + "Minutes" : "Minutes", + "Enabled" : "Activé", + "New Alarm" : "Nouveau Réveil", + "Save" : "Sauvegarder", + "Back" : "Retour", + "Repeat" : "Répétition", + "Delete" : "Supprimer", + "ALARM!" : "ALARM!", + "Sleep" : "Sommeil" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "rép." + } +} diff --git a/lang/hu_HU.json b/lang/hu_HU.json new file mode 100644 index 000000000..8e5df6ed7 --- /dev/null +++ b/lang/hu_HU.json @@ -0,0 +1,21 @@ +{ + "//":"Spanish language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarms" : "Riasztások", + "Hours" : "Óra", + "Minutes" : "Perc", + "Enabled" : "Aktiválva", + "New Alarm" : "Új riasztás", + "Save" : "Mentés", + "Back" : "Vissza", + "Repeat" : "Ismétlés", + "Delete" : "Törlés", + "ALARM!" : "ALARM!", + "Sleep" : "Alvás" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "ismétlés" + } +} diff --git a/lang/index.json b/lang/index.json new file mode 100644 index 000000000..f17bf5e03 --- /dev/null +++ b/lang/index.json @@ -0,0 +1,12 @@ +[ + {"code":"en_GB","name":"British English","url":"en_GB.json"}, + {"code":"de_DE","name":"German","url":"de_DE.json"}, + {"code":"es_ES","name":"Spanish","url":"es_ES.json"}, + {"code":"fi_FI","name":"Finnish","url":"fi_FI.json"}, + {"code":"fr_FR","name":"French","url":"fr_FR.json"}, + {"code":"hu_HU","name":"Hungarian","url":"hu_HU.json"}, + {"code":"it_IT","name":"Italian","url":"it_IT.json"}, + {"code":"nl_NL","name":"Dutch","url":"nl_NL.json"}, + {"code":"sv_SE","name":"Swedish","url":"sv_SE.json"}, + {"code":"tr_TR","name":"Turkish","url":"tr_TR.json"} +] diff --git a/lang/it_IT.json b/lang/it_IT.json new file mode 100644 index 000000000..184c80238 --- /dev/null +++ b/lang/it_IT.json @@ -0,0 +1,21 @@ +{ + "//":"Italian language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarms" : "Allarmi", + "Hours" : "Ore", + "Minutes" : "Minuti", + "Enabled" : "Attivato", + "New Alarm" : "Nuovo allarme", + "Save" : "Salvare", + "Back" : "Indietro", + "Repeat" : "Ripetere", + "Delete" : "Cancellare", + "ALARM!" : "ALARM!", + "Sleep" : "Dormire" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "ripetere" + } +} diff --git a/lang/nl_NL.json b/lang/nl_NL.json new file mode 100644 index 000000000..a04e46928 --- /dev/null +++ b/lang/nl_NL.json @@ -0,0 +1,21 @@ +{ + "//":"Dutch language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarms" : "Alarmen", + "Hours" : "Uren", + "Minutes" : "Minuten", + "Enabled" : "Geactiveerd", + "New Alarm" : "Nieuw alarm", + "Save" : "Opslaan", + "Back" : "Terug", + "Repeat" : "Herhalen", + "Delete" : "Verwijderen", + "ALARM!" : "ALARV.", + "Sleep" : "Stand-by" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "herhalen" + } +} diff --git a/lang/sv_SE.json b/lang/sv_SE.json new file mode 100644 index 000000000..3a006c2bf --- /dev/null +++ b/lang/sv_SE.json @@ -0,0 +1,21 @@ +{ + "//":"Swedish language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarms" : "Larm", + "Hours" : "Timmar", + "Minutes" : "Minuter", + "Enabled" : "Aktiverad", + "New Alarm" : "Ny alarm", + "Save" : "Spara", + "Back" : "Tillbaka", + "Repeat" : "Upprepning", + "Delete" : "Radera", + "ALARM!" : "ALURH!", + "Sleep" : "Sömn" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "uppr." + } +} diff --git a/lang/tr_TR.json b/lang/tr_TR.json new file mode 100644 index 000000000..c59bc7d6b --- /dev/null +++ b/lang/tr_TR.json @@ -0,0 +1,21 @@ +{ + "//":"Turkish language translations", + "GLOBAL": { + "//":"Translations that apply for all apps", + "Alarms" : "Alarmlar", + "Hours" : "Saat", + "Minutes" : "Dakika", + "Enabled" : "Etkinleştirildi", + "New Alarm" : "Yeni alarm", + "Save" : "Sakla", + "Back" : "Geriye", + "Repeat" : "Yineleme", + "Delete" : "Sil", + "ALARM!" : "ALARM!", + "Sleep" : "Uyku" + }, + "alarm": { + "//":"App-specific overrides", + "rpt" : "yineleme" + } +} diff --git a/loader.js b/loader.js index 680cd0f94..768f5f38f 100644 --- a/loader.js +++ b/loader.js @@ -11,7 +11,7 @@ if (window.location.host=="banglejs.com") { 'This is not the official Bangle.js App Loader - you can try the Official Version here.'; } -var RECOMMENDED_VERSION = "2v10"; +var RECOMMENDED_VERSION = "2v11"; // could check http://www.espruino.com/json/BANGLEJS.json for this // We're only interested in Bangles