Merge branch 'espruino:master' into master

master
Jeroen Peters 2021-12-13 14:21:32 +01:00 committed by GitHub
commit 9e166ec7a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 1430 additions and 442 deletions

View File

@ -33,10 +33,11 @@
"id": "hebrew_calendar", "id": "hebrew_calendar",
"name": "Hebrew Calendar", "name": "Hebrew Calendar",
"shortName": "HebCal", "shortName": "HebCal",
"version": "0.03", "version": "0.04",
"description": "lists the date according to the hebrew calendar", "description": "lists the date according to the hebrew calendar",
"icon": "app.png", "icon": "app.png",
"tags": "", "allow_emulator": false,
"tags": "tool,locale",
"supports": [ "supports": [
"BANGLEJS", "BANGLEJS",
"BANGLEJS2" "BANGLEJS2"
@ -47,6 +48,10 @@
"name": "hebrew_calendar.app.js", "name": "hebrew_calendar.app.js",
"url": "app.js" "url": "app.js"
}, },
{
"name": "hebrewDate",
"url": "hebrewDate.js"
},
{ {
"name": "hebrew_calendar.img", "name": "hebrew_calendar.img",
"url": "app-icon.js", "url": "app-icon.js",
@ -54,6 +59,21 @@
} }
] ]
}, },
{ "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", "id": "messages",
"name": "Messages", "name": "Messages",
@ -824,7 +844,7 @@
{ {
"id": "weather", "id": "weather",
"name": "Weather", "name": "Weather",
"version": "0.12", "version": "0.13",
"description": "Show Gadgetbridge weather report", "description": "Show Gadgetbridge weather report",
"icon": "icon.png", "icon": "icon.png",
"screenshots": [{"url":"screenshot.png"}], "screenshots": [{"url":"screenshot.png"}],
@ -1127,7 +1147,7 @@
{ {
"id": "qrcode", "id": "qrcode",
"name": "Custom QR Code", "name": "Custom QR Code",
"version": "0.03", "version": "0.04",
"description": "Use this to upload a customised QR code to Bangle.js", "description": "Use this to upload a customised QR code to Bangle.js",
"icon": "app.png", "icon": "app.png",
"tags": "qrcode", "tags": "qrcode",
@ -3870,7 +3890,7 @@
"id": "qmsched", "id": "qmsched",
"name": "Quiet Mode Schedule and Widget", "name": "Quiet Mode Schedule and Widget",
"shortName": "Quiet Mode", "shortName": "Quiet Mode",
"version": "0.04", "version": "0.05",
"description": "Automatically turn Quiet Mode on or off at set times, and change LCD options while Quiet Mode is active.", "description": "Automatically turn Quiet Mode on or off at set times, and change LCD options while Quiet Mode is active.",
"icon": "app.png", "icon": "app.png",
"screenshots": [{"url":"screenshot_b1_main.png"},{"url":"screenshot_b1_edit.png"},{"url":"screenshot_b1_lcd.png"}, "screenshots": [{"url":"screenshot_b1_main.png"},{"url":"screenshot_b1_edit.png"},{"url":"screenshot_b1_lcd.png"},
@ -4117,14 +4137,17 @@
{ {
"id": "vectorclock", "id": "vectorclock",
"name": "Vector Clock", "name": "Vector Clock",
"version": "0.02", "version": "0.03",
"description": "A digital clock that uses the built-in vector font.", "description": "A digital clock that uses the built-in vector font.",
"icon": "app.png", "icon": "app.png",
"type": "clock", "type": "clock",
"tags": "clock", "tags": "clock",
"supports": ["BANGLEJS"], "supports": ["BANGLEJS", "BANGLEJS2"],
"allow_emulator": true, "allow_emulator": true,
"screenshots": [{"url":"bangle1-vector-clock-screenshot.png"}], "screenshots": [
{"url":"bangle2-vector-clock-screenshot.png"},
{"url":"bangle1-vector-clock-screenshot.png"}
],
"storage": [ "storage": [
{"name":"vectorclock.app.js","url":"app.js"}, {"name":"vectorclock.app.js","url":"app.js"},
{"name":"vectorclock.img","url":"app-icon.js","evaluate":true} {"name":"vectorclock.img","url":"app-icon.js","evaluate":true}
@ -4442,9 +4465,9 @@
"name": "A Battery Widget (with percentage)", "name": "A Battery Widget (with percentage)",
"shortName":"A Battery Widget", "shortName":"A Battery Widget",
"icon": "widget.png", "icon": "widget.png",
"version":"1.01", "version":"1.02",
"type": "widget", "type": "widget",
"supports": ["BANGLEJS2"], "supports": ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"description": "Simple and slim battery widget with charge status and percentage", "description": "Simple and slim battery widget with charge status and percentage",
"tags": "widget,battery", "tags": "widget,battery",
@ -4711,7 +4734,7 @@
{ "id": "pooqroman", { "id": "pooqroman",
"name": "pooq Roman watch face", "name": "pooq Roman watch face",
"shortName":"pooq Roman", "shortName":"pooq Roman",
"version":"0.0.0", "version":"0.02",
"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!", "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", "icon": "app.png",
"type": "clock", "type": "clock",
@ -4746,7 +4769,7 @@
{ {
"id": "weatherClock", "id": "weatherClock",
"name": "Weather Clock", "name": "Weather Clock",
"version": "0.03", "version": "0.04",
"description": "A clock which displays current weather conditions (requires Gadgetbridge and Weather apps).", "description": "A clock which displays current weather conditions (requires Gadgetbridge and Weather apps).",
"icon": "app.png", "icon": "app.png",
"screenshots": [{"url":"screens/screen1.png"}], "screenshots": [{"url":"screens/screen1.png"}],
@ -4843,7 +4866,41 @@
], ],
"data": [{"name":"ptlaunch.patterns.json"}] "data": [{"name":"ptlaunch.patterns.json"}]
}, },
{ "id": "clicompleteclk", {
"id": "rebble",
"name": "Rebble Clock",
"shortName": "Rebble",
"version": "0.01",
"description": "A Pebble style clock, with configurable background, three sidebars including steps, day, date, sunrise, sunset, long live the rebellion",
"readme": "README.md",
"icon": "rebble.png",
"dependencies": {"mylocation":"app"},
"screenshots": [{"url":"screenshot_rebble.png"}],
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS2"],
"storage": [
{"name":"rebble.app.js","url":"rebble.app.js"},
{"name":"rebble.settings.js","url":"rebble.settings.js"},
{"name":"rebble.img","url":"rebble.icon.js","evaluate":true}
]
},
{ "id": "snaky",
"name": "Snaky",
"shortName":"Snaky",
"version":"0.01",
"description": "The classic snake game. Eat apples and don't bite your tail. Control the snake with the touch screen.",
"tags": "game,fun",
"icon": "snaky.png",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"snaky.app.js","url":"snaky.js"},
{"name":"snaky.img","url":"snaky-icon.js","evaluate":true}
]
},
{
"id": "clicompleteclk",
"name": "CLI complete clock", "name": "CLI complete clock",
"shortName":"CLI cmplt clock", "shortName":"CLI cmplt clock",
"version":"0.03", "version":"0.03",
@ -4875,5 +4932,4 @@
{"name":"awairmonitor.img","url":"app-icon.js","evaluate":true} {"name":"awairmonitor.img","url":"app-icon.js","evaluate":true}
] ]
} }
}
] ]

2
apps/golfscore/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: New App!
0.02: multiple player score support

37
apps/golfscore/README.md Normal file
View File

@ -0,0 +1,37 @@
# Golf Score
Lets you keep track of strokes during a game of Golf.
![](mainmenu.png)
![](setupmenu.png)
![](scorecard.png)
![](holemenu.png)
## Usage
1. Open the app,
1. scroll to setup
2. set the number of holes (18 by default, but can be configured)
3. set the number of players (4 by default, but can be 1-20)
4. click back
5. scroll to a hole (hole 1)
6. scroll to a player and set the number of strokes they took (repeat as needed)
7. click next hole and repeat #6 and #7 as needed; or click back
8. at any time, check the score card for a sum total of all the strokes for each player
## Features
Track strokes for multiple players (1-20)
Set number of holes on course
## Controls
N/A
## Requests
Michael Salaverry (github.com/barakplasma)
## Creator
Michael Salaverry

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwIEBgOABQcD4AFDg1wAokYDokOAokDDwkBDwkADwn4nAFD/geDgP8gYFEDwn8gFgDocA+AFCkE/A4IABg//Aoc//4RDn/+Goc/8AFJj4FLEQYFGh4FLIAYFGg4FKh5sBApEfnhTEAok+Aol8vihEAon4AocB+F4ZQYFF8AFDg/AAocPAouAKYcfXQQFHjzEEhjvDA"))

113
apps/golfscore/app.js Normal file
View File

@ -0,0 +1,113 @@
// @ts-check
// @ts-ignore
const menu = require("graphical_menu");
/**
* @type {{showMenu: (config) => void}}
*/
let E;
/**
* @type {{clear: () => void}}
*/
let g;
let holes_count = 18;
let player_count = 4;
/**
* @type {number[][]}
*/
let course = new Array(holes_count).map(() => new Array(player_count).fill(0));
const main_menu = {
"": {
"title": "-- Golf --"
},
"Setup": function () { E.showMenu(setup_menu); },
"Score Card": function () {
calculate_score();
E.showMenu(score_card);
},
};
function calculate_score() {
let scores = course.reduce((acc, hole) => {
hole.forEach((stroke_count, player) => {
acc[player] = acc[player]+stroke_count;
});
return acc;
}, new Array(player_count).fill(0));
score_card = {
"": {
"title": "score card"
},
"< Back": function () { E.showMenu(main_menu); },
};
for (let player = 0; player < player_count; player++) {
score_card["Player - " + (player + 1)] = {
value: scores[player]
};
}
}
let score_card = {};
const setup_menu = {
"": {
"title": "-- Golf Setup --"
},
"Holes": {
value: holes_count,
min: 1, max: 20, step: 1, wrap: true,
onchange: v => { holes_count = v; add_holes(); }
},
"Players": {
value: player_count,
min: 1, max: 10, step: 1, wrap: true,
onchange: v => { player_count = v; }
},
"< Back": function () { E.showMenu(main_menu); },
};
function inc_hole(i, player) { return function (v) { course[i][player] = v; }; }
function add_holes() {
for (let j = 0; j < 20; j++) {
delete main_menu["Hole - " + (j + 1)];
}
for (let i = 0; i < holes_count; i++) {
course[i] = new Array(player_count).fill(0);
main_menu["Hole - " + (i + 1)] = goto_hole_menu(i);
}
E.showMenu(main_menu);
}
function goto_hole_menu(i) {
return function () {
E.showMenu(hole_menu(i));
};
}
function hole_menu(i) {
let menu = {
"": {
"title": `-- Hole ${i + 1}--`
},
"Next hole": goto_hole_menu(i + 1),
"< Back": function () { E.showMenu(main_menu); },
};
for (let player = 0; player < player_count; player++) {
menu[`player - ${player + 1}`] = {
value: course[i][player],
min: 1, max: 20, step: 1, wrap: true,
onchange: inc_hole(i, player)
};
}
return menu;
}
// @ts-ignore
g.clear();
add_holes();

BIN
apps/golfscore/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
apps/golfscore/holemenu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
apps/golfscore/mainmenu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,3 +1,4 @@
0.01: New App! 0.01: New App!
0.02: using TS and rollup to bundle 0.02: using TS and rollup to bundle
0.03: bug fixes and support bangle 1 0.03: bug fixes and support bangle 1
0.04: removing TS

View File

@ -1,5 +1,5 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2021 Michael Salaverry
Copyright (c) 2016-20 Ionică Bizău <bizauionica@gmail.com> (https://ionicabizau.net) Copyright (c) 2016-20 Ionică Bizău <bizauionica@gmail.com> (https://ionicabizau.net)
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy

View File

@ -1,17 +1,26 @@
!function(){"use strict"; g.clear();
/*!
* This script was taked from this page and ported to Node.js by Ionic Bizu let now = new Date();
* http://www.shamash.org/help/javadate.shtml
* let today = require('hebrewDate').hebrewDate(now);
* This script was adapted from C sources written by
* Scott E. Lee, which contain the following copyright notice: var mainmenu = {
* "": {
* Copyright 1993-1995, Scott E. Lee, all rights reserved. "title": "Hebrew Date"
* Permission granted to use, copy, modify, distribute and sell so long as },
* the above copyright and this permission statement are retained in all greg: {
* copies. THERE IS NO WARRANTY - USE AT YOUR OWN RISK. // @ts-ignore
* value: require('locale').date(now, 1),
* Bill Hastings },
* RBI Software Systems date: {
* bhastings@rbi.com value: today.date,
*/var t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};var i=new function(t,i,e,o,r,n,h,a,s,f,u,l,v,c){this[0]=t,this[1]=i,this[2]=e,this[3]=o,this[4]=r,this[5]=n,this[6]=h,this[7]=a,this[8]=s,this[9]=f,this[10]=u,this[11]=l,this[12]=v,this[13]=c}("Tishri","Heshvan","Kislev","Tevet","Shevat","AdarI","AdarII","Nisan","Iyyar","Sivan","Tammuz","Av","Elul"),e=new function(t,i,e,o,r,n,h,a,s,f,u,l,v,c,y,d,m,M,b){this[0]=t,this[1]=i,this[2]=e,this[3]=o,this[4]=r,this[5]=n,this[6]=h,this[7]=a,this[8]=s,this[9]=f,this[10]=u,this[11]=l,this[12]=v,this[13]=c,this[14]=y,this[15]=d,this[16]=m,this[17]=M,this[18]=b}(12,12,13,12,12,13,12,13,12,12,13,12,12,13,12,12,13,12,13);g.clear();let o=new Date,r=function(o){var r,n,h=0,a=0,s=0,f=0,u=0,l=0,v=0;function c(t){var i,o,r,n;for(f=Math.floor((t+310)/6940),r=void 0,n=void 0,r=31524,n=(r+=45971*f)>>16,n+=2744*f,o=Math.floor(n/25920),r=(n-=25920*o)<<16|65535&r,i=Math.floor(r/25920),l=o<<16|i,v=r-=25920*i;l<t-6940+310;)f++,v+=179876755,l+=Math.floor(v/25920),v%=25920;for(u=0;u<18&&!(l>t-74);u++)v+=765433*e[u],l+=Math.floor(v/25920),v%=25920}function y(t,i,e){var o=i,r=o%7;return(e>=19440||!(2==t||5==t||7==t||10==t||13==t||16==t||18==t)&&2==r&&e>=9924||(3==t||6==t||8==t||11==t||14==t||17==t||0==t)&&1==r&&e>=16789)&&(o++,7==++r&&(r=0)),3!=r&&5!=r&&0!=r||o++,o}var d=o;return"object"===(void 0===d?"undefined":t(d))&&(r=o.getMonth()+1,n=o.getDate(),d=o.getFullYear()),function(t){var i,o=0,r=0,n=t-347997;if(c(n),n>=(o=y(u,l,v))){if(s=19*f+u+1,n<o+59)return void(n<o+30?(h=1,a=n-o+1):(h=2,a=n-o-29));v+=765433*e[u],l+=Math.floor(v/25920),r=y((u+1)%19,l,v%=25920)}else{if(s=19*f+u,n>=o-177)return void(n>o-30?(h=13,a=n-o+30):n>o-60?(h=12,a=n-o+60):n>o-89?(h=11,a=n-o+89):n>o-119?(h=10,a=n-o+119):n>o-148?(h=9,a=n-o+148):(h=8,a=n-o+178));if(13==e[(s-1)%19]){if(h=7,(a=n-o+207)>0)return;if(h--,(a+=30)>0)return;h--,a+=30}else{if(h=6,(a=n-o+207)>0)return;h--,a+=30}if(a>0)return;if(h--,(a+=29)>0)return;r=o,c(l-365),o=y(u,l,v)}if(l=n-o-29,355==(i=r-o)||385==i){if(l<=30)return h=2,void(a=l);l-=30}else{if(l<=29)return h=2,void(a=l);l-=29}h=3,a=l}(function(t,i,e){var o=0,r=0,n=void 0;return o=t<0?t+4801:t+4800,i>2?r=i-3:(r=i+9,o--),n=Math.floor(146097*Math.floor(o/100)/4),n+=Math.floor(o%100*1461/4),n+=Math.floor((153*r+2)/5),n+=e-32045}(d,r,n)),{year:s,month:h,date:a,month_name:i[h-1]}}(o);var n={"":{title:"Hebrew Date"},cal:{value:require("locale").date(o,1),onchange:()=>{}},date:{value:r.date,onchange:()=>{}},month:{value:r.month_name,onchange:()=>{}},year:{value:r.year,onchange:()=>{}}};E.showMenu(n)}(); },
month: {
value: today.month_name,
},
year: {
value: today.year,
}
};
// @ts-ignore
E.showMenu(mainmenu);

View File

@ -1,6 +1,5 @@
/*! /*!
* This script was taked from this page and ported to Node.js by Ionic Bizu * This script was taked from this page http://www.shamash.org/help/javadate.shtml and ported to Node.js by Ionică Bizău in https://github.com/IonicaBizau/hebrew-date
* http://www.shamash.org/help/javadate.shtml
* *
* This script was adapted from C sources written by * This script was adapted from C sources written by
* Scott E. Lee, which contain the following copyright notice: * Scott E. Lee, which contain the following copyright notice:
@ -14,33 +13,11 @@
* RBI Software Systems * RBI Software Systems
* bhastings@rbi.com * bhastings@rbi.com
*/ */
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var GREG_SDN_OFFSET = 32045, DAYS_PER_5_MONTHS = 153, DAYS_PER_4_YEARS = 1461, DAYS_PER_400_YEARS = 146097;
var GREG_SDN_OFFSET = 32045, var HALAKIM_PER_HOUR = 1080, HALAKIM_PER_DAY = 25920, HALAKIM_PER_LUNAR_CYCLE = 29 * HALAKIM_PER_DAY + 13753, HALAKIM_PER_METONIC_CYCLE = HALAKIM_PER_LUNAR_CYCLE * (12 * 19 + 7);
DAYS_PER_5_MONTHS = 153, var HEB_SDN_OFFSET = 347997, NEW_MOON_OF_CREATION = 31524, NOON = 18 * HALAKIM_PER_HOUR, AM3_11_20 = 9 * HALAKIM_PER_HOUR + 204, AM9_32_43 = 15 * HALAKIM_PER_HOUR + 589;
DAYS_PER_4_YEARS = 1461, var SUN = 0, MON = 1, TUES = 2, WED = 3, THUR = 4, FRI = 5, SAT = 6;
DAYS_PER_400_YEARS = 146097;
var HALAKIM_PER_HOUR = 1080,
HALAKIM_PER_DAY = 25920,
HALAKIM_PER_LUNAR_CYCLE = 29 * HALAKIM_PER_DAY + 13753,
HALAKIM_PER_METONIC_CYCLE = HALAKIM_PER_LUNAR_CYCLE * (12 * 19 + 7);
var HEB_SDN_OFFSET = 347997,
NEW_MOON_OF_CREATION = 31524,
NOON = 18 * HALAKIM_PER_HOUR,
AM3_11_20 = 9 * HALAKIM_PER_HOUR + 204,
AM9_32_43 = 15 * HALAKIM_PER_HOUR + 589;
var SUN = 0,
MON = 1,
TUES = 2,
WED = 3,
THUR = 4,
FRI = 5,
SAT = 6;
function weekdayarr(d0, d1, d2, d3, d4, d5, d6) { function weekdayarr(d0, d1, d2, d3, d4, d5, d6) {
this[0] = d0; this[0] = d0;
this[1] = d1; this[1] = d1;
@ -50,7 +27,6 @@ function weekdayarr(d0, d1, d2, d3, d4, d5, d6) {
this[5] = d5; this[5] = d5;
this[6] = d6; this[6] = d6;
} }
function gregmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11) { function gregmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11) {
this[0] = m0; this[0] = m0;
this[1] = m1; this[1] = m1;
@ -65,8 +41,7 @@ function gregmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11) {
this[10] = m10; this[10] = m10;
this[11] = m11; this[11] = m11;
} }
function hebrewmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13) {
function hebrewmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13?: any) {
this[0] = m0; this[0] = m0;
this[1] = m1; this[1] = m1;
this[2] = m2; this[2] = m2;
@ -82,7 +57,6 @@ function hebrewmontharr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m
this[12] = m12; this[12] = m12;
this[13] = m13; this[13] = m13;
} }
function monthsperyeararr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18) { function monthsperyeararr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18) {
this[0] = m0; this[0] = m0;
this[1] = m1; this[1] = m1;
@ -104,12 +78,7 @@ function monthsperyeararr(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12,
this[17] = m17; this[17] = m17;
this[18] = m18; this[18] = m18;
} }
var gWeekday = new weekdayarr("Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur"), gMonth = new gregmontharr("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"), hMonth = new hebrewmontharr("Tishri", "Heshvan", "Kislev", "Tevet", "Shevat", "AdarI", "AdarII", "Nisan", "Iyyar", "Sivan", "Tammuz", "Av", "Elul"), mpy = new monthsperyeararr(12, 12, 13, 12, 12, 13, 12, 13, 12, 12, 13, 12, 12, 13, 12, 12, 13, 12, 13);
var gWeekday = new weekdayarr("Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur"),
gMonth = new gregmontharr("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"),
hMonth = new hebrewmontharr("Tishri", "Heshvan", "Kislev", "Tevet", "Shevat", "AdarI", "AdarII", "Nisan", "Iyyar", "Sivan", "Tammuz", "Av", "Elul"),
mpy = new monthsperyeararr(12, 12, 13, 12, 12, 13, 12, 13, 12, 12, 13, 12, 12, 13, 12, 12, 13, 12, 13);
/** /**
* hebrewDate * hebrewDate
* Convert the Gregorian dates into Hebrew calendar dates. * Convert the Gregorian dates into Hebrew calendar dates.
@ -124,55 +93,36 @@ var gWeekday = new weekdayarr("Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "S
* - `month_name`: The Hebrew month name. * - `month_name`: The Hebrew month name.
* - `date`: The Hebrew date. * - `date`: The Hebrew date.
*/ */
export const hebrewDate = function (inputDateOrYear: Date) { function hebrewDate(inputDateOrYear) {
var inputMonth, inputDate; var inputMonth, inputDate;
var hebrewMonth = 0, hebrewDate = 0, hebrewYear = 0, metonicCycle = 0, metonicYear = 0, moladDay = 0, moladHalakim = 0;
var hebrewMonth = 0,
hebrewDate = 0,
hebrewYear = 0,
metonicCycle = 0,
metonicYear = 0,
moladDay = 0,
moladHalakim = 0;
function GregorianToSdn(inputYear, inputMonth, inputDay) { function GregorianToSdn(inputYear, inputMonth, inputDay) {
var year = 0, month = 0, sdn = void 0;
var year = 0,
month = 0,
sdn = void 0;
// Make year a positive number // Make year a positive number
if (inputYear < 0) { if (inputYear < 0) {
year = inputYear + 4801; year = inputYear + 4801;
} else { }
else {
year = inputYear + 4800; year = inputYear + 4800;
} }
// Adjust the start of the year // Adjust the start of the year
if (inputMonth > 2) { if (inputMonth > 2) {
month = inputMonth - 3; month = inputMonth - 3;
} else { }
else {
month = inputMonth + 9; month = inputMonth + 9;
year--; year--;
} }
sdn = Math.floor(Math.floor(year / 100) * DAYS_PER_400_YEARS / 4); sdn = Math.floor(Math.floor(year / 100) * DAYS_PER_400_YEARS / 4);
sdn += Math.floor(year % 100 * DAYS_PER_4_YEARS / 4); sdn += Math.floor(year % 100 * DAYS_PER_4_YEARS / 4);
sdn += Math.floor((month * DAYS_PER_5_MONTHS + 2) / 5); sdn += Math.floor((month * DAYS_PER_5_MONTHS + 2) / 5);
sdn += inputDay - GREG_SDN_OFFSET; sdn += inputDay - GREG_SDN_OFFSET;
return sdn; return sdn;
} }
function SdnToHebrew(sdn) { function SdnToHebrew(sdn) {
var tishri1 = 0, var tishri1 = 0, tishri1After = 0, yearLength = 0, inputDay = sdn - HEB_SDN_OFFSET;
tishri1After = 0,
yearLength = 0,
inputDay = sdn - HEB_SDN_OFFSET;
FindTishriMolad(inputDay); FindTishriMolad(inputDay);
tishri1 = Tishri1(metonicYear, moladDay, moladHalakim); tishri1 = Tishri1(metonicYear, moladDay, moladHalakim);
if (inputDay >= tishri1) { if (inputDay >= tishri1) {
// It found Tishri 1 at the start of the year. // It found Tishri 1 at the start of the year.
hebrewYear = metonicCycle * 19 + metonicYear + 1; hebrewYear = metonicCycle * 19 + metonicYear + 1;
@ -180,7 +130,8 @@ export const hebrewDate = function (inputDateOrYear: Date) {
if (inputDay < tishri1 + 30) { if (inputDay < tishri1 + 30) {
hebrewMonth = 1; hebrewMonth = 1;
hebrewDate = inputDay - tishri1 + 1; hebrewDate = inputDay - tishri1 + 1;
} else { }
else {
hebrewMonth = 2; hebrewMonth = 2;
hebrewDate = inputDay - tishri1 - 29; hebrewDate = inputDay - tishri1 - 29;
} }
@ -191,7 +142,8 @@ export const hebrewDate = function (inputDateOrYear: Date) {
moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY); moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY);
moladHalakim = moladHalakim % HALAKIM_PER_DAY; moladHalakim = moladHalakim % HALAKIM_PER_DAY;
tishri1After = Tishri1((metonicYear + 1) % 19, moladDay, moladHalakim); tishri1After = Tishri1((metonicYear + 1) % 19, moladDay, moladHalakim);
} else { }
else {
// It found Tishri 1 at the end of the year. // It found Tishri 1 at the end of the year.
hebrewYear = metonicCycle * 19 + metonicYear; hebrewYear = metonicCycle * 19 + metonicYear;
if (inputDay >= tishri1 - 177) { if (inputDay >= tishri1 - 177) {
@ -199,44 +151,56 @@ export const hebrewDate = function (inputDateOrYear: Date) {
if (inputDay > tishri1 - 30) { if (inputDay > tishri1 - 30) {
hebrewMonth = 13; hebrewMonth = 13;
hebrewDate = inputDay - tishri1 + 30; hebrewDate = inputDay - tishri1 + 30;
} else if (inputDay > tishri1 - 60) { }
else if (inputDay > tishri1 - 60) {
hebrewMonth = 12; hebrewMonth = 12;
hebrewDate = inputDay - tishri1 + 60; hebrewDate = inputDay - tishri1 + 60;
} else if (inputDay > tishri1 - 89) { }
else if (inputDay > tishri1 - 89) {
hebrewMonth = 11; hebrewMonth = 11;
hebrewDate = inputDay - tishri1 + 89; hebrewDate = inputDay - tishri1 + 89;
} else if (inputDay > tishri1 - 119) { }
else if (inputDay > tishri1 - 119) {
hebrewMonth = 10; hebrewMonth = 10;
hebrewDate = inputDay - tishri1 + 119; hebrewDate = inputDay - tishri1 + 119;
} else if (inputDay > tishri1 - 148) { }
else if (inputDay > tishri1 - 148) {
hebrewMonth = 9; hebrewMonth = 9;
hebrewDate = inputDay - tishri1 + 148; hebrewDate = inputDay - tishri1 + 148;
} else { }
else {
hebrewMonth = 8; hebrewMonth = 8;
hebrewDate = inputDay - tishri1 + 178; hebrewDate = inputDay - tishri1 + 178;
} }
return; return;
} else { }
else {
if (mpy[(hebrewYear - 1) % 19] == 13) { if (mpy[(hebrewYear - 1) % 19] == 13) {
hebrewMonth = 7; hebrewMonth = 7;
hebrewDate = inputDay - tishri1 + 207; hebrewDate = inputDay - tishri1 + 207;
if (hebrewDate > 0) return; if (hebrewDate > 0)
return;
hebrewMonth--; hebrewMonth--;
hebrewDate += 30; hebrewDate += 30;
if (hebrewDate > 0) return; if (hebrewDate > 0)
hebrewMonth--; return;
hebrewDate += 30;
} else {
hebrewMonth = 6;
hebrewDate = inputDay - tishri1 + 207;
if (hebrewDate > 0) return;
hebrewMonth--; hebrewMonth--;
hebrewDate += 30; hebrewDate += 30;
} }
if (hebrewDate > 0) return; else {
hebrewMonth = 6;
hebrewDate = inputDay - tishri1 + 207;
if (hebrewDate > 0)
return;
hebrewMonth--;
hebrewDate += 30;
}
if (hebrewDate > 0)
return;
hebrewMonth--; hebrewMonth--;
hebrewDate += 29; hebrewDate += 29;
if (hebrewDate > 0) return; if (hebrewDate > 0)
return;
// We need the length of the year to figure this out,so find Tishri 1 of this year. // We need the length of the year to figure this out,so find Tishri 1 of this year.
tishri1After = tishri1; tishri1After = tishri1;
FindTishriMolad(moladDay - 365); FindTishriMolad(moladDay - 365);
@ -253,7 +217,8 @@ export const hebrewDate = function (inputDateOrYear: Date) {
return; return;
} }
moladDay -= 30; moladDay -= 30;
} else { }
else {
// Heshvan has 29 days // Heshvan has 29 days
if (moladDay <= 29) { if (moladDay <= 29) {
hebrewMonth = 2; hebrewMonth = 2;
@ -266,7 +231,6 @@ export const hebrewDate = function (inputDateOrYear: Date) {
hebrewMonth = 3; hebrewMonth = 3;
hebrewDate = moladDay; hebrewDate = moladDay;
} }
function FindTishriMolad(inputDay) { function FindTishriMolad(inputDay) {
// Estimate the metonic cycle number. Note that this may be an under // Estimate the metonic cycle number. Note that this may be an under
// estimate because there are 6939.6896 days in a metonic cycle not // estimate because there are 6939.6896 days in a metonic cycle not
@ -287,18 +251,15 @@ export const hebrewDate = function (inputDateOrYear: Date) {
} }
// Find the molad of Tishri closest to this date. // Find the molad of Tishri closest to this date.
for (metonicYear = 0; metonicYear < 18; metonicYear++) { for (metonicYear = 0; metonicYear < 18; metonicYear++) {
if (moladDay > inputDay - 74) break; if (moladDay > inputDay - 74)
break;
moladHalakim += HALAKIM_PER_LUNAR_CYCLE * mpy[metonicYear]; moladHalakim += HALAKIM_PER_LUNAR_CYCLE * mpy[metonicYear];
moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY); moladDay += Math.floor(moladHalakim / HALAKIM_PER_DAY);
moladHalakim = moladHalakim % HALAKIM_PER_DAY; moladHalakim = moladHalakim % HALAKIM_PER_DAY;
} }
} }
function MoladOfMetonicCycle() { function MoladOfMetonicCycle() {
var r1 = void 0, var r1 = void 0, r2 = void 0, d1 = void 0, d2 = void 0;
r2 = void 0,
d1 = void 0,
d2 = void 0;
// Start with the time of the first molad after creation. // Start with the time of the first molad after creation.
r1 = NEW_MOON_OF_CREATION; r1 = NEW_MOON_OF_CREATION;
// Calculate gMetonicCycle * HALAKIM_PER_METONIC_CYCLE. The upper 32 // Calculate gMetonicCycle * HALAKIM_PER_METONIC_CYCLE. The upper 32
@ -317,42 +278,34 @@ export const hebrewDate = function (inputDateOrYear: Date) {
moladDay = d2 << 16 | d1; moladDay = d2 << 16 | d1;
moladHalakim = r1; moladHalakim = r1;
} }
function Tishri1(metonicYear, moladDay, moladHalakim) { function Tishri1(metonicYear, moladDay, moladHalakim) {
var tishri1 = moladDay, var tishri1 = moladDay, dow = tishri1 % 7, leapYear = metonicYear == 2 || metonicYear == 5 || metonicYear == 7 || metonicYear == 10 || metonicYear == 13 || metonicYear == 16 || metonicYear == 18, lastWasLeapYear = metonicYear == 3 || metonicYear == 6 || metonicYear == 8 || metonicYear == 11 || metonicYear == 14 || metonicYear == 17 || metonicYear == 0;
dow = tishri1 % 7,
leapYear = metonicYear == 2 || metonicYear == 5 || metonicYear == 7 || metonicYear == 10 || metonicYear == 13 || metonicYear == 16 || metonicYear == 18,
lastWasLeapYear = metonicYear == 3 || metonicYear == 6 || metonicYear == 8 || metonicYear == 11 || metonicYear == 14 || metonicYear == 17 || metonicYear == 0;
// Apply rules 2,3 and 4 // Apply rules 2,3 and 4
if (moladHalakim >= NOON || !leapYear && dow == TUES && moladHalakim >= AM3_11_20 || lastWasLeapYear && dow == MON && moladHalakim >= AM9_32_43) { if (moladHalakim >= NOON || !leapYear && dow == TUES && moladHalakim >= AM3_11_20 || lastWasLeapYear && dow == MON && moladHalakim >= AM9_32_43) {
tishri1++; tishri1++;
dow++; dow++;
if (dow == 7) dow = 0; if (dow == 7)
dow = 0;
} }
// Apply rule 1 after the others because it can cause an additional delay of one day. // Apply rule 1 after the others because it can cause an additional delay of one day.
if (dow == WED || dow == FRI || dow == SUN) { if (dow == WED || dow == FRI || dow == SUN) {
tishri1++; tishri1++;
} }
return tishri1; return tishri1;
} }
var inputYear = inputDateOrYear;
var inputYear: Date | number = inputDateOrYear;
if ((typeof inputYear === "undefined" ? "undefined" : _typeof(inputYear)) === "object") { if ((typeof inputYear === "undefined" ? "undefined" : _typeof(inputYear)) === "object") {
inputMonth = inputDateOrYear.getMonth() + 1; inputMonth = inputDateOrYear.getMonth() + 1;
inputDate = inputDateOrYear.getDate(); inputDate = inputDateOrYear.getDate();
inputYear = inputDateOrYear.getFullYear(); inputYear = inputDateOrYear.getFullYear();
} }
SdnToHebrew(GregorianToSdn(inputYear, inputMonth, inputDate)); SdnToHebrew(GregorianToSdn(inputYear, inputMonth, inputDate));
return { return {
year: hebrewYear, year: hebrewYear,
month: hebrewMonth, month: hebrewMonth,
date: hebrewDate, date: hebrewDate,
month_name: hMonth[hebrewMonth - 1] month_name: hMonth[hebrewMonth - 1]
}; };
}; }
exports.hebrewDate = hebrewDate;

View File

@ -1,23 +0,0 @@
{
"name": "hebrew_calendar",
"version": "0.0.3",
"description": "Bangle.js app for seeing hebrew calendar",
"main": "app.js",
"types": "app.d.ts",
"scripts": {
"build": "rollup -c"
},
"author": {
"name": "Michael Salaverry",
"url": "https://github.com/barakplasma"
},
"license": "MIT",
"devDependencies": {
"@rollup/plugin-typescript": "^4.1.1",
"rollup": "^2.10.2",
"rollup-plugin-terser": "^5.3.0",
"terser": "^4.7.0",
"tslib": "^2.0.0",
"typescript": "^3.9.2"
}
}

View File

@ -1,15 +0,0 @@
import typescript from '@rollup/plugin-typescript';
import { terser } from 'rollup-plugin-terser';
export default {
input: './src/app.ts',
output: {
dir: '.',
format: 'iife',
name: 'hebrew_calendar'
},
plugins: [
typescript(),
terser(),
]
};

View File

@ -1,34 +0,0 @@
declare var Bangle: any;
declare var g: any;
declare var E: any;
declare var require: any;
g.clear();
let now = new Date();
import { hebrewDate } from "./hebrewDate";
let today = hebrewDate(now);
var mainmenu = {
"" : {
"title" : "Hebrew Date"
},
cal: {
value: require('locale').date(now,1),
onchange : () => {}
},
date: {
value : today.date,
onchange : () => {}
},
month: {
value : today.month_name,
onchange : () => {}
},
year: {
value : today.year,
onchange : () => {}
}
};
E.showMenu(mainmenu);

View File

@ -1,10 +0,0 @@
{
"compilerOptions": {
"module": "es2015",
"noImplicitAny": false,
"target": "es2015"
},
"include": [
"src"
]
}

View File

@ -40,7 +40,16 @@ const charFallbacks = {
"č":"c", "č":"c",
"ř":"r", "ř":"r",
"ő":"o", "ő":"o",
"ě":"e" "ě":"e",
"ę":"e",
"ą":"a",
"ó":"o",
"ż":"z",
"ź":"z",
"ń":"n",
"ł":"l",
"ś":"s",
"ć":"c",
}; };
/* /*
@ -603,6 +612,24 @@ var locales = {
day: "Domingo,Segunda-feira,Terça-feira,Quarta-feira,Quinta-feira,Sexta-feira,Sábado", day: "Domingo,Segunda-feira,Terça-feira,Quarta-feira,Quinta-feira,Sexta-feira,Sábado",
trans: { yes: "sim", Yes: "Sim", no: "não", No: "Não", ok: "ok", on: "on", off: "off" } trans: { yes: "sim", Yes: "Sim", no: "não", No: "Não", ok: "ok", on: "on", off: "off" }
}, },
"pl_PL": {
lang: "pl_PL",
decimal_point: ",",
thousands_sep: " ",
currency_symbol: "zł",
int_curr_symbol: "PLN",
speed: "kmh",
distance: { 0: "m", 1: "km" },
temperature: "°C",
ampm: { 0: "", 1: "" },
timePattern: { 0: "%HH:%MM:%SS", 1: "%HH:%MM" },
datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mar 2021 // 01.03.2021
abmonth: "Sty,Lut,Mar,Kwi,Maj,Cze,Lip,Sie,Wrz,Paź,Lis,Gru",
month: "Styczeń,Luty,Marzec,Kwiecień,Maj,Czerwiec,Lipiec,Sierpień,Wrzesień,Październik,Listopad,Grudzień",
abday: "Ndz,Pon,Wt,Śr,Czw,Pt,Sob",
day: "Niedziela,Poniedziałek,Wtorek,Środa,Czwartek,Piątek,Sobota",
trans: { yes: "tak", Yes: "Tak", no: "nie", No: "Nie", ok: "ok", on: "on", off: "off", "< Back": "< Wstecz" }
},
/*, /*,
"he_IL": { // This won't work until we get a font - see https://github.com/espruino/BangleApps/issues/399 "he_IL": { // This won't work until we get a font - see https://github.com/espruino/BangleApps/issues/399
codePage : "ISO8859-8", codePage : "ISO8859-8",

2
apps/pooqroman/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: New App!
0.02: Make internal menu time out + small fixes

View File

@ -1,3 +1,4 @@
/* -*- mode: Javascript; c-basic-offset: 2; indent-tabs-mode: nil; coding: latin-1 -*- */
// pooqRoman // pooqRoman
// //
// Copyright (c) 2021 Stephen P Spackman // Copyright (c) 2021 Stephen P Spackman
@ -54,8 +55,9 @@ class Options {
this.id = this.constructor.id; this.id = this.constructor.id;
this.file = `${this.id}.json`; this.file = `${this.id}.json`;
this.backing = storage.readJSON(this.file, true) || {}; this.backing = storage.readJSON(this.file, true) || {};
this.defaults = this.constructor.defaults; Object.setPrototypeOf(this.backing, this.constructor.defaults);
Object.keys(this.defaults).forEach(k => this.bless(k)); this.reactivator = _ => this.active();
Object.keys(this.constructor.defaults).forEach(k => this.bless(k));
} }
writeBack(delay) { writeBack(delay) {
@ -71,7 +73,7 @@ class Options {
bless(k) { bless(k) {
Object.defineProperty(this, k, { Object.defineProperty(this, k, {
get: () => this.backing[k] == null ? this.defaults[k] : this.backing[k], get: () => this.backing[k],
set: v => { set: v => {
this.backing[k] = v; this.backing[k] = v;
// Ten second writeback delay, since the user will roll values up and down. // Ten second writeback delay, since the user will roll values up and down.
@ -81,19 +83,31 @@ class Options {
} }
showMenu(m) { showMenu(m) {
if (m instanceof Function) m = m();
if (m) { if (m) {
for (const k in m) if ('init' in m[k]) m[k].value = m[k].init(); for (const k in m) if ('init' in m[k]) m[k].value = m[k].init();
m[''].selected = -1; // Workaround for self-selection bug. m[''].selected = -1; // Workaround for self-selection bug.
Bangle.on('drag', this.reactivator);
this.active();
} else {
if (this.bored) clearTimeout(this.bored);
this.bored = null;
Bangle.removeListener('drag', this.reactivator);
this.emit('done');
} }
g.clear(true);
E.showMenu(m); E.showMenu(m);
} }
reset() { active() {
this.backing = {}; if (this.bored) clearTimeout(this.bored);
this.writeBack(0); this.bored = setTimeout(_ => this.showMenu(), 15000);
} }
interact() {this.showMenu(this.menu);} reset() {
this.backing = {__proto__: this.constructor.defaults};
this.writeBack(0);
}
} }
class RomanOptions extends Options { class RomanOptions extends Options {
@ -101,7 +115,7 @@ class RomanOptions extends Options {
super(); super();
this.menu = { this.menu = {
'': {title: '* face options *'}, '': {title: '* face options *'},
'< Back': _ => {this.showMenu(); this.emit('done');}, '< Back': _ => this.showMenu(),
Ticks: { Ticks: {
init: _ => this.resolution, init: _ => this.resolution,
min: 0, max: 3, min: 0, max: 3,
@ -124,9 +138,11 @@ class RomanOptions extends Options {
onchange: x => this.calendric = x, onchange: x => this.calendric = x,
format: x => ['none', 'day', 'date'][x] format: x => ['none', 'day', 'date'][x]
}, },
Defaults: _ => {this.reset();} Defaults: _ => {this.reset(); this.interact();}
}; };
} }
interact() {this.showMenu(this.menu);}
} }
RomanOptions.id = 'pooqroman'; RomanOptions.id = 'pooqroman';
@ -147,7 +163,7 @@ RomanOptions.defaults = {
hubFg: g.theme.fg, hubFg: g.theme.fg,
alarmFg: '#f00', alarmFg: '#f00',
timerFg: '#0f0', timerFg: '#0f0',
active: g.theme.fg2, activeFg: g.theme.fg2,
}; };
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
@ -434,7 +450,7 @@ class Sidebar {
} }
static gpsColour(o) { static gpsColour(o) {
const fix = Bangle.getGPSFix(); const fix = Bangle.getGPSFix();
return fix && fix.fix ? o.active : o.barFg; return fix && fix.fix ? o.activeFg : o.barFg;
} }
doPower() { doPower() {
const c = Bangle.isCharging(); const c = Bangle.isCharging();
@ -455,7 +471,7 @@ class Sidebar {
if (Bangle.isCompassOn()) { if (Bangle.isCompassOn()) {
const c = Bangle.getCompass(); const c = Bangle.getCompass();
const a = c && this.rate <= 1000; const a = c && this.rate <= 1000;
this.g.setColor(a ? this.options.active : this.options.barFg).drawImage( this.g.setColor(a ? this.options.activeFg : this.options.barFg).drawImage(
compassI, compassI,
this.x + 4 + imageWidth(compassI) / 2, this.x + 4 + imageWidth(compassI) / 2,
this.y + 4 + imageHeight(compassI) / 2, this.y + 4 + imageHeight(compassI) / 2,
@ -470,7 +486,7 @@ class Sidebar {
class Roman { class Roman {
constructor(g, events) { constructor(g, events) {
this.g = g; this.g = g;
this.state = {}; this.state = null;
const options = this.options = new RomanOptions(); const options = this.options = new RomanOptions();
this.events = events.loadFromSystem(this.options); this.events = events.loadFromSystem(this.options);
this.timescales = [1000, [1000, 60000], 60000, 3600000]; this.timescales = [1000, [1000, 60000], 60000, 3600000];
@ -480,7 +496,7 @@ class Roman {
this.seconds = Roman.hand(g, 1, 0.9, 60, _ => options.secondFg); this.seconds = Roman.hand(g, 1, 0.9, 60, _ => options.secondFg);
} }
reset() {this.state = {}; this.g.clear(true);} reset() {this.state = null;}
doIcons(which) {this.state.iconsOk = null;} doIcons(which) {this.state.iconsOk = null;}
@ -544,7 +560,7 @@ class Roman {
render(d, rate) { render(d, rate) {
const g = this.g; const g = this.g;
const state = this.state; const state = this.state || (g.clear(true), this.state = {});
const options = this.options; const options = this.options;
const events = this.events; const events = this.events;
events.clean(d, -39600000); // 11h events.clean(d, -39600000); // 11h
@ -654,8 +670,8 @@ class Clock {
drag: e => { drag: e => {
if (this.t0) { if (this.t0) {
if (e.b) { if (e.b) {
e.x > this.xN && (this.xN = e.x) || e.x > this.xX && (this.xX = e.x); e.x < this.xN && (this.xN = e.x) || e.x > this.xX && (this.xX = e.x);
e.y > this.yN && (this.yN = e.y) || e.y > this.yX && (this.xY = e.y); e.y < this.yN && (this.yN = e.y) || e.y > this.yX && (this.yX = e.y);
} else if (this.xX - this.xN < 20) { } else if (this.xX - this.xN < 20) {
if (e.y - this.e0.y < -50) { if (e.y - this.e0.y < -50) {
this.options.resolution > 0 && this.options.resolution--; this.options.resolution > 0 && this.options.resolution--;
@ -697,6 +713,7 @@ class Clock {
this.exception && clearTimeout(this.exception); this.exception && clearTimeout(this.exception);
this.interval && clearInterval(this.interval); this.interval && clearInterval(this.interval);
this.timeout = this.exception = this.interval = this.rate = null; this.timeout = this.exception = this.interval = this.rate = null;
this.face.reset(); // Cancel any ongoing background rendering
return this; return this;
} }

View File

@ -2,3 +2,4 @@
0.02: Add widget 0.02: Add widget
0.03: Bangle.js 2 support 0.03: Bangle.js 2 support
0.04: Move Quiet Mode LCD options from global settings to this app 0.04: Move Quiet Mode LCD options from global settings to this app
0.05: Avoid immediately redrawing widgets on load

View File

@ -1,5 +1,8 @@
(function() {
WIDGETS["qmsched"] = { WIDGETS["qmsched"] = {
area: "tl", width: 24, draw: function() { area: "tl",
width: ((require("Storage").readJSON("setting.json", 1) || {}).quiet|0) ? 24 : 0,
draw: function() {
const mode = (require("Storage").readJSON("setting.json", 1) || {}).quiet|0; const mode = (require("Storage").readJSON("setting.json", 1) || {}).quiet|0;
if (mode===0) { // Off if (mode===0) { // Off
if (this.width!==0) { if (this.width!==0) {
@ -30,3 +33,4 @@ WIDGETS["qmsched"] = {
.drawLine(x+5, y-3, x+3, y-5).drawLine(x-5, y-3, x-3, y-5); // wriggles .drawLine(x+5, y-3, x+3, y-5).drawLine(x-5, y-3, x-3, y-5); // wriggles
}, },
}; };
})();

View File

@ -1,3 +1,4 @@
0.01: New App! 0.01: New App!
0.02: Add posibillity to generate Wifi code. 0.02: Add posibillity to generate Wifi code.
0.03: Forces integer scaling and adds more configuration (error correction, description, display) 0.03: Forces integer scaling and adds more configuration (error correction, description, display)
0.04: Allow scanning of QR codes from camera or file

View File

@ -3,14 +3,49 @@
<link rel="stylesheet" href="../../css/spectre.min.css"> <link rel="stylesheet" href="../../css/spectre.min.css">
</head> </head>
<body> <body>
<b>Datasource: </b></br>
<input type="radio" id="useTEXT" name="mode" checked/> <input type="radio" id="useTEXT" name="mode" checked/>
<label for="useTEXT">Use text (for example an URL):</label> <label for="useTEXT">Text</label></br>
<input type="text" id="text" class="form-input" value="www.espruino.com"> <input type="radio" id="useWIFI" name="mode"/>
<label for="useWIFI">Wifi Credentials</label></br>
<input type="radio" id="useFILE" name="mode"/>
<label for="useFILE">QR image</label></br>
<input type="radio" id="useCAM" name="mode"/>
<label for="useCAM">QR scan</label></br>
<hr> <hr>
<input type="radio" id="useWIFI" name="mode"/> <div id="srcText">
<label for="useWIFI">Use Wifi Credentials:</label> <p>Text/URL: <input type="text" id="text" class="form-input" value="http://www.espruino.com"></p>
<input type="text" id="ssid" class="form-input" value=""> </div>
<div id="srcScanCam">
<div>
<video id="qrVideo" align="center" width="50%"></video>
</div>
<div>
<select id="camList">
</select>
</div>
<div>
<button id="flashToggle">Flash: <span id="flashState">off</span></button>
</div>
<br>
Detected QR code:
<span id="camQrResult">None</span>
<br>
<button id="startButton" class="btn btn-primary">Start</button>
<button id="stopButton" class="btn btn-primary">Stop</button>
</div>
<div id="srcScanFile">
<input type="file" id="fileSelector">
<br>
Detected QR code:
<span id="fileQrResult">None</span>
</div>
<div id="srcWifi">
<p>Wifi name: <input type="text" id="ssid" class="form-input" value=""></p>
<p>Wifi password: <input type="password" id="password" class="form-input" value=""></p> <p>Wifi password: <input type="password" id="password" class="form-input" value=""></p>
<div class="form-group"> <div class="form-group">
<label for="encryption" class="control-label">Encryption</label> <label for="encryption" class="control-label">Encryption</label>
@ -21,11 +56,13 @@
<option value="nopass">None</option> <option value="nopass">None</option>
</select> </select>
</div> </div>
</div> </div>
<div> <div>
<input type="checkbox" id="hidden" name="hidden"/> <input type="checkbox" id="hidden" name="hidden"/>
<label for="hidden">Wifi is hidden</label> <label for="hidden">Wifi is hidden</label>
</div> </div>
</div>
<hr> <hr>
<p id="errors" style="color:Tomato;"></p> <p id="errors" style="color:Tomato;"></p>
@ -59,23 +96,73 @@
<script src="../../core/lib/qrcode.min.js"></script><!-- https://davidshimjs.github.io/qrcodejs/ --> <script src="../../core/lib/qrcode.min.js"></script><!-- https://davidshimjs.github.io/qrcodejs/ -->
<script src="../../core/lib/heatshrink.js"></script> <script src="../../core/lib/heatshrink.js"></script>
<script src="../../core/lib/imageconverter.js"></script> <script src="../../core/lib/imageconverter.js"></script>
<script src="./qr-scanner.umd.min.js"></script><!-- https://github.com/nimiq/qr-scanner -->
<script> <script>
var targetSize = 200; var targetSize = 200;
var deviceWidth = targetSize;
var deviceHeight = targetSize;
var border = 4;
var scanner = null;
var qrcode = null;
function onInit(device) { function onInit(device) {
console.info("onInit" + device);
if (device && device.info && device.info.g) { if (device && device.info && device.info.g) {
border = 4; deviceWidth = device.info.g.width;
targetSize = Math.min(device.info.g.width - border, device.info.g.height - border); deviceHeight = device.info.g.height;
} }
qrcode = new QRCode("qrcode", {
text: document.getElementById("text").value,
colorDark : "#000000",
colorLight : "#ffffff",
});
refreshQRCode(); refreshQRCode();
} }
const updateFlashAvailability = () => {
scanner.hasFlash().then(hasFlash => {
document.getElementById('flashToggle').style.display = hasFlash ? 'inline-block' : 'none';
});
};
function setResult(label, result) {
console.info("setResult " + result);
label.textContent = result;
scanner.stop();
refreshQRCode();
}
function initQrScanner() {
console.info("initQrScanner");
QrScanner.WORKER_PATH = './qr-scanner-worker.min.js';
if (scanner == null) {
scanner = new QrScanner(document.getElementById('qrVideo'), result => setResult(document.getElementById('camQrResult'), result), error => {
document.getElementById('camQrResult').textContent = error;
document.getElementById('camQrResult').style.color = 'inherit';
});
}
}
function initQrCam(){
scanner.start().then(() => {
updateFlashAvailability();
QrScanner.listCameras(true).then(cameras => cameras.forEach(camera => {
const option = document.createElement('option');
option.value = camera.id;
option.text = camera.label;
document.getElementById('camList').add(option);
}));
});
}
function toggleVis(id){
console.info("Got id", id);
["srcScanFile", "srcText", "srcWifi", "srcScanCam"].forEach(function (item){
document.getElementById(item).style.display = "none";
});
if (id != undefined && id != null) document.getElementById(id).style.display = "block";
refreshQRCode();
}
toggleVis("srcText");
//https://github.com/evgeni/qifi/blob/gh-pages/index.html#L168 //https://github.com/evgeni/qifi/blob/gh-pages/index.html#L168
function escapeString (string) { function escapeString (string) {
var to_escape = ['\\', ';', ',', ':', '"']; var to_escape = ['\\', ';', ',', ':', '"'];
@ -100,6 +187,13 @@
return qrstring; return qrstring;
} }
function refreshQRCode(){ function refreshQRCode(){
if (qrcode == null){
qrcode = new QRCode("qrcode", {
text: document.getElementById("text").value,
colorDark : "#000000",
colorLight : "#ffffff"
});
}
document.getElementById("errors").innerText=""; document.getElementById("errors").innerText="";
qrcode.clear(); // clear the code. qrcode.clear(); // clear the code.
var qrText = ""; var qrText = "";
@ -110,9 +204,17 @@
const hidden = document.getElementById("hidden").checked; const hidden = document.getElementById("hidden").checked;
const wifiString = generateWifiString(ssid, password, hidden, encryption); const wifiString = generateWifiString(ssid, password, hidden, encryption);
qrText= wifiString; qrText= wifiString;
} else if (document.getElementById("useCAM").checked) {
qrText= document.getElementById("camQrResult").innerText;
} else if (document.getElementById("useFILE").checked) {
qrText= document.getElementById("fileQrResult").innerText;
} else { } else {
qrText = document.getElementById("text").value; qrText = document.getElementById("text").value;
} }
console.info("Given qrtext was: " + qrText);
qrcode._htOption.text = qrText; qrcode._htOption.text = qrText;
qrcode._htOption.correctLevel = parseInt(document.getElementById("correction").value); qrcode._htOption.correctLevel = parseInt(document.getElementById("correction").value);
try { try {
@ -122,18 +224,24 @@
console.error(error); console.error(error);
} }
targetSize = Math.min(deviceWidth - border, deviceHeight - border);
console.info("Targetsize: " + targetSize);
var finalSizeQr=targetSize; var finalSizeQr=targetSize;
var finalSizeCanvas=targetSize; var finalSizeCanvas=targetSize;
var integerScale = Math.max(Math.floor(targetSize / (qrcode._oQRCode.moduleCount + 1)),1); var integerScale = Math.max(Math.floor(targetSize / (qrcode._oQRCode.moduleCount + 1)),1);
if (integerScale == 1) document.getElementById("errors").innerText = "Warning, QR will probably be too small to properly scan. Try less data or less error correction."; if (integerScale == 1) document.getElementById("errors").innerText = "Warning, QR will probably be too small to properly scan. Try less data or less error correction.";
if (!document.getElementById("preventIntegerScaling").checked){ console.info("IntegerScale: " + integerScale);
if (!document.getElementById("preventIntegerScaling").checked){
finalSizeQr = integerScale * (qrcode._oQRCode.moduleCount + 1); finalSizeQr = integerScale * (qrcode._oQRCode.moduleCount + 1);
finalSizeCanvas = finalSizeQr - 1; finalSizeCanvas = finalSizeQr - 1;
} }
console.info("FinalSizeQr: " + finalSizeQr);
console.info("FinalSizeCanvas: " + finalSizeCanvas);
qrcode._htOption.width = finalSizeQr; qrcode._htOption.width = finalSizeQr;
qrcode._htOption.height = finalSizeQr; qrcode._htOption.height = finalSizeQr;
@ -146,14 +254,26 @@
console.error(error); console.error(error);
} }
} }
var qrcode;
document.getElementById("useTEXT").addEventListener("change",function(){toggleVis("srcText");});
document.getElementById("useCAM").addEventListener("change",function(){
initQrScanner();
initQrCam();
toggleVis("srcScanCam");
});
document.getElementById("useFILE").addEventListener("change",function(){
initQrScanner();
toggleVis("srcScanFile");
});
document.getElementById("useWIFI").addEventListener("change",function(){toggleVis("srcWifi");});
document.getElementById("ssid").addEventListener("change",refreshQRCode); document.getElementById("ssid").addEventListener("change",refreshQRCode);
document.getElementById("text").addEventListener("change",refreshQRCode); document.getElementById("text").addEventListener("change",refreshQRCode);
document.getElementById("password").addEventListener("change",refreshQRCode); document.getElementById("password").addEventListener("change",refreshQRCode);
document.getElementById("encryption").addEventListener("change",refreshQRCode); document.getElementById("encryption").addEventListener("change",refreshQRCode);
document.getElementById("hidden").addEventListener("change",refreshQRCode); document.getElementById("hidden").addEventListener("change",refreshQRCode);
document.getElementById("useTEXT").addEventListener("change",refreshQRCode); document.getElementById("useTEXT").addEventListener("change",refreshQRCode);
document.getElementById("useCAM").addEventListener("change",refreshQRCode);
document.getElementById("useFILE").addEventListener("change",refreshQRCode);
document.getElementById("useWIFI").addEventListener("change",refreshQRCode); document.getElementById("useWIFI").addEventListener("change",refreshQRCode);
document.getElementById("preventIntegerScaling").addEventListener("change",refreshQRCode); document.getElementById("preventIntegerScaling").addEventListener("change",refreshQRCode);
document.getElementById("correction").addEventListener("change",refreshQRCode); document.getElementById("correction").addEventListener("change",refreshQRCode);
@ -179,13 +299,39 @@ g.drawString(content,g.getWidth()/2,g.getHeight()-(g.getHeight()-img[1])/4));
g.setColor(1,1,1); g.setColor(1,1,1);
`; `;
sendCustomizedApp({ sendCustomizedApp({
storage:[ storage:[{name:"qrcode.app.js", url:"app.js", content:app},]
{name:"qrcode.app.js", url:"app.js", content:app},
]
}); });
}); });
document.getElementById('camList').addEventListener('change', event => {
scanner.setCamera(event.target.value).then(updateFlashAvailability);
});
document.getElementById('flashToggle').addEventListener('click', () => {
scanner.toggleFlash().then(() => document.getElementById('flashState').textContent = scanner.isFlashOn() ? 'on' : 'off');
});
document.getElementById('startButton').addEventListener('click', () => {
scanner.start();
});
document.getElementById('stopButton').addEventListener('click', () => {
scanner.stop();
});
document.getElementById('fileSelector').addEventListener('change', event => {
const file = document.getElementById('fileSelector').files[0];
if (!file) {
return;
}
QrScanner.scanImage(file)
.then(result => setResult(document.getElementById('fileQrResult'), result))
.catch(e => setResult(document.getElementById('fileQrResult'), e || 'No QR code found.'));
});
</script> </script>
</body> </body>
</html> </html>

87
apps/qrcode/qr-scanner-worker.min.js vendored Normal file
View File

@ -0,0 +1,87 @@
'use strict';(function(){function T(a,b){let c=[],d="";b=a.readBits([8,16,16][b]);for(let d=0;d<b;d++){let b=a.readBits(8);c.push(b)}try{d+=decodeURIComponent(c.map(a=>`%${("0"+a.toString(16)).substr(-2)}`).join(""))}catch(e){}return{bytes:c,text:d}}function U(a,b){a=new V(a);let c=9>=b?0:26>=b?1:2;for(b={text:"",bytes:[],chunks:[],version:b};4<=a.available();){var d=a.readBits(4);if(d===t.Terminator)return b;if(d===t.ECI)0===a.readBits(1)?b.chunks.push({type:r.ECI,assignmentNumber:a.readBits(7)}):
0===a.readBits(1)?b.chunks.push({type:r.ECI,assignmentNumber:a.readBits(14)}):0===a.readBits(1)?b.chunks.push({type:r.ECI,assignmentNumber:a.readBits(21)}):b.chunks.push({type:r.ECI,assignmentNumber:-1});else if(d===t.Numeric){var e=a;d=[];for(var f="",g=e.readBits([10,12,14][c]);3<=g;){var h=e.readBits(10);if(1E3<=h)throw Error("Invalid numeric value above 999");var k=Math.floor(h/100),n=Math.floor(h/10)%10;h%=10;d.push(48+k,48+n,48+h);f+=k.toString()+n.toString()+h.toString();g-=3}if(2===g){g=e.readBits(7);
if(100<=g)throw Error("Invalid numeric value above 99");e=Math.floor(g/10);g%=10;d.push(48+e,48+g);f+=e.toString()+g.toString()}else if(1===g){e=e.readBits(4);if(10<=e)throw Error("Invalid numeric value above 9");d.push(48+e);f+=e.toString()}d={bytes:d,text:f};b.text+=d.text;b.bytes.push(...d.bytes);b.chunks.push({type:r.Numeric,text:d.text})}else if(d===t.Alphanumeric){e=a;d=[];f="";for(g=e.readBits([9,11,13][c]);2<=g;)n=e.readBits(11),k=Math.floor(n/45),n%=45,d.push(B[k].charCodeAt(0),B[n].charCodeAt(0)),
f+=B[k]+B[n],g-=2;1===g&&(e=e.readBits(6),d.push(B[e].charCodeAt(0)),f+=B[e]);d={bytes:d,text:f};b.text+=d.text;b.bytes.push(...d.bytes);b.chunks.push({type:r.Alphanumeric,text:d.text})}else if(d===t.Byte)d=T(a,c),b.text+=d.text,b.bytes.push(...d.bytes),b.chunks.push({type:r.Byte,bytes:d.bytes,text:d.text});else if(d===t.Kanji){f=a;d=[];e=f.readBits([8,10,12][c]);for(g=0;g<e;g++)k=f.readBits(13),k=Math.floor(k/192)<<8|k%192,k=7936>k?k+33088:k+49472,d.push(k>>8,k&255);f=(new TextDecoder("shift-jis")).decode(Uint8Array.from(d));
d={bytes:d,text:f};b.text+=d.text;b.bytes.push(...d.bytes);b.chunks.push({type:r.Kanji,bytes:d.bytes,text:d.text})}else d===t.StructuredAppend&&b.chunks.push({type:r.StructuredAppend,currentSequence:a.readBits(4),totalSequence:a.readBits(4),parity:a.readBits(8)})}if(0===a.available()||0===a.readBits(a.available()))return b}function J(a,b){return a^b}function W(a,b,c,d){b.degree()<c.degree()&&([b,c]=[c,b]);let e=a.zero;for(var f=a.one;c.degree()>=d/2;){var g=b;let d=e;b=c;e=f;if(b.isZero())return null;
c=g;f=a.zero;g=b.getCoefficient(b.degree());for(g=a.inverse(g);c.degree()>=b.degree()&&!c.isZero();){let d=c.degree()-b.degree(),e=a.multiply(c.getCoefficient(c.degree()),g);f=f.addOrSubtract(a.buildMonomial(d,e));c=c.addOrSubtract(b.multiplyByMonomial(d,e))}f=f.multiplyPoly(e).addOrSubtract(d);if(c.degree()>=b.degree())return null}d=f.getCoefficient(0);if(0===d)return null;a=a.inverse(d);return[f.multiply(a),c.multiply(a)]}function X(a,b){let c=new Uint8ClampedArray(a.length);c.set(a);a=new Y(285,
256,0);var d=new w(a,c),e=new Uint8ClampedArray(b),f=!1;for(var g=0;g<b;g++){var h=d.evaluateAt(a.exp(g+a.generatorBase));e[e.length-1-g]=h;0!==h&&(f=!0)}if(!f)return c;d=new w(a,e);d=W(a,a.buildMonomial(b,1),d,b);if(null===d)return null;b=d[0];g=b.degree();if(1===g)b=[b.getCoefficient(1)];else{e=Array(g);f=0;for(h=1;h<a.size&&f<g;h++)0===b.evaluateAt(h)&&(e[f]=a.inverse(h),f++);b=f!==g?null:e}if(null==b)return null;d=d[1];e=b.length;f=Array(e);for(g=0;g<e;g++){h=a.inverse(b[g]);let c=1;for(let d=
0;d<e;d++)g!==d&&(c=a.multiply(c,J(1,a.multiply(b[d],h))));f[g]=a.multiply(d.evaluateAt(h),a.inverse(c));0!==a.generatorBase&&(f[g]=a.multiply(f[g],h))}d=f;for(e=0;e<b.length;e++){f=c.length-1-a.log(b[e]);if(0>f)return null;c[f]^=d[e]}return c}function E(a,b){a^=b;for(b=0;a;)b++,a&=a-1;return b}function C(a,b){return b<<1|a}function Z(a,b,c){c=aa[c.dataMask];let d=a.height;var e=17+4*b.versionNumber,f=A.createEmpty(e,e);f.setRegion(0,0,9,9,!0);f.setRegion(e-8,0,8,9,!0);f.setRegion(0,e-8,9,8,!0);for(var g of b.alignmentPatternCenters)for(var h of b.alignmentPatternCenters)6===
g&&6===h||6===g&&h===e-7||g===e-7&&6===h||f.setRegion(g-2,h-2,5,5,!0);f.setRegion(6,9,1,e-17,!0);f.setRegion(9,6,e-17,1,!0);6<b.versionNumber&&(f.setRegion(e-11,0,3,6,!0),f.setRegion(0,e-11,6,3,!0));b=f;g=[];e=h=0;f=!0;for(let k=d-1;0<k;k-=2){6===k&&k--;for(let n=0;n<d;n++){let m=f?d-1-n:n;for(let d=0;2>d;d++){let f=k-d;if(!b.get(f,m)){e++;let b=a.get(f,m);c({y:m,x:f})&&(b=!b);h=h<<1|b;8===e&&(g.push(h),h=e=0)}}}f=!f}return g}function ba(a){var b=a.height,c=Math.floor((b-17)/4);if(6>=c)return K[c-
1];c=0;for(var d=5;0<=d;d--)for(var e=b-9;e>=b-11;e--)c=C(a.get(e,d),c);d=0;for(e=5;0<=e;e--)for(let c=b-9;c>=b-11;c--)d=C(a.get(e,c),d);a=Infinity;let f;for(let e of K){if(e.infoBits===c||e.infoBits===d)return e;b=E(c,e.infoBits);b<a&&(f=e,a=b);b=E(d,e.infoBits);b<a&&(f=e,a=b)}if(3>=a)return f}function ca(a){let b=0;for(var c=0;8>=c;c++)6!==c&&(b=C(a.get(c,8),b));for(c=7;0<=c;c--)6!==c&&(b=C(a.get(8,c),b));var d=a.height;c=0;for(var e=d-1;e>=d-7;e--)c=C(a.get(8,e),c);for(e=d-8;e<d;e++)c=C(a.get(e,
8),c);a=Infinity;d=null;for(let {bits:f,formatInfo:g}of da){if(f===b||f===c)return g;e=E(b,f);e<a&&(d=g,a=e);b!==c&&(e=E(c,f),e<a&&(d=g,a=e))}return 3>=a?d:null}function ea(a,b,c){let d=b.errorCorrectionLevels[c],e=[],f=0;d.ecBlocks.forEach(a=>{for(let b=0;b<a.numBlocks;b++)e.push({numDataCodewords:a.dataCodewordsPerBlock,codewords:[]}),f+=a.dataCodewordsPerBlock+d.ecCodewordsPerBlock});if(a.length<f)return null;a=a.slice(0,f);b=d.ecBlocks[0].dataCodewordsPerBlock;for(c=0;c<b;c++)for(var g of e)g.codewords.push(a.shift());
if(1<d.ecBlocks.length)for(g=d.ecBlocks[0].numBlocks,b=d.ecBlocks[1].numBlocks,c=0;c<b;c++)e[g+c].codewords.push(a.shift());for(;0<a.length;)for(let b of e)b.codewords.push(a.shift());return e}function L(a){let b=ba(a);if(!b)return null;var c=ca(a);if(!c)return null;a=Z(a,b,c);var d=ea(a,b,c.errorCorrectionLevel);if(!d)return null;c=d.reduce((a,b)=>a+b.numDataCodewords,0);c=new Uint8ClampedArray(c);a=0;for(let b of d){d=X(b.codewords,b.codewords.length-b.numDataCodewords);if(!d)return null;for(let e=
0;e<b.numDataCodewords;e++)c[a++]=d[e]}try{return U(c,b.versionNumber)}catch(e){return null}}function M(a,b,c,d){var e=a.x-b.x+c.x-d.x;let f=a.y-b.y+c.y-d.y;if(0===e&&0===f)return{a11:b.x-a.x,a12:b.y-a.y,a13:0,a21:c.x-b.x,a22:c.y-b.y,a23:0,a31:a.x,a32:a.y,a33:1};{let h=b.x-c.x;var g=d.x-c.x;let k=b.y-c.y,n=d.y-c.y;c=h*n-g*k;g=(e*n-g*f)/c;e=(h*f-e*k)/c;return{a11:b.x-a.x+g*b.x,a12:b.y-a.y+g*b.y,a13:g,a21:d.x-a.x+e*d.x,a22:d.y-a.y+e*d.y,a23:e,a31:a.x,a32:a.y,a33:1}}}function fa(a,b,c,d){a=M(a,b,c,d);
return{a11:a.a22*a.a33-a.a23*a.a32,a12:a.a13*a.a32-a.a12*a.a33,a13:a.a12*a.a23-a.a13*a.a22,a21:a.a23*a.a31-a.a21*a.a33,a22:a.a11*a.a33-a.a13*a.a31,a23:a.a13*a.a21-a.a11*a.a23,a31:a.a21*a.a32-a.a22*a.a31,a32:a.a12*a.a31-a.a11*a.a32,a33:a.a11*a.a22-a.a12*a.a21}}function ha(a,b){var c=fa({x:3.5,y:3.5},{x:b.dimension-3.5,y:3.5},{x:b.dimension-6.5,y:b.dimension-6.5},{x:3.5,y:b.dimension-3.5}),d=M(b.topLeft,b.topRight,b.alignmentPattern,b.bottomLeft),e=d.a11*c.a11+d.a21*c.a12+d.a31*c.a13,f=d.a12*c.a11+
d.a22*c.a12+d.a32*c.a13,g=d.a13*c.a11+d.a23*c.a12+d.a33*c.a13,h=d.a11*c.a21+d.a21*c.a22+d.a31*c.a23,k=d.a12*c.a21+d.a22*c.a22+d.a32*c.a23,n=d.a13*c.a21+d.a23*c.a22+d.a33*c.a23,m=d.a11*c.a31+d.a21*c.a32+d.a31*c.a33,l=d.a12*c.a31+d.a22*c.a32+d.a32*c.a33,p=d.a13*c.a31+d.a23*c.a32+d.a33*c.a33;c=A.createEmpty(b.dimension,b.dimension);d=(a,b)=>{const c=g*a+n*b+p;return{x:(e*a+h*b+m)/c,y:(f*a+k*b+l)/c}};for(let e=0;e<b.dimension;e++)for(let f=0;f<b.dimension;f++){let b=d(f+.5,e+.5);c.set(f,e,a.get(Math.floor(b.x),
Math.floor(b.y)))}return{matrix:c,mappingFunction:d}}function x(a){return a.reduce((a,c)=>a+c)}function ia(a,b,c){let d=y(a,b),e=y(b,c),f=y(a,c),g,h,k;e>=d&&e>=f?[g,h,k]=[b,a,c]:f>=e&&f>=d?[g,h,k]=[a,b,c]:[g,h,k]=[a,c,b];0>(k.x-h.x)*(g.y-h.y)-(k.y-h.y)*(g.x-h.x)&&([g,k]=[k,g]);return{bottomLeft:g,topLeft:h,topRight:k}}function ja(a,b,c,d){d=(x(z(a,c,d,5))/7+x(z(a,b,d,5))/7+x(z(c,a,d,5))/7+x(z(b,a,d,5))/7)/4;if(1>d)throw Error("Invalid module size");b=Math.round(y(a,b)/d);a=Math.round(y(a,c)/d);a=
Math.floor((b+a)/2)+7;switch(a%4){case 0:a++;break;case 2:a--}return{dimension:a,moduleSize:d}}function N(a,b,c,d){let e=[{x:Math.floor(a.x),y:Math.floor(a.y)}];var f=Math.abs(b.y-a.y)>Math.abs(b.x-a.x);if(f){var g=Math.floor(a.y);var h=Math.floor(a.x);a=Math.floor(b.y);b=Math.floor(b.x)}else g=Math.floor(a.x),h=Math.floor(a.y),a=Math.floor(b.x),b=Math.floor(b.y);let k=Math.abs(a-g),n=Math.abs(b-h),m=Math.floor(-k/2),l=g<a?1:-1,p=h<b?1:-1,q=!0;for(let v=g,u=h;v!==a+l;v+=l){g=f?u:v;h=f?v:u;if(c.get(g,
h)!==q&&(q=!q,e.push({x:g,y:h}),e.length===d+1))break;m+=n;if(0<m){if(u===b)break;u+=p;m-=k}}c=[];for(f=0;f<d;f++)e[f]&&e[f+1]?c.push(y(e[f],e[f+1])):c.push(0);return c}function z(a,b,c,d){let e=b.y-a.y,f=b.x-a.x;b=N(a,b,c,Math.ceil(d/2));a=N(a,{x:a.x-f,y:a.y-e},c,Math.ceil(d/2));c=b.shift()+a.shift()-1;return a.concat(c).concat(...b)}function F(a,b){let c=x(a)/x(b),d=0;b.forEach((b,f)=>{d+=Math.pow(a[f]-b*c,2)});return{averageSize:c,error:d}}function O(a,b,c){try{let d=z(a,{x:-1,y:a.y},c,b.length),
e=z(a,{x:a.x,y:-1},c,b.length),f=z(a,{x:Math.max(0,a.x-a.y)-1,y:Math.max(0,a.y-a.x)-1},c,b.length),g=z(a,{x:Math.min(c.width,a.x+a.y)+1,y:Math.min(c.height,a.y+a.x)+1},c,b.length),h=F(d,b),k=F(e,b),n=F(f,b),m=F(g,b),l=(h.averageSize+k.averageSize+n.averageSize+m.averageSize)/4;return Math.sqrt(h.error*h.error+k.error*k.error+n.error*n.error+m.error*m.error)+(Math.pow(h.averageSize-l,2)+Math.pow(k.averageSize-l,2)+Math.pow(n.averageSize-l,2)+Math.pow(m.averageSize-l,2))/l}catch(d){return Infinity}}
function H(a,b){for(var c=Math.round(b.x);a.get(c,Math.round(b.y));)c--;for(var d=Math.round(b.x);a.get(d,Math.round(b.y));)d++;c=(c+d)/2;for(d=Math.round(b.y);a.get(Math.round(c),d);)d--;for(b=Math.round(b.y);a.get(Math.round(c),b);)b++;return{x:c,y:(d+b)/2}}function ka(a){var b=[],c=[];let d=[];var e=[];for(let m=0;m<=a.height;m++){var f=0,g=!1;let l=[0,0,0,0,0];for(let b=-1;b<=a.width;b++){var h=a.get(b,m);if(h===g)f++;else{l=[l[1],l[2],l[3],l[4],f];f=1;g=h;var k=x(l)/7;k=Math.abs(l[0]-k)<k&&Math.abs(l[1]-
k)<k&&Math.abs(l[2]-3*k)<3*k&&Math.abs(l[3]-k)<k&&Math.abs(l[4]-k)<k&&!h;var n=x(l.slice(-3))/3;h=Math.abs(l[2]-n)<n&&Math.abs(l[3]-n)<n&&Math.abs(l[4]-n)<n&&h;if(k){let a=b-l[3]-l[4],d=a-l[2];k={startX:d,endX:a,y:m};n=c.filter(b=>d>=b.bottom.startX&&d<=b.bottom.endX||a>=b.bottom.startX&&d<=b.bottom.endX||d<=b.bottom.startX&&a>=b.bottom.endX&&1.5>l[2]/(b.bottom.endX-b.bottom.startX)&&.5<l[2]/(b.bottom.endX-b.bottom.startX));0<n.length?n[0].bottom=k:c.push({top:k,bottom:k})}if(h){let a=b-l[4],c=a-
l[3];h={startX:c,y:m,endX:a};k=e.filter(b=>c>=b.bottom.startX&&c<=b.bottom.endX||a>=b.bottom.startX&&c<=b.bottom.endX||c<=b.bottom.startX&&a>=b.bottom.endX&&1.5>l[2]/(b.bottom.endX-b.bottom.startX)&&.5<l[2]/(b.bottom.endX-b.bottom.startX));0<k.length?k[0].bottom=h:e.push({top:h,bottom:h})}}}b.push(...c.filter(a=>a.bottom.y!==m&&2<=a.bottom.y-a.top.y));c=c.filter(a=>a.bottom.y===m);d.push(...e.filter(a=>a.bottom.y!==m));e=e.filter(a=>a.bottom.y===m)}b.push(...c.filter(a=>2<=a.bottom.y-a.top.y));d.push(...e);
c=[];for(var m of b)2>m.bottom.y-m.top.y||(b=(m.top.startX+m.top.endX+m.bottom.startX+m.bottom.endX)/4,e=(m.top.y+m.bottom.y+1)/2,a.get(Math.round(b),Math.round(e))&&(f=[m.top.endX-m.top.startX,m.bottom.endX-m.bottom.startX,m.bottom.y-m.top.y+1],f=x(f)/f.length,g=O({x:Math.round(b),y:Math.round(e)},[1,1,3,1,1],a),c.push({score:g,x:b,y:e,size:f})));if(3>c.length)return null;c.sort((a,b)=>a.score-b.score);m=[];for(b=0;b<Math.min(c.length,5);++b){e=c[b];f=[];for(var l of c)l!==e&&f.push(Object.assign(Object.assign({},
l),{score:l.score+Math.pow(l.size-e.size,2)/e.size}));f.sort((a,b)=>a.score-b.score);m.push({points:[e,f[0],f[1]],score:e.score+f[0].score+f[1].score})}m.sort((a,b)=>a.score-b.score);let {topRight:p,topLeft:q,bottomLeft:v}=ia(...m[0].points);m=P(a,d,p,q,v);l=[];m&&l.push({alignmentPattern:{x:m.alignmentPattern.x,y:m.alignmentPattern.y},bottomLeft:{x:v.x,y:v.y},dimension:m.dimension,topLeft:{x:q.x,y:q.y},topRight:{x:p.x,y:p.y}});m=H(a,p);b=H(a,q);c=H(a,v);(a=P(a,d,m,b,c))&&l.push({alignmentPattern:{x:a.alignmentPattern.x,
y:a.alignmentPattern.y},bottomLeft:{x:c.x,y:c.y},topLeft:{x:b.x,y:b.y},topRight:{x:m.x,y:m.y},dimension:a.dimension});return 0===l.length?null:l}function P(a,b,c,d,e){let f,g;try{({dimension:f,moduleSize:g}=ja(d,c,e,a))}catch(m){return null}var h=c.x-d.x+e.x,k=c.y-d.y+e.y;c=(y(d,e)+y(d,c))/2/g;e=1-3/c;let n={x:d.x+e*(h-d.x),y:d.y+e*(k-d.y)};b=b.map(b=>{const c=(b.top.startX+b.top.endX+b.bottom.startX+b.bottom.endX)/4;b=(b.top.y+b.bottom.y+1)/2;if(a.get(Math.floor(c),Math.floor(b))){var d=O({x:Math.floor(c),
y:Math.floor(b)},[1,1,1],a)+y({x:c,y:b},n);return{x:c,y:b,score:d}}}).filter(a=>!!a).sort((a,b)=>a.score-b.score);return{alignmentPattern:15<=c&&b.length?b[0]:n,dimension:f}}function Q(a){var b=ka(a);if(!b)return null;for(let e of b){b=ha(a,e);var c=b.matrix;if(null==c)c=null;else{var d=L(c);if(d)c=d;else{for(d=0;d<c.width;d++)for(let a=d+1;a<c.height;a++)c.get(d,a)!==c.get(a,d)&&(c.set(d,a,!c.get(d,a)),c.set(a,d,!c.get(a,d)));c=L(c)}}if(c)return{binaryData:c.bytes,data:c.text,chunks:c.chunks,version:c.version,
location:{topRightCorner:b.mappingFunction(e.dimension,0),topLeftCorner:b.mappingFunction(0,0),bottomRightCorner:b.mappingFunction(e.dimension,e.dimension),bottomLeftCorner:b.mappingFunction(0,e.dimension),topRightFinderPattern:e.topRight,topLeftFinderPattern:e.topLeft,bottomLeftFinderPattern:e.bottomLeft,bottomRightAlignmentPattern:e.alignmentPattern},matrix:b.matrix}}return null}function R(a,b){Object.keys(b).forEach(c=>{a[c]=b[c]})}function I(a,b,c,d={}){let e=Object.create(null);R(e,la);R(e,d);
d="onlyInvert"===e.inversionAttempts||"invertFirst"===e.inversionAttempts;var f="attemptBoth"===e.inversionAttempts||d;var g=e.greyScaleWeights,h=e.canOverwriteImage,k=b*c;if(a.length!==4*k)throw Error("Malformed data passed to binarizer.");var n=0;if(h){var m=new Uint8ClampedArray(a.buffer,n,k);n+=k}m=new S(b,c,m);if(g.useIntegerApproximation)for(var l=0;l<c;l++)for(var p=0;p<b;p++){var q=4*(l*b+p);m.set(p,l,g.red*a[q]+g.green*a[q+1]+g.blue*a[q+2]+128>>8)}else for(l=0;l<c;l++)for(p=0;p<b;p++)q=4*
(l*b+p),m.set(p,l,g.red*a[q]+g.green*a[q+1]+g.blue*a[q+2]);g=Math.ceil(b/8);l=Math.ceil(c/8);p=g*l;if(h){var v=new Uint8ClampedArray(a.buffer,n,p);n+=p}v=new S(g,l,v);for(p=0;p<l;p++)for(q=0;q<g;q++){var u=Infinity,r=0;for(var t=0;8>t;t++)for(let a=0;8>a;a++){let b=m.get(8*q+a,8*p+t);u=Math.min(u,b);r=Math.max(r,b)}t=(u+r)/2;t=Math.min(255,1.11*t);24>=r-u&&(t=u/2,0<p&&0<q&&(r=(v.get(q,p-1)+2*v.get(q-1,p)+v.get(q-1,p-1))/4,u<r&&(t=r)));v.set(q,p,t)}h?(p=new Uint8ClampedArray(a.buffer,n,k),n+=k,p=new A(p,
b)):p=A.createEmpty(b,c);q=null;f&&(h?(a=new Uint8ClampedArray(a.buffer,n,k),q=new A(a,b)):q=A.createEmpty(b,c));for(b=0;b<l;b++)for(a=0;a<g;a++){c=g-3;c=2>a?2:a>c?c:a;h=l-3;h=2>b?2:b>h?h:b;k=0;for(n=-2;2>=n;n++)for(u=-2;2>=u;u++)k+=v.get(c+n,h+u);c=k/25;for(h=0;8>h;h++)for(k=0;8>k;k++)n=8*a+h,u=8*b+k,r=m.get(n,u),p.set(n,u,r<=c),f&&q.set(n,u,!(r<=c))}f=f?{binarized:p,inverted:q}:{binarized:p};let {binarized:w,inverted:x}=f;(f=Q(d?x:w))||"attemptBoth"!==e.inversionAttempts&&"invertFirst"!==e.inversionAttempts||
(f=Q(d?w:x));return f}class A{constructor(a,b){this.width=b;this.height=a.length/b;this.data=a}static createEmpty(a,b){return new A(new Uint8ClampedArray(a*b),a)}get(a,b){return 0>a||a>=this.width||0>b||b>=this.height?!1:!!this.data[b*this.width+a]}set(a,b,c){this.data[b*this.width+a]=c?1:0}setRegion(a,b,c,d,e){for(let f=b;f<b+d;f++)for(let b=a;b<a+c;b++)this.set(b,f,!!e)}}class S{constructor(a,b,c){this.width=a;a*=b;if(c&&c.length!==a)throw Error("Wrong buffer size");this.data=c||new Uint8ClampedArray(a)}get(a,
b){return this.data[b*this.width+a]}set(a,b,c){this.data[b*this.width+a]=c}}class V{constructor(a){this.bitOffset=this.byteOffset=0;this.bytes=a}readBits(a){if(1>a||32<a||a>this.available())throw Error("Cannot read "+a.toString()+" bits");var b=0;if(0<this.bitOffset){b=8-this.bitOffset;var c=a<b?a:b;b-=c;b=(this.bytes[this.byteOffset]&255>>8-c<<b)>>b;a-=c;this.bitOffset+=c;8===this.bitOffset&&(this.bitOffset=0,this.byteOffset++)}if(0<a){for(;8<=a;)b=b<<8|this.bytes[this.byteOffset]&255,this.byteOffset++,
a-=8;0<a&&(c=8-a,b=b<<a|(this.bytes[this.byteOffset]&255>>c<<c)>>c,this.bitOffset+=a)}return b}available(){return 8*(this.bytes.length-this.byteOffset)-this.bitOffset}}var r;(function(a){a.Numeric="numeric";a.Alphanumeric="alphanumeric";a.Byte="byte";a.Kanji="kanji";a.ECI="eci";a.StructuredAppend="structuredappend"})(r||(r={}));var t;(function(a){a[a.Terminator=0]="Terminator";a[a.Numeric=1]="Numeric";a[a.Alphanumeric=2]="Alphanumeric";a[a.Byte=4]="Byte";a[a.Kanji=8]="Kanji";a[a.ECI=7]="ECI";a[a.StructuredAppend=
3]="StructuredAppend"})(t||(t={}));let B="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".split("");class w{constructor(a,b){if(0===b.length)throw Error("No coefficients.");this.field=a;let c=b.length;if(1<c&&0===b[0]){let d=1;for(;d<c&&0===b[d];)d++;if(d===c)this.coefficients=a.zero.coefficients;else for(this.coefficients=new Uint8ClampedArray(c-d),a=0;a<this.coefficients.length;a++)this.coefficients[a]=b[d+a]}else this.coefficients=b}degree(){return this.coefficients.length-1}isZero(){return 0===
this.coefficients[0]}getCoefficient(a){return this.coefficients[this.coefficients.length-1-a]}addOrSubtract(a){if(this.isZero())return a;if(a.isZero())return this;let b=this.coefficients;a=a.coefficients;b.length>a.length&&([b,a]=[a,b]);let c=new Uint8ClampedArray(a.length),d=a.length-b.length;for(var e=0;e<d;e++)c[e]=a[e];for(e=d;e<a.length;e++)c[e]=b[e-d]^a[e];return new w(this.field,c)}multiply(a){if(0===a)return this.field.zero;if(1===a)return this;let b=this.coefficients.length,c=new Uint8ClampedArray(b);
for(let d=0;d<b;d++)c[d]=this.field.multiply(this.coefficients[d],a);return new w(this.field,c)}multiplyPoly(a){if(this.isZero()||a.isZero())return this.field.zero;let b=this.coefficients,c=b.length;a=a.coefficients;let d=a.length,e=new Uint8ClampedArray(c+d-1);for(let h=0;h<c;h++){let c=b[h];for(let b=0;b<d;b++){var f=h+b,g=this.field.multiply(c,a[b]);e[f]=e[h+b]^g}}return new w(this.field,e)}multiplyByMonomial(a,b){if(0>a)throw Error("Invalid degree less than 0");if(0===b)return this.field.zero;
let c=this.coefficients.length;a=new Uint8ClampedArray(c+a);for(let d=0;d<c;d++)a[d]=this.field.multiply(this.coefficients[d],b);return new w(this.field,a)}evaluateAt(a){let b=0;if(0===a)return this.getCoefficient(0);let c=this.coefficients.length;if(1===a)return this.coefficients.forEach(a=>{b^=a}),b;b=this.coefficients[0];for(let d=1;d<c;d++)b=J(this.field.multiply(a,b),this.coefficients[d]);return b}}class Y{constructor(a,b,c){this.primitive=a;this.size=b;this.generatorBase=c;this.expTable=Array(this.size);
this.logTable=Array(this.size);a=1;for(b=0;b<this.size;b++)this.expTable[b]=a,a*=2,a>=this.size&&(a=(a^this.primitive)&this.size-1);for(a=0;a<this.size-1;a++)this.logTable[this.expTable[a]]=a;this.zero=new w(this,Uint8ClampedArray.from([0]));this.one=new w(this,Uint8ClampedArray.from([1]))}multiply(a,b){return 0===a||0===b?0:this.expTable[(this.logTable[a]+this.logTable[b])%(this.size-1)]}inverse(a){if(0===a)throw Error("Can't invert 0");return this.expTable[this.size-this.logTable[a]-1]}buildMonomial(a,
b){if(0>a)throw Error("Invalid monomial degree less than 0");if(0===b)return this.zero;a=new Uint8ClampedArray(a+1);a[0]=b;return new w(this,a)}log(a){if(0===a)throw Error("Can't take log(0)");return this.logTable[a]}exp(a){return this.expTable[a]}}let K=[{infoBits:null,versionNumber:1,alignmentPatternCenters:[],errorCorrectionLevels:[{ecCodewordsPerBlock:7,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:10,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:16}]},{ecCodewordsPerBlock:13,
ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:13}]},{ecCodewordsPerBlock:17,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:9}]}]},{infoBits:null,versionNumber:2,alignmentPatternCenters:[6,18],errorCorrectionLevels:[{ecCodewordsPerBlock:10,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:34}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:28}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:22}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:16}]}]},
{infoBits:null,versionNumber:3,alignmentPatternCenters:[6,22],errorCorrectionLevels:[{ecCodewordsPerBlock:15,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:55}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:13}]}]},{infoBits:null,versionNumber:4,alignmentPatternCenters:[6,26],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:1,
dataCodewordsPerBlock:80}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:32}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:9}]}]},{infoBits:null,versionNumber:5,alignmentPatternCenters:[6,30],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:43}]},{ecCodewordsPerBlock:18,
ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:15},{numBlocks:2,dataCodewordsPerBlock:16}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:11},{numBlocks:2,dataCodewordsPerBlock:12}]}]},{infoBits:null,versionNumber:6,alignmentPatternCenters:[6,34],errorCorrectionLevels:[{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:68}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:27}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:19}]},
{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:15}]}]},{infoBits:31892,versionNumber:7,alignmentPatternCenters:[6,22,38],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:78}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:31}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:4,dataCodewordsPerBlock:15}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:13},
{numBlocks:1,dataCodewordsPerBlock:14}]}]},{infoBits:34236,versionNumber:8,alignmentPatternCenters:[6,24,42],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:97}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:38},{numBlocks:2,dataCodewordsPerBlock:39}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:18},{numBlocks:2,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:14},
{numBlocks:2,dataCodewordsPerBlock:15}]}]},{infoBits:39577,versionNumber:9,alignmentPatternCenters:[6,26,46],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:36},{numBlocks:2,dataCodewordsPerBlock:37}]},{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:16},{numBlocks:4,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:12},
{numBlocks:4,dataCodewordsPerBlock:13}]}]},{infoBits:42195,versionNumber:10,alignmentPatternCenters:[6,28,50],errorCorrectionLevels:[{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:68},{numBlocks:2,dataCodewordsPerBlock:69}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:43},{numBlocks:1,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:19},{numBlocks:2,dataCodewordsPerBlock:20}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,
dataCodewordsPerBlock:15},{numBlocks:2,dataCodewordsPerBlock:16}]}]},{infoBits:48118,versionNumber:11,alignmentPatternCenters:[6,30,54],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:81}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:50},{numBlocks:4,dataCodewordsPerBlock:51}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:22},{numBlocks:4,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:3,
dataCodewordsPerBlock:12},{numBlocks:8,dataCodewordsPerBlock:13}]}]},{infoBits:51042,versionNumber:12,alignmentPatternCenters:[6,32,58],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:92},{numBlocks:2,dataCodewordsPerBlock:93}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:36},{numBlocks:2,dataCodewordsPerBlock:37}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:20},{numBlocks:6,dataCodewordsPerBlock:21}]},
{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:14},{numBlocks:4,dataCodewordsPerBlock:15}]}]},{infoBits:55367,versionNumber:13,alignmentPatternCenters:[6,34,62],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:107}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:37},{numBlocks:1,dataCodewordsPerBlock:38}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:20},{numBlocks:4,dataCodewordsPerBlock:21}]},
{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:11},{numBlocks:4,dataCodewordsPerBlock:12}]}]},{infoBits:58893,versionNumber:14,alignmentPatternCenters:[6,26,46,66],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:115},{numBlocks:1,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:40},{numBlocks:5,dataCodewordsPerBlock:41}]},{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:16},
{numBlocks:5,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:12},{numBlocks:5,dataCodewordsPerBlock:13}]}]},{infoBits:63784,versionNumber:15,alignmentPatternCenters:[6,26,48,70],errorCorrectionLevels:[{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:87},{numBlocks:1,dataCodewordsPerBlock:88}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:41},{numBlocks:5,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:30,
ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:24},{numBlocks:7,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:12},{numBlocks:7,dataCodewordsPerBlock:13}]}]},{infoBits:68472,versionNumber:16,alignmentPatternCenters:[6,26,50,74],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:98},{numBlocks:1,dataCodewordsPerBlock:99}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:45},{numBlocks:3,dataCodewordsPerBlock:46}]},
{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:19},{numBlocks:2,dataCodewordsPerBlock:20}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:15},{numBlocks:13,dataCodewordsPerBlock:16}]}]},{infoBits:70749,versionNumber:17,alignmentPatternCenters:[6,30,54,78],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:107},{numBlocks:5,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:46},
{numBlocks:1,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:22},{numBlocks:15,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:17,dataCodewordsPerBlock:15}]}]},{infoBits:76311,versionNumber:18,alignmentPatternCenters:[6,30,56,82],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:120},{numBlocks:1,dataCodewordsPerBlock:121}]},{ecCodewordsPerBlock:26,
ecBlocks:[{numBlocks:9,dataCodewordsPerBlock:43},{numBlocks:4,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:22},{numBlocks:1,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:19,dataCodewordsPerBlock:15}]}]},{infoBits:79154,versionNumber:19,alignmentPatternCenters:[6,30,58,86],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:113},{numBlocks:4,
dataCodewordsPerBlock:114}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:44},{numBlocks:11,dataCodewordsPerBlock:45}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:21},{numBlocks:4,dataCodewordsPerBlock:22}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:9,dataCodewordsPerBlock:13},{numBlocks:16,dataCodewordsPerBlock:14}]}]},{infoBits:84390,versionNumber:20,alignmentPatternCenters:[6,34,62,90],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,
dataCodewordsPerBlock:107},{numBlocks:5,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:41},{numBlocks:13,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:24},{numBlocks:5,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:15},{numBlocks:10,dataCodewordsPerBlock:16}]}]},{infoBits:87683,versionNumber:21,alignmentPatternCenters:[6,28,50,72,94],errorCorrectionLevels:[{ecCodewordsPerBlock:28,
ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:116},{numBlocks:4,dataCodewordsPerBlock:117}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:22},{numBlocks:6,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:16},{numBlocks:6,dataCodewordsPerBlock:17}]}]},{infoBits:92361,versionNumber:22,alignmentPatternCenters:[6,26,50,74,98],errorCorrectionLevels:[{ecCodewordsPerBlock:28,
ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:111},{numBlocks:7,dataCodewordsPerBlock:112}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:24},{numBlocks:16,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:34,dataCodewordsPerBlock:13}]}]},{infoBits:96236,versionNumber:23,alignmentPatternCenters:[6,30,54,74,102],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,
dataCodewordsPerBlock:121},{numBlocks:5,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:47},{numBlocks:14,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:16,dataCodewordsPerBlock:15},{numBlocks:14,dataCodewordsPerBlock:16}]}]},{infoBits:102084,versionNumber:24,alignmentPatternCenters:[6,28,54,80,106],errorCorrectionLevels:[{ecCodewordsPerBlock:30,
ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:117},{numBlocks:4,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:45},{numBlocks:14,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:24},{numBlocks:16,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:30,dataCodewordsPerBlock:16},{numBlocks:2,dataCodewordsPerBlock:17}]}]},{infoBits:102881,versionNumber:25,alignmentPatternCenters:[6,
32,58,84,110],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:106},{numBlocks:4,dataCodewordsPerBlock:107}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:47},{numBlocks:13,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:24},{numBlocks:22,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:15},{numBlocks:13,dataCodewordsPerBlock:16}]}]},
{infoBits:110507,versionNumber:26,alignmentPatternCenters:[6,30,58,86,114],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:114},{numBlocks:2,dataCodewordsPerBlock:115}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:46},{numBlocks:4,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:28,dataCodewordsPerBlock:22},{numBlocks:6,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:33,dataCodewordsPerBlock:16},
{numBlocks:4,dataCodewordsPerBlock:17}]}]},{infoBits:110734,versionNumber:27,alignmentPatternCenters:[6,34,62,90,118],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:122},{numBlocks:4,dataCodewordsPerBlock:123}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:45},{numBlocks:3,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:23},{numBlocks:26,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:30,
ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:15},{numBlocks:28,dataCodewordsPerBlock:16}]}]},{infoBits:117786,versionNumber:28,alignmentPatternCenters:[6,26,50,74,98,122],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:117},{numBlocks:10,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:45},{numBlocks:23,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:24},{numBlocks:31,
dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:15},{numBlocks:31,dataCodewordsPerBlock:16}]}]},{infoBits:119615,versionNumber:29,alignmentPatternCenters:[6,30,54,78,102,126],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:116},{numBlocks:7,dataCodewordsPerBlock:117}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:21,dataCodewordsPerBlock:45},{numBlocks:7,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,
ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:23},{numBlocks:37,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:15},{numBlocks:26,dataCodewordsPerBlock:16}]}]},{infoBits:126325,versionNumber:30,alignmentPatternCenters:[6,26,52,78,104,130],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:115},{numBlocks:10,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:47},
{numBlocks:10,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:24},{numBlocks:25,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:23,dataCodewordsPerBlock:15},{numBlocks:25,dataCodewordsPerBlock:16}]}]},{infoBits:127568,versionNumber:31,alignmentPatternCenters:[6,30,56,82,108,134],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:115},{numBlocks:3,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,
ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:46},{numBlocks:29,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:42,dataCodewordsPerBlock:24},{numBlocks:1,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:23,dataCodewordsPerBlock:15},{numBlocks:28,dataCodewordsPerBlock:16}]}]},{infoBits:133589,versionNumber:32,alignmentPatternCenters:[6,34,60,86,112,138],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:115}]},
{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:46},{numBlocks:23,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:24},{numBlocks:35,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:15},{numBlocks:35,dataCodewordsPerBlock:16}]}]},{infoBits:136944,versionNumber:33,alignmentPatternCenters:[6,30,58,86,114,142],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:115},
{numBlocks:1,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:14,dataCodewordsPerBlock:46},{numBlocks:21,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:29,dataCodewordsPerBlock:24},{numBlocks:19,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:15},{numBlocks:46,dataCodewordsPerBlock:16}]}]},{infoBits:141498,versionNumber:34,alignmentPatternCenters:[6,34,62,90,118,146],errorCorrectionLevels:[{ecCodewordsPerBlock:30,
ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:115},{numBlocks:6,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:14,dataCodewordsPerBlock:46},{numBlocks:23,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:44,dataCodewordsPerBlock:24},{numBlocks:7,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:59,dataCodewordsPerBlock:16},{numBlocks:1,dataCodewordsPerBlock:17}]}]},{infoBits:145311,versionNumber:35,alignmentPatternCenters:[6,
30,54,78,102,126,150],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:121},{numBlocks:7,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:47},{numBlocks:26,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:39,dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:15},{numBlocks:41,dataCodewordsPerBlock:16}]}]},
{infoBits:150283,versionNumber:36,alignmentPatternCenters:[6,24,50,76,102,128,154],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:121},{numBlocks:14,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:47},{numBlocks:34,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:46,dataCodewordsPerBlock:24},{numBlocks:10,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:2,
dataCodewordsPerBlock:15},{numBlocks:64,dataCodewordsPerBlock:16}]}]},{infoBits:152622,versionNumber:37,alignmentPatternCenters:[6,28,54,80,106,132,158],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:122},{numBlocks:4,dataCodewordsPerBlock:123}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:29,dataCodewordsPerBlock:46},{numBlocks:14,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:49,dataCodewordsPerBlock:24},{numBlocks:10,dataCodewordsPerBlock:25}]},
{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:24,dataCodewordsPerBlock:15},{numBlocks:46,dataCodewordsPerBlock:16}]}]},{infoBits:158308,versionNumber:38,alignmentPatternCenters:[6,32,58,84,110,136,162],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:122},{numBlocks:18,dataCodewordsPerBlock:123}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:46},{numBlocks:32,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:48,
dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:42,dataCodewordsPerBlock:15},{numBlocks:32,dataCodewordsPerBlock:16}]}]},{infoBits:161089,versionNumber:39,alignmentPatternCenters:[6,26,54,82,110,138,166],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:20,dataCodewordsPerBlock:117},{numBlocks:4,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:40,dataCodewordsPerBlock:47},{numBlocks:7,dataCodewordsPerBlock:48}]},
{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:43,dataCodewordsPerBlock:24},{numBlocks:22,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:15},{numBlocks:67,dataCodewordsPerBlock:16}]}]},{infoBits:167017,versionNumber:40,alignmentPatternCenters:[6,30,58,86,114,142,170],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:118},{numBlocks:6,dataCodewordsPerBlock:119}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:18,
dataCodewordsPerBlock:47},{numBlocks:31,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:34,dataCodewordsPerBlock:24},{numBlocks:34,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:20,dataCodewordsPerBlock:15},{numBlocks:61,dataCodewordsPerBlock:16}]}]}],da=[{bits:21522,formatInfo:{errorCorrectionLevel:1,dataMask:0}},{bits:20773,formatInfo:{errorCorrectionLevel:1,dataMask:1}},{bits:24188,formatInfo:{errorCorrectionLevel:1,dataMask:2}},{bits:23371,formatInfo:{errorCorrectionLevel:1,
dataMask:3}},{bits:17913,formatInfo:{errorCorrectionLevel:1,dataMask:4}},{bits:16590,formatInfo:{errorCorrectionLevel:1,dataMask:5}},{bits:20375,formatInfo:{errorCorrectionLevel:1,dataMask:6}},{bits:19104,formatInfo:{errorCorrectionLevel:1,dataMask:7}},{bits:30660,formatInfo:{errorCorrectionLevel:0,dataMask:0}},{bits:29427,formatInfo:{errorCorrectionLevel:0,dataMask:1}},{bits:32170,formatInfo:{errorCorrectionLevel:0,dataMask:2}},{bits:30877,formatInfo:{errorCorrectionLevel:0,dataMask:3}},{bits:26159,
formatInfo:{errorCorrectionLevel:0,dataMask:4}},{bits:25368,formatInfo:{errorCorrectionLevel:0,dataMask:5}},{bits:27713,formatInfo:{errorCorrectionLevel:0,dataMask:6}},{bits:26998,formatInfo:{errorCorrectionLevel:0,dataMask:7}},{bits:5769,formatInfo:{errorCorrectionLevel:3,dataMask:0}},{bits:5054,formatInfo:{errorCorrectionLevel:3,dataMask:1}},{bits:7399,formatInfo:{errorCorrectionLevel:3,dataMask:2}},{bits:6608,formatInfo:{errorCorrectionLevel:3,dataMask:3}},{bits:1890,formatInfo:{errorCorrectionLevel:3,
dataMask:4}},{bits:597,formatInfo:{errorCorrectionLevel:3,dataMask:5}},{bits:3340,formatInfo:{errorCorrectionLevel:3,dataMask:6}},{bits:2107,formatInfo:{errorCorrectionLevel:3,dataMask:7}},{bits:13663,formatInfo:{errorCorrectionLevel:2,dataMask:0}},{bits:12392,formatInfo:{errorCorrectionLevel:2,dataMask:1}},{bits:16177,formatInfo:{errorCorrectionLevel:2,dataMask:2}},{bits:14854,formatInfo:{errorCorrectionLevel:2,dataMask:3}},{bits:9396,formatInfo:{errorCorrectionLevel:2,dataMask:4}},{bits:8579,formatInfo:{errorCorrectionLevel:2,
dataMask:5}},{bits:11994,formatInfo:{errorCorrectionLevel:2,dataMask:6}},{bits:11245,formatInfo:{errorCorrectionLevel:2,dataMask:7}}],aa=[a=>0===(a.y+a.x)%2,a=>0===a.y%2,a=>0===a.x%3,a=>0===(a.y+a.x)%3,a=>0===(Math.floor(a.y/2)+Math.floor(a.x/3))%2,a=>0===a.x*a.y%2+a.x*a.y%3,a=>0===(a.y*a.x%2+a.y*a.x%3)%2,a=>0===((a.y+a.x)%2+a.y*a.x%3)%2],y=(a,b)=>Math.sqrt(Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2)),la={inversionAttempts:"attemptBoth",greyScaleWeights:{red:.2126,green:.7152,blue:.0722,useIntegerApproximation:!1},
canOverwriteImage:!0};I.default=I;let G="dontInvert",D={red:77,green:150,blue:29,useIntegerApproximation:!0};self.onmessage=a=>{let b=a.data.data;switch(a.data.type){case "decode":a=I(b.data,b.width,b.height,{inversionAttempts:G,greyScaleWeights:D});self.postMessage({type:"qrResult",data:a?a.data:null});break;case "grayscaleWeights":D.red=b.red;D.green=b.green;D.blue=b.blue;D.useIntegerApproximation=b.useIntegerApproximation;break;case "inversionMode":switch(b){case "original":G="dontInvert";break;
case "invert":G="onlyInvert";break;case "both":G="attemptBoth";break;default:throw Error("Invalid inversion mode");}break;case "close":self.close()}}})()
//# sourceMappingURL=qr-scanner-worker.min.js.map

File diff suppressed because one or more lines are too long

20
apps/qrcode/qr-scanner.umd.min.js vendored Normal file
View File

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

File diff suppressed because one or more lines are too long

1
apps/rebble/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: First release

26
apps/rebble/README.md Normal file
View File

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

272
apps/rebble/rebble.app.js Normal file

File diff suppressed because one or more lines are too long

View File

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

BIN
apps/rebble/rebble.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

7
apps/snaky/README.md Normal file
View File

@ -0,0 +1,7 @@
# Snaky
Eat apples and don't bite your tail.
## Controls
Use the touch screen, drag up, down, right or left.

1
apps/snaky/snaky-icon.js Normal file
View File

@ -0,0 +1 @@
E.toArrayBuffer(atob("MDCEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC7u7u7u7AAAJmZmZmZmZmZkAAAAAAAAAu7u7u7u7uwAJmZmZmZmZmZkAAAAAAAALuyu7u7u7uwAJmZmZmZmZmZkAAAAAAAALIiIru7IiKwAJmZmZmZmZmZkAAAAAAAALISIru7IiK7AJmZmZmZmZmZkAAAAAAAALsiK7u7IjK7AJmZmZmZmZmZkAAAAAAAALu7u7u7u7u7AJmZmZmZmZmZkAAAAAAAALu7u7u7u7u7AAAAAAAJmZmZkAAAAAAAALsru7u7siu7AAAAAAAAmZmZkAAAAAAAALuxEiIiIruwAAAAAAAJmZmZkAAAAAAAAAu7IiIiu7uwAAAAAAAJmZmZkAAAAAAAAAu7u7u7u7sAAAAAAAAJmZmZkAAAAAAAAAALuqqqqwAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAAmZmZkAAAAAAAAAAAmZmZmQAAAAAAAAAJmZmZkAAAAAAAAAAAmZmZmZmZmZmZmZmZmZmZkAAAAAAAAAAAmZmZmZmZmZmZmZmZmZmZkAAAAAAAAAAAmZmZmZmZmZmZmZmZmZmZkAAAAAAAAAAAmZmZmZmZmZmZmZmZmZmZkAAAAAAAAAAAmZmZmZmZmZmZmZmZmZmZkAAAAAAAAAAAmZmZmZmZmZmZmZmZmZmZkAAAAAAAAAAAmZmZmZmZmZmZmZmZmZmZkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="))

185
apps/snaky/snaky.js Normal file
View File

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

BIN
apps/snaky/snaky.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 876 B

View File

@ -1,2 +1,3 @@
0.1: New watch face 0.1: New watch face
0.2: Use Bangle.setUI for button/launcher handling 0.2: Use Bangle.setUI for button/launcher handling
0.3: Bangle.js 2 support

View File

@ -5,9 +5,10 @@ function padNum(n, l) {
return ("0".repeat(l)+n).substr(-l); return ("0".repeat(l)+n).substr(-l);
} }
let rects = {}; var rects = {};
let rectsToClear = {}; var rectsToClear = {};
let commands = []; var commands = [];
var showSeconds = true;
function pushCommand(command) { function pushCommand(command) {
let hash = E.CRC32(E.toJS(arguments)); let hash = E.CRC32(E.toJS(arguments));
@ -20,17 +21,20 @@ function executeCommands() {
"ram"; "ram";
for (let hash in rectsToClear) delete rects[hash]; 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); 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) rects[c.hash] = c.command();
for (let c of commands) {
c.command();
rects[c.hash] = g.getModified(true);
}
rectsToClear = Object.assign({}, rects); rectsToClear = Object.assign({}, rects);
commands = []; commands = [];
} }
function drawVectorText(text, size, x, y, alignX, alignY) { function drawVectorText(text, size, x, y, alignX, alignY) {
g.setFont("Vector", size).setFontAlign(alignX, alignY).drawString(text, x, y); 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() { function draw() {
@ -43,11 +47,12 @@ function draw() {
let secondsText = padNum(d.getSeconds(), 2); let secondsText = padNum(d.getSeconds(), 2);
let dowText = locale.dow(d); let dowText = locale.dow(d);
let dateText = locale.date(d, true); let dateText = locale.date(d, true);
let width = g.getWidth() - 2;
g.setFont("Vector", 256); g.setFont("Vector", 256);
let timeFontSize = g.getWidth() / ((g.stringWidth(timeText) / 256) + (Math.max(g.stringWidth(meridian), g.stringWidth(secondsText)) / 512 * 9 / 10)); let timeFontSize = width / ((g.stringWidth(timeText) / 256) + (Math.max(g.stringWidth(meridian), g.stringWidth(secondsText)) / 512 * 9 / 10));
let dowFontSize = g.getWidth() / (g.stringWidth(dowText) / 256); let dowFontSize = width / (g.stringWidth(dowText) / 256);
let dateFontSize = g.getWidth() / (g.stringWidth(dateText) / 256); let dateFontSize = width / (g.stringWidth(dateText) / 256);
let timeHeight = g.setFont("Vector", timeFontSize).getFontHeight() * 9 / 10; let timeHeight = g.setFont("Vector", timeFontSize).getFontHeight() * 9 / 10;
let dowHeight = g.setFont("Vector", dowFontSize).getFontHeight(); let dowHeight = g.setFont("Vector", dowFontSize).getFontHeight();
@ -56,26 +61,28 @@ function draw() {
let remainingHeight = g.getHeight() - 24 - timeHeight - dowHeight - dateHeight; let remainingHeight = g.getHeight() - 24 - timeHeight - dowHeight - dateHeight;
let spacer = remainingHeight / 4; let spacer = remainingHeight / 4;
let x = 2;
let y = 24 + spacer; let y = 24 + spacer;
pushCommand(drawVectorText, timeText, timeFontSize, 0, y, -1, -1); pushCommand(drawVectorText, timeText, timeFontSize, x, y, -1, -1);
pushCommand(drawVectorText, meridian, timeFontSize*9/20, g.getWidth(), y, 1, -1); pushCommand(drawVectorText, meridian, timeFontSize*9/20, x + width, y, 1, -1);
pushCommand(drawVectorText, secondsText, timeFontSize*9/20, g.getWidth(), y + timeHeight, 1, 1); if (showSeconds) pushCommand(drawVectorText, secondsText, timeFontSize*9/20, x + width, y + timeHeight, 1, 1);
y += timeHeight + spacer; 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; y += dowHeight + spacer;
pushCommand(drawVectorText, dateText, dateFontSize, g.getWidth()/2, y, 0, -1); pushCommand(drawVectorText, dateText, dateFontSize, x + width/2, y, 0, -1);
executeCommands(); executeCommands();
} }
let timeout; var timeout;
function tick() { function tick() {
draw(); 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) { Bangle.on('lcdPower', function(on) {
@ -84,6 +91,13 @@ Bangle.on('lcdPower', function(on) {
if (on) tick(); if (on) tick();
}); });
Bangle.on('lock', function(locked) {
if (timeout) clearTimeout(timeout);
timeout = null;
showSeconds = !locked;
tick();
});
g.clear(); g.clear();
tick(); tick();
Bangle.loadWidgets(); Bangle.loadWidgets();

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -9,3 +9,4 @@
0.10: Use new Layout library 0.10: Use new Layout library
0.11: Bangle.js 2 support 0.11: Bangle.js 2 support
0.12: Allow hiding the widget 0.12: Allow hiding the widget
0.13: Tweak Bangle.js 2 light theme colors

View File

@ -54,14 +54,62 @@ exports.get = function() {
scheduleExpiry(storage.readJSON('weather.json')||{}); scheduleExpiry(storage.readJSON('weather.json')||{});
exports.drawIcon = function(cond, x, y, r) { 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) { function drawSun(x, y, r) {
g.setColor(B2 ? '#FF0' : (g.theme.dark ? "#FE0" : "#FC0")); g.setColor(palette.sun);
g.fillCircle(x, y, r); g.fillCircle(x, y, r);
} }
function drawCloud(x, y, r, c) { function drawCloud(x, y, r, c) {
const u = r/12; 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.setColor(c);
g.fillCircle(x-8*u, y+3*u, 4*u); g.fillCircle(x-8*u, y+3*u, 4*u);
g.fillCircle(x-4*u, y-2*u, 5*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) { 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); 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) { 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 y1 = y+1/2*r;
const y2 = y+1*r; const y2 = y+1*r;
const poly = g.fillPolyAA ? p => g.fillPolyAA(p) : p => g.fillPoly(p); 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 drawThunderstorm(x, y, r) {
function drawLightning(x, y, r) { function drawLightning(x, y, r) {
g.setColor(B2 ? '#FF0' : (g.theme.dark ? "#FE0" : "#FC0")); g.setColor(palette.lightning);
g.fillPoly([ g.fillPoly([
x-2/6*r, y-r, x-2/6*r, y-r,
x-4/6*r, y+1/6*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; const w = 1/12*r;
for(let i = 0; i<=6; ++i) { for(let i = 0; i<=6; ++i) {
const points = [ const points = [
@ -187,7 +235,7 @@ exports.drawIcon = function(cond, x, y, r) {
[-0.2, 0.3], [-0.2, 0.3],
]; ];
g.setColor(B2 ? '#FFF' : (g.theme.dark ? "#FFF" : "#CCC")); g.setColor(palette.mist);
for(let i = 0; i<5; ++i) { 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, 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); y+(0.4*i-0.7)*r-1);
@ -197,7 +245,7 @@ exports.drawIcon = function(cond, x, y, r) {
} }
function drawUnknown(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); g.setColor(g.theme.fg).setFontAlign(0, 0).setFont('Vector', r*2).drawString("?", x+r/10, y+r/6);
} }

View File

@ -1,3 +1,4 @@
0.01: New App! 0.01: New App!
0.02: Minor layout format tweak so it uses less memory and draws ok on Bangle.js 1 (#1012) 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.03: Minor layout extra spaces.
0.04: Layout now compatible with Bangle.js 2

View File

@ -73,12 +73,10 @@ var clockLayout = new Layout( {
{type: "img", filly: 1, id: "weatherIcon", src: sunIcon}, {type: "img", filly: 1, id: "weatherIcon", src: sunIcon},
{type: "v", fillx:1, c: [ {type: "v", fillx:1, c: [
{type: "h", c: [ {type: "h", c: [
{type: "txt", font: "10%", id: "temp", label: "000"}, {type: "txt", font: "10%", id: "temp", label: "000 °C"},
{type: "txt", font: "10%", id: "tempUnit", label: "°C"},
]}, ]},
{type: "h", c: [ {type: "h", c: [
{type: "txt", font: "10%", id: "wind", label: "00"}, {type: "txt", font: "10%", id: "wind", label: "00 km/h"},
{type: "txt", font: "10%", id: "windUnit", label: "km/h"},
]} ]}
] ]
}, },
@ -106,18 +104,14 @@ function draw() {
if(weatherJson && weatherJson.weather){ if(weatherJson && weatherJson.weather){
var currentWeather = weatherJson.weather; var currentWeather = weatherJson.weather;
const temp = locale.temp(currentWeather.temp-273.15).match(/^(\D*\d*)(.*)$/); const temp = locale.temp(currentWeather.temp-273.15).match(/^(\D*\d*)(.*)$/);
clockLayout.temp.label = temp[1]; clockLayout.temp.label = temp[1] + " " + temp[2];
clockLayout.tempUnit.label = temp[2];
clockLayout.weatherIcon.src = chooseIcon(currentWeather.txt); clockLayout.weatherIcon.src = chooseIcon(currentWeather.txt);
const wind = locale.speed(currentWeather.wind).match(/^(\D*\d*)(.*)$/); const wind = locale.speed(currentWeather.wind).match(/^(\D*\d*)(.*)$/);
clockLayout.wind.label = wind[1] + " ".repeat(wind[2].length-1); clockLayout.wind.label = wind[1] + " " + wind[2] + " " + (currentWeather.wrose||'').toUpperCase();
clockLayout.windUnit.label = wind[2] + " " + (currentWeather.wrose||'').toUpperCase();
} }
else{ else{
clockLayout.temp.label = "Err"; clockLayout.temp.label = "Err";
clockLayout.tempUnit.label = "";
clockLayout.wind.label = "No Data"; clockLayout.wind.label = "No Data";
clockLayout.windUnit.label = "";
clockLayout.weatherIcon.src = errIcon; clockLayout.weatherIcon.src = errIcon;
} }
clockLayout.clear(); clockLayout.clear();

View File

@ -1,2 +1,3 @@
1.00: Release for Bangle 2 (2021/11/18) 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.01: Internal id update to wid_* as per Gordon's request (2021/11/21)
1.02: Support dark themes

View File

@ -1,9 +1,9 @@
(function(){ (function(){
let COLORS = { let COLORS = {
'white': "#fff", 'white': g.theme.dark ? "#000" : "#fff",
'black': "#000", 'black': g.theme.dark ? "#fff" : "#000",
'charging': "#08f", 'charging': "#08f",
'high': "#000", 'high': g.theme.dark ? "#fff" : "#000",
'low': "#f00", 'low': "#f00",
}; };