mirror of
https://github.com/MagicMirrorOrg/MagicMirror.git
synced 2026-01-11 18:47:55 -05:00
Compare commits
3481 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10dc315f3b | ||
|
|
0f1457b5d7 | ||
|
|
090873d4c2 | ||
|
|
da00c168ae | ||
|
|
8286d5a06e | ||
|
|
dc5fb978a7 | ||
|
|
a4ab0cbe09 | ||
|
|
4a341b381e | ||
|
|
3fa98bc1aa | ||
|
|
87e2e87ce6 | ||
|
|
c25b6dc16c | ||
|
|
6a786aa090 | ||
|
|
a97d87bce8 | ||
|
|
fba91329f1 | ||
|
|
353cc3b00f | ||
|
|
057ef63586 | ||
|
|
c9fb38981e | ||
|
|
9ea7de8b44 | ||
|
|
020c8ccd2a | ||
|
|
4d09abe725 | ||
|
|
5079e30caf | ||
|
|
3e5c1a278b | ||
|
|
4ded60874f | ||
|
|
ff1a843de6 | ||
|
|
e6cefcf948 | ||
|
|
a857412f13 | ||
|
|
e507f95b2a | ||
|
|
61cf92c67a | ||
|
|
7a4eddc592 | ||
|
|
3fcbf15915 | ||
|
|
efafb1c28a | ||
|
|
67bedf8648 | ||
|
|
c1d35b0f91 | ||
|
|
d3a715bd6b | ||
|
|
28b52cd24f | ||
|
|
46e77f805c | ||
|
|
dd23db0ad8 | ||
|
|
30cf7f8afe | ||
|
|
7802e0bb88 | ||
|
|
07e75b8550 | ||
|
|
1e9fad8278 | ||
|
|
0975826457 | ||
|
|
e5ff320591 | ||
|
|
02c1e47749 | ||
|
|
a50824eeee | ||
|
|
0c3f9f4ed9 | ||
|
|
6b12601c6f | ||
|
|
225bece44e | ||
|
|
a5a5e73196 | ||
|
|
f8085ed78f | ||
|
|
4e6e84c637 | ||
|
|
846212798c | ||
|
|
571f95eb2f | ||
|
|
202eeea33f | ||
|
|
af212057db | ||
|
|
0fd0fea7e7 | ||
|
|
eb6ef3c8ff | ||
|
|
cdb8d35cf6 | ||
|
|
684dcdcef3 | ||
|
|
155351f5dd | ||
|
|
0419e06e7c | ||
|
|
2ea38bd9a4 | ||
|
|
30db9c30c8 | ||
|
|
a8ef594dab | ||
|
|
9858d5b495 | ||
|
|
20bd85b676 | ||
|
|
83ec8ca24f | ||
|
|
9622d02230 | ||
|
|
b72d0ed37e | ||
|
|
f966e504a5 | ||
|
|
a0366e794b | ||
|
|
3c3ce24397 | ||
|
|
30290f3eb2 | ||
|
|
616431e04b | ||
|
|
d0aeb90f0a | ||
|
|
86220fa721 | ||
|
|
0f58a56a07 | ||
|
|
29451562e3 | ||
|
|
911687af2a | ||
|
|
1c9c33b87b | ||
|
|
f485462af1 | ||
|
|
39d7ceb017 | ||
|
|
8251792a0d | ||
|
|
54ac450f92 | ||
|
|
299e4a497f | ||
|
|
3f851c1fd6 | ||
|
|
8cf16f1049 | ||
|
|
b373aa6250 | ||
|
|
2f4b8cd642 | ||
|
|
85b6df3738 | ||
|
|
c675421a6a | ||
|
|
a7b571e5d0 | ||
|
|
b3a9b7ef0e | ||
|
|
e984893853 | ||
|
|
3024319027 | ||
|
|
21ba652413 | ||
|
|
e4b8cd92f2 | ||
|
|
1a4a9f6501 | ||
|
|
e950cdaf32 | ||
|
|
f97be2f8f3 | ||
|
|
be0c8f4f16 | ||
|
|
46fd2de315 | ||
|
|
72f0d77865 | ||
|
|
d43679d59e | ||
|
|
ce46fb5384 | ||
|
|
43b33cb6de | ||
|
|
0344399253 | ||
|
|
3a9b154cb2 | ||
|
|
1074fbacfe | ||
|
|
00ff3ab380 | ||
|
|
db874a011c | ||
|
|
35a2839a2f | ||
|
|
cdb9b9bb87 | ||
|
|
87a3e4d440 | ||
|
|
284bed677e | ||
|
|
667be460e5 | ||
|
|
c49386bb58 | ||
|
|
adb50a9623 | ||
|
|
ac1d2372f4 | ||
|
|
f54690c829 | ||
|
|
053f9c34aa | ||
|
|
e7dd2b4aee | ||
|
|
6f82f9e01b | ||
|
|
d531730dc1 | ||
|
|
00bdf6aaa6 | ||
|
|
97f3514677 | ||
|
|
137facf95a | ||
|
|
8afba3a5c4 | ||
|
|
9c5383dc37 | ||
|
|
260bc9664e | ||
|
|
655ca83356 | ||
|
|
472bf1665c | ||
|
|
db129cc19b | ||
|
|
b735f8a524 | ||
|
|
1e34764588 | ||
|
|
99aaae491c | ||
|
|
f288581c69 | ||
|
|
3c5d50bce9 | ||
|
|
a01f08391b | ||
|
|
51a1399bca | ||
|
|
b735cb96a0 | ||
|
|
d00c25e107 | ||
|
|
8a5e87b116 | ||
|
|
2779d19d5c | ||
|
|
38d4a8b198 | ||
|
|
ccf98c0c22 | ||
|
|
bd0d91d1b4 | ||
|
|
056f3a6ccb | ||
|
|
20a50f8382 | ||
|
|
21284e7795 | ||
|
|
e0ceed5a63 | ||
|
|
958a2ee6ec | ||
|
|
ea264cb15e | ||
|
|
d8f19e631c | ||
|
|
ce5c0ed5ba | ||
|
|
839ca9ecfb | ||
|
|
720bc12c00 | ||
|
|
1ba845fb06 | ||
|
|
e86fa9d24a | ||
|
|
e348a61085 | ||
|
|
2f70366299 | ||
|
|
c466b20558 | ||
|
|
021f8d25a5 | ||
|
|
a19c3a43d8 | ||
|
|
819923e66d | ||
|
|
6c3100e250 | ||
|
|
1065eda47f | ||
|
|
afd676a958 | ||
|
|
12405b66d0 | ||
|
|
ecd0b6fa83 | ||
|
|
3a8587378c | ||
|
|
a05c08ed48 | ||
|
|
469a90787b | ||
|
|
9e5a9b5ced | ||
|
|
f311ba3f7c | ||
|
|
fc68321bd5 | ||
|
|
8a4173d0ad | ||
|
|
90af31cb2f | ||
|
|
61bcd9337b | ||
|
|
7944045893 | ||
|
|
2c3f83c70d | ||
|
|
92ab705ff5 | ||
|
|
2951f0c40c | ||
|
|
8a23bccb70 | ||
|
|
aa6ad01fb9 | ||
|
|
ef325896c5 | ||
|
|
a270c73d7c | ||
|
|
16feda895d | ||
|
|
874a50d142 | ||
|
|
ae32645470 | ||
|
|
d4412fe06f | ||
|
|
b42c05fb56 | ||
|
|
8d0da61bd4 | ||
|
|
8b8be261cd | ||
|
|
2b6ceed897 | ||
|
|
8abca801a2 | ||
|
|
1eae4425d8 | ||
|
|
a6386bd60e | ||
|
|
1460f002ab | ||
|
|
0d6f736c2c | ||
|
|
da88e11d04 | ||
|
|
0a58767563 | ||
|
|
198525f2ce | ||
|
|
3220702034 | ||
|
|
262711a127 | ||
|
|
078438442a | ||
|
|
5ac20d65ac | ||
|
|
5d2f706c32 | ||
|
|
844a59d880 | ||
|
|
82c742f964 | ||
|
|
b73cfd8a60 | ||
|
|
8466ff0c1a | ||
|
|
1e0fc7eb0d | ||
|
|
74b0c3ea57 | ||
|
|
a735275864 | ||
|
|
8a1d46deb4 | ||
|
|
b624aeec45 | ||
|
|
e83b4d7d42 | ||
|
|
da302fce32 | ||
|
|
e09fec56e2 | ||
|
|
568f952573 | ||
|
|
fc1e488391 | ||
|
|
5781c258e0 | ||
|
|
13c542aeda | ||
|
|
02148f68e5 | ||
|
|
3c84002abd | ||
|
|
01aa57e493 | ||
|
|
af09b4214a | ||
|
|
43b6c71205 | ||
|
|
480e1adfbf | ||
|
|
8c04712784 | ||
|
|
0c61ba8f2d | ||
|
|
485f662d75 | ||
|
|
26caeec0c1 | ||
|
|
4a7cb88a3e | ||
|
|
d11696015d | ||
|
|
3b76ca4f9b | ||
|
|
6f3239d514 | ||
|
|
e8f60d39de | ||
|
|
a3bad8aec4 | ||
|
|
644aa26b75 | ||
|
|
ecd9828afc | ||
|
|
7462d61e16 | ||
|
|
b95ef7250d | ||
|
|
569dec1b0b | ||
|
|
1bc0270d7b | ||
|
|
ebfeebc40b | ||
|
|
ec80b25087 | ||
|
|
7a2278d7b6 | ||
|
|
28b93b70fa | ||
|
|
0431f45190 | ||
|
|
96ce444061 | ||
|
|
32776e7ca3 | ||
|
|
35d516ad4d | ||
|
|
e6a17d27b9 | ||
|
|
52edec1b64 | ||
|
|
6440d2289c | ||
|
|
11afadcea8 | ||
|
|
2e981987f2 | ||
|
|
69efca0bdb | ||
|
|
eefb92367e | ||
|
|
8fa96c2836 | ||
|
|
04dae52a6c | ||
|
|
37c9da1351 | ||
|
|
86d7ec6270 | ||
|
|
82f1e0fe32 | ||
|
|
6ec0aa8894 | ||
|
|
3dbe8bfbbf | ||
|
|
20d82bab78 | ||
|
|
60f6123b64 | ||
|
|
74887a58f6 | ||
|
|
76821d16fc | ||
|
|
233a6b5e90 | ||
|
|
a1d9337c81 | ||
|
|
e3fec6c3e4 | ||
|
|
5de88b4cc1 | ||
|
|
af5e6655e0 | ||
|
|
a8716799c9 | ||
|
|
1672027091 | ||
|
|
934ac886cb | ||
|
|
1b47c4d16f | ||
|
|
e8fd906aa1 | ||
|
|
57b6ea1297 | ||
|
|
102ff15a99 | ||
|
|
19148cfc84 | ||
|
|
97c2bab58a | ||
|
|
1f473228ce | ||
|
|
340a8e4176 | ||
|
|
67201a52cc | ||
|
|
30e164fe0a | ||
|
|
cfcb20a468 | ||
|
|
24eac7ef41 | ||
|
|
94162f1b45 | ||
|
|
01c19efc0c | ||
|
|
e4f2a8a23b | ||
|
|
89b33d2c62 | ||
|
|
3ced35a2f8 | ||
|
|
fd4576b234 | ||
|
|
812ad829c9 | ||
|
|
207a9ba723 | ||
|
|
f9bf17d701 | ||
|
|
43b96ccd1e | ||
|
|
14e8cdd9b2 | ||
|
|
259068b860 | ||
|
|
a1a4192835 | ||
|
|
45f09dcc8c | ||
|
|
15c3f11f4a | ||
|
|
1a21027850 | ||
|
|
8427a9fc68 | ||
|
|
f09b89f975 | ||
|
|
7bec84f767 | ||
|
|
8f4cbcf817 | ||
|
|
c3382274a2 | ||
|
|
493055f861 | ||
|
|
11fbbd49f3 | ||
|
|
8ce37d53cd | ||
|
|
baa4012872 | ||
|
|
16c5eba2be | ||
|
|
ec08cb32aa | ||
|
|
86fb1b938b | ||
|
|
5aa7097a6e | ||
|
|
fd3f520e95 | ||
|
|
49ff92892f | ||
|
|
904f5a2656 | ||
|
|
bb105dd832 | ||
|
|
fd934c0514 | ||
|
|
66609428a2 | ||
|
|
0056e0bc6d | ||
|
|
056b66a764 | ||
|
|
3438a5a374 | ||
|
|
9f3806dabf | ||
|
|
1d4d5cc4e7 | ||
|
|
3901543697 | ||
|
|
a4d73e2a67 | ||
|
|
f6854f58ff | ||
|
|
ad7f7d2890 | ||
|
|
498db63cac | ||
|
|
05659820d0 | ||
|
|
02779ef725 | ||
|
|
935c9b6a42 | ||
|
|
522f7644a3 | ||
|
|
b1a67d1fc5 | ||
|
|
0674c0aff6 | ||
|
|
c8664d5952 | ||
|
|
8ce1e2c956 | ||
|
|
90cca28e01 | ||
|
|
e489b7101c | ||
|
|
eee64c8064 | ||
|
|
a0d4e8dafc | ||
|
|
e6b94df06d | ||
|
|
6f1c7d6253 | ||
|
|
2d5a19b676 | ||
|
|
d1e8dded68 | ||
|
|
9888f66c84 | ||
|
|
5ec51d0ccc | ||
|
|
79f9331073 | ||
|
|
43bcf4ab98 | ||
|
|
f4eae72c48 | ||
|
|
9d14d3e5b7 | ||
|
|
26d3141489 | ||
|
|
5c44f51d6d | ||
|
|
50f3f32ba8 | ||
|
|
8fa858ca8c | ||
|
|
8b1d1671f7 | ||
|
|
73aa35ea2c | ||
|
|
a391445e5f | ||
|
|
530c5d416a | ||
|
|
319a13c0ef | ||
|
|
ec2fedd797 | ||
|
|
7489d19784 | ||
|
|
3b5a0e8d66 | ||
|
|
29ed63286c | ||
|
|
108da4b6d4 | ||
|
|
a00a1c64d1 | ||
|
|
275825cfe1 | ||
|
|
cd2fce56a6 | ||
|
|
cab850fb35 | ||
|
|
045dc600b0 | ||
|
|
1b199c1682 | ||
|
|
936fa637ec | ||
|
|
b9ccb7a892 | ||
|
|
95b33be3cf | ||
|
|
cde27b89c2 | ||
|
|
476e52e004 | ||
|
|
8dc88fe64b | ||
|
|
d00da790af | ||
|
|
c61fac75e4 | ||
|
|
e949af6fd5 | ||
|
|
c1e38b917e | ||
|
|
1244121216 | ||
|
|
46543daa13 | ||
|
|
6277384bf9 | ||
|
|
17549fed9c | ||
|
|
377e42affc | ||
|
|
0ac5d56865 | ||
|
|
b59ee6ad7e | ||
|
|
2d7c5a827f | ||
|
|
85c32ef843 | ||
|
|
dc089d7db9 | ||
|
|
d570f910f8 | ||
|
|
8f2731911b | ||
|
|
9d22420027 | ||
|
|
029da3791b | ||
|
|
caa51b1029 | ||
|
|
2973a03d40 | ||
|
|
679d464f4c | ||
|
|
93c0787efd | ||
|
|
a1708a1469 | ||
|
|
862bf78e63 | ||
|
|
8c01df50a2 | ||
|
|
921932920d | ||
|
|
d021c9b4ae | ||
|
|
309a18e9c0 | ||
|
|
73322c96a6 | ||
|
|
121e7db330 | ||
|
|
9f5e1b59fb | ||
|
|
3282ed4fea | ||
|
|
c7d9192f18 | ||
|
|
bd0f707aed | ||
|
|
8b30634ebe | ||
|
|
ca8acb14f1 | ||
|
|
26b8f8d0d5 | ||
|
|
a066153556 | ||
|
|
2ee7131c28 | ||
|
|
cb1f65732e | ||
|
|
37237d9c10 | ||
|
|
92cc41dec6 | ||
|
|
246dc663c4 | ||
|
|
4ed3235590 | ||
|
|
0939e405b4 | ||
|
|
584c51ef56 | ||
|
|
26ed05ba3a | ||
|
|
018cb91526 | ||
|
|
e16db2233f | ||
|
|
5d90a08011 | ||
|
|
2d3849a671 | ||
|
|
e0dcdc2110 | ||
|
|
22fcee2a16 | ||
|
|
8c0141367b | ||
|
|
5bb72cfed8 | ||
|
|
6d829fa575 | ||
|
|
85ed1b85ae | ||
|
|
c9d0bd2d7f | ||
|
|
ea31d34649 | ||
|
|
1d2f929d3f | ||
|
|
ffbf0804d9 | ||
|
|
ca0b89ecd3 | ||
|
|
dc9d6f6b79 | ||
|
|
f73520559e | ||
|
|
a4df38d963 | ||
|
|
4a162543f6 | ||
|
|
7a3ea37798 | ||
|
|
fdd389d6b7 | ||
|
|
b91fccc0e3 | ||
|
|
d911b075ab | ||
|
|
4339cdd8a4 | ||
|
|
dd32d3a492 | ||
|
|
d5caadd906 | ||
|
|
c4bc3e2687 | ||
|
|
2c5909a138 | ||
|
|
42c13fa584 | ||
|
|
3b442d4bfc | ||
|
|
9831f81b6e | ||
|
|
d3282506c9 | ||
|
|
2afff6c432 | ||
|
|
be3616abe2 | ||
|
|
daa6f5051c | ||
|
|
1e5bd98f02 | ||
|
|
7e5bfa8dd2 | ||
|
|
53363b0618 | ||
|
|
b2f71a2ce1 | ||
|
|
5d4a575919 | ||
|
|
7d521ed3ce | ||
|
|
bb9ad3daa9 | ||
|
|
442f270ee0 | ||
|
|
7ab74c6cc9 | ||
|
|
6d60baa2d6 | ||
|
|
6d3308621f | ||
|
|
fd49be1d9b | ||
|
|
c40a5648dd | ||
|
|
d9a8d2627a | ||
|
|
c7a88e2f12 | ||
|
|
9d2d170c2d | ||
|
|
df3aa22c59 | ||
|
|
a4aabfcdae | ||
|
|
ce99e70bf9 | ||
|
|
963b1aa6b1 | ||
|
|
008ac2876b | ||
|
|
2330b166f6 | ||
|
|
23c0e01565 | ||
|
|
13073bc98d | ||
|
|
8c319903dd | ||
|
|
2334cbd78a | ||
|
|
0cae954f80 | ||
|
|
c60446a015 | ||
|
|
d0c6a4ee6d | ||
|
|
f2d03a511e | ||
|
|
367233c318 | ||
|
|
9461c1692a | ||
|
|
3b32605b5e | ||
|
|
4d21f8d022 | ||
|
|
aac67570d4 | ||
|
|
5f2c465274 | ||
|
|
fdaa0bc876 | ||
|
|
a692d6be09 | ||
|
|
efbb9648c4 | ||
|
|
3d73153e59 | ||
|
|
8fa2256fb0 | ||
|
|
4fe974e7a8 | ||
|
|
21f76a8f27 | ||
|
|
aeb287fa1d | ||
|
|
1405e8821c | ||
|
|
37e31bac5b | ||
|
|
d31b696846 | ||
|
|
cc01c1f0db | ||
|
|
4a7076e01c | ||
|
|
d306bb25dc | ||
|
|
457c80fe76 | ||
|
|
bb972f8449 | ||
|
|
e6ef64968b | ||
|
|
6f3b87cfd1 | ||
|
|
f449feb3f8 | ||
|
|
b179c8e2b7 | ||
|
|
6aa0a4a47f | ||
|
|
766140f483 | ||
|
|
52aa8b868a | ||
|
|
8a3a4d6fae | ||
|
|
94f212a411 | ||
|
|
4e1dce70a3 | ||
|
|
bbf48a0dd0 | ||
|
|
a35e8f3315 | ||
|
|
205de7233e | ||
|
|
abb5dc5739 | ||
|
|
3a5a29efc0 | ||
|
|
9d249406e3 | ||
|
|
42d9e7a090 | ||
|
|
c202c0d705 | ||
|
|
8a1f9b7de6 | ||
|
|
18820c383d | ||
|
|
b38b879ee3 | ||
|
|
7fc7d626bc | ||
|
|
c8a0f1d0de | ||
|
|
c8aedc6f29 | ||
|
|
1e7ce8bb5c | ||
|
|
5386ca1592 | ||
|
|
08ed019426 | ||
|
|
55c6a5aa27 | ||
|
|
bd6bbf864a | ||
|
|
703335c2f1 | ||
|
|
c04fa496bf | ||
|
|
b9d19cfcb4 | ||
|
|
d8f093b226 | ||
|
|
63a2d83d26 | ||
|
|
7e42e98cb6 | ||
|
|
93deebe923 | ||
|
|
1eecf4b2c7 | ||
|
|
e7fc4ef1e7 | ||
|
|
3c54d4af15 | ||
|
|
ebfbf0e1f8 | ||
|
|
14aa4036e0 | ||
|
|
0c60d54c3f | ||
|
|
e510d279a2 | ||
|
|
be6f1f9c4a | ||
|
|
b1fb177e4b | ||
|
|
ec187fe109 | ||
|
|
9ec329b7ae | ||
|
|
d08bd4e866 | ||
|
|
941d5d7cd9 | ||
|
|
ef8d85773c | ||
|
|
e668d488b4 | ||
|
|
d4eb5facfd | ||
|
|
a1285b120b | ||
|
|
8650876e2b | ||
|
|
81ab12c89c | ||
|
|
4ddb8cec85 | ||
|
|
711c566498 | ||
|
|
cc5336900c | ||
|
|
291a8f5c1f | ||
|
|
8c0e98ed43 | ||
|
|
7a976c0239 | ||
|
|
fb557b9130 | ||
|
|
b56a930f60 | ||
|
|
5819ef346b | ||
|
|
b3dcc82c71 | ||
|
|
06e8308dc2 | ||
|
|
c8a9c9b84e | ||
|
|
e85b49c573 | ||
|
|
c7c6dc4e67 | ||
|
|
172d668416 | ||
|
|
b651dc845b | ||
|
|
c755f44153 | ||
|
|
ed28ce1874 | ||
|
|
194af0e985 | ||
|
|
ab3015df6b | ||
|
|
f36df159e0 | ||
|
|
61462cf57e | ||
|
|
427d186c86 | ||
|
|
84e9c47a67 | ||
|
|
18989d593a | ||
|
|
fc624359c5 | ||
|
|
1914573634 | ||
|
|
1fd36458a6 | ||
|
|
37ee0e568f | ||
|
|
3f8363a5b2 | ||
|
|
e6c0011789 | ||
|
|
eb37a495a2 | ||
|
|
54542f7f07 | ||
|
|
8e38910dd8 | ||
|
|
31b3f778fc | ||
|
|
ca3275757b | ||
|
|
501a314597 | ||
|
|
447c0bffdc | ||
|
|
46df59d77a | ||
|
|
8c85e240b7 | ||
|
|
2464d01891 | ||
|
|
66642a19c5 | ||
|
|
f7f4e92e0a | ||
|
|
2674bf22d8 | ||
|
|
351e0f3a0b | ||
|
|
ff7dc95e93 | ||
|
|
4e4d3418b3 | ||
|
|
3659b5b5c9 | ||
|
|
65e1b60fb7 | ||
|
|
e2427fe299 | ||
|
|
b08f882324 | ||
|
|
2a31ece0c6 | ||
|
|
cafa4211a6 | ||
|
|
82b50d3059 | ||
|
|
39cb582e07 | ||
|
|
fe6645a420 | ||
|
|
a49816f4eb | ||
|
|
7ac97f854c | ||
|
|
8f203852ca | ||
|
|
c342f7bce6 | ||
|
|
fe1c5df9d5 | ||
|
|
8aa7a55559 | ||
|
|
e9461586cb | ||
|
|
a91f2de26a | ||
|
|
5a4ae99283 | ||
|
|
437d030502 | ||
|
|
f3d45eff69 | ||
|
|
f22e39e22b | ||
|
|
7470a3b813 | ||
|
|
0b2e836e3d | ||
|
|
c3d57eef4f | ||
|
|
ca2571b438 | ||
|
|
6c926b8876 | ||
|
|
5517a913d4 | ||
|
|
ef7720ff06 | ||
|
|
daa13b4b30 | ||
|
|
9d4e237c09 | ||
|
|
883c169ef7 | ||
|
|
2b2b07f6b4 | ||
|
|
f338b68f36 | ||
|
|
c877b5fc70 | ||
|
|
6aeec81072 | ||
|
|
3f78c664eb | ||
|
|
3b7df1d5c2 | ||
|
|
b6fc063c75 | ||
|
|
7b56817ae6 | ||
|
|
75c8c3f50b | ||
|
|
be07ff2129 | ||
|
|
56846b258c | ||
|
|
f67310c8fa | ||
|
|
3dff3a1d4a | ||
|
|
142d30b1c6 | ||
|
|
c925e88475 | ||
|
|
56392e41d0 | ||
|
|
cd7b6450c6 | ||
|
|
de6a9f5811 | ||
|
|
d7295948fd | ||
|
|
f4c3e412a2 | ||
|
|
0d461e14d9 | ||
|
|
845c2571fe | ||
|
|
d5c02d1031 | ||
|
|
30e93a9372 | ||
|
|
e5cdcd190c | ||
|
|
271f1cd71b | ||
|
|
4abb790e12 | ||
|
|
89c9d4dfc4 | ||
|
|
5511c15921 | ||
|
|
e160f9a0f6 | ||
|
|
65ea341ed0 | ||
|
|
561827e896 | ||
|
|
1315aceb44 | ||
|
|
19afd89df5 | ||
|
|
cbcc893f64 | ||
|
|
a41bae3132 | ||
|
|
8ef8388c32 | ||
|
|
e2d4a0fde8 | ||
|
|
65b1d61295 | ||
|
|
1b38e73eb2 | ||
|
|
e2afee3275 | ||
|
|
7f2ecbd04f | ||
|
|
21dbaa1a03 | ||
|
|
09c1ea992c | ||
|
|
c8c327b6ab | ||
|
|
3bb801f579 | ||
|
|
ca49d8adb3 | ||
|
|
88b15797a0 | ||
|
|
28ebbd229c | ||
|
|
d7f6f46805 | ||
|
|
c41d6965ab | ||
|
|
602119fa41 | ||
|
|
4c2a9b47f5 | ||
|
|
5a26fefd6b | ||
|
|
85d26f7320 | ||
|
|
9487af1852 | ||
|
|
a5b09d7846 | ||
|
|
6ba5056c96 | ||
|
|
50b4d05ef5 | ||
|
|
13ebe6d0e1 | ||
|
|
95dedf0d80 | ||
|
|
89a9368166 | ||
|
|
c10e6159da | ||
|
|
c30693b8a8 | ||
|
|
7fc4f61182 | ||
|
|
b44ab88927 | ||
|
|
2ca81f965a | ||
|
|
9f4dc1e382 | ||
|
|
5ec9704e7f | ||
|
|
7ad0cfd5f6 | ||
|
|
ac8f679dfd | ||
|
|
a7ee2ef3a6 | ||
|
|
6e7edd9824 | ||
|
|
81b310086f | ||
|
|
cce57c7229 | ||
|
|
9cffa3dd67 | ||
|
|
7b3a59455d | ||
|
|
fefe5659d3 | ||
|
|
db72e22e61 | ||
|
|
a85ed709af | ||
|
|
e24d1a1261 | ||
|
|
d6f1bf18de | ||
|
|
97e5d923fa | ||
|
|
36762a6e46 | ||
|
|
40a9f9dd85 | ||
|
|
85a52523cf | ||
|
|
5ed7974099 | ||
|
|
70ca9ce2e0 | ||
|
|
af9f555e8f | ||
|
|
08b9e7b5b5 | ||
|
|
84f74c53b5 | ||
|
|
762bae907c | ||
|
|
2b738fa14b | ||
|
|
8aa745471b | ||
|
|
63950f572a | ||
|
|
81aab7e86f | ||
|
|
1fd5fd4832 | ||
|
|
ceaf478a84 | ||
|
|
61256f98ca | ||
|
|
818ed5d189 | ||
|
|
0ca00e5059 | ||
|
|
a2c743167d | ||
|
|
786641662c | ||
|
|
8380896deb | ||
|
|
1152689f34 | ||
|
|
d2afdc82f1 | ||
|
|
5bf90ae31d | ||
|
|
b533e3d3e1 | ||
|
|
4d9df309c8 | ||
|
|
65126365a6 | ||
|
|
0afe403057 | ||
|
|
d500353722 | ||
|
|
eea8b4dfbf | ||
|
|
20a9150ea3 | ||
|
|
c51e613568 | ||
|
|
69a887fb05 | ||
|
|
01c1d150c6 | ||
|
|
c51c09348e | ||
|
|
0a4df191a7 | ||
|
|
760995773e | ||
|
|
94ff8a9b04 | ||
|
|
10d5529f79 | ||
|
|
2b511b67c3 | ||
|
|
b595cdd31e | ||
|
|
e64870ca58 | ||
|
|
a40f8d5b3a | ||
|
|
61b8871ead | ||
|
|
4b5a3ed44d | ||
|
|
7d6b7b2691 | ||
|
|
3dba46b74f | ||
|
|
2767e31a28 | ||
|
|
40886bcf08 | ||
|
|
04dde74572 | ||
|
|
29f3ac065f | ||
|
|
d2b30b4f9c | ||
|
|
3a42663dea | ||
|
|
905d0ca409 | ||
|
|
7e2c78666e | ||
|
|
6a5f0225fe | ||
|
|
ddb01fca31 | ||
|
|
684a3cd49e | ||
|
|
ad5e42115b | ||
|
|
2d6b8d0c47 | ||
|
|
61471e5449 | ||
|
|
b6f7ab7a9c | ||
|
|
2d061be98e | ||
|
|
d6e97b8c76 | ||
|
|
9e44660746 | ||
|
|
03e6ca58ab | ||
|
|
e884cc1f75 | ||
|
|
94c63b0554 | ||
|
|
c7c8c40a70 | ||
|
|
91c726c706 | ||
|
|
c9805e7ac9 | ||
|
|
a4db0e40e3 | ||
|
|
305b55cb2a | ||
|
|
272ca1ac4f | ||
|
|
bbc6a1b38f | ||
|
|
525c235d3b | ||
|
|
0e93713464 | ||
|
|
8f60e103f9 | ||
|
|
9cc702241d | ||
|
|
0d61c44232 | ||
|
|
9a8faac316 | ||
|
|
05f710cb5c | ||
|
|
c6184769e7 | ||
|
|
515d1bd920 | ||
|
|
b19cb17bba | ||
|
|
c1e808bce6 | ||
|
|
d8b7292d4b | ||
|
|
edd5e2f5bc | ||
|
|
efc6cb73b2 | ||
|
|
ecd79dc34b | ||
|
|
cdfc8b825d | ||
|
|
301344c96d | ||
|
|
5279995c3b | ||
|
|
5176b06b59 | ||
|
|
ed61ac624d | ||
|
|
4da6e3ecee | ||
|
|
9e5561936a | ||
|
|
843cd0eff6 | ||
|
|
6b6ee934a1 | ||
|
|
aea57ffaf4 | ||
|
|
87b48661fa | ||
|
|
556fa44858 | ||
|
|
1c3e196508 | ||
|
|
9b6812ad0c | ||
|
|
b7944c7fa4 | ||
|
|
2fbedca746 | ||
|
|
340d04a48c | ||
|
|
460a383ffc | ||
|
|
52c4e3a256 | ||
|
|
4338f11eb1 | ||
|
|
42bab052e0 | ||
|
|
0cb377618e | ||
|
|
5e4d25b957 | ||
|
|
153d1853fa | ||
|
|
22a3448461 | ||
|
|
2cad869680 | ||
|
|
bc912a8ea4 | ||
|
|
db9176c284 | ||
|
|
06308210c0 | ||
|
|
63d9904370 | ||
|
|
caaeff5cb7 | ||
|
|
d8c93d3455 | ||
|
|
cb28e5fddc | ||
|
|
7d7ec1a00b | ||
|
|
a5bc8dfa3f | ||
|
|
d74f055180 | ||
|
|
73be6c35a6 | ||
|
|
24f74e1400 | ||
|
|
8900e069f3 | ||
|
|
1dfec119bb | ||
|
|
9e23d35f01 | ||
|
|
cd2b240308 | ||
|
|
749f366a4a | ||
|
|
420aaa92fa | ||
|
|
985698bbc3 | ||
|
|
f9c9139f20 | ||
|
|
f308e7541f | ||
|
|
cb7ccd7854 | ||
|
|
a4953028d0 | ||
|
|
3d6485588d | ||
|
|
51bb9fede7 | ||
|
|
269c429959 | ||
|
|
fcdc84a12a | ||
|
|
ecdd9734eb | ||
|
|
84fc2c65af | ||
|
|
c8849a17b6 | ||
|
|
d8b49218e9 | ||
|
|
e95023e8cc | ||
|
|
878710e2cf | ||
|
|
0677d0a810 | ||
|
|
9c98fea8f4 | ||
|
|
e958f33450 | ||
|
|
937080b011 | ||
|
|
aee5803dd2 | ||
|
|
f1f394b871 | ||
|
|
81a32b56f0 | ||
|
|
a6aae70a55 | ||
|
|
823eb23773 | ||
|
|
1ff51822df | ||
|
|
500147e130 | ||
|
|
d90de18d99 | ||
|
|
ba4f48662f | ||
|
|
d208437c05 | ||
|
|
01faa2e1d7 | ||
|
|
c630c387d6 | ||
|
|
a774718607 | ||
|
|
9430c70d0d | ||
|
|
f3e893fddb | ||
|
|
11e144ca64 | ||
|
|
fbceab707e | ||
|
|
5b2efc43b9 | ||
|
|
8d85d1aa2d | ||
|
|
b469fc7577 | ||
|
|
9db54831c8 | ||
|
|
9b88bde09a | ||
|
|
aad03a74c5 | ||
|
|
55eb6e2e5c | ||
|
|
ca07355873 | ||
|
|
11a59e26b2 | ||
|
|
ec6d9e3521 | ||
|
|
230accd31e | ||
|
|
a24a4a747e | ||
|
|
1e97b5c27a | ||
|
|
a314ea1aa3 | ||
|
|
a77128d5f7 | ||
|
|
1b2673367e | ||
|
|
ce10e91a60 | ||
|
|
5244b37d2c | ||
|
|
1239c6716b | ||
|
|
67f6258ab0 | ||
|
|
9f63172b43 | ||
|
|
bd8bfeb525 | ||
|
|
4918c4ef4b | ||
|
|
00d9ea9344 | ||
|
|
33537cde76 | ||
|
|
3d5db5c9ca | ||
|
|
fdf339514d | ||
|
|
937c4e485a | ||
|
|
837c060e1f | ||
|
|
00bacd7dde | ||
|
|
781031775e | ||
|
|
97aee3d375 | ||
|
|
34698751f2 | ||
|
|
3c31460f2f | ||
|
|
d5cb60b19c | ||
|
|
3a7cfe3208 | ||
|
|
f079cdad64 | ||
|
|
2822303138 | ||
|
|
e38de75520 | ||
|
|
2723604d3e | ||
|
|
82ee051c1a | ||
|
|
7bdf49b7e0 | ||
|
|
32521aba6b | ||
|
|
819c4cde1c | ||
|
|
776c486b1a | ||
|
|
bcd97120a4 | ||
|
|
312bfb8509 | ||
|
|
11c9a50931 | ||
|
|
94c0656bcd | ||
|
|
13313d0b25 | ||
|
|
3a20db1d76 | ||
|
|
00148b4cc8 | ||
|
|
a31546b1ff | ||
|
|
361b62b8e2 | ||
|
|
37327b77a7 | ||
|
|
7ef8a5bb11 | ||
|
|
11cfb8af32 | ||
|
|
7315f7d283 | ||
|
|
7d58eb718e | ||
|
|
651be76776 | ||
|
|
4a5c6f1d39 | ||
|
|
5745d71d6a | ||
|
|
db62b7421a | ||
|
|
4084c57789 | ||
|
|
f90bec985a | ||
|
|
90f911c529 | ||
|
|
6c88b106db | ||
|
|
cef69d1b97 | ||
|
|
f76a7fb331 | ||
|
|
3e7b8b0663 | ||
|
|
a6eb3ad037 | ||
|
|
9468749384 | ||
|
|
37417fa1bb | ||
|
|
217146351e | ||
|
|
818ec33cef | ||
|
|
cd1671830a | ||
|
|
a5fca87dd0 | ||
|
|
f06ce55626 | ||
|
|
853085e755 | ||
|
|
2b7accaf68 | ||
|
|
5533d93172 | ||
|
|
f0e8c865fe | ||
|
|
36400c0a83 | ||
|
|
c5383557b5 | ||
|
|
b645007884 | ||
|
|
63ac137206 | ||
|
|
808cbf8e0b | ||
|
|
8ed77ba0c7 | ||
|
|
66c74c51e4 | ||
|
|
7a272ef0ab | ||
|
|
60b817ec8e | ||
|
|
a41ecaf7cc | ||
|
|
d41afa0e53 | ||
|
|
a6284e05e5 | ||
|
|
e56f61441d | ||
|
|
7b4b7dffa2 | ||
|
|
77a214ef9c | ||
|
|
cf2723aafb | ||
|
|
499e99cfc5 | ||
|
|
a7b83e9fe3 | ||
|
|
964504b9c3 | ||
|
|
ec65e66c58 | ||
|
|
e694b080be | ||
|
|
70894b3938 | ||
|
|
fb7115fc13 | ||
|
|
a619fc4fef | ||
|
|
7c6c5fd06f | ||
|
|
2970568eab | ||
|
|
62cb3a610e | ||
|
|
f600c163ca | ||
|
|
77cb68e5ac | ||
|
|
c6314576aa | ||
|
|
515c183070 | ||
|
|
63b9c0e6b8 | ||
|
|
84893b1664 | ||
|
|
835668d96d | ||
|
|
2bce15dc6e | ||
|
|
8f1a212b52 | ||
|
|
5c08bde0fa | ||
|
|
98a84c031e | ||
|
|
ea1715384e | ||
|
|
d9a4ee4f65 | ||
|
|
62017c4661 | ||
|
|
702b98f510 | ||
|
|
69aafd7d6a | ||
|
|
c1559dd8c8 | ||
|
|
4df1895560 | ||
|
|
cac92da6e4 | ||
|
|
5d39d85215 | ||
|
|
99b4c43fd5 | ||
|
|
b2f59d6813 | ||
|
|
c7d79bb893 | ||
|
|
aa80c468c4 | ||
|
|
6008cba2db | ||
|
|
caf56671dc | ||
|
|
44eccf5ee4 | ||
|
|
8f96e4847c | ||
|
|
ae3e307f33 | ||
|
|
3fe0c758ed | ||
|
|
7240fb32d2 | ||
|
|
e23a3461ba | ||
|
|
727eb0cfd7 | ||
|
|
3796076360 | ||
|
|
ef9576f8c4 | ||
|
|
94fc4cb8a2 | ||
|
|
ccb248db91 | ||
|
|
e7de447725 | ||
|
|
7ff5429cb7 | ||
|
|
17c581b4aa | ||
|
|
41e5c2939f | ||
|
|
7e2ab51298 | ||
|
|
03f917fd9c | ||
|
|
d24e10a728 | ||
|
|
a17ac1c16e | ||
|
|
dd6b972be4 | ||
|
|
7d0c9ba0d9 | ||
|
|
6fa211634f | ||
|
|
1ca24c7f38 | ||
|
|
bcfbccae59 | ||
|
|
20b75ce6ed | ||
|
|
2ea15d7bf5 | ||
|
|
18e14c597f | ||
|
|
06d75999d7 | ||
|
|
7047a7cae6 | ||
|
|
20d2124867 | ||
|
|
3c7a85361e | ||
|
|
1ffbbdac99 | ||
|
|
eeccca8842 | ||
|
|
e2d2dbd2ba | ||
|
|
c61f0409fb | ||
|
|
806be39a6d | ||
|
|
6170b0d059 | ||
|
|
bca838495e | ||
|
|
e31a747250 | ||
|
|
4cf430e146 | ||
|
|
ef554cf6ec | ||
|
|
fcd91daee6 | ||
|
|
396c78b46a | ||
|
|
4677a3fd89 | ||
|
|
834ab5c6b9 | ||
|
|
1599e8f7ff | ||
|
|
7430704002 | ||
|
|
4d7b19c8cb | ||
|
|
40f535cf3c | ||
|
|
17425dcaf7 | ||
|
|
6b87fc64af | ||
|
|
35174b0348 | ||
|
|
fd53541719 | ||
|
|
7c68bff9f5 | ||
|
|
6c64991951 | ||
|
|
ca04ff0f37 | ||
|
|
7742575cab | ||
|
|
cdcdce702d | ||
|
|
c80e04fe8d | ||
|
|
c3b3ea107a | ||
|
|
1dc530c549 | ||
|
|
8b0b70e757 | ||
|
|
b508a629e8 | ||
|
|
abb0dadead | ||
|
|
e6fb18df56 | ||
|
|
43ba13f3bc | ||
|
|
ba705f5563 | ||
|
|
34e188ec1f | ||
|
|
b0d97dd170 | ||
|
|
7caeae61f5 | ||
|
|
416ace4c86 | ||
|
|
979041ee91 | ||
|
|
f0939b8af5 | ||
|
|
d7a7002bdd | ||
|
|
d9601de075 | ||
|
|
ef570558cf | ||
|
|
db3e81408f | ||
|
|
e8771cdea8 | ||
|
|
057eab2173 | ||
|
|
e2a7024eeb | ||
|
|
4650986dfa | ||
|
|
868b5e4617 | ||
|
|
f12860c7b1 | ||
|
|
8f751812a6 | ||
|
|
07a5092eb3 | ||
|
|
29c9c92ba6 | ||
|
|
edfa327158 | ||
|
|
869a6e66cc | ||
|
|
36abbfc048 | ||
|
|
cfc3e6d2f4 | ||
|
|
e38dbee6a6 | ||
|
|
68a7c857c0 | ||
|
|
80eef2ab8c | ||
|
|
1d652aa746 | ||
|
|
b07c43aa36 | ||
|
|
3880c8dc2c | ||
|
|
c0ab2ac297 | ||
|
|
de684dcb63 | ||
|
|
29ef1db86b | ||
|
|
5dfd8a61be | ||
|
|
358e2b3ccf | ||
|
|
4203065a06 | ||
|
|
bc4e0190a0 | ||
|
|
dd7004cbc9 | ||
|
|
bdc5c8f620 | ||
|
|
02d36e22ee | ||
|
|
331e8c4aa6 | ||
|
|
d622277c11 | ||
|
|
8f781ea4ab | ||
|
|
ebc1e5bf12 | ||
|
|
ce9a61622e | ||
|
|
e4891e699f | ||
|
|
d8765578c8 | ||
|
|
3a034ecec8 | ||
|
|
a3dea45089 | ||
|
|
de99c8a5e4 | ||
|
|
d3b8dbeea0 | ||
|
|
cff2f64155 | ||
|
|
7b8de35405 | ||
|
|
02ae0df2cc | ||
|
|
b386cea69d | ||
|
|
758ffb75a9 | ||
|
|
78fbc7f392 | ||
|
|
9c58472576 | ||
|
|
f8c4afc228 | ||
|
|
b169d65619 | ||
|
|
9bf0d4f804 | ||
|
|
4443f57f8a | ||
|
|
ea5d8590d5 | ||
|
|
5d5feb4c71 | ||
|
|
feb5351ec3 | ||
|
|
7630c25ef3 | ||
|
|
24238094e5 | ||
|
|
7cc9a03db8 | ||
|
|
2b2e8508d9 | ||
|
|
a70716f225 | ||
|
|
d9fcc46994 | ||
|
|
2d8acec6f0 | ||
|
|
cd06d8c63a | ||
|
|
a06ca55107 | ||
|
|
9686a9ba77 | ||
|
|
f7f4043ccd | ||
|
|
b7b55173a6 | ||
|
|
954253c7e2 | ||
|
|
cbe4d2cd7f | ||
|
|
40101129b5 | ||
|
|
4bb32c6d09 | ||
|
|
6e09ceeda6 | ||
|
|
d6a6a53623 | ||
|
|
4a97052708 | ||
|
|
3a4902ad4a | ||
|
|
77d14bc218 | ||
|
|
1d2a39a855 | ||
|
|
98b53b6b3d | ||
|
|
0148d8beaf | ||
|
|
5bfd84d3be | ||
|
|
351eb95feb | ||
|
|
56788f0933 | ||
|
|
017a376616 | ||
|
|
c5888cec66 | ||
|
|
3d5ad29eac | ||
|
|
c608636b7a | ||
|
|
1a97107b2d | ||
|
|
5ca3fbeaea | ||
|
|
44896db668 | ||
|
|
12efb87a23 | ||
|
|
9181be86ba | ||
|
|
053b01e036 | ||
|
|
86041d0968 | ||
|
|
bd87f63e91 | ||
|
|
b79b49e8f3 | ||
|
|
a0dde39d97 | ||
|
|
2e03868021 | ||
|
|
29384c2ba3 | ||
|
|
320743ab8d | ||
|
|
399e171083 | ||
|
|
184164b677 | ||
|
|
a8bd196234 | ||
|
|
239d425940 | ||
|
|
baa3c1461c | ||
|
|
fa8e398e90 | ||
|
|
6d9675a299 | ||
|
|
91e8ce62d4 | ||
|
|
06e641015f | ||
|
|
1c83059482 | ||
|
|
90b24d824a | ||
|
|
62457d0e48 | ||
|
|
90c96f7479 | ||
|
|
f87adebe41 | ||
|
|
8f24cc8d13 | ||
|
|
992802d196 | ||
|
|
8546d6730c | ||
|
|
0092289105 | ||
|
|
7c3923ad00 | ||
|
|
ef82039401 | ||
|
|
b01b9758e0 | ||
|
|
88b00f689b | ||
|
|
0a340d5d57 | ||
|
|
50545a83b8 | ||
|
|
4a57ff40d8 | ||
|
|
0238455a5a | ||
|
|
a53e963001 | ||
|
|
984608e23f | ||
|
|
f680c83d2d | ||
|
|
a79e51c76f | ||
|
|
2dfb349609 | ||
|
|
766f21b525 | ||
|
|
733dfa1467 | ||
|
|
63aa840b55 | ||
|
|
8b2d544576 | ||
|
|
d6046d2422 | ||
|
|
409939360f | ||
|
|
1d21f39fbc | ||
|
|
a477140a4b | ||
|
|
1bbf2d8ce6 | ||
|
|
40a65eec51 | ||
|
|
8431ebf2e8 | ||
|
|
bdcc0c5373 | ||
|
|
9cbf331533 | ||
|
|
77640714cc | ||
|
|
9457d95c3f | ||
|
|
c2ff949f2d | ||
|
|
ba8685a122 | ||
|
|
55464ed0dd | ||
|
|
fdf3691c87 | ||
|
|
aa6699cf3e | ||
|
|
b79b48baac | ||
|
|
5d22dbd99e | ||
|
|
4686bb5584 | ||
|
|
1f62b8f0b6 | ||
|
|
5759ed3728 | ||
|
|
827fbfb78f | ||
|
|
dc363de610 | ||
|
|
b9d6a235e3 | ||
|
|
ebc57fe494 | ||
|
|
e224ec4ae0 | ||
|
|
a5da347177 | ||
|
|
a257b15f86 | ||
|
|
a70cc53d82 | ||
|
|
1df2de9202 | ||
|
|
9e394ea349 | ||
|
|
b55685d610 | ||
|
|
2156aac046 | ||
|
|
6914465e3d | ||
|
|
f3847ec6f3 | ||
|
|
e1fe8d1d89 | ||
|
|
675c937a4a | ||
|
|
c8f53bdf8e | ||
|
|
3541d5adde | ||
|
|
b52da7c9fc | ||
|
|
de57daa3cd | ||
|
|
e70e011a9c | ||
|
|
99febb99f1 | ||
|
|
874d79be36 | ||
|
|
b33663c9f8 | ||
|
|
7e69fa39eb | ||
|
|
de06490539 | ||
|
|
40a30c24a0 | ||
|
|
de04c12d3c | ||
|
|
38fb53b058 | ||
|
|
ed617c5943 | ||
|
|
986337da0c | ||
|
|
8dd7621f29 | ||
|
|
88d862303d | ||
|
|
cc274ffebe | ||
|
|
28a108f79b | ||
|
|
b9f75bf7d2 | ||
|
|
39994d5797 | ||
|
|
8e28be6558 | ||
|
|
8a65bef004 | ||
|
|
b94dc5044b | ||
|
|
b853c00dd4 | ||
|
|
7a0bc81f48 | ||
|
|
10bc326490 | ||
|
|
1920f8158e | ||
|
|
0ed2ba0183 | ||
|
|
95adc0aec1 | ||
|
|
ebee80d10e | ||
|
|
63836185d9 | ||
|
|
d0195e0509 | ||
|
|
5d9bcd9918 | ||
|
|
db04c26d24 | ||
|
|
56b399655e | ||
|
|
24e15c0568 | ||
|
|
f0c516e82d | ||
|
|
f38203ef62 | ||
|
|
a77c026803 | ||
|
|
5b6306671c | ||
|
|
25610222bc | ||
|
|
c17f941fb9 | ||
|
|
ae6ab1d203 | ||
|
|
92accf99b4 | ||
|
|
b02702fe80 | ||
|
|
5c549ec6e5 | ||
|
|
af459a5a28 | ||
|
|
07770601f6 | ||
|
|
cc96b86b3a | ||
|
|
1547f4d8b2 | ||
|
|
75054fcc70 | ||
|
|
390b3b173b | ||
|
|
78daa65d28 | ||
|
|
3bbdd08d24 | ||
|
|
cec1f12918 | ||
|
|
d923ae2107 | ||
|
|
85931155e6 | ||
|
|
4fd87aca09 | ||
|
|
600e0ec7e3 | ||
|
|
51fbff1a4a | ||
|
|
03b1389ee5 | ||
|
|
8f014e9d82 | ||
|
|
cecc6f7561 | ||
|
|
62ba81c6a6 | ||
|
|
c5e3422fcd | ||
|
|
bd5a46b4ab | ||
|
|
3a972bbbab | ||
|
|
7768ea28bd | ||
|
|
75add44e86 | ||
|
|
7d94365cbf | ||
|
|
2d830fb8e7 | ||
|
|
633bf36fe7 | ||
|
|
c0a5e23d95 | ||
|
|
d3798344dd | ||
|
|
ed37460402 | ||
|
|
9b6ba65cdb | ||
|
|
42a9631926 | ||
|
|
676a8a6421 | ||
|
|
cdbf022ce0 | ||
|
|
db79e1271e | ||
|
|
d2b3efacf9 | ||
|
|
a2ab94f971 | ||
|
|
a0d92d764b | ||
|
|
649b78e3f2 | ||
|
|
e7df1c3e56 | ||
|
|
53833ae0c3 | ||
|
|
66b914774a | ||
|
|
fc89feec4e | ||
|
|
d311dbd9d5 | ||
|
|
f97aa67100 | ||
|
|
9a8add780c | ||
|
|
3b48f1d042 | ||
|
|
332b54e7a5 | ||
|
|
007b2f0c88 | ||
|
|
3f083862e7 | ||
|
|
39619d5277 | ||
|
|
d4fe01f9b9 | ||
|
|
6db61b4357 | ||
|
|
f245cbf7f2 | ||
|
|
6f2b04669f | ||
|
|
9a46081d0b | ||
|
|
3c7e507ca1 | ||
|
|
7117725e69 | ||
|
|
ba428c6cfe | ||
|
|
d76c924ad1 | ||
|
|
cad7debc5b | ||
|
|
40725aa2a2 | ||
|
|
6034891fed | ||
|
|
9dd9862d33 | ||
|
|
48c72e319b | ||
|
|
4b6208fd9c | ||
|
|
168904a159 | ||
|
|
28f1498ec3 | ||
|
|
d3028e10d3 | ||
|
|
5eb0b77a8a | ||
|
|
4aace5b95a | ||
|
|
044dbd4a65 | ||
|
|
5dbae7c9d7 | ||
|
|
ec44cb2761 | ||
|
|
b06cf55c0b | ||
|
|
b601f6a138 | ||
|
|
ddebc63488 | ||
|
|
35440822be | ||
|
|
f4c6bcfb8e | ||
|
|
6365c5c9ef | ||
|
|
dd0334d30d | ||
|
|
c462a44973 | ||
|
|
6f88f5db83 | ||
|
|
93617f62a2 | ||
|
|
61d5f39408 | ||
|
|
c1fddaa7dd | ||
|
|
188aa14d82 | ||
|
|
5c25dd5b6d | ||
|
|
7c579cf7b7 | ||
|
|
c755c823fa | ||
|
|
116588c237 | ||
|
|
f02c1e4dc7 | ||
|
|
c4e8cc1641 | ||
|
|
93e68ad147 | ||
|
|
7ba88a83f0 | ||
|
|
c9293327ce | ||
|
|
fa1f35a89e | ||
|
|
845ce7a711 | ||
|
|
2b40007563 | ||
|
|
217034c4a7 | ||
|
|
0b9d4f17ab | ||
|
|
7fb0ec12dd | ||
|
|
3581158a7b | ||
|
|
facfa73214 | ||
|
|
0ef4a86d42 | ||
|
|
d4ec4795c3 | ||
|
|
b13d0aa283 | ||
|
|
a6965342e7 | ||
|
|
752dfa5b7f | ||
|
|
87aa283f22 | ||
|
|
6598ae080f | ||
|
|
7c5e8a66e4 | ||
|
|
c9577bcdc5 | ||
|
|
8254c2e83c | ||
|
|
20a9ac841d | ||
|
|
ae86b75d89 | ||
|
|
93a0afe612 | ||
|
|
439027220b | ||
|
|
4a07272d7a | ||
|
|
81432b54a3 | ||
|
|
b84a6e0c02 | ||
|
|
37dc5a00e8 | ||
|
|
e6edf85fbe | ||
|
|
cb533a26f2 | ||
|
|
a7278f76a8 | ||
|
|
80bd32382f | ||
|
|
db21ced104 | ||
|
|
717c6555cb | ||
|
|
a412e4af5c | ||
|
|
3350bf1ac6 | ||
|
|
4aa3353a1d | ||
|
|
ff48a58537 | ||
|
|
08fa511d17 | ||
|
|
c295115ffc | ||
|
|
5fb14610ec | ||
|
|
e87c2350b7 | ||
|
|
44e691e840 | ||
|
|
b5a7234cf3 | ||
|
|
d12509957f | ||
|
|
f01e7b7e20 | ||
|
|
6aa156d956 | ||
|
|
ef5ea93de1 | ||
|
|
b4913f51f2 | ||
|
|
dc3e960e79 | ||
|
|
0fe79b5288 | ||
|
|
1f76bd1942 | ||
|
|
3545f80920 | ||
|
|
0b2d1564ef | ||
|
|
fdacf824b3 | ||
|
|
5c01a44644 | ||
|
|
4eb49d872b | ||
|
|
34e5f29419 | ||
|
|
f4910f0a8e | ||
|
|
c8c14611dc | ||
|
|
491201991e | ||
|
|
401f3574fd | ||
|
|
173a86172c | ||
|
|
9ecbff024a | ||
|
|
1b5be34be4 | ||
|
|
ceb3a997b6 | ||
|
|
b1ead7fec8 | ||
|
|
d534dbb006 | ||
|
|
e56377117b | ||
|
|
63483dc6c3 | ||
|
|
66ceafd010 | ||
|
|
dd793650c3 | ||
|
|
afd829307d | ||
|
|
09abdc0f12 | ||
|
|
ed4d17f578 | ||
|
|
aeeeb5a37b | ||
|
|
dcb2e51587 | ||
|
|
cbc2eaf908 | ||
|
|
8808031e7c | ||
|
|
23ac7213d3 | ||
|
|
add7b44d0b | ||
|
|
1e4b7599a7 | ||
|
|
54443b038a | ||
|
|
c3f03e3f95 | ||
|
|
18135624f6 | ||
|
|
11238d6b71 | ||
|
|
848f94b1e0 | ||
|
|
d47cfe9504 | ||
|
|
70dccff293 | ||
|
|
e40873710b | ||
|
|
b140ef3b7a | ||
|
|
3b7b74aa67 | ||
|
|
de8e5b2d69 | ||
|
|
afea33b0e3 | ||
|
|
b44fbc1e4f | ||
|
|
c4dee3dd8d | ||
|
|
091e024032 | ||
|
|
0e030f7f48 | ||
|
|
3341c9e3bf | ||
|
|
91ddc00f7e | ||
|
|
3049ba0b24 | ||
|
|
55a161fafe | ||
|
|
349af24c81 | ||
|
|
788f1c4b3e | ||
|
|
889af461c6 | ||
|
|
df86e59089 | ||
|
|
c6bf69cce4 | ||
|
|
e492012004 | ||
|
|
94c46f9881 | ||
|
|
1eaa9d32ea | ||
|
|
0e2e8d2e2a | ||
|
|
cfb39c6364 | ||
|
|
173499f496 | ||
|
|
1081049074 | ||
|
|
961dc85514 | ||
|
|
6434acd492 | ||
|
|
ccb27c89d8 | ||
|
|
8053256203 | ||
|
|
4abd7301fd | ||
|
|
d6fe5ab417 | ||
|
|
a739fbdf1d | ||
|
|
c90a1ab6dc | ||
|
|
05ef68e079 | ||
|
|
dee4a7f3c7 | ||
|
|
75753df0d8 | ||
|
|
30c5d78647 | ||
|
|
6bb4db3842 | ||
|
|
cc0907fcd7 | ||
|
|
20b018bcc7 | ||
|
|
aafe2fa8d0 | ||
|
|
ee7bd73b3f | ||
|
|
b67f3bd629 | ||
|
|
38b81e79f8 | ||
|
|
b73c549131 | ||
|
|
d21d9f0141 | ||
|
|
7bb85032a1 | ||
|
|
d90446ad28 | ||
|
|
8b5e2f5528 | ||
|
|
1bcc3ab7f1 | ||
|
|
3359c3cd45 | ||
|
|
af812f3c90 | ||
|
|
1e6201093b | ||
|
|
959ea69427 | ||
|
|
cea744a914 | ||
|
|
e8baf48764 | ||
|
|
41242a2ae1 | ||
|
|
f1dee488a7 | ||
|
|
3b4ff1818e | ||
|
|
497145b1b5 | ||
|
|
10eb41d319 | ||
|
|
4daf2e4a3d | ||
|
|
f3266a5111 | ||
|
|
27cac4e8b8 | ||
|
|
60b9a5b9da | ||
|
|
eaaa62a7f3 | ||
|
|
6ce732ec3d | ||
|
|
fb0cc61e09 | ||
|
|
be29b5daf8 | ||
|
|
f010adabd0 | ||
|
|
c93b263b1f | ||
|
|
15f34d6b54 | ||
|
|
d0eeb55999 | ||
|
|
08c0d39b23 | ||
|
|
d0ecde3277 | ||
|
|
45ec57afd7 | ||
|
|
b0cd053083 | ||
|
|
698a11be58 | ||
|
|
efb08fb1e1 | ||
|
|
f89bc8422e | ||
|
|
4bf4889a08 | ||
|
|
01ab8ba38e | ||
|
|
ae6d15e812 | ||
|
|
1abfbe1d34 | ||
|
|
d3095297c2 | ||
|
|
79d40d5644 | ||
|
|
008e305a84 | ||
|
|
0379611edd | ||
|
|
96d883c1c7 | ||
|
|
be0f262e37 | ||
|
|
1676adf071 | ||
|
|
a5d5630067 | ||
|
|
275956caba | ||
|
|
bf8ed87fc9 | ||
|
|
fb3afac097 | ||
|
|
eda8b037a9 | ||
|
|
8ef14f7a54 | ||
|
|
9974e35656 | ||
|
|
15bc5431b6 | ||
|
|
f767531d89 | ||
|
|
7285ada9dd | ||
|
|
b9b9773df9 | ||
|
|
c29a83b259 | ||
|
|
fa45e66da6 | ||
|
|
7cbcdddac9 | ||
|
|
a2f17900da | ||
|
|
fb7e97b8ad | ||
|
|
0c92a8a8e9 | ||
|
|
dcc59380e5 | ||
|
|
f9bf25f96d | ||
|
|
cbcbea8b08 | ||
|
|
62eb4f20da | ||
|
|
43d5311e5e | ||
|
|
a6f08a09d5 | ||
|
|
d93f5d7785 | ||
|
|
c7170e6dc2 | ||
|
|
bcf3ca7339 | ||
|
|
0388f5787a | ||
|
|
6514df9d96 | ||
|
|
8f5b9869dc | ||
|
|
580c5fe23f | ||
|
|
b0af5b26ba | ||
|
|
9e898932f6 | ||
|
|
1f873b93f6 | ||
|
|
f414707f11 | ||
|
|
505825056c | ||
|
|
38f7716738 | ||
|
|
a69d08b554 | ||
|
|
3ccdb64833 | ||
|
|
78d8bff599 | ||
|
|
a756fed70b | ||
|
|
3e2a1e3548 | ||
|
|
96b2f2b3a4 | ||
|
|
d81d7d4f68 | ||
|
|
20244c4fb5 | ||
|
|
b50d31ffe2 | ||
|
|
d709a44960 | ||
|
|
4e4d07ced6 | ||
|
|
d775bc9d7e | ||
|
|
85528761eb | ||
|
|
ad3eac9ddb | ||
|
|
613f9fccd2 | ||
|
|
3d1741c904 | ||
|
|
26be14ba67 | ||
|
|
daa0755920 | ||
|
|
305d60e09b | ||
|
|
d0029efd02 | ||
|
|
fb4d42bf5b | ||
|
|
20eec53b14 | ||
|
|
8343db44db | ||
|
|
649652e373 | ||
|
|
e37ed7c32d | ||
|
|
f9a525068b | ||
|
|
aa11e6d62e | ||
|
|
6802d152da | ||
|
|
3b40f393d8 | ||
|
|
020443ae8a | ||
|
|
1d0baccffc | ||
|
|
5426f0f329 | ||
|
|
790249dd1a | ||
|
|
446a201d25 | ||
|
|
b6538d5e18 | ||
|
|
edd6043059 | ||
|
|
fe4ffeb7f1 | ||
|
|
27b3875bfb | ||
|
|
e2dbe8a0a2 | ||
|
|
29fc7910b7 | ||
|
|
22e2fdc707 | ||
|
|
584786eb9f | ||
|
|
d803d9eaf9 | ||
|
|
bad6575d83 | ||
|
|
fbcb7ae836 | ||
|
|
2c1a1b10c8 | ||
|
|
155fb16a8a | ||
|
|
b1ab2ce96a | ||
|
|
f299ba6218 | ||
|
|
d167ad1923 | ||
|
|
93626e8154 | ||
|
|
d5040c091a | ||
|
|
868daef0f0 | ||
|
|
dc8e85e7f2 | ||
|
|
22d32d7ca3 | ||
|
|
2d500f8074 | ||
|
|
452cdc17c6 | ||
|
|
bcbfee0321 | ||
|
|
dab2e7ede3 | ||
|
|
d91acb8352 | ||
|
|
373dd8058e | ||
|
|
de6310e52a | ||
|
|
8c297a4a4c | ||
|
|
4eb5c817bc | ||
|
|
07e4b26b9e | ||
|
|
b63aa62985 | ||
|
|
ca701c0580 | ||
|
|
e37043a6a8 | ||
|
|
7c26975d14 | ||
|
|
47f8a43637 | ||
|
|
1238c0cefe | ||
|
|
4c35fda045 | ||
|
|
780124c2f5 | ||
|
|
38e0af41ce | ||
|
|
601e99eec0 | ||
|
|
745a2adee7 | ||
|
|
9e83234df1 | ||
|
|
e45eeadf7d | ||
|
|
9a778cea6b | ||
|
|
20823bfc87 | ||
|
|
be3d703692 | ||
|
|
e2df5739f0 | ||
|
|
7bb11d6436 | ||
|
|
80b84212cc | ||
|
|
4a1bee769b | ||
|
|
de99a7aeaf | ||
|
|
a0a02701b0 | ||
|
|
1314ae1555 | ||
|
|
67cf0e745c | ||
|
|
538a2acbf5 | ||
|
|
8d0e453666 | ||
|
|
ace04f0b30 | ||
|
|
f8e25d6c4a | ||
|
|
b2bc43da4f | ||
|
|
cb12e540d2 | ||
|
|
39955af2fa | ||
|
|
86e1f0615d | ||
|
|
dc81ab6dee | ||
|
|
f8cf6a65ae | ||
|
|
ba909c696d | ||
|
|
df0515cebf | ||
|
|
46c0e14d67 | ||
|
|
97d7733464 | ||
|
|
afda84ef09 | ||
|
|
61d6e74102 | ||
|
|
8d74258ce2 | ||
|
|
5dfba0b834 | ||
|
|
056370ec08 | ||
|
|
4e2c254558 | ||
|
|
38c2bdb447 | ||
|
|
0cee4717e2 | ||
|
|
221b04c466 | ||
|
|
237e9b7191 | ||
|
|
9457e44a88 | ||
|
|
80a9d40a44 | ||
|
|
0715325a63 | ||
|
|
0a026fef0f | ||
|
|
5fbf650d2d | ||
|
|
dabdde0c3f | ||
|
|
b6ca92a7ef | ||
|
|
62f7339170 | ||
|
|
eaec682ea7 | ||
|
|
12110a4442 | ||
|
|
df597f53b2 | ||
|
|
d7d40254d4 | ||
|
|
e7c4a2cce6 | ||
|
|
0eb1c0cea6 | ||
|
|
07d35a8513 | ||
|
|
16c887814e | ||
|
|
22a50b72fd | ||
|
|
a79e1b6ca1 | ||
|
|
995296ef53 | ||
|
|
f4d5996a88 | ||
|
|
ab732b5435 | ||
|
|
241ff5cb6e | ||
|
|
ec2169e079 | ||
|
|
c75662e720 | ||
|
|
d567fd4842 | ||
|
|
0776dfc80e | ||
|
|
79a662fb93 | ||
|
|
8b009b7ee9 | ||
|
|
c4face30cc | ||
|
|
d4dbb5cb51 | ||
|
|
1e27187652 | ||
|
|
3ff278291f | ||
|
|
4e8bf216df | ||
|
|
e7b9100f1c | ||
|
|
08aa9790f3 | ||
|
|
5f13fd2dca | ||
|
|
f4c6f42c38 | ||
|
|
6a10d08189 | ||
|
|
f646360af6 | ||
|
|
95f265ebbf | ||
|
|
39d0142993 | ||
|
|
da16172244 | ||
|
|
71aded0fae | ||
|
|
e173b3ea41 | ||
|
|
6c4f9466b9 | ||
|
|
664196c5ef | ||
|
|
c8d5044e7a | ||
|
|
d8c31fe560 | ||
|
|
516db855f5 | ||
|
|
9cdcf08ab1 | ||
|
|
326fa73b22 | ||
|
|
79cacefd07 | ||
|
|
07e28bfee6 | ||
|
|
980b017fbe | ||
|
|
4567fd1eb0 | ||
|
|
8c150c23f3 | ||
|
|
b4fd570269 | ||
|
|
681a845ef3 | ||
|
|
6e051d73c1 | ||
|
|
e381e1a313 | ||
|
|
2d03ff63cf | ||
|
|
309de631ed | ||
|
|
ad240cf52f | ||
|
|
99e3a47dde | ||
|
|
3fa810b7b8 | ||
|
|
bad7316b80 | ||
|
|
4757c36233 | ||
|
|
9ca6180207 | ||
|
|
30179ad977 | ||
|
|
b799609749 | ||
|
|
efb6994ae7 | ||
|
|
cd129fb055 | ||
|
|
880a977dd6 | ||
|
|
6eceffb403 | ||
|
|
795e33881c | ||
|
|
d310c3857f | ||
|
|
837e275bfd | ||
|
|
7be6031e19 | ||
|
|
8c53908cc4 | ||
|
|
589b54984a | ||
|
|
3f30ed5251 | ||
|
|
3f9181905a | ||
|
|
29f3a81666 | ||
|
|
5efc43260e | ||
|
|
e01794a07f | ||
|
|
5fde095a6f | ||
|
|
3237af2d85 | ||
|
|
953b666ebd | ||
|
|
417bb4bf37 | ||
|
|
a55213d88a | ||
|
|
ec84d190f4 | ||
|
|
1b0bad72de | ||
|
|
931055708d | ||
|
|
15fd570b49 | ||
|
|
ff9c6bac0a | ||
|
|
713111254b | ||
|
|
5b1462a3e8 | ||
|
|
2bf18d8bda | ||
|
|
7f2e643e62 | ||
|
|
ef172592b8 | ||
|
|
5e51a438a7 | ||
|
|
7f768059e6 | ||
|
|
ea70175a17 | ||
|
|
74a736691a | ||
|
|
86ff44adfc | ||
|
|
58b763e935 | ||
|
|
5c90cc59aa | ||
|
|
710ab44073 | ||
|
|
b28c5e6807 | ||
|
|
e5ead9ed44 | ||
|
|
d99897cf9d | ||
|
|
701a7cae02 | ||
|
|
068b6a5470 | ||
|
|
45d597ac49 | ||
|
|
a518d3f33f | ||
|
|
2f05228d91 | ||
|
|
335ae0105f | ||
|
|
62ce7a0e37 | ||
|
|
afa0fb8da1 | ||
|
|
763d835f4e | ||
|
|
0289872dcd | ||
|
|
2810141bd6 | ||
|
|
93ea22c69d | ||
|
|
daf76a311c | ||
|
|
b94271a725 | ||
|
|
1456aeedf2 | ||
|
|
4c6589a57e | ||
|
|
5b9a61b7db | ||
|
|
96e71695c5 | ||
|
|
2e7dd1bde3 | ||
|
|
2de543f5f9 | ||
|
|
a6ee20fca4 | ||
|
|
2d2f159e04 | ||
|
|
5568e0c2ad | ||
|
|
cb2cc0cb9e | ||
|
|
5e573ca980 | ||
|
|
dcb4a315a6 | ||
|
|
4bd36fc29e | ||
|
|
1a7971ec82 | ||
|
|
c1eda034b3 | ||
|
|
2294d722c7 | ||
|
|
7d053be6d1 | ||
|
|
7a508661eb | ||
|
|
c976242ce5 | ||
|
|
2b77c372a3 | ||
|
|
b61cc67997 | ||
|
|
de8db1a86d | ||
|
|
abfdf0e1c2 | ||
|
|
1d662e354b | ||
|
|
663e9a9b5e | ||
|
|
8535409962 | ||
|
|
cd5623b348 | ||
|
|
673e051bd5 | ||
|
|
66c949057e | ||
|
|
aed09f0c64 | ||
|
|
c05f306b0d | ||
|
|
739fb99ced | ||
|
|
7fc82ccead | ||
|
|
8ae947f59c | ||
|
|
d34b493b7d | ||
|
|
4df6e0ee7d | ||
|
|
aac4ef05e4 | ||
|
|
ea2f53e166 | ||
|
|
f144ec67ab | ||
|
|
7e42dcf0b5 | ||
|
|
87c4dc313a | ||
|
|
0d29339898 | ||
|
|
6d83f18490 | ||
|
|
0ba125c2d7 | ||
|
|
6abd120a5c | ||
|
|
5cf7e89ce6 | ||
|
|
353786cb61 | ||
|
|
586beea21e | ||
|
|
b80454a1f4 | ||
|
|
19f80cf506 | ||
|
|
d01100bb15 | ||
|
|
e0414e4eb9 | ||
|
|
1eb10e3c22 | ||
|
|
4772fbcd4a | ||
|
|
18b61e35be | ||
|
|
bb7faf0fb3 | ||
|
|
1603742adc | ||
|
|
8a830e40bb | ||
|
|
d8adcf5a84 | ||
|
|
1d9a404f77 | ||
|
|
26dcaf6078 | ||
|
|
a91e4014bf | ||
|
|
06af327e5e | ||
|
|
35e3b889c3 | ||
|
|
5ef6ba0258 | ||
|
|
12754ff135 | ||
|
|
d09a1e254f | ||
|
|
b89ee67daf | ||
|
|
7bd256c311 | ||
|
|
4add7cd0b3 | ||
|
|
5ac20cc4cf | ||
|
|
9d7b0487d5 | ||
|
|
59aa84f6c8 | ||
|
|
765b03c868 | ||
|
|
41ce3db8f9 | ||
|
|
00e3ef757c | ||
|
|
84dc0b2959 | ||
|
|
2e48099070 | ||
|
|
77779bbcd9 | ||
|
|
e08bc01c33 | ||
|
|
bf24ee369f | ||
|
|
876f12db89 | ||
|
|
8b84459e13 | ||
|
|
dccfcc9663 | ||
|
|
203e1cc9b9 | ||
|
|
b612f0cdec | ||
|
|
961301fbec | ||
|
|
2a38d99cf1 | ||
|
|
7903328c2d | ||
|
|
56d2b4a80c | ||
|
|
2c6ecaab5b | ||
|
|
45d289a65e | ||
|
|
318c8c68b0 | ||
|
|
fbd47a7f3b | ||
|
|
07533f5658 | ||
|
|
66b7e3e1f5 | ||
|
|
4fee4d1903 | ||
|
|
90d70beea2 | ||
|
|
a7297d2685 | ||
|
|
86ae704e86 | ||
|
|
2d1e993fe1 | ||
|
|
04b550e435 | ||
|
|
c492ea77f1 | ||
|
|
61d9112ba7 | ||
|
|
d7fe36de71 | ||
|
|
db0bd3fa2d | ||
|
|
551619e772 | ||
|
|
29bae230a4 | ||
|
|
83be49156f | ||
|
|
561ae102fb | ||
|
|
a05e69b855 | ||
|
|
8eb772d80b | ||
|
|
1590693547 | ||
|
|
66f93ee541 | ||
|
|
8893df118e | ||
|
|
2c77cb5ca5 | ||
|
|
8a101f9e9a | ||
|
|
ce98d0184d | ||
|
|
402dea3c8b | ||
|
|
223cf4b2b2 | ||
|
|
8fb7e79bb3 | ||
|
|
5a2d386976 | ||
|
|
96622533e0 | ||
|
|
8814ce05a9 | ||
|
|
c15148fc07 | ||
|
|
3404ebbbb8 | ||
|
|
51494cabc8 | ||
|
|
1eb326683e | ||
|
|
98bcfbef7e | ||
|
|
9416980096 | ||
|
|
b34505c086 | ||
|
|
a65edf52ca | ||
|
|
d1513f2575 | ||
|
|
12d20c35be | ||
|
|
9f822c0991 | ||
|
|
96c338859b | ||
|
|
a2464dce73 | ||
|
|
53476b723d | ||
|
|
ca92a0af5c | ||
|
|
6e6cd90f6d | ||
|
|
adc84e1f93 | ||
|
|
20687d915a | ||
|
|
12a34f0b09 | ||
|
|
e5e49e4347 | ||
|
|
9f61256e5e | ||
|
|
ac6e370a78 | ||
|
|
a9c2c2178a | ||
|
|
c67419bb55 | ||
|
|
631c270fc7 | ||
|
|
3c82dfc0a5 | ||
|
|
063574023b | ||
|
|
5f539b133b | ||
|
|
0bb52a6058 | ||
|
|
0db40bbb32 | ||
|
|
f66114e203 | ||
|
|
2fcc064b0f | ||
|
|
a4c441a1b7 | ||
|
|
cbb6e4d6f3 | ||
|
|
34361ccd1c | ||
|
|
c953936798 | ||
|
|
6fd2a6ef34 | ||
|
|
be05f1a71f | ||
|
|
0de65d9c0f | ||
|
|
a60f4e3bad | ||
|
|
ab30ea766b | ||
|
|
099a7c46b7 | ||
|
|
a7afcd9644 | ||
|
|
7c80eec755 | ||
|
|
07c50c20b6 | ||
|
|
08d841f6c5 | ||
|
|
6b144c5816 | ||
|
|
322cec0980 | ||
|
|
73a47b2853 | ||
|
|
0036ec2214 | ||
|
|
98dc69893e | ||
|
|
af48af5085 | ||
|
|
0b2279a113 | ||
|
|
ba63a44ec8 | ||
|
|
7c20d2f97c | ||
|
|
8e7369f0b5 | ||
|
|
5f80deb5b7 | ||
|
|
826fdbf342 | ||
|
|
7e5186c3c7 | ||
|
|
2bc5253725 | ||
|
|
f58787bb50 | ||
|
|
88a01d39ee | ||
|
|
ee88897b18 | ||
|
|
b26f9e316d | ||
|
|
0e486b45e1 | ||
|
|
f730d2fc27 | ||
|
|
26303c8617 | ||
|
|
a6485b61a4 | ||
|
|
7db07bca35 | ||
|
|
19bbe0fc1f | ||
|
|
2b2c2de583 | ||
|
|
b05534472c | ||
|
|
30aa8d469c | ||
|
|
c4282a3593 | ||
|
|
778107aae9 | ||
|
|
581af762f9 | ||
|
|
31e63b576b | ||
|
|
efb592dce5 | ||
|
|
fa5b936d7b | ||
|
|
4904bd53ef | ||
|
|
320ce372f5 | ||
|
|
7a803abec8 | ||
|
|
f34407fc43 | ||
|
|
b41bda569d | ||
|
|
fd2919fd1c | ||
|
|
dce8edf742 | ||
|
|
298e32aada | ||
|
|
2b2136867d | ||
|
|
c1830aa37c | ||
|
|
a3d4049c9c | ||
|
|
058b4bbe6c | ||
|
|
c4c7b2ed5b | ||
|
|
aee607ea81 | ||
|
|
5eea5939c0 | ||
|
|
ac53d64ffc | ||
|
|
9ebee8c03e | ||
|
|
93965fd98b | ||
|
|
59c25ef915 | ||
|
|
7e9c4848fb | ||
|
|
467b1ad4f1 | ||
|
|
f45bd18cc2 | ||
|
|
4f844abc0c | ||
|
|
b688dcd4ba | ||
|
|
d5e902679b | ||
|
|
eae21e1371 | ||
|
|
947c2e556d | ||
|
|
d68d4c2c76 | ||
|
|
1ef451c048 | ||
|
|
ec20748594 | ||
|
|
764f05b42a | ||
|
|
5c41e24b99 | ||
|
|
9725091233 | ||
|
|
8f8dfabe0f | ||
|
|
b9749bad61 | ||
|
|
31609a8aba | ||
|
|
28e8cc2675 | ||
|
|
033d3c92ab | ||
|
|
3484fcfd2a | ||
|
|
a8e18d7f99 | ||
|
|
db30b58ee9 | ||
|
|
3748488200 | ||
|
|
c53a20a577 | ||
|
|
dfcb2b610a | ||
|
|
95278a78ff | ||
|
|
400d577546 | ||
|
|
60cf6cec5e | ||
|
|
13901b9028 | ||
|
|
23454a918d | ||
|
|
fa64ecf513 | ||
|
|
bb8c0f1d9c | ||
|
|
6e33e063da | ||
|
|
e44893f91e | ||
|
|
f2c3fc20de | ||
|
|
d903fe400f | ||
|
|
22e4e4125a | ||
|
|
badddfe3d7 | ||
|
|
e14816dcfa | ||
|
|
4b83722a66 | ||
|
|
c798913fd2 | ||
|
|
636dbe5b95 | ||
|
|
ab28d0e09f | ||
|
|
a74efd285c | ||
|
|
3e1eb03517 | ||
|
|
fb90574d44 | ||
|
|
c0fb337dfb | ||
|
|
e7775bb9d9 | ||
|
|
64f2a67573 | ||
|
|
154e33a2c3 | ||
|
|
63ae2b8095 | ||
|
|
6165a6af6c | ||
|
|
4a70e3cd31 | ||
|
|
5ccf053cba | ||
|
|
5a6f5133af | ||
|
|
1a2b4f8260 | ||
|
|
5436050df1 | ||
|
|
d3f3946971 | ||
|
|
597a7f7b40 | ||
|
|
02f549d1dd | ||
|
|
0c096bb090 | ||
|
|
8bc6cd7fc9 | ||
|
|
65d9e5d1fc | ||
|
|
47c356692f | ||
|
|
ef9157174c | ||
|
|
7345807871 | ||
|
|
19b9d3737e | ||
|
|
db3be3d80a | ||
|
|
60c7522f22 | ||
|
|
60002793b0 | ||
|
|
9e5a4189d7 | ||
|
|
945cbdd3a7 | ||
|
|
f974fd0660 | ||
|
|
af9fdfa224 | ||
|
|
77c72d2bb3 | ||
|
|
8404617090 | ||
|
|
cc9a429689 | ||
|
|
44f50eba5b | ||
|
|
f4509e24c6 | ||
|
|
e17a40fdb2 | ||
|
|
181cb235df | ||
|
|
e3456cb74f | ||
|
|
e377552f12 | ||
|
|
9ab9e4894d | ||
|
|
5782979203 | ||
|
|
a4bb279da8 | ||
|
|
8df9549a42 | ||
|
|
de873c5f6f | ||
|
|
3425e929e9 | ||
|
|
60c5d96037 | ||
|
|
4b4c3ddb2f | ||
|
|
1ea50ea917 | ||
|
|
29f68d218d | ||
|
|
daad8bca69 | ||
|
|
f4408aa72c | ||
|
|
0117cd478b | ||
|
|
9ad22d7405 | ||
|
|
de4d0989e9 | ||
|
|
0c884c2669 | ||
|
|
5d7cfc1c10 | ||
|
|
fd2b070a6a | ||
|
|
455819566b | ||
|
|
b39113f0ae | ||
|
|
af0f1939a3 | ||
|
|
becb9fc43e | ||
|
|
8b9c274fdd | ||
|
|
0701f3496b | ||
|
|
09c03e8ca7 | ||
|
|
9285da6c12 | ||
|
|
6a0e16885d | ||
|
|
f0db135b1d | ||
|
|
4fb86bd699 | ||
|
|
c62082b924 | ||
|
|
e8f592c6e0 | ||
|
|
61935942cf | ||
|
|
f1d8bb84b7 | ||
|
|
7918448be2 | ||
|
|
f89f704a69 | ||
|
|
ae33de7e7c | ||
|
|
b23e474648 | ||
|
|
611bc26685 | ||
|
|
0dc39166ac | ||
|
|
8d970b7858 | ||
|
|
7033c4a182 | ||
|
|
2c0ca78265 | ||
|
|
0d3c03d1e3 | ||
|
|
b4dfce4a44 | ||
|
|
91ca3939b3 | ||
|
|
92db5d3a35 | ||
|
|
f252960aaf | ||
|
|
4aec39df7a | ||
|
|
15b19925f2 | ||
|
|
bfb376505b | ||
|
|
ec75ff5292 | ||
|
|
28eb0be4ad | ||
|
|
4d3b6b1486 | ||
|
|
d3fd9a188d | ||
|
|
41a15f90cb | ||
|
|
9c604a7886 | ||
|
|
8ad1d6fd97 | ||
|
|
a18e7eb089 | ||
|
|
1f5ea40bf6 | ||
|
|
44509e027c | ||
|
|
fe3758b1bb | ||
|
|
ebe2a30463 | ||
|
|
1216a26d13 | ||
|
|
65800853f4 | ||
|
|
ccb81179ab | ||
|
|
f2ea701d34 | ||
|
|
1d4aa27f1c | ||
|
|
bb08fe8113 | ||
|
|
e2099f0749 | ||
|
|
3c60feed02 | ||
|
|
5466e1b733 | ||
|
|
328f15c2ea | ||
|
|
873125abe1 | ||
|
|
5df818a19c | ||
|
|
a5dc3cd018 | ||
|
|
afe0e3c1d6 | ||
|
|
91e1f958f2 | ||
|
|
2a94ee55cc | ||
|
|
ccf612f536 | ||
|
|
c6fa0cc072 | ||
|
|
c282bb2fe1 | ||
|
|
72d18fd7e1 | ||
|
|
6802873cd1 | ||
|
|
1bdc46969c | ||
|
|
989ee0e281 | ||
|
|
0f27d646bb | ||
|
|
81aca500b3 | ||
|
|
50f2dded64 | ||
|
|
d26d94b62a | ||
|
|
2811f76714 | ||
|
|
67b21ad766 | ||
|
|
6d07d5dccb | ||
|
|
da0e3d5c8b | ||
|
|
b129fe908c | ||
|
|
f5c57e84c7 | ||
|
|
ceb4ef2642 | ||
|
|
1c235aa761 | ||
|
|
cfc8117c3c | ||
|
|
aa1f515fcf | ||
|
|
4fdd12bc48 | ||
|
|
34f04b1946 | ||
|
|
5770b9dc0e | ||
|
|
afe2b934de | ||
|
|
11fe6cfbb0 | ||
|
|
db10226005 | ||
|
|
a15b8077a3 | ||
|
|
a3836b5e17 | ||
|
|
f8d80422b2 | ||
|
|
9848f80630 | ||
|
|
6ea1732630 | ||
|
|
d97571ce0c | ||
|
|
1e3b866c8b | ||
|
|
2168838365 | ||
|
|
219021873d | ||
|
|
120b505361 | ||
|
|
347a2977fa | ||
|
|
c2e90864ac | ||
|
|
1027efd6e5 | ||
|
|
bd0de83d31 | ||
|
|
4d3523b677 | ||
|
|
03f15e0075 | ||
|
|
043779f4af | ||
|
|
25b5daf6a5 | ||
|
|
db444f5d7e | ||
|
|
16499d2fb8 | ||
|
|
ddff2b2982 | ||
|
|
e697ee72fa | ||
|
|
f98c59172b | ||
|
|
3badafaa82 | ||
|
|
cbf65a9355 | ||
|
|
365bc900b0 | ||
|
|
bd0da63f4c | ||
|
|
db0b663a3d | ||
|
|
fd7fe129e2 | ||
|
|
27253c360b | ||
|
|
ab226d16c8 | ||
|
|
d6394402b8 | ||
|
|
6be5fac953 | ||
|
|
c4435279de | ||
|
|
a9bdc8ad85 | ||
|
|
df2a5fc789 | ||
|
|
db87f9e15b | ||
|
|
5af2768d33 | ||
|
|
474695643f | ||
|
|
97ab88b39a | ||
|
|
3773d40201 | ||
|
|
b3fd01fe04 | ||
|
|
75c4ca77c2 | ||
|
|
692ddc60c7 | ||
|
|
1a296a8ca1 | ||
|
|
f5595dd4c3 | ||
|
|
11c0221f81 | ||
|
|
2d0aad3da9 | ||
|
|
ff19cb68b4 | ||
|
|
e62df3b3b1 | ||
|
|
1d024cc339 | ||
|
|
8bda91aafb | ||
|
|
fd9963e7eb | ||
|
|
dc81ca4f53 | ||
|
|
fb754f9bc7 | ||
|
|
7045f6571f | ||
|
|
50d9bccacf | ||
|
|
9064769185 | ||
|
|
5fc16bdbfb | ||
|
|
172e4e939d | ||
|
|
f914b728ff | ||
|
|
54857e843b | ||
|
|
64b34e98c7 | ||
|
|
1ccf74bca1 | ||
|
|
49a534a61b | ||
|
|
b8889c6a1f | ||
|
|
ccbc5c9d17 | ||
|
|
555e01ec87 | ||
|
|
7869f472c3 | ||
|
|
90f60f95f7 | ||
|
|
0f0a71a109 | ||
|
|
a43c8a2def | ||
|
|
cbafaf5d56 | ||
|
|
62e4e13f5a | ||
|
|
e69908abef | ||
|
|
6cb3cf8747 | ||
|
|
2f71a43420 | ||
|
|
f57ad57e62 | ||
|
|
03f5d9b102 | ||
|
|
6b17e1820c | ||
|
|
e0f6fca987 | ||
|
|
1fe95a5fe4 | ||
|
|
1257ecf100 | ||
|
|
4163304b24 | ||
|
|
d35f2c4a4f | ||
|
|
e5d5c5f9a7 | ||
|
|
6fcaec3ca8 | ||
|
|
a2892ad097 | ||
|
|
79c79146a5 | ||
|
|
cd37ba308c | ||
|
|
b35c0f7e39 | ||
|
|
14b1b649cb | ||
|
|
14c0307c09 | ||
|
|
42f22119f2 | ||
|
|
75f4771616 | ||
|
|
83f7cb2033 | ||
|
|
43a4c6198c | ||
|
|
8c2fafecd7 | ||
|
|
d004c0ccd1 | ||
|
|
406ae4e8c3 | ||
|
|
cd8bee1371 | ||
|
|
123392c549 | ||
|
|
42ffe213fd | ||
|
|
fd738e5c8b | ||
|
|
6f95b2c2ad | ||
|
|
f66c2b078c | ||
|
|
c6f5c120ba | ||
|
|
677e93430d | ||
|
|
a9a90c5545 | ||
|
|
ee0418e719 | ||
|
|
6fc1141477 | ||
|
|
b02920c43c | ||
|
|
049d249609 | ||
|
|
fc2a554415 | ||
|
|
42b806b500 | ||
|
|
aeb3ccaf09 | ||
|
|
86fdd91597 | ||
|
|
e68fea185d | ||
|
|
a3eaf9f473 | ||
|
|
e6a2b9f06e | ||
|
|
a78973702b | ||
|
|
354b745c39 | ||
|
|
ce6cd6acdc | ||
|
|
8e129143f1 | ||
|
|
6d50cbba6f | ||
|
|
7731878f36 | ||
|
|
9f659eef1b | ||
|
|
14cc642e54 | ||
|
|
c47852cd0a | ||
|
|
c3bfaa31ee | ||
|
|
832505a315 | ||
|
|
cb71667336 | ||
|
|
1032e97d58 | ||
|
|
b4271da13e | ||
|
|
e2dc5ef4f2 | ||
|
|
c75ee042a8 | ||
|
|
d474d518ca | ||
|
|
52b8dbcbb1 | ||
|
|
0d8d8f0426 | ||
|
|
ed12deae25 | ||
|
|
7f1e7c981d | ||
|
|
cc26688d82 | ||
|
|
a0fa3a6063 | ||
|
|
110a1a640d | ||
|
|
bbdc43c750 | ||
|
|
c73b9b4882 | ||
|
|
e249092f91 | ||
|
|
5c0b04bfc8 | ||
|
|
bc257f4951 | ||
|
|
e738ee0812 | ||
|
|
ebb2db17f3 | ||
|
|
4214293b76 | ||
|
|
ce3ee909bf | ||
|
|
09ba1e2470 | ||
|
|
68444b81cc | ||
|
|
d3c0b9a438 | ||
|
|
6e2f1f72c6 | ||
|
|
c82eec09b7 | ||
|
|
d4093d8c98 | ||
|
|
90616c82b6 | ||
|
|
de69fe1745 | ||
|
|
9bd42ac221 | ||
|
|
2f9a272696 | ||
|
|
552ea0356e | ||
|
|
ff6bd91ef7 | ||
|
|
835f20f872 | ||
|
|
1e9b35d18f | ||
|
|
3818e48218 | ||
|
|
5d63065057 | ||
|
|
ae41ed1d51 | ||
|
|
36ead2251a | ||
|
|
dc7093f68a | ||
|
|
6c79d2b008 | ||
|
|
422349c2d1 | ||
|
|
7dbfa0b203 | ||
|
|
57ea2ac039 | ||
|
|
a9e664e89d | ||
|
|
c0e2210da3 | ||
|
|
b13c6f283a | ||
|
|
8181b9a31c | ||
|
|
105e4f990d | ||
|
|
9670d74345 | ||
|
|
86e553e756 | ||
|
|
566ea9a110 | ||
|
|
f59f035a7e | ||
|
|
4c6d4f3caf | ||
|
|
8af87bac22 | ||
|
|
690567659c | ||
|
|
d9c7ee8976 | ||
|
|
95cf554de4 | ||
|
|
47a87a14f5 | ||
|
|
5e860b6850 | ||
|
|
9a983e7565 | ||
|
|
00197d131d | ||
|
|
9758f3c9d2 | ||
|
|
e7d7425b6e | ||
|
|
b566c81ae7 | ||
|
|
742837e7a1 | ||
|
|
5087dbd756 | ||
|
|
b893ae7c14 | ||
|
|
1f7863057f | ||
|
|
3dd3afd21f | ||
|
|
387d3b7335 | ||
|
|
782bfd058b | ||
|
|
c0d936fa4d | ||
|
|
63819c5757 | ||
|
|
08cbac6277 | ||
|
|
68c0a9aec4 | ||
|
|
1c585815a1 | ||
|
|
853ec7320f | ||
|
|
b8917a3c0e | ||
|
|
c4d70e7f9b | ||
|
|
7f1b11d19b | ||
|
|
02a32dea40 | ||
|
|
d9e20ea17a | ||
|
|
039c2fe0a6 | ||
|
|
a967e73f9e | ||
|
|
222e4154a1 | ||
|
|
56413ee94e | ||
|
|
93c07b2b1e | ||
|
|
27ed64a1af | ||
|
|
ad68106c3c | ||
|
|
a591cf1d21 | ||
|
|
03a4b8f5cf | ||
|
|
2d8d25808d | ||
|
|
a82fcce734 | ||
|
|
0734e136d0 | ||
|
|
2913120ff7 | ||
|
|
bd277b087a | ||
|
|
fc00966b8b | ||
|
|
495a76353f | ||
|
|
6ea225ed2a | ||
|
|
c1a5f59c42 | ||
|
|
49fb9108e9 | ||
|
|
4cb3b514ab | ||
|
|
c95a37130a | ||
|
|
29e82cc509 | ||
|
|
a4cb53fdb4 | ||
|
|
865dce6f68 | ||
|
|
1d02154d99 | ||
|
|
3a8d72db31 | ||
|
|
ec57a240d5 | ||
|
|
949f7587dc | ||
|
|
55cce646e0 | ||
|
|
4c3dce694a | ||
|
|
46d5b70391 | ||
|
|
08a102d182 | ||
|
|
1cf8e08d4b | ||
|
|
65a8b83150 | ||
|
|
f4c8db654c | ||
|
|
5c4d1c0259 | ||
|
|
a459f8b84c | ||
|
|
0d672420f7 | ||
|
|
2f7be0559a | ||
|
|
0623ae1f87 | ||
|
|
8ae946c2c5 | ||
|
|
bb75f6ffce | ||
|
|
fcfe83f0cc | ||
|
|
5cbcded929 | ||
|
|
2a95f792ec | ||
|
|
daa2808448 | ||
|
|
fc06ffdcee | ||
|
|
1601179081 | ||
|
|
7fe374b67e | ||
|
|
7a353bbe48 | ||
|
|
c103ff271b | ||
|
|
ab9e0f891a | ||
|
|
af85ef8cfb | ||
|
|
3cea6e5d0c | ||
|
|
6d1b0ae498 | ||
|
|
07d5b07748 | ||
|
|
d47b9cd5c0 | ||
|
|
d542c063d7 | ||
|
|
4bb3d33907 | ||
|
|
62bec25b6e | ||
|
|
3ba16f1773 | ||
|
|
c302030301 | ||
|
|
49abe2b11f | ||
|
|
544e7fce88 | ||
|
|
b9e7f018e2 | ||
|
|
f31049cf69 | ||
|
|
2b3b73b3f1 | ||
|
|
704bb5a848 | ||
|
|
01f7d7add2 | ||
|
|
831d8b6d36 | ||
|
|
2e9b6ead2e | ||
|
|
d71fd0ff2d | ||
|
|
6466bd4ba7 | ||
|
|
a97fa1abb6 | ||
|
|
6171e5cc6e | ||
|
|
456502893c | ||
|
|
443a90c7ba | ||
|
|
b8a72245dc | ||
|
|
d019f88e26 | ||
|
|
01cf4cc1ed | ||
|
|
396bbd83c8 | ||
|
|
92e0affb85 | ||
|
|
6552157894 | ||
|
|
af41e4892f | ||
|
|
09531b399a | ||
|
|
6251b77b28 | ||
|
|
0099b6d4a2 | ||
|
|
adb8de9754 | ||
|
|
8ac8b666bf | ||
|
|
095feddf73 | ||
|
|
4882a801d2 | ||
|
|
1a1736ca0e | ||
|
|
a7619771a6 | ||
|
|
6febcf3b94 | ||
|
|
3947deb7bd | ||
|
|
98855de71f | ||
|
|
8010e6220d | ||
|
|
09e6a6a4ad | ||
|
|
da772e6649 | ||
|
|
65b8f78ca1 | ||
|
|
23bcac2e55 | ||
|
|
e6a88eef35 | ||
|
|
6654c461a6 | ||
|
|
a48cb052c2 | ||
|
|
809d932087 | ||
|
|
19d906c5e4 | ||
|
|
2f933a90fd | ||
|
|
3607875714 | ||
|
|
fec8829041 | ||
|
|
f58e758a41 | ||
|
|
cebed0824f | ||
|
|
97120bd1f1 | ||
|
|
4237d0c7e3 | ||
|
|
243cdfefa2 | ||
|
|
a12a4cd8f2 | ||
|
|
980b8a2924 | ||
|
|
989dca5792 | ||
|
|
c9eb06bb70 | ||
|
|
af89906b81 | ||
|
|
de71b1b397 | ||
|
|
2decee1d87 | ||
|
|
6a184b069b | ||
|
|
48db615ac8 | ||
|
|
bb9caa4838 | ||
|
|
3c11c3fc16 | ||
|
|
6c1dbc5e61 | ||
|
|
21bf1dd1a3 | ||
|
|
78aa2b491c | ||
|
|
53d2c7e89f | ||
|
|
cdf78db629 | ||
|
|
15c5725829 | ||
|
|
96653170a2 | ||
|
|
eade89da48 | ||
|
|
9b86049964 | ||
|
|
9e2bbd50b6 | ||
|
|
0d18266ad1 | ||
|
|
83ee0534f2 | ||
|
|
8ad00025ad | ||
|
|
7d08a7c9cd | ||
|
|
c8d5a5319e | ||
|
|
a08fa721cf | ||
|
|
89bc3137ba | ||
|
|
4d8d30f897 | ||
|
|
d67e9468c0 | ||
|
|
7dda36ab97 | ||
|
|
32df3e80b1 | ||
|
|
3c33969d23 | ||
|
|
c2aec5ae36 | ||
|
|
7afc908c32 | ||
|
|
c66ecbdd29 | ||
|
|
e61bccab36 | ||
|
|
cfeef98261 | ||
|
|
c949548150 | ||
|
|
829ccfca88 | ||
|
|
950fa84d1c | ||
|
|
5666729fcf | ||
|
|
3a13886af9 | ||
|
|
db6e0ccf63 | ||
|
|
35f2ebc2aa | ||
|
|
60ac4a3e86 | ||
|
|
c966d34b07 | ||
|
|
a05d7059fb | ||
|
|
facc1dc944 | ||
|
|
df2ebbf384 | ||
|
|
e210471c7a | ||
|
|
f42d552dad | ||
|
|
edfd9e46b6 | ||
|
|
27644776f3 | ||
|
|
a4797dcc73 | ||
|
|
677312648b | ||
|
|
ace460c69b | ||
|
|
b922fe35ab | ||
|
|
222e2166ff | ||
|
|
7dc25ec425 | ||
|
|
980835981f | ||
|
|
e425adc230 | ||
|
|
2ea7ef327e | ||
|
|
6c9d33f05c | ||
|
|
c584b51726 | ||
|
|
3c8fa3382b | ||
|
|
e5c75259e0 | ||
|
|
f78e437a42 | ||
|
|
8f61b1d320 | ||
|
|
84f3c6f7e6 | ||
|
|
bfb6c001a0 | ||
|
|
f0fa9f2033 | ||
|
|
c82cc30968 | ||
|
|
b0cb14b515 | ||
|
|
7f86d2aa0a | ||
|
|
482dd4db76 | ||
|
|
0f44fd2290 | ||
|
|
690d461693 | ||
|
|
f1ca72aee9 | ||
|
|
a3a6fc0d15 | ||
|
|
4f4fe5f06b | ||
|
|
c59f47f4b5 | ||
|
|
0ae810b8c6 | ||
|
|
ac9f42a8fe | ||
|
|
eab7e56ece | ||
|
|
7329515a4d | ||
|
|
6d6bf2df3a | ||
|
|
3008bfbe0a | ||
|
|
9399517cfb | ||
|
|
b117fe222d | ||
|
|
62567264f0 | ||
|
|
c054b3b0de | ||
|
|
83fb5b4d1b | ||
|
|
07a86800c9 | ||
|
|
688b5f29b6 | ||
|
|
e311f17062 | ||
|
|
b4cdb22e24 | ||
|
|
b63194c0aa | ||
|
|
0bab32315c | ||
|
|
7e881dd850 | ||
|
|
d6aaafb069 | ||
|
|
731fcb8829 | ||
|
|
af0e94a4b7 | ||
|
|
deda869cc5 | ||
|
|
434089395a | ||
|
|
048985bdf4 | ||
|
|
70d6d4246d | ||
|
|
70befe900c | ||
|
|
9fd81bf6c7 | ||
|
|
a388fe3acb | ||
|
|
d4946d931a | ||
|
|
9812654bfa | ||
|
|
557426246c | ||
|
|
d0a3e8f789 | ||
|
|
50b37f2ddd | ||
|
|
e4197012f6 | ||
|
|
df00a1e0a3 | ||
|
|
41327bfb03 | ||
|
|
66ef72a040 | ||
|
|
af660bc631 | ||
|
|
18edb13e76 | ||
|
|
e05431c847 | ||
|
|
5c61f95093 | ||
|
|
9a407c727a | ||
|
|
cad655a15b | ||
|
|
1fa5c53b72 | ||
|
|
d01b1a706c | ||
|
|
678d5fc532 | ||
|
|
c4e01fa566 | ||
|
|
e694622b83 | ||
|
|
1d16c96ca4 | ||
|
|
ab1f34726b | ||
|
|
c9f201755e | ||
|
|
094ef592a7 | ||
|
|
e7493c38d1 | ||
|
|
6fd0fcfdb5 | ||
|
|
6da534a961 | ||
|
|
f26e43144e | ||
|
|
427529171c | ||
|
|
cd146cf822 | ||
|
|
ced412372c | ||
|
|
2f6e650c1b | ||
|
|
5c125ca9b4 | ||
|
|
89c0939d58 | ||
|
|
8ea955fb23 | ||
|
|
73a297f23f | ||
|
|
95f1382fbf | ||
|
|
80df01c8ee | ||
|
|
c2df864119 | ||
|
|
2cc9356c32 | ||
|
|
89234c0163 | ||
|
|
1b7fe286a6 | ||
|
|
7c2d797ed0 | ||
|
|
4d2eedc56b | ||
|
|
ece0d9301f | ||
|
|
cc10cd88d3 | ||
|
|
b9f308c832 | ||
|
|
6816bd8bad | ||
|
|
0e026de5bd | ||
|
|
a419ad201d | ||
|
|
a66a4f62bd | ||
|
|
e721d2204b | ||
|
|
f293cf6e81 | ||
|
|
0cde1d4969 | ||
|
|
bd49bd6e33 | ||
|
|
84dc1fa151 | ||
|
|
5858e862d9 | ||
|
|
8b004a549a | ||
|
|
4a53e4207c | ||
|
|
6510de5d29 | ||
|
|
a6191320a1 | ||
|
|
a75ff0c061 | ||
|
|
0d45eb91db | ||
|
|
95edbc16bb | ||
|
|
15a5e3c779 | ||
|
|
0433432e62 | ||
|
|
e7ca73bbad | ||
|
|
8d8374b8e2 | ||
|
|
4dc5bbe601 | ||
|
|
55371f9c78 | ||
|
|
af63f4098f | ||
|
|
2b4939a875 | ||
|
|
d5d22844ab | ||
|
|
7dab00be87 | ||
|
|
67582d388a | ||
|
|
0671e7d456 | ||
|
|
c9e6af96d0 | ||
|
|
ee48a56603 | ||
|
|
4c03f39437 | ||
|
|
d8d425c963 | ||
|
|
ec64ac9f1d | ||
|
|
7ed682c186 | ||
|
|
9dcb579741 | ||
|
|
19f837aa41 | ||
|
|
b267491934 | ||
|
|
a369dddde8 | ||
|
|
29d87d7c48 | ||
|
|
4847f50093 | ||
|
|
7c848b1253 | ||
|
|
607aa46032 | ||
|
|
430193891f | ||
|
|
cbb82ed635 | ||
|
|
1dfb72703a | ||
|
|
5edf377f4b | ||
|
|
43a33d267a | ||
|
|
c4048f4c7b | ||
|
|
501c89ae3a | ||
|
|
5899497aa7 | ||
|
|
2c758a9981 | ||
|
|
66eb99e506 | ||
|
|
b583140077 | ||
|
|
f378c93dd3 | ||
|
|
5d29fa5e62 | ||
|
|
54f04c9141 | ||
|
|
fc0c706100 | ||
|
|
02ac9fa0d7 | ||
|
|
ca95c75df3 | ||
|
|
4ff86795b9 | ||
|
|
30eaec29d3 | ||
|
|
742d38c9ec | ||
|
|
56a4597784 | ||
|
|
b2a7d3584b | ||
|
|
b8167f89e5 | ||
|
|
c916472b22 | ||
|
|
bdfe7c5e69 | ||
|
|
749475799a | ||
|
|
daf9c807b5 | ||
|
|
5dfecfdee3 | ||
|
|
d267c03ee2 | ||
|
|
864e3c7a50 | ||
|
|
747e1a818d | ||
|
|
f468dbf427 | ||
|
|
88572e90d4 | ||
|
|
41fc7dd73d | ||
|
|
c87c588c79 | ||
|
|
5ab4b57411 | ||
|
|
3c936c6957 | ||
|
|
0ca903a146 | ||
|
|
4e91919990 | ||
|
|
e023a68780 | ||
|
|
17452cd5b6 | ||
|
|
c76f2c8483 | ||
|
|
90207f9b68 | ||
|
|
0ec5371b61 | ||
|
|
8549aeb466 | ||
|
|
504d3cd131 | ||
|
|
83ed298eba | ||
|
|
a90f2cb156 | ||
|
|
1a54bafdd5 | ||
|
|
3cc186f7e4 | ||
|
|
a2497d7564 | ||
|
|
c01c882081 | ||
|
|
af279df5c9 | ||
|
|
1ea7ce2589 | ||
|
|
476c577bc1 | ||
|
|
e3b91c4d71 | ||
|
|
1e2d7acf4a | ||
|
|
1def120616 | ||
|
|
25ddf2c651 | ||
|
|
e5d384e808 | ||
|
|
63351553d8 | ||
|
|
4630740ac7 | ||
|
|
0ff4884d3a | ||
|
|
2dfdedf7b0 | ||
|
|
e1fa5fb180 | ||
|
|
4e47f9eb68 | ||
|
|
c4c8955bc2 | ||
|
|
f47b808478 | ||
|
|
493367da5e | ||
|
|
4ca185cf45 | ||
|
|
8f4effbb8d | ||
|
|
a4ba7f277e | ||
|
|
c3f97a0cf1 | ||
|
|
c1b8fc1233 | ||
|
|
a481638c03 | ||
|
|
c12dfc7b4d | ||
|
|
34bc527709 | ||
|
|
ee134d0d7c | ||
|
|
f795ee7fd9 | ||
|
|
1bba75881f | ||
|
|
34db2d9efa | ||
|
|
91dd308952 | ||
|
|
3d97e26cde | ||
|
|
104c1ecbec | ||
|
|
88266ec4e3 | ||
|
|
f5be159187 | ||
|
|
1194d86460 | ||
|
|
76d6dca63f | ||
|
|
1b69e62e2e | ||
|
|
e89839359f | ||
|
|
177d113cd9 | ||
|
|
853fa87012 | ||
|
|
a592ca25ff | ||
|
|
a8c9926fa7 | ||
|
|
bf5587fe09 | ||
|
|
7c8a27e894 | ||
|
|
d248b11ffc | ||
|
|
1282e010ee | ||
|
|
d5d1ca948e | ||
|
|
a08704e8ed | ||
|
|
2a8c0c0c08 | ||
|
|
2ae0c98b4c | ||
|
|
c427050a92 | ||
|
|
43a0829f44 | ||
|
|
e9b8b91861 | ||
|
|
6f104f5056 | ||
|
|
e720efabdc | ||
|
|
45cb770e41 | ||
|
|
3f1cc66767 | ||
|
|
d1f5118bb5 | ||
|
|
b90f1f786e | ||
|
|
c6f424201b | ||
|
|
a7db8cf7cd | ||
|
|
e2ed0f5e55 | ||
|
|
d000e3c8a9 | ||
|
|
cf465a1184 | ||
|
|
f3119824f4 | ||
|
|
ef069c1689 | ||
|
|
68e5a9dfda | ||
|
|
fd1a31afb6 | ||
|
|
6d7d4a15d9 | ||
|
|
518a7d0dcc | ||
|
|
a4bdfe6b7d | ||
|
|
281269aa0d | ||
|
|
33521ebd31 | ||
|
|
18ae08de8f | ||
|
|
3d3c4ba02f | ||
|
|
89917c1582 | ||
|
|
90c6758562 | ||
|
|
5be8084473 | ||
|
|
8a066a4ec0 | ||
|
|
b98237fa44 | ||
|
|
c4f9388668 | ||
|
|
0f44b27e54 | ||
|
|
10d05e1870 | ||
|
|
921557161d | ||
|
|
eb092b9f20 | ||
|
|
28ca8a9f9f | ||
|
|
d7be56df7a | ||
|
|
ce2c163f4b | ||
|
|
5272462368 | ||
|
|
42354a6728 | ||
|
|
754fe3df91 | ||
|
|
a7a0350c1a | ||
|
|
a8b2ca7cfe | ||
|
|
e82867820b | ||
|
|
13305d111e | ||
|
|
ae37fa2bc5 | ||
|
|
c81db752f6 | ||
|
|
5d9d71dee8 | ||
|
|
1f558d46a4 | ||
|
|
36eb899c3b | ||
|
|
e223d752cb | ||
|
|
fcb503e885 | ||
|
|
c1055234d8 | ||
|
|
35ec98cf13 | ||
|
|
66a9138666 | ||
|
|
279582ff21 | ||
|
|
a9540ffabe | ||
|
|
8f02cb83ed | ||
|
|
5e94c8be7b | ||
|
|
ea6e9af5da | ||
|
|
1bb72262ba | ||
|
|
61ace7bd97 | ||
|
|
1a417cc36f | ||
|
|
00a11f4343 | ||
|
|
491bbacddd | ||
|
|
1b9daa0adb | ||
|
|
ccdc981756 | ||
|
|
b44dcc9f30 | ||
|
|
479ceb14e4 | ||
|
|
50e41d1999 | ||
|
|
f6e2073de0 | ||
|
|
271aab9746 | ||
|
|
8d9bf5cf17 | ||
|
|
ceb7ed093e | ||
|
|
abdbcbb741 | ||
|
|
51ac07cd34 | ||
|
|
4b29698c6c | ||
|
|
f93285846a | ||
|
|
44347db52e | ||
|
|
f2749ff17f | ||
|
|
3735342dfa | ||
|
|
fed6e1e82f | ||
|
|
58a59b624a | ||
|
|
1338918a89 | ||
|
|
831e7cdb3f | ||
|
|
2b8583e652 | ||
|
|
b0220e4200 | ||
|
|
0d94ef10ce | ||
|
|
544a1381fc | ||
|
|
bc17ef896e | ||
|
|
0bcbf999fb | ||
|
|
0c4c44f4b6 | ||
|
|
486956b762 | ||
|
|
8d1ef5291f | ||
|
|
611627ca97 | ||
|
|
732225b781 | ||
|
|
58f88075c8 | ||
|
|
e17ef5eccf | ||
|
|
70c697f094 | ||
|
|
4b5625e0b4 | ||
|
|
7df48870df | ||
|
|
4a309c49e5 | ||
|
|
b45899bf90 | ||
|
|
5ae3c8acfe | ||
|
|
ef5df5f01b | ||
|
|
9af7451a61 | ||
|
|
7048c550b7 | ||
|
|
f035627f42 | ||
|
|
7659ff1f77 | ||
|
|
3df4062a55 | ||
|
|
02bbbe668b | ||
|
|
2b82c8fdc9 | ||
|
|
93abaed0c2 | ||
|
|
b7fff6d452 | ||
|
|
d09f01db90 | ||
|
|
e0c1a58b84 | ||
|
|
1b82649cca | ||
|
|
9317e2a36b | ||
|
|
51aff8ccdc | ||
|
|
bdd3ea45af | ||
|
|
3eb5c77ac4 | ||
|
|
ccc85784c6 | ||
|
|
46806e8607 | ||
|
|
95f914d6bd | ||
|
|
0f86011106 | ||
|
|
acdd3c0602 | ||
|
|
05b7237add | ||
|
|
51f4e9e160 | ||
|
|
7bbac35d6b | ||
|
|
d280fe457c | ||
|
|
b5772af4c3 | ||
|
|
7ab79bb76e | ||
|
|
00e3cc26de | ||
|
|
4b491c43ff | ||
|
|
41233a933e | ||
|
|
b84e553d34 | ||
|
|
19b9fa1996 | ||
|
|
6abdc4172b | ||
|
|
5e0c0137d3 | ||
|
|
3e468d1348 | ||
|
|
051af521b2 | ||
|
|
19b6ad3568 | ||
|
|
5ae514e9b8 | ||
|
|
826ee98165 | ||
|
|
f4c021a634 | ||
|
|
717ae1903e | ||
|
|
3cd7cdd4fb | ||
|
|
ff612799c0 | ||
|
|
d364b04cad | ||
|
|
212aef79aa | ||
|
|
9ca2b08a0e | ||
|
|
fc3ac88806 | ||
|
|
c550c4c212 | ||
|
|
f0df8a4a09 | ||
|
|
65923abcae | ||
|
|
c4f3009bb5 | ||
|
|
405a81c8f0 | ||
|
|
7a067a0f6e | ||
|
|
32c9f197b1 | ||
|
|
85151b23d3 | ||
|
|
fc0a4d90df | ||
|
|
9558616f59 | ||
|
|
5be5741ac3 | ||
|
|
80c0e6a4e0 | ||
|
|
9bc8f54233 | ||
|
|
a4c02cef54 | ||
|
|
935f4132ab | ||
|
|
a1cb82e7db | ||
|
|
146d782729 | ||
|
|
ac8bf5e939 | ||
|
|
2a70620844 | ||
|
|
7494b867a7 | ||
|
|
2874ee8609 | ||
|
|
e61145eabe | ||
|
|
4f01c574d2 | ||
|
|
06a471033c | ||
|
|
3761bfd26f | ||
|
|
07ad1b1de4 | ||
|
|
5725af55a8 | ||
|
|
d0f75ffda9 | ||
|
|
4117938140 | ||
|
|
f50c206d4f | ||
|
|
ed95cb791c | ||
|
|
9d0bf6d409 | ||
|
|
5e68a8200d | ||
|
|
15887994fe | ||
|
|
8bec8281d2 | ||
|
|
2e5a5812a9 | ||
|
|
744f9316a9 | ||
|
|
a6da9e8943 | ||
|
|
14d3bb273c | ||
|
|
37f8c0783d | ||
|
|
05a946e2bf | ||
|
|
a1b3e0778a | ||
|
|
93f8ade350 | ||
|
|
2a3a942b8e | ||
|
|
796583c0be | ||
|
|
df31b56cf3 | ||
|
|
e5f4446c82 | ||
|
|
0252495eca | ||
|
|
5e75a6b4a0 | ||
|
|
3002a4f9b3 | ||
|
|
f627c5e5a1 | ||
|
|
4d0a93fc3b | ||
|
|
3f040e7744 | ||
|
|
e572d544ef | ||
|
|
ab3f585a2b | ||
|
|
8b792c5fe0 | ||
|
|
f184116824 | ||
|
|
ab2c91ba20 | ||
|
|
f2a786254e | ||
|
|
7cbc8a0ede | ||
|
|
08bc644de1 | ||
|
|
f12125f2a4 | ||
|
|
4e86de7fc2 | ||
|
|
38796a0d40 | ||
|
|
8562bdfc4a | ||
|
|
74c96d8521 | ||
|
|
5f8ef64ca4 | ||
|
|
5a5f5318d1 | ||
|
|
db9e615e4d | ||
|
|
2aae0528b5 | ||
|
|
8d29bd5c32 | ||
|
|
77cb8530e5 | ||
|
|
bfd7404c85 | ||
|
|
836148a335 | ||
|
|
1ab00ac804 | ||
|
|
7a3db8f670 | ||
|
|
fd3676067a | ||
|
|
de61e4bf8c | ||
|
|
3d0de834e0 | ||
|
|
a897772df2 | ||
|
|
5726d796f9 | ||
|
|
8381960737 | ||
|
|
b61d41f569 | ||
|
|
af827b5645 | ||
|
|
13a01d76f7 | ||
|
|
2007f5ece4 | ||
|
|
a91a2c2609 | ||
|
|
0890fd521e | ||
|
|
4338cb305d | ||
|
|
84e02c828f | ||
|
|
be8707570c | ||
|
|
39602e0a98 | ||
|
|
bc2f2f5323 | ||
|
|
66e71282a0 | ||
|
|
bca56f88af | ||
|
|
35068631ec | ||
|
|
bd9557b11e | ||
|
|
0f2c19f40f | ||
|
|
4248ae7792 | ||
|
|
535a4205b1 | ||
|
|
ab14fc8440 | ||
|
|
86eea16271 | ||
|
|
52a9caa96e | ||
|
|
d8fe1a6e44 | ||
|
|
ae86090a03 | ||
|
|
87aa887b2f | ||
|
|
81378b5e25 | ||
|
|
6f615b8202 | ||
|
|
44c1266a8c | ||
|
|
920f720699 | ||
|
|
a7c394de6c | ||
|
|
4529675ca6 | ||
|
|
935909f24b | ||
|
|
7b61a0253c | ||
|
|
64e039cf8f | ||
|
|
65ae1f6f5a | ||
|
|
fa38ad3a74 | ||
|
|
fd500ac411 | ||
|
|
c6b5bb84e9 | ||
|
|
3b1f1a41dc | ||
|
|
d778f2dd8b | ||
|
|
287452169b | ||
|
|
6fdcd7f7e9 | ||
|
|
b16a49109f | ||
|
|
7d8cde2f0f | ||
|
|
a3fedb47cb | ||
|
|
095f08d443 | ||
|
|
a7948ee1da | ||
|
|
54eeb27f09 | ||
|
|
d0e57df1d8 | ||
|
|
b4df63f37f | ||
|
|
16a7ea2b75 | ||
|
|
3892ce3ed4 | ||
|
|
6c47de0170 | ||
|
|
8793196bb3 | ||
|
|
a3a9a8e3f1 | ||
|
|
bef5b48fce | ||
|
|
bc0ba2f524 | ||
|
|
1b387dabde | ||
|
|
fe8cb7c461 | ||
|
|
bfbf62d398 | ||
|
|
b22b845d57 | ||
|
|
104a62e24d | ||
|
|
4639911a04 | ||
|
|
6677f2f5d4 | ||
|
|
8adb16e1f6 | ||
|
|
9c9e93974e | ||
|
|
44a6604fe8 | ||
|
|
47a18666b6 | ||
|
|
a231ebc408 | ||
|
|
33ab2f9cc5 | ||
|
|
e56c1a5853 | ||
|
|
27a47d4e4a | ||
|
|
614e5e62af | ||
|
|
f45a2e7806 | ||
|
|
c03485b93e | ||
|
|
900e668cd6 | ||
|
|
2657e1194c | ||
|
|
ec5ad089a3 | ||
|
|
69a3354617 | ||
|
|
6e60d30970 | ||
|
|
d6fa1f9cbc | ||
|
|
bc8dccd4b5 | ||
|
|
14f48ec440 | ||
|
|
fd1b847077 | ||
|
|
ed3c0e42fb | ||
|
|
d72d6d925e | ||
|
|
4ae034b21c | ||
|
|
bbc7a1fff0 | ||
|
|
68ed3e4996 | ||
|
|
03ae6366ea | ||
|
|
af2f469b84 | ||
|
|
a64699d064 | ||
|
|
ac96baae66 | ||
|
|
15d9569fa4 | ||
|
|
bd99affb13 | ||
|
|
88166b7c73 | ||
|
|
3543ebb9a6 | ||
|
|
924f239d1b | ||
|
|
b641285cf4 | ||
|
|
c22ce3b9a2 | ||
|
|
64091b5463 | ||
|
|
09f0b251ab | ||
|
|
9bf22e5e13 | ||
|
|
360e1e327e | ||
|
|
4b8bc65fb7 | ||
|
|
1b9d91936b | ||
|
|
7457432c2a | ||
|
|
30bcc8e7f0 | ||
|
|
6232bf070d | ||
|
|
978b0ed811 | ||
|
|
24acd14da0 | ||
|
|
4950b69152 | ||
|
|
f25da27c58 | ||
|
|
12a5c4a311 | ||
|
|
3fcc163261 | ||
|
|
18a670ae29 | ||
|
|
f443fef091 | ||
|
|
58adf75c18 | ||
|
|
bee04bec44 | ||
|
|
01cc32004a | ||
|
|
5ede0d35f2 | ||
|
|
a1b8d951c1 | ||
|
|
880e2401e1 | ||
|
|
3434bd8460 | ||
|
|
239db35ba0 | ||
|
|
6310750168 | ||
|
|
133e41e3be | ||
|
|
b616cfa75c | ||
|
|
86a0b5f59a | ||
|
|
becf029583 | ||
|
|
f36f2357ef | ||
|
|
faa6d91f7c | ||
|
|
be5ecc6d0d | ||
|
|
00a18d54bb | ||
|
|
bad20c3e0b | ||
|
|
14a72dec15 | ||
|
|
7ef43caa1e | ||
|
|
244c4c6201 | ||
|
|
8d29947e32 | ||
|
|
29fc705672 | ||
|
|
2859db15fe | ||
|
|
9ab020518e | ||
|
|
838635ae4d | ||
|
|
a88e4932e6 | ||
|
|
56c94a2986 | ||
|
|
a4c3d9cb17 | ||
|
|
c31d392e19 | ||
|
|
a34a5c4db0 | ||
|
|
0441b9fb50 | ||
|
|
3e871ea33d | ||
|
|
54dda91726 | ||
|
|
0a19570f2c | ||
|
|
f280aa227e | ||
|
|
af34dc4d4d | ||
|
|
0ab1079c9c | ||
|
|
d2cb0bde91 | ||
|
|
5b34f7cf8c | ||
|
|
9e6e34eda0 | ||
|
|
cfd6cdc50f | ||
|
|
78346cda46 | ||
|
|
c547ab4570 | ||
|
|
939b54787e | ||
|
|
7b5b81504b | ||
|
|
5578a274f6 | ||
|
|
9e0c4a89f3 | ||
|
|
426728058c | ||
|
|
18390503b5 | ||
|
|
6e79b8404a | ||
|
|
4567272baa | ||
|
|
6ff3a37665 | ||
|
|
5420314784 | ||
|
|
f1dad4c9fb | ||
|
|
aba0b29c77 | ||
|
|
638263e8eb | ||
|
|
e2040b7194 | ||
|
|
0006e3b4bd | ||
|
|
06efd01032 | ||
|
|
1a20bd2f4d | ||
|
|
598cf61eb4 | ||
|
|
7b6772e77b | ||
|
|
c4bfa6d4d8 | ||
|
|
700ebc53f8 | ||
|
|
4db83293e5 | ||
|
|
0fcdc2e118 | ||
|
|
4b8d44aa2e | ||
|
|
915e716936 | ||
|
|
bb63232f55 | ||
|
|
43288b2e97 | ||
|
|
824eade8b2 | ||
|
|
df26058172 | ||
|
|
2f3219704e | ||
|
|
5c0532e6c6 | ||
|
|
a4d5b74b57 | ||
|
|
864106d336 | ||
|
|
5a612e792e | ||
|
|
e473dfc15e | ||
|
|
f931316dc8 | ||
|
|
e323a315e1 | ||
|
|
a55a2bcca0 | ||
|
|
f0c739c94c | ||
|
|
60db5abb54 | ||
|
|
9ab7356730 | ||
|
|
7f2537f9b1 | ||
|
|
6fe3ca1058 | ||
|
|
99dbb3abed | ||
|
|
053bdd0467 | ||
|
|
5554dce924 | ||
|
|
ec7a4734df | ||
|
|
40e3e71b48 | ||
|
|
5eed80c28e | ||
|
|
e2436ad796 | ||
|
|
7f0d82e960 | ||
|
|
5ecdca28fb | ||
|
|
67bd6eac99 | ||
|
|
f8ce8ece1e | ||
|
|
679521c8a0 | ||
|
|
f8c1644dee | ||
|
|
882e9731ac | ||
|
|
3cd882f618 | ||
|
|
98e333cef9 | ||
|
|
21a0063756 | ||
|
|
76f1a82746 | ||
|
|
e38dd346d9 | ||
|
|
a8184ec17d | ||
|
|
5fb751222b | ||
|
|
9910af04b0 | ||
|
|
6f5c86775b | ||
|
|
9c78af65e1 | ||
|
|
e14010da35 | ||
|
|
c61278f1ad | ||
|
|
576c668d84 | ||
|
|
f93a464b7d | ||
|
|
06271678a6 | ||
|
|
af0ea4d555 | ||
|
|
e448e87252 | ||
|
|
e4c54cc655 | ||
|
|
671338425a | ||
|
|
5237cf9eb1 | ||
|
|
33586f4bdf | ||
|
|
2cc05e1878 | ||
|
|
44c0f409a9 | ||
|
|
dde6d8e623 | ||
|
|
f387e28ed4 | ||
|
|
e0072630f3 | ||
|
|
fdf80c4584 | ||
|
|
68b3f1a8e6 | ||
|
|
f044f3ece2 | ||
|
|
46312cb44c | ||
|
|
167a548112 | ||
|
|
a381c91af7 | ||
|
|
eb0446bfe1 | ||
|
|
62fcf8402c | ||
|
|
f320f6e9b6 | ||
|
|
2c387f6ae6 | ||
|
|
702145dada | ||
|
|
a1973faa06 | ||
|
|
ac7fc587ee | ||
|
|
240487c258 | ||
|
|
4e07c7ef4b | ||
|
|
147791dbcd | ||
|
|
caf7056ce0 | ||
|
|
38e42cf71e | ||
|
|
584a12e1f5 | ||
|
|
d601919dc4 | ||
|
|
e5351c573b | ||
|
|
ccff5c5921 | ||
|
|
c3ef992219 | ||
|
|
c698d27a29 | ||
|
|
05162ebd59 | ||
|
|
6f991b3238 | ||
|
|
a14373a3e6 | ||
|
|
6a61dc47ac | ||
|
|
971729dfdd | ||
|
|
5c1feb2848 | ||
|
|
4455bf13c9 | ||
|
|
f28b0316f4 | ||
|
|
9999b3aa0c | ||
|
|
f9734dbc8b | ||
|
|
c1bd469086 | ||
|
|
900ba886ed | ||
|
|
ab0dce5dea | ||
|
|
6327670b6f | ||
|
|
7717b295bd | ||
|
|
b243d25399 | ||
|
|
59b339aba5 | ||
|
|
0d6ada13c4 | ||
|
|
dd32e61fcc | ||
|
|
cbf533aa7d | ||
|
|
5bccb1a555 | ||
|
|
139a364da6 | ||
|
|
60fcafa3b8 | ||
|
|
d7ef4adbc8 | ||
|
|
c89e5785d8 | ||
|
|
af910aa3f7 | ||
|
|
7e873880f5 | ||
|
|
1be0edaf03 | ||
|
|
609e498973 | ||
|
|
1585b7a142 | ||
|
|
c55ac659c1 | ||
|
|
1de52f7b64 | ||
|
|
26a6eb506e | ||
|
|
ede3e669c7 | ||
|
|
d00aa21c96 | ||
|
|
6181098f68 | ||
|
|
1d4c1624c4 | ||
|
|
899d05bc32 | ||
|
|
15856574d7 | ||
|
|
c09f8e97f7 | ||
|
|
4fb7f099de | ||
|
|
90c6283742 | ||
|
|
872d8584fb | ||
|
|
74afe433fb | ||
|
|
e85d6a39f8 | ||
|
|
2a41e55ff2 | ||
|
|
1e30811112 | ||
|
|
22f0d752ea | ||
|
|
4675bae804 | ||
|
|
57bd3c5a4e | ||
|
|
29db2dcd14 | ||
|
|
df153ee1a1 | ||
|
|
331c12269c | ||
|
|
d7844a1e39 | ||
|
|
d96b82500a | ||
|
|
74f1da81f2 | ||
|
|
472bc529f9 | ||
|
|
4266c0b279 |
15
.editorconfig
Normal file
15
.editorconfig
Normal file
@@ -0,0 +1,15 @@
|
||||
# editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
max_line_length = 250
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{js,json}]
|
||||
indent_size = 4
|
||||
indent_style = tab
|
||||
1
.eslintignore
Normal file
1
.eslintignore
Normal file
@@ -0,0 +1 @@
|
||||
modules/default/calendar/vendor/*
|
||||
30
.eslintrc.json
Normal file
30
.eslintrc.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"extends": ["eslint:recommended", "plugin:prettier/recommended", "plugin:jsdoc/recommended"],
|
||||
"plugins": ["prettier", "jsdoc"],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"mocha": true,
|
||||
"node": true
|
||||
},
|
||||
"globals": {
|
||||
"config": true,
|
||||
"Log": true,
|
||||
"MM": true,
|
||||
"Module": true,
|
||||
"moment": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
"ecmaVersion": 2017,
|
||||
"ecmaFeatures": {
|
||||
"globalReturn": true
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"eqeqeq": "error",
|
||||
"no-prototype-builtins": "off",
|
||||
"no-unused-vars": "off"
|
||||
}
|
||||
}
|
||||
48
.github/CONTRIBUTING.md
vendored
Normal file
48
.github/CONTRIBUTING.md
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
# Contribution Policy for MagicMirror²
|
||||
|
||||
Thanks for contributing to MagicMirror²!
|
||||
|
||||
We hold our code to standard, and these standards are documented below.
|
||||
|
||||
If you wish to run our linters, use `npm run lint` without any arguments.
|
||||
|
||||
### JavaScript: Run ESLint
|
||||
|
||||
We use [ESLint](https://eslint.org) on our JavaScript files.
|
||||
|
||||
Our ESLint configuration is in our .eslintrc.json and .eslintignore files.
|
||||
|
||||
To run ESLint, use `npm run lint:js`.
|
||||
|
||||
### CSS: Run StyleLint
|
||||
|
||||
We use [StyleLint](https://stylelint.io) to lint our CSS. Our configuration is in our .stylelintrc file.
|
||||
|
||||
To run StyleLint, use `npm run lint:style`.
|
||||
|
||||
### Submitting Issues
|
||||
|
||||
Please only submit reproducible issues.
|
||||
|
||||
If you're not sure if it's a real bug or if it's just you, please open a topic on the forum: [https://forum.magicmirror.builders/category/15/bug-hunt](https://forum.magicmirror.builders/category/15/bug-hunt)
|
||||
Problems installing or configuring your MagicMirror? Check out: [https://forum.magicmirror.builders/category/10/troubleshooting](https://forum.magicmirror.builders/category/10/troubleshooting)
|
||||
|
||||
When submitting a new issue, please supply the following information:
|
||||
|
||||
**Platform**: Place your platform here... give us your web browser/Electron version _and_ your hardware (Raspberry Pi 2/3, Windows, Mac, Linux, System V UNIX).
|
||||
|
||||
**Node Version**: Make sure it's version 0.12.13 or later.
|
||||
|
||||
**MagicMirror Version**: Now that the versions have split, tell us if you are using the PHP version (v1) or the newer JavaScript version (v2).
|
||||
|
||||
**Description**: Provide a detailed description about the issue and include specific details to help us understand the problem. Adding screenshots will help describing the problem.
|
||||
|
||||
**Steps to Reproduce**: List the step by step process to reproduce the issue.
|
||||
|
||||
**Expected Results**: Describe what you expected to see.
|
||||
|
||||
**Actual Results**: Describe what you actually saw.
|
||||
|
||||
**Configuration**: What does the used config.js file look like? Don't forget to remove any sensitive information!
|
||||
|
||||
**Additional Notes**: Provide any other relevant notes not previously mentioned. This is optional.
|
||||
42
.github/ISSUE_TEMPLATE.md
vendored
Normal file
42
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
## I'm not sure if this is a bug
|
||||
|
||||
If you're not sure if it's a real bug or if it's just you, please open a topic on the forum: [https://forum.magicmirror.builders/category/15/bug-hunt](https://forum.magicmirror.builders/category/15/bug-hunt)
|
||||
|
||||
## I'm having troubles installing or configuring MagicMirror
|
||||
|
||||
Problems installing or configuring your MagicMirror? Check out: [https://forum.magicmirror.builders/category/10/troubleshooting](https://forum.magicmirror.builders/category/10/troubleshooting)
|
||||
|
||||
## I found a bug in the MagicMirror installer
|
||||
|
||||
If you are facing an issue or found a bug while trying to install MagicMirror via the installer please report it in the respective GitHub repository:
|
||||
[https://github.com/sdetweil/MagicMirror_scripts](https://github.com/sdetweil/MagicMirror_scripts)
|
||||
|
||||
## I found a bug in the MagicMirror Docker image
|
||||
|
||||
If you are facing an issue or found a bug while running MagicMirror inside a Docker container please create an issue in the GitHub repository of the MagicMirror Docker image:
|
||||
[https://github.com/bastilimbach/docker-MagicMirror](https://github.com/bastilimbach/docker-MagicMirror)
|
||||
|
||||
---
|
||||
|
||||
## I found a bug in MagicMirror
|
||||
|
||||
Please make sure to only submit reproducible issues. You can safely remove everything above the dividing line.
|
||||
When submitting a new issue, please supply the following information:
|
||||
|
||||
**Platform**: Place your platform here... give us your web browser/Electron version _and_ your hardware (Raspberry Pi 2/3, Windows, Mac, Linux, System V UNIX).
|
||||
|
||||
**Node Version**: Make sure it's version 8 or later.
|
||||
|
||||
**MagicMirror Version**: Please let us now which version of MagicMirror you are running. It can be found in the `package.log` file.
|
||||
|
||||
**Description**: Provide a detailed description about the issue and include specific details to help us understand the problem. Adding screenshots will help describing the problem.
|
||||
|
||||
**Steps to Reproduce**: List the step by step process to reproduce the issue.
|
||||
|
||||
**Expected Results**: Describe what you expected to see.
|
||||
|
||||
**Actual Results**: Describe what you actually saw.
|
||||
|
||||
**Configuration**: What does the used config.js file look like? Don't forget to remove any sensitive information!
|
||||
|
||||
**Additional Notes**: Provide any other relevant notes not previously mentioned. This is optional.
|
||||
13
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
13
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
> Please send your pull requests the develop branch.
|
||||
> Don't forget to add the change to CHANGELOG.md.
|
||||
|
||||
**Note**: Sometimes the development moves very fast. It is highly
|
||||
recommended that you update your branch of `develop` before creating a
|
||||
pull request to send us your changes. This makes everyone's lives
|
||||
easier (including yours) and helps us out on the development team.
|
||||
Thanks!
|
||||
|
||||
- Does the pull request solve a **related** issue?
|
||||
- If so, can you reference the issue?
|
||||
- What does the pull request accomplish? Use a list if needed.
|
||||
- If it includes major visual changes please add screenshots.
|
||||
BIN
.github/header.png
vendored
Normal file
BIN
.github/header.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
.github/header.psd
vendored
Normal file
BIN
.github/header.psd
vendored
Normal file
Binary file not shown.
19
.github/stale.yml
vendored
Normal file
19
.github/stale.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 60
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
- under investigation
|
||||
- pr welcome
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: wontfix
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
15
.github/workflows/enforce-changelog.yml
vendored
Normal file
15
.github/workflows/enforce-changelog.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: "Enforce Changelog"
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled]
|
||||
|
||||
jobs:
|
||||
# Enforces the update of a changelog file on every pull request
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: dangoslen/changelog-enforcer@v1.6.1
|
||||
with:
|
||||
changeLogPath: 'CHANGELOG.md'
|
||||
skipLabels: 'Skip Changelog'
|
||||
35
.github/workflows/node-ci.js.yml
vendored
Normal file
35
.github/workflows/node-ci.js.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: Automated Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
pull_request:
|
||||
branches: [ master, develop ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.x, 14.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: |
|
||||
Xvfb :99 -screen 0 1024x768x16 &
|
||||
export DISPLAY=:99
|
||||
npm install
|
||||
npm run test:prettier
|
||||
npm run test:js
|
||||
npm run test:css
|
||||
npm run test:e2e
|
||||
npm run test:unit
|
||||
82
.gitignore
vendored
Normal file
82
.gitignore
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
# Various Node ignoramuses.
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
lib-cov
|
||||
coverage
|
||||
.lock-wscript
|
||||
build/Release
|
||||
/node_modules/**/*
|
||||
fonts/node_modules/**/*
|
||||
vendor/node_modules/**/*
|
||||
!/tests/node_modules/**/*
|
||||
jspm_modules
|
||||
.npm
|
||||
.node_repl_history
|
||||
.nyc_output/
|
||||
|
||||
# Visual Studio Code ignoramuses.
|
||||
.vscode/
|
||||
|
||||
# IDE Code ignoramuses.
|
||||
.idea/
|
||||
|
||||
# Various Windows ignoramuses.
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
$RECYCLE.BIN/
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
*.lnk
|
||||
|
||||
# Various OSX ignoramuses.
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
Icon
|
||||
._*
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# Various Linux ignoramuses.
|
||||
.fuse_hidden*
|
||||
.directory
|
||||
.Trash-*
|
||||
|
||||
# Ignore all modules except the default modules.
|
||||
/modules/**
|
||||
!/modules/default
|
||||
!/modules/default/**
|
||||
!/modules/README.md**
|
||||
|
||||
# Ignore changes to the custom css files.
|
||||
/css/custom.css
|
||||
|
||||
# Ignore users config file but keep the sample.
|
||||
/config/*
|
||||
!/config/config.js.sample
|
||||
|
||||
# Vim
|
||||
## swap
|
||||
[._]*.s[a-w][a-z]
|
||||
[._]s[a-w][a-z]
|
||||
|
||||
## diff patch
|
||||
*.orig
|
||||
*.rej
|
||||
*.bak
|
||||
5
.prettierignore
Normal file
5
.prettierignore
Normal file
@@ -0,0 +1,5 @@
|
||||
package-lock.json
|
||||
/config/**/*
|
||||
/vendor/**/*
|
||||
!/vendor/vendor.js
|
||||
.github/**/*
|
||||
3
.prettierrc.json
Normal file
3
.prettierrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"trailingComma": "none"
|
||||
}
|
||||
7
.stylelintrc.json
Normal file
7
.stylelintrc.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": ["stylelint-prettier/recommended"],
|
||||
"plugins": ["stylelint-prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": true
|
||||
}
|
||||
}
|
||||
920
CHANGELOG.md
Normal file
920
CHANGELOG.md
Normal file
@@ -0,0 +1,920 @@
|
||||
# MagicMirror² Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](https://semver.org/).
|
||||
|
||||
❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/donate) With your help we can continue to improve the MagicMirror²
|
||||
|
||||
## [2.14.0] - 2021-01-01
|
||||
|
||||
Special thanks to the following contributors: @Alvinger, @AndyPoms, @ashishtank, @bluemanos, @flopp999, @jakemulley, @jakobsarwary1, @marvai-vgtu, @mirontoli, @rejas, @sdetweil, @Snille & @Sub028.
|
||||
|
||||
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`.
|
||||
|
||||
### Added
|
||||
|
||||
- Added new log level "debug" to the logger.
|
||||
- Added new parameter "useKmh" to weather module for displaying wind speed as kmh.
|
||||
- Chuvash translation.
|
||||
- Added Weatherbit as a provider to Weather module.
|
||||
- Added SMHI as a provider to Weather module.
|
||||
- Added Hindi & Gujarati translation.
|
||||
- Added optional support for DEGREE position in Feels like translation.
|
||||
- Added support for variables in nunjucks templates for translate filter.
|
||||
- Added Chuvash translation.
|
||||
- Calendar: new options "limitDays" and "coloredEvents".
|
||||
- Added new option "limitDays" - limit the number of discreet days displayed.
|
||||
- Added new option "customEvents" - use custom symbol/color based on keyword in event title.
|
||||
- Added GitHub workflows for automated testing and changelog enforcement.
|
||||
|
||||
### Updated
|
||||
|
||||
- Merging .gitignore in the config-folder with the .gitignore in the root-folder.
|
||||
- Weather module - forecast now show TODAY and TOMORROW instead of weekday, to make it easier to understand.
|
||||
- Update dependencies to latest versions.
|
||||
- Update dependencies eslint, feedme, simple-git and socket.io to latest versions.
|
||||
- Update lithuanian translation.
|
||||
- Update config sample.
|
||||
- Highlight required version mismatch.
|
||||
- No select Text for TouchScreen use.
|
||||
- Corrected logic for timeFormat "relative" and "absolute".
|
||||
- Added missing function call in module.show()
|
||||
- Translator variables can have falsy values (e.g. empty string)
|
||||
- Fix issue with weather module with DEGREE label in FEELS like
|
||||
|
||||
### Deleted
|
||||
|
||||
- Removed Travis CI intergration.
|
||||
|
||||
### Fixed
|
||||
|
||||
- JSON Parse translation files with comments crashing UI. (#2149)
|
||||
- Calendar parsing where RRULE bug returns wrong date, add Windows timezone name support. (#2145, #2151)
|
||||
- Wrong node-ical version installed (package.json) requested version. (#2153)
|
||||
- Fix calendar fetcher subsequent timing. (#2160)
|
||||
- Rename Greek translation to correct ISO 639-1 alpha-2 code (gr > el). (#2155)
|
||||
- Add a space after icons of sunrise and sunset. (#2169)
|
||||
- Fix calendar when no DTEND record found in event, startDate overlay when endDate set. (#2177)
|
||||
- Fix windspeed convertion error in ukmetoffice weather provider. (#2189)
|
||||
- Fix console.debug not having timestamps. (#2199)
|
||||
- Fix calendar full day event east of UTC start time. (#2200)
|
||||
- Fix non-fullday recurring rule processing. (#2216)
|
||||
- Catch errors when parsing calendar data with ical. (#2022)
|
||||
- Fix Default Alert Module does not hide black overlay when alert is dismissed manually. (#2228)
|
||||
- Weather module - Always displays night icons when local is other then English. (#2221)
|
||||
- Update Node-ical 0.12.4 , fix invalid RRULE format in cal entries
|
||||
- Fix package.json for optional electron dependency (2378)
|
||||
- Update node-ical version again, 0.12.5, change RRULE fix (#2371, #2379)
|
||||
- Remove undefined objects from modules array (#2382)
|
||||
- Update node-ical version again, 0.12.7, change RRULE fix (#2371, #2379), node-ical now throws error (which we catch)
|
||||
- Update simple-git version to 2.31 unhandled promise rejection (#2383)
|
||||
|
||||
## [2.13.0] - 2020-10-01
|
||||
|
||||
Special thanks to the following contributors: @bryanzzhu, @bugsounet, @chamakura, @cjbrunner, @easyas314, @larryare, @oemel09, @rejas, @sdetweil & @sthuber90.
|
||||
|
||||
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`.
|
||||
|
||||
### Added
|
||||
|
||||
- `--dry-run` option adde in fetch call within updatenotification node_helper. This is to prevent
|
||||
MagicMirror from consuming any fetch result. Causes conflict with MMPM when attempting to check
|
||||
for updates to MagicMirror and/or MagicMirror modules.
|
||||
- Test coverage with Istanbul, run it with `npm run test:coverage`.
|
||||
- Add lithuanian language.
|
||||
- Added support in weatherforecast for OpenWeather onecall API.
|
||||
- Added config option to calendar-icons for recurring- and fullday-events.
|
||||
- Added current, hourly (max 48), and daily (max 7) weather forecasts to weather module via OpenWeatherMap One Call API.
|
||||
- Added eslint-plugin for jsdoc comments.
|
||||
- Added new configDeepMerge option for module developers.
|
||||
|
||||
### Updated
|
||||
|
||||
- Change incorrect weather.js default properties.
|
||||
- Cleaned up newsfeed module.
|
||||
- Cleaned up jsdoc comments.
|
||||
- Cleaned up clock tests.
|
||||
- Move lodash into devDependencies, update other dependencies.
|
||||
- Switch from ical to node-ical library.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix backward compatibility issues for Safari < 11.
|
||||
- Fix the use of "maxNumberOfDays" in the module "weatherforecast depending on the endpoint (forecast/daily or forecast)". [#2018](https://github.com/MichMich/MagicMirror/issues/2018)
|
||||
- Fix calendar display. Account for current timezone. [#2068](https://github.com/MichMich/MagicMirror/issues/2068)
|
||||
- Fix logLevel being set before loading config.
|
||||
- Fix incorrect namespace links in svg clockfaces. [#2072](https://github.com/MichMich/MagicMirror/issues/2072)
|
||||
- Fix weather/providers/weathergov for API guidelines. [#2045](https://github.com/MichMich/MagicMirror/issues/2045)
|
||||
- Fix "undefined" in weather modules header. [#1985](https://github.com/MichMich/MagicMirror/issues/1985)
|
||||
- Fix #2110, #2111, #2118: Recurring full day events should not use timezone adjustment. Just compare month/day.
|
||||
|
||||
## [2.12.0] - 2020-07-01
|
||||
|
||||
Special thanks to the following contributors: @AndreKoepke, @andrezibaia, @bryanzzhu, @chamakura, @DarthBrento, @Ekristoffe, @khassel, @Legion2, @ndom91, @radokristof, @rejas, @XBCreepinJesus & @ZoneMR.
|
||||
|
||||
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`.
|
||||
|
||||
### Added
|
||||
|
||||
- Added option to config the level of logging.
|
||||
- Added prettier for an even cleaner codebase.
|
||||
- Hide Sunrise/Sunset in Weather module.
|
||||
- Hide Sunrise/Sunset in Current Weather module.
|
||||
- Added Met Office DataHub (UK) provider.
|
||||
|
||||
### Updated
|
||||
|
||||
- Cleaned up alert module code.
|
||||
- Cleaned up check_config code.
|
||||
- Replaced grunt-based linters with their non-grunt equivalents.
|
||||
- Switch to most of the eslint:recommended rules and fix warnings.
|
||||
- Replaced insecure links with https ones.
|
||||
- Cleaned up all "no-undef" warnings from eslint.
|
||||
- Added location title wrapping for calendar module.
|
||||
- Updated the BG translation.
|
||||
|
||||
### Deleted
|
||||
|
||||
- Removed truetype (ttf) fonts.
|
||||
|
||||
### Fixed
|
||||
|
||||
- The broken modules due to Socket.io change from last release. [#1973](https://github.com/MichMich/MagicMirror/issues/1973)
|
||||
- Add backward compatibility for old module code in socketclient.js. [#1973](https://github.com/MichMich/MagicMirror/issues/1973)
|
||||
- Support multiple instances of calendar module with different config. [#1109](https://github.com/MichMich/MagicMirror/issues/1109)
|
||||
- Fix the use of "maxNumberOfDays" in the module "weatherforecast". [#2018](https://github.com/MichMich/MagicMirror/issues/2018)
|
||||
- Throw error when check_config fails. [#1928](https://github.com/MichMich/MagicMirror/issues/1928)
|
||||
- Bug fix related to 'maxEntries' not displaying Calendar events. [#2050](https://github.com/MichMich/MagicMirror/issues/2050)
|
||||
- Updated ical library to latest version. [#1926](https://github.com/MichMich/MagicMirror/issues/1926)
|
||||
- Fix config check after merge of prettier [#2109](https://github.com/MichMich/MagicMirror/issues/2109)
|
||||
|
||||
## [2.11.0] - 2020-04-01
|
||||
|
||||
🚨 READ THIS BEFORE UPDATING 🚨
|
||||
|
||||
In the past years the project has grown a lot. This came with a huge downside: poor maintainability. If I let the project continue the way it was, it would eventually crash and burn. More important: I would completely lose the drive and interest to continue the project. Because of this the decision was made to simplify the core by removing all side features like automatic installers and support for exotic platforms. This release (2.11.0) is the first real release that will reflect (parts) of these changes. As a result of this, some things might break. So before you continue make sure to backup your installation. Your config, your modules or better yet: your full MagicMirror folder. In other words: update at your own risk.
|
||||
|
||||
For more information regarding this major change, please check issue [#1860](https://github.com/MichMich/MagicMirror/issues/1860).
|
||||
|
||||
### Deleted
|
||||
|
||||
- Remove installers.
|
||||
- Remove externalized scripts.
|
||||
- Remove jshint dependency, instead eslint checks your config file now
|
||||
|
||||
### Added
|
||||
|
||||
- Brazilian translation for "FEELS".
|
||||
- Ukrainian translation.
|
||||
- Finnish translation for "PRECIP", "UPDATE_INFO_MULTIPLE" and "UPDATE_INFO_SINGLE".
|
||||
- Added the ability to hide the temp label and weather icon in the `currentweather` module to allow showing only information such as wind and sunset/rise.
|
||||
- The `clock` module now optionally displays sun and moon data, including rise/set times, remaining daylight, and percent of moon illumination.
|
||||
- Added Hebrew translation.
|
||||
- Add HTTPS support and update config.js.sample
|
||||
- Run tests on long term support and latest stable version of nodejs
|
||||
- Added the ability to configure a list of modules that shouldn't be update checked.
|
||||
- Run linters on git commits
|
||||
- Added date functionality to compliments: display birthday wishes or celebrate an anniversary
|
||||
- Add HTTPS support for clientonly-mode.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Force declaration of public ip address in config file (ISSUE #1852)
|
||||
- Fixes `run-start.sh`: If running in docker-container, don't check the environment, just start electron (ISSUE #1859)
|
||||
- Fix calendar time offset for recurring events crossing Daylight Savings Time (ISSUE #1798)
|
||||
- Fix regression in currentweather module causing 'undefined' to show up when config.hideTemp is false
|
||||
- Fix FEELS translation for Croatian
|
||||
- Fixed weather tests [#1840](https://github.com/MichMich/MagicMirror/issues/1840)
|
||||
- Fixed Socket.io can't be used with Reverse Proxy in serveronly mode [#1934](https://github.com/MichMich/MagicMirror/issues/1934)
|
||||
- Fix update checking skipping 3rd party modules the first time
|
||||
|
||||
### Changed
|
||||
|
||||
- Remove documentation from core repository and link to new dedicated docs site: [docs.magicmirror.builders](https://docs.magicmirror.builders).
|
||||
- Updated config.js.sample: Corrected some grammar on `config.js.sample` comment section.
|
||||
- Removed `run-start.sh` script and update start commands:
|
||||
- To start using electron, use `npm run start`.
|
||||
- To start in server only mode, use `npm run server`.
|
||||
- Remove redundant logging from modules.
|
||||
- Timestamp in log output now also contains the date
|
||||
- Turkish translation.
|
||||
- Option to configure the size of the currentweather module.
|
||||
- Changed "Gevoelstemperatuur" to "Voelt als" shorter text.
|
||||
|
||||
## [2.10.1] - 2020-01-10
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated README.md: Added links to the official documentation website and remove links to broken installer.
|
||||
|
||||
## [2.10.0] - 2020-01-01
|
||||
|
||||
Special thanks to @sdetweil for all his great contributions!
|
||||
|
||||
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`.
|
||||
|
||||
### Added
|
||||
|
||||
- Timestamps in log output.
|
||||
- Padding in dateheader mode of the calendar module.
|
||||
- New upgrade script to help users consume regular updates installers/upgrade-script.sh.
|
||||
- New script to help setup pm2, without install installers/fixuppm2.sh.
|
||||
|
||||
### Updated
|
||||
|
||||
- Updated lower bound of `lodash` and `helmet` dependencies for security patches.
|
||||
- Updated compliments.js to handle newline in text, as textfields to not interpolate contents.
|
||||
- Updated raspberry.sh installer script to handle new platform issues, split node/npm, pm2, and screen saver changes.
|
||||
- Improve handling for armv6l devices, where electron support has gone away, add optional serveronly config option.
|
||||
- Improved run-start.sh to handle for serveronly mode, by choice, or when electron not available.
|
||||
- Only check for xwindows running if not on macOS.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed issue in weatherforecast module where predicted amount of rain was not using the decimal symbol specified in config.js.
|
||||
- Module header now updates correctly, if a module need to dynamically show/hide its header based on a condition.
|
||||
- Fix handling of config.js for serverOnly mode commented out.
|
||||
- Fixed issue in calendar module where the debug script didn't work correctly with authentication.
|
||||
- Fixed issue that some full day events were not correctly recognized as such.
|
||||
- Display full day events lasting multiple days as happening today instead of some days ago if they are still ongoing.
|
||||
|
||||
## [2.9.0] - 2019-10-01
|
||||
|
||||
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`. If you are having issues running Electron, make sure your [Raspbian is up to date](https://www.raspberrypi.org/documentation/raspbian/updating.md).
|
||||
|
||||
### Added
|
||||
|
||||
- Spanish translation for "PRECIP".
|
||||
- Adding a Malay (Malaysian) translation for MagicMirror².
|
||||
- Add test check URLs of vendors 200 and 404 HTTP CODE.
|
||||
- Add tests for new weather module and helper to stub ajax requests.
|
||||
|
||||
### Updated
|
||||
|
||||
- Updatenotification module: Display update notification for a limited (configurable) time.
|
||||
- Enabled e2e/vendor_spec.js tests.
|
||||
- The css/custom.css will be renamed after the next release. We've added into `run-start.sh` an instruction by GIT to ignore with `--skip-worktree` and `rm --cached`. [#1540](https://github.com/MichMich/MagicMirror/issues/1540)
|
||||
- Disable sending of notification CLOCK_SECOND when displaySeconds is false.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Updatenotification module: Properly handle race conditions, prevent crash.
|
||||
- Send `NEWS_FEED` notification also for the first news messages which are shown.
|
||||
- Fixed issue where weather module would not refresh data after a network or API outage. [#1722](https://github.com/MichMich/MagicMirror/issues/1722)
|
||||
- Fixed weatherforecast module not displaying rain amount on fallback endpoint.
|
||||
- Notifications CLOCK_SECOND & CLOCK_MINUTE being from startup instead of matched against the clock and avoid drifting.
|
||||
|
||||
## [2.8.0] - 2019-07-01
|
||||
|
||||
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`. If you are having issues running Electron, make sure your [Raspbian is up to date](https://www.raspberrypi.org/documentation/raspbian/updating.md).
|
||||
|
||||
### Added
|
||||
|
||||
- Option to show event location in calendar
|
||||
- Finnish translation for "Feels" and "Weeks"
|
||||
- Russian translation for “Feels”
|
||||
- Calendar module: added `nextDaysRelative` config option
|
||||
- Add `broadcastPastEvents` config option for calendars to include events from the past `maximumNumberOfDays` in event broadcasts
|
||||
- Added feature to broadcast news feed items `NEWS_FEED` and updated news items `NEWS_FEED_UPDATED` in default [newsfeed](https://github.com/MichMich/MagicMirror/tree/develop/modules/default/newsfeed) module (when news is updated) with documented default and `config.js` options in [README.md](https://github.com/MichMich/MagicMirror/blob/develop/modules/default/newsfeed/README.md)
|
||||
- Added notifications to default `clock` module broadcasting `CLOCK_SECOND` and `CLOCK_MINUTE` for the respective time elapsed.
|
||||
- Added UK Met Office Datapoint feed as a provider in the default weather module.
|
||||
- Added new provider class
|
||||
- Added suncalc.js dependency to calculate sun times (not provided in UK Met Office feed)
|
||||
- Added "tempUnits" and "windUnits" to allow, for example, temp in metric (i.e. celsius) and wind in imperial (i.e. mph). These will override "units" if specified, otherwise the "units" value will be used.
|
||||
- Use Feels Like temp from feed if present
|
||||
- Optionally display probability of precipitation (PoP) in current weather (UK Met Office data)
|
||||
- Automatically try to fix eslint errors by passing `--fix` option to it
|
||||
- Added sunrise and sunset times to weathergov weather provider [#1705](https://github.com/MichMich/MagicMirror/issues/1705)
|
||||
- Added "useLocationAsHeader" to display "location" in `config.js` as header when location name is not returned
|
||||
- Added to `newsfeed.js`: in order to design the news article better with css, three more class-names were introduced: newsfeed-desc, newsfeed-desc, newsfeed-desc
|
||||
|
||||
### Updated
|
||||
|
||||
- English translation for "Feels" to "Feels like"
|
||||
- Fixed the example calendar url in `config.js.sample`
|
||||
- Update `ical.js` to solve various calendar issues.
|
||||
- Update weather city list url [#1676](https://github.com/MichMich/MagicMirror/issues/1676)
|
||||
- Only update clock once per minute when seconds aren't shown
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed uncaught exception, race condition on module update
|
||||
- Fixed issue [#1696](https://github.com/MichMich/MagicMirror/issues/1696), some ical files start date to not parse to date type
|
||||
- Allowance HTML5 autoplay-policy (policy is changed from Chrome 66 updates)
|
||||
- Handle SIGTERM messages
|
||||
- Fixes sliceMultiDayEvents so it respects maximumNumberOfDays
|
||||
- Minor types in default NewsFeed [README.md](https://github.com/MichMich/MagicMirror/blob/develop/modules/default/newsfeed/README.md)
|
||||
- Fix typos and small syntax errors, cleanup dependencies, remove multiple-empty-lines, add semi-rule
|
||||
- Fixed issues with calendar not displaying one-time changes to repeating events
|
||||
- Updated the fetchedLocationName variable in currentweather.js so that city shows up in the header
|
||||
|
||||
### Updated installer
|
||||
|
||||
- give non-pi2+ users (pi0, odroid, jetson nano, mac, windows, ...) option to continue install
|
||||
- use current username vs hardcoded 'pi' to support non-pi install
|
||||
- check for npm installed. node install doesn't do npm anymore
|
||||
- check for mac as part of PM2 install, add install option string
|
||||
- update pm2 config with current username instead of hard coded 'pi'
|
||||
- check for screen saver config, "/etc/xdg/lxsession", bypass if not setup
|
||||
|
||||
## [2.7.1] - 2019-04-02
|
||||
|
||||
Fixed `package.json` version number.
|
||||
|
||||
## [2.7.0] - 2019-04-01
|
||||
|
||||
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`. If you are having issues running Electron, make sure your [Raspbian is up to date](https://www.raspberrypi.org/documentation/raspbian/updating.md).
|
||||
|
||||
### Added
|
||||
|
||||
- Italian translation for "Feels"
|
||||
- Basic Klingon (tlhIngan Hol) translations
|
||||
- Disabled the screensaver on raspbian with installation script
|
||||
- Added option to truncate the number of vertical lines a calendar item can span if `wrapEvents` is enabled.
|
||||
- Danish translation for "Feels" and "Weeks"
|
||||
- Added option to split multiple day events in calendar to separate numbered events
|
||||
- Slovakian translation
|
||||
- Alerts now can contain Font Awesome icons
|
||||
- Notifications display time can be set in request
|
||||
- Newsfeed: added support for `ARTICLE_INFO_REQUEST` notification
|
||||
- Add `name` config option for calendars to be sent along with event broadcasts
|
||||
|
||||
### Updated
|
||||
|
||||
- Bumped the Electron dependency to v3.0.13 to support the most recent Raspbian. [#1500](https://github.com/MichMich/MagicMirror/issues/1500)
|
||||
- Updated modernizr code in alert module, fixed a small typo there too
|
||||
- More verbose error message on console if the config is malformed
|
||||
- Updated installer script to install Node.js version 10.x
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed temperature displays in currentweather and weatherforecast modules [#1503](https://github.com/MichMich/MagicMirror/issues/1503), [#1511](https://github.com/MichMich/MagicMirror/issues/1511).
|
||||
- Fixed unhandled error on bad git data in updatenotification module [#1285](https://github.com/MichMich/MagicMirror/issues/1285).
|
||||
- Weather forecast now works with openweathermap in new weather module. Daily data are displayed, see issue [#1504](https://github.com/MichMich/MagicMirror/issues/1504).
|
||||
- Fixed analogue clock border display issue where non-black backgrounds used (previous fix for issue 611)
|
||||
- Fixed compatibility issues caused when modules request different versions of Font Awesome, see issue [#1522](https://github.com/MichMich/MagicMirror/issues/1522). MagicMirror now uses [Font Awesome 5 with v4 shims included for backwards compatibility](https://fontawesome.com/how-to-use/on-the-web/setup/upgrading-from-version-4#shims).
|
||||
- Installation script problems with raspbian
|
||||
- Calendar: only show repeating count if the event is actually repeating [#1534](https://github.com/MichMich/MagicMirror/pull/1534)
|
||||
- Calendar: Fix exdate handling when multiple values are specified (comma separated)
|
||||
- Calendar: Fix relative date handling for fulldate events, calculate difference always from start of day [#1572](https://github.com/MichMich/MagicMirror/issues/1572)
|
||||
- Fix null dereference in moduleNeedsUpdate when the module isn't visible
|
||||
- Calendar: Fixed event end times by setting default calendarEndTime to "LT" (Local time format). [#1479]
|
||||
- Calendar: Fixed missing calendar fetchers after server process restarts [#1589](https://github.com/MichMich/MagicMirror/issues/1589)
|
||||
- Notification: fixed background color (was white text on white background)
|
||||
- Use getHeader instead of data.header when creating the DOM so overwriting the function also propagates into it
|
||||
- Fix documentation of `useKMPHwind` option in currentweather
|
||||
|
||||
### New weather module
|
||||
|
||||
- Fixed weather forecast table display [#1499](https://github.com/MichMich/MagicMirror/issues/1499).
|
||||
- Dimmed loading indicator for weather forecast.
|
||||
- Implemented config option `decimalSymbol` [#1499](https://github.com/MichMich/MagicMirror/issues/1499).
|
||||
- Aligned indoor values in current weather vertical [#1499](https://github.com/MichMich/MagicMirror/issues/1499).
|
||||
- Added humidity support to nunjuck unit filter.
|
||||
- Do not display degree symbol for temperature in Kelvin [#1503](https://github.com/MichMich/MagicMirror/issues/1503).
|
||||
- Weather forecast now works with openweathermap for both, `/forecast` and `/forecast/daily`, in new weather module. If you use the `/forecast`-weatherEndpoint, the hourly data are converted to daily data, see issues [#1504](https://github.com/MichMich/MagicMirror/issues/1504), [#1513](https://github.com/MichMich/MagicMirror/issues/1513).
|
||||
- Added fade, fadePoint and maxNumberOfDays properties to the forecast mode [#1516](https://github.com/MichMich/MagicMirror/issues/1516)
|
||||
- Fixed Loading string and decimalSymbol string replace [#1538](https://github.com/MichMich/MagicMirror/issues/1538)
|
||||
- Show Snow amounts in new weather module [#1545](https://github.com/MichMich/MagicMirror/issues/1545)
|
||||
- Added weather.gov as a new weather provider for US locations
|
||||
|
||||
## [2.6.0] - 2019-01-01
|
||||
|
||||
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`. If you are having issues updating, make sure you are running the latest version of Node.
|
||||
|
||||
### ✨ Experimental ✨
|
||||
|
||||
- New default [module weather](modules/default/weather). This module will eventually replace the current `currentweather` and `weatherforecast` modules. The new module is still pretty experimental, but it's included so you can give it a try and help us improve this module. Please give us you feedback using [this forum post](https://forum.magicmirror.builders/topic/9335/default-weather-module-refactoring).
|
||||
|
||||
A huge, huge, huge thanks to user @fewieden for all his hard work on the new `weather` module!
|
||||
|
||||
### Added
|
||||
|
||||
- Possibility to add classes to the cell of symbol, title and time of the events of calendar.
|
||||
- Font-awesome 5, still has 4 for backwards compatibility.
|
||||
- Missing `showEnd` in calendar documentation
|
||||
- Screenshot for the new feed module
|
||||
- Screenshot for the compliments module
|
||||
- Screenshot for the clock module
|
||||
- Screenshot for the current weather
|
||||
- Screenshot for the weather forecast module
|
||||
- Portuguese translation for "Feels"
|
||||
- Croatian translation
|
||||
- Fading for dateheaders timeFormat in Calendar [#1464](https://github.com/MichMich/MagicMirror/issues/1464)
|
||||
- Documentation for the existing `scale` option in the Weather Forecast module.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Allow parsing recurring calendar events where the start date is before 1900
|
||||
- Fixed Polish translation for Single Update Info
|
||||
- Ignore entries with unparseable details in the calendar module
|
||||
- Bug showing FullDayEvents one day too long in calendar fixed
|
||||
- Bug in newsfeed when `removeStartTags` is used on the description [#1478](https://github.com/MichMich/MagicMirror/issues/1478)
|
||||
|
||||
### Updated
|
||||
|
||||
- The default calendar setting `showEnd` is changed to `false`.
|
||||
|
||||
### Changed
|
||||
|
||||
- The Weather Forecast module by default displays the ° symbol after every numeric value to be consistent with the Current Weather module.
|
||||
|
||||
## [2.5.0] - 2018-10-01
|
||||
|
||||
### Added
|
||||
|
||||
- Romanian translation for "Feels"
|
||||
- Support multi-line compliments
|
||||
- Simplified Chinese translation for "Feels"
|
||||
- Polish translate for "Feels"
|
||||
- French translate for "Feels"
|
||||
- Translations for newsfeed module
|
||||
- Support for toggling news article in fullscreen
|
||||
- Hungarian translation for "Feels" and "Week"
|
||||
- Spanish translation for "Feels"
|
||||
- Add classes instead of inline style to the message from the module Alert
|
||||
- Support for events having a duration instead of an end
|
||||
- Support for showing end of events through config parameters showEnd and dateEndFormat
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed gzip encoded calendar loading issue #1400.
|
||||
- Mixup between german and spanish translation for newsfeed.
|
||||
- Fixed close dates to be absolute, if no configured in the config.js - module Calendar
|
||||
- Fixed the updatenotification module message about new commits in the repository, so they can be correctly localized in singular and plural form.
|
||||
- Fix for weatherforecast rainfall rounding [#1374](https://github.com/MichMich/MagicMirror/issues/1374)
|
||||
- Fix calendar parsing issue for Midori on RasperryPi Zero w, related to issue #694.
|
||||
- Fix weather city ID link in sample config
|
||||
- Fixed issue with clientonly not updating with IP address and port provided on command line.
|
||||
|
||||
### Updated
|
||||
|
||||
- Updated Simplified Chinese translation
|
||||
- Swedish translations
|
||||
- Hungarian translations for the updatenotification module
|
||||
- Updated Norsk bokmål translation
|
||||
- Updated Norsk nynorsk translation
|
||||
- Consider multi days event as full day events
|
||||
|
||||
## [2.4.1] - 2018-07-04
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix weather parsing issue #1332.
|
||||
|
||||
## [2.4.0] - 2018-07-01
|
||||
|
||||
⚠️ **Warning:** This release includes an updated version of Electron. This requires a Raspberry Pi configuration change to allow the best performance and prevent the CPU from overheating. Please read the information on the [MagicMirror Wiki](https://github.com/michmich/magicmirror/wiki/configuring-the-raspberry-pi#enable-the-open-gl-driver-to-decrease-electrons-cpu-usage).
|
||||
|
||||
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`
|
||||
|
||||
### Added
|
||||
|
||||
- Enabled translation of feelsLike for module currentweather
|
||||
- Added support for on-going calendar events
|
||||
- Added scroll up in fullscreen newsfeed article view
|
||||
- Changed fullscreen newsfeed width from 100% to 100vw (better results)
|
||||
- Added option to calendar module that colors only the symbol instead of the whole line
|
||||
- Added option for new display format in the calendar module with date headers with times/events below.
|
||||
- Ability to fetch compliments from a remote server
|
||||
- Add regex filtering to calendar module
|
||||
- Customize classes for table
|
||||
- Added option to newsfeed module to only log error parsing a news article if enabled
|
||||
- Add update translations for Português Brasileiro
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade to Electron 2.0.0.
|
||||
- Remove yarn-or-npm which breaks production builds.
|
||||
- Invoke module suspend even if no dom content. [#1308](https://github.com/MichMich/MagicMirror/issues/1308)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed issue where wind chill could not be displayed in Fahrenheit. [#1247](https://github.com/MichMich/MagicMirror/issues/1247)
|
||||
- Fixed issues where a module crashes when it tries to dismiss a non existing alert. [#1240](https://github.com/MichMich/MagicMirror/issues/1240)
|
||||
- In default module currentWeather/currentWeather.js line 296, 300, self.config.animationSpeed can not be found because the notificationReceived function does not have "self" variable.
|
||||
- Fixed browser-side code to work on the Midori browser.
|
||||
- Fixed issue where heat index was reporting incorrect values in Celsius and Fahrenheit. [#1263](https://github.com/MichMich/MagicMirror/issues/1263)
|
||||
- Fixed weatherforecast to use dt_txt field instead of dt to handle timezones better
|
||||
- Newsfeed now remembers to show the description when `"ARTICLE_LESS_DETAILS"` is called if the user wants to always show the description. [#1282](https://github.com/MichMich/MagicMirror/issues/1282)
|
||||
- `clientonly/*.js` is now linted, and one linting error is fixed
|
||||
- Fix issue #1196 by changing underscore to hyphen in locale id, in align with momentjs.
|
||||
- Fixed issue where heat index and wind chill were reporting incorrect values in Kelvin. [#1263](https://github.com/MichMich/MagicMirror/issues/1263)
|
||||
|
||||
### Updated
|
||||
|
||||
- Updated Italian translation
|
||||
- Updated German translation
|
||||
- Updated Dutch translation
|
||||
|
||||
## [2.3.1] - 2018-04-01
|
||||
|
||||
### Fixed
|
||||
|
||||
- Downgrade electron to 1.4.15 to solve the black screen issue.[#1243](https://github.com/MichMich/MagicMirror/issues/1243)
|
||||
|
||||
## [2.3.0] - 2018-04-01
|
||||
|
||||
### Added
|
||||
|
||||
- Add new settings in compliments module: setting time intervals for morning and afternoon
|
||||
- Add system notification `MODULE_DOM_CREATED` for notifying each module when their Dom has been fully loaded.
|
||||
- Add types for module.
|
||||
- Implement Danger.js to notify contributors when CHANGELOG.md is missing in PR.
|
||||
- Allow scrolling in full page article view of default newsfeed module with gesture events from [MMM-Gestures](https://github.com/thobach/MMM-Gestures)
|
||||
- Changed 'compliments.js' - update DOM if remote compliments are loaded instead of waiting one updateInterval to show custom compliments
|
||||
- Automated unit tests utils, deprecated, translator, cloneObject(lockstrings)
|
||||
- Automated integration tests translations
|
||||
- Add advanced filtering to the excludedEvents configuration of the default calendar module
|
||||
- New currentweather module config option: `showFeelsLike`: Shows how it actually feels like. (wind chill or heat index)
|
||||
- New currentweather module config option: `useKMPHwind`: adds an option to see wind speed in Kmph instead of just m/s or Beaufort.
|
||||
- Add dc:date to parsing in newsfeed module, which allows parsing of more rss feeds.
|
||||
|
||||
### Changed
|
||||
|
||||
- Add link to GitHub repository which contains the respective Dockerfile.
|
||||
- Optimized automated unit tests cloneObject, cmpVersions
|
||||
- Update notifications use now translation templates instead of normal strings.
|
||||
- Yarn can be used now as an installation tool
|
||||
- Changed Electron dependency to v1.7.13.
|
||||
|
||||
### Fixed
|
||||
|
||||
- News article in fullscreen (iframe) is now shown in front of modules.
|
||||
- Forecast respects maxNumberOfDays regardless of endpoint.
|
||||
- Fix exception on translation of objects.
|
||||
|
||||
## [2.2.2] - 2018-01-02
|
||||
|
||||
### Added
|
||||
|
||||
- Add missing `package-lock.json`.
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed Electron dependency to v1.7.10.
|
||||
|
||||
## [2.2.1] - 2018-01-01
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed linting errors.
|
||||
|
||||
## [2.2.0] - 2018-01-01
|
||||
|
||||
**Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`
|
||||
|
||||
### Changed
|
||||
|
||||
- Calendar week is now handled with a variable translation in order to move number language specific.
|
||||
- Reverted the Electron dependency back to 1.4.15 since newer version don't seem to work on the Raspberry Pi very well.
|
||||
|
||||
### Added
|
||||
|
||||
- Add option to use [Nunjucks](https://mozilla.github.io/nunjucks/) templates in modules. (See `helloworld` module as an example.)
|
||||
- Add Bulgarian translations for MagicMirror² and Alert module.
|
||||
- Add graceful shutdown of modules by calling `stop` function of each `node_helper` on SIGINT before exiting.
|
||||
- Link update subtext to Github diff of current version versus tracking branch.
|
||||
- Add Catalan translation.
|
||||
- Add ability to filter out newsfeed items based on prohibited words found in title (resolves #1071)
|
||||
- Add options to truncate description support of a feed in newsfeed module
|
||||
- Add reloadInterval option for particular feed in newsfeed module
|
||||
- Add no-cache entries of HTTP headers in newsfeed module (fetcher)
|
||||
- Add Czech translation.
|
||||
- Add option for decimal symbols other than the decimal point for temperature values in both default weather modules: WeatherForecast and CurrentWeather.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed issue with calendar module showing more than `maximumEntries` allows
|
||||
- WeatherForecast and CurrentWeather are now using HTTPS instead of HTTP
|
||||
- Correcting translation for Indonesian language
|
||||
- Fix issue where calendar icons wouldn't align correctly
|
||||
|
||||
## [2.1.3] - 2017-10-01
|
||||
|
||||
**Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`
|
||||
|
||||
### Changed
|
||||
|
||||
- Remove Roboto fonts files inside `fonts` and these are installed by npm install command.
|
||||
|
||||
### Added
|
||||
|
||||
- Add `clientonly` script to start only the electron client for a remote server.
|
||||
- Add symbol and color properties of event when `CALENDAR_EVENTS` notification is broadcasted from `default/calendar` module.
|
||||
- Add `.vscode/` folder to `.gitignore` to keep custom Visual Studio Code config out of git.
|
||||
- Add unit test the capitalizeFirstLetter function of newsfeed module.
|
||||
- Add new unit tests for function `shorten` in calendar module.
|
||||
- Add new unit tests for function `getLocaleSpecification` in calendar module.
|
||||
- Add unit test for js/class.js.
|
||||
- Add unit tests for function `roundValue` in currentweather module.
|
||||
- Add test e2e showWeek feature in spanish language.
|
||||
- Add warning Log when is used old authentication method in the calendar module.
|
||||
- Add test e2e for helloworld module with default config text.
|
||||
- Add ability for `currentweather` module to display indoor humidity via INDOOR_HUMIDITY notification.
|
||||
- Add Welsh (Cymraeg) translation.
|
||||
- Add Slack badge to Readme.
|
||||
|
||||
### Updated
|
||||
|
||||
- Changed 'default.js' - listen on all attached interfaces by default.
|
||||
- Add execution of `npm list` after the test are ran in Travis CI.
|
||||
- Change hooks for the vendors e2e tests.
|
||||
- Add log when clientonly failed on starting.
|
||||
- Add warning color when are using full ip whitelist.
|
||||
- Set version of the `express-ipfilter` on 0.3.1.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed issue with incorrect alignment of analog clock when displayed in the center column of the MM.
|
||||
- Fixed ipWhitelist behaviour to make empty whitelist ([]) allow any and all hosts access to the MM.
|
||||
- Fixed issue with calendar module where 'excludedEvents' count towards 'maximumEntries'.
|
||||
- Fixed issue with calendar module where global configuration of maximumEntries was not overridden by calendar specific config (see module doc).
|
||||
- Fixed issue where `this.file(filename)` returns a path with two hashes.
|
||||
- Workaround for the WeatherForecast API limitation.
|
||||
|
||||
## [2.1.2] - 2017-07-01
|
||||
|
||||
### Changed
|
||||
|
||||
- Revert Docker related changes in favor of [docker-MagicMirror](https://github.com/bastilimbach/docker-MagicMirror). All Docker images are outsourced. ([#856](https://github.com/MichMich/MagicMirror/pull/856))
|
||||
- Change Docker base image (Debian + Node) to an arm based distro (AlpineARM + Node) ([#846](https://github.com/MichMich/MagicMirror/pull/846))
|
||||
- Fix the dockerfile to have it running from the first time.
|
||||
|
||||
### Added
|
||||
|
||||
- Add in option to wrap long calendar events to multiple lines using `wrapEvents` configuration option.
|
||||
- Add test e2e `show title newsfeed` for newsfeed module.
|
||||
- Add task to check configuration file.
|
||||
- Add test check URLs of vendors.
|
||||
- Add test of match current week number on clock module with showWeek configuration.
|
||||
- Add test default modules present modules/default/defaultmodules.js.
|
||||
- Add unit test calendar_modules function capFirst.
|
||||
- Add test for check if exists the directories present in defaults modules.
|
||||
- Add support for showing wind direction as an arrow instead of abbreviation in currentWeather module.
|
||||
- Add support for writing translation functions to support flexible word order
|
||||
- Add test for check if exits the directories present in defaults modules.
|
||||
- Add calendar option to set a separate date format for full day events.
|
||||
- Add ability for `currentweather` module to display indoor temperature via INDOOR_TEMPERATURE notification
|
||||
- Add ability to change the path of the `custom.css`.
|
||||
- Add translation Dutch to Alert module.
|
||||
- Added Romanian translation.
|
||||
|
||||
### Updated
|
||||
|
||||
- Added missing keys to Polish translation.
|
||||
- Added missing key to German translation.
|
||||
- Added better translation with flexible word order to Finnish translation.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix instruction in README for using automatically installer script.
|
||||
- Bug of duplicated compliments as described in [here](https://forum.magicmirror.builders/topic/2381/compliments-module-stops-cycling-compliments).
|
||||
- Fix double message about port when server is starting
|
||||
- Corrected Swedish translations for TODAY/TOMORROW/DAYAFTERTOMORROW.
|
||||
- Removed unused import from js/electron.js
|
||||
- Made calendar.js respect config.timeFormat irrespective of locale setting.
|
||||
- Fixed alignment of analog clock when a large calendar is displayed in the same side bar.
|
||||
|
||||
## [2.1.1] - 2017-04-01
|
||||
|
||||
**Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`
|
||||
|
||||
### Changed
|
||||
|
||||
- Add `anytime` group for Compliments module.
|
||||
- Compliments module can use remoteFile without default daytime arrays defined.
|
||||
- Installer: Use init config.js from config.js.sample.
|
||||
- Switched out `rrule` package for `rrule-alt` and fixes in `ical.js` in order to fix calendar issues. ([#565](https://github.com/MichMich/MagicMirror/issues/565))
|
||||
- Make mouse events pass through the region fullscreen_above to modules below.
|
||||
- Scaled the splash screen down to make it a bit more subtle.
|
||||
- Replace HTML tables with markdown tables in README files.
|
||||
- Added `DAYAFTERTOMORROW`, `UPDATE_NOTIFICATION` and `UPDATE_NOTIFICATION_MODULE` to Finnish translations.
|
||||
- Run `npm test` on Travis automatically.
|
||||
- Show the splash screen image even when is reboot or halted.
|
||||
- Added some missing translation strings in the sv.json file.
|
||||
- Run task jsonlint to check translation files.
|
||||
- Restructured Test Suite.
|
||||
|
||||
### Added
|
||||
|
||||
- Added Docker support (Pull Request [#673](https://github.com/MichMich/MagicMirror/pull/673)).
|
||||
- Calendar-specific support for `maximumEntries`, and `maximumNumberOfDays`.
|
||||
- Add loaded function to modules, providing an async callback.
|
||||
- Made default newsfeed module aware of gesture events from [MMM-Gestures](https://github.com/thobach/MMM-Gestures)
|
||||
- Add use pm2 for manager process into Installer RaspberryPi script.
|
||||
- Russian Translation.
|
||||
- Afrikaans Translation.
|
||||
- Add postinstall script to notify user that MagicMirror installed successfully despite warnings from NPM.
|
||||
- Init tests using mocha.
|
||||
- Option to use RegExp in Calendar's titleReplace.
|
||||
- Hungarian Translation.
|
||||
- Icelandic Translation.
|
||||
- Add use a script to prevent when is run by SSH session set DISPLAY environment.
|
||||
- Enable ability to set configuration file by the environment variable called MM_CONFIG_FILE.
|
||||
- Option to give each calendar a different color.
|
||||
- Option for colored min-temp and max-temp.
|
||||
- Add test e2e helloworld.
|
||||
- Add test e2e environment.
|
||||
- Add `chai-as-promised` npm module to devDependencies.
|
||||
- Basic set of tests for clock module.
|
||||
- Run e2e test in Travis.
|
||||
- Estonian Translation.
|
||||
- Add test for compliments module for parts of day.
|
||||
- Korean Translation.
|
||||
- Added console warning on startup when deprecated config options are used.
|
||||
- Add option to display temperature unit label to the current weather module.
|
||||
- Added ability to disable wrapping of news items.
|
||||
- Added in the ability to hide events in the calendar module based on simple string filters.
|
||||
- Updated Norwegian translation.
|
||||
- Added hideLoading option for News Feed module.
|
||||
- Added configurable dateFormat to clock module.
|
||||
- Added multiple calendar icon support.
|
||||
- Added tests for Translations, dev argument, version, dev console.
|
||||
- Added test anytime feature compliments module.
|
||||
- Added test ipwhitelist configuration directive.
|
||||
- Added test for calendar module: default, basic-auth, backward compatibility, fail-basic-auth.
|
||||
- Added meta tags to support fullscreen mode on iOS (for server mode)
|
||||
- Added `ignoreOldItems` and `ignoreOlderThan` options to the News Feed module
|
||||
- Added test for MM_PORT environment variable.
|
||||
- Added a configurable Week section to the clock module.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Update .gitignore to not ignore default modules folder.
|
||||
- Remove white flash on boot up.
|
||||
- Added `update` in Raspberry Pi installation script.
|
||||
- Fix an issue where the analog clock looked scrambled. ([#611](https://github.com/MichMich/MagicMirror/issues/611))
|
||||
- If units are set to imperial, the showRainAmount option of weatherforecast will show the correct unit.
|
||||
- Module currentWeather: check if temperature received from api is defined.
|
||||
- Fix an issue with module hidden status changing to `true` although lock string prevented showing it.
|
||||
- Fix newsfeed module bug (removeStartTags)
|
||||
- Fix when is set MM_PORT environment variable.
|
||||
- Fixed missing animation on `this.show(speed)` when module is alone in a region.
|
||||
|
||||
## [2.1.0] - 2016-12-31
|
||||
|
||||
**Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`
|
||||
|
||||
### Added
|
||||
|
||||
- Finnish translation.
|
||||
- Danish translation.
|
||||
- Turkish translation.
|
||||
- Option to limit access to certain IP addresses based on the value of `ipWhitelist` in the `config.js`, default is access from localhost only (Issue [#456](https://github.com/MichMich/MagicMirror/issues/456)).
|
||||
- Added ability to change the point of time when calendar events get relative.
|
||||
- Add Splash screen on boot.
|
||||
- Add option to show humidity in currentWeather module.
|
||||
- Add VSCode IntelliSense support.
|
||||
- Module API: Add Visibility locking to module system. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules#visibility-locking) for more information.
|
||||
- Module API: Method to overwrite the module's header. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules#getheader) for more information.
|
||||
- Module API: Option to define the minimum MagicMirror version to run a module. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules#requiresversion) for more information.
|
||||
- Calendar module now broadcasts the event list to all other modules using the notification system. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules/default/calendar) for more information.
|
||||
- Possibility to use the calendar feed as the source for the weather (currentweather & weatherforecast) location data. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules/default/weatherforecast) for more information.
|
||||
- Added option to show rain amount in the weatherforecast default module
|
||||
- Add module `updatenotification` to get an update whenever a new version is available. [See documentation](https://github.com/MichMich/MagicMirror/tree/develop/modules/default/updatenotification) for more information.
|
||||
- Add the ability to set timezone on the date display in the Clock Module
|
||||
- Ability to set date format in calendar module
|
||||
- Possibility to use currentweather for the compliments
|
||||
- Added option `disabled` for modules.
|
||||
- Added option `address` to set bind address.
|
||||
- Added option `onlyTemp` for currentweather module to show only current temperature and weather icon.
|
||||
- Added option `remoteFile` to compliments module to load compliment array from filesystem.
|
||||
- Added option `zoom` to scale the whole mirror display with a given factor.
|
||||
- Added option `roundTemp` for currentweather and weatherforecast modules to display temperatures rounded to nearest integer.
|
||||
- Added ability set the classes option to compliments module for style and text size of compliments.
|
||||
- Added ability to configure electronOptions
|
||||
- Calendar module: option to hide private events
|
||||
- Add root_path for global vars
|
||||
|
||||
### Updated
|
||||
|
||||
- Modified translations for Frysk.
|
||||
- Modified core English translations.
|
||||
- Updated package.json as a result of Snyk security update.
|
||||
- Improve object instantiation to prevent reference errors.
|
||||
- Improve logger. `Log.log()` now accepts multiple arguments.
|
||||
- Remove extensive logging in newsfeed node helper.
|
||||
- Calendar times are now uniformly capitalized.
|
||||
- Modules are now secure, and Helmet is now used to prevent abuse of the Mirror's API.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Solve an issue where module margins would appear when the first module of a section was hidden.
|
||||
- Solved visual display errors on chrome, if all modules in one of the right sections are hidden.
|
||||
- Global and Module default config values are no longer modified when setting config values.
|
||||
- Hide a region if all modules in a region are hidden. Prevention unwanted margins.
|
||||
- Replaced `electron-prebuilt` package with `electron` in order to fix issues that would happen after 2017.
|
||||
- Documentation of alert module
|
||||
|
||||
## [2.0.5] - 2016-09-20
|
||||
|
||||
### Added
|
||||
|
||||
- Added ability to remove tags from the beginning or end of newsfeed items in 'newsfeed.js'.
|
||||
- Added ability to define "the day after tomorrow" for calendar events (Definition for German and Dutch already included).
|
||||
- Added CII Badge (we are compliant with the CII Best Practices)
|
||||
- Add support for doing http basic auth when loading calendars
|
||||
- Add the ability to turn off and on the date display in the Clock Module
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix typo in installer.
|
||||
- Add message to unsupported Pi error to mention that Pi Zeros must use server only mode, as ARMv6 is unsupported. Closes #374.
|
||||
- Fix API url for weather API.
|
||||
|
||||
### Updated
|
||||
|
||||
- Force fullscreen when kioskmode is active.
|
||||
- Update the .github templates and information with more modern information.
|
||||
- Update the Gruntfile with a more functional StyleLint implementation.
|
||||
|
||||
## [2.0.4] - 2016-08-07
|
||||
|
||||
### Added
|
||||
|
||||
- Brazilian Portuguese Translation.
|
||||
- Option to enable Kiosk mode.
|
||||
- Added ability to start the app with Dev Tools.
|
||||
- Added ability to turn off the date display in `clock.js` when in analog mode.
|
||||
- Greek Translation
|
||||
|
||||
### Fixed
|
||||
|
||||
- Prevent `getModules()` selectors from returning duplicate entries.
|
||||
- Append endpoints of weather modules with `/` to retrieve the correct data. (Issue [#337](https://github.com/MichMich/MagicMirror/issues/337))
|
||||
- Corrected grammar in `module.js` from 'suspend' to 'suspended'.
|
||||
- Fixed openweathermap.org URL in config sample.
|
||||
- Prevent currentweather module from crashing when received data object is incorrect.
|
||||
- Fix issue where translation loading prevented the UI start-up when the language was set to 'en'. (Issue [#388](https://github.com/MichMich/MagicMirror/issues/388))
|
||||
|
||||
### Updated
|
||||
|
||||
- Updated package.json to fix possible vulnerabilities. (Using Snyk)
|
||||
- Updated weathericons
|
||||
- Updated default weatherforecast to work with the new icons.
|
||||
- More detailed error message in case config file couldn't be loaded.
|
||||
|
||||
## [2.0.3] - 2016-07-12
|
||||
|
||||
### Added
|
||||
|
||||
- Add max newsitems parameter to the newsfeed module.
|
||||
- Translations for Simplified Chinese, Traditional Chinese and Japanese.
|
||||
- Polish Translation
|
||||
- Add an analog clock in addition to the digital one.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Edit Alert Module to display title & message if they are provided in the notification (Issue [#300](https://github.com/MichMich/MagicMirror/issues/300))
|
||||
- Removed 'null' reference from updateModuleContent(). This fixes recent Edge and Internet Explorer browser displays (Issue [#319](https://github.com/MichMich/MagicMirror/issues/319))
|
||||
|
||||
### Changed
|
||||
|
||||
- Added default string to calendar titleReplace.
|
||||
|
||||
## [2.0.2] - 2016-06-05
|
||||
|
||||
### Added
|
||||
|
||||
- Norwegian Translations (nb and nn)
|
||||
- Portuguese Translation
|
||||
- Swedish Translation
|
||||
|
||||
### Fixed
|
||||
|
||||
- Added reference to Italian Translation.
|
||||
- Added the missing NE translation to all languages. [#344](https://github.com/MichMich/MagicMirror/issues/344)
|
||||
- Added proper User-Agent string to calendar call.
|
||||
|
||||
### Changed
|
||||
|
||||
- Add option to use locationID in weather modules.
|
||||
|
||||
## [2.0.1] - 2016-05-18
|
||||
|
||||
### Added
|
||||
|
||||
- Changelog
|
||||
- Italian Translation
|
||||
|
||||
### Changed
|
||||
|
||||
- Improve the installer by fetching the latest Node.js without any 3rd party interferences.
|
||||
|
||||
## [2.0.0] - 2016-05-03
|
||||
|
||||
### Initial release of MagicMirror²
|
||||
|
||||
It includes (but is not limited to) the following features:
|
||||
|
||||
- Modular system allowing 3rd party plugins.
|
||||
- An Node/Electron based application taking away the need for external servers or browsers.
|
||||
- A complete development API documentation.
|
||||
- Small cute fairies that kiss you while you sleep.
|
||||
|
||||
## [1.0.0] - 2014-02-16
|
||||
|
||||
### Initial release of MagicMirror.
|
||||
|
||||
This was part of the blogpost: [https://michaelteeuw.nl/post/83916869600/magic-mirror-part-vi-production-of-the](https://michaelteeuw.nl/post/83916869600/magic-mirror-part-vi-production-of-the)
|
||||
17
LICENSE.md
Normal file
17
LICENSE.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# The MIT License (MIT)
|
||||
|
||||
Copyright © 2016-2020 Michael Teeuw
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
**The software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.**
|
||||
68
README.md
68
README.md
@@ -1,52 +1,44 @@
|
||||
MagicMirror
|
||||
===========
|
||||

|
||||
|
||||
##Introduction
|
||||
<p align="center">
|
||||
<a href="https://david-dm.org/MichMich/MagicMirror"><img src="https://david-dm.org/MichMich/MagicMirror.svg" alt="Dependency Status"></a>
|
||||
<a href="https://david-dm.org/MichMich/MagicMirror#info=devDependencies"><img src="https://david-dm.org/MichMich/MagicMirror/dev-status.svg" alt="devDependency Status"></a>
|
||||
<a href="https://bestpractices.coreinfrastructure.org/projects/347"><img src="https://bestpractices.coreinfrastructure.org/projects/347/badge"></a>
|
||||
<a href="https://choosealicense.com/licenses/mit"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
|
||||
<a href="https://github.com/MichMich/MagicMirror/actions?query=workflow%3A%22Automated+Tests%22"><img src="https://github.com/MichMich/MagicMirror/workflows/Automated%20Tests/badge.svg" alt="Tests"></a>
|
||||
</p>
|
||||
|
||||
The super magic interface of my personal Magic Mirror. More information about this project can be found on my [blog](http://michaelteeuw.nl/tagged/magicmirror).
|
||||
**MagicMirror²** is an open source modular smart mirror platform. With a growing list of installable modules, the **MagicMirror²** allows you to convert your hallway or bathroom mirror into your personal assistant. **MagicMirror²** is built by the creator of [the original MagicMirror](https://michaelteeuw.nl/tagged/magicmirror) with the incredible help of a [growing community of contributors](https://github.com/MichMich/MagicMirror/graphs/contributors).
|
||||
|
||||
Runs as a php script on a web server with basically no external dependencies. *Can use socket.io for XBEE integration, but isn't required for basic functionality*.
|
||||
MagicMirror² focuses on a modular plugin system and uses [Electron](https://www.electronjs.org/) as an application wrapper. So no more web server or browser installs necessary!
|
||||
|
||||
## Documentation
|
||||
|
||||
##Configuration
|
||||
For the full documentation including **[installation instructions](https://docs.magicmirror.builders/getting-started/installation.html)**, please visit our dedicated documentation website: [https://docs.magicmirror.builders](https://docs.magicmirror.builders).
|
||||
|
||||
Modify `js/config.js` to change some general variables (language, weather location, compliments, news feed RSS and to add your own ICS calendar)
|
||||
## Links
|
||||
|
||||
To use the OpenWeatherMap API, you'll need a free API key. Checkout [this blogpost](http://michaelteeuw.nl/post/131504229357/what-happened-to-the-weather) for more information.
|
||||
- Website: [https://magicmirror.builders](https://magicmirror.builders)
|
||||
- Documentation: [https://docs.magicmirror.builders](https://docs.magicmirror.builders)
|
||||
- Forum: [https://forum.magicmirror.builders](https://forum.magicmirror.builders)
|
||||
- Discord: [https://discord.gg/J5BAtvx](https://discord.gg/J5BAtvx)
|
||||
- Blog: [https://michaelteeuw.nl/tagged/magicmirror](https://michaelteeuw.nl/tagged/magicmirror)
|
||||
- Donations: [https://magicmirror.builders/#donate](https://magicmirror.builders/#donate)
|
||||
|
||||
##Code
|
||||
## Contributing Guidelines
|
||||
|
||||
###[main.js](js/main.js)
|
||||
Contributions of all kinds are welcome, not only in the form of code but also with regards bug reports and documentation. For the full contribution guidelines, check out: [https://docs.magicmirror.builders/getting-started/contributing.html](https://docs.magicmirror.builders/getting-started/contributing.html)
|
||||
|
||||
This file initiates the separate pieces of functionality that will appear in the view. It also includes various utility functions that are used to update what is visible.
|
||||
## Enjoying MagicMirror? Consider a donation!
|
||||
|
||||
###[Calendar](js/calendar)
|
||||
MagicMirror² is opensource and free. That doesn't mean we don't need any money.
|
||||
|
||||
Parsing functionality for the calendar that retrieves and updates the calendar based on the interval set at the top of the [calendar.js](js/calendar/calendar.js) file. This was actually a straight pull from the original main.js file but the parsing code may deserve an upgrade.
|
||||
Please consider a donation to help us cover the ongoing costs like webservers and email services.
|
||||
If we receive enough donations we might even be able to free up some working hours and spend some extra time improving the MagicMirror² core.
|
||||
|
||||
###[Compliments](js/compliments)
|
||||
To donate, please follow [this](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=G5D8E9MR5DTD2&source=url) link.
|
||||
|
||||
Functionality related to inserting compliments into the view and rotating them based on a specific interval set at the top of the [compliments.js](js/compliments/compliments.js) file.
|
||||
|
||||
###[News](js/news)
|
||||
|
||||
Takes an array of news feeds (or a single string) from the config file and retrieves each one so that it can be displayed in a loop based on the interval set at the top of the [news.js](js/news/news.js) file.
|
||||
|
||||
###[Time](js/time)
|
||||
|
||||
Updates the time on the screen on one second interval.
|
||||
|
||||
###[Version](js/version)
|
||||
|
||||
Checks the git version and refreshes if a new version has been pulled.
|
||||
|
||||
###[Weather](js/weather)
|
||||
|
||||
Takes the user's inserted location, language, unit type, and OpenWeatherMap API key and grabs the five day weather forecast from OpenWeatherMap. You need to set the API key in the config for this to work. (See *configuration*.)
|
||||
|
||||
##Extensions
|
||||
|
||||
###[MagicMirror-Extensions by PaViRo](https://github.com/paviro/MagicMirror-Extensions)
|
||||
|
||||
**Current features:** FRITZ!Box Callmonitor <br>
|
||||
**Future features:** Faceregognition, personalized views, online banking through HBCI and multiple calenders based on faceregognition.
|
||||
<p align="center">
|
||||
<br>
|
||||
<a href="https://forum.magicmirror.builders/topic/728/magicmirror-is-voted-number-1-in-the-magpi-top-50"><img src="https://magicmirror.builders/img/magpi-best-watermark-custom.png" width="150" alt="MagPi Top 50"></a>
|
||||
</p>
|
||||
|
||||
128
clientonly/index.js
Normal file
128
clientonly/index.js
Normal file
@@ -0,0 +1,128 @@
|
||||
"use strict";
|
||||
|
||||
// Use separate scope to prevent global scope pollution
|
||||
(function () {
|
||||
var config = {};
|
||||
|
||||
/**
|
||||
* Helper function to get server address/hostname from either the commandline or env
|
||||
*/
|
||||
function getServerAddress() {
|
||||
/**
|
||||
* Get command line parameters
|
||||
* Assumes that a cmdline parameter is defined with `--key [value]`
|
||||
*
|
||||
* @param {string} key key to look for at the command line
|
||||
* @param {string} defaultValue value if no key is given at the command line
|
||||
*
|
||||
* @returns {string} the value of the parameter
|
||||
*/
|
||||
function getCommandLineParameter(key, defaultValue = undefined) {
|
||||
var index = process.argv.indexOf(`--${key}`);
|
||||
var value = index > -1 ? process.argv[index + 1] : undefined;
|
||||
return value !== undefined ? String(value) : defaultValue;
|
||||
}
|
||||
|
||||
// Prefer command line arguments over environment variables
|
||||
["address", "port"].forEach((key) => {
|
||||
config[key] = getCommandLineParameter(key, process.env[key.toUpperCase()]);
|
||||
});
|
||||
|
||||
// determine if "--use-tls"-flag was provided
|
||||
config["tls"] = process.argv.indexOf("--use-tls") > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the config from the specified server url
|
||||
*
|
||||
* @param {string} url location where the server is running.
|
||||
*
|
||||
* @returns {Promise} the config
|
||||
*/
|
||||
function getServerConfig(url) {
|
||||
// Return new pending promise
|
||||
return new Promise((resolve, reject) => {
|
||||
// Select http or https module, depending on requested url
|
||||
const lib = url.startsWith("https") ? require("https") : require("http");
|
||||
const request = lib.get(url, (response) => {
|
||||
var configData = "";
|
||||
|
||||
// Gather incoming data
|
||||
response.on("data", function (chunk) {
|
||||
configData += chunk;
|
||||
});
|
||||
// Resolve promise at the end of the HTTP/HTTPS stream
|
||||
response.on("end", function () {
|
||||
resolve(JSON.parse(configData));
|
||||
});
|
||||
});
|
||||
|
||||
request.on("error", function (error) {
|
||||
reject(new Error(`Unable to read config from server (${url} (${error.message}`));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a message to the console in case of errors
|
||||
*
|
||||
* @param {string} [message] error message to print
|
||||
* @param {number} code error code for the exit call
|
||||
*/
|
||||
function fail(message, code = 1) {
|
||||
if (message !== undefined && typeof message === "string") {
|
||||
console.log(message);
|
||||
} else {
|
||||
console.log("Usage: 'node clientonly --address 192.168.1.10 --port 8080 [--use-tls]'");
|
||||
}
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
getServerAddress();
|
||||
|
||||
(config.address && config.port) || fail();
|
||||
var prefix = config.tls ? "https://" : "http://";
|
||||
|
||||
// Only start the client if a non-local server was provided
|
||||
if (["localhost", "127.0.0.1", "::1", "::ffff:127.0.0.1", undefined].indexOf(config.address) === -1) {
|
||||
getServerConfig(`${prefix}${config.address}:${config.port}/config/`)
|
||||
.then(function (configReturn) {
|
||||
// Pass along the server config via an environment variable
|
||||
var env = Object.create(process.env);
|
||||
var options = { env: env };
|
||||
configReturn.address = config.address;
|
||||
configReturn.port = config.port;
|
||||
configReturn.tls = config.tls;
|
||||
env.config = JSON.stringify(configReturn);
|
||||
|
||||
// Spawn electron application
|
||||
const electron = require("electron");
|
||||
const child = require("child_process").spawn(electron, ["js/electron.js"], options);
|
||||
|
||||
// Pipe all child process output to current stdout
|
||||
child.stdout.on("data", function (buf) {
|
||||
process.stdout.write(`Client: ${buf}`);
|
||||
});
|
||||
|
||||
// Pipe all child process errors to current stderr
|
||||
child.stderr.on("data", function (buf) {
|
||||
process.stderr.write(`Client: ${buf}`);
|
||||
});
|
||||
|
||||
child.on("error", function (err) {
|
||||
process.stdout.write(`Client: ${err}`);
|
||||
});
|
||||
|
||||
child.on("close", (code) => {
|
||||
if (code !== 0) {
|
||||
console.log(`There something wrong. The clientonly is not running code ${code}`);
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(function (reason) {
|
||||
fail(`Unable to connect to server: (${reason})`);
|
||||
});
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
})();
|
||||
107
config/config.js.sample
Normal file
107
config/config.js.sample
Normal file
@@ -0,0 +1,107 @@
|
||||
/* Magic Mirror Config Sample
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*
|
||||
* For more information on how you can configure this file
|
||||
* See https://github.com/MichMich/MagicMirror#configuration
|
||||
*
|
||||
*/
|
||||
|
||||
var config = {
|
||||
address: "localhost", // Address to listen on, can be:
|
||||
// - "localhost", "127.0.0.1", "::1" to listen on loopback interface
|
||||
// - another specific IPv4/6 to listen on a specific interface
|
||||
// - "0.0.0.0", "::" to listen on any interface
|
||||
// Default, when address config is left out or empty, is "localhost"
|
||||
port: 8080,
|
||||
basePath: "/", // The URL path where MagicMirror is hosted. If you are using a Reverse proxy
|
||||
// you must set the sub path here. basePath must end with a /
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], // Set [] to allow all IP addresses
|
||||
// or add a specific IPv4 of 192.168.1.5 :
|
||||
// ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.1.5"],
|
||||
// or IPv4 range of 192.168.3.0 --> 192.168.3.15 use CIDR format :
|
||||
// ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.3.0/28"],
|
||||
|
||||
useHttps: false, // Support HTTPS or not, default "false" will use HTTP
|
||||
httpsPrivateKey: "", // HTTPS private key path, only require when useHttps is true
|
||||
httpsCertificate: "", // HTTPS Certificate path, only require when useHttps is true
|
||||
|
||||
language: "en",
|
||||
logLevel: ["INFO", "LOG", "WARN", "ERROR"], // Add "DEBUG" for even more logging
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
// serverOnly: true/false/"local" ,
|
||||
// local for armv6l processors, default
|
||||
// starts serveronly and then starts chrome browser
|
||||
// false, default for all NON-armv6l devices
|
||||
// true, force serveronly mode, because you want to.. no UI on this device
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "alert",
|
||||
},
|
||||
{
|
||||
module: "updatenotification",
|
||||
position: "top_bar"
|
||||
},
|
||||
{
|
||||
module: "clock",
|
||||
position: "top_left"
|
||||
},
|
||||
{
|
||||
module: "calendar",
|
||||
header: "US Holidays",
|
||||
position: "top_left",
|
||||
config: {
|
||||
calendars: [
|
||||
{
|
||||
symbol: "calendar-check",
|
||||
url: "webcal://www.calendarlabs.com/ical-calendar/ics/76/US_Holidays.ics" }
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
module: "compliments",
|
||||
position: "lower_third"
|
||||
},
|
||||
{
|
||||
module: "currentweather",
|
||||
position: "top_right",
|
||||
config: {
|
||||
location: "New York",
|
||||
locationID: "5128581", //ID from http://bulk.openweathermap.org/sample/city.list.json.gz; unzip the gz file and find your city
|
||||
appid: "YOUR_OPENWEATHER_API_KEY"
|
||||
}
|
||||
},
|
||||
{
|
||||
module: "weatherforecast",
|
||||
position: "top_right",
|
||||
header: "Weather Forecast",
|
||||
config: {
|
||||
location: "New York",
|
||||
locationID: "5128581", //ID from http://bulk.openweathermap.org/sample/city.list.json.gz; unzip the gz file and find your city
|
||||
appid: "YOUR_OPENWEATHER_API_KEY"
|
||||
}
|
||||
},
|
||||
{
|
||||
module: "newsfeed",
|
||||
position: "bottom_bar",
|
||||
config: {
|
||||
feeds: [
|
||||
{
|
||||
title: "New York Times",
|
||||
url: "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml"
|
||||
}
|
||||
],
|
||||
showSourceTitle: true,
|
||||
showPublishDate: true,
|
||||
broadcastNewsFeeds: true,
|
||||
broadcastNewsUpdates: true
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
@@ -1,5 +0,0 @@
|
||||
<?php
|
||||
include "functions/gzip.php";
|
||||
$url = $_GET["url"];
|
||||
echo get_url($url);
|
||||
?>
|
||||
@@ -1,42 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* @function get_url
|
||||
* @purpose To fetch GZipped web content.
|
||||
* @author Michael Teeuw
|
||||
*/
|
||||
function get_url($url) {
|
||||
/*
|
||||
* @array
|
||||
* Prepare the options that we need for our GZip request.
|
||||
*/
|
||||
$opts = array(
|
||||
"http" => array(
|
||||
"method" => "GET",
|
||||
"header" => "Accept-Language: en-US,en;q=0.8rn" . "Accept-Encoding: gzip,deflate,sdchrn" . "Accept-Charset:UTF-8,*;q=0.5rn" . "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:19.0) Gecko/20100101 Firefox/19.0 FirePHP/0.4rn",
|
||||
"ignore_errors" => true
|
||||
),
|
||||
/*
|
||||
* @array
|
||||
* Put a Band-Aid over some SSL issues.
|
||||
*/
|
||||
"ssl" => array(
|
||||
"verify_peer" => false,
|
||||
"verify_peer_name" => false
|
||||
)
|
||||
);
|
||||
$context = stream_context_create($opts);
|
||||
$content = file_get_contents($url, false, $context);
|
||||
/*
|
||||
* @note If http response header mentions that content is gzipped, then uncompress it.
|
||||
*/
|
||||
foreach($http_response_header as $c => $h) {
|
||||
if(stristr($h, "content-encoding") and stristr($h, "gzip")) {
|
||||
/*
|
||||
* @note Now, let's begin the actual purpose of this function:
|
||||
*/
|
||||
$content = gzinflate(substr($content, 10, -8));
|
||||
}
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
?>
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
echo json_encode(
|
||||
array(
|
||||
"gitHash" => trim(`git rev-parse HEAD`)
|
||||
)
|
||||
);
|
||||
?>
|
||||
394
css/main.css
394
css/main.css
@@ -1,224 +1,236 @@
|
||||
body,
|
||||
html {
|
||||
cursor: none;
|
||||
overflow: hidden;
|
||||
background: #000;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-family: "HelveticaNeue-Light", sans-serif;
|
||||
letter-spacing: -2px;
|
||||
color: #fff;
|
||||
font-size: 75px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 60px;
|
||||
position: absolute;
|
||||
height: calc(100% - 120px);
|
||||
width: calc(100% - 120px);
|
||||
background: #000;
|
||||
color: #aaa;
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
font-weight: 400;
|
||||
font-size: 2em;
|
||||
line-height: 1.5em;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: geometricprecision;
|
||||
}
|
||||
|
||||
.wi {
|
||||
line-height: 75px;
|
||||
/**
|
||||
* Default styles.
|
||||
*/
|
||||
|
||||
.dimmed {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.top {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
.normal {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.left {
|
||||
position: absolute;
|
||||
left: 50px;
|
||||
}
|
||||
|
||||
.right {
|
||||
position: absolute;
|
||||
right: 50px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.center-ver {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
height: 200px;
|
||||
margin-top: -100px;
|
||||
line-height: 100px;
|
||||
}
|
||||
|
||||
.lower-third {
|
||||
position: absolute;
|
||||
top: 66.666%;
|
||||
height: 200px;
|
||||
margin-top: -100px;
|
||||
line-height: 100px;
|
||||
}
|
||||
|
||||
.center-hor {
|
||||
position: absolute;
|
||||
right: 50px;
|
||||
left: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
}
|
||||
|
||||
.xxsmall,
|
||||
.xsmall,
|
||||
.small {
|
||||
font-family: "HelveticaNeue-Medium", sans-serif;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.xxsmall {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.xxsmall .wi {
|
||||
line-height: 15px;
|
||||
.bright {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.xsmall {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.xsmall .wi {
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.small .wi {
|
||||
font-size: 20px;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
.medium {
|
||||
font-size: 35px;
|
||||
letter-spacing: -1px;
|
||||
font-family: "HelveticaNeue-Light", sans-serif;
|
||||
}
|
||||
|
||||
.medium .wi {
|
||||
font-size: 30px;
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
.xdimmed {
|
||||
color: #666;
|
||||
.large {
|
||||
font-size: 65px;
|
||||
line-height: 65px;
|
||||
}
|
||||
|
||||
.dimmed {
|
||||
color: #aaa;
|
||||
.xlarge {
|
||||
font-size: 75px;
|
||||
line-height: 75px;
|
||||
letter-spacing: -3px;
|
||||
}
|
||||
|
||||
.thin {
|
||||
font-family: Roboto, sans-serif;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.light {
|
||||
font-family: "HelveticaNeue-UltraLight", sans-serif;
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: relative;
|
||||
top: -10px;
|
||||
display: inline-block;
|
||||
font-size: 45px;
|
||||
padding-right: 5px;
|
||||
font-weight: 100;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.icon-small {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
padding-left: 10px;
|
||||
padding-right: -10px;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.time .sec {
|
||||
font-size: 25px;
|
||||
color: #666;
|
||||
padding-left: 5px;
|
||||
position: relative;
|
||||
top: -35px;
|
||||
}
|
||||
|
||||
.forecast-table {
|
||||
float: right;
|
||||
text-align: right;
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.forecast-table .day,
|
||||
.forecast-table .temp-min,
|
||||
.forecast-table .temp-max {
|
||||
width: 50px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.forecast-table .temp-max {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.forecast-table .day {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.calendar-table {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.calendar-table .days {
|
||||
padding-left: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.dishwasher {
|
||||
background-color: white;
|
||||
color: black;
|
||||
margin: 0 200px;
|
||||
font-size: 60px;
|
||||
border-radius: 1000px;
|
||||
border-radius: 1200px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'HelveticaNeue-UltraLight';
|
||||
src: url('font/HelveticaNeue-UltraLight.eot');
|
||||
/* IE9 Compat Modes */
|
||||
src: url('font/HelveticaNeue-UltraLight.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||
url('font/HelveticaNeue-UltraLight.woff') format('woff'), /* Modern Browsers */
|
||||
url('font/HelveticaNeue-UltraLight.ttf') format('truetype'), /* Safari, Android, iOS */
|
||||
url('font/HelveticaNeue-UltraLight.svg#9453ea8da727d260bcdbfa605bdbb5d2') format('svg');
|
||||
/* Legacy iOS */
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'HelveticaNeue-Medium';
|
||||
src: url('font/HelveticaNeue-Medium.eot');
|
||||
/* IE9 Compat Modes */
|
||||
src: url('font/HelveticaNeue-Medium.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||
url('font/HelveticaNeue-Medium.woff') format('woff'), /* Modern Browsers */
|
||||
url('font/HelveticaNeue-Medium.ttf') format('truetype'), /* Safari, Android, iOS */
|
||||
url('font/HelveticaNeue-Medium.svg#d7af0fd9278f330eed98b60dddea7bd6') format('svg');
|
||||
/* Legacy iOS */
|
||||
font-style: normal;
|
||||
.regular {
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'HelveticaNeue-Light';
|
||||
src: url('font/HelveticaNeue-Light.eot');
|
||||
/* IE9 Compat Modes */
|
||||
src: url('font/HelveticaNeue-Light.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||
url('font/HelveticaNeue-Light.woff') format('woff'), /* Modern Browsers */
|
||||
url('font/HelveticaNeue-Light.ttf') format('truetype'), /* Safari, Android, iOS */
|
||||
url('font/HelveticaNeue-Light.svg#7384ecabcada72f0e077cd45d8e1c705') format('svg');
|
||||
/* Legacy iOS */
|
||||
font-style: normal;
|
||||
font-weight: 200;
|
||||
}
|
||||
.bold {
|
||||
font-family: "Roboto Condensed", sans-serif;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
header {
|
||||
text-transform: uppercase;
|
||||
font-size: 15px;
|
||||
font-family: "Roboto Condensed", Arial, Helvetica, sans-serif;
|
||||
font-weight: 400;
|
||||
border-bottom: 1px solid #666;
|
||||
line-height: 15px;
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: 10px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
sup {
|
||||
font-size: 50%;
|
||||
line-height: 50%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Module styles.
|
||||
*/
|
||||
|
||||
.module {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.region.bottom .module {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.no-wrap {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.pre-line {
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Region Definitions.
|
||||
*/
|
||||
|
||||
.region {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.region.fullscreen {
|
||||
position: absolute;
|
||||
top: -60px;
|
||||
left: -60px;
|
||||
right: -60px;
|
||||
bottom: -60px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.region.fullscreen * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.region.right {
|
||||
right: 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.region.top {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.region.top .container {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.region.bottom .container {
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.region.top .container:empty {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.region.top.center,
|
||||
.region.bottom.center {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.region.top.right,
|
||||
.region.top.left,
|
||||
.region.top.center {
|
||||
top: 100%;
|
||||
}
|
||||
|
||||
.region.bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.region.bottom .container:empty {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.region.bottom.right,
|
||||
.region.bottom.center,
|
||||
.region.bottom.left {
|
||||
bottom: 100%;
|
||||
}
|
||||
|
||||
.region.bar {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.region.third,
|
||||
.region.middle.center {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.region.upper.third {
|
||||
top: 33%;
|
||||
}
|
||||
|
||||
.region.middle.center {
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.region.lower.third {
|
||||
top: 66%;
|
||||
}
|
||||
|
||||
.region.left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.region table {
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
border-collapse: separate;
|
||||
}
|
||||
|
||||
@@ -1,327 +0,0 @@
|
||||
/*!
|
||||
* Weather Icons Beta 1
|
||||
* Weather themed icons for Bootstrap
|
||||
* ------------------------------------------------------------------------------
|
||||
* Maintained at http://erikflowers.github.io/weather-icons
|
||||
* http://twitter.com/Erik_UX
|
||||
*
|
||||
* License
|
||||
* ------------------------------------------------------------------------------
|
||||
* - Fpmt licensed under SIL OFL 1.1 -
|
||||
* http://scripts.sil.org/OFL
|
||||
* - CSS and LESS are licensed under MIT License -
|
||||
* http://opensource.org/licenses/mit-license.html
|
||||
* - Documentation licensed under CC BY 3.0 -
|
||||
* http://creativecommons.org/licenses/by/3.0/
|
||||
* - Inspired by and works great as a companion with Font Aweosme
|
||||
* "Font Awesome by Dave Gandy - http://fontawesome.io"
|
||||
*
|
||||
* Weather Icons Bootstrap Package Author - Erik Flowers - erik@helloerik.com
|
||||
* Weather Icons gives full credit for inspiration to Font Awesome and makes no
|
||||
* claim to invention, intellectual property, or ownership of methodology.
|
||||
*
|
||||
* Support Open Source!
|
||||
*
|
||||
* ------------------------------------------------------------------------------
|
||||
* Email: erik@helloerik.com
|
||||
* Twitter: http://twitter.com/Erik_UX
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'weather';
|
||||
src: url('../font/weathericons-regular-webfont.eot');
|
||||
src: url('../font/weathericons-regular-webfont.eot?#iefix') format('embedded-opentype'), url('../font/weathericons-regular-webfont.woff') format('woff'), url('../font/weathericons-regular-webfont.ttf') format('truetype'), url('../font/weathericons-regular-webfont.svg#weathericons-regular-webfontRg') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
[class^="wi-"],
|
||||
[class*=" wi-"] {
|
||||
font-family: weather;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
text-decoration: inherit;
|
||||
text-transform: none;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
*margin-right: .3em;
|
||||
}
|
||||
[class^="wi-"]:before,
|
||||
[class*=" wi-"]:before {
|
||||
text-decoration: inherit;
|
||||
display: inline-block;
|
||||
speak: none;
|
||||
}
|
||||
.wi-day-cloudy-gusts:before {
|
||||
content: "\f000";
|
||||
}
|
||||
.wi-day-cloudy-windy:before {
|
||||
content: "\f001";
|
||||
}
|
||||
.wi-day-cloudy:before {
|
||||
content: "\f002";
|
||||
}
|
||||
.wi-day-fog:before {
|
||||
content: "\f003";
|
||||
}
|
||||
.wi-day-hail:before {
|
||||
content: "\f004";
|
||||
}
|
||||
.wi-day-lightning:before {
|
||||
content: "\f005";
|
||||
}
|
||||
.wi-day-rain-mix:before {
|
||||
content: "\f006";
|
||||
}
|
||||
.wi-day-rain-wind:before {
|
||||
content: "\f007";
|
||||
}
|
||||
.wi-day-rain:before {
|
||||
content: "\f008";
|
||||
}
|
||||
.wi-day-showers:before {
|
||||
content: "\f009";
|
||||
}
|
||||
.wi-day-snow:before {
|
||||
content: "\f00a";
|
||||
}
|
||||
.wi-day-sprinkle:before {
|
||||
content: "\f00b";
|
||||
}
|
||||
.wi-day-sunny-overcast:before {
|
||||
content: "\f00c";
|
||||
}
|
||||
.wi-day-sunny:before {
|
||||
content: "\f00d";
|
||||
}
|
||||
.wi-day-storm-showers:before {
|
||||
content: "\f00e";
|
||||
}
|
||||
.wi-day-thunderstorm:before {
|
||||
content: "\f010";
|
||||
}
|
||||
.wi-cloudy-gusts:before {
|
||||
content: "\f011";
|
||||
}
|
||||
.wi-cloudy-windy:before {
|
||||
content: "\f012";
|
||||
}
|
||||
.wi-cloudy:before {
|
||||
content: "\f013";
|
||||
}
|
||||
.wi-fog:before {
|
||||
content: "\f014";
|
||||
}
|
||||
.wi-hail:before {
|
||||
content: "\f015";
|
||||
}
|
||||
.wi-lightning:before {
|
||||
content: "\f016";
|
||||
}
|
||||
.wi-rain-mix:before {
|
||||
content: "\f017";
|
||||
}
|
||||
.wi-rain-wind:before {
|
||||
content: "\f018";
|
||||
}
|
||||
.wi-rain:before {
|
||||
content: "\f019";
|
||||
}
|
||||
.wi-showers:before {
|
||||
content: "\f01a";
|
||||
}
|
||||
.wi-snow:before {
|
||||
content: "\f01b";
|
||||
}
|
||||
.wi-sprinkle:before {
|
||||
content: "\f01c";
|
||||
}
|
||||
.wi-storm-showers:before {
|
||||
content: "\f01d";
|
||||
}
|
||||
.wi-thunderstorm:before {
|
||||
content: "\f01e";
|
||||
}
|
||||
.wi-windy:before {
|
||||
content: "\f021";
|
||||
}
|
||||
.wi-night-alt-cloudy-gusts:before {
|
||||
content: "\f022";
|
||||
}
|
||||
.wi-night-alt-cloudy-windy:before {
|
||||
content: "\f023";
|
||||
}
|
||||
.wi-night-alt-hail:before {
|
||||
content: "\f024";
|
||||
}
|
||||
.wi-night-alt-lightning:before {
|
||||
content: "\f025";
|
||||
}
|
||||
.wi-night-alt-rain-mix:before {
|
||||
content: "\f026";
|
||||
}
|
||||
.wi-night-alt-rain-wind:before {
|
||||
content: "\f027";
|
||||
}
|
||||
.wi-night-alt-rain:before {
|
||||
content: "\f028";
|
||||
}
|
||||
.wi-night-alt-showers:before {
|
||||
content: "\f029";
|
||||
}
|
||||
.wi-night-alt-snow:before {
|
||||
content: "\f02a";
|
||||
}
|
||||
.wi-night-alt-sprinkle:before {
|
||||
content: "\f02b";
|
||||
}
|
||||
.wi-night-alt-storm-showers:before {
|
||||
content: "\f02c";
|
||||
}
|
||||
.wi-night-alt-thunderstorm:before {
|
||||
content: "\f02d";
|
||||
}
|
||||
.wi-night-clear:before {
|
||||
content: "\f02e";
|
||||
}
|
||||
.wi-night-cloudy-gusts:before {
|
||||
content: "\f02f";
|
||||
}
|
||||
.wi-night-cloudy-windy:before {
|
||||
content: "\f030";
|
||||
}
|
||||
.wi-night-cloudy:before {
|
||||
content: "\f031";
|
||||
}
|
||||
.wi-night-hail:before {
|
||||
content: "\f032";
|
||||
}
|
||||
.wi-night-lightning:before {
|
||||
content: "\f033";
|
||||
}
|
||||
.wi-night-rain-mix:before {
|
||||
content: "\f034";
|
||||
}
|
||||
.wi-night-rain-wind:before {
|
||||
content: "\f035";
|
||||
}
|
||||
.wi-night-rain:before {
|
||||
content: "\f036";
|
||||
}
|
||||
.wi-night-showers:before {
|
||||
content: "\f037";
|
||||
}
|
||||
.wi-night-snow:before {
|
||||
content: "\f038";
|
||||
}
|
||||
.wi-night-sprinkle:before {
|
||||
content: "\f039";
|
||||
}
|
||||
.wi-night-storm-showers:before {
|
||||
content: "\f03a";
|
||||
}
|
||||
.wi-night-thunderstorm:before {
|
||||
content: "\f03b";
|
||||
}
|
||||
.wi-celcius:before {
|
||||
content: "\f03c";
|
||||
}
|
||||
.wi-cloud-down:before {
|
||||
content: "\f03d";
|
||||
}
|
||||
.wi-cloud-refresh:before {
|
||||
content: "\f03e";
|
||||
}
|
||||
.wi-cloud-up:before {
|
||||
content: "\f040";
|
||||
}
|
||||
.wi-cloud:before {
|
||||
content: "\f041";
|
||||
}
|
||||
.wi-degrees:before {
|
||||
content: "\f042";
|
||||
}
|
||||
.wi-down-left:before {
|
||||
content: "\f043";
|
||||
}
|
||||
.wi-down:before {
|
||||
content: "\f044";
|
||||
}
|
||||
.wi-fahrenheit:before {
|
||||
content: "\f045";
|
||||
}
|
||||
.wi-horizon-alt:before {
|
||||
content: "\f046";
|
||||
}
|
||||
.wi-horizon:before {
|
||||
content: "\f047";
|
||||
}
|
||||
.wi-left:before {
|
||||
content: "\f048";
|
||||
}
|
||||
.wi-lightning:before {
|
||||
content: "\f016";
|
||||
}
|
||||
.wi-night-fog:before {
|
||||
content: "\f04a";
|
||||
}
|
||||
.wi-refresh-alt:before {
|
||||
content: "\f04b";
|
||||
}
|
||||
.wi-refresh:before {
|
||||
content: "\f04c";
|
||||
}
|
||||
.wi-right:before {
|
||||
content: "\f04d";
|
||||
}
|
||||
.wi-sprinkles:before {
|
||||
content: "\f04e";
|
||||
}
|
||||
.wi-strong-wind:before {
|
||||
content: "\f050";
|
||||
}
|
||||
.wi-sunrise:before {
|
||||
content: "\f051";
|
||||
}
|
||||
.wi-sunset:before {
|
||||
content: "\f052";
|
||||
}
|
||||
.wi-thermometer-exterior:before {
|
||||
content: "\f053";
|
||||
}
|
||||
.wi-thermometer-internal:before {
|
||||
content: "\f054";
|
||||
}
|
||||
.wi-thermometer:before {
|
||||
content: "\f055";
|
||||
}
|
||||
.wi-tornado:before {
|
||||
content: "\f056";
|
||||
}
|
||||
.wi-up-right:before {
|
||||
content: "\f057";
|
||||
}
|
||||
.wi-up:before {
|
||||
content: "\f058";
|
||||
}
|
||||
.wi-wind-east:before {
|
||||
content: "\f059";
|
||||
}
|
||||
.wi-wind-north-east:before {
|
||||
content: "\f05a";
|
||||
}
|
||||
.wi-wind-north-west:before {
|
||||
content: "\f05b";
|
||||
}
|
||||
.wi-wind-north:before {
|
||||
content: "\f05c";
|
||||
}
|
||||
.wi-wind-south-east:before {
|
||||
content: "\f05d";
|
||||
}
|
||||
.wi-wind-south-west:before {
|
||||
content: "\f05e";
|
||||
}
|
||||
.wi-wind-south:before {
|
||||
content: "\f060";
|
||||
}
|
||||
.wi-wind-west:before {
|
||||
content: "\f061";
|
||||
}
|
||||
17
dangerfile.js
Normal file
17
dangerfile.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { danger, fail, warn } from "danger";
|
||||
|
||||
// Check if the CHANGELOG.md file has been edited
|
||||
// Fail the build and post a comment reminding submitters to do so if it wasn't changed
|
||||
if (!danger.git.modified_files.includes("CHANGELOG.md")) {
|
||||
warn("Please include an updated `CHANGELOG.md` file.<br>This way we can keep track of all the contributions.");
|
||||
}
|
||||
|
||||
// Check if the PR request is send to the master branch.
|
||||
// This should only be done by MichMich.
|
||||
if (danger.github.pr.base.ref === "master" && danger.github.pr.user.login !== "MichMich") {
|
||||
// Check if the PR body or title includes the text: #accepted.
|
||||
// If not, the PR will fail.
|
||||
if ((danger.github.pr.body + danger.github.pr.title).includes("#accepted")) {
|
||||
fail("Please send all your pull requests to the `develop` branch.<br>Pull requests on the `master` branch will not be accepted.");
|
||||
}
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 338 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 383 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 401 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,121 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata></metadata>
|
||||
<defs>
|
||||
<font id="weather_iconsregular" horiz-adv-x="2548" >
|
||||
<font-face units-per-em="2048" ascent="1755" descent="-293" />
|
||||
<missing-glyph horiz-adv-x="685" />
|
||||
<glyph horiz-adv-x="2048" />
|
||||
<glyph horiz-adv-x="2048" />
|
||||
<glyph unicode="
" horiz-adv-x="2048" />
|
||||
<glyph unicode=" " horiz-adv-x="685" />
|
||||
<glyph unicode="	" horiz-adv-x="685" />
|
||||
<glyph unicode=" " horiz-adv-x="685" />
|
||||
<glyph unicode=" " horiz-adv-x="1122" />
|
||||
<glyph unicode=" " horiz-adv-x="2245" />
|
||||
<glyph unicode=" " horiz-adv-x="1122" />
|
||||
<glyph unicode=" " horiz-adv-x="2245" />
|
||||
<glyph unicode=" " horiz-adv-x="748" />
|
||||
<glyph unicode=" " horiz-adv-x="561" />
|
||||
<glyph unicode=" " horiz-adv-x="374" />
|
||||
<glyph unicode=" " horiz-adv-x="374" />
|
||||
<glyph unicode=" " horiz-adv-x="280" />
|
||||
<glyph unicode=" " horiz-adv-x="449" />
|
||||
<glyph unicode=" " horiz-adv-x="124" />
|
||||
<glyph unicode=" " horiz-adv-x="449" />
|
||||
<glyph unicode=" " horiz-adv-x="561" />
|
||||
<glyph unicode="" horiz-adv-x="571" d="M0 0z" />
|
||||
<glyph unicode="" d="M-287 90q0 -38 29 -64q27 -27 65 -27h627q41 0 72.5 -30t31.5 -73t-31.5 -74t-72.5 -31t-73 32q-29 26 -65 26q-38 0 -64 -25.5t-26 -63.5t26 -64q85 -85 202 -85q118 0 201 83.5t83 201.5t-83 202t-201 84h-627q-38 0 -66 -27.5t-28 -64.5zM-287 411q0 -35 29 -61 q27 -27 65 -27h1170q118 0 201.5 83.5t83.5 201.5t-83 200t-202 82q-121 0 -201 -81q-25 -26 -25 -65t24.5 -63.5t63.5 -24.5q38 0 66 25q30 30 72 30t72.5 -30t30.5 -73t-30.5 -74t-72.5 -31h-1170q-38 0 -66 -27.5t-28 -64.5zM-3 662q0 -13 17 -13h153q12 0 22 15 q36 87 111.5 143.5t169.5 63.5l56 8q20 0 20 18l7 55q17 173 146 289t304 116q176 0 305.5 -115.5t147.5 -289.5l7 -62q0 -19 19 -19h174q144 0 245.5 -100.5t101.5 -242.5q0 -143 -101.5 -244.5t-245.5 -101.5h-736q-19 0 -19 -19v-146q0 -18 19 -18h736q143 0 264.5 71 t192 193t70.5 265q0 118 -45 216q121 159 121 353q0 150 -75 279t-204 204.5t-279 75.5q-248 0 -413 -185q-128 65 -285 65q-225 0 -398 -139.5t-220 -356.5q-136 -32 -240.5 -131t-145.5 -234v-4q-2 -3 -2 -9zM869 1838q0 -38 28 -64l65 -69q26 -26 65 -26q40 0 65.5 24.5 t25.5 64.5q0 38 -24 64l-70 69q-25 28 -63 28t-65 -26.5t-27 -64.5zM1437 1368q115 109 264 109q155 0 267 -112t112 -268q0 -104 -55 -195q-153 153 -369 153h-35q-38 173 -184 313zM1609 1927q0 -37 27 -62.5t65 -25.5t64.5 25.5t26.5 62.5v218q0 38 -26.5 65t-64.5 27 t-65 -27t-27 -65v-218zM2201 1685q0 -39 24 -64q27 -27 65 -27.5t61 27.5l156 153q27 27 27 65q0 37 -27 64t-65 27t-64 -26l-153 -157q-24 -25 -24 -62zM2283 423q0 -37 27 -64l68 -69q32 -26 67 -26q30 0 62 26q27 27 27 64q0 35 -27 65l-68 69q-26 26 -62 26 q-40 0 -67 -26.5t-27 -64.5zM2445 1097q0 -39 26 -63q24 -28 62 -28h216q37 0 64 27t27 64q0 38 -27 65t-64 27h-216q-38 0 -63 -27t-25 -65z" />
|
||||
<glyph unicode="" d="M-253 94q0 -38 27 -65q70 -29 93 -29h840q38 0 65 27.5t27 65.5q0 37 -27 62.5t-65 25.5h-840q-42 0 -81 -25.5t-39 -61.5zM-104 414q0 -38 27 -63q24 -28 62 -28h1004q38 0 64.5 26.5t26.5 64.5t-26.5 65t-64.5 27h-1004q-37 0 -63 -27t-26 -65zM8 667q0 -14 18 -14h149 q19 0 23 14q41 85 115.5 140.5t168.5 66.5h59q17 0 17 20l8 60q16 173 146 290t306 117q174 0 304.5 -117t148.5 -290l8 -60q0 -20 18 -20h172q142 0 245 -102t103 -242q0 -144 -102.5 -246.5t-245.5 -102.5h-735q-19 0 -19 -20v-142q0 -19 19 -19h735q144 0 266 71t193 193 t71 266q0 114 -47 214q125 155 125 350q0 150 -76 279t-205.5 204.5t-280.5 75.5q-117 0 -223.5 -47.5t-185.5 -133.5q-132 69 -288 69q-226 0 -401 -140t-223 -358q-136 -34 -239.5 -133.5t-144.5 -235.5q-2 -2 -2 -7zM85 -246q0 -39 27 -63q26 -27 63 -27h1004 q37 0 63.5 26.5t26.5 63.5q0 40 -26 66.5t-64 26.5h-1004q-38 0 -64 -26.5t-26 -66.5zM882 1835q0 -39 27 -64l67 -70q26 -26 64 -26q41 0 67 25.5t26 65.5q0 36 -27 63l-68 69q-27 27 -62 27q-40 0 -67 -26.5t-27 -63.5zM1455 1368q106 101 260 101q156 0 265.5 -109.5 t109.5 -265.5q0 -107 -49 -193q-151 152 -372 152h-32q-46 184 -182 315zM1625 1925q0 -38 26.5 -63t63.5 -25q40 0 65 25t25 63v218q0 39 -25.5 65.5t-64.5 26.5q-37 0 -63.5 -27t-26.5 -65v-218zM2215 1681q0 -39 26 -64q20 -26 64 -26q43 0 63 26l157 154q26 24 26 66 q0 37 -27 63.5t-65 26.5t-65 -25l-153 -158q-26 -25 -26 -63zM2299 416q0 -38 26 -64l69 -67q39 -29 65 -29q27 0 66 29q26 26 26 64q0 37 -26 65l-69 65q-30 28 -69 28q-38 0 -63 -26t-25 -65zM2454 1090q0 -40 28 -65q29 -27 64 -27h217q36 0 63 27t27 65q0 36 -26.5 60.5 t-63.5 24.5h-217q-39 0 -65.5 -24.5t-26.5 -60.5z" />
|
||||
<glyph unicode="" d="M8 528q0 -144 70.5 -266t191.5 -192.5t264 -70.5h1155q143 0 265 70.5t193 192.5t71 266q0 106 -45 213q122 149 122 353q0 114 -44 217.5t-119 178.5t-178.5 119t-217.5 44q-237 0 -414 -186q-124 70 -288 70q-225 0 -398 -139.5t-222 -357.5q-179 -41 -292.5 -184 t-113.5 -328zM190 528q0 134 89.5 231t224.5 113l53 3q20 0 20 19l7 58q22 173 150 289.5t300 116.5q176 0 306.5 -117t146.5 -289l8 -62q4 -18 22 -18h172q141 0 243 -102t102 -242q0 -145 -101.5 -247.5t-243.5 -102.5h-1155q-140 0 -242 103.5t-102 246.5zM905 1837 q0 -39 26 -67l70 -68q40 -30 68 -27q33 0 59 27.5t26 66.5t-28 63l-63 70q-29 26 -65 26q-39 0 -66 -26.5t-27 -64.5zM1475 1365q111 107 261 107q158 0 268.5 -110t110.5 -268q0 -100 -54 -196q-155 153 -372 153h-34q-40 174 -180 314zM1648 1928q0 -41 25 -66t63 -25 q41 0 66 25t25 66v218q0 38 -25.5 63t-65.5 25q-38 0 -63 -25t-25 -63v-218zM2237 1683q0 -41 24 -66q34 -26 66 -26q29 0 63 26l153 153q26 29 26 68q0 38 -26 64t-63 26q-38 0 -62 -26l-157 -153q-24 -28 -24 -66zM2322 421q0 -38 27 -67l69 -67q24 -26 62 -26t63.5 26.5 t25.5 66.5q0 36 -26 62l-69 69q-26 26 -61 26q-38 0 -64.5 -26t-26.5 -64zM2478 1094q0 -37 28 -62q26 -26 65 -26h218q37 0 61.5 25t24.5 63t-24.5 64.5t-61.5 26.5h-218q-38 0 -65.5 -27t-27.5 -64z" />
|
||||
<glyph unicode="" d="M-290 85q0 -38 26.5 -63t66.5 -25h1997q38 0 64.5 25.5t26.5 62.5q0 38 -26.5 64.5t-64.5 26.5h-1997q-40 0 -66.5 -26.5t-26.5 -64.5zM-10 419q0 -37 27 -62q27 -29 64 -29h1997q37 0 62.5 26.5t25.5 64.5q0 37 -25 62.5t-63 25.5h-1997q-38 0 -64.5 -25.5t-26.5 -62.5z M2 675q0 -14 17 -14h154q10 0 21 17q38 83 113.5 136t166.5 60l59 8q18 0 18 19l7 54q17 173 146.5 289t304.5 116q173 0 302 -115t147 -286l8 -62q0 -18 21 -18h171q103 0 187.5 -55t125.5 -146q11 -17 22 -17h153q22 0 15 24l-22 59q122 151 122 353q0 113 -44 216 t-118 178t-178 119t-218 44q-247 0 -408 -179q-135 67 -286 67q-224 0 -399 -141t-224 -359q-284 -75 -381 -357q-3 -5 -3 -10zM176 -243q0 -38 29 -64q25 -28 62 -28h1999q38 0 65 27.5t27 64.5t-27 61.5t-65 24.5h-1999q-38 0 -64.5 -24.5t-26.5 -61.5zM877 1834 q0 -38 25 -63l68 -68q27 -29 63 -29t63 26.5t27 65.5t-26 67l-68 65q-26 28 -64 28q-39 0 -63.5 -26.5t-24.5 -65.5zM1443 1367q108 108 260 108q157 0 267.5 -111t110.5 -267q0 -104 -52 -192q-152 152 -371 152h-35q-44 186 -180 310zM1615 1925q0 -37 25.5 -62.5 t62.5 -25.5q40 0 67 25.5t27 62.5v218q0 37 -28 64t-66 27q-37 0 -62.5 -26.5t-25.5 -64.5v-218zM2201 1682q0 -37 25 -64q59 -58 132 0l152 153q26 28 26 68q0 37 -26 63t-63 26q-38 0 -64 -26l-157 -157q-25 -27 -25 -63zM2287 424q0 -38 27 -64l69 -69q26 -26 62 -26 q33 0 65 26q26 28 26 68q0 36 -26 60l-69 70q-28 26 -64 26q-38 0 -64 -26.5t-26 -64.5zM2445 1097q0 -39 28 -64q24 -27 63 -27h218q37 0 62.5 26.5t25.5 64.5t-25.5 64t-62.5 26h-218q-38 0 -64.5 -26.5t-26.5 -63.5z" />
|
||||
<glyph unicode="" d="M1 530q0 -214 149 -367.5t363 -163.5q20 0 20 18v143q0 19 -20 19q-137 7 -233.5 109.5t-96.5 241.5q0 133 90.5 231t224.5 114l57 4q21 0 21 19l7 59q17 173 146.5 289.5t305.5 116.5q174 0 305.5 -116.5t149.5 -289.5l8 -62q0 -20 19 -20h172q143 0 247.5 -102.5 t104.5 -242.5q0 -139 -97 -241.5t-234 -109.5q-21 0 -21 -19v-143q0 -18 21 -18q214 7 362.5 161.5t148.5 369.5q0 116 -45 214q126 154 126 355q0 151 -75.5 280.5t-205 205.5t-280.5 76q-250 0 -416 -187q-128 70 -290 70q-226 0 -401.5 -140t-225.5 -359 q-177 -42 -292 -186t-115 -329zM570 -223q16 -35 49 -48q32 -16 67.5 -2.5t47.5 47.5q16 35 2.5 69t-47.5 47q-32 17 -66 3t-50 -50q-16 -28 -3 -66zM639 80q0 -22 10 -41q31 -49 95 -49q51 0 74 69l111 343q13 39 -7.5 71.5t-58.5 39.5q-35 11 -67.5 -6.5t-43.5 -53.5 l-110 -344q-3 -15 -3 -29zM838 -519q0 -21 5 -31q14 -35 48 -48q15 -8 37 -8q10 0 32 6q35 13 50.5 48.5t-0.5 70.5t-48 48.5t-66 -0.5q-31 -12 -44.5 -37.5t-13.5 -48.5zM903 1843q0 -40 26 -64l69 -69q26 -26 58 -29q33 -5 65.5 24.5t32.5 68.5q0 38 -26 64l-67 68 q-30 27 -67 27q-39 0 -65 -26t-26 -64zM917 -232q0 -26 16.5 -51t50.5 -35q18 -4 26 -4q24 0 41 8q32 13 46 61l192 655q11 38 -6 69.5t-53 41.5q-38 11 -70 -6t-43 -54l-197 -660q-3 -15 -3 -25zM1285 -191q0 -22 6 -33q14 -33 48 -47q17 -8 37 -8q10 0 32 6q36 14 49 47 q13 35 0.5 67.5t-44.5 48.5q-36 17 -70.5 3t-51.5 -50q-6 -11 -6 -34zM1361 83q0 -25 16.5 -48.5t49.5 -33.5q13 -3 27 -3q62 0 84 65l110 339q12 37 -7 69t-55 42q-38 11 -68.5 -6t-42.5 -54l-109 -341q-5 -22 -5 -29zM1475 1371q107 103 266 103q156 0 266.5 -109.5 t110.5 -265.5q0 -100 -55 -197q-153 154 -374 154h-33q-47 185 -181 315zM1648 1933q0 -38 27.5 -65t65.5 -27q37 0 62.5 27t25.5 65v220q0 38 -25.5 65t-62.5 27q-38 0 -65.5 -27t-27.5 -65v-220zM2240 1689q0 -39 27 -64q24 -28 61.5 -27.5t64.5 27.5l154 154q29 24 29 64 q0 38 -27.5 65t-65.5 27q-35 0 -61 -29l-155 -153q-27 -25 -27 -64zM2326 419q0 -36 26 -64l70 -67q23 -29 64 -29q38 0 61 29q29 26 29 64t-29 65l-69 66q-25 28 -62 28t-63.5 -27t-26.5 -65zM2482 1099q0 -38 28 -64q28 -28 66 -28h217q38 0 65.5 27t27.5 65 q0 37 -27 62.5t-66 25.5h-217q-40 0 -67 -25.5t-27 -62.5z" />
|
||||
<glyph unicode="" d="M5 528q0 -213 148 -366t362 -163q19 0 19 18v146q0 19 -19 19q-139 11 -234 110.5t-95 235.5q0 134 91 233.5t224 110.5l56 8q20 0 20 18l7 54q17 173 146.5 289.5t304.5 116.5q174 0 304 -116t149 -290l7 -62q0 -18 19 -18h172q145 0 247 -101t102 -243 q0 -136 -95 -235.5t-234 -110.5q-20 0 -20 -19v-146q0 -18 20 -18q213 7 361 161t148 368q0 108 -50 221q126 150 126 349q0 114 -44.5 217.5t-119 178.5t-178 119t-216.5 44q-247 0 -414 -185q-137 66 -284 66q-227 0 -401.5 -139t-223.5 -359q-176 -41 -290.5 -184.5 t-114.5 -327.5zM805 -674h32l566 837q6 7 3 14.5t-14 7.5h-233l244 449q12 23 -14 23h-315q-13 0 -23 -15l-229 -610q-4 -22 15 -22h230zM898 1841q0 -39 27 -66l69 -69q67 -51 129 0q27 30 27 68q0 36 -27 63l-67 69q-30 27 -66 27q-38 0 -65 -27.5t-27 -64.5zM1469 1369 q109 109 264 109q156 0 266 -111t110 -269q0 -97 -54 -193q-156 150 -369 150h-33q-43 182 -184 314zM1641 1933q0 -40 26 -66t66 -26q38 0 63 25.5t25 66.5v218q0 38 -25 63t-63 25t-65 -25t-27 -63v-218zM2230 1686q0 -38 28 -64q29 -27 60 -27q26 0 66 27l153 153 q28 30 28 67q0 39 -27 65t-65 26q-35 0 -62 -27l-153 -153q-28 -30 -28 -67zM2317 425q0 -37 26 -65l68 -70q26 -26 62 -26q38 0 65 27.5t27 66.5q0 35 -28 61l-65 70q-30 25 -66 25q-38 0 -63.5 -26t-25.5 -63zM2472 1098q0 -36 27 -61q27 -27 64 -27h219q38 0 64.5 25.5 t26.5 62.5q0 38 -27 65t-64 27h-219q-37 0 -64 -27t-27 -65z" />
|
||||
<glyph unicode="" d="M1 523q0 -212 146 -363t360 -161q19 0 19 18v142q0 19 -19 19q-137 7 -231 106.5t-94 238.5q0 135 90 234t224 110l56 8q17 0 17 15l8 59q20 175 147.5 290t302.5 115t305.5 -115t147.5 -287l7 -62q4 -18 23 -18h171q142 0 244 -102.5t102 -246.5q0 -139 -94 -238.5 t-231 -106.5q-21 0 -21 -19v-142q0 -18 21 -18q213 7 359 159t146 365q0 108 -41 214q124 154 124 357q0 113 -44 216.5t-119 178.5t-178.5 119.5t-217.5 44.5q-246 0 -412 -186q-143 70 -292 70q-226 0 -398.5 -140t-221.5 -360q-180 -41 -293 -184.5t-113 -329.5z M573 -227q0 -27 17 -54t50 -37q37 -11 68.5 4t42.5 60l15 65q8 36 -10 67.5t-55 42.5q-36 11 -68.5 -8t-42.5 -57l-15 -63q-2 -6 -2 -20zM654 82q0 -35 26 -61q25 -27 61 -27q38 0 64 25.5t26 62.5t-26 62.5t-64 25.5q-37 0 -62 -25t-25 -63zM719 325q-2 -25 14 -48.5 t51 -34.5q33 -10 66.5 7.5t44.5 54.5l30 96q12 39 -7.5 69.5t-58.5 41.5q-35 11 -67 -7t-43 -53l-27 -98q-3 -27 -3 -28zM842 -560q0 -27 17 -52.5t52 -35.5q14 -3 26 -3q69 0 85 65l15 63q10 40 -9 72.5t-57 39.5q-34 11 -66.5 -7.5t-43.5 -54.5l-15 -63q-4 -18 -4 -24z M902 1834q0 -38 26 -64l70 -68q22 -25 57.5 -27.5t66.5 27.5q27 27 27 63q0 37 -27 64l-67 69q-25 25 -63 28q-37 0 -63.5 -27t-26.5 -65zM926 -250q0 -36 26 -62t62 -26q38 0 63 25t25 63q0 37 -25 62t-63 25t-63 -25t-25 -62zM992 -8q-2 -24 14.5 -50t48.5 -32 q37 -10 68 6t44 60l28 96q11 35 -7.5 67t-56.5 43q-35 11 -68 -8.5t-44 -56.5l-24 -96q-3 -14 -3 -29zM1287 -237q0 -27 16 -51.5t49 -34.5q5 0 15 -2t15 -2q65 0 81 70l15 64q11 34 -7.5 67t-54.5 44q-39 10 -71.5 -8.5t-43.5 -56.5l-11 -63q0 -2 -1.5 -12t-1.5 -15z M1366 78q0 -37 26 -61q24 -26 62 -26t63 25t25 62q0 38 -25 63t-63 25t-63 -25t-25 -63zM1431 325q0 -27 17.5 -51.5t52.5 -34.5q3 0 12.5 -2t14.5 -2q17 0 39 10q33 17 43 56l27 96q10 36 -8 67.5t-54 42.5q-37 11 -68.5 -6t-42.5 -54l-30 -97q0 -3 -1.5 -11.5t-1.5 -13.5z M1469 1361q106 106 262 106q158 0 269 -108.5t111 -264.5q0 -106 -57 -200q-157 157 -373 157h-33q-45 176 -179 310zM1645 1923q0 -37 24.5 -61.5t61.5 -24.5q39 0 66 24.5t27 61.5v220q0 37 -27.5 64t-65.5 27q-37 0 -61.5 -26.5t-24.5 -64.5v-220zM2233 1681 q0 -40 24 -64q57 -57 129 0l153 153q27 27 27 67q0 38 -26.5 64.5t-64.5 26.5q-37 0 -65 -26l-153 -158q-24 -23 -24 -63zM2317 417q0 -38 28 -63l65 -67q32 -26 65 -26q32 0 64 26q27 25 27 63q0 36 -27 66l-69 65q-24 27 -61 27t-64 -27q-28 -26 -28 -64zM2474 1094 q0 -38 28 -63q29 -29 66 -29h216q37 0 62.5 27t25.5 65t-25.5 64.5t-62.5 26.5h-216q-38 0 -66 -27t-28 -64z" />
|
||||
<glyph unicode="" d="M-3 527q0 -180 106.5 -321.5t274.5 -188.5q16 -2 27 8l124 150q-143 0 -244.5 103.5t-101.5 248.5q0 134 90 231.5t225 113.5l53 3q20 0 20 20l8 58q17 173 147 290t306 117t307 -117t148 -290l7 -63q7 -18 23 -18h174q141 0 243.5 -101.5t102.5 -243.5 q0 -135 -90.5 -236t-220.5 -112q-78 -9 -96 -30l-235 -301q-22 -30 -17 -67t33 -60q24 -27 63.5 -22t63.5 38l206 262q98 10 185.5 56t150.5 116t100 163t37 193q0 117 -44 214q125 151 125 355q0 113 -44.5 217t-120 179t-179.5 120t-218 45q-241 0 -415 -186 q-127 70 -289 70q-226 0 -400.5 -140.5t-221.5 -359.5q-181 -41 -297 -185t-116 -329zM413 -352q12 -35 49 -51q35 -16 71 -1t49 50q15 33 0.5 66.5t-47.5 49.5q-35 16 -70.5 2t-48.5 -49q-13 -41 -3 -67zM606 -59v-12q3 -35 32 -59q28 -24 67 -21t61 31l233 301 q23 29 19.5 67.5t-31.5 60.5q-29 24 -67 20t-64 -33l-230 -299q-20 -25 -20 -56zM661 -578q0 -14 5 -32q14 -35 49 -49q18 -7 38 -7q15 0 33 5q34 13 47 47q16 35 3 71t-47 49q-35 16 -71.5 1.5t-48.5 -48.5q-8 -17 -8 -37zM839 -325v-11q3 -37 34 -62q24 -26 64 -21.5 t64 36.5l442 558q22 32 18.5 68.5t-32.5 62.5q-29 23 -66 18.5t-60 -33.5l-445 -561q-19 -23 -19 -55zM901 1839q0 -39 28 -63l65 -70q25 -25 59 -28q33 -5 65.5 24t32.5 68q0 36 -26 64l-69 69q-24 26 -63 26q-38 0 -65 -26t-27 -64zM1215 -447q0 -19 7 -33q14 -35 48 -49 q17 -8 36 -8q10 0 32 6q36 14 49 49q16 35 3 70.5t-47 48.5q-35 16 -71.5 2t-49.5 -48q-7 -18 -7 -38zM1472 1368q113 103 264 103q158 0 270 -109.5t112 -265.5q0 -106 -55 -198q-155 155 -372 155h-35q-44 184 -184 315zM1645 1929q0 -37 26.5 -62.5t64.5 -25.5 q39 0 66 25.5t27 62.5v221q0 37 -27.5 64t-65.5 27t-64.5 -26.5t-26.5 -64.5v-221zM2240 1686q0 -38 24 -64q28 -27 65.5 -27.5t60.5 27.5l159 154q26 26 26 65q0 37 -27.5 65t-65.5 28q-35 0 -64 -27l-154 -157q-24 -26 -24 -64zM2322 416q0 -37 27 -64l69 -67 q26 -26 59 -29h5q29 0 67 29q26 26 26 62q0 38 -26 67l-70 66q-25 27 -63 27q-40 0 -67 -26.5t-27 -64.5zM2482 1096q0 -37 27 -64q26 -29 64 -29h218q37 0 64 27.5t27 65.5q0 37 -26.5 62.5t-64.5 25.5h-218q-38 0 -64.5 -25.5t-26.5 -62.5z" />
|
||||
<glyph unicode="" d="M5 529q0 -213 148.5 -366.5t363.5 -163.5q18 0 18 18v143q0 18 -18 18q-137 7 -234 109.5t-97 241.5q0 132 91 230t225 114l56 5q20 0 20 18l7 58q17 173 147 290t305 117q174 0 304.5 -117t148.5 -290l8 -61q0 -20 19 -20h172q142 0 246 -102t104 -242 q0 -139 -96 -241.5t-233 -109.5q-21 0 -21 -18v-143q0 -18 21 -18q213 7 361 161.5t148 368.5q0 117 -44 214q124 154 124 353q0 152 -75.5 281.5t-204.5 204.5t-280 75q-248 0 -413 -186q-131 70 -289 70q-226 0 -401.5 -140.5t-224.5 -358.5q-177 -42 -291.5 -185.5 t-114.5 -327.5zM581 -202q0 -27 17 -52.5t52 -35.5q18 -4 30 -4q62 0 81 68l169 627q11 38 -8.5 71t-56.5 40q-35 11 -67.5 -7t-43.5 -54l-169 -630q-4 -16 -4 -23zM858 -531q0 -30 16 -55.5t55 -31.5q18 -4 29 -4q25 0 47.5 18.5t28.5 51.5l256 953q10 37 -7 68.5t-53 42.5 q-37 11 -69.5 -7t-42.5 -54l-256 -953q0 -3 -2 -13.5t-2 -15.5zM904 1840q0 -39 26 -65l70 -70q32 -24 65 -24q34 0 62 25t28 63q0 39 -26 68l-67 69q-29 27 -64 27q-40 0 -67 -27.5t-27 -65.5zM1302 -205q0 -26 16.5 -50.5t49.5 -34.5q18 -4 26 -4q29 0 53.5 15.5 t31.5 52.5l169 627q10 37 -8 68.5t-54 42.5q-38 11 -69.5 -7t-41.5 -54l-169 -630q-4 -18 -4 -26zM1475 1368q113 107 264 107q157 0 267.5 -110.5t110.5 -268.5q0 -100 -55 -197q-155 155 -373 155h-34q-45 185 -180 314zM1648 1928q0 -37 26.5 -62.5t64.5 -25.5 q37 0 62.5 25t25.5 63v219q0 41 -25 67t-63 26q-40 0 -65.5 -26.5t-25.5 -66.5v-219zM2238 1685q0 -39 28 -63q22 -25 59 -27.5t67 27.5l153 153q29 27 29 67q0 39 -27 65t-66 26q-33 0 -62 -27l-153 -154q-28 -27 -28 -67zM2325 421q0 -38 26 -67l68 -67q28 -22 64 -22 l3 -2q36 0 62 26t26 65q0 38 -29 62l-65 70q-29 26 -66 26t-63 -26.5t-26 -64.5zM2480 1096q0 -39 27 -63q28 -28 67 -28h217q37 0 64 26.5t27 64.5t-27 65.5t-64 27.5h-217q-38 0 -66 -27.5t-28 -65.5z" />
|
||||
<glyph unicode="" d="M1 530q0 -213 148.5 -365.5t362.5 -163.5q18 0 18 18v143q0 19 -18 19q-136 7 -231 108.5t-95 240.5q0 133 90 230.5t225 113.5l52 4q21 0 21 19l7 57q17 173 146.5 289.5t304.5 116.5t305 -116.5t146 -289.5l8 -61q0 -19 19 -19h178q140 0 242 -102t102 -242 q0 -136 -95.5 -237t-230.5 -112q-18 0 -18 -19v-143q0 -18 18 -18q140 4 256 76.5t182.5 192t66.5 260.5q0 114 -44 209q121 150 121 350q0 114 -44 217.5t-119 178.5t-178.5 119t-217.5 44q-241 0 -406 -177q-127 68 -291 68q-226 0 -399 -139.5t-220 -357.5 q-180 -41 -296 -184t-116 -328zM579 -184q0 -29 17 -56.5t49 -38.5q31 -11 65.5 6.5t46.5 58.5l26 112q11 33 -7.5 66.5t-54.5 44.5q-40 10 -72 -9t-39 -57l-29 -106q-2 -14 -2 -21zM718 319q0 -61 68 -84q35 -12 68 5.5t44 56.5l27 110q11 33 -7.5 66t-54.5 45 q-39 10 -71 -8.5t-39 -55.5l-32 -109q-3 -14 -3 -26zM848 -522q0 -26 18 -53t52 -38q1 0 10.5 -1.5t15.5 -1.5q22 0 39 8q30 13 44 62l29 106q11 37 -7.5 69.5t-54.5 42.5q-37 11 -69.5 -7.5t-42.5 -54.5l-29 -109q-5 -20 -5 -23zM897 1832q0 -40 28 -66l69 -69 q54 -54 125 0q26 28 26 65t-26 65l-66 70q-26 26 -64 26t-65 -27t-27 -64zM992 -11q0 -27 17 -54t50 -37q37 -11 68 5t43 60l27 108q11 37 -7 69.5t-54 43.5q-40 11 -72.5 -8.5t-39.5 -56.5l-30 -110q-2 -6 -2 -20zM1289 -195q4 -59 68 -88l26 -4q26 0 51 17t35 53l30 108 q10 38 -9.5 70.5t-56.5 39.5q-33 11 -66 -7.5t-44 -54.5l-29 -109zM1438 313q0 -26 16.5 -51.5t48.5 -34.5l27 -3q31 0 55 19t30 50l30 106q11 37 -7.5 69t-54.5 42q-37 11 -68.5 -6.5t-41.5 -53.5l-32 -113q-3 -13 -3 -24zM1473 1365q106 102 256 102q157 0 268.5 -110.5 t111.5 -267.5q0 -88 -51 -188q-153 153 -370 153h-35q-43 180 -180 311zM1638 1923q0 -40 25.5 -65.5t65.5 -25.5t66 25.5t26 65.5v215q0 40 -26 66t-66 26t-65.5 -26t-25.5 -66v-215zM2230 1678q0 -40 24 -65q33 -27 66 -27q30 0 63 27l153 153q26 29 26 67t-26 64t-63 26 t-65 -26l-154 -153q-24 -28 -24 -66zM2314 416q0 -37 28 -67l66 -68q33 -33 66 -33q30 0 62 33q29 29 28 65.5t-28 63.5l-69 70q-26 26 -61 26q-38 0 -65 -26.5t-27 -63.5zM2471 1089q0 -36 26 -62t62 -26h218q41 0 67 25t26 63q0 40 -26.5 66t-66.5 26h-218 q-37 0 -62.5 -27t-25.5 -65z" />
|
||||
<glyph unicode="" d="M1 525q0 -138 68 -257t185.5 -191t256.5 -76q18 0 18 18v142q0 20 -18 20q-136 7 -232.5 108.5t-96.5 235.5q0 132 90.5 230t223.5 114l56 6q19 0 19 20l8 54q17 175 146 291.5t304 116.5q174 0 304 -116.5t147 -288.5l8 -62q0 -18 18 -18h172q144 0 246 -102.5 t102 -244.5q0 -134 -96.5 -235.5t-231.5 -108.5q-20 0 -20 -20v-142q0 -18 20 -18q212 7 360 160t148 364q0 121 -46 217q126 151 126 352q0 150 -75 278.5t-204 203.5t-279 75q-246 0 -413 -185q-130 70 -286 70q-225 0 -399.5 -139.5t-223.5 -356.5q-179 -44 -292 -187 t-113 -328zM678 93q0 -38 25.5 -65t62.5 -27t62.5 27t25.5 65q0 36 -25.5 62t-62.5 26t-62.5 -26t-25.5 -62zM678 -294q0 -33 26 -61q28 -26 62 -26q38 0 63 25t25 62q0 38 -25 63t-63 25t-63 -25t-25 -63zM896 1832q0 -39 26 -63l70 -70q26 -26 57 -27q33 -5 65 23.5 t32 67.5t-26 68l-67 65q-24 27 -63 27q-40 0 -67 -26.5t-27 -64.5zM1017 -112q0 -36 26 -64q26 -26 62 -26q38 0 65 26t27 64q0 37 -27 63.5t-65 26.5q-36 0 -62 -26.5t-26 -63.5zM1017 272q0 -36 26 -62t62 -26q38 0 65 25.5t27 62.5t-27 63t-65 26q-36 0 -62 -26t-26 -63z M1017 -502q0 -35 26 -61q27 -27 62 -27q38 0 65 25t27 63q0 37 -27 64t-65 27q-35 0 -61.5 -27t-26.5 -64zM1361 93q0 -38 26 -65t63 -27t62.5 27t25.5 65q0 36 -25.5 62t-62.5 26t-63 -26t-26 -62zM1361 -294q0 -34 25 -61q28 -26 64 -26q38 0 63 25t25 62q0 38 -25 63 t-63 25t-63.5 -25.5t-25.5 -62.5zM1465 1365q110 106 263 106q155 0 265 -111t110 -266q0 -100 -54 -196q-153 153 -371 153h-33q-47 186 -180 314zM1637 1921q0 -37 26.5 -62t64.5 -25t63 25t25 62v218q0 38 -25.5 65t-62.5 27t-64 -27t-27 -65v-218zM2224 1679 q0 -37 27 -63q24 -25 59.5 -27.5t66.5 27.5l153 153q28 28 28 65q0 38 -27 65t-65 27q-34 0 -61 -27l-154 -156q-27 -25 -27 -64zM2310 421q0 -40 26 -66l69 -67q39 -26 67 -26q33 0 59.5 27.5t26.5 65.5q0 36 -28 62l-66 69q-28 26 -65 26t-63 -26.5t-26 -64.5zM2464 1094 q0 -37 29 -63q27 -29 65 -29h216q37 0 64 27t27 65t-26.5 64.5t-64.5 26.5h-216q-38 0 -66 -27t-28 -64z" />
|
||||
<glyph unicode="" d="M-2 529q0 -139 67.5 -258.5t184.5 -191.5t256 -78q20 0 20 18v142q0 20 -20 20q-136 7 -232 108.5t-96 239.5q0 132 90 229.5t223 113.5l56 3q21 0 21 19l8 58q17 173 145.5 289t303.5 116q172 0 302.5 -116.5t149.5 -288.5l6 -62q0 -18 19 -18h172q141 0 245 -102 t104 -241q0 -138 -96 -239.5t-233 -108.5q-20 0 -20 -20v-142q0 -18 20 -18q139 4 256 76.5t184 192.5t67 259q0 118 -44 213q124 152 124 352q0 151 -75 279.5t-203 203t-278 74.5q-248 0 -413 -185q-131 70 -287 70q-225 0 -399.5 -139.5t-223.5 -356.5 q-176 -42 -290 -185t-114 -326zM627 435q0 -61 45.5 -104t108.5 -43q64 0 106.5 42.5t42.5 104.5q0 40 -37 107.5t-69 105.5q-34 36 -43 45l-38 -43q-43 -46 -79.5 -110t-36.5 -105zM893 1834q0 -41 26 -65l68 -70q68 -51 130 0q26 31 26 68t-26 63l-68 69q-30 27 -65 27 q-39 0 -65 -27t-26 -65zM945 13q0 -105 72.5 -176.5t175.5 -71.5q104 0 176.5 73t72.5 175q0 87 -85 210q-74 97 -136 159q-13 9 -28 24l-25 -24q-57 -52 -136 -157q-87 -121 -87 -212zM1102 719q0 -40 30.5 -69t73.5 -29q41 0 70 29t29 69q0 66 -99 171l-27 -27 q-29 -31 -53 -74t-24 -70zM1462 1365q106 106 263 106q156 0 265 -110t109 -267q0 -96 -54 -196q-154 153 -371 153h-34q-46 186 -178 314zM1633 1926q0 -38 27.5 -65t64.5 -27t61.5 27t24.5 65v217q0 38 -24.5 63t-61.5 25q-38 0 -65 -25t-27 -63v-217zM2221 1679 q0 -39 27 -63q28 -26 59 -26q29 0 66 26l153 153q28 28 28 66q0 39 -26.5 65t-64.5 26q-35 0 -62 -27l-153 -153q-27 -27 -27 -67zM2306 423q0 -40 25 -68l70 -62q23 -29 63 -29q39 0 62 29q28 25 28 62q0 36 -28 62l-65 69q-28 26 -66 26q-37 0 -63 -26t-26 -63zM2461 1094 q0 -36 27 -62q28 -26 66 -26h216q38 0 65 25.5t27 62.5t-27 64t-65 27h-216q-38 0 -65.5 -27t-27.5 -64z" />
|
||||
<glyph unicode="" horiz-adv-x="1971" d="M-282 899q0 -43 30 -71t77 -28h180q43 0 73.5 28.5t30.5 70.5q0 46 -30 76t-74 30h-180q-47 0 -77 -29.5t-30 -76.5zM-94 239q0 -155 113.5 -268.5t268.5 -113.5h731q155 0 265.5 112t110.5 270q0 79 -21 131q133 85 210.5 226.5t77.5 302.5q0 130 -51 248.5t-136.5 204 t-204 136.5t-247.5 51q-175 0 -322.5 -86t-232 -233t-84.5 -321v-36q-160 -91 -211 -266q-121 -38 -194 -135t-73 -223zM73 1750q0 -45 28 -72l172 -180q75 -57 150 0q30 30 30 75q0 43 -30 75l-176 175q-34 31 -74 31q-45 0 -72.5 -30t-27.5 -74zM118 239q0 67 43 115 t109 54l66 9q21 0 21 24l10 60q11 92 77.5 154t157.5 62q93 0 161.5 -62t79.5 -154l10 -69q10 -24 26 -24h140q69 0 120 -50t51 -119q0 -71 -51 -122.5t-120 -51.5h-731q-72 0 -121 51t-49 123zM590 921q11 175 135 294t298 119q178 0 302.5 -127t124.5 -308 q0 -112 -54 -207.5t-147 -154.5q-94 78 -217 78q-47 140 -164.5 223t-265.5 83h-12zM919 1854q0 -44 30.5 -74.5t73.5 -30.5q44 0 74.5 30.5t30.5 74.5v250q0 42 -30.5 70.5t-74.5 28.5q-43 0 -73.5 -28.5t-30.5 -70.5v-250zM1593 230q0 -43 29 -75l175 -173q66 -68 149 0 q29 27 29 72q0 42 -29 71l-179 179q-29 27 -73 27t-72.5 -29t-28.5 -72zM1593 1575q0 -45 29 -77q29 -29 72 -29q44 0 73 29l179 180q29 27 29 72q0 44 -30.5 74t-74.5 30q-42 0 -73 -31l-175 -175q-29 -29 -29 -73zM1869 899q0 -43 30 -71t77 -28h179q43 0 73.5 28.5 t30.5 70.5q0 46 -30 76t-74 30h-179q-47 0 -77 -29.5t-30 -76.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1828" d="M-248 771q0 39 27 66q28 26 64 26h218q37 0 61.5 -27t24.5 -65t-24.5 -64.5t-61.5 -26.5h-218q-37 0 -64 27t-27 64zM57 32q0 37 25 65l157 152q24 25 63 25q38 0 63.5 -24t25.5 -61q0 -39 -26 -68l-152 -152q-65 -51 -131 0q-25 27 -25 63zM57 1509q0 37 25 65 q31 26 68 26q35 0 63 -26l152 -157q26 -24 26 -63q0 -38 -25.5 -63.5t-63.5 -25.5q-39 0 -63 26l-157 152q-25 27 -25 66zM329 771q0 149 75 277.5t203.5 203.5t277.5 75q112 0 215 -44.5t177.5 -119t118.5 -177.5t44 -215q0 -150 -74.5 -278t-202.5 -202.5t-278 -74.5 t-278 74.5t-203 202.5t-75 278zM510 771q0 -156 109.5 -266.5t265.5 -110.5t266.5 110.5t110.5 266.5q0 154 -110.5 263t-266.5 109q-155 0 -265 -109t-110 -263zM794 -58q0 38 26.5 64t64.5 26q39 0 65 -26t26 -64v-212q0 -39 -26.5 -66t-64.5 -27t-64.5 27t-26.5 66v212z M794 1595v218q0 37 27 64t64 27t64 -27t27 -64v-218q0 -37 -26.5 -61.5t-64.5 -24.5t-64.5 24.5t-26.5 61.5zM1383 189q0 37 24 60q24 25 60 25q39 0 64 -25l156 -152q26 -28 26 -65t-26 -63q-64 -50 -128 0l-152 152q-24 27 -24 68zM1383 1354q0 40 24 63l152 157 q28 26 63 26q38 0 64.5 -27t26.5 -64q0 -40 -26 -66l-156 -152q-29 -26 -64 -26q-36 0 -60 25.5t-24 63.5zM1624 771q0 38 26 66q26 26 61 26h216q37 0 64.5 -27.5t27.5 -64.5t-27.5 -64t-64.5 -27h-216q-37 0 -62 26.5t-25 64.5z" />
|
||||
<glyph unicode="" d="M-1 530q0 -179 106.5 -320t275.5 -188l-70 -181q-8 -23 15 -23h227l-152 -449h31l465 604q6 7 1.5 14.5t-15.5 7.5h-233l265 496q12 23 -14 23h-316q-15 0 -25 -16l-114 -307q-115 29 -190 123.5t-75 215.5q0 135 90 233t225 114l54 3q19 0 24 15l8 63 q17 173 146.5 290.5t305.5 117.5q174 0 305.5 -117.5t149.5 -290.5l8 -63q0 -18 18 -18h173q143 0 246 -102.5t103 -244.5q0 -139 -95.5 -241.5t-232.5 -109.5q-21 0 -21 -19v-143q0 -18 21 -18q141 4 258.5 76.5t185.5 193t68 261.5q0 117 -46 215q122 154 122 351 q0 152 -75.5 281.5t-205 205t-281.5 75.5q-115 0 -224 -48t-188 -133q-129 69 -289 69q-227 0 -402.5 -140.5t-224.5 -359.5q-177 -41 -292.5 -186t-115.5 -330zM872 -528q0 -24 17 -50t49 -36q18 -4 27 -4q24 0 41 8q33 13 45 61l30 110q10 40 -9 71.5t-57 39.5 q-33 11 -66 -8t-45 -55l-27 -111q-5 -22 -5 -26zM897 1841q0 -38 26 -64l70 -70q25 -25 58 -28q33 -5 65.5 24t32.5 68q0 37 -27 64l-67 70q-26 26 -64 26q-40 0 -67 -26t-27 -64zM1015 -16q-1 -26 15 -50.5t51 -34.5q34 -11 66.5 6.5t44.5 59.5l32 110q11 35 -8.5 67.5 t-57.5 43.5q-36 11 -68.5 -7.5t-43.5 -56.5l-27 -112q-4 -18 -4 -26zM1317 -195q0 -23 11 -42q21 -34 58 -46q18 -6 30 -6q20 0 33 8q32 12 48 64l26 108q11 38 -6.5 69.5t-53.5 41.5q-37 11 -69.5 -7t-43.5 -54l-30 -111q-3 -14 -3 -25zM1463 319q0 -28 17.5 -53.5 t52.5 -35.5q27 -9 65 5q31 13 45 61l30 107q10 40 -8.5 71.5t-56.5 39.5q-36 11 -68 -6.5t-43 -53.5l-31 -113q-3 -21 -3 -22zM1474 1369q107 103 261 103q157 0 267.5 -109.5t110.5 -266.5q0 -105 -50 -193q-154 154 -375 154h-33q-47 190 -181 312zM1643 1931 q0 -38 27 -65t65 -27t63.5 26.5t25.5 65.5v220q0 39 -25.5 65.5t-63.5 26.5t-65 -27t-27 -65v-220zM2239 1687q0 -39 25 -64q22 -25 58.5 -27.5t67.5 27.5l154 154q27 25 27 64t-27 64q-26 28 -64 28q-37 0 -62 -28l-154 -154q-25 -25 -25 -64zM2322 416q0 -37 27 -64 l69 -67q29 -27 65 -27l3 -2q35 0 58 29q27 26 27 64t-27 65l-66 66q-29 29 -65 29q-38 0 -64.5 -27.5t-26.5 -65.5zM2478 1096q0 -39 28 -63q25 -28 64 -28h220q38 0 65 26.5t27 64.5t-26.5 63.5t-65.5 25.5h-220q-39 0 -65.5 -25.5t-26.5 -63.5z" />
|
||||
<glyph unicode="" d="M-1 528q0 -177 105 -316t274 -190l-68 -181q-7 -22 15 -22h226l-133 -421h31l445 575q6 7 1.5 14.5t-15.5 7.5h-232l264 494q11 23 -15 23h-314q-15 0 -25 -15l-115 -307q-114 29 -189 124t-75 214q0 133 91 231t225 114l56 7q20 0 20 19l7 54q17 173 147 290t305 117 q173 0 303.5 -117t149.5 -290l7 -62q0 -18 19 -18h172q145 0 247.5 -101t102.5 -244q0 -136 -95 -235.5t-234 -110.5q-21 0 -21 -19v-146q0 -18 21 -18q213 7 361 161t148 368q0 120 -45 214q125 151 125 354q0 150 -75.5 279t-204.5 204.5t-280 75.5q-246 0 -413 -185 q-129 69 -288 69q-226 0 -401 -140t-224 -358q-177 -41 -291.5 -184.5t-114.5 -328.5zM862 -494q-2 -24 14.5 -47t49.5 -35q10 -3 21 -3q27 0 54.5 16t36.5 50l244 914q10 37 -7 68.5t-52 42.5q-37 11 -69.5 -7t-42.5 -54l-246 -917q-3 -13 -3 -28zM897 1837q0 -39 26 -63 l69 -70q27 -27 65 -27.5t61 27.5q29 23 29 64q0 39 -26 63l-67 70q-28 26 -65 26q-40 0 -66 -26t-26 -64zM1305 -168q0 -19 12 -40q20 -32 53 -46q11 -5 32 -5q24 0 35 6q32 13 44 62l159 592q10 38 -8 69t-54 42q-37 11 -69 -7t-42 -54l-158 -595q0 -4 -2 -12t-2 -12z M1467 1367q107 103 264 103q156 0 267 -109t111 -265q0 -100 -55 -197q-159 157 -373 157h-33q-49 186 -181 311zM1640 1927q0 -38 27 -65t64 -27q38 0 63 27t25 65v219q0 38 -25 65t-63 27q-37 0 -64 -27t-27 -65v-219zM2230 1683q0 -39 28 -64q23 -27 60.5 -27t64.5 27 l154 155q28 25 28 63t-27 65t-65 27q-36 0 -62 -28l-153 -154q-28 -25 -28 -64zM2315 418q0 -35 27 -64l69 -67q20 -23 59 -26l2 -1q1 0 4 -0.5t5 -0.5q30 0 56 28q28 26 28 64q0 37 -28 65l-70 65q-26 29 -61 29q-37 0 -64 -27t-27 -65zM2471 1096q0 -38 28 -64 q29 -29 66 -29h217q37 0 64.5 27.5t27.5 65.5q0 37 -27 62.5t-65 25.5h-217q-40 0 -67 -25.5t-27 -62.5z" />
|
||||
<glyph unicode="" horiz-adv-x="2194" d="M-231 90q0 39 28 67q28 26 64 26h577q118 0 201.5 -84.5t83.5 -202.5q0 -119 -83 -202.5t-202 -83.5t-203 84q-25 25 -25 64t24.5 64.5t64.5 25.5q38 0 66 -26q32 -31 73 -31q42 0 73 30.5t31 74.5q0 42 -31 72.5t-73 30.5h-577q-38 0 -65 27t-27 64zM-231 414 q0 38 28 66q28 26 64 26h1124q42 0 73 31t31 74t-31 73.5t-73 30.5q-44 0 -73 -30q-28 -24 -67 -24t-63.5 24.5t-24.5 63.5q0 40 24 65q81 81 204 81q119 0 202.5 -83t83.5 -201t-83.5 -201.5t-202.5 -83.5h-1124q-38 0 -65 25.5t-27 62.5zM-2 666q0 -13 17 -13h154 q13 0 23 16q36 87 112 144t170 64l56 8q21 0 21 18l8 55q17 173 146.5 290.5t305.5 117.5q177 0 307 -116.5t148 -291.5l8 -63q0 -18 19 -18h172q145 0 248.5 -102t103.5 -245t-103.5 -245t-248.5 -102h-737q-19 0 -19 -18v-148q0 -18 19 -18h737q145 0 267.5 71t194 193.5 t71.5 266.5q0 145 -71.5 267t-194 193t-267.5 71h-33q-50 213 -223.5 349t-397.5 136q-226 0 -400 -140.5t-221 -359.5q-137 -32 -243 -131.5t-147 -235.5v-4q-2 -5 -2 -9z" />
|
||||
<glyph unicode="" horiz-adv-x="2194" d="M-352 87q0 39 27 65t67 26h957q38 0 63 -25.5t25 -65.5q0 -38 -25.5 -63.5t-62.5 -25.5h-957q-40 0 -67 25.5t-27 63.5zM-71 413q0 38 29 64q23 24 63 24h960q37 0 61.5 -25t24.5 -63t-24.5 -65t-61.5 -27h-960q-37 0 -64.5 27.5t-27.5 64.5zM-5 662q0 -15 18 -15h152 q15 0 25 15q36 87 111 144t168 64l58 7q18 0 18 19l7 55q18 174 148.5 290t306.5 116q174 0 302 -114.5t150 -288.5l8 -63q0 -17 19 -17h171q145 0 247.5 -102t102.5 -247q0 -140 -104 -243.5t-246 -103.5h-735q-19 0 -19 -18v-143q0 -19 19 -19h735q108 0 206.5 41.5 t169 112.5t112.5 168.5t42 204.5q0 144 -70.5 265.5t-192.5 192t-267 70.5h-33q-52 215 -224.5 351.5t-392.5 136.5q-227 0 -401.5 -141.5t-221.5 -361.5q-138 -31 -242 -129.5t-145 -236.5v-2q-2 -4 -2 -8zM117 -243q0 39 28 64q24 25 63 25h959q39 0 65.5 -25.5 t26.5 -63.5t-27 -65t-65 -27h-959q-37 0 -64 27t-27 65z" />
|
||||
<glyph unicode="" horiz-adv-x="2377" d="M3 454q0 159 99.5 282.5t254.5 158.5q41 188 189.5 307.5t342.5 119.5q189 0 337.5 -116.5t192.5 -298.5h29q188 0 321.5 -132.5t133.5 -320.5q0 -123 -61 -228t-166 -166t-228 -61h-990q-92 0 -176.5 36t-145.5 97t-97 145.5t-36 176.5zM159 454q0 -122 87.5 -209.5 t211.5 -87.5h990q124 0 212.5 87.5t88.5 209.5t-88.5 209t-212.5 87h-147q-16 0 -16 16l-7 52q-16 151 -127 250.5t-262 99.5t-262 -100t-125 -250l-7 -45q0 -16 -17 -16l-48 -7q-115 -10 -193 -95t-78 -201zM1103 1384q-16 -15 8 -22q66 -29 114 -59q18 -5 24 3 q97 92 226 92q130 0 224.5 -86.5t105.5 -213.5l10 -68h151q104 0 178.5 -74.5t74.5 -177.5q0 -96 -65.5 -167t-162.5 -82q-16 0 -16 -17v-121q0 -17 16 -17q161 10 272 127t111 277q0 169 -119.5 288.5t-288.5 119.5h-16q-42 160 -175.5 263.5t-299.5 103.5 q-225 0 -372 -169z" />
|
||||
<glyph unicode="" horiz-adv-x="2171" d="M-294 86q0 39 27 65t67 26h1991q40 0 65.5 -25.5t25.5 -65.5q0 -37 -26.5 -62t-64.5 -25h-1991q-40 0 -67 25t-27 62zM-15 421q0 38 29 64q24 24 63 24h1992q37 0 61.5 -25t24.5 -63t-25 -64.5t-61 -26.5h-1992q-38 0 -65 27t-27 64zM-1 675q0 -14 16 -14h153q11 0 22 17 q38 83 113.5 136t165.5 60l58 8q18 0 18 19l7 53q17 173 146.5 288.5t303.5 115.5q173 0 301.5 -114t146.5 -286l8 -61q0 -18 21 -18h170q103 0 187.5 -55t125.5 -146q11 -17 22 -17h153q19 0 15 24q-47 164 -186 268t-317 104h-34q-53 213 -223.5 348.5t-389.5 135.5 q-224 0 -397.5 -140.5t-222.5 -358.5q-136 -32 -239 -129t-143 -232v4q-1 -3 -1 -10zM171 -241q0 38 28 63q24 24 64 24h1993q38 0 64.5 -25t26.5 -62q0 -38 -27 -65t-64 -27h-1993q-37 0 -64.5 27.5t-27.5 64.5z" />
|
||||
<glyph unicode="" horiz-adv-x="2211" d="M2 528q0 -213 148.5 -366t362.5 -163q18 0 18 18v143q0 18 -18 18q-137 7 -233 109t-96 241q0 132 90 230t223 114l57 3q21 0 21 20l7 57q17 173 146 289.5t304 116.5q174 0 304.5 -116.5t148.5 -289.5l8 -62q0 -18 18 -18h172q142 0 246 -102t104 -242 q0 -139 -96.5 -241t-232.5 -109q-21 0 -21 -18v-143q0 -18 21 -18q104 3 198.5 46.5t162.5 114t108 167t40 201.5q0 143 -71.5 263.5t-193.5 190t-265 69.5h-34q-52 214 -224.5 350t-392.5 136q-225 0 -400 -139.5t-224 -357.5q-177 -41 -291.5 -184.5t-114.5 -327.5z M583 -240q15 -34 49 -49q32 -16 66.5 -2t47.5 48q16 35 3 68.5t-48 46.5q-32 17 -66 3t-50 -50q-15 -26 -2 -65zM650 61q0 -25 17 -48.5t50 -34.5q31 -18 66 -1.5t47 63.5l96 361q10 40 -8.5 71.5t-56.5 38.5q-13 3 -27 3q-25 0 -49.5 -16t-34.5 -47l-95 -362q-5 -22 -5 -28 zM849 -535q0 -19 6 -31q14 -35 48 -48q15 -8 35 -8q10 0 32 6q36 13 51.5 48t-0.5 70t-48 49t-67 0q-30 -13 -43.5 -38t-13.5 -48zM927 -257q0 -59 67 -78q18 -4 27 -4q24 0 41 8q32 12 44 61l179 671q10 38 -7 69t-53 41q-14 3 -28 3q-27 0 -52.5 -16t-31.5 -47l-182 -676 q-4 -22 -4 -32zM1294 -208q0 -19 8 -34q13 -31 47 -47q18 -7 36 -7q10 0 32 6q34 13 49 47q12 35 -0.5 67t-44.5 48q-36 17 -69.5 3t-49.5 -50q-8 -15 -8 -33zM1370 66q0 -23 17 -48t48 -35q25 -5 29 -5q15 0 39 11q32 15 43 56l96 356q0 3 2 13t2 15q0 25 -16.5 49 t-48.5 33q-14 3 -29 3q-26 0 -49.5 -15.5t-32.5 -47.5l-96 -358q0 -3 -2 -12t-2 -15z" />
|
||||
<glyph unicode="" horiz-adv-x="1371" d="M-62 -283h43l733 1086q15 29 -16 29h-303l319 581q14 29 -20 29h-407q-17 0 -31 -19l-296 -789q-4 -29 21 -29h292zM846 392h28l557 818q8 13 4 21t-18 8h-224l233 430q19 32 -19 32h-292q-21 0 -32 -20l-221 -585q-10 -30 20 -30h219z" />
|
||||
<glyph unicode="" horiz-adv-x="2211" d="M-5 522q0 -211 149 -364t361 -160q20 0 20 18v142q0 19 -20 19q-135 7 -232 108.5t-97 236.5q0 134 90.5 233.5t223.5 110.5l56 8q21 0 21 19l8 53q16 175 145 293t305 118q173 0 303.5 -117t149.5 -289l7 -62q0 -19 19 -19h171q144 0 247 -103t103 -245 q0 -135 -96.5 -236.5t-232.5 -108.5q-21 0 -21 -19v-142q0 -18 21 -18q213 7 361 159.5t148 364.5q0 143 -71.5 264.5t-193.5 192t-265 70.5h-33q-53 214 -225 350.5t-392 136.5q-225 0 -400 -140t-224 -357q-180 -46 -293 -188.5t-113 -328.5zM573 -227q0 -27 17 -54 t50 -37q37 -11 68.5 4t42.5 60l15 65q8 36 -10 67.5t-55 42.5q-36 11 -68.5 -8t-42.5 -57l-15 -63q-2 -6 -2 -20zM654 82q0 -35 26 -61q25 -27 61 -27q38 0 64 25.5t26 62.5t-26 62.5t-64 25.5q-37 0 -62 -25t-25 -63zM719 325q-2 -25 14 -48.5t51 -34.5q33 -10 66.5 7.5 t44.5 54.5l30 96q12 39 -7.5 69.5t-58.5 41.5q-35 11 -67 -7t-43 -53l-27 -98q-3 -27 -3 -28zM842 -560q0 -27 17 -52.5t52 -35.5q14 -3 26 -3q69 0 85 65l15 63q10 40 -9 72.5t-57 39.5q-34 11 -66.5 -7.5t-43.5 -54.5l-15 -63q-4 -18 -4 -24zM926 -250q0 -36 26 -62 t62 -26q38 0 63 25t25 63q0 37 -25 62t-63 25t-63 -25t-25 -62zM992 -8q-2 -24 14.5 -50t48.5 -32q37 -10 68 6t44 60l28 96q11 35 -7.5 67t-56.5 43q-35 11 -68 -8.5t-44 -56.5l-24 -96q-3 -14 -3 -29zM1287 -237q0 -27 16 -51.5t49 -34.5q5 0 15 -2t15 -2q65 0 81 70 l15 64q11 34 -7.5 67t-54.5 44q-39 10 -71.5 -8.5t-43.5 -56.5l-11 -63q0 -2 -1.5 -12t-1.5 -15zM1366 78q0 -37 26 -61q24 -26 62 -26t63 25t25 62q0 38 -25 63t-63 25t-63 -25t-25 -63zM1431 325q0 -27 17.5 -51.5t52.5 -34.5q3 0 12.5 -2t14.5 -2q17 0 39 10q33 17 43 56 l27 96q10 36 -8 67.5t-54 42.5q-37 11 -68.5 -6t-42.5 -54l-30 -97q0 -3 -1.5 -11.5t-1.5 -13.5z" />
|
||||
<glyph unicode="" horiz-adv-x="2217" d="M2 525q0 -179 104.5 -318.5t272.5 -190.5q14 -3 27 8l123 153q-142 0 -243 102t-101 246q0 135 90 234.5t224 110.5l56 7q18 0 18 14l8 59q20 176 148 291.5t303 115.5q176 0 307 -116.5t147 -290.5l8 -62q4 -18 23 -18h172q144 0 245 -101t101 -244q0 -133 -89.5 -232.5 t-220.5 -115.5q-20 0 -53 -6q-30 -3 -43 -24l-253 -298q-23 -29 -17.5 -67t33.5 -61q17 -20 61 -20q42 0 65 37l225 260q199 20 336 172t137 355q0 108 -41.5 206t-112 168.5t-168 112.5t-205.5 42h-34q-53 212 -226.5 347.5t-396.5 135.5q-149 0 -280.5 -63.5t-221.5 -177 t-121 -258.5q-180 -40 -293.5 -183.5t-113.5 -329.5zM363 -330q0 -10 6 -32q14 -34 47 -47q35 -16 71 -2t49 48q16 35 2 69t-49 46q-36 17 -69 3t-49 -50q-8 -17 -8 -35zM560 -70v-11q3 -36 32 -60q36 -20 66 -20q36 0 65 35l248 295q23 28 21 70q-4 34 -29.5 57t-55.5 23 q-8 0 -14 -1q-38 -8 -61 -35l-251 -296q-21 -24 -21 -57zM642 -573q0 -19 8 -34q13 -34 47 -50q17 -8 36 -8q20 0 33 8q36 14 49 44q16 35 2.5 71t-47.5 49q-35 16 -71.5 2t-48.5 -48q-8 -15 -8 -34zM821 -323v-12q4 -35 32.5 -58.5t58.5 -23.5q36 0 67 35l464 559 q19 22 19 55v13q-4 36 -29.5 57t-56.5 21h-14q-37 -2 -59 -33l-464 -555q-18 -26 -18 -58zM1195 -446q0 -14 6 -32q14 -35 49 -50q22 -6 33 -6q23 0 36 8q36 14 49 45q16 27 2 71q-13 33 -47 47l-37 10q-17 -3 -35 -8q-35 -13 -48 -47q-8 -19 -8 -38z" />
|
||||
<glyph unicode="" horiz-adv-x="2217" d="M2 527q0 184 115 327.5t291 184.5q50 218 225 358t400 140q220 0 392.5 -136.5t224.5 -350.5h35q143 0 265 -69.5t193 -190t71 -263.5q0 -105 -40 -201.5t-108 -167t-162.5 -114t-198.5 -46.5q-20 0 -20 18v142q0 19 20 19q136 7 232.5 109t96.5 241q0 140 -103.5 242 t-245.5 102h-173q-18 0 -18 18l-8 62q-18 173 -149 289.5t-304 116.5q-175 0 -304.5 -116.5t-145.5 -289.5l-8 -57q0 -20 -21 -20l-56 -3q-133 -16 -223.5 -114t-90.5 -230q0 -139 96 -241t233 -109q18 0 18 -19v-142q0 -18 -18 -18q-214 10 -362.5 163t-148.5 366z M574 -186q0 10 3 21l174 615q10 31 34.5 47t49.5 16q12 0 27 -3q38 -7 57 -39t8 -71l-174 -611q-20 -68 -88 -68q-5 0 -11 2q-9 3 -12 3q-34 10 -51 35.5t-17 52.5zM854 -486l259 936q7 31 32 47t53 16q13 0 28 -3q36 -10 53 -41.5t7 -68.5l-260 -937q-6 -27 -30.5 -46 t-53.5 -19q-16 0 -28 5q-32 8 -55 42q-17 27 -5 69zM1294 -189q0 11 3 24l174 615q9 31 32.5 47t49.5 16q14 0 29 -3q32 -9 48.5 -33t16.5 -50q0 -5 -2 -14.5t-2 -12.5l-173 -611q-6 -31 -30.5 -49.5t-54.5 -18.5l-26 5q-32 9 -48.5 34t-16.5 51z" />
|
||||
<glyph unicode="" horiz-adv-x="2228" d="M-1 525q0 185 115.5 329t296.5 185q47 220 221.5 361.5t400.5 141.5q223 0 396.5 -136t226.5 -352h35q108 0 206 -42t168 -112.5t111 -168.5t41 -206q0 -214 -146.5 -367t-359.5 -160q-20 0 -20 18v143q0 18 20 18q135 11 231 111.5t96 236.5q0 143 -102.5 246 t-244.5 103h-173q-19 0 -23 19l-8 61q-11 114 -74.5 206.5t-163.5 145t-216 52.5q-176 0 -305.5 -116.5t-146.5 -290.5l-7 -58q0 -15 -17 -15l-56 -8q-135 -11 -225.5 -110t-90.5 -235q0 -139 94.5 -239.5t231.5 -108.5q19 0 19 -18v-143q0 -18 -19 -18q-105 5 -200 48 t-163.5 113t-108.5 165.5t-40 200.5zM577 -197q0 5 2 14t2 12l29 110q11 36 42.5 54.5t68.5 7.5q36 -11 55 -42.5t8 -68.5l-26 -108q-21 -70 -81 -70q-4 0 -9.5 0.5t-12 1.5t-10.5 1q-34 10 -51 36.5t-17 51.5zM718 317q0 4 1.5 12t1.5 10l32 112q10 31 34 47.5t50 16.5 l27 -3q36 -11 54.5 -42.5t7.5 -68.5l-27 -107q-11 -44 -43 -62.5t-69 -2.5q-35 10 -52 36t-17 52zM849 -531q0 5 2 13.5t2 12.5l30 106q10 38 42.5 57.5t69.5 8.5q36 -11 54.5 -43.5t7.5 -69.5l-27 -111q-17 -65 -85 -65q-8 0 -26 4q-35 6 -52.5 32.5t-17.5 54.5zM992 -22 q0 14 3 27l30 110q11 36 43.5 54.5t69.5 7.5q36 -10 54 -41.5t6 -69.5l-27 -106q-20 -72 -76 -72q-7 0 -33 6q-35 7 -52.5 31.5t-17.5 52.5zM1293 -201q0 11 3 26l30 106q11 38 43.5 57.5t67.5 9.5q37 -11 56.5 -43.5t9.5 -67.5l-31 -113q-16 -65 -85 -65q-13 0 -27 3 q-32 6 -49 32.5t-18 54.5zM1440 307q0 5 5 28l32 110q6 34 31 50.5t52 14.5q7 1 26 -3q35 -8 55 -41q19 -27 8 -69l-26 -110q-21 -69 -81 -69q-10 0 -36 6q-33 10 -49.5 33.5t-16.5 49.5z" />
|
||||
<glyph unicode="" horiz-adv-x="2217" d="M-1 523q0 185 113.5 328.5t292.5 187.5q50 218 224.5 358t400.5 140q220 0 392.5 -136.5t224.5 -350.5h34q143 0 265 -70.5t193 -192t71 -264.5q0 -212 -148 -365t-360 -160q-21 0 -21 18v142q0 19 21 19q136 7 232.5 108.5t96.5 237.5q0 142 -103 245t-247 103h-172 q-19 0 -19 18l-8 62q-18 173 -148.5 290t-303.5 117q-176 0 -305.5 -118t-146.5 -293l-7 -53q0 -20 -20 -20l-56 -7q-133 -11 -223.5 -110.5t-90.5 -233.5q0 -136 96.5 -237.5t232.5 -108.5q18 0 18 -19v-142q0 -18 -18 -18q-214 7 -362.5 160t-148.5 365zM678 89 q0 36 25.5 62t62.5 26t62.5 -26t25.5 -62q0 -38 -25.5 -64.5t-62.5 -26.5t-62.5 26.5t-25.5 64.5zM678 -298q0 38 26 64q26 24 62 24q38 0 63 -25t25 -63t-25 -63t-63 -25t-63 25t-25 63zM1018 -117q0 37 27 66q26 26 61 26q38 0 65 -27.5t27 -64.5t-27 -63.5t-65 -26.5 q-36 0 -62 26.5t-26 63.5zM1018 269q0 37 27 64q28 26 61 26q38 0 65 -26.5t27 -63.5t-27 -62.5t-65 -25.5q-36 0 -62 25.5t-26 62.5zM1018 -507q0 36 27 65q26 26 61 26q38 0 65 -27t27 -64t-27 -62.5t-65 -25.5q-36 0 -62 25.5t-26 62.5zM1362 89q0 35 27 61.5t64 26.5 t62.5 -26t25.5 -62q0 -38 -25.5 -64.5t-62.5 -26.5t-64 27t-27 64zM1362 -298q0 35 27 64q25 24 64 24q37 0 62.5 -25t25.5 -63t-25.5 -63t-62.5 -25q-38 0 -64.5 25.5t-26.5 62.5z" />
|
||||
<glyph unicode="" horiz-adv-x="2211" d="M-1 527q0 184 115 327.5t291 184.5q49 218 224 357.5t401 139.5q220 0 392 -136t225 -350h33q143 0 265 -69.5t193 -190t71 -263.5q0 -214 -147.5 -368t-360.5 -161q-21 0 -21 18v142q0 19 21 19q137 7 233 109t96 241t-104 241.5t-246 102.5h-171q-20 0 -20 18l-7 62 q-17 172 -147.5 289t-304.5 117q-175 0 -305 -116.5t-147 -289.5l-7 -58q0 -19 -20 -19l-56 -3q-131 -6 -222.5 -107t-91.5 -237q0 -139 96 -241t233 -109q18 0 18 -19v-142q0 -18 -18 -18q-214 10 -362.5 163t-148.5 366zM631 433q0 39 39 105.5t73 107.5q34 38 41 45 l38 -43q41 -44 76.5 -108.5t35.5 -106.5q0 -63 -43 -105t-107 -42q-63 0 -108 43t-45 104zM949 8q0 44 25.5 102t62.5 109q29 41 71 90t63 69q22 20 27 25l26 -25q60 -53 136 -156q38 -53 63 -111t25 -103q0 -103 -72.5 -175.5t-177.5 -72.5q-103 0 -176 72t-73 176z M1106 718q0 67 104 171l25 -27q75 -92 75 -144q0 -41 -29.5 -70.5t-70.5 -29.5q-43 0 -73.5 29.5t-30.5 70.5z" />
|
||||
<glyph unicode="" horiz-adv-x="2211" d="M1 527q0 -177 106 -317t274 -191l-70 -179q-5 -23 15 -23h227l-111 -403h30l423 557q6 7 2 14.5t-15 7.5h-232l264 495q10 22 -15 22h-314q-15 0 -24 -15l-115 -306q-114 29 -188.5 124t-74.5 214q0 133 90 230.5t224 113.5l56 8q21 0 21 18l8 55q16 173 145.5 290 t304.5 117q174 0 304.5 -117t149.5 -290l7 -62q0 -19 19 -19h173q145 0 247 -100.5t102 -243.5q0 -136 -95 -235.5t-234 -109.5q-20 0 -20 -20v-146q0 -18 20 -18q104 3 198.5 47t162.5 114.5t108 167t40 200.5q0 144 -71 265.5t-193 192t-265 70.5h-35q-53 213 -225 348 t-393 135q-226 0 -401 -140t-224 -358q-176 -41 -291 -184.5t-115 -328.5zM871 -525q0 -25 16.5 -49.5t49.5 -34.5q22 -5 28 -5q14 0 40 11q33 15 44 56l30 112q10 35 -8.5 67t-56.5 43q-35 10 -67.5 -8.5t-43.5 -56.5l-29 -107q-3 -27 -3 -28zM1014 -16q0 -23 16.5 -46.5 t48.5 -35.5q36 -12 68 5t43 56l32 111q10 35 -8.5 67.5t-56.5 43.5q-36 10 -69.5 -8.5t-44.5 -55.5l-26 -108q-3 -27 -3 -29zM1315 -192q0 -27 17.5 -54t50.5 -37q4 0 12.5 -2t12.5 -2q21 0 40 8q31 12 45 61l26 108q11 37 -6.5 70.5t-53.5 44.5q-40 10 -72.5 -9t-39.5 -57 l-29 -110q-3 -9 -3 -21zM1461 319q-1 -25 15.5 -50t51.5 -40q12 -6 29 -6q15 0 35 9q33 16 47 61l31 110q3 21 3 23q0 27 -17.5 52.5t-52.5 35.5q-4 0 -12 2t-12 2q-27 0 -51.5 -16.5t-34.5 -51.5l-30 -108q-2 -8 -2 -23z" />
|
||||
<glyph unicode="" horiz-adv-x="2211" d="M2 527q0 -177 106 -317t275 -191l-70 -179q-5 -23 15 -23h227l-105 -459h30l418 613q6 7 2 14.5t-15 7.5h-232l264 495q11 23 -15 23h-315q-15 0 -25 -16l-114 -306q-114 29 -189 124t-75 214q0 133 90.5 231t224.5 114l56 7q21 0 21 19l7 54q17 173 146.5 290t304.5 117 q174 0 304.5 -117t149.5 -290l7 -62q0 -18 19 -18h172q145 0 248 -101.5t103 -243.5q0 -136 -95.5 -235.5t-235.5 -110.5q-20 0 -20 -19v-146q0 -18 20 -18q213 7 361.5 161t148.5 368q0 144 -71 265.5t-193 192t-266 70.5h-33q-53 212 -226 347.5t-393 135.5 q-226 0 -401 -140t-224 -358q-177 -41 -292 -184.5t-115 -328.5zM871 -509q0 -63 63 -82q3 0 12 -1.5t14 -1.5q27 0 52.5 15.5t34.5 51.5l240 927q10 38 -7 69t-53 42q-27 3 -28 3q-26 0 -50.5 -16t-33.5 -48l-241 -930q-3 -13 -3 -29zM1314 -182q0 -19 12 -40 q22 -35 53 -47q17 -5 32 -5q16 0 35 8q32 14 44 61l155 605q4 18 4 29q0 24 -17 48t-49 34q-27 3 -28 3q-27 0 -50 -15.5t-32 -48.5l-155 -608q0 -4 -2 -12t-2 -12z" />
|
||||
<glyph unicode="" horiz-adv-x="2211" d="M2 678q0 36 25 60t61 24h1289q35 0 58 -23.5t23 -60.5q0 -35 -23 -58t-58 -23h-1289q-36 0 -61 23t-25 58zM261 987q0 35 25 59q24 24 59 24h1290q34 0 58 -24t24 -59t-24 -59.5t-58 -24.5h-1290q-35 0 -59.5 24.5t-24.5 59.5zM432 375q0 34 26 58q23 23 59 23h1290 q36 0 60 -23t24 -58t-24.5 -60t-59.5 -25h-1290q-35 0 -60 25t-25 60zM1561 678q0 37 24 60.5t61 23.5h481q37 0 60.5 -24t23.5 -60q0 -35 -24 -58t-60 -23h-481q-37 0 -61 23t-24 58z" />
|
||||
<glyph unicode="" d="M-10 89q0 -38 28 -64q27 -27 65 -27h613q42 0 73.5 -30.5t31.5 -72.5q0 -43 -31.5 -74t-73.5 -31q-41 0 -73 32q-29 26 -65 26q-38 0 -64.5 -26.5t-26.5 -63.5q0 -35 27 -64q84 -84 202 -84t201.5 83t83.5 202q0 118 -83.5 202t-201.5 84h-613q-38 0 -65.5 -27.5 t-27.5 -64.5zM-10 411q0 -36 28 -62t65 -26h1158q118 0 201.5 83t83.5 201t-83.5 200.5t-201.5 82.5q-122 0 -202 -81q-24 -25 -24 -65t24.5 -64t62.5 -24t66 24q30 30 73 30q41 0 72 -30t31 -73q0 -42 -31 -73t-72 -31h-1158q-38 0 -65.5 -27.5t-27.5 -64.5zM258 662 q0 -14 17 -14h154q12 0 22 16q36 87 111.5 143.5t168.5 63.5l56 8q21 0 21 18l8 55q17 173 146.5 289.5t304.5 116.5t305 -116t148 -290l7 -62q0 -19 19 -19h174q145 0 247 -101t102 -243t-102.5 -244t-246.5 -102h-737q-18 0 -18 -19v-146q0 -18 18 -18h737q143 0 264.5 71 t192.5 193t71 265q0 141 -73 264q117 113 161 275l16 72q2 2 2 8q0 7 -15 17l-64 22q-91 28 -155 89.5t-91.5 131t-27.5 139.5q0 43 9 84l15 66q4 15 -15 24l-84 26q-70 17 -133 17q-59 0 -120 -14t-126 -43.5t-126.5 -85t-106.5 -130.5q-113 48 -247 48q-226 0 -399 -140 t-220 -357q-137 -32 -242 -131t-145 -235v-3q-3 -5 -3 -9zM1670 1397q50 88 138 134.5t182 46.5q22 0 33 -1q-2 -14 -2 -38q0 -151 84 -289t232 -207q-25 -60 -80 -110q-142 122 -337 122h-35q-43 201 -215 342z" />
|
||||
<glyph unicode="" d="M-146 86q0 -38 26.5 -63t66.5 -25h1004q38 0 63 25t25 63q0 40 -25 65.5t-63 25.5h-1004q-40 0 -66.5 -25.5t-26.5 -65.5zM135 411q0 -39 27 -64q25 -27 64 -27h1006q37 0 62.5 26.5t25.5 64.5q0 37 -25 62.5t-63 25.5h-1006q-38 0 -64.5 -25.5t-26.5 -62.5zM246 661 q0 -15 19 -15h152q14 0 24 15q36 87 111 144t168 64l58 8q20 0 20 18l7 55q17 174 147 290.5t306 116.5q175 0 303.5 -115t150.5 -289l8 -62q0 -18 18 -18h173q145 0 246.5 -102t101.5 -246q0 -141 -103 -244.5t-245 -103.5h-737q-19 0 -19 -18v-143q0 -18 19 -18h737 q144 0 266 70t193 191.5t71 265.5q0 142 -70 264q117 104 162 275l19 73q1 2 1 7q0 13 -16 17l-66 23q-70 21 -124 61t-85.5 89.5t-47 100.5t-15.5 101q0 43 11 93l15 65q7 15 -15 24l-88 26q-70 17 -132 17q-60 0 -121 -14t-126 -43.5t-125.5 -85t-105.5 -130.5 q-124 51 -254 51q-226 0 -400 -140.5t-221 -360.5q-139 -32 -243.5 -130.5t-145.5 -235.5v-3q-2 -2 -2 -8zM322 -245q0 -38 28 -64q26 -27 64 -27h1005q38 0 65.5 27t27.5 64t-27.5 62.5t-65.5 25.5h-1005q-38 0 -65 -25.5t-27 -62.5zM1665 1394q55 88 143.5 136.5 t181.5 46.5q18 0 27 -1v-26q0 -156 84.5 -297.5t231.5 -211.5q-27 -62 -82 -114q-141 126 -340 126h-34q-40 193 -212 341z" />
|
||||
<glyph unicode="" horiz-adv-x="2331" d="M-1 527q0 -213 148.5 -366t362.5 -163q19 0 19 18v143q0 18 -19 18q-137 7 -233 109t-96 241q0 133 90 231t224 114l56 3q20 0 20 19l8 58q17 173 147 290t305 117q173 0 303.5 -117t148.5 -290l8 -62q0 -18 19 -18h172q142 0 246 -102.5t104 -242.5q0 -139 -96 -241 t-233 -109q-21 0 -21 -18v-143q0 -18 21 -18q213 7 360.5 161t147.5 368q0 138 -68 257q125 117 164 279l15 73q3 2 3 7q0 12 -18 17l-59 18q-91 27 -155.5 90.5t-91.5 133t-27 140.5q-1 39 8 84l15 62q5 14 -15 23l-85 27q-67 20 -140 20q-56 0 -116 -13.5t-124.5 -43 t-126.5 -85t-107 -130.5q-120 52 -252 52q-226 0 -401 -140t-224 -358q-177 -41 -292 -184.5t-115 -328.5zM583 -223q14 -34 49 -48q31 -16 66 -2.5t48 47.5q16 35 2.5 69t-47.5 47q-32 16 -66 2t-50 -49q-16 -25 -2 -66zM650 79q0 -24 16.5 -48t48.5 -34q41 -16 72 0.5 t43 60.5l93 342q11 40 -8 71.5t-57 39.5q-35 11 -67.5 -7t-43.5 -54l-93 -343q0 -3 -2 -13t-2 -15zM849 -518q0 -20 6 -31q14 -35 48 -48q15 -8 35 -8q10 0 32 6q36 13 51.5 48t-0.5 70t-48 49t-66 0q-31 -13 -44.5 -38t-13.5 -48zM928 -235q0 -61 67 -83q15 -5 26 -5 q17 0 37 9q35 13 49 61l176 653q10 38 -7 69t-53 42q-37 11 -69.5 -7t-42.5 -54l-180 -658q-3 -14 -3 -27zM1295 -191q0 -20 7 -33q13 -32 47 -47q19 -8 37 -8q10 0 32 6q35 14 48 47q13 35 0.5 67.5t-44.5 48.5q-35 16 -69.5 2.5t-50.5 -49.5q-7 -13 -7 -34zM1371 82 q0 -25 17 -48.5t49 -33.5q27 -3 29 -3q64 0 81 65l94 338q10 38 -8 69t-54 42q-37 11 -68 -6.5t-42 -54.5l-94 -339q-4 -18 -4 -29zM1441 1389q50 92 138.5 140t187.5 47q18 0 27 -2v-33q0 -156 83.5 -293.5t231.5 -207.5q-31 -70 -80 -114q-141 125 -347 125h-34 q-41 200 -207 338z" />
|
||||
<glyph unicode="" horiz-adv-x="2331" d="M-1 527q0 -213 148.5 -366t362.5 -163q18 0 18 18v146q0 19 -18 19q-139 11 -234 110.5t-95 235.5q0 134 90.5 233.5t223.5 110.5l56 8q20 0 20 18l8 55q17 173 146.5 289.5t304.5 116.5q174 0 304 -116t148 -290l8 -62q0 -19 19 -19h172q145 0 247.5 -101t102.5 -243 q0 -136 -95 -235.5t-234 -110.5q-21 0 -21 -19v-146q0 -18 21 -18q213 7 360.5 161t147.5 368q0 143 -73 264q128 116 165 275l16 77q1 1 1 7q0 12 -17 17l-62 18q-91 26 -155 88t-91 131.5t-27 140.5q0 39 10 84l15 66q4 15 -15 24l-84 26q-70 17 -133 17q-58 0 -119 -14 t-127 -43.5t-128 -85t-107 -130.5q-113 48 -249 48q-226 0 -400.5 -139.5t-224.5 -357.5q-176 -42 -291 -185.5t-115 -327.5zM817 31q-8 -23 15 -23h230l-140 -442h32l445 596q6 7 2.5 14.5t-14.5 7.5h-234l246 449q11 23 -15 23h-314q-13 0 -23 -15zM1435 1391 q53 91 141.5 139t184.5 48q20 0 29 -2v-33q0 -154 83 -291t231 -209q-25 -59 -80 -114q-147 126 -343 126h-34q-45 199 -212 336z" />
|
||||
<glyph unicode="" horiz-adv-x="2331" d="M-5 525q0 -212 149 -366t363 -161q20 0 20 18v143q0 18 -20 18q-136 7 -233 110t-97 238q0 136 91 235.5t225 110.5l57 7q20 0 20 19l7 54q17 176 147 293.5t306 117.5q174 0 305.5 -117t149.5 -290l8 -61q0 -20 18 -20h172q145 0 248.5 -103t103.5 -246 q0 -88 -44 -166.5t-120.5 -127.5t-166.5 -54q-21 0 -21 -18v-143q0 -18 21 -18q105 3 200 46.5t163 114t108 166.5t40 200q0 141 -73 268q133 119 169 280l15 73q2 2 2 8q0 9 -17 17l-60 19q-70 21 -124.5 62.5t-86.5 92.5t-48.5 103.5t-16.5 102.5q-3 38 9 88l15 62 q7 16 -15 25l-85 26q-65 18 -138 18q-58 0 -119.5 -14t-127 -44t-127.5 -86.5t-107 -131.5q-130 49 -250 49q-226 0 -402 -140.5t-226 -359.5q-180 -46 -293.5 -189t-113.5 -329zM573 -227q0 -27 17 -54t50 -37q37 -11 68.5 4t42.5 60l15 65q8 36 -10 67.5t-55 42.5 q-36 11 -68.5 -8t-42.5 -57l-15 -63q-2 -6 -2 -20zM654 82q0 -35 26 -61q25 -27 61 -27q38 0 64 25.5t26 62.5t-26 62.5t-64 25.5q-37 0 -62 -25t-25 -63zM719 325q-2 -25 14 -48.5t51 -34.5q33 -10 66.5 7.5t44.5 54.5l30 96q12 39 -7.5 69.5t-58.5 41.5q-35 11 -67 -7 t-43 -53l-27 -98q-3 -27 -3 -28zM842 -560q0 -27 17 -52.5t52 -35.5q14 -3 26 -3q69 0 85 65l15 63q10 40 -9 72.5t-57 39.5q-34 11 -66.5 -7.5t-43.5 -54.5l-15 -63q-4 -18 -4 -24zM926 -250q0 -36 26 -62t62 -26q38 0 63 25t25 63q0 37 -25 62t-63 25t-63 -25t-25 -62z M992 -8q-2 -24 14.5 -50t48.5 -32q37 -10 68 6t44 60l28 96q11 35 -7.5 67t-56.5 43q-35 11 -68 -8.5t-44 -56.5l-24 -96q-3 -14 -3 -29zM1287 -237q0 -27 16 -51.5t49 -34.5q5 0 15 -2t15 -2q65 0 81 70l15 64q11 34 -7.5 67t-54.5 44q-39 10 -71.5 -8.5t-43.5 -56.5 l-11 -63q0 -2 -1.5 -12t-1.5 -15zM1366 78q0 -37 26 -61q24 -26 62 -26t63 25t25 62q0 38 -25 63t-63 25t-63 -25t-25 -63zM1431 325q0 -27 17.5 -51.5t52.5 -34.5q3 0 12.5 -2t14.5 -2q17 0 39 10q33 17 43 56l27 96q10 36 -8 67.5t-54 42.5q-37 11 -68.5 -6t-42.5 -54 l-30 -97q0 -3 -1.5 -11.5t-1.5 -13.5zM1438 1397q51 93 139.5 143t183.5 49q15 0 32 -3v-34q-2 -157 82.5 -294.5t236.5 -206.5q-32 -64 -85 -118q-148 122 -344 122h-33q-50 209 -212 342z" />
|
||||
<glyph unicode="" horiz-adv-x="2331" d="M-1 525q0 -179 106 -320.5t274 -188.5q14 -3 27 8l120 150q-140 0 -242.5 104t-102.5 247q0 134 90 231.5t225 109.5l53 7q20 0 20 20l8 57q20 174 150 291t305 117q174 0 305 -117.5t150 -290.5l7 -62q0 -18 19 -18h173q142 0 244 -102t102 -243q0 -136 -90 -236.5 t-221 -111.5q-72 -9 -92 -30l-250 -310q-24 -27 -19 -67q4 -38 33.5 -63t60.5 -25q36 0 68 40l217 276q99 10 187.5 56t152 115.5t100.5 162.5t37 193q0 139 -73 260q130 118 169 280v10l21 82l-82 28q-92 27 -156 89t-91.5 131t-27.5 140q0 41 9 85l20 83l-90 25q-1 1 -3 2 t-3 2q-2 1 -4.5 2t-3.5 1q-70 17 -133 17q-61 -1 -122.5 -15t-128 -44t-128.5 -86t-107 -132q-120 52 -250 52q-226 0 -401 -140t-225 -358q-181 -44 -294 -186.5t-113 -327.5zM394 -327q0 -21 7 -33q14 -35 46 -49q35 -16 71 -3t49 47q16 35 1.5 71.5t-48.5 48.5 q-35 16 -70.5 2.5t-48.5 -48.5q-7 -15 -7 -36zM591 -66v-12q3 -36 35 -59q21 -24 58 -23t70 34l248 311q22 28 17.5 66.5t-32.5 61.5t-67 19t-61 -36l-248 -305q-20 -23 -20 -57zM651 -622q14 -34 50 -48q17 -8 35 -8q10 0 32 6q34 14 47 47q13 35 1 69t-47 51 q-35 13 -67 -0.5t-48 -46.5q-18 -31 -3 -70zM822 -338v-9q4 -36 33.5 -61.5t59.5 -25.5q35 0 64 36l461 572q23 27 20 66t-31 62q-32 23 -70.5 19.5t-60.5 -31.5l-456 -572q-20 -25 -20 -56zM1195 -456q0 -18 8 -35q13 -36 51 -50q36 -14 65 -2q35 14 49 49q16 35 3 68 t-48 50q-35 13 -69.5 0t-50.5 -45q-8 -19 -8 -35zM1440 1391q50 92 138.5 140.5t187.5 46.5q19 0 28 -1v-32q0 -155 85.5 -293.5t233.5 -209.5q-40 -75 -84 -119q-142 126 -343 126h-35q-49 206 -211 342z" />
|
||||
<glyph unicode="" horiz-adv-x="2331" d="M-1 528q0 -213 149 -366.5t363 -163.5q19 0 19 18v143q0 18 -19 18q-137 8 -233.5 110t-96.5 241q0 133 90.5 231.5t224.5 114.5l57 4q21 0 21 18l7 58q17 173 147 290.5t305 117.5q174 0 305 -117.5t149 -290.5l8 -61q0 -19 18 -19h173q143 0 247.5 -102.5t104.5 -243.5 q0 -138 -97 -240.5t-234 -110.5q-21 0 -21 -18v-143q0 -18 21 -18q213 7 361.5 161.5t148.5 368.5q0 141 -73 262q129 117 169 279l15 77q2 1 2 7q0 12 -17 17l-60 19q-91 26 -156 88.5t-92.5 131.5t-27.5 137q-3 36 9 88l15 65q7 16 -15 26l-84 26q-72 16 -135 16 q-60 0 -122 -14t-128 -43.5t-128 -85t-107 -130.5q-132 48 -249 48q-226 0 -401.5 -140.5t-225.5 -359.5q-177 -41 -292 -185t-115 -329zM592 -201q0 -27 17.5 -52.5t52.5 -35.5q37 -11 68.5 4.5t42.5 59.5l153 626q10 40 -9 72t-56 39q-35 11 -67.5 -7t-43.5 -54l-155 -629 q-3 -21 -3 -23zM870 -529q0 -25 16.5 -49t47.5 -33q18 -4 26 -4q70 0 87 63l240 953q10 39 -7.5 69.5t-53.5 41.5q-37 11 -70.5 -7.5t-41.5 -53.5l-241 -952q0 -2 -1.5 -12.5t-1.5 -15.5zM1314 -210q0 -57 67 -79q22 -6 32 -6q18 0 37 8q32 13 41 62l154 626q10 38 -8 69 t-54 42q-37 11 -70 -7t-41 -54l-153 -629q-5 -27 -5 -32zM1440 1395q51 93 139.5 141.5t187.5 46.5q18 0 28 -1v-32q-2 -155 82.5 -293t236.5 -210q-36 -73 -84 -114q-147 121 -344 121h-33q-44 203 -213 341z" />
|
||||
<glyph unicode="" horiz-adv-x="2331" d="M-1 527q0 -213 148.5 -366t362.5 -163q18 0 18 18v146q0 19 -18 19q-139 11 -234 110.5t-95 235.5q0 132 90 230t224 114l56 8q20 0 20 18l8 55q17 173 146.5 289.5t304.5 116.5q174 0 304 -116t148 -290l8 -62q0 -19 19 -19h172q144 0 247 -101t103 -243 q0 -136 -95 -235.5t-234 -110.5q-21 0 -21 -19v-146q0 -18 21 -18q213 7 360.5 161t147.5 368q0 139 -69 259q130 117 169 279v14l20 84l-81 22q-130 39 -202 144.5t-72 216.5q0 42 9 83l21 85l-91 27l-15 4q-75 15 -132 15q-60 0 -122 -14t-128.5 -44t-128 -85.5 t-106.5 -131.5q-119 52 -252 52q-226 0 -399.5 -140t-220.5 -358q-180 -41 -295.5 -184t-115.5 -328zM575 -195q0 -24 16 -47.5t48 -35.5q35 -12 68 5.5t44 54.5l30 113q11 33 -7.5 66t-54.5 44q-37 10 -69.5 -9t-44.5 -57l-26 -106q-4 -20 -4 -28zM713 310q0 -20 10 -38 q12 -27 58 -41q39 -10 70 6.5t43 55.5l29 110q11 37 -7 68.5t-54 41.5q-39 10 -71.5 -7.5t-43.5 -55.5l-28 -108q-6 -22 -6 -32zM847 -528q0 -27 16.5 -52.5t48.5 -35.5q18 -5 24 -5q14 0 40 11q32 13 46 59l32 112q11 34 -7.5 67t-54.5 44q-39 10 -71.5 -8.5t-43.5 -56.5 l-27 -108q-3 -13 -3 -27zM989 -19q0 -24 16 -47.5t49 -35.5q36 -11 68 6t43 56l29 111q11 34 -6.5 66.5t-52.5 43.5q-39 10 -72.5 -9t-44.5 -57l-26 -107q0 -3 -1 -8.5t-1.5 -10t-0.5 -8.5zM1288 -207q0 -61 67 -80q13 -3 27 -3q24 0 39 7q32 13 46 61l30 109 q10 40 -8.5 71.5t-56.5 39.5q-37 11 -68.5 -7.5t-42.5 -54.5l-30 -111q0 -4 -1 -10.5t-1.5 -12t-0.5 -9.5zM1435 307q0 -25 17 -49t50 -34q23 -5 25 -5q14 0 40 11q33 13 47 59l29 111q10 35 -9 67t-56 43q-35 11 -67.5 -7.5t-43.5 -55.5l-27 -113q-5 -23 -5 -27zM1440 1391 q51 90 141.5 140t193.5 50h22q-2 -28 -2 -40q0 -152 84 -289t232 -209q-34 -67 -84 -117q-149 129 -346 129h-34q-40 189 -207 336z" />
|
||||
<glyph unicode="" horiz-adv-x="2331" d="M-1 525q0 -212 149 -366t363 -161q19 0 19 18v143q0 18 -19 18q-136 7 -233 109.5t-97 238.5t90.5 235t224.5 110l57 8q21 0 21 18l7 55q17 175 146.5 293t305.5 118q174 0 305 -117.5t149 -290.5l8 -61q0 -19 18 -19h173q145 0 248.5 -103t103.5 -246 q0 -136 -97.5 -238.5t-233.5 -109.5q-21 0 -21 -18v-143q0 -18 21 -18q140 4 257.5 76t185 192t67.5 259q0 144 -70 265q119 107 163 275l18 77q2 2 2 8q0 12 -17 17l-65 18q-70 21 -124.5 61t-86 89.5t-47 101t-15.5 101.5q0 43 11 92l12 66q4 15 -15 24l-85 26 q-70 17 -133 17q-59 0 -120 -14t-126 -44t-126 -85.5t-106 -130.5q-129 53 -254 53q-226 0 -401.5 -140.5t-225.5 -359.5q-180 -45 -293.5 -188t-113.5 -329zM680 89q0 -38 25.5 -64.5t62.5 -26.5q38 0 63.5 26.5t25.5 64.5q0 36 -25.5 62t-63.5 26q-37 0 -62.5 -26 t-25.5 -62zM680 -299q0 -36 26 -62t62 -26q38 0 63.5 25t25.5 63t-25.5 63t-63.5 25t-63 -25t-25 -63zM1022 -118q0 -39 26 -63q26 -26 62 -26q38 0 65 26t27 63q0 38 -27 65.5t-65 27.5q-35 0 -61.5 -27.5t-26.5 -65.5zM1022 270q0 -38 26 -62q26 -26 62 -26q39 0 65.5 25 t26.5 63q0 37 -27 63.5t-65 26.5q-36 0 -62 -26.5t-26 -63.5zM1022 -510q0 -35 26 -61q27 -27 62 -27q38 0 65 25.5t27 62.5q0 38 -27 65.5t-65 27.5q-35 0 -61.5 -27.5t-26.5 -65.5zM1367 89q0 -38 26.5 -64.5t63.5 -26.5t62.5 26.5t25.5 64.5q0 36 -25.5 62t-62.5 26 t-63.5 -26t-26.5 -62zM1367 -299q0 -36 26 -62t64 -26q37 0 62.5 25.5t25.5 62.5q0 38 -25 63t-63 25t-64 -25t-26 -63zM1440 1395q50 87 138.5 134.5t182.5 49.5q23 0 34 -2v-33q0 -155 83.5 -292.5t232.5 -209.5q-25 -58 -81 -114q-142 126 -344 126h-33q-44 197 -213 341 z" />
|
||||
<glyph unicode="" horiz-adv-x="2331" d="M2 529q0 -213 148.5 -366t362.5 -164q20 0 20 19v142q0 19 -20 19q-137 7 -233 109t-96 241q0 133 90 231t224 114l56 4q21 0 21 19l8 58q17 173 146.5 289.5t304.5 116.5q174 0 305 -117t149 -289l7 -61q0 -20 19 -20h173q142 0 246 -102.5t104 -242.5 q0 -139 -96.5 -241t-233.5 -109q-20 0 -20 -19v-142q0 -19 20 -19q140 5 257.5 77t185 192.5t67.5 260.5q0 139 -73 261q126 120 165 275l14 78q3 1 3 7q0 12 -17 17l-62 18q-91 26 -156 87.5t-92.5 129.5t-27.5 137q0 46 12 91l15 65q4 16 -15 25l-85 26q-72 16 -135 16 q-58 0 -119 -14t-126.5 -43t-127.5 -84t-107 -128q-130 49 -249 49q-226 0 -401 -140.5t-225 -358.5q-177 -42 -291.5 -185.5t-114.5 -328.5zM635 435q0 -61 45 -104t109 -43t106.5 42.5t42.5 104.5q0 41 -37 109t-69 105q-25 28 -43 46l-38 -44q-43 -47 -79.5 -110.5 t-36.5 -105.5zM954 10q0 -105 73 -177t176 -72q105 0 178 73t73 176q0 87 -86 213q-71 93 -137 159q-13 9 -28 24l-25 -24q-63 -58 -136 -158q-38 -53 -63 -111t-25 -103zM1111 721q0 -41 30.5 -70t74.5 -29q41 0 70 29t29 70q0 68 -99 173l-26 -29q-30 -32 -54.5 -74.5 t-24.5 -69.5zM1438 1395q56 91 142.5 137.5t182.5 46.5q19 0 29 -2v-32q0 -154 84.5 -291.5t233.5 -210.5q-25 -60 -80 -110q-148 122 -343 122h-34q-49 210 -215 340z" />
|
||||
<glyph unicode="" horiz-adv-x="2331" d="M1 529q0 -177 106 -316.5t275 -190.5l-69 -181q-7 -22 15 -22h226l-138 -446h30l451 600q6 7 2.5 14.5t-14.5 7.5h-232l264 495q11 23 -15 23h-316q-14 0 -24 -16l-114 -307q-114 29 -189.5 124.5t-75.5 214.5q0 133 90.5 231t224.5 114l56 8q21 0 21 19l8 54 q17 173 146.5 290t304.5 117q174 0 305 -117.5t149 -289.5l8 -61q0 -20 18 -20h173q145 0 248 -101.5t103 -243.5q0 -136 -96 -236t-235 -110q-20 0 -20 -20v-145q0 -19 20 -19q213 7 361.5 161.5t148.5 368.5q0 139 -70 261q124 119 162 280l19 73q1 1 1 7q0 13 -16 17 l-66 18q-92 27 -156 89.5t-90.5 130t-25.5 134.5q0 44 11 91l12 65q4 16 -15 25l-85 26q-75 15 -132 15q-59 1 -120.5 -12.5t-126.5 -43t-125.5 -84t-105.5 -128.5q-136 49 -254 49q-226 0 -401 -140.5t-225 -358.5q-177 -42 -292 -185.5t-115 -328.5zM873 -523 q0 -25 16.5 -50t48.5 -35q25 -5 29 -5q15 0 39 11q33 16 45 56l30 112q10 35 -8.5 67.5t-56.5 43.5q-35 10 -67.5 -9t-43.5 -57l-28 -107q0 -3 -2 -12.5t-2 -14.5zM1016 -16q0 -69 65 -80q23 -5 29 -5q60 0 82 67l32 109q10 35 -9 67.5t-57 43.5q-36 11 -68.5 -7.5 t-43.5 -56.5l-27 -108q-3 -14 -3 -30zM1315 -194q2 -27 19 -51.5t51 -36.5q26 -6 36 -6q54 0 75 72l26 109q11 37 -6.5 70t-53.5 44q-40 10 -72.5 -9t-39.5 -57l-31 -110q0 -4 -2 -12.5t-2 -12.5zM1441 1395q53 89 143 137.5t189 48.5h22v-34q0 -102 36 -198t108.5 -177 t171.5 -129q-27 -60 -81 -110q-146 125 -343 125h-34q-45 200 -212 337zM1462 318q0 -69 69 -88q22 -4 32 -4q58 0 79 69l30 111q1 8 1 23q2 27 -15 52.5t-52 35.5q-2 0 -10.5 1.5t-13.5 1.5q-26 0 -51 -16.5t-35 -50.5l-31 -109q0 -1 -1.5 -11t-1.5 -15z" />
|
||||
<glyph unicode="" horiz-adv-x="2331" d="M-3 531q0 -178 105.5 -317.5t274.5 -190.5l-70 -180q-4 -22 16 -22h227l-104 -374h31l417 527q6 8 1.5 15.5t-15.5 7.5h-232l264 494q12 23 -15 23h-315q-14 0 -24 -15l-115 -307q-115 29 -190 124.5t-75 214.5q0 133 91 231.5t225 114.5l56 8q20 0 20 18l8 55 q17 173 147 290t305 117q173 0 304.5 -117.5t150.5 -289.5l6 -63q0 -18 20 -18h172q145 0 248 -101.5t103 -244.5q0 -136 -95.5 -236t-234.5 -111q-21 0 -21 -18v-147q0 -19 21 -19q140 5 257.5 77t185 192.5t67.5 261.5q0 139 -70 261q127 121 166 280l15 77q2 2 2 8 q0 11 -17 16l-61 19q-134 38 -204.5 142.5t-70.5 217.5q0 42 9 85l15 65q7 16 -15 25q-34 14 -97 28t-123 14q-58 0 -119 -14t-126.5 -43.5t-127.5 -85t-107 -130.5q-132 48 -253 48q-226 0 -401.5 -140t-225.5 -358q-177 -41 -291.5 -185.5t-114.5 -329.5zM861 -517 q0 -26 15 -49.5t47 -32.5q17 -3 32 -3q66 0 82 67l246 938q10 38 -7 69.5t-53 41.5q-37 11 -69.5 -6.5t-42.5 -52.5l-247 -943q-3 -14 -3 -29zM1305 -195q0 -26 15.5 -49t49.5 -33q5 0 17 -1.5t18 -1.5q60 0 76 67l160 616q10 39 -8 70t-54 41q-37 11 -68.5 -6t-41.5 -53 l-160 -621q-4 -22 -4 -29zM1438 1398q53 91 141.5 139t187.5 48q19 0 28 -1v-34q0 -154 84 -291t232 -210q-29 -65 -85 -114q-147 126 -343 126h-34q-45 200 -211 337z" />
|
||||
<glyph unicode="" horiz-adv-x="1600" d="M15 786q0 153 60 293t161 241.5t241.5 162t293.5 60.5h123q25 -7 25 -30l4 -94q6 -203 146.5 -346t340.5 -151l88 -7q25 0 25 -25v-104q1 -205 -99.5 -379.5t-274 -276t-378.5 -101.5q-156 0 -296 60t-241 161.5t-160 241.5t-59 294zM210 786q0 -162 82 -294.5 t210 -203.5t269 -71q93 0 187.5 36.5t176 102t140 167.5t75.5 223q-261 54 -418.5 223.5t-181.5 398.5q-151 -8 -276.5 -93t-194.5 -215.5t-69 -273.5z" />
|
||||
<glyph unicode="" d="M-3 93q0 -39 28 -64q29 -29 65 -29h628q42 0 73.5 -30t31.5 -73q0 -41 -32 -72.5t-73 -31.5q-40 0 -72 32q-30 28 -67 28t-63 -27t-26 -65q0 -37 27 -64q83 -83 201 -83t201 82.5t83 200.5q0 119 -83 203t-201 84h-628q-38 0 -65.5 -27t-27.5 -64zM-3 414q0 -37 28 -62 q24 -26 65 -26h1173q118 0 201 83t83 201t-83 200.5t-201 82.5q-122 0 -202 -80q-24 -27 -24 -67q0 -38 24.5 -62.5t63.5 -24.5q40 0 65 24q30 30 73 30q41 0 72 -30t31 -73t-31 -74t-72 -31h-1173q-38 0 -65.5 -27t-27.5 -64zM280 664q0 -13 17 -13h153q13 0 23 15 q36 87 111.5 144t169.5 64l56 7q21 0 21 20l7 53q17 173 146.5 289.5t304.5 116.5t304.5 -115.5t148.5 -290.5l7 -61q0 -19 19 -19h174q144 0 245.5 -101t101.5 -243q0 -143 -101.5 -244.5t-245.5 -101.5h-737q-19 0 -19 -19v-146q0 -19 19 -19h737q143 0 264.5 71t192 193 t70.5 266q0 118 -45 216q121 159 121 355q0 114 -44.5 217.5t-119.5 178t-178.5 118.5t-216.5 44q-246 0 -413 -185q-125 65 -284 65q-226 0 -399 -139t-220 -358q-136 -32 -241.5 -131t-146.5 -233v-4q-2 -5 -2 -10zM1722 1371q115 109 264 109q156 0 268 -112t112 -267 q0 -97 -54 -197q-153 153 -370 153h-36q-37 171 -184 314z" />
|
||||
<glyph unicode="" d="M-133 91q0 -40 29 -64q27 -28 65 -28h1018q37 0 62.5 27t25.5 65t-25.5 65t-62.5 27h-1018q-38 0 -66 -27.5t-28 -64.5zM149 414q0 -37 28 -62q26 -26 64 -26h1020q38 0 63 25t25 63t-25.5 64.5t-62.5 26.5h-1020q-38 0 -65 -26.5t-27 -64.5zM271 663q0 -12 20 -12h152 q14 0 24 15q36 87 112.5 144t169.5 64l58 8q18 0 18 19l8 54q17 173 147.5 290t306.5 117t306 -116t148 -291l8 -61q0 -20 18 -20h172q146 0 248.5 -101t102.5 -244q0 -142 -102.5 -244t-248.5 -102h-737q-18 0 -18 -18v-147q0 -19 18 -19h737q145 0 267.5 71t194 193 t71.5 266q0 113 -47 214q124 162 124 351q0 152 -75.5 281.5t-205 204.5t-282.5 75q-244 0 -410 -178q-136 65 -289 65q-227 0 -402 -140t-221 -359q-140 -32 -244.5 -128.5t-145.5 -231.5zM336 -239q0 -36 29 -62q26 -26 64 -26h1019q38 0 64.5 25t26.5 63t-27 65t-64 27 h-1019q-38 0 -65.5 -27.5t-27.5 -64.5zM1723 1369q108 104 263 104q158 0 269.5 -111t111.5 -268q0 -100 -53 -189q-157 153 -375 153h-34q-39 175 -182 311z" />
|
||||
<glyph unicode="" horiz-adv-x="2285" d="M3 529q0 -143 70 -264.5t191 -192.5t264 -71h1153q143 0 264.5 71t192.5 192.5t71 264.5q0 104 -44 213q121 148 121 352q0 152 -74.5 280.5t-203 203.5t-280.5 75q-239 0 -413 -186q-122 69 -286 69q-225 0 -398 -139t-222 -357q-179 -40 -292.5 -183t-113.5 -328z M185 529q0 133 89 230t224 113l73 3l7 77q22 173 150.5 289.5t300.5 116.5q115 0 214.5 -53t162.5 -146.5t74 -206.5l11 -80h190q141 0 243 -101.5t102 -241.5q0 -144 -101.5 -247t-243.5 -103h-1153q-139 0 -241 104t-102 246zM1469 1365q110 106 259 106 q158 0 268.5 -109.5t110.5 -267.5q0 -100 -54 -196q-153 153 -372 153h-33q-44 179 -179 314z" />
|
||||
<glyph unicode="" horiz-adv-x="2297" d="M-1 529q0 -213 148.5 -366t362.5 -164q19 0 19 19v142q0 19 -19 19q-137 7 -233 109t-96 241q0 133 90 231t224 114l56 8q20 0 20 19l8 54q17 173 147 289.5t305 116.5q174 0 305 -117t149 -289l6 -62q0 -19 20 -19h172q145 0 247.5 -101t102.5 -244q0 -139 -96 -241 t-233 -109q-21 0 -21 -19v-142q0 -19 21 -19q140 5 257.5 77t185 192.5t67.5 260.5q0 118 -46 214q126 153 126 354q0 151 -75.5 280t-204.5 204.5t-280 75.5q-249 0 -414 -185q-131 70 -289 70q-226 0 -401 -140.5t-225 -358.5q-176 -42 -291 -185.5t-115 -328.5zM571 -215 q14 -35 50 -49q32 -16 66.5 -2.5t47.5 47.5q14 35 1 69.5t-47 50.5q-32 13 -65.5 -1t-49.5 -46q-17 -28 -3 -69zM639 87q0 -25 16.5 -49t49.5 -33q36 -11 68.5 6t45.5 59l106 332q12 39 -8 71t-59 40q-35 11 -66.5 -6.5t-42.5 -53.5l-107 -339q0 -3 -1 -8.5t-1.5 -10 t-0.5 -8.5zM838 -511q0 -14 5 -31q14 -35 50 -48q13 -8 35 -8q10 0 32 6q35 13 50.5 48.5t-0.5 70.5t-48 49t-66 1q-31 -13 -44.5 -38.5t-13.5 -49.5zM917 -226q0 -28 16 -51t51 -30q3 0 12 -2t14 -2q69 5 86 66l187 647q11 39 -6 69.5t-53 41.5q-37 11 -69 -6.5t-43 -53.5 l-192 -647q0 -4 -1 -10.5t-1.5 -12t-0.5 -9.5zM1283 -183q0 -12 6 -34q13 -34 49 -47q15 -7 38 -7q19 0 31 6q36 14 49 46q13 35 0 69t-45 51q-35 13 -69.5 -1t-50.5 -46q-8 -17 -8 -37zM1360 88q0 -57 66 -80q22 -6 32 -6q20 0 36 9q28 14 43 59l105 332q12 37 -6.5 68.5 t-54.5 42.5q-37 11 -68 -6.5t-43 -53.5l-105 -334q-5 -27 -5 -31zM1470 1369q110 106 264 106q156 0 266.5 -111t110.5 -267q0 -99 -55 -196q-157 157 -373 157h-34q-49 185 -179 311z" />
|
||||
<glyph unicode="" horiz-adv-x="2285" d="M-6 525q0 -105 40 -200.5t108.5 -165t162.5 -112.5t199 -48q19 0 19 18v143q0 18 -19 18q-139 10 -234 110t-95 237q0 134 90.5 233t223.5 111l56 6q21 0 21 19l8 55q11 114 73.5 207t162 145.5t214.5 52.5q174 0 304 -115t149 -287l6 -62q0 -18 20 -18h171 q144 0 247 -102.5t103 -244.5q0 -137 -95 -237t-234 -110q-21 0 -21 -18v-143q0 -18 21 -18q214 10 361 161.5t147 364.5q0 102 -50 220q127 159 127 349q0 152 -75.5 281t-204 204t-279.5 75q-253 0 -412 -185q-130 68 -285 68q-225 0 -400 -140.5t-224 -359.5 q-179 -41 -292.5 -184t-113.5 -328zM810 32q-4 -22 15 -22h231l-141 -445h31l446 595q6 7 3 14.5t-14 7.5h-234l245 452q12 23 -15 23h-314q-12 0 -22 -15zM1457 1366q107 106 264 106t266.5 -110t109.5 -268q0 -95 -55 -192q-155 149 -368 149h-33q-47 190 -184 315z" />
|
||||
<glyph unicode="" horiz-adv-x="2308" d="M3 527q0 -139 68.5 -259t186.5 -192.5t259 -76.5q19 0 19 18v143q0 19 -19 19q-137 7 -234.5 109.5t-97.5 238.5q0 135 91.5 235t225.5 111l56 8q20 0 20 18l8 55q17 176 147.5 294.5t306.5 118.5q174 0 305.5 -118t149.5 -291l8 -62q0 -18 18 -18h174 q144 0 247.5 -103.5t103.5 -247.5q0 -136 -97.5 -238.5t-233.5 -109.5q-20 0 -20 -19v-143q0 -18 20 -18q141 4 258.5 76.5t185 192.5t67.5 259q0 118 -44 215q129 159 129 359q0 153 -75.5 282.5t-205 205t-281.5 75.5q-119 0 -229.5 -49.5t-190.5 -136.5q-141 69 -289 69 q-227 0 -403 -140.5t-226 -359.5q-180 -46 -294 -190t-114 -330zM573 -227q0 -27 17 -54t50 -37q37 -11 68.5 4t42.5 60l15 65q8 36 -10 67.5t-55 42.5q-36 11 -68.5 -8t-42.5 -57l-15 -63q-2 -6 -2 -20zM654 82q0 -35 26 -61q25 -27 61 -27q38 0 64 25.5t26 62.5t-26 62.5 t-64 25.5q-37 0 -62 -25t-25 -63zM719 325q-2 -25 14 -48.5t51 -34.5q33 -10 66.5 7.5t44.5 54.5l30 96q12 39 -7.5 69.5t-58.5 41.5q-35 11 -67 -7t-43 -53l-27 -98q-3 -27 -3 -28zM842 -560q0 -27 17 -52.5t52 -35.5q14 -3 26 -3q69 0 85 65l15 63q10 40 -9 72.5t-57 39.5 q-34 11 -66.5 -7.5t-43.5 -54.5l-15 -63q-4 -18 -4 -24zM926 -250q0 -36 26 -62t62 -26q38 0 63 25t25 63q0 37 -25 62t-63 25t-63 -25t-25 -62zM992 -8q-2 -24 14.5 -50t48.5 -32q37 -10 68 6t44 60l28 96q11 35 -7.5 67t-56.5 43q-35 11 -68 -8.5t-44 -56.5l-24 -96 q-3 -14 -3 -29zM1287 -237q0 -27 16 -51.5t49 -34.5q5 0 15 -2t15 -2q65 0 81 70l15 64q11 34 -7.5 67t-54.5 44q-39 10 -71.5 -8.5t-43.5 -56.5l-11 -63q0 -2 -1.5 -12t-1.5 -15zM1366 78q0 -37 26 -61q24 -26 62 -26t63 25t25 62q0 38 -25 63t-63 25t-63 -25t-25 -63z M1431 325q0 -27 17.5 -51.5t52.5 -34.5q3 0 12.5 -2t14.5 -2q17 0 39 10q33 17 43 56l27 96q10 36 -8 67.5t-54 42.5q-37 11 -68.5 -6t-42.5 -54l-30 -97q0 -3 -1.5 -11.5t-1.5 -13.5zM1480 1370q111 111 269 111q157 0 267.5 -111.5t110.5 -268.5q0 -105 -55 -197 q-154 154 -377 154h-34q-44 186 -181 312z" />
|
||||
<glyph unicode="" horiz-adv-x="2320" d="M-5 515q0 -181 107 -321t280 -192q15 -2 26 8l121 156q-143 0 -246.5 102.5t-103.5 246.5q0 138 91.5 239t228.5 112l56 8q18 0 18 15l8 60q19 176 150.5 294t307.5 118q178 0 310 -117t149 -292l8 -63q4 -18 22 -18h175q144 0 248 -105t104 -251q0 -135 -91.5 -235.5 t-224.5 -113.5q-76 0 -97 -30l-233 -256q-25 -29 -20 -66.5t34 -67.5q21 -28 62.5 -24.5t64.5 40.5l209 222q132 13 242.5 87.5t175 192t64.5 251.5q0 101 -46 222q124 158 124 354q0 156 -76 286.5t-207 206t-285 75.5q-251 0 -416 -182q-139 69 -296 69q-229 0 -405 -142 t-226 -366q-183 -41 -298 -186.5t-115 -336.5zM400 -285q0 -21 6 -34q13 -34 47 -47q36 -16 72.5 -2.5t48.5 48.5q16 35 2 71.5t-49 49.5q-36 13 -70 -0.5t-50 -47.5q-7 -15 -7 -38zM597 -24q0 -37 34 -71q68 -58 134 16l234 260q24 28 19 65.5t-34 60.5q-29 22 -66.5 17 t-62.5 -31l-236 -254q-22 -32 -22 -63zM665 -550q0 -19 8 -34q14 -36 45 -49q19 -8 39 -8q12 0 34 6q34 15 47 48q16 32 2 68t-48 49q-36 17 -69.5 3t-51.5 -51q-6 -18 -6 -32zM843 -295v-14q3 -38 35 -64q23 -29 63.5 -25.5t63.5 40.5l450 524q25 29 21 68t-37 61 q-28 23 -65 20t-62 -32l-449 -525q-20 -21 -20 -53zM1231 -384q-11 -36 0 -66q14 -35 48 -52q17 -8 38 -8q10 0 32 6q36 14 49 49q16 36 2.5 70t-47.5 47q-36 17 -70 3.5t-52 -49.5zM1487 1368q109 109 264 109q161 0 273 -112.5t112 -273.5q0 -100 -55 -194 q-156 156 -378 156h-34q-40 182 -182 315z" />
|
||||
<glyph unicode="" horiz-adv-x="2297" d="M1 525q0 -105 40 -200.5t108.5 -165t163 -112.5t199.5 -48q18 0 18 18v143q0 18 -18 18q-139 10 -234 110t-95 237q0 134 90.5 233.5t223.5 110.5l56 6q21 0 21 20l7 54q17 174 146 289.5t305 115.5q175 0 305 -114.5t148 -286.5l6 -62q0 -19 20 -19h171 q144 0 247 -102.5t103 -244.5q0 -137 -95 -237t-234 -110q-21 0 -21 -18v-143q0 -18 21 -18q214 10 362 161.5t148 364.5q0 112 -46 212q126 153 126 358q0 150 -75.5 279t-204.5 204.5t-279 75.5q-249 0 -414 -185q-141 69 -288 69q-226 0 -400.5 -140.5t-224.5 -360.5 q-176 -41 -291 -184.5t-115 -327.5zM586 -187q0 -64 69 -83q33 -11 67 6.5t44 53.5l160 611q10 35 -9.5 67t-57.5 43q-35 11 -67 -7.5t-42 -55.5l-160 -608q-4 -18 -4 -27zM863 -513q0 -19 9 -39q16 -33 54 -46q4 0 10.5 -1t12 -1.5t9.5 -0.5q69 0 81 70l246 932 q11 35 -7 67t-54 43q-39 11 -72 -7.5t-40 -55.5l-246 -933q-3 -15 -3 -28zM1306 -186q0 -25 17 -50t48 -34q11 -3 24 -3q27 0 52 14.5t34 48.5l160 611q12 33 -7 66t-55 44q-39 11 -70.5 -7.5t-38.5 -55.5l-160 -608q-4 -18 -4 -26zM1470 1362q107 107 264 107 q156 0 266 -109t110 -265q0 -100 -55 -197q-153 153 -373 153h-33q-45 187 -179 311z" />
|
||||
<glyph unicode="" horiz-adv-x="2297" d="M-1 526q0 -105 40 -200.5t108.5 -165.5t163.5 -113t200 -48q19 0 19 18v143q0 18 -19 18q-136 7 -232.5 109.5t-96.5 238.5t90 235t224 110l57 7q16 0 16 16l8 57q18 175 149 291.5t307 116.5q174 0 304.5 -115.5t149.5 -288.5l7 -61q0 -20 19 -20h173q142 0 244 -102 t102 -246q0 -136 -95 -238.5t-231 -109.5q-20 0 -20 -18v-143q0 -18 20 -18q214 10 362 162t148 365q0 100 -45 213q122 160 122 351q0 151 -75.5 280.5t-204.5 205t-280 75.5q-245 0 -412 -178q-139 69 -288 69q-227 0 -402 -141t-224 -361q-181 -42 -294.5 -183.5 t-113.5 -330.5zM574 -194q0 -27 15.5 -51.5t51.5 -36.5q18 -9 39.5 -7.5t43 21t29.5 51.5l30 108q10 40 -8.5 72t-56.5 39q-34 11 -67 -7.5t-44 -53.5l-30 -111q-3 -14 -3 -24zM714 315q1 -31 18 -55.5t51 -29.5q27 -5 32 -5q60 0 81 70l30 107q10 37 -8.5 70.5t-56.5 44.5 q-36 10 -68 -8.5t-43 -55.5l-32 -112q-4 -20 -4 -26zM846 -528q0 -28 16.5 -53t51.5 -35q22 -5 30 -5q14 0 40 11q27 13 41 59l32 108q10 40 -9 72.5t-56 39.5q-35 11 -68.5 -7.5t-44.5 -54.5l-30 -111q-3 -14 -3 -24zM987 -16q0 -27 17.5 -52.5t52.5 -35.5q36 -11 67.5 5 t43.5 60l30 108q11 40 -7.5 71t-56.5 38q-35 12 -68 -6.5t-44 -54.5l-30 -111q-5 -18 -5 -22zM1291 -202q-2 -25 14.5 -49.5t49.5 -35.5l28 -3q18 -1 40 8q33 13 44 58l31 112q11 37 -8 68.5t-55 42.5q-40 10 -71 -9t-38 -57l-31 -107q0 -3 -2 -12.5t-2 -15.5zM1435 310 q0 -28 17.5 -53t52.5 -32q1 0 11 -1.5t15 -1.5q70 0 87 66l30 110q10 40 -9 72t-57 39q-37 11 -68.5 -7.5t-41.5 -56.5l-32 -109q-5 -22 -5 -26zM1472 1366q107 103 261 103q156 0 266 -111.5t110 -267.5q0 -102 -51 -189q-154 154 -372 154h-35q-40 176 -179 311z" />
|
||||
<glyph unicode="" horiz-adv-x="2297" d="M-5 529q0 -213 149 -366.5t363 -163.5q20 0 20 18v143q0 18 -20 18q-137 8 -233.5 110t-96.5 241q0 133 91 231t225 114l56 4q20 0 20 19l8 58q17 173 147 290t305 117q174 0 304.5 -117t148.5 -290l8 -61q0 -20 19 -20h172q145 0 248 -101.5t103 -243.5 q0 -139 -96.5 -241t-233.5 -110q-21 0 -21 -18v-143q0 -18 21 -18q214 7 362.5 161.5t148.5 368.5q0 114 -46 214q126 154 126 354q0 152 -75.5 281.5t-204.5 204.5t-280 75q-248 0 -415 -185q-129 69 -289 69q-226 0 -401.5 -140.5t-225.5 -358.5q-177 -42 -292 -185.5 t-115 -328.5zM677 90q0 -35 26 -61q27 -27 62 -27q37 0 62.5 25t25.5 63t-25 63t-63 25t-63 -25t-25 -63zM677 -297q0 -38 26 -62q26 -26 62 -26q38 0 63 25t25 63t-25.5 64.5t-62.5 26.5t-62.5 -26.5t-25.5 -64.5zM1017 -112q0 -37 27 -65.5t62 -28.5q37 0 64.5 28t27.5 66 q0 35 -27.5 61.5t-64.5 26.5q-35 0 -62 -26.5t-27 -61.5zM1017 271q0 -36 26 -62t63 -26q38 0 65 25.5t27 62.5t-27 63.5t-65 26.5q-36 0 -62.5 -26.5t-26.5 -63.5zM1017 -504q0 -37 26.5 -64t62.5 -27q38 0 65 27t27 64q0 35 -27.5 61.5t-64.5 26.5q-35 0 -62 -26.5 t-27 -61.5zM1362 90q0 -32 27 -61q27 -27 64 -27t62.5 25t25.5 63t-25 63t-63 25t-64.5 -25t-26.5 -63zM1362 -297q0 -35 27 -62q26 -26 64 -26t63 25t25 63t-25.5 64.5t-62.5 26.5t-64 -27t-27 -64zM1467 1369q108 108 266 108q157 0 266.5 -111t109.5 -269q0 -94 -54 -196 q-154 154 -374 154h-33q-47 186 -181 314z" />
|
||||
<glyph unicode="" horiz-adv-x="2297" d="M-2 529q0 -213 148.5 -366.5t363.5 -163.5q18 0 18 18v146q0 20 -18 20q-140 10 -235.5 110t-95.5 236q0 135 91 234.5t225 110.5l56 7q20 0 20 20l7 53q17 173 147 290t306 117q175 0 305 -116.5t149 -290.5l6 -61q0 -19 20 -19h171q145 0 248 -101.5t103 -243.5 q0 -136 -95 -236t-234 -110q-22 0 -22 -20v-146q0 -18 22 -18q213 7 361.5 161.5t148.5 368.5q0 118 -46 214q126 153 126 358q0 151 -75.5 280.5t-204.5 205t-280 75.5q-244 0 -419 -187q-132 67 -284 67q-226 0 -401.5 -140t-224.5 -359q-177 -42 -292 -185.5t-115 -328.5 zM631 435q0 -60 45.5 -104t108.5 -44t106.5 43t43.5 105q0 41 -37.5 109t-70.5 107q-3 4 -19.5 21.5t-22.5 25.5l-38 -43q-44 -48 -80 -112.5t-36 -107.5zM950 10q0 -101 73.5 -175t175.5 -74q103 0 177 74t74 175q0 43 -25 103t-60 111q-74 95 -138 159q-9 7 -28 26 l-62 -62q-71 -68 -129 -166.5t-58 -170.5zM1107 725q0 -43 30 -71.5t76 -28.5q41 0 70 28.5t29 71.5q0 65 -99 172l-27 -28q-30 -32 -54.5 -74.5t-24.5 -69.5zM1469 1369q107 106 265 106q156 0 266 -109t110 -265q0 -101 -54 -197q-160 154 -374 154h-33q-45 188 -180 311z " />
|
||||
<glyph unicode="" horiz-adv-x="2297" d="M-1 533q0 -176 106 -317.5t274 -191.5l-68 -177q-7 -22 15 -22h226l-122 -436h31l435 586q6 7 1.5 15t-15.5 8h-232l264 498q12 23 -15 23h-315q-16 0 -21 -15l-117 -312q-114 29 -189.5 125t-75.5 216q0 134 90.5 233.5t224.5 111.5l56 7q21 0 21 18l8 55 q17 174 146.5 290.5t305.5 116.5q175 0 305 -116.5t149 -290.5l7 -62q0 -18 19 -18h173q145 0 248 -101.5t103 -243.5q0 -137 -96 -237.5t-235 -110.5q-20 0 -20 -19v-147q0 -18 20 -18q213 7 361.5 162t148.5 370q0 114 -45 213q123 161 123 351q0 114 -45 217.5 t-120 178.5t-179 119.5t-217 44.5q-118 0 -226 -48t-186 -134q-131 70 -288 70q-227 0 -402 -139.5t-225 -359.5q-177 -41 -292 -185t-115 -328zM871 -520q0 -28 17 -54.5t49 -32.5q1 0 11 -1.5t15 -1.5q69 5 86 65l31 113q10 35 -9.5 67.5t-56.5 43.5q-35 10 -67.5 -9 t-43.5 -57l-28 -107q-4 -20 -4 -26zM1014 -11q0 -24 16.5 -48t49.5 -36q35 -12 67 5.5t43 56.5l32 111q10 40 -8.5 72t-56.5 39q-36 11 -69 -7.5t-45 -56.5l-26 -109q0 -3 -1 -8.5t-1.5 -10t-0.5 -8.5zM1315 -184q0 -30 17.5 -55.5t50.5 -34.5q25 -5 28 -5q64 0 83 64 l26 113q11 35 -6 67t-53 44q-40 10 -73.5 -9.5t-40.5 -56.5l-29 -106q-3 -12 -3 -21zM1461 322q0 -26 17 -50.5t52 -36.5q27 -3 28 -3q19 0 40 9q31 16 42 56l31 111q10 35 -9.5 67.5t-56.5 43.5q-36 11 -68 -7.5t-43 -56.5l-30 -109q-3 -14 -3 -24zM1472 1373 q106 102 261 102q157 0 267 -110.5t110 -267.5q0 -101 -52 -190q-158 154 -372 154h-35q-49 186 -179 312z" />
|
||||
<glyph unicode="" horiz-adv-x="2297" d="M2 529q0 -178 106 -318t274 -187l-69 -179q-8 -23 15 -23h226l-111 -447h30l423 600q6 7 1.5 15t-15.5 8h-232l264 492q11 23 -15 23h-314q-14 0 -24 -15l-114 -306q-114 28 -188.5 122t-74.5 215q0 133 89.5 230.5t223.5 113.5l57 4q20 0 20 19l8 57q17 173 146 289.5 t304 116.5q173 0 303 -117t148 -289l8 -60q0 -20 19 -20h172q142 0 245.5 -102t103.5 -242q0 -138 -96 -239.5t-232 -108.5q-21 0 -21 -19v-143q0 -18 21 -18q139 4 256 76.5t184 192.5t67 259q0 119 -44 214q124 146 124 353q0 150 -75 279t-203.5 204t-278.5 75 q-248 0 -413 -185q-129 69 -288 69q-225 0 -400 -139.5t-224 -357.5q-176 -41 -291 -184.5t-115 -327.5zM848 -525q0 -26 15.5 -49t48.5 -33q3 0 8.5 -1t10 -1.5t8.5 -0.5q67 0 85 64l259 948q10 37 -7 68.5t-52 42.5q-37 11 -69.5 -6.5t-42.5 -53.5l-261 -949 q-3 -27 -3 -29zM1290 -201q0 -26 16 -50t51 -35q3 0 8.5 -1t10 -1.5t8.5 -0.5q19 0 38 8q31 13 44 59l175 624q10 37 -8 68.5t-54 42.5q-37 11 -68.5 -6.5t-41.5 -53.5l-174 -628q-5 -23 -5 -26zM1469 1367q108 102 264 102q155 0 264.5 -109t109.5 -264q0 -103 -53 -197 q-154 154 -372 154h-34q-45 184 -179 314z" />
|
||||
<glyph unicode="" horiz-adv-x="1131" d="M1 1160q0 80 57 137t137 57q79 0 135.5 -57t56.5 -137t-56.5 -137t-135.5 -57t-136.5 57t-57.5 137zM95 1160q0 -41 30 -71t70 -30q41 0 71 30t30 71t-30 71t-71 30q-42 0 -71 -29.5t-29 -71.5zM510 688q0 -123 68 -219q35 -50 98.5 -79.5t144.5 -29.5q233 0 292 178 q6 23 -6.5 43.5t-35.5 24.5q-23 6 -43 -7t-25 -36q0 -1 -1.5 -5.5l-2.5 -7.5q-18 -30 -48 -48q-50 -30 -130 -30q-51 0 -90 17q-64 27 -91 95q-18 44 -18 104v344q0 24 3 48q6 61 48 111q47 56 148 56q82 0 130 -29q32 -19 48 -48q1 -3 2.5 -8.5t1.5 -6.5q6 -22 25 -32.5 t43 -5.5q23 5 35.5 24.5t6.5 42.5v1l-9 25q-8 18 -29.5 46t-48.5 47q-77 56 -205 56q-81 0 -144 -28.5t-98 -77.5q-69 -95 -69 -221v-344z" />
|
||||
<glyph unicode="" horiz-adv-x="2217" d="M3 531q0 -214 149 -367t363 -163q19 0 19 18v143q0 19 -19 19q-137 7 -233.5 109t-96.5 241q0 133 91 231.5t225 114.5l56 3q20 0 20 18l8 59q16 173 146 289.5t306 116.5q174 0 304.5 -116.5t148.5 -289.5l8 -62q0 -18 19 -18h172q142 0 246.5 -103t104.5 -243 q0 -139 -96.5 -241t-233.5 -109q-21 0 -21 -19v-143q0 -18 21 -18q213 7 361 161t148 369q0 144 -71 265t-193 190.5t-266 69.5h-34q-52 214 -224.5 351t-393.5 137q-226 0 -401.5 -140t-225.5 -359q-176 -41 -291.5 -185.5t-115.5 -328.5zM746 368q0 -37 27 -64l275 -278 q20 -25 62 -25q45 0 65 25l276 278q27 29 27 64q0 37 -26.5 62t-64.5 25t-66 -25l-120 -118v425q0 38 -26.5 63t-64.5 25t-63 -25t-25 -63v-425l-118 118q-28 25 -66 25q-40 0 -66 -24.5t-26 -62.5z" />
|
||||
<glyph unicode="" horiz-adv-x="2217" d="M6 527q0 -105 40 -200.5t108.5 -165t163 -112.5t199.5 -48q18 0 18 18v142q0 20 -18 20q-139 10 -234.5 110t-95.5 236q0 134 91 233.5t224 110.5l56 8q20 0 20 18l8 55q11 114 74 207.5t162.5 146t214.5 52.5q175 0 305 -115t148 -288l7 -61q0 -19 20 -19h172 q144 0 247 -103t103 -245q0 -136 -95 -236t-234 -110q-21 0 -21 -20v-142q0 -18 21 -18q214 10 361 161.5t147 364.5q0 143 -71 264.5t-193 192.5t-265 71h-34q-53 213 -225.5 350t-392.5 137q-225 0 -400.5 -141.5t-225.5 -360.5q-176 -41 -290.5 -185t-114.5 -328z M670 392q0 -119 59.5 -220.5t161 -161.5t219.5 -60q184 0 314.5 130.5t130.5 311.5q0 38 -26.5 64.5t-64.5 26.5t-66 -27t-28 -64q0 -108 -76 -184.5t-184 -76.5q-107 0 -183.5 77t-76.5 184q0 96 57.5 171t140.5 82l-45 -42q-24 -24 -24 -61q0 -40 24 -70q22 -24 57 -25 t75 25l194 199q26 26 26 62q0 41 -26 65l-194 195q-30 28 -66 28q-37 0 -63.5 -27t-26.5 -65t24 -62l38 -38q-160 -27 -265.5 -149t-105.5 -288z" />
|
||||
<glyph unicode="" horiz-adv-x="2217" d="M0 530q0 -213 148.5 -366t362.5 -163q18 0 18 18v142q0 20 -18 20q-137 7 -233 108.5t-96 240.5q0 132 90 230t224 114h56q21 0 21 19l6 61q17 173 147 290t305 117q173 0 303.5 -117t148.5 -290l8 -61q0 -19 19 -19h172q142 0 246 -102.5t104 -241.5q0 -138 -96.5 -240 t-232.5 -109q-21 0 -21 -20v-142q0 -18 21 -18q139 4 256 76t184.5 192.5t67.5 260.5q0 107 -42 204t-113 167t-169 111.5t-205 41.5h-34q-53 214 -225 350.5t-392 136.5q-226 0 -401 -140.5t-224 -358.5q-177 -41 -291.5 -184.5t-114.5 -327.5zM741 463q0 -40 26 -68 q24 -24 65 -24q39 0 66 24l118 122v-424q0 -38 25 -65t63 -27q37 0 64 27t27 65v420l120 -118q64 -49 130 0q26 24 26 66q0 38 -26 66l-276 274q-26 26 -64 26q-39 0 -63 -26l-275 -274q-26 -26 -26 -64z" />
|
||||
<glyph unicode="" horiz-adv-x="2217" d="M3 531q0 -214 149 -367t363 -163q19 0 19 18v143q0 19 -19 19q-137 7 -233.5 109t-96.5 241q0 133 91 231.5t225 114.5h56q20 0 20 18l8 62q16 173 146 289.5t306 116.5q174 0 304.5 -116.5t148.5 -289.5l8 -62q0 -18 19 -18h172q142 0 246.5 -103t104.5 -243 q0 -139 -96.5 -241t-233.5 -109q-21 0 -21 -19v-143q0 -18 21 -18q213 7 361 161t148 369q0 144 -71 265t-193 190.5t-266 69.5h-34q-52 214 -224.5 351t-393.5 137q-226 0 -401.5 -140t-225.5 -359q-176 -41 -291.5 -185.5t-115.5 -328.5z" />
|
||||
<glyph unicode="" horiz-adv-x="377" d="M-3 1349q0 80 57 137t137 57q79 0 135.5 -57t56.5 -137t-56.5 -137.5t-135.5 -57.5t-136.5 57.5t-57.5 137.5zM90 1349q0 -41 30 -71t71 -30t70.5 30t29.5 71t-29.5 70.5t-70.5 29.5q-42 0 -71.5 -29t-29.5 -71z" />
|
||||
<glyph unicode="" horiz-adv-x="685" d="M6 542q0 -31 20.5 -51.5t50.5 -20.5h306q30 0 50.5 20.5t20.5 51.5q0 30 -20.5 49.5t-50.5 19.5h-133l415 414q18 21 18 53q0 30 -18 49q-20 19 -54 19q-31 0 -50 -19l-412 -413v133q0 31 -20.5 51.5t-51.5 20.5q-30 0 -50.5 -21t-20.5 -51v-305z" />
|
||||
<glyph unicode="" horiz-adv-x="685" d="M-6 574q0 -34 28 -59l256 -261q25 -25 61 -25q38 0 63 25l256 261q27 24 27 59q0 36 -24.5 60.5t-60.5 24.5t-61 -25l-113 -112v703q0 36 -25.5 60t-61.5 24t-61 -24t-25 -60v-703l-114 112q-25 25 -59 25q-36 0 -61 -24.5t-25 -60.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1131" d="M3 1157q0 79 58 137q57 57 137 57q79 0 135.5 -57t56.5 -137t-56.5 -137.5t-135.5 -57.5q-80 0 -137.5 58t-57.5 137zM97 1157q0 -41 30 -71t71 -30t70.5 30t29.5 71t-29.5 70.5t-70.5 29.5q-42 0 -71.5 -29t-29.5 -71zM568 422q0 -23 16.5 -39.5t39.5 -16.5t39.5 16.5 t16.5 39.5v404h305q23 0 39.5 17t16.5 40q0 24 -16 40t-40 16h-305v291h408q23 0 38.5 16.5t15.5 39.5t-15.5 39.5t-38.5 16.5h-509q-11 0 -11 -12v-908z" />
|
||||
<glyph unicode="" horiz-adv-x="2285" d="M-13 635q0 42 29 66q23 24 66 24h222q39 0 64.5 -25.5t25.5 -64.5q0 -38 -26 -65t-64 -27h-222q-39 0 -67 27t-28 65zM87 93q0 38 27 65q26 25 65 25h1942q39 0 66.5 -26t27.5 -64t-28 -65.5t-66 -27.5h-1942q-38 0 -65 27.5t-27 65.5zM304 1391q0 41 25 63q27 29 66 29 t66 -29l154 -156q27 -26 27 -64q0 -40 -26 -65.5t-64 -25.5q-35 0 -64 26l-159 157q-25 22 -25 65zM582 635q0 -139 57 -249q5 -15 25 -15h178q12 0 14.5 6.5t-7.5 17.5q-84 103 -84 240q0 159 114 271t272 112q159 0 271.5 -112t112.5 -271q0 -136 -86 -240q-6 -10 -6 -13 q-1 -5 3 -8t11 -3h182q14 0 22 15q59 111 59 249q0 153 -76.5 284.5t-208 208.5t-284.5 77t-284.5 -77t-208 -208.5t-76.5 -284.5zM1058 1481v223q0 38 27.5 66t65.5 28t66 -28t28 -66v-223q0 -38 -28 -66t-66 -28t-65.5 28t-27.5 66zM1658 1234q0 38 27 64l154 156 q27 29 65 29q40 0 67 -26.5t27 -65.5q0 -41 -24 -65l-161 -157q-26 -24 -63 -24q-40 -1 -66 24t-26 65zM1906 635q0 42 27 66q23 24 62 24h223q39 0 66.5 -25.5t27.5 -64.5q0 -38 -28 -65t-66 -27h-223q-37 0 -63 27t-26 65z" />
|
||||
<glyph unicode="" horiz-adv-x="2285" d="M73 94q0 42 30 66q22 25 67 25h1956q40 0 68 -26t28 -65q0 -38 -28 -66.5t-68 -28.5h-1956q-40 0 -68.5 28.5t-28.5 66.5zM283 978q0 41 26 67q27 28 68 28q37 0 64 -28l160 -160q29 -29 29 -67q0 -37 -29 -66q-24 -29 -64 -28.5t-68 28.5l-160 160q-26 25 -26 66z M597 382q-7 -25 16 -25h158q11 0 24 18q48 102 142 162t209 60q116 0 212 -60t144 -162q13 -18 24 -18h159q20 0 16 25q-54 180 -208 293.5t-347 113.5q-192 0 -343.5 -113t-205.5 -294zM1055 1071v228q0 40 26 68t65 28q40 0 67 -27.5t27 -68.5v-228q0 -41 -27 -68t-67 -27 q-39 0 -65 27.5t-26 67.5zM1665 818q0 37 28 67l163 160q25 28 66 28q39 0 64 -27t25 -68q0 -42 -25 -66l-157 -160q-27 -27 -61 -30q-36 -4 -69.5 26t-33.5 70z" />
|
||||
<glyph unicode="" horiz-adv-x="1085" d="M6 734q0 -38 25 -63l262 -256q23 -26 58 -26q36 0 61 24t25 60q0 35 -26 61l-112 112h703q36 0 60 25.5t24 61.5t-24 61.5t-60 26.5h-703l112 113q26 26 26 59q0 36 -25 61t-61 25q-33 0 -58 -28l-262 -256q-25 -25 -25 -61z" />
|
||||
<glyph unicode="" horiz-adv-x="2285" d="M-288 99q0 -36 27 -61t66 -25h1985q38 0 64.5 25t26.5 61q0 39 -26 65.5t-65 26.5h-1985q-38 0 -65.5 -27t-27.5 -65zM-10 431q0 -38 28 -63q22 -29 63 -29h1985q37 0 62 27t25 65q0 37 -25 62t-62 25h-1985q-38 0 -64.5 -25t-26.5 -62zM6 689v5q-7 -24 13 -24h154 q9 0 20 16q38 83 113.5 136t165.5 60l57 8q20 0 20 19l6 53q17 172 145.5 287.5t302.5 115.5q172 0 301 -114t147 -285l7 -61q0 -19 22 -19h169q103 0 187 -55t125 -145q11 -16 23 -16h151q19 0 16 24q-35 89 -49 113q116 104 160 274l18 70q4 10 -1.5 17t-12.5 7l-66 23 q-143 41 -217.5 169t-39.5 273l14 62q8 15 -14 25l-90 25q-175 43 -343 -28t-263 -226q-127 49 -244 49q-222 0 -395.5 -140t-223.5 -357q-135 -32 -236.5 -129t-141.5 -232zM175 -226q0 -38 28 -63q27 -29 63 -29h1989q37 0 63 27t26 65q0 37 -26 62t-63 25h-1989 q-38 0 -64.5 -25t-26.5 -62zM1402 1403q56 92 150.5 142t199.5 40q-11 -167 75 -312.5t237 -215.5q-24 -56 -80 -114q-147 121 -335 121h-34q-51 210 -213 339z" />
|
||||
<glyph unicode="" horiz-adv-x="685" d="M-2 741q0 -192 134 -326.5t326 -134.5q30 0 51.5 23t21.5 54t-21 51.5t-52 20.5q-130 0 -220.5 91t-90.5 221q0 124 85.5 216t200.5 92l-35 -34q-24 -23 -22 -52q0 -29 21 -51.5t52 -22.5q32 0 54 22l161 160q21 17 21 54q0 35 -21 50l-161 163q-21 22 -53 22 q-31 0 -53 -22t-22 -53q0 -33 23 -54l32 -30q-184 -13 -308 -144.5t-124 -315.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1108" d="M-7 674q0 -151 75.5 -279.5t204 -203t278.5 -74.5q153 0 281.5 74t202.5 202t74 281q0 38 -25.5 65t-62.5 27t-64.5 -27t-27.5 -65q0 -157 -110.5 -267.5t-267.5 -110.5q-155 0 -264 110.5t-109 267.5q0 136 84.5 246.5t196.5 117.5l-41 -39q-26 -28 -26 -62 q0 -36 26 -64q58 -56 129 0l196 194q26 20 26 66q0 41 -26 61l-196 198q-28 26 -61 26q-40 0 -67 -26.5t-27 -64.5q0 -39 26 -65l41 -38q-200 -35 -333 -191.5t-133 -358.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1085" d="M0 799q0 -36 24 -61t59 -21h703l-111 -115q-25 -25 -25 -59q0 -36 25.5 -61t61.5 -25q34 1 57 29l261 257q25 25 25 62q-2 37 -26 62l-263 255q-24 26 -58 25q-36 0 -61 -24t-25 -60t25 -61l113 -115h-703q-36 -1 -59.5 -26.5t-22.5 -61.5z" />
|
||||
<glyph unicode="" horiz-adv-x="857" d="M2 1027q0 41 40.5 108t77.5 111q10 12 25 29.5t17 19.5l39 -46q44 -49 81 -114t37 -108q0 -66 -45.5 -113t-111.5 -47t-113 47t-47 113zM335 584q0 46 26 107.5t64 113.5q31 41 75 91.5t66 71.5q4 2 27 26l28 -26q59 -55 140 -162q41 -55 67 -115t26 -107 q0 -109 -75.5 -184t-185.5 -75q-108 0 -183 76t-75 183zM497 1320q0 68 108 181l26 -30q30 -35 54.5 -78.5t24.5 -72.5q0 -44 -30.5 -73.5t-74.5 -29.5q-46 0 -77 29.5t-31 73.5z" />
|
||||
<glyph unicode="" d="M6 521q0 -40 29 -66q26 -30 68 -30h1567q46 0 77.5 -32t31.5 -79q0 -46 -31 -76.5t-78 -30.5t-78 31q-26 28 -64 28q-40 0 -68.5 -27.5t-28.5 -65.5q0 -40 30 -67q90 -88 209 -88q125 0 214 86t89 210t-89.5 213.5t-213.5 89.5h-1567q-40 0 -68.5 -28t-28.5 -68zM6 871 q0 -38 29 -65q28 -28 68 -28h2138q125 0 214.5 87t89.5 210q0 124 -89.5 212t-214.5 88q-123 0 -208 -85q-30 -27 -30 -71q0 -41 27.5 -67t67.5 -26t68 26q30 33 75 33q46 0 77.5 -32t31.5 -78t-31.5 -77t-77.5 -31h-2138q-40 0 -68.5 -28t-28.5 -68z" />
|
||||
<glyph unicode="" d="M-40 693q0 40 32 74q36 31 75 31h248q43 0 72 -31t29 -74q0 -46 -29 -77t-72 -31h-248q-43 0 -75 32.5t-32 75.5zM315 1544q0 43 28 73q36 31 75 31q42 0 73 -31l175 -176q30 -35 30 -74q0 -45 -29 -75t-70 -30q-39 0 -75 31l-179 173q-28 34 -28 78zM528 104q0 45 31 73 q28 28 73 28h281l332 312q16 13 36 0l337 -312h295q43 0 73.5 -29.5t30.5 -71.5q0 -43 -30.5 -74t-73.5 -31h-361q-16 0 -31 8l-257 242l-255 -242q-13 -8 -30 -8h-347q-43 0 -73.5 31t-30.5 74zM627 693q0 -155 66 -282q4 -19 27 -19h201q12 0 16 9t-2 20 q-102 123 -102 272q0 180 128 306t308 126q179 0 305.5 -126t126.5 -306q0 -150 -101 -272q-7 -11 -3.5 -20t16.5 -9h203q21 0 25 19q66 123 66 282q0 130 -51 248t-136.5 203.5t-203.5 136t-247 50.5q-130 0 -248.5 -50.5t-205 -136t-137.5 -203.5t-51 -248zM1163 1648v245 q0 46 30 75.5t76 29.5q45 0 74.5 -30t29.5 -75v-245q0 -46 -29.5 -75.5t-74.5 -29.5q-46 0 -76 29.5t-30 75.5zM1840 1367q0 40 29 74l173 176q31 31 75 31t74 -30.5t30 -73.5q0 -46 -28 -78l-180 -173q-34 -31 -75 -31q-43 0 -70.5 29.5t-27.5 75.5zM2119 693q0 43 28.5 74 t70.5 31h251q43 0 74 -31t31 -74q0 -44 -31 -76t-74 -32h-251q-43 0 -71 31t-28 77z" />
|
||||
<glyph unicode="" d="M-22 681q0 44 32 72q28 28 73 28h246q43 0 71.5 -28.5t28.5 -71.5q0 -44 -28.5 -75t-71.5 -31h-246q-43 0 -74 31.5t-31 74.5zM329 1519q0 44 28 72q32 32 74 32q46 0 72 -32l174 -174q74 -70 0 -144q-30 -30 -70 -30q-35 0 -72 30l-178 174q-28 30 -28 72zM539 103 q0 42 32 74q29 29 72 29h343q17 0 30 -8l251 -237l255 237q11 8 31 8h352q43 0 73.5 -30t30.5 -73t-30.5 -73.5t-73.5 -30.5h-286l-334 -303q-20 -14 -36 0l-329 303h-277q-43 0 -73.5 30.5t-30.5 73.5zM639 681q0 -157 63 -280q8 -17 28 -17h199q13 0 16 8t-8 19 q-95 119 -95 270q0 176 126 299.5t304 123.5q176 0 301.5 -124t125.5 -299q0 -152 -94 -270q-11 -11 -8 -19t16 -8h201q20 0 25 17q66 125 66 280q0 127 -50.5 244.5t-135.5 202.5t-202 135.5t-245 50.5t-245 -50.5t-202 -135.5t-135.5 -202.5t-50.5 -244.5zM1168 1621v248 q0 43 30.5 73.5t73.5 30.5t73.5 -30.5t30.5 -73.5v-248q0 -43 -30.5 -73.5t-73.5 -30.5t-73.5 30.5t-30.5 73.5zM1838 1345q0 46 27 72l174 174q26 32 72 32q44 0 73.5 -30.5t29.5 -73.5q0 -44 -28 -72l-177 -174q-37 -30 -74 -30q-42 0 -69.5 29.5t-27.5 72.5zM2113 681 q0 43 28.5 71.5t71.5 28.5h246q43 0 73.5 -29t30.5 -71q0 -43 -30.5 -74.5t-73.5 -31.5h-246q-43 0 -71.5 31t-28.5 75z" />
|
||||
<glyph unicode="" horiz-adv-x="685" d="M-1 393q0 86 39.5 160.5t111.5 124.5v668q0 81 55.5 137t136.5 56t137 -56t56 -137v-668q71 -50 110.5 -124.5t39.5 -160.5q0 -143 -100.5 -243t-242.5 -100t-242.5 100t-100.5 243zM117 393q0 -94 66 -160.5t159 -66.5q94 0 161.5 67.5t67.5 159.5q0 63 -32.5 116.5 t-88.5 83.5l-19 9q-10 4 -10 20v724q0 32 -22.5 54t-56.5 22q-33 0 -56 -22t-23 -54v-724q0 -16 -9 -20l-20 -9q-55 -30 -86 -83t-31 -117z" />
|
||||
<glyph unicode="" horiz-adv-x="685" d="M153 401q0 -81 56 -137.5t134 -56.5q79 0 136 56.5t57 137.5q0 71 -49 126t-120 66v558q0 7 -7 14.5t-17 7.5q-22 0 -22 -22v-558q-70 -11 -119 -66t-49 -126z" />
|
||||
<glyph unicode="" horiz-adv-x="685" d="M-10 377q0 87 40.5 163t113.5 126v681q0 82 56 139.5t138 57.5q83 0 140 -57.5t57 -139.5v-681q72 -51 112.5 -126.5t40.5 -162.5q0 -145 -102.5 -247.5t-247.5 -102.5t-246.5 102.5t-101.5 247.5zM110 377q0 -96 67 -164t161 -68q97 0 166 68.5t69 163.5q0 64 -33 118 t-91 84l-19 11q-11 5 -11 19v738q0 32 -23.5 54.5t-57.5 22.5q-32 0 -55.5 -22.5t-23.5 -54.5v-738q0 -15 -10 -19l-19 -11q-57 -30 -88.5 -83.5t-31.5 -118.5zM170 381q0 -72 49 -122.5t119 -50.5t121 50.5t51 122.5q0 64 -44 113t-106 57v495q0 6 -7 12.5t-15 6.5 q-19 0 -19 -19v-495q-62 -8 -105.5 -57t-43.5 -113z" />
|
||||
<glyph unicode="" horiz-adv-x="1485" d="M5 718q0 -71 74 -121q135 -95 408 -95q122 0 227 21q111 24 181.5 75.5t70.5 119.5q0 21 -8 42q142 35 223 94.5t81 136.5q0 19 -6 41q233 83 233 224q0 90 -102 160q-200 133 -586 133q-181 0 -329 -32q-159 -33 -258 -102.5t-99 -158.5q0 -51 34 -97 q-122 -69 -122 -168q0 -76 76 -135q-98 -57 -98 -138zM46 411q0 -75 92 -117.5t232 -42.5q142 0 235 42.5t93 117.5q0 28 -18 47t-46 19q-23 0 -41 -16t-23 -39q-20 -16 -75 -29.5t-125 -13.5q-113 0 -183 32q16 16 17.5 41t-10.5 42q-16 22 -41.5 27t-47.5 -9 q-59 -40 -59 -101zM133 718q1 2 12 12q9 9 38.5 23t65.5 24l8 4q166 -55 388 -55q97 0 176 11l18 -16q-9 -16 -44 -34q-41 -22 -126.5 -39t-181.5 -17t-182.5 17t-128.5 39q-34 15 -43 31zM141 143q0 -65 73.5 -100t182.5 -35q111 0 185.5 35t74.5 100q0 26 -19 46t-45 20 q-48 0 -63 -49q-40 -25 -133 -25q-83 0 -132 25q-15 49 -63 49q-26 0 -43.5 -19t-17.5 -47zM154 991q0 6 11 19q25 31 97 60q196 -105 539 -105q175 0 329 32v-6q0 -16 -18 -31q-42 -37 -157 -70q-124 -37 -310 -37q-188 0 -312 37q-119 33 -159 70q-20 16 -20 31zM243 1256 q0 18 24 40q46 43 183 83q146 43 351 43q206 0 354 -43q138 -40 183 -83q23 -23 23 -40t-23 -38q-45 -43 -183 -84q-148 -43 -354 -43q-205 0 -351 43q-138 40 -183 84q-24 21 -24 38z" />
|
||||
<glyph unicode="" horiz-adv-x="685" d="M5 557q0 -31 18 -50q20 -19 54 -19q31 0 50 19l411 413v-133q0 -31 20.5 -51.5t51.5 -20.5q30 0 50.5 21t20.5 51v306q0 31 -20.5 51.5t-50.5 20.5h-306q-30 0 -50.5 -20.5t-20.5 -51.5q0 -30 20.5 -50t50.5 -20h133l-414 -414q-18 -21 -18 -52z" />
|
||||
<glyph unicode="" horiz-adv-x="685" d="M-2 1013q-1 -36 23 -61.5t60 -24.5q36 -2 61 25l113 112v-703q-1 -36 24.5 -60t62.5 -23q35 -2 61 22t27 60v703l112 -112q24 -25 59 -25q37 0 62 24.5t25 60.5q-2 37 -27 58l-255 262q-25 24 -63 27q-38 -2 -63 -28l-256 -260q-26 -22 -26 -57z" />
|
||||
<glyph unicode="" horiz-adv-x="1542" d="M10 769q0 156 60.5 298.5t163 244.5t244 163t297.5 61q157 0 299 -61t245 -163.5t164 -244.5t61 -297q0 -156 -61 -298.5t-164 -245.5t-245 -163.5t-298 -60.5q-155 0 -297 60.5t-244.5 163t-163.5 244.5t-61 299zM178 769q0 -162 80.5 -299.5t218.5 -217t299 -79.5 q121 0 232 47.5t191.5 127.5t128 190.5t47.5 231.5q0 245 -177 422q-178 176 -423 176q-121 0 -231.5 -48t-190.5 -128t-127.5 -191t-47.5 -232zM502 521l89 239q5 10 0 21l-89 235q-5 10 0.5 16t16.5 1l599 -252q10 -1 10 -11q0 -9 -10 -10l-599 -257q-11 -4 -16.5 1.5 t-0.5 16.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1542" d="M26 968q22 82 64 161t108.5 154t149 131.5t193 90.5t233.5 34q319 0 544 -226q148 -148 201.5 -346t0.5 -397q-22 -82 -63.5 -161t-107.5 -154t-148 -131.5t-193 -91t-234 -34.5q-320 0 -545 226q-148 147 -202 346t-1 398zM173 769q0 -249 176 -425t425 -176 q122 0 233 47.5t191.5 128t128 191.5t47.5 234q0 163 -80.5 301t-218.5 218.5t-301 80.5q-123 0 -234 -47.5t-191.5 -128t-128 -191.5t-47.5 -233zM392 761q0 7 11 10l605 250q11 2 16 -3.5t1 -16.5l-248 -604q-2 -11 -10 -11q-11 0 -16 11l-103 230q-9 14 -15 15l-230 104 q-11 4 -11 15z" />
|
||||
<glyph unicode="" horiz-adv-x="1542" d="M26 568.5q-53 198.5 0 396.5q22 82 63.5 160.5t107.5 153t148 131t192.5 90.5t233.5 34q317 0 542 -225q148 -147 201 -345t0 -396q-22 -82 -63 -160.5t-107 -153.5t-148 -131.5t-192 -90.5t-233 -34q-319 0 -544 225q-148 147 -201 345.5zM171 767q0 -249 176 -424 q175 -176 424 -176q162 0 299.5 80.5t218 218.5t80.5 301q0 162 -80.5 299.5t-218 218t-299.5 80.5q-163 0 -301 -80.5t-218.5 -218t-80.5 -299.5zM519 998q-2 11 3.5 17t15.5 2l603 -249q11 -2 11 -10q0 -9 -11 -14l-230 -104q-11 -4 -15 -15l-104 -230q-5 -11 -14 -11 q-8 0 -10 11z" />
|
||||
<glyph unicode="" horiz-adv-x="1542" d="M2 765q0 209 103.5 386.5t280.5 280.5t385 103q156 0 298 -61t245 -164t164 -245.5t61 -299.5q0 -156 -61 -298.5t-164 -245t-245 -163t-298 -60.5q-157 0 -299.5 61t-245.5 164t-163.5 244.5t-60.5 297.5zM171 765q0 -242 178 -422q177 -177 422 -177q162 0 300 80.5 t219 218.5t81 300t-81 300.5t-219 219t-300 80.5t-300 -80.5t-219 -219t-81 -300.5zM505 507l256 600q2 11 10 11q9 0 11 -11l255 -600q4 -11 -2 -17t-17 0l-236 89q-10 5 -21 0l-239 -89q-10 -6 -16 0t-1 17z" />
|
||||
<glyph unicode="" horiz-adv-x="1542" d="M8.5 572q-53.5 200 0.5 397q22 82 63.5 161t108 154.5t149.5 132t194 91t234 34.5q318 0 544 -228q148 -148 201.5 -346t0.5 -397q-22 -82 -63.5 -161t-108 -154.5t-148.5 -132t-192.5 -91t-233.5 -34.5q-322 0 -547 226q-149 148 -202.5 348zM155 770q0 -250 176 -426 t427 -176q162 0 300.5 80.5t219 219t80.5 302.5q0 122 -47.5 233t-128 191.5t-191.5 128t-233 47.5q-164 0 -303 -80.5t-219.5 -219t-80.5 -300.5zM375 777q0 10 10 14l231 104q10 5 15 15l103 229q9 12 16 12q8 0 11 -12l248 -605q2 -11 -2 -15t-16 -2l-606 248 q-10 3 -10 12z" />
|
||||
<glyph unicode="" horiz-adv-x="1542" d="M30.5 573q-53.5 200 0.5 397q22 82 63.5 161t107.5 154t148.5 131.5t193.5 91t234 34.5q318 0 543 -228q149 -147 202.5 -345.5t-0.5 -395.5q-22 -82 -63 -161.5t-107.5 -154.5t-148.5 -132t-192.5 -91.5t-233.5 -34.5q-322 0 -545 227q-149 147 -202.5 347zM176 771 q0 -249 176 -426q175 -175 426 -175q162 0 300 80t218.5 218.5t80.5 302.5q0 162 -80.5 300.5t-218.5 219t-300 80.5q-164 0 -302.5 -80.5t-219 -219t-80.5 -300.5zM525 536l250 605q1 10 10 10q10 0 14 -10l104 -231q2 -9 16 -14l230 -104q11 -4 11 -14q0 -9 -11 -12 l-605 -248q-10 -4 -15 1t-4 17z" />
|
||||
<glyph unicode="" horiz-adv-x="1542" d="M6 766q0 156 61.5 298.5t164.5 245.5t245 163.5t298 60.5t298.5 -60.5t245.5 -163.5t164 -245t61 -299t-61 -299t-164 -244.5t-245.5 -163t-298.5 -60.5t-298.5 61t-245.5 164t-164 245t-61 297zM176 766q0 -245 177 -422q176 -176 422 -176q163 0 301.5 80.5t219 218 t80.5 299.5q0 121 -47.5 232t-128.5 191.5t-192 128t-233 47.5q-121 0 -231.5 -47.5t-191 -128t-128.5 -191.5t-48 -232zM510 1025q-5 11 1 16.5t16 0.5l238 -89q10 -4 22 0l236 89q10 5 16 -0.5t2 -16.5l-254 -599q-3 -10 -12 -10q-7 0 -10 10z" />
|
||||
<glyph unicode="" horiz-adv-x="1542" d="M6 769q0 157 61.5 300t165 246.5t246 164.5t298.5 61t298.5 -61t246 -164.5t164.5 -246.5t61 -300t-61 -300t-164.5 -245.5t-246 -163.5t-298.5 -61q-157 0 -300 61t-246 164.5t-164 246t-61 298.5zM176 769q0 -246 178 -424q177 -177 423 -177q163 0 301.5 81 t219.5 219.5t81 300.5t-81 300.5t-219.5 219.5t-301.5 81q-162 0 -300.5 -81t-219.5 -219.5t-81 -300.5zM424 769q0 9 11 10l602 258q11 5 16.5 -1.5t0.5 -16.5l-89 -240q-7 -10 0 -20l89 -238q5 -10 -0.5 -16t-16.5 -1l-602 255q-11 1 -11 10z" />
|
||||
</font>
|
||||
</defs></svg>
|
||||
|
Before Width: | Height: | Size: 94 KiB |
Binary file not shown.
Binary file not shown.
12
fonts/package-lock.json
generated
Normal file
12
fonts/package-lock.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "magicmirror-fonts",
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"roboto-fontface": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.10.0.tgz",
|
||||
"integrity": "sha512-OlwfYEgA2RdboZohpldlvJ1xngOins5d7ejqnIBWr9KaMxsnBqotpptRXTyfNRLnFpqzX6sTDt+X+a+6udnU8g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
15
fonts/package.json
Normal file
15
fonts/package.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "magicmirror-fonts",
|
||||
"description": "Package for fonts use by MagicMirror Core.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/MichMich/MagicMirror.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/MichMich/MagicMirror/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"roboto-fontface": "^0.10.0"
|
||||
}
|
||||
}
|
||||
58
fonts/roboto.css
Normal file
58
fonts/roboto.css
Normal file
@@ -0,0 +1,58 @@
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
font-style: normal;
|
||||
font-weight: 100;
|
||||
src: local("Roboto Thin"), local("Roboto-Thin"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Thin.woff2") format("woff2"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Thin.woff") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Roboto Condensed";
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local("Roboto Condensed Light"), local("RobotoCondensed-Light"), url("node_modules/roboto-fontface/fonts/roboto-condensed/Roboto-Condensed-Light.woff2") format("woff2"),
|
||||
url("node_modules/roboto-fontface/fonts/roboto-condensed/Roboto-Condensed-Light.woff") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Roboto Condensed";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local("Roboto Condensed"), local("RobotoCondensed-Regular"), url("node_modules/roboto-fontface/fonts/roboto-condensed/Roboto-Condensed-Regular.woff2") format("woff2"),
|
||||
url("node_modules/roboto-fontface/fonts/roboto-condensed/Roboto-Condensed-Regular.woff") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Roboto Condensed";
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local("Roboto Condensed Bold"), local("RobotoCondensed-Bold"), url("node_modules/roboto-fontface/fonts/roboto-condensed/Roboto-Condensed-Bold.woff2") format("woff2"),
|
||||
url("node_modules/roboto-fontface/fonts/roboto-condensed/Roboto-Condensed-Bold.woff") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local("Roboto"), local("Roboto-Regular"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Regular.woff2") format("woff2"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Regular.woff") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local("Roboto Medium"), local("Roboto-Medium"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Medium.woff2") format("woff2"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Medium.woff") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local("Roboto Bold"), local("Roboto-Bold"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Bold.woff2") format("woff2"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Bold.woff") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Roboto;
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local("Roboto Light"), local("Roboto-Light"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Light.woff2") format("woff2"), url("node_modules/roboto-fontface/fonts/roboto/Roboto-Light.woff") format("woff");
|
||||
}
|
||||
55
index.html
Normal file
55
index.html
Normal file
@@ -0,0 +1,55 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>MagicMirror²</title>
|
||||
<meta name="google" content="notranslate" />
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
|
||||
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
|
||||
<link rel="stylesheet" type="text/css" href="css/main.css">
|
||||
<link rel="stylesheet" type="text/css" href="fonts/roboto.css">
|
||||
<!-- custom.css is loaded by the loader.js to make sure it's loaded after the module css files. -->
|
||||
|
||||
<script type="text/javascript">
|
||||
var version = "#VERSION#";
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="region fullscreen below"><div class="container"></div></div>
|
||||
<div class="region top bar">
|
||||
<div class="container"></div>
|
||||
<div class="region top left"><div class="container"></div></div>
|
||||
<div class="region top center"><div class="container"></div></div>
|
||||
<div class="region top right"><div class="container"></div></div>
|
||||
</div>
|
||||
<div class="region upper third"><div class="container"></div></div>
|
||||
<div class="region middle center"><div class="container"></div></div>
|
||||
<div class="region lower third"><div class="container"><br/></div></div>
|
||||
<div class="region bottom bar">
|
||||
<div class="container"></div>
|
||||
<div class="region bottom left"><div class="container"></div></div>
|
||||
<div class="region bottom center"><div class="container"></div></div>
|
||||
<div class="region bottom right"><div class="container"></div></div>
|
||||
</div>
|
||||
<div class="region fullscreen above"><div class="container"></div></div>
|
||||
<script type="text/javascript" src="socket.io/socket.io.js"></script>
|
||||
<script type="text/javascript" src="vendor/node_modules/nunjucks/browser/nunjucks.min.js"></script>
|
||||
<script type="text/javascript" src="js/defaults.js"></script>
|
||||
<script type="text/javascript" src="#CONFIG_FILE#"></script>
|
||||
<script type="text/javascript" src="vendor/vendor.js"></script>
|
||||
<script type="text/javascript" src="modules/default/defaultmodules.js"></script>
|
||||
<script type="text/javascript" src="js/logger.js"></script>
|
||||
<script type="text/javascript" src="translations/translations.js"></script>
|
||||
<script type="text/javascript" src="js/translator.js"></script>
|
||||
<script type="text/javascript" src="js/class.js"></script>
|
||||
<script type="text/javascript" src="js/module.js"></script>
|
||||
<script type="text/javascript" src="js/loader.js"></script>
|
||||
<script type="text/javascript" src="js/socketclient.js"></script>
|
||||
<script type="text/javascript" src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
39
index.php
39
index.php
@@ -1,39 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Magic Mirror</title>
|
||||
<style type="text/css">
|
||||
<?php include('css/main.css') ?>
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="css/weather-icons.css">
|
||||
<script type="text/javascript">
|
||||
var gitHash = '<?php echo trim(`git rev-parse HEAD`) ?>';
|
||||
</script>
|
||||
<meta name="google" value="notranslate" />
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="top left"><div class="date small dimmed"></div><div class="time"></div><div class="calendar xxsmall"></div></div>
|
||||
<div class="top right"><div class="windsun small dimmed"></div><div class="temp"></div><div class="forecast small dimmed"></div></div>
|
||||
<div class="center-ver center-hor"><!-- <div class="dishwasher light">Vaatwasser is klaar!</div> --></div>
|
||||
<div class="lower-third center-hor"><div class="compliment light"></div></div>
|
||||
<div class="bottom center-hor"><div class="news medium"></div></div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="js/jquery.js"></script>
|
||||
<script src="js/jquery.feedToJSON.js"></script>
|
||||
<script src="js/ical_parser.js"></script>
|
||||
<script src="js/moment-with-locales.min.js"></script>
|
||||
<script src="js/config.js"></script>
|
||||
<script src="js/rrule.js"></script>
|
||||
<script src="js/version/version.js"></script>
|
||||
<script src="js/calendar/calendar.js"></script>
|
||||
<script src="js/compliments/compliments.js"></script>
|
||||
<script src="js/weather/weather.js"></script>
|
||||
<script src="js/time/time.js"></script>
|
||||
<script src="js/news/news.js"></script>
|
||||
<script src="js/main.js?nocache=<?php echo md5(microtime()) ?>"></script>
|
||||
<!-- <script src="js/socket.io.min.js"></script> -->
|
||||
</body>
|
||||
</html>
|
||||
3
installers/mm.sh
Executable file
3
installers/mm.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
# This file is still here to keep PM2 working on older installations.
|
||||
cd ~/MagicMirror
|
||||
DISPLAY=:0 npm start
|
||||
297
js/app.js
Normal file
297
js/app.js
Normal file
@@ -0,0 +1,297 @@
|
||||
/* Magic Mirror
|
||||
* The Core App (Server)
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
var Log = require(__dirname + "/logger.js");
|
||||
var Server = require(__dirname + "/server.js");
|
||||
var Utils = require(__dirname + "/utils.js");
|
||||
var defaultModules = require(__dirname + "/../modules/default/defaultmodules.js");
|
||||
|
||||
// Alias modules mentioned in package.js under _moduleAliases.
|
||||
require("module-alias/register");
|
||||
|
||||
// Get version number.
|
||||
global.version = JSON.parse(fs.readFileSync("package.json", "utf8")).version;
|
||||
Log.log("Starting MagicMirror: v" + global.version);
|
||||
|
||||
// global absolute root path
|
||||
global.root_path = path.resolve(__dirname + "/../");
|
||||
|
||||
if (process.env.MM_CONFIG_FILE) {
|
||||
global.configuration_file = process.env.MM_CONFIG_FILE;
|
||||
}
|
||||
|
||||
// FIXME: Hotfix Pull Request
|
||||
// https://github.com/MichMich/MagicMirror/pull/673
|
||||
if (process.env.MM_PORT) {
|
||||
global.mmPort = process.env.MM_PORT;
|
||||
}
|
||||
|
||||
// The next part is here to prevent a major exception when there
|
||||
// is no internet connection. This could probable be solved better.
|
||||
process.on("uncaughtException", function (err) {
|
||||
Log.error("Whoops! There was an uncaught exception...");
|
||||
Log.error(err);
|
||||
Log.error("MagicMirror will not quit, but it might be a good idea to check why this happened. Maybe no internet connection?");
|
||||
Log.error("If you think this really is an issue, please open an issue on GitHub: https://github.com/MichMich/MagicMirror/issues");
|
||||
});
|
||||
|
||||
/**
|
||||
* The core app.
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
var App = function () {
|
||||
var nodeHelpers = [];
|
||||
|
||||
/**
|
||||
* Loads the config file. Combines it with the defaults, and runs the
|
||||
* callback with the found config as argument.
|
||||
*
|
||||
* @param {Function} callback Function to be called after loading the config
|
||||
*/
|
||||
var loadConfig = function (callback) {
|
||||
Log.log("Loading config ...");
|
||||
var defaults = require(__dirname + "/defaults.js");
|
||||
|
||||
// For this check proposed to TestSuite
|
||||
// https://forum.magicmirror.builders/topic/1456/test-suite-for-magicmirror/8
|
||||
var configFilename = path.resolve(global.root_path + "/config/config.js");
|
||||
if (typeof global.configuration_file !== "undefined") {
|
||||
configFilename = path.resolve(global.configuration_file);
|
||||
}
|
||||
|
||||
try {
|
||||
fs.accessSync(configFilename, fs.F_OK);
|
||||
var c = require(configFilename);
|
||||
checkDeprecatedOptions(c);
|
||||
var config = Object.assign(defaults, c);
|
||||
callback(config);
|
||||
} catch (e) {
|
||||
if (e.code === "ENOENT") {
|
||||
Log.error(Utils.colors.error("WARNING! Could not find config file. Please create one. Starting with default configuration."));
|
||||
} else if (e instanceof ReferenceError || e instanceof SyntaxError) {
|
||||
Log.error(Utils.colors.error("WARNING! Could not validate config file. Starting with default configuration. Please correct syntax errors at or above this line: " + e.stack));
|
||||
} else {
|
||||
Log.error(Utils.colors.error("WARNING! Could not load config file. Starting with default configuration. Error found: " + e));
|
||||
}
|
||||
callback(defaults);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks the config for deprecated options and throws a warning in the logs
|
||||
* if it encounters one option from the deprecated.js list
|
||||
*
|
||||
* @param {object} userConfig The user config
|
||||
*/
|
||||
var checkDeprecatedOptions = function (userConfig) {
|
||||
var deprecated = require(global.root_path + "/js/deprecated.js");
|
||||
var deprecatedOptions = deprecated.configs;
|
||||
|
||||
var usedDeprecated = [];
|
||||
|
||||
deprecatedOptions.forEach(function (option) {
|
||||
if (userConfig.hasOwnProperty(option)) {
|
||||
usedDeprecated.push(option);
|
||||
}
|
||||
});
|
||||
if (usedDeprecated.length > 0) {
|
||||
Log.warn(Utils.colors.warn("WARNING! Your config is using deprecated options: " + usedDeprecated.join(", ") + ". Check README and CHANGELOG for more up-to-date ways of getting the same functionality."));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads a specific module.
|
||||
*
|
||||
* @param {string} module The name of the module (including subpath).
|
||||
* @param {Function} callback Function to be called after loading
|
||||
*/
|
||||
var loadModule = function (module, callback) {
|
||||
var elements = module.split("/");
|
||||
var moduleName = elements[elements.length - 1];
|
||||
var moduleFolder = __dirname + "/../modules/" + module;
|
||||
|
||||
if (defaultModules.indexOf(moduleName) !== -1) {
|
||||
moduleFolder = __dirname + "/../modules/default/" + module;
|
||||
}
|
||||
|
||||
var helperPath = moduleFolder + "/node_helper.js";
|
||||
|
||||
var loadModule = true;
|
||||
try {
|
||||
fs.accessSync(helperPath, fs.R_OK);
|
||||
} catch (e) {
|
||||
loadModule = false;
|
||||
Log.log("No helper found for module: " + moduleName + ".");
|
||||
}
|
||||
|
||||
if (loadModule) {
|
||||
var Module = require(helperPath);
|
||||
var m = new Module();
|
||||
|
||||
if (m.requiresVersion) {
|
||||
Log.log("Check MagicMirror version for node helper '" + moduleName + "' - Minimum version: " + m.requiresVersion + " - Current version: " + global.version);
|
||||
if (cmpVersions(global.version, m.requiresVersion) >= 0) {
|
||||
Log.log("Version is ok!");
|
||||
} else {
|
||||
Log.log("Version is incorrect. Skip module: '" + moduleName + "'");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m.setName(moduleName);
|
||||
m.setPath(path.resolve(moduleFolder));
|
||||
nodeHelpers.push(m);
|
||||
|
||||
m.loaded(callback);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads all modules.
|
||||
*
|
||||
* @param {Module[]} modules All modules to be loaded
|
||||
* @param {Function} callback Function to be called after loading
|
||||
*/
|
||||
var loadModules = function (modules, callback) {
|
||||
Log.log("Loading module helpers ...");
|
||||
|
||||
var loadNextModule = function () {
|
||||
if (modules.length > 0) {
|
||||
var nextModule = modules[0];
|
||||
loadModule(nextModule, function () {
|
||||
modules = modules.slice(1);
|
||||
loadNextModule();
|
||||
});
|
||||
} else {
|
||||
// All modules are loaded
|
||||
Log.log("All module helpers loaded.");
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
loadNextModule();
|
||||
};
|
||||
|
||||
/**
|
||||
* Compare two semantic version numbers and return the difference.
|
||||
*
|
||||
* @param {string} a Version number a.
|
||||
* @param {string} b Version number b.
|
||||
*
|
||||
* @returns {number} A positive number if a is larger than b, a negative
|
||||
* number if a is smaller and 0 if they are the same
|
||||
*/
|
||||
function cmpVersions(a, b) {
|
||||
var i, diff;
|
||||
var regExStrip0 = /(\.0+)+$/;
|
||||
var segmentsA = a.replace(regExStrip0, "").split(".");
|
||||
var segmentsB = b.replace(regExStrip0, "").split(".");
|
||||
var l = Math.min(segmentsA.length, segmentsB.length);
|
||||
|
||||
for (i = 0; i < l; i++) {
|
||||
diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10);
|
||||
if (diff) {
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
return segmentsA.length - segmentsB.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the core app.
|
||||
*
|
||||
* It loads the config, then it loads all modules. When it's done it
|
||||
* executes the callback with the config as argument.
|
||||
*
|
||||
* @param {Function} callback Function to be called after start
|
||||
*/
|
||||
this.start = function (callback) {
|
||||
loadConfig(function (c) {
|
||||
config = c;
|
||||
|
||||
Log.setLogLevel(config.logLevel);
|
||||
|
||||
var modules = [];
|
||||
|
||||
for (var m in config.modules) {
|
||||
var module = config.modules[m];
|
||||
if (modules.indexOf(module.module) === -1 && !module.disabled) {
|
||||
modules.push(module.module);
|
||||
}
|
||||
}
|
||||
|
||||
loadModules(modules, function () {
|
||||
var server = new Server(config, function (app, io) {
|
||||
Log.log("Server started ...");
|
||||
|
||||
for (var h in nodeHelpers) {
|
||||
var nodeHelper = nodeHelpers[h];
|
||||
nodeHelper.setExpressApp(app);
|
||||
nodeHelper.setSocketIO(io);
|
||||
nodeHelper.start();
|
||||
}
|
||||
|
||||
Log.log("Sockets connected & modules started ...");
|
||||
|
||||
if (typeof callback === "function") {
|
||||
callback(config);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Stops the core app. This calls each node_helper's STOP() function, if it
|
||||
* exists.
|
||||
*
|
||||
* Added to fix #1056
|
||||
*/
|
||||
this.stop = function () {
|
||||
for (var h in nodeHelpers) {
|
||||
var nodeHelper = nodeHelpers[h];
|
||||
if (typeof nodeHelper.stop === "function") {
|
||||
nodeHelper.stop();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Listen for SIGINT signal and call stop() function.
|
||||
*
|
||||
* Added to fix #1056
|
||||
* Note: this is only used if running `server-only`. Otherwise
|
||||
* this.stop() is called by app.on("before-quit"... in `electron.js`
|
||||
*/
|
||||
process.on("SIGINT", () => {
|
||||
Log.log("[SIGINT] Received. Shutting down server...");
|
||||
setTimeout(() => {
|
||||
process.exit(0);
|
||||
}, 3000); // Force quit after 3 seconds
|
||||
this.stop();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
/**
|
||||
* Listen to SIGTERM signals so we can stop everything when we
|
||||
* are asked to stop by the OS.
|
||||
*/
|
||||
process.on("SIGTERM", () => {
|
||||
Log.log("[SIGTERM] Received. Shutting down server...");
|
||||
setTimeout(() => {
|
||||
process.exit(0);
|
||||
}, 3000); // Force quit after 3 seconds
|
||||
this.stop();
|
||||
process.exit(0);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = new App();
|
||||
@@ -1,139 +0,0 @@
|
||||
var calendar = {
|
||||
eventList: [],
|
||||
calendarLocation: '.calendar',
|
||||
updateInterval: 1000,
|
||||
updateDataInterval: 60000,
|
||||
fadeInterval: 1000,
|
||||
intervalId: null,
|
||||
dataIntervalId: null,
|
||||
maximumEntries: config.calendar.maximumEntries || 10
|
||||
}
|
||||
|
||||
calendar.updateData = function (callback) {
|
||||
|
||||
new ical_parser("controllers/calendar.php" + "?url="+encodeURIComponent(config.calendar.url), function(cal) {
|
||||
var events = cal.getEvents();
|
||||
this.eventList = [];
|
||||
|
||||
for (var i in events) {
|
||||
|
||||
var e = events[i];
|
||||
for (var key in e) {
|
||||
var value = e[key];
|
||||
var seperator = key.search(';');
|
||||
if (seperator >= 0) {
|
||||
var mainKey = key.substring(0,seperator);
|
||||
var subKey = key.substring(seperator+1);
|
||||
|
||||
var dt;
|
||||
if (subKey == 'VALUE=DATE') {
|
||||
//date
|
||||
dt = new Date(value.substring(0,4), value.substring(4,6) - 1, value.substring(6,8));
|
||||
} else {
|
||||
//time
|
||||
dt = new Date(value.substring(0,4), value.substring(4,6) - 1, value.substring(6,8), value.substring(9,11), value.substring(11,13), value.substring(13,15));
|
||||
}
|
||||
|
||||
if (mainKey == 'DTSTART') e.startDate = dt;
|
||||
if (mainKey == 'DTEND') e.endDate = dt;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.startDate == undefined){
|
||||
//some old events in Gmail Calendar is "start_date"
|
||||
//FIXME: problems with Gmail's TimeZone
|
||||
var days = moment(e.DTSTART).diff(moment(), 'days');
|
||||
var seconds = moment(e.DTSTART).diff(moment(), 'seconds');
|
||||
var startDate = moment(e.DTSTART);
|
||||
} else {
|
||||
var days = moment(e.startDate).diff(moment(), 'days');
|
||||
var seconds = moment(e.startDate).diff(moment(), 'seconds');
|
||||
var startDate = moment(e.startDate);
|
||||
}
|
||||
|
||||
//only add fututre events, days doesn't work, we need to check seconds
|
||||
if (seconds >= 0) {
|
||||
if (seconds <= 60*60*5 || seconds >= 60*60*24*2) {
|
||||
var time_string = moment(startDate).fromNow();
|
||||
}else {
|
||||
var time_string = moment(startDate).calendar()
|
||||
}
|
||||
if (!e.RRULE) {
|
||||
this.eventList.push({'description':e.SUMMARY,'seconds':seconds,'days':time_string});
|
||||
}
|
||||
e.seconds = seconds;
|
||||
}
|
||||
|
||||
// Special handling for rrule events
|
||||
if (e.RRULE) {
|
||||
var options = new RRule.parseString(e.RRULE);
|
||||
options.dtstart = e.startDate;
|
||||
var rule = new RRule(options);
|
||||
|
||||
var oneYear = new Date();
|
||||
oneYear.setFullYear(oneYear.getFullYear() + 1);
|
||||
|
||||
var dates = rule.between(new Date(), oneYear, true, function (date, i){return i < 10});
|
||||
for (date in dates) {
|
||||
var dt = new Date(dates[date]);
|
||||
var days = moment(dt).diff(moment(), 'days');
|
||||
var seconds = moment(dt).diff(moment(), 'seconds');
|
||||
var startDate = moment(dt);
|
||||
if (seconds >= 0) {
|
||||
if (seconds <= 60*60*5 || seconds >= 60*60*24*2) {
|
||||
var time_string = moment(dt).fromNow();
|
||||
} else {
|
||||
var time_string = moment(dt).calendar()
|
||||
}
|
||||
this.eventList.push({'description':e.SUMMARY,'seconds':seconds,'days':time_string});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.eventList = this.eventList.sort(function(a,b){return a.seconds-b.seconds});
|
||||
|
||||
// Limit the number of entries.
|
||||
this.eventList = this.eventList.slice(0, calendar.maximumEntries);
|
||||
|
||||
if (callback !== undefined && Object.prototype.toString.call(callback) === '[object Function]') {
|
||||
callback(this.eventList);
|
||||
}
|
||||
|
||||
}.bind(this));
|
||||
|
||||
}
|
||||
|
||||
calendar.updateCalendar = function (eventList) {
|
||||
|
||||
table = $('<table/>').addClass('xsmall').addClass('calendar-table');
|
||||
opacity = 1;
|
||||
|
||||
for (var i in eventList) {
|
||||
var e = eventList[i];
|
||||
|
||||
var row = $('<tr/>').css('opacity',opacity);
|
||||
row.append($('<td/>').html(e.description).addClass('description'));
|
||||
row.append($('<td/>').html(e.days).addClass('days dimmed'));
|
||||
table.append(row);
|
||||
|
||||
opacity -= 1 / eventList.length;
|
||||
}
|
||||
|
||||
$(this.calendarLocation).updateWithText(table, this.fadeInterval);
|
||||
|
||||
}
|
||||
|
||||
calendar.init = function () {
|
||||
|
||||
this.updateData(this.updateCalendar.bind(this));
|
||||
|
||||
this.intervalId = setInterval(function () {
|
||||
this.updateCalendar(this.eventList)
|
||||
}.bind(this), this.updateInterval);
|
||||
|
||||
this.dataIntervalId = setInterval(function () {
|
||||
this.updateData(this.updateCalendar.bind(this));
|
||||
}.bind(this), this.updateDataInterval);
|
||||
|
||||
}
|
||||
74
js/check_config.js
Normal file
74
js/check_config.js
Normal file
@@ -0,0 +1,74 @@
|
||||
/* Magic Mirror
|
||||
*
|
||||
* Check the configuration file for errors
|
||||
*
|
||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const Linter = require("eslint").Linter;
|
||||
const linter = new Linter();
|
||||
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
|
||||
const rootPath = path.resolve(__dirname + "/../");
|
||||
const Log = require(rootPath + "/js/logger.js");
|
||||
const Utils = require(rootPath + "/js/utils.js");
|
||||
|
||||
/**
|
||||
* Returns a string with path of configuration file.
|
||||
* Check if set by environment variable MM_CONFIG_FILE
|
||||
*
|
||||
* @returns {string} path and filename of the config file
|
||||
*/
|
||||
function getConfigFile() {
|
||||
// FIXME: This function should be in core. Do you want refactor me ;) ?, be good!
|
||||
let configFileName = path.resolve(rootPath + "/config/config.js");
|
||||
if (process.env.MM_CONFIG_FILE) {
|
||||
configFileName = path.resolve(process.env.MM_CONFIG_FILE);
|
||||
}
|
||||
return configFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the config file using eslint.
|
||||
*/
|
||||
function checkConfigFile() {
|
||||
const configFileName = getConfigFile();
|
||||
|
||||
// Check if file is present
|
||||
if (fs.existsSync(configFileName) === false) {
|
||||
Log.error(Utils.colors.error("File not found: "), configFileName);
|
||||
throw new Error("No config file present!");
|
||||
}
|
||||
|
||||
// Check permission
|
||||
try {
|
||||
fs.accessSync(configFileName, fs.F_OK);
|
||||
} catch (e) {
|
||||
Log.error(Utils.colors.error(e));
|
||||
throw new Error("No permission to access config file!");
|
||||
}
|
||||
|
||||
// Validate syntax of the configuration file.
|
||||
Log.info(Utils.colors.info("Checking file... "), configFileName);
|
||||
|
||||
// I'm not sure if all ever is utf-8
|
||||
fs.readFile(configFileName, "utf-8", function (err, data) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
const messages = linter.verify(data);
|
||||
if (messages.length === 0) {
|
||||
Log.info(Utils.colors.pass("Your configuration file doesn't contain syntax errors :)"));
|
||||
} else {
|
||||
Log.error(Utils.colors.error("Your configuration file contains syntax errors :("));
|
||||
// In case the there errors show messages and return
|
||||
messages.forEach((error) => {
|
||||
Log.error("Line", error.line, "col", error.column, error.message);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
checkConfigFile();
|
||||
110
js/class.js
Normal file
110
js/class.js
Normal file
@@ -0,0 +1,110 @@
|
||||
/* global Class, xyz */
|
||||
|
||||
/* Simple JavaScript Inheritance
|
||||
* By John Resig https://johnresig.com/
|
||||
*
|
||||
* Inspired by base2 and Prototype
|
||||
*
|
||||
* MIT Licensed.
|
||||
*/
|
||||
(function () {
|
||||
var initializing = false;
|
||||
var fnTest = /xyz/.test(function () {
|
||||
xyz;
|
||||
})
|
||||
? /\b_super\b/
|
||||
: /.*/;
|
||||
|
||||
// The base Class implementation (does nothing)
|
||||
this.Class = function () {};
|
||||
|
||||
// Create a new Class that inherits from this class
|
||||
Class.extend = function (prop) {
|
||||
var _super = this.prototype;
|
||||
|
||||
// Instantiate a base class (but only create the instance,
|
||||
// don't run the init constructor)
|
||||
initializing = true;
|
||||
var prototype = new this();
|
||||
initializing = false;
|
||||
|
||||
// Make a copy of all prototype properties, to prevent reference issues.
|
||||
for (var p in prototype) {
|
||||
prototype[p] = cloneObject(prototype[p]);
|
||||
}
|
||||
|
||||
// Copy the properties over onto the new prototype
|
||||
for (var name in prop) {
|
||||
// Check if we're overwriting an existing function
|
||||
prototype[name] =
|
||||
typeof prop[name] === "function" && typeof _super[name] === "function" && fnTest.test(prop[name])
|
||||
? (function (name, fn) {
|
||||
return function () {
|
||||
var tmp = this._super;
|
||||
|
||||
// Add a new ._super() method that is the same method
|
||||
// but on the super-class
|
||||
this._super = _super[name];
|
||||
|
||||
// The method only need to be bound temporarily, so we
|
||||
// remove it when we're done executing
|
||||
var ret = fn.apply(this, arguments);
|
||||
this._super = tmp;
|
||||
|
||||
return ret;
|
||||
};
|
||||
})(name, prop[name])
|
||||
: prop[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* The dummy class constructor
|
||||
*/
|
||||
function Class() {
|
||||
// All construction is actually done in the init method
|
||||
if (!initializing && this.init) {
|
||||
this.init.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate our constructed prototype object
|
||||
Class.prototype = prototype;
|
||||
|
||||
// Enforce the constructor to be what we expect
|
||||
Class.prototype.constructor = Class;
|
||||
|
||||
// And make this class extendable
|
||||
Class.extend = arguments.callee;
|
||||
|
||||
return Class;
|
||||
};
|
||||
})();
|
||||
|
||||
/**
|
||||
* Define the clone method for later use. Helper Method.
|
||||
*
|
||||
* @param {object} obj Object to be cloned
|
||||
*
|
||||
* @returns {object} the cloned object
|
||||
*/
|
||||
function cloneObject(obj) {
|
||||
if (obj === null || typeof obj !== "object") {
|
||||
return obj;
|
||||
}
|
||||
|
||||
var temp = obj.constructor(); // give temp the original obj's constructor
|
||||
for (var key in obj) {
|
||||
temp[key] = cloneObject(obj[key]);
|
||||
|
||||
if (key === "lockStrings") {
|
||||
Log.log(key);
|
||||
}
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = Class;
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
var compliments = {
|
||||
complimentLocation: '.compliment',
|
||||
currentCompliment: '',
|
||||
complimentList: {
|
||||
'morning': config.compliments.morning,
|
||||
'afternoon': config.compliments.afternoon,
|
||||
'evening': config.compliments.evening
|
||||
},
|
||||
updateInterval: config.compliments.interval || 30000,
|
||||
fadeInterval: config.compliments.fadeInterval || 4000,
|
||||
intervalId: null
|
||||
};
|
||||
|
||||
/**
|
||||
* Changes the compliment visible on the screen
|
||||
*/
|
||||
compliments.updateCompliment = function () {
|
||||
|
||||
|
||||
|
||||
var _list = [];
|
||||
|
||||
var hour = moment().hour();
|
||||
|
||||
// In the followign if statement we use .slice() on the
|
||||
// compliments array to make a copy by value.
|
||||
// This way the original array of compliments stays in tact.
|
||||
|
||||
if (hour >= 3 && hour < 12) {
|
||||
// Morning compliments
|
||||
_list = compliments.complimentList['morning'].slice();
|
||||
} else if (hour >= 12 && hour < 17) {
|
||||
// Afternoon compliments
|
||||
_list = compliments.complimentList['afternoon'].slice();
|
||||
} else if (hour >= 17 || hour < 3) {
|
||||
// Evening compliments
|
||||
_list = compliments.complimentList['evening'].slice();
|
||||
} else {
|
||||
// Edge case in case something weird happens
|
||||
// This will select a compliment from all times of day
|
||||
Object.keys(compliments.complimentList).forEach(function (_curr) {
|
||||
_list = _list.concat(compliments.complimentList[_curr]).slice();
|
||||
});
|
||||
}
|
||||
|
||||
// Search for the location of the current compliment in the list
|
||||
var _spliceIndex = _list.indexOf(compliments.currentCompliment);
|
||||
|
||||
// If it exists, remove it so we don't see it again
|
||||
if (_spliceIndex !== -1) {
|
||||
_list.splice(_spliceIndex, 1);
|
||||
}
|
||||
|
||||
// Randomly select a location
|
||||
var _randomIndex = Math.floor(Math.random() * _list.length);
|
||||
compliments.currentCompliment = _list[_randomIndex];
|
||||
|
||||
$('.compliment').updateWithText(compliments.currentCompliment, compliments.fadeInterval);
|
||||
|
||||
}
|
||||
|
||||
compliments.init = function () {
|
||||
|
||||
this.updateCompliment();
|
||||
|
||||
this.intervalId = setInterval(function () {
|
||||
this.updateCompliment();
|
||||
}.bind(this), this.updateInterval)
|
||||
|
||||
}
|
||||
43
js/config.js
43
js/config.js
@@ -1,43 +0,0 @@
|
||||
var config = {
|
||||
lang: 'nl',
|
||||
time: {
|
||||
timeFormat: 12
|
||||
},
|
||||
weather: {
|
||||
//change weather params here:
|
||||
//units: metric or imperial
|
||||
params: {
|
||||
q: 'Baarn,Netherlands',
|
||||
units: 'metric',
|
||||
// if you want a different lang for the weather that what is set above, change it here
|
||||
lang: 'nl',
|
||||
APPID: 'YOUR_FREE_OPENWEATHER_API_KEY'
|
||||
}
|
||||
},
|
||||
compliments: {
|
||||
interval: 30000,
|
||||
fadeInterval: 4000,
|
||||
morning: [
|
||||
'Good morning, handsome!',
|
||||
'Enjoy your day!',
|
||||
'How was your sleep?'
|
||||
],
|
||||
afternoon: [
|
||||
'Hello, beauty!',
|
||||
'You look sexy!',
|
||||
'Looking good today!'
|
||||
],
|
||||
evening: [
|
||||
'Wow, you look hot!',
|
||||
'You look nice!',
|
||||
'Hi, sexy!'
|
||||
]
|
||||
},
|
||||
calendar: {
|
||||
maximumEntries: 10,
|
||||
url: "https://p01-calendarws.icloud.com/ca/subscribe/1/n6x7Farxpt7m9S8bHg1TGArSj7J6kanm_2KEoJPL5YIAk3y70FpRo4GyWwO-6QfHSY5mXtHcRGVxYZUf7U3HPDOTG5x0qYnno1Zr_VuKH2M"
|
||||
},
|
||||
news: {
|
||||
feed: 'http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml'
|
||||
}
|
||||
}
|
||||
83
js/defaults.js
Normal file
83
js/defaults.js
Normal file
@@ -0,0 +1,83 @@
|
||||
/* global mmPort */
|
||||
|
||||
/* Magic Mirror
|
||||
* Config Defaults
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
var address = "localhost";
|
||||
var port = 8080;
|
||||
if (typeof mmPort !== "undefined") {
|
||||
port = mmPort;
|
||||
}
|
||||
var defaults = {
|
||||
address: address,
|
||||
port: port,
|
||||
basePath: "/",
|
||||
kioskmode: false,
|
||||
electronOptions: {},
|
||||
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
|
||||
|
||||
language: "en",
|
||||
timeFormat: 24,
|
||||
units: "metric",
|
||||
zoom: 1,
|
||||
customCss: "css/custom.css",
|
||||
|
||||
modules: [
|
||||
{
|
||||
module: "updatenotification",
|
||||
position: "top_center"
|
||||
},
|
||||
{
|
||||
module: "helloworld",
|
||||
position: "upper_third",
|
||||
classes: "large thin",
|
||||
config: {
|
||||
text: "Magic Mirror<sup>2</sup>"
|
||||
}
|
||||
},
|
||||
{
|
||||
module: "helloworld",
|
||||
position: "middle_center",
|
||||
config: {
|
||||
text: "Please create a config file."
|
||||
}
|
||||
},
|
||||
{
|
||||
module: "helloworld",
|
||||
position: "middle_center",
|
||||
classes: "small dimmed",
|
||||
config: {
|
||||
text: "See README for more information."
|
||||
}
|
||||
},
|
||||
{
|
||||
module: "helloworld",
|
||||
position: "middle_center",
|
||||
classes: "xsmall",
|
||||
config: {
|
||||
text: "If you get this message while your config file is already<br>created, your config file probably contains an error.<br>Use a JavaScript linter to validate your file."
|
||||
}
|
||||
},
|
||||
{
|
||||
module: "helloworld",
|
||||
position: "bottom_bar",
|
||||
classes: "xsmall dimmed",
|
||||
config: {
|
||||
text: "www.michaelteeuw.nl"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
paths: {
|
||||
modules: "modules",
|
||||
vendor: "vendor"
|
||||
}
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = defaults;
|
||||
}
|
||||
16
js/deprecated.js
Normal file
16
js/deprecated.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/* Magic Mirror Deprecated Config Options List
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*
|
||||
* Olex S. original idea this deprecated option
|
||||
*/
|
||||
|
||||
var deprecated = {
|
||||
configs: ["kioskmode"]
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = deprecated;
|
||||
}
|
||||
132
js/electron.js
Normal file
132
js/electron.js
Normal file
@@ -0,0 +1,132 @@
|
||||
"use strict";
|
||||
|
||||
const electron = require("electron");
|
||||
const core = require("./app.js");
|
||||
const Log = require("./logger.js");
|
||||
|
||||
// Config
|
||||
var config = process.env.config ? JSON.parse(process.env.config) : {};
|
||||
// Module to control application life.
|
||||
const app = electron.app;
|
||||
// Module to create native browser window.
|
||||
const BrowserWindow = electron.BrowserWindow;
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function createWindow() {
|
||||
app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required");
|
||||
var electronOptionsDefaults = {
|
||||
width: 800,
|
||||
height: 600,
|
||||
x: 0,
|
||||
y: 0,
|
||||
darkTheme: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
zoomFactor: config.zoom
|
||||
},
|
||||
backgroundColor: "#000000"
|
||||
};
|
||||
|
||||
// DEPRECATED: "kioskmode" backwards compatibility, to be removed
|
||||
// settings these options directly instead provides cleaner interface
|
||||
if (config.kioskmode) {
|
||||
electronOptionsDefaults.kiosk = true;
|
||||
} else {
|
||||
electronOptionsDefaults.fullscreen = true;
|
||||
electronOptionsDefaults.autoHideMenuBar = true;
|
||||
}
|
||||
|
||||
var electronOptions = Object.assign({}, electronOptionsDefaults, config.electronOptions);
|
||||
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow(electronOptions);
|
||||
|
||||
// and load the index.html of the app.
|
||||
// If config.address is not defined or is an empty string (listening on all interfaces), connect to localhost
|
||||
|
||||
var prefix;
|
||||
if (config["tls"] !== null && config["tls"]) {
|
||||
prefix = "https://";
|
||||
} else {
|
||||
prefix = "http://";
|
||||
}
|
||||
|
||||
var address = (config.address === void 0) | (config.address === "") ? (config.address = "localhost") : config.address;
|
||||
mainWindow.loadURL(`${prefix}${address}:${config.port}`);
|
||||
|
||||
// Open the DevTools if run with "npm start dev"
|
||||
if (process.argv.includes("dev")) {
|
||||
mainWindow.webContents.openDevTools();
|
||||
}
|
||||
|
||||
// Set responders for window events.
|
||||
mainWindow.on("closed", function () {
|
||||
mainWindow = null;
|
||||
});
|
||||
|
||||
if (config.kioskmode) {
|
||||
mainWindow.on("blur", function () {
|
||||
mainWindow.focus();
|
||||
});
|
||||
|
||||
mainWindow.on("leave-full-screen", function () {
|
||||
mainWindow.setFullScreen(true);
|
||||
});
|
||||
|
||||
mainWindow.on("resize", function () {
|
||||
setTimeout(function () {
|
||||
mainWindow.reload();
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
app.on("ready", function () {
|
||||
Log.log("Launching application.");
|
||||
createWindow();
|
||||
});
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on("window-all-closed", function () {
|
||||
createWindow();
|
||||
});
|
||||
|
||||
app.on("activate", function () {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (mainWindow === null) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
/* This method will be called when SIGINT is received and will call
|
||||
* each node_helper's stop function if it exists. Added to fix #1056
|
||||
*
|
||||
* Note: this is only used if running Electron. Otherwise
|
||||
* core.stop() is called by process.on("SIGINT"... in `app.js`
|
||||
*/
|
||||
app.on("before-quit", (event) => {
|
||||
Log.log("Shutting down server...");
|
||||
event.preventDefault();
|
||||
setTimeout(() => {
|
||||
process.exit(0);
|
||||
}, 3000); // Force-quit after 3 seconds.
|
||||
core.stop();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
// Start the core application if server is run on localhost
|
||||
// This starts all node helpers and starts the webserver.
|
||||
if (["localhost", "127.0.0.1", "::1", "::ffff:127.0.0.1", undefined].indexOf(config.address) > -1) {
|
||||
core.start(function (c) {
|
||||
config = c;
|
||||
});
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
/**
|
||||
* Javascript ical Parser
|
||||
* Proof of concept method of reading icalendar (.ics) files with javascript.
|
||||
*
|
||||
* @author: Carl Saggs
|
||||
* @source: https://github.com/thybag/
|
||||
* @version: 0.2
|
||||
*/
|
||||
function ical_parser(feed_url, callback){
|
||||
//store of unproccesed data.
|
||||
this.raw_data = null;
|
||||
//Store of proccessed data.
|
||||
this.events = [];
|
||||
|
||||
/**
|
||||
* loadFile
|
||||
* Using AJAX to load the requested .ics file, passing it to the callback when completed.
|
||||
* @param url URL of .ics file
|
||||
* @param callback Function to call on completion.
|
||||
*/
|
||||
this.loadFile = function(url, callback){
|
||||
//Create request object
|
||||
try {xmlhttp = window.XMLHttpRequest?new XMLHttpRequest(): new ActiveXObject("Microsoft.XMLHTTP");} catch (e) { }
|
||||
//Grab file
|
||||
xmlhttp.onreadystatechange = function(){
|
||||
if ((xmlhttp.readyState == 4) && (xmlhttp.status == 200)) {
|
||||
//On success, run callback.
|
||||
callback(xmlhttp.responseText);
|
||||
}
|
||||
}
|
||||
xmlhttp.open("GET", url, true);
|
||||
xmlhttp.send(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* makeDate
|
||||
* Convert the dateformat used by ICalendar in to one more suitable for javascript.
|
||||
* @param String ical_date
|
||||
* @return dt object, includes javascript Date + day name, hour/minutes/day/month/year etc.
|
||||
*/
|
||||
this.makeDate = function(ical_date){
|
||||
//break date apart
|
||||
var dtutc = {
|
||||
year: ical_date.substr(0,4),
|
||||
month: ical_date.substr(4,2),
|
||||
day: ical_date.substr(6,2),
|
||||
hour: ical_date.substr(9,2),
|
||||
minute: ical_date.substr(11,2)
|
||||
}
|
||||
//Create JS date (months start at 0 in JS - don't ask)
|
||||
var utcdatems = Date.UTC(dtutc.year, (dtutc.month-1), dtutc.day, dtutc.hour, dtutc.minute);
|
||||
var dt = {};
|
||||
dt.date = new Date(utcdatems);
|
||||
|
||||
dt.year = dt.date.getFullYear();
|
||||
dt.month = ('0' + (dt.date.getMonth()+1)).slice(-2);
|
||||
dt.day = ('0' + dt.date.getDate()).slice(-2);
|
||||
dt.hour = ('0' + dt.date.getHours()).slice(-2);
|
||||
dt.minute = ('0' + dt.date.getMinutes()).slice(-2);
|
||||
|
||||
//Get the full name of the given day
|
||||
dt.dayname =["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"][dt.date.getDay()];
|
||||
dt.monthname = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ][dt.date.getMonth()] ;
|
||||
|
||||
return dt;
|
||||
}
|
||||
|
||||
/**
|
||||
* parseICAL
|
||||
* Convert the ICAL format in to a number of javascript objects (Each representing a date)
|
||||
*
|
||||
* @param data Raw ICAL data
|
||||
*/
|
||||
this.parseICAL = function(data){
|
||||
//Ensure cal is empty
|
||||
this.events = [];
|
||||
|
||||
//Clean string and split the file so we can handle it (line by line)
|
||||
cal_array = data.replace(new RegExp( "\\r", "g" ), "").replace(/\n /g,"").split("\n");
|
||||
|
||||
//Keep track of when we are activly parsing an event
|
||||
var in_event = false;
|
||||
//Use as a holder for the current event being proccessed.
|
||||
var cur_event = null;
|
||||
for(var i=0;i<cal_array.length;i++){
|
||||
ln = cal_array[i];
|
||||
//If we encounted a new Event, create a blank event object + set in event options.
|
||||
if(!in_event && ln == 'BEGIN:VEVENT'){
|
||||
in_event = true;
|
||||
cur_event = {};
|
||||
}
|
||||
//If we encounter end event, complete the object and add it to our events array then clear it for reuse.
|
||||
if(in_event && ln == 'END:VEVENT'){
|
||||
in_event = false;
|
||||
this.events.push(cur_event);
|
||||
cur_event = null;
|
||||
}
|
||||
//If we are in an event
|
||||
else if(in_event){
|
||||
//var lntrim = ln.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
|
||||
//var lnsplit = lntrim.split(':');
|
||||
//type = lnsplit[0];
|
||||
//val = lnsplit[1];
|
||||
|
||||
//Split the item based on the first ":"
|
||||
idx = ln.indexOf(':');
|
||||
//Apply trimming to values to reduce risks of badly formatted ical files.
|
||||
type = ln.substr(0,idx).replace(/^\s\s*/, '').replace(/\s\s*$/, '');//Trim
|
||||
val = ln.substr(idx+1).replace(/^\s\s*/, '').replace(/\s\s*$/, '');
|
||||
|
||||
//If the type is a start date, proccess it and store details
|
||||
if(type =='DTSTART'){
|
||||
dt = this.makeDate(val);
|
||||
val = dt.date;
|
||||
//These are helpful for display
|
||||
cur_event.start_time = dt.hour+':'+dt.minute;
|
||||
cur_event.start_date = dt.day+'/'+dt.month+'/'+dt.year;
|
||||
cur_event.day = dt.dayname;
|
||||
cur_event.start_date_long = dt.day+'. '+dt.monthname+' '+dt.year ;
|
||||
}
|
||||
//If the type is an end date, do the same as above
|
||||
else if(type =='DTEND'){
|
||||
dt = this.makeDate(val);
|
||||
val = dt.date;
|
||||
//These are helpful for display
|
||||
cur_event.end_time = dt.hour+':'+dt.minute;
|
||||
cur_event.end_date = dt.day+'/'+dt.month+'/'+dt.year;
|
||||
cur_event.day = dt.dayname;
|
||||
}
|
||||
//Convert timestamp
|
||||
else if(type =='DTSTAMP'){
|
||||
val = this.makeDate(val).date;
|
||||
}
|
||||
else {
|
||||
val = val
|
||||
.replace(/\\r\\n/g,'<br />')
|
||||
.replace(/\\n/g,'<br />')
|
||||
.replace(/\\,/g,',');
|
||||
}
|
||||
|
||||
//Add the value to our event object.
|
||||
cur_event[type] = val;
|
||||
}
|
||||
}
|
||||
//Run this to finish proccessing our Events.
|
||||
this.complete();
|
||||
}
|
||||
/**
|
||||
* complete
|
||||
* Sort all events in to a sensible order and run the original callback
|
||||
*/
|
||||
this.complete = function(){
|
||||
//Sort the data so its in date order.
|
||||
this.events.sort(function(a,b){
|
||||
return a.DTSTART-b.DTSTART;
|
||||
});
|
||||
//Run callback method, if was defined. (return self)
|
||||
if(typeof callback == 'function') callback(this);
|
||||
}
|
||||
/**
|
||||
* getEvents
|
||||
* return all events found in the ical file.
|
||||
*
|
||||
* @return list of events objects
|
||||
*/
|
||||
this.getEvents = function(){
|
||||
return this.events;
|
||||
}
|
||||
|
||||
/**
|
||||
* getFutureEvents
|
||||
* return all events sheduled to take place after the current date.
|
||||
*
|
||||
* @return list of events objects
|
||||
*/
|
||||
this.getFutureEvents = function(){
|
||||
var future_events = [], current_date = new Date();
|
||||
|
||||
this.events.forEach(function(itm){
|
||||
//If the event ends after the current time, add it to the array to return.
|
||||
if(itm.DTEND > current_date) future_events.push(itm);
|
||||
});
|
||||
return future_events;
|
||||
}
|
||||
|
||||
/**
|
||||
* getPastEvents
|
||||
* return all events sheduled to take place before the current date.
|
||||
*
|
||||
* @return list of events objects
|
||||
*/
|
||||
this.getPastEvents = function(){
|
||||
var past_events = [], current_date = new Date();
|
||||
|
||||
this.events.forEach(function(itm){
|
||||
//If the event ended before the current time, add it to the array to return.
|
||||
if(itm.DTEND <= current_date) past_events.push(itm);
|
||||
});
|
||||
return past_events.reverse();
|
||||
}
|
||||
|
||||
/**
|
||||
* load
|
||||
* load a new ICAL file.
|
||||
*
|
||||
* @param ical file url
|
||||
*/
|
||||
this.load = function(ical_file){
|
||||
var tmp_this = this;
|
||||
this.raw_data = null;
|
||||
this.loadFile(ical_file, function(data){
|
||||
//if the file loads, store the data and invoke the parser
|
||||
tmp_this.raw_data = data;
|
||||
tmp_this.parseICAL(data);
|
||||
});
|
||||
}
|
||||
|
||||
//Store this so we can use it in the callback from the load function.
|
||||
var tmp_this = this;
|
||||
//Store the feed url
|
||||
this.feed_url = feed_url;
|
||||
//Load the file
|
||||
this.load(this.feed_url);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
//jQuery extension to fetch an rss feed and return it as json via YQL
|
||||
//created by dboz@airshp.com
|
||||
(function($) {
|
||||
|
||||
$.extend({
|
||||
feedToJson: function(options, callback) {
|
||||
if ($.isFunction(options)) {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
options = $.extend($.feedToJson.defaults,options);
|
||||
var url = options.yqlURL + options.yqlQS + "'" + encodeURIComponent(options.feed) + "'" + "&_nocache=" + options.cacheBuster;
|
||||
return $.getJSON(url, function(data){
|
||||
//console.log(data.query.results);
|
||||
data = data.query.results;
|
||||
$.isFunction(callback) && callback(data); //allows the callback function to be the only option
|
||||
$.isFunction(options.success) && options.success(data);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//defaults
|
||||
$.feedToJson.defaults = {
|
||||
yqlURL : 'https://query.yahooapis.com/v1/public/yql', //yql
|
||||
yqlQS : '?format=json&callback=?&q=select%20*%20from%20rss%20where%20url%3D', //yql query string
|
||||
feed:'http://instagr.am/tags/tacos/feed/recent.rss', //instagram recent posts tagged 'tacos'
|
||||
cachebuster: Math.floor((new Date().getTime()) / 1200 / 1000), //yql caches feeds, so we change the feed url every 20min
|
||||
success:null //success callback
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
// eo feedToJson
|
||||
4
js/jquery.js
vendored
4
js/jquery.js
vendored
File diff suppressed because one or more lines are too long
263
js/loader.js
Normal file
263
js/loader.js
Normal file
@@ -0,0 +1,263 @@
|
||||
/* global defaultModules, vendor */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module and File loaders.
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
var Loader = (function () {
|
||||
/* Create helper variables */
|
||||
|
||||
var loadedModuleFiles = [];
|
||||
var loadedFiles = [];
|
||||
var moduleObjects = [];
|
||||
|
||||
/* Private Methods */
|
||||
|
||||
/**
|
||||
* Loops thru all modules and requests load for every module.
|
||||
*/
|
||||
var loadModules = function () {
|
||||
var moduleData = getModuleData();
|
||||
|
||||
var loadNextModule = function () {
|
||||
if (moduleData.length > 0) {
|
||||
var nextModule = moduleData[0];
|
||||
loadModule(nextModule, function () {
|
||||
moduleData = moduleData.slice(1);
|
||||
loadNextModule();
|
||||
});
|
||||
} else {
|
||||
// All modules loaded. Load custom.css
|
||||
// This is done after all the modules so we can
|
||||
// overwrite all the defined styles.
|
||||
|
||||
loadFile(config.customCss, function () {
|
||||
// custom.css loaded. Start all modules.
|
||||
startModules();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
loadNextModule();
|
||||
};
|
||||
|
||||
/**
|
||||
* Loops thru all modules and requests start for every module.
|
||||
*/
|
||||
var startModules = function () {
|
||||
for (var m in moduleObjects) {
|
||||
var module = moduleObjects[m];
|
||||
module.start();
|
||||
}
|
||||
|
||||
// Notify core of loaded modules.
|
||||
MM.modulesStarted(moduleObjects);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve list of all modules.
|
||||
*
|
||||
* @returns {object[]} module data as configured in config
|
||||
*/
|
||||
var getAllModules = function () {
|
||||
return config.modules;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate array with module information including module paths.
|
||||
*
|
||||
* @returns {object[]} Module information.
|
||||
*/
|
||||
var getModuleData = function () {
|
||||
var modules = getAllModules();
|
||||
var moduleFiles = [];
|
||||
|
||||
for (var m in modules) {
|
||||
var moduleData = modules[m];
|
||||
var module = moduleData.module;
|
||||
|
||||
var elements = module.split("/");
|
||||
var moduleName = elements[elements.length - 1];
|
||||
var moduleFolder = config.paths.modules + "/" + module;
|
||||
|
||||
if (defaultModules.indexOf(moduleName) !== -1) {
|
||||
moduleFolder = config.paths.modules + "/default/" + module;
|
||||
}
|
||||
|
||||
if (moduleData.disabled === true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
moduleFiles.push({
|
||||
index: m,
|
||||
identifier: "module_" + m + "_" + module,
|
||||
name: moduleName,
|
||||
path: moduleFolder + "/",
|
||||
file: moduleName + ".js",
|
||||
position: moduleData.position,
|
||||
header: moduleData.header,
|
||||
configDeepMerge: typeof moduleData.configDeepMerge === "boolean" ? moduleData.configDeepMerge : false,
|
||||
config: moduleData.config,
|
||||
classes: typeof moduleData.classes !== "undefined" ? moduleData.classes + " " + module : module
|
||||
});
|
||||
}
|
||||
|
||||
return moduleFiles;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load modules via ajax request and create module objects.s
|
||||
*
|
||||
* @param {object} module Information about the module we want to load.
|
||||
* @param {Function} callback Function called when done.
|
||||
*/
|
||||
var loadModule = function (module, callback) {
|
||||
var url = module.path + "/" + module.file;
|
||||
|
||||
var afterLoad = function () {
|
||||
var moduleObject = Module.create(module.name);
|
||||
if (moduleObject) {
|
||||
bootstrapModule(module, moduleObject, function () {
|
||||
callback();
|
||||
});
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
if (loadedModuleFiles.indexOf(url) !== -1) {
|
||||
afterLoad();
|
||||
} else {
|
||||
loadFile(url, function () {
|
||||
loadedModuleFiles.push(url);
|
||||
afterLoad();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Bootstrap modules by setting the module data and loading the scripts & styles.
|
||||
*
|
||||
* @param {object} module Information about the module we want to load.
|
||||
* @param {Module} mObj Modules instance.
|
||||
* @param {Function} callback Function called when done.
|
||||
*/
|
||||
var bootstrapModule = function (module, mObj, callback) {
|
||||
Log.info("Bootstrapping module: " + module.name);
|
||||
|
||||
mObj.setData(module);
|
||||
|
||||
mObj.loadScripts(function () {
|
||||
Log.log("Scripts loaded for: " + module.name);
|
||||
mObj.loadStyles(function () {
|
||||
Log.log("Styles loaded for: " + module.name);
|
||||
mObj.loadTranslations(function () {
|
||||
Log.log("Translations loaded for: " + module.name);
|
||||
moduleObjects.push(mObj);
|
||||
callback();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Load a script or stylesheet by adding it to the dom.
|
||||
*
|
||||
* @param {string} fileName Path of the file we want to load.
|
||||
* @param {Function} callback Function called when done.
|
||||
*/
|
||||
var loadFile = function (fileName, callback) {
|
||||
var extension = fileName.slice((Math.max(0, fileName.lastIndexOf(".")) || Infinity) + 1);
|
||||
|
||||
switch (extension.toLowerCase()) {
|
||||
case "js":
|
||||
Log.log("Load script: " + fileName);
|
||||
var script = document.createElement("script");
|
||||
script.type = "text/javascript";
|
||||
script.src = fileName;
|
||||
script.onload = function () {
|
||||
if (typeof callback === "function") {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
script.onerror = function () {
|
||||
Log.error("Error on loading script:", fileName);
|
||||
if (typeof callback === "function") {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementsByTagName("body")[0].appendChild(script);
|
||||
break;
|
||||
case "css":
|
||||
Log.log("Load stylesheet: " + fileName);
|
||||
var stylesheet = document.createElement("link");
|
||||
stylesheet.rel = "stylesheet";
|
||||
stylesheet.type = "text/css";
|
||||
stylesheet.href = fileName;
|
||||
stylesheet.onload = function () {
|
||||
if (typeof callback === "function") {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
stylesheet.onerror = function () {
|
||||
Log.error("Error on loading stylesheet:", fileName);
|
||||
if (typeof callback === "function") {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
document.getElementsByTagName("head")[0].appendChild(stylesheet);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/* Public Methods */
|
||||
return {
|
||||
/**
|
||||
* Load all modules as defined in the config.
|
||||
*/
|
||||
loadModules: function () {
|
||||
loadModules();
|
||||
},
|
||||
|
||||
/**
|
||||
* Load a file (script or stylesheet).
|
||||
* Prevent double loading and search for files in the vendor folder.
|
||||
*
|
||||
* @param {string} fileName Path of the file we want to load.
|
||||
* @param {Module} module The module that calls the loadFile function.
|
||||
* @param {Function} callback Function called when done.
|
||||
*/
|
||||
loadFile: function (fileName, module, callback) {
|
||||
if (loadedFiles.indexOf(fileName.toLowerCase()) !== -1) {
|
||||
Log.log("File already loaded: " + fileName);
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
if (fileName.indexOf("http://") === 0 || fileName.indexOf("https://") === 0 || fileName.indexOf("/") !== -1) {
|
||||
// This is an absolute or relative path.
|
||||
// Load it and then return.
|
||||
loadedFiles.push(fileName.toLowerCase());
|
||||
loadFile(fileName, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vendor[fileName] !== undefined) {
|
||||
// This file is available in the vendor folder.
|
||||
// Load it from this vendor folder.
|
||||
loadedFiles.push(fileName.toLowerCase());
|
||||
loadFile(config.paths.vendor + "/" + vendor[fileName], callback);
|
||||
return;
|
||||
}
|
||||
|
||||
// File not loaded yet.
|
||||
// Load it based on the module path.
|
||||
loadedFiles.push(fileName.toLowerCase());
|
||||
loadFile(module.file(fileName), callback);
|
||||
}
|
||||
};
|
||||
})();
|
||||
50
js/logger.js
Normal file
50
js/logger.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/* Magic Mirror
|
||||
* Log
|
||||
*
|
||||
* This logger is very simple, but needs to be extended.
|
||||
* This system can eventually be used to push the log messages to an external target.
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof exports === "object") {
|
||||
// add timestamps in front of log messages
|
||||
require("console-stamp")(console, {
|
||||
pattern: "yyyy-mm-dd HH:MM:ss.l",
|
||||
include: ["debug", "log", "info", "warn", "error"]
|
||||
});
|
||||
|
||||
// Node, CommonJS-like
|
||||
module.exports = factory(root.config);
|
||||
} else {
|
||||
// Browser globals (root is window)
|
||||
root.Log = factory(root.config);
|
||||
}
|
||||
})(this, function (config) {
|
||||
const logLevel = {
|
||||
debug: Function.prototype.bind.call(console.debug, console),
|
||||
log: Function.prototype.bind.call(console.log, console),
|
||||
info: Function.prototype.bind.call(console.info, console),
|
||||
warn: Function.prototype.bind.call(console.warn, console),
|
||||
error: Function.prototype.bind.call(console.error, console),
|
||||
group: Function.prototype.bind.call(console.group, console),
|
||||
groupCollapsed: Function.prototype.bind.call(console.groupCollapsed, console),
|
||||
groupEnd: Function.prototype.bind.call(console.groupEnd, console),
|
||||
time: Function.prototype.bind.call(console.time, console),
|
||||
timeEnd: Function.prototype.bind.call(console.timeEnd, console),
|
||||
timeStamp: Function.prototype.bind.call(console.timeStamp, console)
|
||||
};
|
||||
|
||||
logLevel.setLogLevel = function (newLevel) {
|
||||
if (newLevel) {
|
||||
Object.keys(logLevel).forEach(function (key, index) {
|
||||
if (!newLevel.includes(key.toLocaleUpperCase())) {
|
||||
logLevel[key] = function () {};
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return logLevel;
|
||||
});
|
||||
666
js/main.js
Executable file → Normal file
666
js/main.js
Executable file → Normal file
@@ -1,60 +1,616 @@
|
||||
jQuery.fn.updateWithText = function(text, speed)
|
||||
{
|
||||
var dummy = $('<div/>').html(text);
|
||||
/* global Loader, defaults, Translator */
|
||||
|
||||
if ($(this).html() != dummy.html())
|
||||
{
|
||||
$(this).fadeOut(speed/2, function() {
|
||||
$(this).html(text);
|
||||
$(this).fadeIn(speed/2, function() {
|
||||
//done
|
||||
/* Magic Mirror
|
||||
* Main System
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
var MM = (function () {
|
||||
var modules = [];
|
||||
|
||||
/* Private Methods */
|
||||
|
||||
/**
|
||||
* Create dom objects for all modules that are configured for a specific position.
|
||||
*/
|
||||
var createDomObjects = function () {
|
||||
var domCreationPromises = [];
|
||||
|
||||
modules.forEach(function (module) {
|
||||
if (typeof module.data.position !== "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
var wrapper = selectWrapper(module.data.position);
|
||||
|
||||
var dom = document.createElement("div");
|
||||
dom.id = module.identifier;
|
||||
dom.className = module.name;
|
||||
|
||||
if (typeof module.data.classes === "string") {
|
||||
dom.className = "module " + dom.className + " " + module.data.classes;
|
||||
}
|
||||
|
||||
dom.opacity = 0;
|
||||
wrapper.appendChild(dom);
|
||||
|
||||
var moduleHeader = document.createElement("header");
|
||||
moduleHeader.innerHTML = module.getHeader();
|
||||
moduleHeader.className = "module-header";
|
||||
dom.appendChild(moduleHeader);
|
||||
|
||||
if (typeof module.getHeader() === "undefined" || module.getHeader() !== "") {
|
||||
moduleHeader.style.display = "none;";
|
||||
} else {
|
||||
moduleHeader.style.display = "block;";
|
||||
}
|
||||
|
||||
var moduleContent = document.createElement("div");
|
||||
moduleContent.className = "module-content";
|
||||
dom.appendChild(moduleContent);
|
||||
|
||||
var domCreationPromise = updateDom(module, 0);
|
||||
domCreationPromises.push(domCreationPromise);
|
||||
domCreationPromise
|
||||
.then(function () {
|
||||
sendNotification("MODULE_DOM_CREATED", null, null, module);
|
||||
})
|
||||
.catch(Log.error);
|
||||
});
|
||||
|
||||
updateWrapperStates();
|
||||
|
||||
Promise.all(domCreationPromises).then(function () {
|
||||
sendNotification("DOM_OBJECTS_CREATED");
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Select the wrapper dom object for a specific position.
|
||||
*
|
||||
* @param {string} position The name of the position.
|
||||
*
|
||||
* @returns {HTMLElement} the wrapper element
|
||||
*/
|
||||
var selectWrapper = function (position) {
|
||||
var classes = position.replace("_", " ");
|
||||
var parentWrapper = document.getElementsByClassName(classes);
|
||||
if (parentWrapper.length > 0) {
|
||||
var wrapper = parentWrapper[0].getElementsByClassName("container");
|
||||
if (wrapper.length > 0) {
|
||||
return wrapper[0];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a notification to all modules.
|
||||
*
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
* @param {Module} sender The module that sent the notification.
|
||||
* @param {Module} [sendTo] The (optional) module to send the notification to.
|
||||
*/
|
||||
var sendNotification = function (notification, payload, sender, sendTo) {
|
||||
for (var m in modules) {
|
||||
var module = modules[m];
|
||||
if (module !== sender && (!sendTo || module === sendTo)) {
|
||||
module.notificationReceived(notification, payload, sender);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the dom for a specific module.
|
||||
*
|
||||
* @param {Module} module The module that needs an update.
|
||||
* @param {number} [speed] The (optional) number of microseconds for the animation.
|
||||
*
|
||||
* @returns {Promise} Resolved when the dom is fully updated.
|
||||
*/
|
||||
var updateDom = function (module, speed) {
|
||||
return new Promise(function (resolve) {
|
||||
var newContentPromise = module.getDom();
|
||||
var newHeader = module.getHeader();
|
||||
|
||||
if (!(newContentPromise instanceof Promise)) {
|
||||
// convert to a promise if not already one to avoid if/else's everywhere
|
||||
newContentPromise = Promise.resolve(newContentPromise);
|
||||
}
|
||||
|
||||
newContentPromise
|
||||
.then(function (newContent) {
|
||||
var updatePromise = updateDomWithContent(module, speed, newHeader, newContent);
|
||||
|
||||
updatePromise.then(resolve).catch(Log.error);
|
||||
})
|
||||
.catch(Log.error);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the dom with the specified content
|
||||
*
|
||||
* @param {Module} module The module that needs an update.
|
||||
* @param {number} [speed] The (optional) number of microseconds for the animation.
|
||||
* @param {string} newHeader The new header that is generated.
|
||||
* @param {HTMLElement} newContent The new content that is generated.
|
||||
*
|
||||
* @returns {Promise} Resolved when the module dom has been updated.
|
||||
*/
|
||||
var updateDomWithContent = function (module, speed, newHeader, newContent) {
|
||||
return new Promise(function (resolve) {
|
||||
if (module.hidden || !speed) {
|
||||
updateModuleContent(module, newHeader, newContent);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!moduleNeedsUpdate(module, newHeader, newContent)) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!speed) {
|
||||
updateModuleContent(module, newHeader, newContent);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
hideModule(module, speed / 2, function () {
|
||||
updateModuleContent(module, newHeader, newContent);
|
||||
if (!module.hidden) {
|
||||
showModule(module, speed / 2);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the content has changed.
|
||||
*
|
||||
* @param {Module} module The module to check.
|
||||
* @param {string} newHeader The new header that is generated.
|
||||
* @param {HTMLElement} newContent The new content that is generated.
|
||||
*
|
||||
* @returns {boolean} True if the module need an update, false otherwise
|
||||
*/
|
||||
var moduleNeedsUpdate = function (module, newHeader, newContent) {
|
||||
var moduleWrapper = document.getElementById(module.identifier);
|
||||
if (moduleWrapper === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var contentWrapper = moduleWrapper.getElementsByClassName("module-content");
|
||||
var headerWrapper = moduleWrapper.getElementsByClassName("module-header");
|
||||
|
||||
var headerNeedsUpdate = false;
|
||||
var contentNeedsUpdate = false;
|
||||
|
||||
if (headerWrapper.length > 0) {
|
||||
headerNeedsUpdate = newHeader !== headerWrapper[0].innerHTML;
|
||||
}
|
||||
|
||||
var tempContentWrapper = document.createElement("div");
|
||||
tempContentWrapper.appendChild(newContent);
|
||||
contentNeedsUpdate = tempContentWrapper.innerHTML !== contentWrapper[0].innerHTML;
|
||||
|
||||
return headerNeedsUpdate || contentNeedsUpdate;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the content of a module on screen.
|
||||
*
|
||||
* @param {Module} module The module to check.
|
||||
* @param {string} newHeader The new header that is generated.
|
||||
* @param {HTMLElement} newContent The new content that is generated.
|
||||
*/
|
||||
var updateModuleContent = function (module, newHeader, newContent) {
|
||||
var moduleWrapper = document.getElementById(module.identifier);
|
||||
if (moduleWrapper === null) {
|
||||
return;
|
||||
}
|
||||
var headerWrapper = moduleWrapper.getElementsByClassName("module-header");
|
||||
var contentWrapper = moduleWrapper.getElementsByClassName("module-content");
|
||||
|
||||
contentWrapper[0].innerHTML = "";
|
||||
contentWrapper[0].appendChild(newContent);
|
||||
|
||||
headerWrapper[0].innerHTML = newHeader;
|
||||
if (headerWrapper.length > 0 && newHeader) {
|
||||
headerWrapper[0].style.display = "block";
|
||||
} else {
|
||||
headerWrapper[0].style.display = "none";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the module.
|
||||
*
|
||||
* @param {Module} module The module to hide.
|
||||
* @param {number} speed The speed of the hide animation.
|
||||
* @param {Function} callback Called when the animation is done.
|
||||
* @param {object} [options] Optional settings for the hide method.
|
||||
*/
|
||||
var hideModule = function (module, speed, callback, options) {
|
||||
options = options || {};
|
||||
|
||||
// set lockString if set in options.
|
||||
if (options.lockString) {
|
||||
// Log.log("Has lockstring: " + options.lockString);
|
||||
if (module.lockStrings.indexOf(options.lockString) === -1) {
|
||||
module.lockStrings.push(options.lockString);
|
||||
}
|
||||
}
|
||||
|
||||
var moduleWrapper = document.getElementById(module.identifier);
|
||||
if (moduleWrapper !== null) {
|
||||
moduleWrapper.style.transition = "opacity " + speed / 1000 + "s";
|
||||
moduleWrapper.style.opacity = 0;
|
||||
|
||||
clearTimeout(module.showHideTimer);
|
||||
module.showHideTimer = setTimeout(function () {
|
||||
// To not take up any space, we just make the position absolute.
|
||||
// since it's fade out anyway, we can see it lay above or
|
||||
// below other modules. This works way better than adjusting
|
||||
// the .display property.
|
||||
moduleWrapper.style.position = "fixed";
|
||||
|
||||
updateWrapperStates();
|
||||
|
||||
if (typeof callback === "function") {
|
||||
callback();
|
||||
}
|
||||
}, speed);
|
||||
} else {
|
||||
// invoke callback even if no content, issue 1308
|
||||
if (typeof callback === "function") {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the module.
|
||||
*
|
||||
* @param {Module} module The module to show.
|
||||
* @param {number} speed The speed of the show animation.
|
||||
* @param {Function} callback Called when the animation is done.
|
||||
* @param {object} [options] Optional settings for the show method.
|
||||
*/
|
||||
var showModule = function (module, speed, callback, options) {
|
||||
options = options || {};
|
||||
|
||||
// remove lockString if set in options.
|
||||
if (options.lockString) {
|
||||
var index = module.lockStrings.indexOf(options.lockString);
|
||||
if (index !== -1) {
|
||||
module.lockStrings.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there are no more lockstrings set, or the force option is set.
|
||||
// Otherwise cancel show action.
|
||||
if (module.lockStrings.length !== 0 && options.force !== true) {
|
||||
Log.log("Will not show " + module.name + ". LockStrings active: " + module.lockStrings.join(","));
|
||||
return;
|
||||
}
|
||||
|
||||
module.hidden = false;
|
||||
|
||||
// If forced show, clean current lockstrings.
|
||||
if (module.lockStrings.length !== 0 && options.force === true) {
|
||||
Log.log("Force show of module: " + module.name);
|
||||
module.lockStrings = [];
|
||||
}
|
||||
|
||||
var moduleWrapper = document.getElementById(module.identifier);
|
||||
if (moduleWrapper !== null) {
|
||||
moduleWrapper.style.transition = "opacity " + speed / 1000 + "s";
|
||||
// Restore the position. See hideModule() for more info.
|
||||
moduleWrapper.style.position = "static";
|
||||
|
||||
updateWrapperStates();
|
||||
|
||||
// Waiting for DOM-changes done in updateWrapperStates before we can start the animation.
|
||||
var dummy = moduleWrapper.parentElement.parentElement.offsetHeight;
|
||||
moduleWrapper.style.opacity = 1;
|
||||
|
||||
clearTimeout(module.showHideTimer);
|
||||
module.showHideTimer = setTimeout(function () {
|
||||
if (typeof callback === "function") {
|
||||
callback();
|
||||
}
|
||||
}, speed);
|
||||
} else {
|
||||
// invoke callback
|
||||
if (typeof callback === "function") {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks for all positions if it has visible content.
|
||||
* If not, if will hide the position to prevent unwanted margins.
|
||||
* This method should be called by the show and hide methods.
|
||||
*
|
||||
* Example:
|
||||
* If the top_bar only contains the update notification. And no update is available,
|
||||
* the update notification is hidden. The top bar still occupies space making for
|
||||
* an ugly top margin. By using this function, the top bar will be hidden if the
|
||||
* update notification is not visible.
|
||||
*/
|
||||
var updateWrapperStates = function () {
|
||||
var positions = ["top_bar", "top_left", "top_center", "top_right", "upper_third", "middle_center", "lower_third", "bottom_left", "bottom_center", "bottom_right", "bottom_bar", "fullscreen_above", "fullscreen_below"];
|
||||
|
||||
positions.forEach(function (position) {
|
||||
var wrapper = selectWrapper(position);
|
||||
var moduleWrappers = wrapper.getElementsByClassName("module");
|
||||
|
||||
var showWrapper = false;
|
||||
Array.prototype.forEach.call(moduleWrappers, function (moduleWrapper) {
|
||||
if (moduleWrapper.style.position === "" || moduleWrapper.style.position === "static") {
|
||||
showWrapper = true;
|
||||
}
|
||||
});
|
||||
|
||||
wrapper.style.display = showWrapper ? "block" : "none";
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads the core config and combines it with the system defaults.
|
||||
*/
|
||||
var loadConfig = function () {
|
||||
// FIXME: Think about how to pass config around without breaking tests
|
||||
/* eslint-disable */
|
||||
if (typeof config === "undefined") {
|
||||
config = defaults;
|
||||
Log.error("Config file is missing! Please create a config file.");
|
||||
return;
|
||||
}
|
||||
|
||||
config = Object.assign({}, defaults, config);
|
||||
/* eslint-enable */
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds special selectors on a collection of modules.
|
||||
*
|
||||
* @param {Module[]} modules Array of modules.
|
||||
*/
|
||||
var setSelectionMethodsForModules = function (modules) {
|
||||
/**
|
||||
* Filter modules with the specified classes.
|
||||
*
|
||||
* @param {string|string[]} className one or multiple classnames (array or space divided).
|
||||
*
|
||||
* @returns {Module[]} Filtered collection of modules.
|
||||
*/
|
||||
var withClass = function (className) {
|
||||
return modulesByClass(className, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Filter modules without the specified classes.
|
||||
*
|
||||
* @param {string|string[]} className one or multiple classnames (array or space divided).
|
||||
*
|
||||
* @returns {Module[]} Filtered collection of modules.
|
||||
*/
|
||||
var exceptWithClass = function (className) {
|
||||
return modulesByClass(className, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters a collection of modules based on classname(s).
|
||||
*
|
||||
* @param {string|string[]} className one or multiple classnames (array or space divided).
|
||||
* @param {boolean} include if the filter should include or exclude the modules with the specific classes.
|
||||
*
|
||||
* @returns {Module[]} Filtered collection of modules.
|
||||
*/
|
||||
var modulesByClass = function (className, include) {
|
||||
var searchClasses = className;
|
||||
if (typeof className === "string") {
|
||||
searchClasses = className.split(" ");
|
||||
}
|
||||
|
||||
var newModules = modules.filter(function (module) {
|
||||
var classes = module.data.classes.toLowerCase().split(" ");
|
||||
|
||||
for (var c in searchClasses) {
|
||||
var searchClass = searchClasses[c];
|
||||
if (classes.indexOf(searchClass.toLowerCase()) !== -1) {
|
||||
return include;
|
||||
}
|
||||
}
|
||||
|
||||
return !include;
|
||||
});
|
||||
|
||||
setSelectionMethodsForModules(newModules);
|
||||
return newModules;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes a module instance from the collection.
|
||||
*
|
||||
* @param {object} module The module instance to remove from the collection.
|
||||
*
|
||||
* @returns {Module[]} Filtered collection of modules.
|
||||
*/
|
||||
var exceptModule = function (module) {
|
||||
var newModules = modules.filter(function (mod) {
|
||||
return mod.identifier !== module.identifier;
|
||||
});
|
||||
|
||||
setSelectionMethodsForModules(newModules);
|
||||
return newModules;
|
||||
};
|
||||
|
||||
/**
|
||||
* Walks thru a collection of modules and executes the callback with the module as an argument.
|
||||
*
|
||||
* @param {Function} callback The function to execute with the module as an argument.
|
||||
*/
|
||||
var enumerate = function (callback) {
|
||||
modules.map(function (module) {
|
||||
callback(module);
|
||||
});
|
||||
};
|
||||
|
||||
if (typeof modules.withClass === "undefined") {
|
||||
Object.defineProperty(modules, "withClass", { value: withClass, enumerable: false });
|
||||
}
|
||||
if (typeof modules.exceptWithClass === "undefined") {
|
||||
Object.defineProperty(modules, "exceptWithClass", { value: exceptWithClass, enumerable: false });
|
||||
}
|
||||
if (typeof modules.exceptModule === "undefined") {
|
||||
Object.defineProperty(modules, "exceptModule", { value: exceptModule, enumerable: false });
|
||||
}
|
||||
if (typeof modules.enumerate === "undefined") {
|
||||
Object.defineProperty(modules, "enumerate", { value: enumerate, enumerable: false });
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
/* Public Methods */
|
||||
|
||||
/**
|
||||
* Main init method.
|
||||
*/
|
||||
init: function () {
|
||||
Log.info("Initializing MagicMirror.");
|
||||
loadConfig();
|
||||
|
||||
Log.setLogLevel(config.logLevel);
|
||||
|
||||
Translator.loadCoreTranslations(config.language);
|
||||
Loader.loadModules();
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets called when all modules are started.
|
||||
*
|
||||
* @param {Module[]} moduleObjects All module instances.
|
||||
*/
|
||||
modulesStarted: function (moduleObjects) {
|
||||
modules = [];
|
||||
moduleObjects.forEach((module) => modules.push(module));
|
||||
|
||||
Log.info("All modules started!");
|
||||
sendNotification("ALL_MODULES_STARTED");
|
||||
|
||||
createDomObjects();
|
||||
},
|
||||
|
||||
/**
|
||||
* Send a notification to all modules.
|
||||
*
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
* @param {Module} sender The module that sent the notification.
|
||||
*/
|
||||
sendNotification: function (notification, payload, sender) {
|
||||
if (arguments.length < 3) {
|
||||
Log.error("sendNotification: Missing arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof notification !== "string") {
|
||||
Log.error("sendNotification: Notification should be a string.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(sender instanceof Module)) {
|
||||
Log.error("sendNotification: Sender should be a module.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Further implementation is done in the private method.
|
||||
sendNotification(notification, payload, sender);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the dom for a specific module.
|
||||
*
|
||||
* @param {Module} module The module that needs an update.
|
||||
* @param {number} [speed] The number of microseconds for the animation.
|
||||
*/
|
||||
updateDom: function (module, speed) {
|
||||
if (!(module instanceof Module)) {
|
||||
Log.error("updateDom: Sender should be a module.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Further implementation is done in the private method.
|
||||
updateDom(module, speed);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a collection of all modules currently active.
|
||||
*
|
||||
* @returns {Module[]} A collection of all modules currently active.
|
||||
*/
|
||||
getModules: function () {
|
||||
setSelectionMethodsForModules(modules);
|
||||
return modules;
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the module.
|
||||
*
|
||||
* @param {Module} module The module to hide.
|
||||
* @param {number} speed The speed of the hide animation.
|
||||
* @param {Function} callback Called when the animation is done.
|
||||
* @param {object} [options] Optional settings for the hide method.
|
||||
*/
|
||||
hideModule: function (module, speed, callback, options) {
|
||||
module.hidden = true;
|
||||
hideModule(module, speed, callback, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the module.
|
||||
*
|
||||
* @param {Module} module The module to show.
|
||||
* @param {number} speed The speed of the show animation.
|
||||
* @param {Function} callback Called when the animation is done.
|
||||
* @param {object} [options] Optional settings for the show method.
|
||||
*/
|
||||
showModule: function (module, speed, callback, options) {
|
||||
// do not change module.hidden yet, only if we really show it later
|
||||
showModule(module, speed, callback, options);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// Add polyfill for Object.assign.
|
||||
if (typeof Object.assign !== "function") {
|
||||
(function () {
|
||||
Object.assign = function (target) {
|
||||
"use strict";
|
||||
if (target === undefined || target === null) {
|
||||
throw new TypeError("Cannot convert undefined or null to object");
|
||||
}
|
||||
var output = Object(target);
|
||||
for (var index = 1; index < arguments.length; index++) {
|
||||
var source = arguments[index];
|
||||
if (source !== undefined && source !== null) {
|
||||
for (var nextKey in source) {
|
||||
if (source.hasOwnProperty(nextKey)) {
|
||||
output[nextKey] = source[nextKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
};
|
||||
})();
|
||||
}
|
||||
|
||||
jQuery.fn.outerHTML = function(s) {
|
||||
return s
|
||||
? this.before(s).remove()
|
||||
: jQuery("<p>").append(this.eq(0).clone()).html();
|
||||
};
|
||||
|
||||
function roundVal(temp)
|
||||
{
|
||||
return Math.round(temp * 10) / 10;
|
||||
}
|
||||
|
||||
jQuery(document).ready(function($) {
|
||||
|
||||
var eventList = [];
|
||||
|
||||
var lastCompliment;
|
||||
var compliment;
|
||||
|
||||
moment.locale(config.lang);
|
||||
|
||||
//connect do Xbee monitor
|
||||
// var socket = io.connect('http://rpi-alarm.local:8082');
|
||||
// socket.on('dishwasher', function (dishwasherReady) {
|
||||
// if (dishwasherReady) {
|
||||
// $('.dishwasher').fadeIn(2000);
|
||||
// $('.lower-third').fadeOut(2000);
|
||||
// } else {
|
||||
// $('.dishwasher').fadeOut(2000);
|
||||
// $('.lower-third').fadeIn(2000);
|
||||
// }
|
||||
// });
|
||||
|
||||
version.init();
|
||||
|
||||
time.init();
|
||||
|
||||
calendar.init();
|
||||
|
||||
compliments.init();
|
||||
|
||||
weather.init();
|
||||
|
||||
news.init();
|
||||
|
||||
});
|
||||
MM.init();
|
||||
|
||||
543
js/module.js
Normal file
543
js/module.js
Normal file
@@ -0,0 +1,543 @@
|
||||
/* global Class, cloneObject, Loader, MMSocket, nunjucks, Translator */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module Blueprint.
|
||||
* @typedef {Object} Module
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*
|
||||
*/
|
||||
var Module = Class.extend({
|
||||
/*********************************************************
|
||||
* All methods (and properties) below can be subclassed. *
|
||||
*********************************************************/
|
||||
|
||||
// Set the minimum MagicMirror module version for this module.
|
||||
requiresVersion: "2.0.0",
|
||||
|
||||
// Module config defaults.
|
||||
defaults: {},
|
||||
|
||||
// Timer reference used for showHide animation callbacks.
|
||||
showHideTimer: null,
|
||||
|
||||
// Array to store lockStrings. These strings are used to lock
|
||||
// visibility when hiding and showing module.
|
||||
lockStrings: [],
|
||||
|
||||
// Storage of the nunjuck Environment,
|
||||
// This should not be referenced directly.
|
||||
// Use the nunjucksEnvironment() to get it.
|
||||
_nunjucksEnvironment: null,
|
||||
|
||||
/**
|
||||
* Called when the module is instantiated.
|
||||
*/
|
||||
init: function () {
|
||||
//Log.log(this.defaults);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the module is started.
|
||||
*/
|
||||
start: function () {
|
||||
Log.info("Starting module: " + this.name);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a list of scripts the module requires to be loaded.
|
||||
*
|
||||
* @returns {string[]} An array with filenames.
|
||||
*/
|
||||
getScripts: function () {
|
||||
return [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a list of stylesheets the module requires to be loaded.
|
||||
*
|
||||
* @returns {string[]} An array with filenames.
|
||||
*/
|
||||
getStyles: function () {
|
||||
return [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a map of translation files the module requires to be loaded.
|
||||
*
|
||||
* return Map<String, String> -
|
||||
*
|
||||
* @returns {*} A map with langKeys and filenames.
|
||||
*/
|
||||
getTranslations: function () {
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Generates the dom which needs to be displayed. This method is called by the Magic Mirror core.
|
||||
* This method can to be subclassed if the module wants to display info on the mirror.
|
||||
* Alternatively, the getTemplate method could be subclassed.
|
||||
*
|
||||
* @returns {HTMLElement|Promise} The dom or a promise with the dom to display.
|
||||
*/
|
||||
getDom: function () {
|
||||
var self = this;
|
||||
return new Promise(function (resolve) {
|
||||
var div = document.createElement("div");
|
||||
var template = self.getTemplate();
|
||||
var templateData = self.getTemplateData();
|
||||
|
||||
// Check to see if we need to render a template string or a file.
|
||||
if (/^.*((\.html)|(\.njk))$/.test(template)) {
|
||||
// the template is a filename
|
||||
self.nunjucksEnvironment().render(template, templateData, function (err, res) {
|
||||
if (err) {
|
||||
Log.error(err);
|
||||
}
|
||||
|
||||
div.innerHTML = res;
|
||||
|
||||
resolve(div);
|
||||
});
|
||||
} else {
|
||||
// the template is a template string.
|
||||
div.innerHTML = self.nunjucksEnvironment().renderString(template, templateData);
|
||||
|
||||
resolve(div);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Generates the header string which needs to be displayed if a user has a header configured for this module.
|
||||
* This method is called by the Magic Mirror core, but only if the user has configured a default header for the module.
|
||||
* This method needs to be subclassed if the module wants to display modified headers on the mirror.
|
||||
*
|
||||
* @returns {string} The header to display above the header.
|
||||
*/
|
||||
getHeader: function () {
|
||||
return this.data.header;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the template for the module which is used by the default getDom implementation.
|
||||
* This method needs to be subclassed if the module wants to use a template.
|
||||
* It can either return a template sting, or a template filename.
|
||||
* If the string ends with '.html' it's considered a file from within the module's folder.
|
||||
*
|
||||
* @returns {string} The template string of filename.
|
||||
*/
|
||||
getTemplate: function () {
|
||||
return '<div class="normal">' + this.name + '</div><div class="small dimmed">' + this.identifier + "</div>";
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the data to be used in the template.
|
||||
* This method needs to be subclassed if the module wants to use a custom data.
|
||||
*
|
||||
* @returns {object} The data for the template
|
||||
*/
|
||||
getTemplateData: function () {
|
||||
return {};
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by the Magic Mirror core when a notification arrives.
|
||||
*
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
* @param {Module} sender The module that sent the notification.
|
||||
*/
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
if (sender) {
|
||||
// Log.log(this.name + " received a module notification: " + notification + " from sender: " + sender.name);
|
||||
} else {
|
||||
// Log.log(this.name + " received a system notification: " + notification);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the nunjucks environment for the current module.
|
||||
* The environment is checked in the _nunjucksEnvironment instance variable.
|
||||
*
|
||||
* @returns {object} The Nunjucks Environment
|
||||
*/
|
||||
nunjucksEnvironment: function () {
|
||||
if (this._nunjucksEnvironment !== null) {
|
||||
return this._nunjucksEnvironment;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
this._nunjucksEnvironment = new nunjucks.Environment(new nunjucks.WebLoader(this.file(""), { async: true }), {
|
||||
trimBlocks: true,
|
||||
lstripBlocks: true
|
||||
});
|
||||
|
||||
this._nunjucksEnvironment.addFilter("translate", function (str, variables) {
|
||||
return self.translate(str, variables);
|
||||
});
|
||||
|
||||
return this._nunjucksEnvironment;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a socket notification arrives.
|
||||
*
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
*/
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
Log.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload);
|
||||
},
|
||||
|
||||
/*
|
||||
* Called when the module is hidden.
|
||||
*/
|
||||
suspend: function () {
|
||||
Log.log(this.name + " is suspended.");
|
||||
},
|
||||
|
||||
/*
|
||||
* Called when the module is shown.
|
||||
*/
|
||||
resume: function () {
|
||||
Log.log(this.name + " is resumed.");
|
||||
},
|
||||
|
||||
/*********************************************
|
||||
* The methods below don"t need subclassing. *
|
||||
*********************************************/
|
||||
|
||||
/**
|
||||
* Set the module data.
|
||||
*
|
||||
* @param {Module} data The module data
|
||||
*/
|
||||
setData: function (data) {
|
||||
this.data = data;
|
||||
this.name = data.name;
|
||||
this.identifier = data.identifier;
|
||||
this.hidden = false;
|
||||
|
||||
this.setConfig(data.config, data.configDeepMerge);
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the module config and combine it with the module defaults.
|
||||
*
|
||||
* @param {object} config The combined module config.
|
||||
* @param {boolean} deep Merge module config in deep.
|
||||
*/
|
||||
setConfig: function (config, deep) {
|
||||
this.config = deep ? configMerge({}, this.defaults, config) : Object.assign({}, this.defaults, config);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a socket object. If it doesn't exist, it's created.
|
||||
* It also registers the notification callback.
|
||||
*
|
||||
* @returns {MMSocket} a socket object
|
||||
*/
|
||||
socket: function () {
|
||||
if (typeof this._socket === "undefined") {
|
||||
this._socket = new MMSocket(this.name);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this._socket.setNotificationCallback(function (notification, payload) {
|
||||
self.socketNotificationReceived(notification, payload);
|
||||
});
|
||||
|
||||
return this._socket;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the path to a module file.
|
||||
*
|
||||
* @param {string} file Filename
|
||||
* @returns {string} the file path
|
||||
*/
|
||||
file: function (file) {
|
||||
return (this.data.path + "/" + file).replace("//", "/");
|
||||
},
|
||||
|
||||
/**
|
||||
* Load all required stylesheets by requesting the MM object to load the files.
|
||||
*
|
||||
* @param {Function} callback Function called when done.
|
||||
*/
|
||||
loadStyles: function (callback) {
|
||||
this.loadDependencies("getStyles", callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Load all required scripts by requesting the MM object to load the files.
|
||||
*
|
||||
* @param {Function} callback Function called when done.
|
||||
*/
|
||||
loadScripts: function (callback) {
|
||||
this.loadDependencies("getScripts", callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper method to load all dependencies.
|
||||
*
|
||||
* @param {string} funcName Function name to call to get scripts or styles.
|
||||
* @param {Function} callback Function called when done.
|
||||
*/
|
||||
loadDependencies: function (funcName, callback) {
|
||||
var self = this;
|
||||
var dependencies = this[funcName]();
|
||||
|
||||
var loadNextDependency = function () {
|
||||
if (dependencies.length > 0) {
|
||||
var nextDependency = dependencies[0];
|
||||
Loader.loadFile(nextDependency, self, function () {
|
||||
dependencies = dependencies.slice(1);
|
||||
loadNextDependency();
|
||||
});
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
loadNextDependency();
|
||||
},
|
||||
|
||||
/**
|
||||
* Load all translations.
|
||||
*
|
||||
* @param {Function} callback Function called when done.
|
||||
*/
|
||||
loadTranslations: function (callback) {
|
||||
var self = this;
|
||||
var translations = this.getTranslations();
|
||||
var lang = config.language.toLowerCase();
|
||||
|
||||
// The variable `first` will contain the first
|
||||
// defined translation after the following line.
|
||||
for (var first in translations) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (translations) {
|
||||
var translationFile = translations[lang] || undefined;
|
||||
var translationsFallbackFile = translations[first];
|
||||
|
||||
// If a translation file is set, load it and then also load the fallback translation file.
|
||||
// Otherwise only load the fallback translation file.
|
||||
if (translationFile !== undefined && translationFile !== translationsFallbackFile) {
|
||||
Translator.load(self, translationFile, false, function () {
|
||||
Translator.load(self, translationsFallbackFile, true, callback);
|
||||
});
|
||||
} else {
|
||||
Translator.load(self, translationsFallbackFile, true, callback);
|
||||
}
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Request the translation for a given key with optional variables and default value.
|
||||
*
|
||||
* @param {string} key The key of the string to translate
|
||||
* @param {string|object} [defaultValueOrVariables] The default value or variables for translating.
|
||||
* @param {string} [defaultValue] The default value with variables.
|
||||
* @returns {string} the translated key
|
||||
*/
|
||||
translate: function (key, defaultValueOrVariables, defaultValue) {
|
||||
if (typeof defaultValueOrVariables === "object") {
|
||||
return Translator.translate(this, key, defaultValueOrVariables) || defaultValue || "";
|
||||
}
|
||||
return Translator.translate(this, key) || defaultValueOrVariables || "";
|
||||
},
|
||||
|
||||
/**
|
||||
* Request an (animated) update of the module.
|
||||
*
|
||||
* @param {number} [speed] The speed of the animation.
|
||||
*/
|
||||
updateDom: function (speed) {
|
||||
MM.updateDom(this, speed);
|
||||
},
|
||||
|
||||
/**
|
||||
* Send a notification to all modules.
|
||||
*
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
*/
|
||||
sendNotification: function (notification, payload) {
|
||||
MM.sendNotification(notification, payload, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Send a socket notification to the node helper.
|
||||
*
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
*/
|
||||
sendSocketNotification: function (notification, payload) {
|
||||
this.socket().sendNotification(notification, payload);
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide this module.
|
||||
*
|
||||
* @param {number} speed The speed of the hide animation.
|
||||
* @param {Function} callback Called when the animation is done.
|
||||
* @param {object} [options] Optional settings for the hide method.
|
||||
*/
|
||||
hide: function (speed, callback, options) {
|
||||
if (typeof callback === "object") {
|
||||
options = callback;
|
||||
callback = function () {};
|
||||
}
|
||||
|
||||
callback = callback || function () {};
|
||||
options = options || {};
|
||||
|
||||
var self = this;
|
||||
MM.hideModule(
|
||||
self,
|
||||
speed,
|
||||
function () {
|
||||
self.suspend();
|
||||
callback();
|
||||
},
|
||||
options
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Show this module.
|
||||
*
|
||||
* @param {number} speed The speed of the show animation.
|
||||
* @param {Function} callback Called when the animation is done.
|
||||
* @param {object} [options] Optional settings for the show method.
|
||||
*/
|
||||
show: function (speed, callback, options) {
|
||||
if (typeof callback === "object") {
|
||||
options = callback;
|
||||
callback = function () {};
|
||||
}
|
||||
|
||||
callback = callback || function () {};
|
||||
options = options || {};
|
||||
|
||||
var self = this;
|
||||
MM.showModule(
|
||||
this,
|
||||
speed,
|
||||
function () {
|
||||
self.resume();
|
||||
callback();
|
||||
},
|
||||
options
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Merging MagicMirror (or other) default/config script by @bugsounet
|
||||
* Merge 2 objects or/with array
|
||||
*
|
||||
* Usage:
|
||||
* -------
|
||||
* this.config = configMerge({}, this.defaults, this.config)
|
||||
* -------
|
||||
* arg1: initial object
|
||||
* arg2: config model
|
||||
* arg3: config to merge
|
||||
* -------
|
||||
* why using it ?
|
||||
* Object.assign() function don't to all job
|
||||
* it don't merge all thing in deep
|
||||
* -> object in object and array is not merging
|
||||
* -------
|
||||
*
|
||||
* Todo: idea of Mich determinate what do you want to merge or not
|
||||
*
|
||||
* @param {object} result the initial object
|
||||
* @returns {object} the merged config
|
||||
*/
|
||||
function configMerge(result) {
|
||||
var stack = Array.prototype.slice.call(arguments, 1);
|
||||
var item;
|
||||
var key;
|
||||
while (stack.length) {
|
||||
item = stack.shift();
|
||||
for (key in item) {
|
||||
if (item.hasOwnProperty(key)) {
|
||||
if (typeof result[key] === "object" && result[key] && Object.prototype.toString.call(result[key]) !== "[object Array]") {
|
||||
if (typeof item[key] === "object" && item[key] !== null) {
|
||||
result[key] = configMerge({}, result[key], item[key]);
|
||||
} else {
|
||||
result[key] = item[key];
|
||||
}
|
||||
} else {
|
||||
result[key] = item[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Module.definitions = {};
|
||||
|
||||
Module.create = function (name) {
|
||||
// Make sure module definition is available.
|
||||
if (!Module.definitions[name]) {
|
||||
return;
|
||||
}
|
||||
|
||||
var moduleDefinition = Module.definitions[name];
|
||||
var clonedDefinition = cloneObject(moduleDefinition);
|
||||
|
||||
// Note that we clone the definition. Otherwise the objects are shared, which gives problems.
|
||||
var ModuleClass = Module.extend(clonedDefinition);
|
||||
|
||||
return new ModuleClass();
|
||||
};
|
||||
|
||||
Module.register = function (name, moduleDefinition) {
|
||||
if (moduleDefinition.requiresVersion) {
|
||||
Log.log("Check MagicMirror version for module '" + name + "' - Minimum version: " + moduleDefinition.requiresVersion + " - Current version: " + window.version);
|
||||
if (cmpVersions(window.version, moduleDefinition.requiresVersion) >= 0) {
|
||||
Log.log("Version is ok!");
|
||||
} else {
|
||||
Log.warn("Version is incorrect. Skip module: '" + name + "'");
|
||||
return;
|
||||
}
|
||||
}
|
||||
Log.log("Module registered: " + name);
|
||||
Module.definitions[name] = moduleDefinition;
|
||||
};
|
||||
|
||||
/**
|
||||
* Compare two semantic version numbers and return the difference.
|
||||
*
|
||||
* @param {string} a Version number a.
|
||||
* @param {string} b Version number b.
|
||||
* @returns {number} A positive number if a is larger than b, a negative
|
||||
* number if a is smaller and 0 if they are the same
|
||||
*/
|
||||
function cmpVersions(a, b) {
|
||||
var i, diff;
|
||||
var regExStrip0 = /(\.0+)+$/;
|
||||
var segmentsA = a.replace(regExStrip0, "").split(".");
|
||||
var segmentsB = b.replace(regExStrip0, "").split(".");
|
||||
var l = Math.min(segmentsA.length, segmentsB.length);
|
||||
|
||||
for (i = 0; i < l; i++) {
|
||||
diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10);
|
||||
if (diff) {
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
return segmentsA.length - segmentsB.length;
|
||||
}
|
||||
80
js/moment-with-locales.min.js
vendored
80
js/moment-with-locales.min.js
vendored
File diff suppressed because one or more lines are too long
152
js/news/news.js
152
js/news/news.js
@@ -1,152 +0,0 @@
|
||||
// A lot of this code is from the original feedToJson function that was included with this project
|
||||
// The new code allows for multiple feeds to be used but a bunch of variables and such have literally been copied and pasted into this code and some help from here: http://jsfiddle.net/BDK46/
|
||||
// The original version can be found here: http://airshp.com/2011/jquery-plugin-feed-to-json/
|
||||
var news = {
|
||||
feed: config.news.feed || null,
|
||||
newsLocation: '.news',
|
||||
newsItems: [],
|
||||
seenNewsItem: [],
|
||||
_yqURL: 'https://query.yahooapis.com/v1/public/yql',
|
||||
_yqlQS: '?format=json&q=select%20*%20from%20rss%20where%20url%3D',
|
||||
_cacheBuster: Math.floor((new Date().getTime()) / 1200 / 1000),
|
||||
_failedAttempts: 0,
|
||||
fetchInterval: config.news.fetchInterval || 60000,
|
||||
updateInterval: config.news.interval || 5500,
|
||||
fadeInterval: 2000,
|
||||
intervalId: null,
|
||||
fetchNewsIntervalId: null
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the query string that will be used to grab a converted RSS feed into a JSON object via Yahoo
|
||||
* @param {string} feed The original location of the RSS feed
|
||||
* @return {string} The new location of the RSS feed provided by Yahoo
|
||||
*/
|
||||
news.buildQueryString = function (feed) {
|
||||
|
||||
return this._yqURL + this._yqlQS + '\'' + encodeURIComponent(feed) + '\'';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the news for each feed provided in the config file
|
||||
*/
|
||||
news.fetchNews = function () {
|
||||
|
||||
// Reset the news feed
|
||||
this.newsItems = [];
|
||||
|
||||
this.feed.forEach(function (_curr) {
|
||||
|
||||
var _yqUrlString = this.buildQueryString(_curr);
|
||||
this.fetchFeed(_yqUrlString);
|
||||
|
||||
}.bind(this));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a GET request to Yahoo's service
|
||||
* @param {string} yqUrl The URL being used to grab the RSS feed (in JSON format)
|
||||
*/
|
||||
news.fetchFeed = function (yqUrl) {
|
||||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
datatype:'jsonp',
|
||||
url: yqUrl,
|
||||
success: function (data) {
|
||||
|
||||
if (data.query.count > 0) {
|
||||
this.parseFeed(data.query.results.item);
|
||||
} else {
|
||||
console.error('No feed results for: ' + yqUrl);
|
||||
}
|
||||
|
||||
}.bind(this),
|
||||
error: function () {
|
||||
// non-specific error message that should be updated
|
||||
console.error('No feed results for: ' + yqUrl);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses each item in a single news feed
|
||||
* @param {Object} data The news feed that was returned by Yahoo
|
||||
* @return {boolean} Confirms that the feed was parsed correctly
|
||||
*/
|
||||
news.parseFeed = function (data) {
|
||||
|
||||
var _rssItems = [];
|
||||
|
||||
for (var i = 0, count = data.length; i < count; i++) {
|
||||
|
||||
_rssItems.push(data[i].title);
|
||||
|
||||
}
|
||||
|
||||
this.newsItems = this.newsItems.concat(_rssItems);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through each available and unseen news feed after it has been retrieved from Yahoo and shows it on the screen
|
||||
* When all news titles have been exhausted, the list resets and randomly chooses from the original set of items
|
||||
* @return {boolean} Confirms that there is a list of news items to loop through and that one has been shown on the screen
|
||||
*/
|
||||
news.showNews = function () {
|
||||
|
||||
// If all items have been seen, swap seen to unseen
|
||||
if (this.newsItems.length === 0 && this.seenNewsItem.length !== 0) {
|
||||
|
||||
if (this._failedAttempts === 20) {
|
||||
console.error('Failed to show a news story 20 times, stopping any attempts');
|
||||
return false;
|
||||
}
|
||||
|
||||
this._failedAttempts++;
|
||||
|
||||
setTimeout(function () {
|
||||
this.showNews();
|
||||
}.bind(this), 3000);
|
||||
|
||||
} else if (this.newsItems.length === 0 && this.seenNewsItem.length !== 0) {
|
||||
this.newsItems = this.seenNewsItem.splice(0);
|
||||
}
|
||||
|
||||
var _location = Math.floor(Math.random() * this.newsItems.length);
|
||||
|
||||
var _item = news.newsItems.splice(_location, 1)[0];
|
||||
|
||||
this.seenNewsItem.push(_item);
|
||||
|
||||
$(this.newsLocation).updateWithText(_item, this.fadeInterval);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
news.init = function () {
|
||||
|
||||
if (this.feed === null || (this.feed instanceof Array === false && typeof this.feed !== 'string')) {
|
||||
return false;
|
||||
} else if (typeof this.feed === 'string') {
|
||||
this.feed = [this.feed];
|
||||
}
|
||||
|
||||
this.fetchNews();
|
||||
this.showNews();
|
||||
|
||||
this.fetchNewsIntervalId = setInterval(function () {
|
||||
this.fetchNews()
|
||||
}.bind(this), this.fetchInterval)
|
||||
|
||||
this.intervalId = setInterval(function () {
|
||||
this.showNews();
|
||||
}.bind(this), this.updateInterval);
|
||||
|
||||
}
|
||||
126
js/node_helper.js
Normal file
126
js/node_helper.js
Normal file
@@ -0,0 +1,126 @@
|
||||
/* Magic Mirror
|
||||
* Node Helper Superclass
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const Class = require("./class.js");
|
||||
const Log = require("./logger.js");
|
||||
const express = require("express");
|
||||
|
||||
var NodeHelper = Class.extend({
|
||||
init: function () {
|
||||
Log.log("Initializing new module helper ...");
|
||||
},
|
||||
|
||||
loaded: function (callback) {
|
||||
Log.log("Module helper loaded: " + this.name);
|
||||
callback();
|
||||
},
|
||||
|
||||
start: function () {
|
||||
Log.log("Starting module helper: " + this.name);
|
||||
},
|
||||
|
||||
/* stop()
|
||||
* Called when the MagicMirror server receives a `SIGINT`
|
||||
* Close any open connections, stop any sub-processes and
|
||||
* gracefully exit the module.
|
||||
*
|
||||
*/
|
||||
stop: function () {
|
||||
Log.log("Stopping module helper: " + this.name);
|
||||
},
|
||||
|
||||
/* socketNotificationReceived(notification, payload)
|
||||
* This method is called when a socket notification arrives.
|
||||
*
|
||||
* argument notification string - The identifier of the notification.
|
||||
* argument payload mixed - The payload of the notification.
|
||||
*/
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
Log.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload);
|
||||
},
|
||||
|
||||
/* setName(name)
|
||||
* Set the module name.
|
||||
*
|
||||
* argument name string - Module name.
|
||||
*/
|
||||
setName: function (name) {
|
||||
this.name = name;
|
||||
},
|
||||
|
||||
/* setPath(path)
|
||||
* Set the module path.
|
||||
*
|
||||
* argument path string - Module path.
|
||||
*/
|
||||
setPath: function (path) {
|
||||
this.path = path;
|
||||
},
|
||||
|
||||
/* sendSocketNotification(notification, payload)
|
||||
* Send a socket notification to the node helper.
|
||||
*
|
||||
* argument notification string - The identifier of the notification.
|
||||
* argument payload mixed - The payload of the notification.
|
||||
*/
|
||||
sendSocketNotification: function (notification, payload) {
|
||||
this.io.of(this.name).emit(notification, payload);
|
||||
},
|
||||
|
||||
/* setExpressApp(app)
|
||||
* Sets the express app object for this module.
|
||||
* This allows you to host files from the created webserver.
|
||||
*
|
||||
* argument app Express app - The Express app object.
|
||||
*/
|
||||
setExpressApp: function (app) {
|
||||
this.expressApp = app;
|
||||
|
||||
var publicPath = this.path + "/public";
|
||||
app.use("/" + this.name, express.static(publicPath));
|
||||
},
|
||||
|
||||
/* setSocketIO(io)
|
||||
* Sets the socket io object for this module.
|
||||
* Binds message receiver.
|
||||
*
|
||||
* argument io Socket.io - The Socket io object.
|
||||
*/
|
||||
setSocketIO: function (io) {
|
||||
var self = this;
|
||||
self.io = io;
|
||||
|
||||
Log.log("Connecting socket for: " + this.name);
|
||||
var namespace = this.name;
|
||||
io.of(namespace).on("connection", function (socket) {
|
||||
// add a catch all event.
|
||||
var onevent = socket.onevent;
|
||||
socket.onevent = function (packet) {
|
||||
var args = packet.data || [];
|
||||
onevent.call(this, packet); // original call
|
||||
packet.data = ["*"].concat(args);
|
||||
onevent.call(this, packet); // additional call to catch-all
|
||||
};
|
||||
|
||||
// register catch all.
|
||||
socket.on("*", function (notification, payload) {
|
||||
if (notification !== "*") {
|
||||
//Log.log('received message in namespace: ' + namespace);
|
||||
self.socketNotificationReceived(notification, payload);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
NodeHelper.create = function (moduleDefinition) {
|
||||
return NodeHelper.extend(moduleDefinition);
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = NodeHelper;
|
||||
}
|
||||
1910
js/rrule.js
1910
js/rrule.js
File diff suppressed because it is too large
Load Diff
88
js/server.js
Normal file
88
js/server.js
Normal file
@@ -0,0 +1,88 @@
|
||||
/* Magic Mirror
|
||||
* Server
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
var express = require("express");
|
||||
var app = require("express")();
|
||||
var path = require("path");
|
||||
var ipfilter = require("express-ipfilter").IpFilter;
|
||||
var fs = require("fs");
|
||||
var helmet = require("helmet");
|
||||
|
||||
var Log = require("./logger.js");
|
||||
var Utils = require("./utils.js");
|
||||
|
||||
var Server = function (config, callback) {
|
||||
var port = config.port;
|
||||
if (process.env.MM_PORT) {
|
||||
port = process.env.MM_PORT;
|
||||
}
|
||||
|
||||
var server = null;
|
||||
if (config.useHttps) {
|
||||
var options = {
|
||||
key: fs.readFileSync(config.httpsPrivateKey),
|
||||
cert: fs.readFileSync(config.httpsCertificate)
|
||||
};
|
||||
server = require("https").Server(options, app);
|
||||
} else {
|
||||
server = require("http").Server(app);
|
||||
}
|
||||
var io = require("socket.io")(server);
|
||||
|
||||
Log.log("Starting server on port " + port + " ... ");
|
||||
|
||||
server.listen(port, config.address ? config.address : "localhost");
|
||||
|
||||
if (config.ipWhitelist instanceof Array && config.ipWhitelist.length === 0) {
|
||||
Log.warn(Utils.colors.warn("You're using a full whitelist configuration to allow for all IPs"));
|
||||
}
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
var result = ipfilter(config.ipWhitelist, { mode: config.ipWhitelist.length === 0 ? "deny" : "allow", log: false })(req, res, function (err) {
|
||||
if (err === undefined) {
|
||||
return next();
|
||||
}
|
||||
Log.log(err.message);
|
||||
res.status(403).send("This device is not allowed to access your mirror. <br> Please check your config.js or config.js.sample to change this.");
|
||||
});
|
||||
});
|
||||
app.use(helmet({ contentSecurityPolicy: false }));
|
||||
|
||||
app.use("/js", express.static(__dirname));
|
||||
var directories = ["/config", "/css", "/fonts", "/modules", "/vendor", "/translations", "/tests/configs"];
|
||||
var directory;
|
||||
for (var i in directories) {
|
||||
directory = directories[i];
|
||||
app.use(directory, express.static(path.resolve(global.root_path + directory)));
|
||||
}
|
||||
|
||||
app.get("/version", function (req, res) {
|
||||
res.send(global.version);
|
||||
});
|
||||
|
||||
app.get("/config", function (req, res) {
|
||||
res.send(config);
|
||||
});
|
||||
|
||||
app.get("/", function (req, res) {
|
||||
var html = fs.readFileSync(path.resolve(global.root_path + "/index.html"), { encoding: "utf8" });
|
||||
html = html.replace("#VERSION#", global.version);
|
||||
|
||||
var configFile = "config/config.js";
|
||||
if (typeof global.configuration_file !== "undefined") {
|
||||
configFile = global.configuration_file;
|
||||
}
|
||||
html = html.replace("#CONFIG_FILE#", configFile);
|
||||
|
||||
res.send(html);
|
||||
});
|
||||
|
||||
if (typeof callback === "function") {
|
||||
callback(app, io);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = Server;
|
||||
2
js/socket.io.min.js
vendored
2
js/socket.io.min.js
vendored
File diff suppressed because one or more lines are too long
54
js/socketclient.js
Normal file
54
js/socketclient.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/* global io */
|
||||
|
||||
/* Magic Mirror
|
||||
* TODO add description
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
var MMSocket = function (moduleName) {
|
||||
var self = this;
|
||||
|
||||
if (typeof moduleName !== "string") {
|
||||
throw new Error("Please set the module name for the MMSocket.");
|
||||
}
|
||||
|
||||
self.moduleName = moduleName;
|
||||
|
||||
// Private Methods
|
||||
var base = "/";
|
||||
if (typeof config !== "undefined" && typeof config.basePath !== "undefined") {
|
||||
base = config.basePath;
|
||||
}
|
||||
self.socket = io("/" + self.moduleName, {
|
||||
path: base + "socket.io"
|
||||
});
|
||||
var notificationCallback = function () {};
|
||||
|
||||
var onevent = self.socket.onevent;
|
||||
self.socket.onevent = function (packet) {
|
||||
var args = packet.data || [];
|
||||
onevent.call(this, packet); // original call
|
||||
packet.data = ["*"].concat(args);
|
||||
onevent.call(this, packet); // additional call to catch-all
|
||||
};
|
||||
|
||||
// register catch all.
|
||||
self.socket.on("*", function (notification, payload) {
|
||||
if (notification !== "*") {
|
||||
notificationCallback(notification, payload);
|
||||
}
|
||||
});
|
||||
|
||||
// Public Methods
|
||||
this.setNotificationCallback = function (callback) {
|
||||
notificationCallback = callback;
|
||||
};
|
||||
|
||||
this.sendNotification = function (notification, payload) {
|
||||
if (typeof payload === "undefined") {
|
||||
payload = {};
|
||||
}
|
||||
self.socket.emit(notification, payload);
|
||||
};
|
||||
};
|
||||
@@ -1,34 +0,0 @@
|
||||
var time = {
|
||||
timeFormat: config.time.timeFormat || 24,
|
||||
dateLocation: '.date',
|
||||
timeLocation: '.time',
|
||||
updateInterval: 1000,
|
||||
intervalId: null
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the time that is shown on the screen
|
||||
*/
|
||||
time.updateTime = function () {
|
||||
|
||||
var _now = moment(),
|
||||
_date = _now.format('dddd, LL');
|
||||
|
||||
$(this.dateLocation).html(_date);
|
||||
$(this.timeLocation).html(_now.format(this._timeFormat+':mm[<span class="sec">]ss[</span>]'));
|
||||
|
||||
}
|
||||
|
||||
time.init = function () {
|
||||
|
||||
if (parseInt(time.timeFormat) === 12) {
|
||||
time._timeFormat = 'hh'
|
||||
} else {
|
||||
time._timeFormat = 'HH';
|
||||
}
|
||||
|
||||
this.intervalId = setInterval(function () {
|
||||
this.updateTime();
|
||||
}.bind(this), 1000);
|
||||
|
||||
}
|
||||
169
js/translator.js
Normal file
169
js/translator.js
Normal file
@@ -0,0 +1,169 @@
|
||||
/* global translations */
|
||||
|
||||
/* Magic Mirror
|
||||
* Translator (l10n)
|
||||
*
|
||||
* By Christopher Fenner https://github.com/CFenner
|
||||
* MIT Licensed.
|
||||
*/
|
||||
var Translator = (function () {
|
||||
/**
|
||||
* Load a JSON file via XHR.
|
||||
*
|
||||
* @param {string} file Path of the file we want to load.
|
||||
* @param {Function} callback Function called when done.
|
||||
*/
|
||||
function loadJSON(file, callback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.overrideMimeType("application/json");
|
||||
xhr.open("GET", file, true);
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||
// needs error handler try/catch at least
|
||||
let fileinfo = null;
|
||||
try {
|
||||
fileinfo = JSON.parse(xhr.responseText);
|
||||
} catch (exception) {
|
||||
// nothing here, but don't die
|
||||
Log.error(" loading json file =" + file + " failed");
|
||||
}
|
||||
callback(fileinfo);
|
||||
}
|
||||
};
|
||||
xhr.send(null);
|
||||
}
|
||||
|
||||
return {
|
||||
coreTranslations: {},
|
||||
coreTranslationsFallback: {},
|
||||
translations: {},
|
||||
translationsFallback: {},
|
||||
|
||||
/**
|
||||
* Load a translation for a given key for a given module.
|
||||
*
|
||||
* @param {Module} module The module to load the translation for.
|
||||
* @param {string} key The key of the text to translate.
|
||||
* @param {object} variables The variables to use within the translation template (optional)
|
||||
* @returns {string} the translated key
|
||||
*/
|
||||
translate: function (module, key, variables) {
|
||||
variables = variables || {}; //Empty object by default
|
||||
|
||||
/**
|
||||
* Combines template and variables like:
|
||||
* template: "Please wait for {timeToWait} before continuing with {work}."
|
||||
* variables: {timeToWait: "2 hours", work: "painting"}
|
||||
* to: "Please wait for 2 hours before continuing with painting."
|
||||
*
|
||||
* @param {string} template Text with placeholder
|
||||
* @param {object} variables Variables for the placeholder
|
||||
* @returns {string} the template filled with the variables
|
||||
*/
|
||||
function createStringFromTemplate(template, variables) {
|
||||
if (Object.prototype.toString.call(template) !== "[object String]") {
|
||||
return template;
|
||||
}
|
||||
if (variables.fallback && !template.match(new RegExp("{.+}"))) {
|
||||
template = variables.fallback;
|
||||
}
|
||||
return template.replace(new RegExp("{([^}]+)}", "g"), function (_unused, varName) {
|
||||
return varName in variables ? variables[varName] : "{" + varName + "}";
|
||||
});
|
||||
}
|
||||
|
||||
if (this.translations[module.name] && key in this.translations[module.name]) {
|
||||
// Log.log("Got translation for " + key + " from module translation: ");
|
||||
return createStringFromTemplate(this.translations[module.name][key], variables);
|
||||
}
|
||||
|
||||
if (key in this.coreTranslations) {
|
||||
// Log.log("Got translation for " + key + " from core translation.");
|
||||
return createStringFromTemplate(this.coreTranslations[key], variables);
|
||||
}
|
||||
|
||||
if (this.translationsFallback[module.name] && key in this.translationsFallback[module.name]) {
|
||||
// Log.log("Got translation for " + key + " from module translation fallback.");
|
||||
return createStringFromTemplate(this.translationsFallback[module.name][key], variables);
|
||||
}
|
||||
|
||||
if (key in this.coreTranslationsFallback) {
|
||||
// Log.log("Got translation for " + key + " from core translation fallback.");
|
||||
return createStringFromTemplate(this.coreTranslationsFallback[key], variables);
|
||||
}
|
||||
|
||||
return key;
|
||||
},
|
||||
|
||||
/**
|
||||
* Load a translation file (json) and remember the data.
|
||||
*
|
||||
* @param {Module} module The module to load the translation file for.
|
||||
* @param {string} file Path of the file we want to load.
|
||||
* @param {boolean} isFallback Flag to indicate fallback translations.
|
||||
* @param {Function} callback Function called when done.
|
||||
*/
|
||||
load: function (module, file, isFallback, callback) {
|
||||
if (!isFallback) {
|
||||
Log.log(module.name + " - Load translation: " + file);
|
||||
} else {
|
||||
Log.log(module.name + " - Load translation fallback: " + file);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
if (!this.translationsFallback[module.name]) {
|
||||
loadJSON(module.file(file), function (json) {
|
||||
if (!isFallback) {
|
||||
self.translations[module.name] = json;
|
||||
} else {
|
||||
self.translationsFallback[module.name] = json;
|
||||
}
|
||||
callback();
|
||||
});
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the core translations.
|
||||
*
|
||||
* @param {string} lang The language identifier of the core language.
|
||||
*/
|
||||
loadCoreTranslations: function (lang) {
|
||||
var self = this;
|
||||
|
||||
if (lang in translations) {
|
||||
Log.log("Loading core translation file: " + translations[lang]);
|
||||
loadJSON(translations[lang], function (translations) {
|
||||
self.coreTranslations = translations;
|
||||
});
|
||||
} else {
|
||||
Log.log("Configured language not found in core translations.");
|
||||
}
|
||||
|
||||
self.loadCoreTranslationsFallback();
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the core translations fallback.
|
||||
* The first language defined in translations.js will be used.
|
||||
*/
|
||||
loadCoreTranslationsFallback: function () {
|
||||
var self = this;
|
||||
|
||||
// The variable `first` will contain the first
|
||||
// defined translation after the following line.
|
||||
for (var first in translations) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
Log.log("Loading core translation fallback file: " + translations[first]);
|
||||
loadJSON(translations[first], function (translations) {
|
||||
self.coreTranslationsFallback = translations;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
20
js/utils.js
Normal file
20
js/utils.js
Normal file
@@ -0,0 +1,20 @@
|
||||
/* Magic Mirror
|
||||
* Utils
|
||||
*
|
||||
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
|
||||
* MIT Licensed.
|
||||
*/
|
||||
var colors = require("colors/safe");
|
||||
|
||||
var Utils = {
|
||||
colors: {
|
||||
warn: colors.yellow,
|
||||
error: colors.red,
|
||||
info: colors.blue,
|
||||
pass: colors.green
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = Utils;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
var version = {
|
||||
updateInterval: 600000,
|
||||
intervalId: null
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the version and refreshes the page if a new version has been pulled
|
||||
*/
|
||||
version.checkVersion = function () {
|
||||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: 'controllers/hash.php',
|
||||
success: function (data) {
|
||||
// The githash variable is located in index.php
|
||||
if (data && data.gitHash !== gitHash) {
|
||||
window.location.reload();
|
||||
window.location.href = window.location.href;
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
version.init = function () {
|
||||
|
||||
this.intervalId = setInterval(function () {
|
||||
this.checkVersion();
|
||||
}.bind(this), this.updateInterval);
|
||||
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
var weather = {
|
||||
// Default language is Dutch because that is what the original author used
|
||||
lang: config.lang || 'nl',
|
||||
params: config.weather.params || null,
|
||||
iconTable: {
|
||||
'01d':'wi-day-sunny',
|
||||
'02d':'wi-day-cloudy',
|
||||
'03d':'wi-cloudy',
|
||||
'04d':'wi-cloudy-windy',
|
||||
'09d':'wi-showers',
|
||||
'10d':'wi-rain',
|
||||
'11d':'wi-thunderstorm',
|
||||
'13d':'wi-snow',
|
||||
'50d':'wi-fog',
|
||||
'01n':'wi-night-clear',
|
||||
'02n':'wi-night-cloudy',
|
||||
'03n':'wi-night-cloudy',
|
||||
'04n':'wi-night-cloudy',
|
||||
'09n':'wi-night-showers',
|
||||
'10n':'wi-night-rain',
|
||||
'11n':'wi-night-thunderstorm',
|
||||
'13n':'wi-night-snow',
|
||||
'50n':'wi-night-alt-cloudy-windy'
|
||||
},
|
||||
temperatureLocation: '.temp',
|
||||
windSunLocation: '.windsun',
|
||||
forecastLocation: '.forecast',
|
||||
apiVersion: '2.5',
|
||||
apiBase: 'http://api.openweathermap.org/data/',
|
||||
weatherEndpoint: 'weather',
|
||||
forecastEndpoint: 'forecast/daily',
|
||||
updateInterval: config.weather.interval || 6000,
|
||||
fadeInterval: config.weather.fadeInterval || 1000,
|
||||
intervalId: null
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds a float to one decimal place
|
||||
* @param {float} temperature The temperature to be rounded
|
||||
* @return {float} The new floating point value
|
||||
*/
|
||||
weather.roundValue = function (temperature) {
|
||||
return parseFloat(temperature).toFixed(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the wind speed (km/h) into the values given by the Beaufort Wind Scale
|
||||
* @see http://www.spc.noaa.gov/faq/tornado/beaufort.html
|
||||
* @param {int} kmh The wind speed in Kilometers Per Hour
|
||||
* @return {int} The wind speed converted into its corresponding Beaufort number
|
||||
*/
|
||||
weather.ms2Beaufort = function(ms) {
|
||||
var kmh = ms * 60 * 60 / 1000;
|
||||
var speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000];
|
||||
for (var beaufort in speeds) {
|
||||
var speed = speeds[beaufort];
|
||||
if (speed > kmh) {
|
||||
return beaufort;
|
||||
}
|
||||
}
|
||||
return 12;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current temperature and weather patter from the OpenWeatherMap API
|
||||
*/
|
||||
weather.updateCurrentWeather = function () {
|
||||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: weather.apiBase + '/' + weather.apiVersion + '/' + weather.weatherEndpoint,
|
||||
dataType: 'json',
|
||||
data: weather.params,
|
||||
success: function (data) {
|
||||
|
||||
var _temperature = this.roundValue(data.main.temp),
|
||||
_temperatureMin = this.roundValue(data.main.temp_min),
|
||||
_temperatureMax = this.roundValue(data.main.temp_max),
|
||||
_wind = this.roundValue(data.wind.speed),
|
||||
_iconClass = this.iconTable[data.weather[0].icon];
|
||||
|
||||
var _icon = '<span class="icon ' + _iconClass + ' dimmed wi"></span>';
|
||||
|
||||
var _newTempHtml = _icon + '' + _temperature + '°';
|
||||
|
||||
$(this.temperatureLocation).updateWithText(_newTempHtml, this.fadeInterval);
|
||||
|
||||
var _now = moment().format('HH:mm'),
|
||||
_sunrise = moment(data.sys.sunrise*1000).format('HH:mm'),
|
||||
_sunset = moment(data.sys.sunset*1000).format('HH:mm');
|
||||
|
||||
var _newWindHtml = '<span class="wi wi-strong-wind xdimmed"></span> ' + this.ms2Beaufort(_wind),
|
||||
_newSunHtml = '<span class="wi wi-sunrise xdimmed"></span> ' + _sunrise;
|
||||
|
||||
if (_sunrise < _now && _sunset > _now) {
|
||||
_newSunHtml = '<span class="wi wi-sunset xdimmed"></span> ' + _sunset;
|
||||
}
|
||||
|
||||
$(this.windSunLocation).updateWithText(_newWindHtml + ' ' + _newSunHtml, this.fadeInterval);
|
||||
|
||||
}.bind(this),
|
||||
error: function () {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the 5 Day Forecast from the OpenWeatherMap API
|
||||
*/
|
||||
weather.updateWeatherForecast = function () {
|
||||
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: weather.apiBase + '/' + weather.apiVersion + '/' + weather.forecastEndpoint,
|
||||
data: weather.params,
|
||||
success: function (data) {
|
||||
|
||||
var _opacity = 1,
|
||||
_forecastHtml = '';
|
||||
|
||||
_forecastHtml += '<table class="forecast-table">';
|
||||
|
||||
for (var i = 0, count = data.list.length; i < count; i++) {
|
||||
|
||||
var _forecast = data.list[i];
|
||||
|
||||
_forecastHtml += '<tr style="opacity:' + _opacity + '">';
|
||||
|
||||
_forecastHtml += '<td class="day">' + moment(_forecast.dt, 'X').format('ddd') + '</td>';
|
||||
_forecastHtml += '<td class="icon-small ' + this.iconTable[_forecast.weather[0].icon] + '"></td>';
|
||||
_forecastHtml += '<td class="temp-max">' + this.roundValue(_forecast.temp.max) + '</td>';
|
||||
_forecastHtml += '<td class="temp-min">' + this.roundValue(_forecast.temp.min) + '</td>';
|
||||
|
||||
_forecastHtml += '</tr>';
|
||||
|
||||
_opacity -= 0.155;
|
||||
|
||||
}
|
||||
|
||||
_forecastHtml += '</table>';
|
||||
|
||||
$(this.forecastLocation).updateWithText(_forecastHtml, this.fadeInterval);
|
||||
|
||||
}.bind(this),
|
||||
error: function () {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
weather.init = function () {
|
||||
|
||||
if (this.params.lang === undefined) {
|
||||
this.params.lang = this.lang;
|
||||
}
|
||||
|
||||
if (this.params.cnt === undefined) {
|
||||
this.params.cnt = 5;
|
||||
}
|
||||
|
||||
this.intervalId = setInterval(function () {
|
||||
this.updateCurrentWeather();
|
||||
this.updateWeatherForecast();
|
||||
}.bind(this), this.updateInterval);
|
||||
|
||||
}
|
||||
10
jsconfig.json
Normal file
10
jsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=759670
|
||||
// for the documentation about the jsconfig.json format
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"exclude": ["modules", "node_modules"]
|
||||
}
|
||||
31
module-types.ts
Normal file
31
module-types.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
type ModuleProperties = {
|
||||
defaults?: object,
|
||||
start?(): void,
|
||||
getHeader?(): string,
|
||||
getTemplate?(): string,
|
||||
getTemplateData?(): object,
|
||||
notificationReceived?(notification: string, payload: any, sender: object): void,
|
||||
socketNotificationReceived?(notification: string, payload: any): void,
|
||||
suspend?(): void,
|
||||
resume?(): void,
|
||||
getDom?(): HTMLElement,
|
||||
getStyles?(): string[],
|
||||
[key: string]: any,
|
||||
};
|
||||
|
||||
export declare const Module: {
|
||||
register(moduleName: string, moduleProperties: ModuleProperties): void;
|
||||
};
|
||||
|
||||
export declare const Log: {
|
||||
info(message?: any, ...optionalParams: any[]): void,
|
||||
log(message?: any, ...optionalParams: any[]): void,
|
||||
error(message?: any, ...optionalParams: any[]): void,
|
||||
warn(message?: any, ...optionalParams: any[]): void,
|
||||
group(groupTitle?: string, ...optionalParams: any[]): void,
|
||||
groupCollapsed(groupTitle?: string, ...optionalParams: any[]): void,
|
||||
groupEnd(): void,
|
||||
time(timerName?: string): void,
|
||||
timeEnd(timerName?: string): void,
|
||||
timeStamp(timerName?: string): void,
|
||||
};
|
||||
5
modules/default/alert/README.md
Normal file
5
modules/default/alert/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Module: Alert
|
||||
|
||||
The alert module is one of the default modules of the MagicMirror. This module displays notifications from other modules.
|
||||
|
||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/alert.html).
|
||||
167
modules/default/alert/alert.js
Normal file
167
modules/default/alert/alert.js
Normal file
@@ -0,0 +1,167 @@
|
||||
/* global NotificationFx */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: alert
|
||||
*
|
||||
* By Paul-Vincent Roll https://paulvincentroll.com/
|
||||
* MIT Licensed.
|
||||
*/
|
||||
Module.register("alert", {
|
||||
defaults: {
|
||||
// scale|slide|genie|jelly|flip|bouncyflip|exploader
|
||||
effect: "slide",
|
||||
// scale|slide|genie|jelly|flip|bouncyflip|exploader
|
||||
alert_effect: "jelly",
|
||||
//time a notification is displayed in seconds
|
||||
display_time: 3500,
|
||||
//Position
|
||||
position: "center",
|
||||
//shown at startup
|
||||
welcome_message: false
|
||||
},
|
||||
getScripts: function () {
|
||||
return ["notificationFx.js"];
|
||||
},
|
||||
getStyles: function () {
|
||||
return ["notificationFx.css", "font-awesome.css"];
|
||||
},
|
||||
// Define required translations.
|
||||
getTranslations: function () {
|
||||
return {
|
||||
en: "translations/en.json",
|
||||
de: "translations/de.json",
|
||||
nl: "translations/nl.json"
|
||||
};
|
||||
},
|
||||
show_notification: function (message) {
|
||||
if (this.config.effect === "slide") {
|
||||
this.config.effect = this.config.effect + "-" + this.config.position;
|
||||
}
|
||||
let msg = "";
|
||||
if (message.title) {
|
||||
msg += "<span class='thin dimmed medium'>" + message.title + "</span>";
|
||||
}
|
||||
if (message.message) {
|
||||
if (msg !== "") {
|
||||
msg += "<br />";
|
||||
}
|
||||
msg += "<span class='light bright small'>" + message.message + "</span>";
|
||||
}
|
||||
|
||||
new NotificationFx({
|
||||
message: msg,
|
||||
layout: "growl",
|
||||
effect: this.config.effect,
|
||||
ttl: message.timer !== undefined ? message.timer : this.config.display_time
|
||||
}).show();
|
||||
},
|
||||
show_alert: function (params, sender) {
|
||||
let image = "";
|
||||
//Set standard params if not provided by module
|
||||
if (typeof params.timer === "undefined") {
|
||||
params.timer = null;
|
||||
}
|
||||
if (typeof params.imageHeight === "undefined") {
|
||||
params.imageHeight = "80px";
|
||||
}
|
||||
if (typeof params.imageUrl === "undefined" && typeof params.imageFA === "undefined") {
|
||||
params.imageUrl = null;
|
||||
} else if (typeof params.imageFA === "undefined") {
|
||||
image = "<img src='" + params.imageUrl.toString() + "' height='" + params.imageHeight.toString() + "' style='margin-bottom: 10px;'/><br />";
|
||||
} else if (typeof params.imageUrl === "undefined") {
|
||||
image = "<span class='bright " + "fa fa-" + params.imageFA + "' style='margin-bottom: 10px;font-size:" + params.imageHeight.toString() + ";'/></span><br />";
|
||||
}
|
||||
//Create overlay
|
||||
const overlay = document.createElement("div");
|
||||
overlay.id = "overlay";
|
||||
overlay.innerHTML += '<div class="black_overlay"></div>';
|
||||
document.body.insertBefore(overlay, document.body.firstChild);
|
||||
|
||||
//If module already has an open alert close it
|
||||
if (this.alerts[sender.name]) {
|
||||
this.hide_alert(sender);
|
||||
}
|
||||
|
||||
//Display title and message only if they are provided in notification parameters
|
||||
let message = "";
|
||||
if (params.title) {
|
||||
message += "<span class='light dimmed medium'>" + params.title + "</span>";
|
||||
}
|
||||
if (params.message) {
|
||||
if (message !== "") {
|
||||
message += "<br />";
|
||||
}
|
||||
|
||||
message += "<span class='thin bright small'>" + params.message + "</span>";
|
||||
}
|
||||
|
||||
//Store alert in this.alerts
|
||||
this.alerts[sender.name] = new NotificationFx({
|
||||
message: image + message,
|
||||
effect: this.config.alert_effect,
|
||||
ttl: params.timer,
|
||||
onClose: () => this.hide_alert(sender),
|
||||
al_no: "ns-alert"
|
||||
});
|
||||
|
||||
//Show alert
|
||||
this.alerts[sender.name].show();
|
||||
|
||||
//Add timer to dismiss alert and overlay
|
||||
if (params.timer) {
|
||||
setTimeout(() => {
|
||||
this.hide_alert(sender);
|
||||
}, params.timer);
|
||||
}
|
||||
},
|
||||
hide_alert: function (sender) {
|
||||
//Dismiss alert and remove from this.alerts
|
||||
if (this.alerts[sender.name]) {
|
||||
this.alerts[sender.name].dismiss();
|
||||
this.alerts[sender.name] = null;
|
||||
//Remove overlay
|
||||
const overlay = document.getElementById("overlay");
|
||||
overlay.parentNode.removeChild(overlay);
|
||||
}
|
||||
},
|
||||
setPosition: function (pos) {
|
||||
//Add css to body depending on the set position for notifications
|
||||
const sheet = document.createElement("style");
|
||||
if (pos === "center") {
|
||||
sheet.innerHTML = ".ns-box {margin-left: auto; margin-right: auto;text-align: center;}";
|
||||
}
|
||||
if (pos === "right") {
|
||||
sheet.innerHTML = ".ns-box {margin-left: auto;text-align: right;}";
|
||||
}
|
||||
if (pos === "left") {
|
||||
sheet.innerHTML = ".ns-box {margin-right: auto;text-align: left;}";
|
||||
}
|
||||
document.body.appendChild(sheet);
|
||||
},
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
if (notification === "SHOW_ALERT") {
|
||||
if (typeof payload.type === "undefined") {
|
||||
payload.type = "alert";
|
||||
}
|
||||
if (payload.type === "alert") {
|
||||
this.show_alert(payload, sender);
|
||||
} else if (payload.type === "notification") {
|
||||
this.show_notification(payload);
|
||||
}
|
||||
} else if (notification === "HIDE_ALERT") {
|
||||
this.hide_alert(sender);
|
||||
}
|
||||
},
|
||||
start: function () {
|
||||
this.alerts = {};
|
||||
this.setPosition(this.config.position);
|
||||
if (this.config.welcome_message) {
|
||||
if (this.config.welcome_message === true) {
|
||||
this.show_notification({ title: this.translate("sysTitle"), message: this.translate("welcome") });
|
||||
} else {
|
||||
this.show_notification({ title: this.translate("sysTitle"), message: this.config.welcome_message });
|
||||
}
|
||||
}
|
||||
Log.info("Starting module: " + this.name);
|
||||
}
|
||||
});
|
||||
933
modules/default/alert/notificationFx.css
Normal file
933
modules/default/alert/notificationFx.css
Normal file
@@ -0,0 +1,933 @@
|
||||
/* Based on work by https://tympanus.net/codrops/licensing/ */
|
||||
|
||||
.ns-box {
|
||||
background-color: rgba(0, 0, 0, 0.93);
|
||||
padding: 17px;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 10px;
|
||||
z-index: 1;
|
||||
color: black;
|
||||
font-size: 70%;
|
||||
position: relative;
|
||||
display: table;
|
||||
word-wrap: break-word;
|
||||
max-width: 100%;
|
||||
border-width: 1px;
|
||||
border-radius: 5px;
|
||||
border-style: solid;
|
||||
border-color: #666;
|
||||
}
|
||||
|
||||
.ns-alert {
|
||||
border-style: solid;
|
||||
border-color: #fff;
|
||||
padding: 17px;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 10px;
|
||||
z-index: 3;
|
||||
color: white;
|
||||
font-size: 70%;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
right: 0;
|
||||
left: 0;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
top: 40%;
|
||||
width: 40%;
|
||||
height: auto;
|
||||
word-wrap: break-word;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.black_overlay {
|
||||
position: fixed;
|
||||
z-index: 2;
|
||||
background-color: rgba(0, 0, 0, 0.93);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
[class^="ns-effect-"].ns-growl.ns-hide,
|
||||
[class*=" ns-effect-"].ns-growl.ns-hide {
|
||||
animation-direction: reverse;
|
||||
}
|
||||
|
||||
.ns-effect-flip {
|
||||
transform-origin: 50% 100%;
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
|
||||
.ns-effect-flip.ns-show,
|
||||
.ns-effect-flip.ns-hide {
|
||||
animation-name: animFlipFront;
|
||||
animation-duration: 0.3s;
|
||||
}
|
||||
|
||||
.ns-effect-flip.ns-hide {
|
||||
animation-name: animFlipBack;
|
||||
}
|
||||
|
||||
@keyframes animFlipFront {
|
||||
0% {
|
||||
transform: perspective(1000px) rotate3d(1, 0, 0, -90deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: perspective(1000px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animFlipBack {
|
||||
0% {
|
||||
transform: perspective(1000px) rotate3d(1, 0, 0, 90deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: perspective(1000px);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-bouncyflip.ns-show,
|
||||
.ns-effect-bouncyflip.ns-hide {
|
||||
animation-name: flipInX;
|
||||
animation-duration: 0.8s;
|
||||
}
|
||||
|
||||
@keyframes flipInX {
|
||||
0% {
|
||||
transform: perspective(400px) rotate3d(1, 0, 0, -90deg);
|
||||
transition-timing-function: ease-in;
|
||||
}
|
||||
|
||||
40% {
|
||||
transform: perspective(400px) rotate3d(1, 0, 0, 20deg);
|
||||
transition-timing-function: ease-out;
|
||||
}
|
||||
|
||||
60% {
|
||||
transform: perspective(400px) rotate3d(1, 0, 0, -10deg);
|
||||
transition-timing-function: ease-in;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
80% {
|
||||
transform: perspective(400px) rotate3d(1, 0, 0, 5deg);
|
||||
transition-timing-function: ease-out;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: perspective(400px);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-bouncyflip.ns-hide {
|
||||
animation-name: flipInXSimple;
|
||||
animation-duration: 0.3s;
|
||||
}
|
||||
|
||||
@keyframes flipInXSimple {
|
||||
0% {
|
||||
transform: perspective(400px) rotate3d(1, 0, 0, -90deg);
|
||||
transition-timing-function: ease-in;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: perspective(400px);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-exploader {
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
.ns-effect-exploader p {
|
||||
padding: 0.25em 2em 0.25em 3em;
|
||||
}
|
||||
|
||||
.ns-effect-exploader.ns-show {
|
||||
animation-name: animLoad;
|
||||
animation-duration: 1s;
|
||||
}
|
||||
|
||||
@keyframes animLoad {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scale3d(0, 0.3, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-exploader.ns-hide {
|
||||
animation-name: animFade;
|
||||
animation-duration: 0.3s;
|
||||
}
|
||||
|
||||
.ns-effect-exploader.ns-show .ns-box-inner,
|
||||
.ns-effect-exploader.ns-show .ns-close {
|
||||
animation-fill-mode: both;
|
||||
animation-duration: 0.3s;
|
||||
animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
.ns-effect-exploader.ns-show .ns-close {
|
||||
animation-name: animFade;
|
||||
}
|
||||
|
||||
.ns-effect-exploader.ns-show .ns-box-inner {
|
||||
animation-name: animFadeMove;
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
|
||||
@keyframes animFadeMove {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate3d(0, 10px, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animFade {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-scale.ns-show,
|
||||
.ns-effect-scale.ns-hide {
|
||||
animation-name: animScale;
|
||||
animation-duration: 0.25s;
|
||||
}
|
||||
|
||||
@keyframes animScale {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate3d(0, 40px, 0) scale3d(0.1, 0.6, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0) scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-jelly.ns-show {
|
||||
animation-name: animJelly;
|
||||
animation-duration: 1s;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
.ns-effect-jelly.ns-hide {
|
||||
animation-name: animFade;
|
||||
animation-duration: 0.3s;
|
||||
}
|
||||
|
||||
@keyframes animFade {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animJelly {
|
||||
0% {
|
||||
transform: matrix3d(0.7, 0, 0, 0, 0, 0.7, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
2.083333% {
|
||||
transform: matrix3d(0.75266, 0, 0, 0, 0, 0.76342, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
4.166667% {
|
||||
transform: matrix3d(0.81071, 0, 0, 0, 0, 0.84545, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
6.25% {
|
||||
transform: matrix3d(0.86808, 0, 0, 0, 0, 0.9286, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
8.333333% {
|
||||
transform: matrix3d(0.92038, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
10.416667% {
|
||||
transform: matrix3d(0.96482, 0, 0, 0, 0, 1.05202, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
12.5% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1.08204, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
14.583333% {
|
||||
transform: matrix3d(1.02563, 0, 0, 0, 0, 1.09149, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
16.666667% {
|
||||
transform: matrix3d(1.04227, 0, 0, 0, 0, 1.08453, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
18.75% {
|
||||
transform: matrix3d(1.05102, 0, 0, 0, 0, 1.06666, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
20.833333% {
|
||||
transform: matrix3d(1.05334, 0, 0, 0, 0, 1.04355, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
22.916667% {
|
||||
transform: matrix3d(1.05078, 0, 0, 0, 0, 1.02012, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: matrix3d(1.04487, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
27.083333% {
|
||||
transform: matrix3d(1.03699, 0, 0, 0, 0, 0.98534, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
29.166667% {
|
||||
transform: matrix3d(1.02831, 0, 0, 0, 0, 0.97688, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
31.25% {
|
||||
transform: matrix3d(1.01973, 0, 0, 0, 0, 0.97422, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
33.333333% {
|
||||
transform: matrix3d(1.01191, 0, 0, 0, 0, 0.97618, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
35.416667% {
|
||||
transform: matrix3d(1.00526, 0, 0, 0, 0, 0.98122, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
37.5% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.98773, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
39.583333% {
|
||||
transform: matrix3d(0.99617, 0, 0, 0, 0, 0.99433, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
41.666667% {
|
||||
transform: matrix3d(0.99368, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
43.75% {
|
||||
transform: matrix3d(0.99237, 0, 0, 0, 0, 1.00413, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
45.833333% {
|
||||
transform: matrix3d(0.99202, 0, 0, 0, 0, 1.00651, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
47.916667% {
|
||||
transform: matrix3d(0.99241, 0, 0, 0, 0, 1.00726, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: matrix3d(0.99329, 0, 0, 0, 0, 1.00671, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
52.083333% {
|
||||
transform: matrix3d(0.99447, 0, 0, 0, 0, 1.00529, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
54.166667% {
|
||||
transform: matrix3d(0.99577, 0, 0, 0, 0, 1.00346, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
56.25% {
|
||||
transform: matrix3d(0.99705, 0, 0, 0, 0, 1.0016, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
58.333333% {
|
||||
transform: matrix3d(0.99822, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
60.416667% {
|
||||
transform: matrix3d(0.99921, 0, 0, 0, 0, 0.99884, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
62.5% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.99816, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
64.583333% {
|
||||
transform: matrix3d(1.00057, 0, 0, 0, 0, 0.99795, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
66.666667% {
|
||||
transform: matrix3d(1.00095, 0, 0, 0, 0, 0.99811, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
68.75% {
|
||||
transform: matrix3d(1.00114, 0, 0, 0, 0, 0.99851, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
70.833333% {
|
||||
transform: matrix3d(1.00119, 0, 0, 0, 0, 0.99903, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
72.916667% {
|
||||
transform: matrix3d(1.00114, 0, 0, 0, 0, 0.99955, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: matrix3d(1.001, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
77.083333% {
|
||||
transform: matrix3d(1.00083, 0, 0, 0, 0, 1.00033, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
79.166667% {
|
||||
transform: matrix3d(1.00063, 0, 0, 0, 0, 1.00052, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
81.25% {
|
||||
transform: matrix3d(1.00044, 0, 0, 0, 0, 1.00058, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
83.333333% {
|
||||
transform: matrix3d(1.00027, 0, 0, 0, 0, 1.00053, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
85.416667% {
|
||||
transform: matrix3d(1.00012, 0, 0, 0, 0, 1.00042, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
87.5% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1.00027, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
89.583333% {
|
||||
transform: matrix3d(0.99991, 0, 0, 0, 0, 1.00013, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
91.666667% {
|
||||
transform: matrix3d(0.99986, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
93.75% {
|
||||
transform: matrix3d(0.99983, 0, 0, 0, 0, 0.99991, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
95.833333% {
|
||||
transform: matrix3d(0.99982, 0, 0, 0, 0, 0.99985, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
97.916667% {
|
||||
transform: matrix3d(0.99983, 0, 0, 0, 0, 0.99984, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-slide-left.ns-show {
|
||||
animation-name: animSlideElasticLeft;
|
||||
animation-duration: 1s;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
@keyframes animSlideElasticLeft {
|
||||
0% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -1000, 0, 0, 1);
|
||||
}
|
||||
|
||||
1.666667% {
|
||||
transform: matrix3d(1.92933, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -739.26805, 0, 0, 1);
|
||||
}
|
||||
|
||||
3.333333% {
|
||||
transform: matrix3d(1.96989, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -521.82545, 0, 0, 1);
|
||||
}
|
||||
|
||||
5% {
|
||||
transform: matrix3d(1.70901, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -349.26115, 0, 0, 1);
|
||||
}
|
||||
|
||||
6.666667% {
|
||||
transform: matrix3d(1.4235, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -218.3238, 0, 0, 1);
|
||||
}
|
||||
|
||||
8.333333% {
|
||||
transform: matrix3d(1.21065, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -123.29848, 0, 0, 1);
|
||||
}
|
||||
|
||||
10% {
|
||||
transform: matrix3d(1.08167, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -57.59273, 0, 0, 1);
|
||||
}
|
||||
|
||||
11.666667% {
|
||||
transform: matrix3d(1.0165, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -14.72371, 0, 0, 1);
|
||||
}
|
||||
|
||||
13.333333% {
|
||||
transform: matrix3d(0.99057, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 11.12794, 0, 0, 1);
|
||||
}
|
||||
|
||||
15% {
|
||||
transform: matrix3d(0.98478, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 24.86339, 0, 0, 1);
|
||||
}
|
||||
|
||||
16.666667% {
|
||||
transform: matrix3d(0.98719, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 30.40503, 0, 0, 1);
|
||||
}
|
||||
|
||||
18.333333% {
|
||||
transform: matrix3d(0.9916, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 30.75275, 0, 0, 1);
|
||||
}
|
||||
|
||||
20% {
|
||||
transform: matrix3d(0.99541, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 28.10141, 0, 0, 1);
|
||||
}
|
||||
|
||||
21.666667% {
|
||||
transform: matrix3d(0.99795, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 23.98271, 0, 0, 1);
|
||||
}
|
||||
|
||||
23.333333% {
|
||||
transform: matrix3d(0.99936, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 19.40752, 0, 0, 1);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 14.99558, 0, 0, 1);
|
||||
}
|
||||
|
||||
26.666667% {
|
||||
transform: matrix3d(1.00021, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 11.08575, 0, 0, 1);
|
||||
}
|
||||
|
||||
28.333333% {
|
||||
transform: matrix3d(1.00022, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 7.82507, 0, 0, 1);
|
||||
}
|
||||
|
||||
30% {
|
||||
transform: matrix3d(1.00016, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 5.23737, 0, 0, 1);
|
||||
}
|
||||
|
||||
31.666667% {
|
||||
transform: matrix3d(1.0001, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3.27389, 0, 0, 1);
|
||||
}
|
||||
|
||||
33.333333% {
|
||||
transform: matrix3d(1.00005, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1.84893, 0, 0, 1);
|
||||
}
|
||||
|
||||
35% {
|
||||
transform: matrix3d(1.00002, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.86364, 0, 0, 1);
|
||||
}
|
||||
|
||||
36.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.22079, 0, 0, 1);
|
||||
}
|
||||
|
||||
38.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.16687, 0, 0, 1);
|
||||
}
|
||||
|
||||
40% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.37284, 0, 0, 1);
|
||||
}
|
||||
|
||||
41.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.45594, 0, 0, 1);
|
||||
}
|
||||
|
||||
43.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.46116, 0, 0, 1);
|
||||
}
|
||||
|
||||
45% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.4214, 0, 0, 1);
|
||||
}
|
||||
|
||||
46.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.35963, 0, 0, 1);
|
||||
}
|
||||
|
||||
48.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.29103, 0, 0, 1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.22487, 0, 0, 1);
|
||||
}
|
||||
|
||||
51.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.16624, 0, 0, 1);
|
||||
}
|
||||
|
||||
53.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.11734, 0, 0, 1);
|
||||
}
|
||||
|
||||
55% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.07854, 0, 0, 1);
|
||||
}
|
||||
|
||||
56.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.04909, 0, 0, 1);
|
||||
}
|
||||
|
||||
58.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.02773, 0, 0, 1);
|
||||
}
|
||||
|
||||
60% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.01295, 0, 0, 1);
|
||||
}
|
||||
|
||||
61.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.00331, 0, 0, 1);
|
||||
}
|
||||
|
||||
63.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.0025, 0, 0, 1);
|
||||
}
|
||||
|
||||
65% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00559, 0, 0, 1);
|
||||
}
|
||||
|
||||
66.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00684, 0, 0, 1);
|
||||
}
|
||||
|
||||
68.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00692, 0, 0, 1);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00632, 0, 0, 1);
|
||||
}
|
||||
|
||||
71.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00539, 0, 0, 1);
|
||||
}
|
||||
|
||||
73.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00436, 0, 0, 1);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00337, 0, 0, 1);
|
||||
}
|
||||
|
||||
76.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00249, 0, 0, 1);
|
||||
}
|
||||
|
||||
78.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00176, 0, 0, 1);
|
||||
}
|
||||
|
||||
80% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00118, 0, 0, 1);
|
||||
}
|
||||
|
||||
81.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00074, 0, 0, 1);
|
||||
}
|
||||
|
||||
83.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00042, 0, 0, 1);
|
||||
}
|
||||
|
||||
85% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00019, 0, 0, 1);
|
||||
}
|
||||
|
||||
86.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00005, 0, 0, 1);
|
||||
}
|
||||
|
||||
88.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.00004, 0, 0, 1);
|
||||
}
|
||||
|
||||
90% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.00008, 0, 0, 1);
|
||||
}
|
||||
|
||||
91.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.0001, 0, 0, 1);
|
||||
}
|
||||
|
||||
93.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.0001, 0, 0, 1);
|
||||
}
|
||||
|
||||
95% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.00009, 0, 0, 1);
|
||||
}
|
||||
|
||||
96.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.00008, 0, 0, 1);
|
||||
}
|
||||
|
||||
98.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.00007, 0, 0, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-slide-left.ns-hide {
|
||||
animation-name: animSlideLeft;
|
||||
animation-duration: 0.25s;
|
||||
}
|
||||
|
||||
@keyframes animSlideLeft {
|
||||
0% {
|
||||
transform: translate3d(-30px, 0, 0) translate3d(-100%, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-slide-right.ns-show {
|
||||
animation: animSlideElasticRight 2000ms linear both;
|
||||
}
|
||||
|
||||
@keyframes animSlideElasticRight {
|
||||
0% {
|
||||
transform: matrix3d(2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1000, 0, 0, 1);
|
||||
}
|
||||
|
||||
2.15% {
|
||||
transform: matrix3d(1.486, 0, 0, 0, 0, 0.514, 0, 0, 0, 0, 1, 0, 664.594, 0, 0, 1);
|
||||
}
|
||||
|
||||
4.1% {
|
||||
transform: matrix3d(1.147, 0, 0, 0, 0, 0.853, 0, 0, 0, 0, 1, 0, 419.708, 0, 0, 1);
|
||||
}
|
||||
|
||||
4.3% {
|
||||
transform: matrix3d(1.121, 0, 0, 0, 0, 0.879, 0, 0, 0, 0, 1, 0, 398.136, 0, 0, 1);
|
||||
}
|
||||
|
||||
6.46% {
|
||||
transform: matrix3d(0.948, 0, 0, 0, 0, 1.052, 0, 0, 0, 0, 1, 0, 206.714, 0, 0, 1);
|
||||
}
|
||||
|
||||
8.11% {
|
||||
transform: matrix3d(0.908, 0, 0, 0, 0, 1.092, 0, 0, 0, 0, 1, 0, 105.491, 0, 0, 1);
|
||||
}
|
||||
|
||||
8.61% {
|
||||
transform: matrix3d(0.907, 0, 0, 0, 0, 1.093, 0, 0, 0, 0, 1, 0, 81.572, 0, 0, 1);
|
||||
}
|
||||
|
||||
12.11% {
|
||||
transform: matrix3d(0.95, 0, 0, 0, 0, 1.05, 0, 0, 0, 0, 1, 0, -18.434, 0, 0, 1);
|
||||
}
|
||||
|
||||
14.16% {
|
||||
transform: matrix3d(0.979, 0, 0, 0, 0, 1.021, 0, 0, 0, 0, 1, 0, -38.734, 0, 0, 1);
|
||||
}
|
||||
|
||||
16.12% {
|
||||
transform: matrix3d(0.997, 0, 0, 0, 0, 1.003, 0, 0, 0, 0, 1, 0, -43.356, 0, 0, 1);
|
||||
}
|
||||
|
||||
19.72% {
|
||||
transform: matrix3d(1.006, 0, 0, 0, 0, 0.994, 0, 0, 0, 0, 1, 0, -34.155, 0, 0, 1);
|
||||
}
|
||||
|
||||
27.23% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -7.839, 0, 0, 1);
|
||||
}
|
||||
|
||||
30.83% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -1.951, 0, 0, 1);
|
||||
}
|
||||
|
||||
38.34% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1.037, 0, 0, 1);
|
||||
}
|
||||
|
||||
41.99% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.812, 0, 0, 1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.159, 0, 0, 1);
|
||||
}
|
||||
|
||||
60.56% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.025, 0, 0, 1);
|
||||
}
|
||||
|
||||
82.78% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.001, 0, 0, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-slide-right.ns-hide {
|
||||
animation-name: animSlideRight;
|
||||
animation-duration: 0.25s;
|
||||
}
|
||||
|
||||
@keyframes animSlideRight {
|
||||
0% {
|
||||
transform: translate3d(30px, 0, 0) translate3d(100%, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-slide-center.ns-show {
|
||||
animation: animSlideElasticCenter 2000ms linear both;
|
||||
}
|
||||
|
||||
@keyframes animSlideElasticCenter {
|
||||
0% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, -300, 0, 1);
|
||||
}
|
||||
|
||||
2.15% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1.971, 0, 0, 0, 0, 1, 0, 0, -199.378, 0, 1);
|
||||
}
|
||||
|
||||
4.1% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1.294, 0, 0, 0, 0, 1, 0, 0, -125.912, 0, 1);
|
||||
}
|
||||
|
||||
4.3% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1.243, 0, 0, 0, 0, 1, 0, 0, -119.441, 0, 1);
|
||||
}
|
||||
|
||||
6.46% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.895, 0, 0, 0, 0, 1, 0, 0, -62.014, 0, 1);
|
||||
}
|
||||
|
||||
8.11% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.817, 0, 0, 0, 0, 1, 0, 0, -31.647, 0, 1);
|
||||
}
|
||||
|
||||
8.61% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.813, 0, 0, 0, 0, 1, 0, 0, -24.472, 0, 1);
|
||||
}
|
||||
|
||||
12.11% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.9, 0, 0, 0, 0, 1, 0, 0, 5.53, 0, 1);
|
||||
}
|
||||
|
||||
14.16% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.959, 0, 0, 0, 0, 1, 0, 0, 11.62, 0, 1);
|
||||
}
|
||||
|
||||
16.12% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.994, 0, 0, 0, 0, 1, 0, 0, 13.007, 0, 1);
|
||||
}
|
||||
|
||||
19.72% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1.012, 0, 0, 0, 0, 1, 0, 0, 10.247, 0, 1);
|
||||
}
|
||||
|
||||
27.23% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 2.352, 0, 1);
|
||||
}
|
||||
|
||||
30.83% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.999, 0, 0, 0, 0, 1, 0, 0, 0.585, 0, 1);
|
||||
}
|
||||
|
||||
38.34% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, -0.311, 0, 1);
|
||||
}
|
||||
|
||||
41.99% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, -0.244, 0, 1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, -0.048, 0, 1);
|
||||
}
|
||||
|
||||
60.56% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0.007, 0, 1);
|
||||
}
|
||||
|
||||
82.78% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-slide-center.ns-hide {
|
||||
animation-name: animSlideCenter;
|
||||
animation-duration: 0.25s;
|
||||
}
|
||||
|
||||
@keyframes animSlideCenter {
|
||||
0% {
|
||||
transform: translate3d(0, -30px, 0) translate3d(0, -100%, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-genie.ns-show,
|
||||
.ns-effect-genie.ns-hide {
|
||||
animation-name: animGenie;
|
||||
animation-duration: 0.4s;
|
||||
}
|
||||
|
||||
@keyframes animGenie {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate3d(0, calc(200% + 30px), 0) scale3d(0, 1, 1);
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
|
||||
40% {
|
||||
opacity: 0.5;
|
||||
transform: translate3d(0, 0, 0) scale3d(0.02, 1.1, 1);
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
|
||||
70% {
|
||||
opacity: 0.6;
|
||||
transform: translate3d(0, -40px, 0) scale3d(0.8, 1.1, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0) scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
156
modules/default/alert/notificationFx.js
Normal file
156
modules/default/alert/notificationFx.js
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Based on work by
|
||||
*
|
||||
* notificationFx.js v1.0.0
|
||||
* https://tympanus.net/codrops/
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Copyright 2014, Codrops
|
||||
* https://tympanus.net/codrops/
|
||||
*/
|
||||
(function (window) {
|
||||
/**
|
||||
* Extend one object with another one
|
||||
*
|
||||
* @param {object} a The object to extend
|
||||
* @param {object} b The object which extends the other, overwrites existing keys
|
||||
* @returns {object} The merged object
|
||||
*/
|
||||
function extend(a, b) {
|
||||
for (let key in b) {
|
||||
if (b.hasOwnProperty(key)) {
|
||||
a[key] = b[key];
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* NotificationFx constructor
|
||||
*
|
||||
* @param {object} options The configuration options
|
||||
* @class
|
||||
*/
|
||||
function NotificationFx(options) {
|
||||
this.options = extend({}, this.options);
|
||||
extend(this.options, options);
|
||||
this._init();
|
||||
}
|
||||
|
||||
/**
|
||||
* NotificationFx options
|
||||
*/
|
||||
NotificationFx.prototype.options = {
|
||||
// element to which the notification will be appended
|
||||
// defaults to the document.body
|
||||
wrapper: document.body,
|
||||
// the message
|
||||
message: "yo!",
|
||||
// layout type: growl|attached|bar|other
|
||||
layout: "growl",
|
||||
// effects for the specified layout:
|
||||
// for growl layout: scale|slide|genie|jelly
|
||||
// for attached layout: flip|bouncyflip
|
||||
// for other layout: boxspinner|cornerexpand|loadingcircle|thumbslider
|
||||
// ...
|
||||
effect: "slide",
|
||||
// notice, warning, error, success
|
||||
// will add class ns-type-warning, ns-type-error or ns-type-success
|
||||
type: "notice",
|
||||
// if the user doesn´t close the notification then we remove it
|
||||
// after the following time
|
||||
ttl: 6000,
|
||||
al_no: "ns-box",
|
||||
// callbacks
|
||||
onClose: function () {
|
||||
return false;
|
||||
},
|
||||
onOpen: function () {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize and cache some vars
|
||||
*/
|
||||
NotificationFx.prototype._init = function () {
|
||||
// create HTML structure
|
||||
this.ntf = document.createElement("div");
|
||||
this.ntf.className = this.options.al_no + " ns-" + this.options.layout + " ns-effect-" + this.options.effect + " ns-type-" + this.options.type;
|
||||
let strinner = '<div class="ns-box-inner">';
|
||||
strinner += this.options.message;
|
||||
strinner += "</div>";
|
||||
this.ntf.innerHTML = strinner;
|
||||
|
||||
// append to body or the element specified in options.wrapper
|
||||
this.options.wrapper.insertBefore(this.ntf, this.options.wrapper.nextSibling);
|
||||
|
||||
// dismiss after [options.ttl]ms
|
||||
if (this.options.ttl) {
|
||||
this.dismissttl = setTimeout(() => {
|
||||
if (this.active) {
|
||||
this.dismiss();
|
||||
}
|
||||
}, this.options.ttl);
|
||||
}
|
||||
|
||||
// init events
|
||||
this._initEvents();
|
||||
};
|
||||
|
||||
/**
|
||||
* Init events
|
||||
*/
|
||||
NotificationFx.prototype._initEvents = function () {
|
||||
// dismiss notification by tapping on it if someone has a touchscreen
|
||||
this.ntf.querySelector(".ns-box-inner").addEventListener("click", () => {
|
||||
this.dismiss();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Show the notification
|
||||
*/
|
||||
NotificationFx.prototype.show = function () {
|
||||
this.active = true;
|
||||
this.ntf.classList.remove("ns-hide");
|
||||
this.ntf.classList.add("ns-show");
|
||||
this.options.onOpen();
|
||||
};
|
||||
|
||||
/**
|
||||
* Dismiss the notification
|
||||
*/
|
||||
NotificationFx.prototype.dismiss = function () {
|
||||
this.active = false;
|
||||
clearTimeout(this.dismissttl);
|
||||
this.ntf.classList.remove("ns-show");
|
||||
setTimeout(() => {
|
||||
this.ntf.classList.add("ns-hide");
|
||||
|
||||
// callback
|
||||
this.options.onClose();
|
||||
}, 25);
|
||||
|
||||
// after animation ends remove ntf from the DOM
|
||||
const onEndAnimationFn = (ev) => {
|
||||
if (ev.target !== this.ntf) {
|
||||
return false;
|
||||
}
|
||||
this.ntf.removeEventListener("animationend", onEndAnimationFn);
|
||||
|
||||
if (ev.target.parentNode === this.options.wrapper) {
|
||||
this.options.wrapper.removeChild(this.ntf);
|
||||
}
|
||||
};
|
||||
|
||||
this.ntf.addEventListener("animationend", onEndAnimationFn);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add to global namespace
|
||||
*/
|
||||
window.NotificationFx = NotificationFx;
|
||||
})(window);
|
||||
4
modules/default/alert/translations/bg.json
Normal file
4
modules/default/alert/translations/bg.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror нотификация",
|
||||
"welcome": "Добре дошли, стартирането беше успешно"
|
||||
}
|
||||
4
modules/default/alert/translations/da.json
Normal file
4
modules/default/alert/translations/da.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror Notifikation",
|
||||
"welcome": "Velkommen, modulet er succesfuldt startet!"
|
||||
}
|
||||
4
modules/default/alert/translations/de.json
Normal file
4
modules/default/alert/translations/de.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror Benachrichtigung",
|
||||
"welcome": "Willkommen, Start war erfolgreich!"
|
||||
}
|
||||
4
modules/default/alert/translations/en.json
Normal file
4
modules/default/alert/translations/en.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror Notification",
|
||||
"welcome": "Welcome, start was successful!"
|
||||
}
|
||||
4
modules/default/alert/translations/es.json
Normal file
4
modules/default/alert/translations/es.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror Notificaciones",
|
||||
"welcome": "Bienvenido, ¡se iniciado correctamente!"
|
||||
}
|
||||
4
modules/default/alert/translations/fr.json
Normal file
4
modules/default/alert/translations/fr.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror Notification",
|
||||
"welcome": "Bienvenue, le démarrage a été un succès!"
|
||||
}
|
||||
4
modules/default/alert/translations/hu.json
Normal file
4
modules/default/alert/translations/hu.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror értesítés",
|
||||
"welcome": "Üdvözöljük, indulás sikeres!"
|
||||
}
|
||||
4
modules/default/alert/translations/nl.json
Normal file
4
modules/default/alert/translations/nl.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror Notificatie",
|
||||
"welcome": "Welkom, Succesvol gestart!"
|
||||
}
|
||||
4
modules/default/alert/translations/ru.json
Normal file
4
modules/default/alert/translations/ru.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror Уведомление",
|
||||
"welcome": "Добро пожаловать, старт был успешным!"
|
||||
}
|
||||
6
modules/default/calendar/README.md
Executable file
6
modules/default/calendar/README.md
Executable file
@@ -0,0 +1,6 @@
|
||||
# Module: Calendar
|
||||
|
||||
The `calendar` module is one of the default modules of the MagicMirror.
|
||||
This module displays events from a public .ical calendar. It can combine multiple calendars.
|
||||
|
||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/calendar.html).
|
||||
22
modules/default/calendar/calendar.css
Normal file
22
modules/default/calendar/calendar.css
Normal file
@@ -0,0 +1,22 @@
|
||||
.calendar .symbol {
|
||||
padding-left: 0;
|
||||
padding-right: 10px;
|
||||
font-size: 80%;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.calendar .symbol span {
|
||||
display: inline-block;
|
||||
transform: translate(0, 2px);
|
||||
}
|
||||
|
||||
.calendar .title {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.calendar .time {
|
||||
padding-left: 30px;
|
||||
text-align: right;
|
||||
vertical-align: top;
|
||||
}
|
||||
832
modules/default/calendar/calendar.js
Executable file
832
modules/default/calendar/calendar.js
Executable file
@@ -0,0 +1,832 @@
|
||||
/* global cloneObject */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: Calendar
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
Module.register("calendar", {
|
||||
// Define module defaults
|
||||
defaults: {
|
||||
maximumEntries: 10, // Total Maximum Entries
|
||||
maximumNumberOfDays: 365,
|
||||
limitDays: 0, // Limit the number of days shown, 0 = no limit
|
||||
displaySymbol: true,
|
||||
defaultSymbol: "calendar", // Fontawesome Symbol see https://fontawesome.com/cheatsheet?from=io
|
||||
showLocation: false,
|
||||
displayRepeatingCountTitle: false,
|
||||
defaultRepeatingCountTitle: "",
|
||||
maxTitleLength: 25,
|
||||
maxLocationTitleLength: 25,
|
||||
wrapEvents: false, // wrap events to multiple lines breaking at maxTitleLength
|
||||
wrapLocationEvents: false,
|
||||
maxTitleLines: 3,
|
||||
maxEventTitleLines: 3,
|
||||
fetchInterval: 5 * 60 * 1000, // Update every 5 minutes.
|
||||
animationSpeed: 2000,
|
||||
fade: true,
|
||||
urgency: 7,
|
||||
timeFormat: "relative",
|
||||
dateFormat: "MMM Do",
|
||||
dateEndFormat: "LT",
|
||||
fullDayEventDateFormat: "MMM Do",
|
||||
showEnd: false,
|
||||
getRelative: 6,
|
||||
fadePoint: 0.25, // Start on 1/4th of the list.
|
||||
hidePrivate: false,
|
||||
hideOngoing: false,
|
||||
colored: false,
|
||||
coloredSymbolOnly: false,
|
||||
customEvents: [], // Array of {keyword: "", symbol: "", color: ""} where Keyword is a regexp and symbol/color are to be applied for matched
|
||||
tableClass: "small",
|
||||
calendars: [
|
||||
{
|
||||
symbol: "calendar",
|
||||
url: "https://www.calendarlabs.com/templates/ical/US-Holidays.ics"
|
||||
}
|
||||
],
|
||||
titleReplace: {
|
||||
"De verjaardag van ": "",
|
||||
"'s birthday": ""
|
||||
},
|
||||
locationTitleReplace: {
|
||||
"street ": ""
|
||||
},
|
||||
broadcastEvents: true,
|
||||
excludedEvents: [],
|
||||
sliceMultiDayEvents: false,
|
||||
broadcastPastEvents: false,
|
||||
nextDaysRelative: false
|
||||
},
|
||||
|
||||
requiresVersion: "2.1.0",
|
||||
|
||||
// Define required scripts.
|
||||
getStyles: function () {
|
||||
return ["calendar.css", "font-awesome.css"];
|
||||
},
|
||||
|
||||
// Define required scripts.
|
||||
getScripts: function () {
|
||||
return ["moment.js"];
|
||||
},
|
||||
|
||||
// Define required translations.
|
||||
getTranslations: function () {
|
||||
// The translations for the default modules are defined in the core translation files.
|
||||
// Therefor we can just return false. Otherwise we should have returned a dictionary.
|
||||
// If you're trying to build your own module including translations, check out the documentation.
|
||||
return false;
|
||||
},
|
||||
|
||||
// Override start method.
|
||||
start: function () {
|
||||
Log.log("Starting module: " + this.name);
|
||||
|
||||
// Set locale.
|
||||
moment.updateLocale(config.language, this.getLocaleSpecification(config.timeFormat));
|
||||
|
||||
// clear data holder before start
|
||||
this.calendarData = {};
|
||||
|
||||
// indicate no data available yet
|
||||
this.loaded = false;
|
||||
|
||||
for (var c in this.config.calendars) {
|
||||
var calendar = this.config.calendars[c];
|
||||
calendar.url = calendar.url.replace("webcal://", "http://");
|
||||
|
||||
var calendarConfig = {
|
||||
maximumEntries: calendar.maximumEntries,
|
||||
maximumNumberOfDays: calendar.maximumNumberOfDays,
|
||||
broadcastPastEvents: calendar.broadcastPastEvents
|
||||
};
|
||||
if (calendar.symbolClass === "undefined" || calendar.symbolClass === null) {
|
||||
calendarConfig.symbolClass = "";
|
||||
}
|
||||
if (calendar.titleClass === "undefined" || calendar.titleClass === null) {
|
||||
calendarConfig.titleClass = "";
|
||||
}
|
||||
if (calendar.timeClass === "undefined" || calendar.timeClass === null) {
|
||||
calendarConfig.timeClass = "";
|
||||
}
|
||||
|
||||
// we check user and password here for backwards compatibility with old configs
|
||||
if (calendar.user && calendar.pass) {
|
||||
Log.warn("Deprecation warning: Please update your calendar authentication configuration.");
|
||||
Log.warn("https://github.com/MichMich/MagicMirror/tree/v2.1.2/modules/default/calendar#calendar-authentication-options");
|
||||
calendar.auth = {
|
||||
user: calendar.user,
|
||||
pass: calendar.pass
|
||||
};
|
||||
}
|
||||
|
||||
// tell helper to start a fetcher for this calendar
|
||||
// fetcher till cycle
|
||||
this.addCalendar(calendar.url, calendar.auth, calendarConfig);
|
||||
}
|
||||
},
|
||||
|
||||
// Override socket notification handler.
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
if (this.identifier !== payload.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (notification === "CALENDAR_EVENTS") {
|
||||
if (this.hasCalendarURL(payload.url)) {
|
||||
this.calendarData[payload.url] = payload.events;
|
||||
this.loaded = true;
|
||||
|
||||
if (this.config.broadcastEvents) {
|
||||
this.broadcastEvents();
|
||||
}
|
||||
}
|
||||
} else if (notification === "FETCH_ERROR") {
|
||||
Log.error("Calendar Error. Could not fetch calendar: " + payload.url);
|
||||
this.loaded = true;
|
||||
} else if (notification === "INCORRECT_URL") {
|
||||
Log.error("Calendar Error. Incorrect url: " + payload.url);
|
||||
}
|
||||
|
||||
this.updateDom(this.config.animationSpeed);
|
||||
},
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function () {
|
||||
// Define second, minute, hour, and day constants
|
||||
const oneSecond = 1000; // 1,000 milliseconds
|
||||
const oneMinute = oneSecond * 60;
|
||||
const oneHour = oneMinute * 60;
|
||||
const oneDay = oneHour * 24;
|
||||
|
||||
var events = this.createEventList();
|
||||
var wrapper = document.createElement("table");
|
||||
wrapper.className = this.config.tableClass;
|
||||
|
||||
if (events.length === 0) {
|
||||
wrapper.innerHTML = this.loaded ? this.translate("EMPTY") : this.translate("LOADING");
|
||||
wrapper.className = this.config.tableClass + " dimmed";
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
if (this.config.fade && this.config.fadePoint < 1) {
|
||||
if (this.config.fadePoint < 0) {
|
||||
this.config.fadePoint = 0;
|
||||
}
|
||||
var startFade = events.length * this.config.fadePoint;
|
||||
var fadeSteps = events.length - startFade;
|
||||
}
|
||||
|
||||
var currentFadeStep = 0;
|
||||
var lastSeenDate = "";
|
||||
var ev;
|
||||
var needle;
|
||||
|
||||
for (var e in events) {
|
||||
var event = events[e];
|
||||
var dateAsString = moment(event.startDate, "x").format(this.config.dateFormat);
|
||||
if (this.config.timeFormat === "dateheaders") {
|
||||
if (lastSeenDate !== dateAsString) {
|
||||
var dateRow = document.createElement("tr");
|
||||
dateRow.className = "normal";
|
||||
var dateCell = document.createElement("td");
|
||||
|
||||
dateCell.colSpan = "3";
|
||||
dateCell.innerHTML = dateAsString;
|
||||
dateCell.style.paddingTop = "10px";
|
||||
dateRow.appendChild(dateCell);
|
||||
wrapper.appendChild(dateRow);
|
||||
|
||||
if (e >= startFade) {
|
||||
//fading
|
||||
currentFadeStep = e - startFade;
|
||||
dateRow.style.opacity = 1 - (1 / fadeSteps) * currentFadeStep;
|
||||
}
|
||||
|
||||
lastSeenDate = dateAsString;
|
||||
}
|
||||
}
|
||||
|
||||
var eventWrapper = document.createElement("tr");
|
||||
|
||||
if (this.config.colored && !this.config.coloredSymbolOnly) {
|
||||
eventWrapper.style.cssText = "color:" + this.colorForUrl(event.url);
|
||||
}
|
||||
|
||||
eventWrapper.className = "normal event";
|
||||
|
||||
if (this.config.displaySymbol) {
|
||||
var symbolWrapper = document.createElement("td");
|
||||
|
||||
if (this.config.colored && this.config.coloredSymbolOnly) {
|
||||
symbolWrapper.style.cssText = "color:" + this.colorForUrl(event.url);
|
||||
}
|
||||
|
||||
var symbolClass = this.symbolClassForUrl(event.url);
|
||||
symbolWrapper.className = "symbol align-right " + symbolClass;
|
||||
|
||||
var symbols = this.symbolsForEvent(event);
|
||||
// If symbols are displayed and custom symbol is set, replace event symbol
|
||||
if (this.config.displaySymbol && this.config.customEvents.length > 0) {
|
||||
for (ev in this.config.customEvents) {
|
||||
if (typeof this.config.customEvents[ev].symbol !== "undefined" && this.config.customEvents[ev].symbol !== "") {
|
||||
needle = new RegExp(this.config.customEvents[ev].keyword, "gi");
|
||||
if (needle.test(event.title)) {
|
||||
symbols[0] = this.config.customEvents[ev].symbol;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < symbols.length; i++) {
|
||||
var symbol = document.createElement("span");
|
||||
symbol.className = "fa fa-fw fa-" + symbols[i];
|
||||
if (i > 0) {
|
||||
symbol.style.paddingLeft = "5px";
|
||||
}
|
||||
symbolWrapper.appendChild(symbol);
|
||||
}
|
||||
|
||||
eventWrapper.appendChild(symbolWrapper);
|
||||
} else if (this.config.timeFormat === "dateheaders") {
|
||||
var blankCell = document.createElement("td");
|
||||
blankCell.innerHTML = " ";
|
||||
eventWrapper.appendChild(blankCell);
|
||||
}
|
||||
|
||||
var titleWrapper = document.createElement("td"),
|
||||
repeatingCountTitle = "";
|
||||
|
||||
if (this.config.displayRepeatingCountTitle && event.firstYear !== undefined) {
|
||||
repeatingCountTitle = this.countTitleForUrl(event.url);
|
||||
|
||||
if (repeatingCountTitle !== "") {
|
||||
var thisYear = new Date(parseInt(event.startDate)).getFullYear(),
|
||||
yearDiff = thisYear - event.firstYear;
|
||||
|
||||
repeatingCountTitle = ", " + yearDiff + ". " + repeatingCountTitle;
|
||||
}
|
||||
}
|
||||
|
||||
// Color events if custom color is specified
|
||||
if (this.config.customEvents.length > 0) {
|
||||
for (ev in this.config.customEvents) {
|
||||
if (typeof this.config.customEvents[ev].color !== "undefined" && this.config.customEvents[ev].color !== "") {
|
||||
needle = new RegExp(this.config.customEvents[ev].keyword, "gi");
|
||||
if (needle.test(event.title)) {
|
||||
eventWrapper.style.cssText = "color:" + this.config.customEvents[ev].color;
|
||||
titleWrapper.style.cssText = "color:" + this.config.customEvents[ev].color;
|
||||
if (this.config.displaySymbol) {
|
||||
symbolWrapper.style.cssText = "color:" + this.config.customEvents[ev].color;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
titleWrapper.innerHTML = this.titleTransform(event.title, this.config.titleReplace, this.config.wrapEvents, this.config.maxTitleLength, this.config.maxTitleLines) + repeatingCountTitle;
|
||||
|
||||
var titleClass = this.titleClassForUrl(event.url);
|
||||
|
||||
if (!this.config.colored) {
|
||||
titleWrapper.className = "title bright " + titleClass;
|
||||
} else {
|
||||
titleWrapper.className = "title " + titleClass;
|
||||
}
|
||||
|
||||
var timeWrapper;
|
||||
|
||||
if (this.config.timeFormat === "dateheaders") {
|
||||
if (event.fullDayEvent) {
|
||||
titleWrapper.colSpan = "2";
|
||||
titleWrapper.align = "left";
|
||||
} else {
|
||||
timeWrapper = document.createElement("td");
|
||||
timeWrapper.className = "time light " + this.timeClassForUrl(event.url);
|
||||
timeWrapper.align = "left";
|
||||
timeWrapper.style.paddingLeft = "2px";
|
||||
timeWrapper.innerHTML = moment(event.startDate, "x").format("LT");
|
||||
eventWrapper.appendChild(timeWrapper);
|
||||
titleWrapper.align = "right";
|
||||
}
|
||||
|
||||
eventWrapper.appendChild(titleWrapper);
|
||||
} else {
|
||||
timeWrapper = document.createElement("td");
|
||||
|
||||
eventWrapper.appendChild(titleWrapper);
|
||||
var now = new Date();
|
||||
|
||||
if (this.config.timeFormat === "absolute") {
|
||||
// Use dateFormat
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.dateFormat));
|
||||
// Add end time if showEnd
|
||||
if (this.config.showEnd) {
|
||||
timeWrapper.innerHTML += "-";
|
||||
timeWrapper.innerHTML += this.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat));
|
||||
}
|
||||
// For full day events we use the fullDayEventDateFormat
|
||||
if (event.fullDayEvent) {
|
||||
//subtract one second so that fullDayEvents end at 23:59:59, and not at 0:00:00 one the next day
|
||||
event.endDate -= oneSecond;
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.fullDayEventDateFormat));
|
||||
}
|
||||
if (this.config.getRelative > 0 && event.startDate < now) {
|
||||
// Ongoing and getRelative is set
|
||||
timeWrapper.innerHTML = this.capFirst(
|
||||
this.translate("RUNNING", {
|
||||
fallback: this.translate("RUNNING") + " {timeUntilEnd}",
|
||||
timeUntilEnd: moment(event.endDate, "x").fromNow(true)
|
||||
})
|
||||
);
|
||||
} else if (this.config.urgency > 0 && event.startDate - now < this.config.urgency * oneDay) {
|
||||
// Within urgency days
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
||||
}
|
||||
if (event.fullDayEvent && this.config.nextDaysRelative) {
|
||||
// Full days events within the next two days
|
||||
if (event.today) {
|
||||
timeWrapper.innerHTML = this.capFirst(this.translate("TODAY"));
|
||||
} else if (event.startDate - now < oneDay && event.startDate - now > 0) {
|
||||
timeWrapper.innerHTML = this.capFirst(this.translate("TOMORROW"));
|
||||
} else if (event.startDate - now < 2 * oneDay && event.startDate - now > 0) {
|
||||
if (this.translate("DAYAFTERTOMORROW") !== "DAYAFTERTOMORROW") {
|
||||
timeWrapper.innerHTML = this.capFirst(this.translate("DAYAFTERTOMORROW"));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Show relative times
|
||||
if (event.startDate >= now) {
|
||||
// Use relative time
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").calendar());
|
||||
if (event.startDate - now < this.config.getRelative * oneHour) {
|
||||
// If event is within getRelative hours, display 'in xxx' time format or moment.fromNow()
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
||||
}
|
||||
} else {
|
||||
// Ongoing event
|
||||
timeWrapper.innerHTML = this.capFirst(
|
||||
this.translate("RUNNING", {
|
||||
fallback: this.translate("RUNNING") + " {timeUntilEnd}",
|
||||
timeUntilEnd: moment(event.endDate, "x").fromNow(true)
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
timeWrapper.className = "time light " + this.timeClassForUrl(event.url);
|
||||
eventWrapper.appendChild(timeWrapper);
|
||||
}
|
||||
|
||||
wrapper.appendChild(eventWrapper);
|
||||
|
||||
// Create fade effect.
|
||||
if (e >= startFade) {
|
||||
currentFadeStep = e - startFade;
|
||||
eventWrapper.style.opacity = 1 - (1 / fadeSteps) * currentFadeStep;
|
||||
}
|
||||
|
||||
if (this.config.showLocation) {
|
||||
if (event.location !== false) {
|
||||
var locationRow = document.createElement("tr");
|
||||
locationRow.className = "normal xsmall light";
|
||||
|
||||
if (this.config.displaySymbol) {
|
||||
var symbolCell = document.createElement("td");
|
||||
locationRow.appendChild(symbolCell);
|
||||
}
|
||||
|
||||
var descCell = document.createElement("td");
|
||||
descCell.className = "location";
|
||||
descCell.colSpan = "2";
|
||||
descCell.innerHTML = this.titleTransform(event.location, this.config.locationTitleReplace, this.config.wrapLocationEvents, this.config.maxLocationTitleLength, this.config.maxEventTitleLines);
|
||||
locationRow.appendChild(descCell);
|
||||
|
||||
wrapper.appendChild(locationRow);
|
||||
|
||||
if (e >= startFade) {
|
||||
currentFadeStep = e - startFade;
|
||||
locationRow.style.opacity = 1 - (1 / fadeSteps) * currentFadeStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
},
|
||||
|
||||
/**
|
||||
* This function accepts a number (either 12 or 24) and returns a moment.js LocaleSpecification with the
|
||||
* corresponding timeformat to be used in the calendar display. If no number is given (or otherwise invalid input)
|
||||
* it will a localeSpecification object with the system locale time format.
|
||||
*
|
||||
* @param {number} timeFormat Specifies either 12 or 24 hour time format
|
||||
* @returns {moment.LocaleSpecification} formatted time
|
||||
*/
|
||||
getLocaleSpecification: function (timeFormat) {
|
||||
switch (timeFormat) {
|
||||
case 12: {
|
||||
return { longDateFormat: { LT: "h:mm A" } };
|
||||
}
|
||||
case 24: {
|
||||
return { longDateFormat: { LT: "HH:mm" } };
|
||||
}
|
||||
default: {
|
||||
return { longDateFormat: { LT: moment.localeData().longDateFormat("LT") } };
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if this config contains the calendar url.
|
||||
*
|
||||
* @param {string} url The calendar url
|
||||
* @returns {boolean} True if the calendar config contains the url, False otherwise
|
||||
*/
|
||||
hasCalendarURL: function (url) {
|
||||
for (var c in this.config.calendars) {
|
||||
var calendar = this.config.calendars[c];
|
||||
if (calendar.url === url) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates the sorted list of all events.
|
||||
*
|
||||
* @returns {object[]} Array with events.
|
||||
*/
|
||||
createEventList: function () {
|
||||
var events = [];
|
||||
var today = moment().startOf("day");
|
||||
var now = new Date();
|
||||
var future = moment().startOf("day").add(this.config.maximumNumberOfDays, "days").toDate();
|
||||
for (var c in this.calendarData) {
|
||||
var calendar = this.calendarData[c];
|
||||
for (var e in calendar) {
|
||||
var event = JSON.parse(JSON.stringify(calendar[e])); // clone object
|
||||
|
||||
if (event.endDate < now) {
|
||||
continue;
|
||||
}
|
||||
if (this.config.hidePrivate) {
|
||||
if (event.class === "PRIVATE") {
|
||||
// do not add the current event, skip it
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (this.config.hideOngoing) {
|
||||
if (event.startDate < now) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (this.listContainsEvent(events, event)) {
|
||||
continue;
|
||||
}
|
||||
event.url = c;
|
||||
event.today = event.startDate >= today && event.startDate < today + 24 * 60 * 60 * 1000;
|
||||
|
||||
/* if sliceMultiDayEvents is set to true, multiday events (events exceeding at least one midnight) are sliced into days,
|
||||
* otherwise, esp. in dateheaders mode it is not clear how long these events are.
|
||||
*/
|
||||
var maxCount = Math.ceil((event.endDate - 1 - moment(event.startDate, "x").endOf("day").format("x")) / (1000 * 60 * 60 * 24)) + 1;
|
||||
if (this.config.sliceMultiDayEvents && maxCount > 1) {
|
||||
var splitEvents = [];
|
||||
var midnight = moment(event.startDate, "x").clone().startOf("day").add(1, "day").format("x");
|
||||
var count = 1;
|
||||
while (event.endDate > midnight) {
|
||||
var thisEvent = JSON.parse(JSON.stringify(event)); // clone object
|
||||
thisEvent.today = thisEvent.startDate >= today && thisEvent.startDate < today + 24 * 60 * 60 * 1000;
|
||||
thisEvent.endDate = midnight;
|
||||
thisEvent.title += " (" + count + "/" + maxCount + ")";
|
||||
splitEvents.push(thisEvent);
|
||||
|
||||
event.startDate = midnight;
|
||||
count += 1;
|
||||
midnight = moment(midnight, "x").add(1, "day").format("x"); // next day
|
||||
}
|
||||
// Last day
|
||||
event.title += " (" + count + "/" + maxCount + ")";
|
||||
splitEvents.push(event);
|
||||
|
||||
for (event of splitEvents) {
|
||||
if (event.endDate > now && event.endDate <= future) {
|
||||
events.push(event);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
events.push(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
events.sort(function (a, b) {
|
||||
return a.startDate - b.startDate;
|
||||
});
|
||||
|
||||
// Limit the number of days displayed
|
||||
// If limitDays is set > 0, limit display to that number of days
|
||||
if (this.config.limitDays > 0) {
|
||||
var newEvents = [];
|
||||
var lastDate = today.clone().subtract(1, "days").format("YYYYMMDD");
|
||||
var days = 0;
|
||||
var eventDate;
|
||||
for (var ev of events) {
|
||||
eventDate = moment(ev.startDate, "x").format("YYYYMMDD");
|
||||
// if date of event is later than lastdate
|
||||
// check if we already are showing max unique days
|
||||
if (eventDate > lastDate) {
|
||||
// if the only entry in the first day is a full day event that day is not counted as unique
|
||||
if (newEvents.length === 1 && days === 1 && newEvents[0].fullDayEvent) {
|
||||
days--;
|
||||
}
|
||||
days++;
|
||||
if (days > this.config.limitDays) {
|
||||
continue;
|
||||
} else {
|
||||
lastDate = eventDate;
|
||||
}
|
||||
}
|
||||
newEvents.push(ev);
|
||||
}
|
||||
events = newEvents;
|
||||
}
|
||||
|
||||
return events.slice(0, this.config.maximumEntries);
|
||||
},
|
||||
|
||||
listContainsEvent: function (eventList, event) {
|
||||
for (var evt of eventList) {
|
||||
if (evt.title === event.title && parseInt(evt.startDate) === parseInt(event.startDate)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Requests node helper to add calendar url.
|
||||
*
|
||||
* @param {string} url The calendar url to add
|
||||
* @param {object} auth The authentication method and credentials
|
||||
* @param {object} calendarConfig The config of the specific calendar
|
||||
*/
|
||||
addCalendar: function (url, auth, calendarConfig) {
|
||||
var self = this;
|
||||
|
||||
this.sendSocketNotification("ADD_CALENDAR", {
|
||||
id: this.identifier,
|
||||
url: url,
|
||||
excludedEvents: calendarConfig.excludedEvents || this.config.excludedEvents,
|
||||
maximumEntries: calendarConfig.maximumEntries || this.config.maximumEntries,
|
||||
maximumNumberOfDays: calendarConfig.maximumNumberOfDays || this.config.maximumNumberOfDays,
|
||||
fetchInterval: this.config.fetchInterval,
|
||||
symbolClass: calendarConfig.symbolClass,
|
||||
titleClass: calendarConfig.titleClass,
|
||||
timeClass: calendarConfig.timeClass,
|
||||
auth: auth,
|
||||
broadcastPastEvents: calendarConfig.broadcastPastEvents || this.config.broadcastPastEvents
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the symbols for a specific event.
|
||||
*
|
||||
* @param {object} event Event to look for.
|
||||
* @returns {string[]} The symbols
|
||||
*/
|
||||
symbolsForEvent: function (event) {
|
||||
let symbols = this.getCalendarPropertyAsArray(event.url, "symbol", this.config.defaultSymbol);
|
||||
|
||||
if (event.recurringEvent === true && this.hasCalendarProperty(event.url, "recurringSymbol")) {
|
||||
symbols = this.mergeUnique(this.getCalendarPropertyAsArray(event.url, "recurringSymbol", this.config.defaultSymbol), symbols);
|
||||
}
|
||||
|
||||
if (event.fullDayEvent === true && this.hasCalendarProperty(event.url, "fullDaySymbol")) {
|
||||
symbols = this.mergeUnique(this.getCalendarPropertyAsArray(event.url, "fullDaySymbol", this.config.defaultSymbol), symbols);
|
||||
}
|
||||
|
||||
return symbols;
|
||||
},
|
||||
|
||||
mergeUnique: function (arr1, arr2) {
|
||||
return arr1.concat(
|
||||
arr2.filter(function (item) {
|
||||
return arr1.indexOf(item) === -1;
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the symbolClass for a specific calendar url.
|
||||
*
|
||||
* @param {string} url The calendar url
|
||||
* @returns {string} The class to be used for the symbols of the calendar
|
||||
*/
|
||||
symbolClassForUrl: function (url) {
|
||||
return this.getCalendarProperty(url, "symbolClass", "");
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the titleClass for a specific calendar url.
|
||||
*
|
||||
* @param {string} url The calendar url
|
||||
* @returns {string} The class to be used for the title of the calendar
|
||||
*/
|
||||
titleClassForUrl: function (url) {
|
||||
return this.getCalendarProperty(url, "titleClass", "");
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the timeClass for a specific calendar url.
|
||||
*
|
||||
* @param {string} url The calendar url
|
||||
* @returns {string} The class to be used for the time of the calendar
|
||||
*/
|
||||
timeClassForUrl: function (url) {
|
||||
return this.getCalendarProperty(url, "timeClass", "");
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the calendar name for a specific calendar url.
|
||||
*
|
||||
* @param {string} url The calendar url
|
||||
* @returns {string} The name of the calendar
|
||||
*/
|
||||
calendarNameForUrl: function (url) {
|
||||
return this.getCalendarProperty(url, "name", "");
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the color for a specific calendar url.
|
||||
*
|
||||
* @param {string} url The calendar url
|
||||
* @returns {string} The color
|
||||
*/
|
||||
colorForUrl: function (url) {
|
||||
return this.getCalendarProperty(url, "color", "#fff");
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the count title for a specific calendar url.
|
||||
*
|
||||
* @param {string} url The calendar url
|
||||
* @returns {string} The title
|
||||
*/
|
||||
countTitleForUrl: function (url) {
|
||||
return this.getCalendarProperty(url, "repeatingCountTitle", this.config.defaultRepeatingCountTitle);
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper method to retrieve the property for a specific calendar url.
|
||||
*
|
||||
* @param {string} url The calendar url
|
||||
* @param {string} property The property to look for
|
||||
* @param {string} defaultValue The value if the property is not found
|
||||
* @returns {*} The property
|
||||
*/
|
||||
getCalendarProperty: function (url, property, defaultValue) {
|
||||
for (var c in this.config.calendars) {
|
||||
var calendar = this.config.calendars[c];
|
||||
if (calendar.url === url && calendar.hasOwnProperty(property)) {
|
||||
return calendar[property];
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
},
|
||||
|
||||
getCalendarPropertyAsArray: function (url, property, defaultValue) {
|
||||
let p = this.getCalendarProperty(url, property, defaultValue);
|
||||
if (!(p instanceof Array)) p = [p];
|
||||
return p;
|
||||
},
|
||||
|
||||
hasCalendarProperty: function (url, property) {
|
||||
return !!this.getCalendarProperty(url, property, undefined);
|
||||
},
|
||||
|
||||
/**
|
||||
* Shortens a string if it's longer than maxLength and add a ellipsis to the end
|
||||
*
|
||||
* @param {string} string Text string to shorten
|
||||
* @param {number} maxLength The max length of the string
|
||||
* @param {boolean} wrapEvents Wrap the text after the line has reached maxLength
|
||||
* @param {number} maxTitleLines The max number of vertical lines before cutting event title
|
||||
* @returns {string} The shortened string
|
||||
*/
|
||||
shorten: function (string, maxLength, wrapEvents, maxTitleLines) {
|
||||
if (typeof string !== "string") {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (wrapEvents === true) {
|
||||
var temp = "";
|
||||
var currentLine = "";
|
||||
var words = string.split(" ");
|
||||
var line = 0;
|
||||
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
var word = words[i];
|
||||
if (currentLine.length + word.length < (typeof maxLength === "number" ? maxLength : 25) - 1) {
|
||||
// max - 1 to account for a space
|
||||
currentLine += word + " ";
|
||||
} else {
|
||||
line++;
|
||||
if (line > maxTitleLines - 1) {
|
||||
if (i < words.length) {
|
||||
currentLine += "…";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (currentLine.length > 0) {
|
||||
temp += currentLine + "<br>" + word + " ";
|
||||
} else {
|
||||
temp += word + "<br>";
|
||||
}
|
||||
currentLine = "";
|
||||
}
|
||||
}
|
||||
|
||||
return (temp + currentLine).trim();
|
||||
} else {
|
||||
if (maxLength && typeof maxLength === "number" && string.length > maxLength) {
|
||||
return string.trim().slice(0, maxLength) + "…";
|
||||
} else {
|
||||
return string.trim();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Capitalize the first letter of a string
|
||||
*
|
||||
* @param {string} string The string to capitalize
|
||||
* @returns {string} The capitalized string
|
||||
*/
|
||||
capFirst: function (string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
},
|
||||
|
||||
/**
|
||||
* Transforms the title of an event for usage.
|
||||
* Replaces parts of the text as defined in config.titleReplace.
|
||||
* Shortens title based on config.maxTitleLength and config.wrapEvents
|
||||
*
|
||||
* @param {string} title The title to transform.
|
||||
* @param {object} titleReplace Pairs of strings to be replaced in the title
|
||||
* @param {boolean} wrapEvents Wrap the text after the line has reached maxLength
|
||||
* @param {number} maxTitleLength The max length of the string
|
||||
* @param {number} maxTitleLines The max number of vertical lines before cutting event title
|
||||
* @returns {string} The transformed title.
|
||||
*/
|
||||
titleTransform: function (title, titleReplace, wrapEvents, maxTitleLength, maxTitleLines) {
|
||||
for (var needle in titleReplace) {
|
||||
var replacement = titleReplace[needle];
|
||||
|
||||
var regParts = needle.match(/^\/(.+)\/([gim]*)$/);
|
||||
if (regParts) {
|
||||
// the parsed pattern is a regexp.
|
||||
needle = new RegExp(regParts[1], regParts[2]);
|
||||
}
|
||||
|
||||
title = title.replace(needle, replacement);
|
||||
}
|
||||
|
||||
title = this.shorten(title, maxTitleLength, wrapEvents, maxTitleLines);
|
||||
return title;
|
||||
},
|
||||
|
||||
/**
|
||||
* Broadcasts the events to all other modules for reuse.
|
||||
* The all events available in one array, sorted on startdate.
|
||||
*/
|
||||
broadcastEvents: function () {
|
||||
var eventList = [];
|
||||
for (var url in this.calendarData) {
|
||||
var calendar = this.calendarData[url];
|
||||
for (var e in calendar) {
|
||||
var event = cloneObject(calendar[e]);
|
||||
event.symbol = this.symbolsForEvent(event);
|
||||
event.calendarName = this.calendarNameForUrl(url);
|
||||
event.color = this.colorForUrl(url);
|
||||
delete event.url;
|
||||
eventList.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
eventList.sort(function (a, b) {
|
||||
return a.startDate - b.startDate;
|
||||
});
|
||||
|
||||
this.sendNotification("CALENDAR_EVENTS", eventList);
|
||||
}
|
||||
});
|
||||
670
modules/default/calendar/calendarfetcher.js
Normal file
670
modules/default/calendar/calendarfetcher.js
Normal file
@@ -0,0 +1,670 @@
|
||||
/* Magic Mirror
|
||||
* Node Helper: Calendar - CalendarFetcher
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const Log = require("../../../js/logger.js");
|
||||
const ical = require("node-ical");
|
||||
const request = require("request");
|
||||
|
||||
/**
|
||||
* Moment date
|
||||
*
|
||||
* @external Moment
|
||||
* @see {@link http://momentjs.com}
|
||||
*/
|
||||
const moment = require("moment");
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} url The url of the calendar to fetch
|
||||
* @param {number} reloadInterval Time in ms the calendar is fetched again
|
||||
* @param {string[]} excludedEvents An array of words / phrases from event titles that will be excluded from being shown.
|
||||
* @param {number} maximumEntries The maximum number of events fetched.
|
||||
* @param {number} maximumNumberOfDays The maximum number of days an event should be in the future.
|
||||
* @param {object} auth The object containing options for authentication against the calendar.
|
||||
* @param {boolean} includePastEvents If true events from the past maximumNumberOfDays will be fetched too
|
||||
* @class
|
||||
*/
|
||||
const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, includePastEvents) {
|
||||
const self = this;
|
||||
|
||||
let reloadTimer = null;
|
||||
let events = [];
|
||||
|
||||
let fetchFailedCallback = function () {};
|
||||
let eventsReceivedCallback = function () {};
|
||||
|
||||
/**
|
||||
* Initiates calendar fetch.
|
||||
*/
|
||||
const fetchCalendar = function () {
|
||||
clearTimeout(reloadTimer);
|
||||
reloadTimer = null;
|
||||
|
||||
const nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
|
||||
const opts = {
|
||||
headers: {
|
||||
"User-Agent": "Mozilla/5.0 (Node.js " + nodeVersion + ") MagicMirror/" + global.version + " (https://github.com/MichMich/MagicMirror/)"
|
||||
},
|
||||
gzip: true
|
||||
};
|
||||
|
||||
if (auth) {
|
||||
if (auth.method === "bearer") {
|
||||
opts.auth = {
|
||||
bearer: auth.pass
|
||||
};
|
||||
} else {
|
||||
opts.auth = {
|
||||
user: auth.user,
|
||||
pass: auth.pass,
|
||||
sendImmediately: auth.method !== "digest"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
request(url, opts, function (err, r, requestData) {
|
||||
if (err) {
|
||||
fetchFailedCallback(self, err);
|
||||
scheduleTimer();
|
||||
return;
|
||||
} else if (r.statusCode !== 200) {
|
||||
fetchFailedCallback(self, r.statusCode + ": " + r.statusMessage);
|
||||
scheduleTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
let data = [];
|
||||
|
||||
try {
|
||||
data = ical.parseICS(requestData);
|
||||
} catch (error) {
|
||||
fetchFailedCallback(self, error.message);
|
||||
scheduleTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
Log.debug(" parsed data=" + JSON.stringify(data));
|
||||
|
||||
const newEvents = [];
|
||||
|
||||
// limitFunction doesn't do much limiting, see comment re: the dates array in rrule section below as to why we need to do the filtering ourselves
|
||||
const limitFunction = function (date, i) {
|
||||
return true;
|
||||
};
|
||||
|
||||
const eventDate = function (event, time) {
|
||||
return isFullDayEvent(event) ? moment(event[time], "YYYYMMDD") : moment(new Date(event[time]));
|
||||
};
|
||||
Log.debug("there are " + Object.entries(data).length + " calendar entries");
|
||||
Object.entries(data).forEach(([key, event]) => {
|
||||
const now = new Date();
|
||||
const today = moment().startOf("day").toDate();
|
||||
const future = moment().startOf("day").add(maximumNumberOfDays, "days").subtract(1, "seconds").toDate(); // Subtract 1 second so that events that start on the middle of the night will not repeat.
|
||||
let past = today;
|
||||
Log.debug("have entries ");
|
||||
if (includePastEvents) {
|
||||
past = moment().startOf("day").subtract(maximumNumberOfDays, "days").toDate();
|
||||
}
|
||||
|
||||
// FIXME: Ugly fix to solve the facebook birthday issue.
|
||||
// Otherwise, the recurring events only show the birthday for next year.
|
||||
let isFacebookBirthday = false;
|
||||
if (typeof event.uid !== "undefined") {
|
||||
if (event.uid.indexOf("@facebook.com") !== -1) {
|
||||
isFacebookBirthday = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.type === "VEVENT") {
|
||||
let startDate = eventDate(event, "start");
|
||||
let endDate;
|
||||
|
||||
Log.debug("\nevent=" + JSON.stringify(event));
|
||||
if (typeof event.end !== "undefined") {
|
||||
endDate = eventDate(event, "end");
|
||||
} else if (typeof event.duration !== "undefined") {
|
||||
endDate = startDate.clone().add(moment.duration(event.duration));
|
||||
} else {
|
||||
if (!isFacebookBirthday) {
|
||||
// make copy of start date, separate storage area
|
||||
endDate = moment(startDate.format("x"), "x");
|
||||
} else {
|
||||
endDate = moment(startDate).add(1, "days");
|
||||
}
|
||||
}
|
||||
|
||||
Log.debug(" start=" + startDate.toDate() + " end=" + endDate.toDate());
|
||||
|
||||
// calculate the duration of the event for use with recurring events.
|
||||
let duration = parseInt(endDate.format("x")) - parseInt(startDate.format("x"));
|
||||
|
||||
if (event.start.length === 8) {
|
||||
startDate = startDate.startOf("day");
|
||||
}
|
||||
|
||||
const title = getTitleFromEvent(event);
|
||||
|
||||
let excluded = false,
|
||||
dateFilter = null;
|
||||
|
||||
for (let f in excludedEvents) {
|
||||
let filter = excludedEvents[f],
|
||||
testTitle = title.toLowerCase(),
|
||||
until = null,
|
||||
useRegex = false,
|
||||
regexFlags = "g";
|
||||
|
||||
if (filter instanceof Object) {
|
||||
if (typeof filter.until !== "undefined") {
|
||||
until = filter.until;
|
||||
}
|
||||
|
||||
if (typeof filter.regex !== "undefined") {
|
||||
useRegex = filter.regex;
|
||||
}
|
||||
|
||||
// If additional advanced filtering is added in, this section
|
||||
// must remain last as we overwrite the filter object with the
|
||||
// filterBy string
|
||||
if (filter.caseSensitive) {
|
||||
filter = filter.filterBy;
|
||||
testTitle = title;
|
||||
} else if (useRegex) {
|
||||
filter = filter.filterBy;
|
||||
testTitle = title;
|
||||
regexFlags += "i";
|
||||
} else {
|
||||
filter = filter.filterBy.toLowerCase();
|
||||
}
|
||||
} else {
|
||||
filter = filter.toLowerCase();
|
||||
}
|
||||
|
||||
if (testTitleByFilter(testTitle, filter, useRegex, regexFlags)) {
|
||||
if (until) {
|
||||
dateFilter = until;
|
||||
} else {
|
||||
excluded = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (excluded) {
|
||||
return;
|
||||
}
|
||||
|
||||
const location = event.location || false;
|
||||
const geo = event.geo || false;
|
||||
const description = event.description || false;
|
||||
|
||||
if (typeof event.rrule !== "undefined" && event.rrule !== null && !isFacebookBirthday) {
|
||||
const rule = event.rrule;
|
||||
let addedEvents = 0;
|
||||
|
||||
const pastMoment = moment(past);
|
||||
const futureMoment = moment(future);
|
||||
|
||||
// can cause problems with e.g. birthdays before 1900
|
||||
if ((rule.options && rule.origOptions && rule.origOptions.dtstart && rule.origOptions.dtstart.getFullYear() < 1900) || (rule.options && rule.options.dtstart && rule.options.dtstart.getFullYear() < 1900)) {
|
||||
rule.origOptions.dtstart.setYear(1900);
|
||||
rule.options.dtstart.setYear(1900);
|
||||
}
|
||||
|
||||
// For recurring events, get the set of start dates that fall within the range
|
||||
// of dates we're looking for.
|
||||
// kblankenship1989 - to fix issue #1798, converting all dates to locale time first, then converting back to UTC time
|
||||
let pastLocal = 0;
|
||||
let futureLocal = 0;
|
||||
if (isFullDayEvent(event)) {
|
||||
// if full day event, only use the date part of the ranges
|
||||
pastLocal = pastMoment.toDate();
|
||||
futureLocal = futureMoment.toDate();
|
||||
} else {
|
||||
// if we want past events
|
||||
if (includePastEvents) {
|
||||
// use the calculated past time for the between from
|
||||
pastLocal = pastMoment.toDate();
|
||||
} else {
|
||||
// otherwise use NOW.. cause we shouldnt use any before now
|
||||
pastLocal = moment().toDate(); //now
|
||||
}
|
||||
futureLocal = futureMoment.toDate(); // future
|
||||
}
|
||||
Log.debug(" between=" + pastLocal + " to " + futureLocal);
|
||||
const dates = rule.between(pastLocal, futureLocal, true, limitFunction);
|
||||
Log.debug("title=" + event.summary + " dates=" + JSON.stringify(dates));
|
||||
// The "dates" array contains the set of dates within our desired date range range that are valid
|
||||
// for the recurrence rule. *However*, it's possible for us to have a specific recurrence that
|
||||
// had its date changed from outside the range to inside the range. For the time being,
|
||||
// we'll handle this by adding *all* recurrence entries into the set of dates that we check,
|
||||
// because the logic below will filter out any recurrences that don't actually belong within
|
||||
// our display range.
|
||||
// Would be great if there was a better way to handle this.
|
||||
if (event.recurrences !== undefined) {
|
||||
for (let r in event.recurrences) {
|
||||
// Only add dates that weren't already in the range we added from the rrule so that
|
||||
// we don"t double-add those events.
|
||||
if (moment(new Date(r)).isBetween(pastMoment, futureMoment) !== true) {
|
||||
dates.push(new Date(r));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Loop through the set of date entries to see which recurrences should be added to our event list.
|
||||
for (let d in dates) {
|
||||
let date = dates[d];
|
||||
// ical.js started returning recurrences and exdates as ISOStrings without time information.
|
||||
// .toISOString().substring(0,10) is the method they use to calculate keys, so we'll do the same
|
||||
// (see https://github.com/peterbraden/ical.js/pull/84 )
|
||||
const dateKey = date.toISOString().substring(0, 10);
|
||||
let curEvent = event;
|
||||
let showRecurrence = true;
|
||||
|
||||
// for full day events, the time might be off from RRULE/Luxon problem
|
||||
if (isFullDayEvent(event)) {
|
||||
Log.debug("fullday");
|
||||
// if the offset is negative, east of GMT where the problem is
|
||||
if (date.getTimezoneOffset() < 0) {
|
||||
// get the offset of today where we are processing
|
||||
// this will be the correction we need to apply
|
||||
let nowOffset = new Date().getTimezoneOffset();
|
||||
Log.debug("now offset is " + nowOffset);
|
||||
// reduce the time by the offset
|
||||
Log.debug(" recurring date is " + date + " offset is " + date.getTimezoneOffset());
|
||||
// apply the correction to the date/time to get it UTC relative
|
||||
date = new Date(date.getTime() - Math.abs(nowOffset) * 60000);
|
||||
// the duration was calculated way back at the top before we could correct the start time..
|
||||
// fix it for this event entry
|
||||
duration = 24 * 60 * 60 * 1000;
|
||||
Log.debug("new recurring date is " + date);
|
||||
}
|
||||
}
|
||||
startDate = moment(date);
|
||||
|
||||
let adjustDays = getCorrection(event, date);
|
||||
|
||||
// For each date that we're checking, it's possible that there is a recurrence override for that one day.
|
||||
if (curEvent.recurrences !== undefined && curEvent.recurrences[dateKey] !== undefined) {
|
||||
// We found an override, so for this recurrence, use a potentially different title, start date, and duration.
|
||||
curEvent = curEvent.recurrences[dateKey];
|
||||
startDate = moment(curEvent.start);
|
||||
duration = parseInt(moment(curEvent.end).format("x")) - parseInt(startDate.format("x"));
|
||||
}
|
||||
// If there's no recurrence override, check for an exception date. Exception dates represent exceptions to the rule.
|
||||
else if (curEvent.exdate !== undefined && curEvent.exdate[dateKey] !== undefined) {
|
||||
// This date is an exception date, which means we should skip it in the recurrence pattern.
|
||||
showRecurrence = false;
|
||||
}
|
||||
Log.debug("duration=" + duration);
|
||||
|
||||
endDate = moment(parseInt(startDate.format("x")) + duration, "x");
|
||||
if (startDate.format("x") === endDate.format("x")) {
|
||||
endDate = endDate.endOf("day");
|
||||
}
|
||||
|
||||
const recurrenceTitle = getTitleFromEvent(curEvent);
|
||||
|
||||
// If this recurrence ends before the start of the date range, or starts after the end of the date range, don"t add
|
||||
// it to the event list.
|
||||
if (endDate.isBefore(past) || startDate.isAfter(future)) {
|
||||
showRecurrence = false;
|
||||
}
|
||||
|
||||
if (timeFilterApplies(now, endDate, dateFilter)) {
|
||||
showRecurrence = false;
|
||||
}
|
||||
|
||||
if (showRecurrence === true) {
|
||||
Log.debug("saving event =" + description);
|
||||
addedEvents++;
|
||||
newEvents.push({
|
||||
title: recurrenceTitle,
|
||||
startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"),
|
||||
endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"),
|
||||
fullDayEvent: isFullDayEvent(event),
|
||||
recurringEvent: true,
|
||||
class: event.class,
|
||||
firstYear: event.start.getFullYear(),
|
||||
location: location,
|
||||
geo: geo,
|
||||
description: description
|
||||
});
|
||||
}
|
||||
}
|
||||
// end recurring event parsing
|
||||
} else {
|
||||
// Single event.
|
||||
const fullDayEvent = isFacebookBirthday ? true : isFullDayEvent(event);
|
||||
// Log.debug("full day event")
|
||||
|
||||
if (includePastEvents) {
|
||||
// Past event is too far in the past, so skip.
|
||||
if (endDate < past) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// It's not a fullday event, and it is in the past, so skip.
|
||||
if (!fullDayEvent && endDate < new Date()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// It's a fullday event, and it is before today, So skip.
|
||||
if (fullDayEvent && endDate <= today) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// It exceeds the maximumNumberOfDays limit, so skip.
|
||||
if (startDate > future) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (timeFilterApplies(now, endDate, dateFilter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust start date so multiple day events will be displayed as happening today even though they started some days ago already
|
||||
if (fullDayEvent && startDate <= today) {
|
||||
startDate = moment(today);
|
||||
}
|
||||
// if the start and end are the same, then make end the 'end of day' value (start is at 00:00:00)
|
||||
if (fullDayEvent && startDate.format("x") === endDate.format("x")) {
|
||||
endDate = endDate.endOf("day");
|
||||
}
|
||||
// get correction for date saving and dst change between now and then
|
||||
let adjustDays = getCorrection(event, startDate.toDate());
|
||||
// Every thing is good. Add it to the list.
|
||||
newEvents.push({
|
||||
title: title,
|
||||
startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"),
|
||||
endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"),
|
||||
fullDayEvent: fullDayEvent,
|
||||
class: event.class,
|
||||
location: location,
|
||||
geo: geo,
|
||||
description: description
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
newEvents.sort(function (a, b) {
|
||||
return a.startDate - b.startDate;
|
||||
});
|
||||
|
||||
// include up to maximumEntries current or upcoming events
|
||||
// If past events should be included, include all past events
|
||||
const now = moment();
|
||||
var entries = 0;
|
||||
events = [];
|
||||
for (let ne of newEvents) {
|
||||
if (moment(ne.endDate, "x").isBefore(now)) {
|
||||
if (includePastEvents) events.push(ne);
|
||||
continue;
|
||||
}
|
||||
entries++;
|
||||
// If max events has been saved, skip the rest
|
||||
if (entries > maximumEntries) break;
|
||||
events.push(ne);
|
||||
}
|
||||
|
||||
self.broadcastEvents();
|
||||
scheduleTimer();
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
*
|
||||
* get the time correction, either dst/std or full day in cases where utc time is day before plus offset
|
||||
*
|
||||
*/
|
||||
const getCorrection = function (event, date) {
|
||||
let adjustHours = 0;
|
||||
// if a timezone was specified
|
||||
if (!event.start.tz) {
|
||||
Log.debug(" if no tz, guess based on now");
|
||||
event.start.tz = moment.tz.guess();
|
||||
}
|
||||
Log.debug("initial tz=" + event.start.tz);
|
||||
|
||||
// if there is a start date specified
|
||||
if (event.start.tz) {
|
||||
// if this is a windows timezone
|
||||
if (event.start.tz.includes(" ")) {
|
||||
// use the lookup table to get theIANA name as moment and date don't know MS timezones
|
||||
let tz = getIanaTZFromMS(event.start.tz);
|
||||
Log.debug("corrected TZ=" + tz);
|
||||
// watch out for unregistered windows timezone names
|
||||
// if we had a successfule lookup
|
||||
if (tz) {
|
||||
// change the timezone to the IANA name
|
||||
event.start.tz = tz;
|
||||
// Log.debug("corrected timezone="+event.start.tz)
|
||||
}
|
||||
}
|
||||
Log.debug("corrected tz=" + event.start.tz);
|
||||
let current_offset = 0; // offset from TZ string or calculated
|
||||
let mm = 0; // date with tz or offset
|
||||
let start_offset = 0; // utc offset of created with tz
|
||||
// if there is still an offset, lookup failed, use it
|
||||
if (event.start.tz.startsWith("(")) {
|
||||
const regex = /[+|-]\d*:\d*/;
|
||||
const start_offsetString = event.start.tz.match(regex).toString().split(":");
|
||||
let start_offset = parseInt(start_offsetString[0]);
|
||||
start_offset *= event.start.tz[1] === "-" ? -1 : 1;
|
||||
adjustHours = start_offset;
|
||||
Log.debug("defined offset=" + start_offset + " hours");
|
||||
current_offset = start_offset;
|
||||
event.start.tz = "";
|
||||
Log.debug("ical offset=" + current_offset + " date=" + date);
|
||||
mm = moment(date);
|
||||
let x = parseInt(moment(new Date()).utcOffset());
|
||||
Log.debug("net mins=" + (current_offset * 60 - x));
|
||||
|
||||
mm = mm.add(x - current_offset * 60, "minutes");
|
||||
adjustHours = (current_offset * 60 - x) / 60;
|
||||
event.start = mm.toDate();
|
||||
Log.debug("adjusted date=" + event.start);
|
||||
} else {
|
||||
// get the start time in that timezone
|
||||
Log.debug("start date/time=" + moment(event.start).toDate());
|
||||
start_offset = moment.tz(moment(event.start), event.start.tz).utcOffset();
|
||||
Log.debug("start offset=" + start_offset);
|
||||
|
||||
Log.debug("start date/time w tz =" + moment.tz(moment(event.start), event.start.tz).toDate());
|
||||
|
||||
// get the specified date in that timezone
|
||||
mm = moment.tz(moment(date), event.start.tz);
|
||||
Log.debug("event date=" + mm.toDate());
|
||||
current_offset = mm.utcOffset();
|
||||
}
|
||||
Log.debug("event offset=" + current_offset + " hour=" + mm.format("H") + " event date=" + mm.toDate());
|
||||
|
||||
// if the offset is greater than 0, east of london
|
||||
if (current_offset !== start_offset) {
|
||||
// big offset
|
||||
Log.debug("offset");
|
||||
let h = parseInt(mm.format("H"));
|
||||
// check if the event time is less than the offset
|
||||
if (h > 0 && h < Math.abs(current_offset) / 60) {
|
||||
// if so, rrule created a wrong date (utc day, oops, with utc yesterday adjusted time)
|
||||
// we need to fix that
|
||||
adjustHours = 24;
|
||||
// Log.debug("adjusting date")
|
||||
}
|
||||
//-300 > -240
|
||||
//if (Math.abs(current_offset) > Math.abs(start_offset)){
|
||||
if (current_offset > start_offset) {
|
||||
adjustHours -= 1;
|
||||
Log.debug("adjust down 1 hour dst change");
|
||||
//} else if (Math.abs(current_offset) < Math.abs(start_offset)) {
|
||||
} else if (current_offset < start_offset) {
|
||||
adjustHours += 1;
|
||||
Log.debug("adjust up 1 hour dst change");
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.debug("adjustHours=" + adjustHours);
|
||||
return adjustHours;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* lookup iana tz from windows
|
||||
*/
|
||||
let zoneTable = null;
|
||||
const getIanaTZFromMS = function (msTZName) {
|
||||
if (!zoneTable) {
|
||||
const p = require("path");
|
||||
zoneTable = require(p.join(__dirname, "windowsZones.json"));
|
||||
}
|
||||
// Get hash entry
|
||||
const he = zoneTable[msTZName];
|
||||
// If found return iana name, else null
|
||||
return he ? he.iana[0] : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Schedule the timer for the next update.
|
||||
*/
|
||||
const scheduleTimer = function () {
|
||||
clearTimeout(reloadTimer);
|
||||
reloadTimer = setTimeout(function () {
|
||||
fetchCalendar();
|
||||
}, reloadInterval);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if an event is a fullday event.
|
||||
*
|
||||
* @param {object} event The event object to check.
|
||||
* @returns {boolean} True if the event is a fullday event, false otherwise
|
||||
*/
|
||||
const isFullDayEvent = function (event) {
|
||||
if (event.start.length === 8 || event.start.dateOnly || event.datetype === "date") {
|
||||
return true;
|
||||
}
|
||||
|
||||
const start = event.start || 0;
|
||||
const startDate = new Date(start);
|
||||
const end = event.end || 0;
|
||||
if ((end - start) % (24 * 60 * 60 * 1000) === 0 && startDate.getHours() === 0 && startDate.getMinutes() === 0) {
|
||||
// Is 24 hours, and starts on the middle of the night.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines if the user defined time filter should apply
|
||||
*
|
||||
* @param {Date} now Date object using previously created object for consistency
|
||||
* @param {Moment} endDate Moment object representing the event end date
|
||||
* @param {string} filter The time to subtract from the end date to determine if an event should be shown
|
||||
* @returns {boolean} True if the event should be filtered out, false otherwise
|
||||
*/
|
||||
const timeFilterApplies = function (now, endDate, filter) {
|
||||
if (filter) {
|
||||
const until = filter.split(" "),
|
||||
value = parseInt(until[0]),
|
||||
increment = until[1].slice(-1) === "s" ? until[1] : until[1] + "s", // Massage the data for moment js
|
||||
filterUntil = moment(endDate.format()).subtract(value, increment);
|
||||
|
||||
return now < filterUntil.format("x");
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the title from the event.
|
||||
*
|
||||
* @param {object} event The event object to check.
|
||||
* @returns {string} The title of the event, or "Event" if no title is found.
|
||||
*/
|
||||
const getTitleFromEvent = function (event) {
|
||||
let title = "Event";
|
||||
if (event.summary) {
|
||||
title = typeof event.summary.val !== "undefined" ? event.summary.val : event.summary;
|
||||
} else if (event.description) {
|
||||
title = event.description;
|
||||
}
|
||||
|
||||
return title;
|
||||
};
|
||||
|
||||
const testTitleByFilter = function (title, filter, useRegex, regexFlags) {
|
||||
if (useRegex) {
|
||||
// Assume if leading slash, there is also trailing slash
|
||||
if (filter[0] === "/") {
|
||||
// Strip leading and trailing slashes
|
||||
filter = filter.substr(1).slice(0, -1);
|
||||
}
|
||||
|
||||
filter = new RegExp(filter, regexFlags);
|
||||
|
||||
return filter.test(title);
|
||||
} else {
|
||||
return title.includes(filter);
|
||||
}
|
||||
};
|
||||
|
||||
/* public methods */
|
||||
|
||||
/**
|
||||
* Initiate fetchCalendar();
|
||||
*/
|
||||
this.startFetch = function () {
|
||||
fetchCalendar();
|
||||
};
|
||||
|
||||
/**
|
||||
* Broadcast the existing events.
|
||||
*/
|
||||
this.broadcastEvents = function () {
|
||||
Log.info("Calendar-Fetcher: Broadcasting " + events.length + " events.");
|
||||
eventsReceivedCallback(self);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the on success callback
|
||||
*
|
||||
* @param {Function} callback The on success callback.
|
||||
*/
|
||||
this.onReceive = function (callback) {
|
||||
eventsReceivedCallback = callback;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the on error callback
|
||||
*
|
||||
* @param {Function} callback The on error callback.
|
||||
*/
|
||||
this.onError = function (callback) {
|
||||
fetchFailedCallback = callback;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the url of this fetcher.
|
||||
*
|
||||
* @returns {string} The url of this fetcher.
|
||||
*/
|
||||
this.url = function () {
|
||||
return url;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns current available events for this fetcher.
|
||||
*
|
||||
* @returns {object[]} The current available events for this fetcher.
|
||||
*/
|
||||
this.events = function () {
|
||||
return events;
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = CalendarFetcher;
|
||||
38
modules/default/calendar/debug.js
Normal file
38
modules/default/calendar/debug.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/* CalendarFetcher Tester
|
||||
* use this script with `node debug.js` to test the fetcher without the need
|
||||
* of starting the MagicMirror core. Adjust the values below to your desire.
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const CalendarFetcher = require("./calendarfetcher.js");
|
||||
|
||||
const url = "https://calendar.google.com/calendar/ical/pkm1t2uedjbp0uvq1o7oj1jouo%40group.calendar.google.com/private-08ba559f89eec70dd74bbd887d0a3598/basic.ics"; // Standard test URL
|
||||
//const url = "https://www.googleapis.com/calendar/v3/calendars/primary/events/"; // URL for Bearer auth (must be configured in Google OAuth2 first)
|
||||
const fetchInterval = 60 * 60 * 1000;
|
||||
const maximumEntries = 10;
|
||||
const maximumNumberOfDays = 365;
|
||||
const user = "magicmirror";
|
||||
const pass = "MyStrongPass";
|
||||
const auth = {
|
||||
user: user,
|
||||
pass: pass
|
||||
};
|
||||
|
||||
console.log("Create fetcher ...");
|
||||
|
||||
const fetcher = new CalendarFetcher(url, fetchInterval, [], maximumEntries, maximumNumberOfDays, auth);
|
||||
|
||||
fetcher.onReceive(function (fetcher) {
|
||||
console.log(fetcher.events());
|
||||
console.log("------------------------------------------------------------");
|
||||
});
|
||||
|
||||
fetcher.onError(function (fetcher, error) {
|
||||
console.log("Fetcher error:");
|
||||
console.log(error);
|
||||
});
|
||||
|
||||
fetcher.startFetch();
|
||||
|
||||
console.log("Create fetcher done! ");
|
||||
77
modules/default/calendar/node_helper.js
Normal file
77
modules/default/calendar/node_helper.js
Normal file
@@ -0,0 +1,77 @@
|
||||
/* Magic Mirror
|
||||
* Node Helper: Calendar
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const NodeHelper = require("node_helper");
|
||||
const validUrl = require("valid-url");
|
||||
const CalendarFetcher = require("./calendarfetcher.js");
|
||||
const Log = require("../../../js/logger");
|
||||
|
||||
module.exports = NodeHelper.create({
|
||||
// Override start method.
|
||||
start: function () {
|
||||
Log.log("Starting node helper for: " + this.name);
|
||||
this.fetchers = [];
|
||||
},
|
||||
|
||||
// Override socketNotificationReceived method.
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
if (notification === "ADD_CALENDAR") {
|
||||
this.createFetcher(payload.url, payload.fetchInterval, payload.excludedEvents, payload.maximumEntries, payload.maximumNumberOfDays, payload.auth, payload.broadcastPastEvents, payload.id);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a fetcher for a new url if it doesn't exist yet.
|
||||
* Otherwise it reuses the existing one.
|
||||
*
|
||||
* @param {string} url The url of the calendar
|
||||
* @param {number} fetchInterval How often does the calendar needs to be fetched in ms
|
||||
* @param {string[]} excludedEvents An array of words / phrases from event titles that will be excluded from being shown.
|
||||
* @param {number} maximumEntries The maximum number of events fetched.
|
||||
* @param {number} maximumNumberOfDays The maximum number of days an event should be in the future.
|
||||
* @param {object} auth The object containing options for authentication against the calendar.
|
||||
* @param {boolean} broadcastPastEvents If true events from the past maximumNumberOfDays will be included in event broadcasts
|
||||
* @param {string} identifier ID of the module
|
||||
*/
|
||||
createFetcher: function (url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents, identifier) {
|
||||
var self = this;
|
||||
|
||||
if (!validUrl.isUri(url)) {
|
||||
self.sendSocketNotification("INCORRECT_URL", { id: identifier, url: url });
|
||||
return;
|
||||
}
|
||||
|
||||
var fetcher;
|
||||
if (typeof self.fetchers[identifier + url] === "undefined") {
|
||||
Log.log("Create new calendar fetcher for url: " + url + " - Interval: " + fetchInterval);
|
||||
fetcher = new CalendarFetcher(url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents);
|
||||
|
||||
fetcher.onReceive(function (fetcher) {
|
||||
self.sendSocketNotification("CALENDAR_EVENTS", {
|
||||
id: identifier,
|
||||
url: fetcher.url(),
|
||||
events: fetcher.events()
|
||||
});
|
||||
});
|
||||
|
||||
fetcher.onError(function (fetcher, error) {
|
||||
Log.error("Calendar Error. Could not fetch calendar: ", fetcher.url(), error);
|
||||
self.sendSocketNotification("FETCH_ERROR", {
|
||||
id: identifier,
|
||||
url: fetcher.url(),
|
||||
error: error
|
||||
});
|
||||
});
|
||||
|
||||
self.fetchers[identifier + url] = fetcher;
|
||||
} else {
|
||||
Log.log("Use existing calendar fetcher for url: " + url);
|
||||
fetcher = self.fetchers[identifier + url];
|
||||
}
|
||||
|
||||
fetcher.startFetch();
|
||||
}
|
||||
});
|
||||
237
modules/default/calendar/windowsZones.json
Normal file
237
modules/default/calendar/windowsZones.json
Normal file
@@ -0,0 +1,237 @@
|
||||
{
|
||||
"Dateline Standard Time": { "iana": ["Etc/GMT+12"] },
|
||||
"UTC-11": { "iana": ["Etc/GMT+11"] },
|
||||
"Aleutian Standard Time": { "iana": ["America/Adak"] },
|
||||
"Hawaiian Standard Time": { "iana": ["Pacific/Honolulu"] },
|
||||
"Marquesas Standard Time": { "iana": ["Pacific/Marquesas"] },
|
||||
"Alaskan Standard Time": { "iana": ["America/Anchorage"] },
|
||||
"UTC-09": { "iana": ["Etc/GMT+9"] },
|
||||
"Pacific Standard Time (Mexico)": { "iana": ["America/Tijuana"] },
|
||||
"UTC-08": { "iana": ["Etc/GMT+8"] },
|
||||
"Pacific Standard Time": { "iana": ["America/Los_Angeles"] },
|
||||
"US Mountain Standard Time": { "iana": ["America/Phoenix"] },
|
||||
"Mountain Standard Time (Mexico)": { "iana": ["America/Chihuahua"] },
|
||||
"Mountain Standard Time": { "iana": ["America/Denver"] },
|
||||
"Central America Standard Time": { "iana": ["America/Guatemala"] },
|
||||
"Central Standard Time": { "iana": ["America/Chicago"] },
|
||||
"Easter Island Standard Time": { "iana": ["Pacific/Easter"] },
|
||||
"Central Standard Time (Mexico)": { "iana": ["America/Mexico_City"] },
|
||||
"Canada Central Standard Time": { "iana": ["America/Regina"] },
|
||||
"SA Pacific Standard Time": { "iana": ["America/Bogota"] },
|
||||
"Eastern Standard Time (Mexico)": { "iana": ["America/Cancun"] },
|
||||
"Eastern Standard Time": { "iana": ["America/New_York"] },
|
||||
"Haiti Standard Time": { "iana": ["America/Port-au-Prince"] },
|
||||
"Cuba Standard Time": { "iana": ["America/Havana"] },
|
||||
"US Eastern Standard Time": { "iana": ["America/Indianapolis"] },
|
||||
"Turks And Caicos Standard Time": { "iana": ["America/Grand_Turk"] },
|
||||
"Paraguay Standard Time": { "iana": ["America/Asuncion"] },
|
||||
"Atlantic Standard Time": { "iana": ["America/Halifax"] },
|
||||
"Venezuela Standard Time": { "iana": ["America/Caracas"] },
|
||||
"Central Brazilian Standard Time": { "iana": ["America/Cuiaba"] },
|
||||
"SA Western Standard Time": { "iana": ["America/La_Paz"] },
|
||||
"Pacific SA Standard Time": { "iana": ["America/Santiago"] },
|
||||
"Newfoundland Standard Time": { "iana": ["America/St_Johns"] },
|
||||
"Tocantins Standard Time": { "iana": ["America/Araguaina"] },
|
||||
"E. South America Standard Time": { "iana": ["America/Sao_Paulo"] },
|
||||
"SA Eastern Standard Time": { "iana": ["America/Cayenne"] },
|
||||
"Argentina Standard Time": { "iana": ["America/Buenos_Aires"] },
|
||||
"Greenland Standard Time": { "iana": ["America/Godthab"] },
|
||||
"Montevideo Standard Time": { "iana": ["America/Montevideo"] },
|
||||
"Magallanes Standard Time": { "iana": ["America/Punta_Arenas"] },
|
||||
"Saint Pierre Standard Time": { "iana": ["America/Miquelon"] },
|
||||
"Bahia Standard Time": { "iana": ["America/Bahia"] },
|
||||
"UTC-02": { "iana": ["Etc/GMT+2"] },
|
||||
"Azores Standard Time": { "iana": ["Atlantic/Azores"] },
|
||||
"Cape Verde Standard Time": { "iana": ["Atlantic/Cape_Verde"] },
|
||||
"UTC": { "iana": ["Etc/GMT"] },
|
||||
"GMT Standard Time": { "iana": ["Europe/London"] },
|
||||
"Greenwich Standard Time": { "iana": ["Atlantic/Reykjavik"] },
|
||||
"Sao Tome Standard Time": { "iana": ["Africa/Sao_Tome"] },
|
||||
"Morocco Standard Time": { "iana": ["Africa/Casablanca"] },
|
||||
"W. Europe Standard Time": { "iana": ["Europe/Berlin"] },
|
||||
"Central Europe Standard Time": { "iana": ["Europe/Budapest"] },
|
||||
"Romance Standard Time": { "iana": ["Europe/Paris"] },
|
||||
"Central European Standard Time": { "iana": ["Europe/Warsaw"] },
|
||||
"W. Central Africa Standard Time": { "iana": ["Africa/Lagos"] },
|
||||
"Jordan Standard Time": { "iana": ["Asia/Amman"] },
|
||||
"GTB Standard Time": { "iana": ["Europe/Bucharest"] },
|
||||
"Middle East Standard Time": { "iana": ["Asia/Beirut"] },
|
||||
"Egypt Standard Time": { "iana": ["Africa/Cairo"] },
|
||||
"E. Europe Standard Time": { "iana": ["Europe/Chisinau"] },
|
||||
"Syria Standard Time": { "iana": ["Asia/Damascus"] },
|
||||
"West Bank Standard Time": { "iana": ["Asia/Hebron"] },
|
||||
"South Africa Standard Time": { "iana": ["Africa/Johannesburg"] },
|
||||
"FLE Standard Time": { "iana": ["Europe/Kiev"] },
|
||||
"Israel Standard Time": { "iana": ["Asia/Jerusalem"] },
|
||||
"Kaliningrad Standard Time": { "iana": ["Europe/Kaliningrad"] },
|
||||
"Sudan Standard Time": { "iana": ["Africa/Khartoum"] },
|
||||
"Libya Standard Time": { "iana": ["Africa/Tripoli"] },
|
||||
"Namibia Standard Time": { "iana": ["Africa/Windhoek"] },
|
||||
"Arabic Standard Time": { "iana": ["Asia/Baghdad"] },
|
||||
"Turkey Standard Time": { "iana": ["Europe/Istanbul"] },
|
||||
"Arab Standard Time": { "iana": ["Asia/Riyadh"] },
|
||||
"Belarus Standard Time": { "iana": ["Europe/Minsk"] },
|
||||
"Russian Standard Time": { "iana": ["Europe/Moscow"] },
|
||||
"E. Africa Standard Time": { "iana": ["Africa/Nairobi"] },
|
||||
"Iran Standard Time": { "iana": ["Asia/Tehran"] },
|
||||
"Arabian Standard Time": { "iana": ["Asia/Dubai"] },
|
||||
"Astrakhan Standard Time": { "iana": ["Europe/Astrakhan"] },
|
||||
"Azerbaijan Standard Time": { "iana": ["Asia/Baku"] },
|
||||
"Russia Time Zone 3": { "iana": ["Europe/Samara"] },
|
||||
"Mauritius Standard Time": { "iana": ["Indian/Mauritius"] },
|
||||
"Saratov Standard Time": { "iana": ["Europe/Saratov"] },
|
||||
"Georgian Standard Time": { "iana": ["Asia/Tbilisi"] },
|
||||
"Volgograd Standard Time": { "iana": ["Europe/Volgograd"] },
|
||||
"Caucasus Standard Time": { "iana": ["Asia/Yerevan"] },
|
||||
"Afghanistan Standard Time": { "iana": ["Asia/Kabul"] },
|
||||
"West Asia Standard Time": { "iana": ["Asia/Tashkent"] },
|
||||
"Ekaterinburg Standard Time": { "iana": ["Asia/Yekaterinburg"] },
|
||||
"Pakistan Standard Time": { "iana": ["Asia/Karachi"] },
|
||||
"Qyzylorda Standard Time": { "iana": ["Asia/Qyzylorda"] },
|
||||
"India Standard Time": { "iana": ["Asia/Calcutta"] },
|
||||
"Sri Lanka Standard Time": { "iana": ["Asia/Colombo"] },
|
||||
"Nepal Standard Time": { "iana": ["Asia/Katmandu"] },
|
||||
"Central Asia Standard Time": { "iana": ["Asia/Almaty"] },
|
||||
"Bangladesh Standard Time": { "iana": ["Asia/Dhaka"] },
|
||||
"Omsk Standard Time": { "iana": ["Asia/Omsk"] },
|
||||
"Myanmar Standard Time": { "iana": ["Asia/Rangoon"] },
|
||||
"SE Asia Standard Time": { "iana": ["Asia/Bangkok"] },
|
||||
"Altai Standard Time": { "iana": ["Asia/Barnaul"] },
|
||||
"W. Mongolia Standard Time": { "iana": ["Asia/Hovd"] },
|
||||
"North Asia Standard Time": { "iana": ["Asia/Krasnoyarsk"] },
|
||||
"N. Central Asia Standard Time": { "iana": ["Asia/Novosibirsk"] },
|
||||
"Tomsk Standard Time": { "iana": ["Asia/Tomsk"] },
|
||||
"China Standard Time": { "iana": ["Asia/Shanghai"] },
|
||||
"North Asia East Standard Time": { "iana": ["Asia/Irkutsk"] },
|
||||
"Singapore Standard Time": { "iana": ["Asia/Singapore"] },
|
||||
"W. Australia Standard Time": { "iana": ["Australia/Perth"] },
|
||||
"Taipei Standard Time": { "iana": ["Asia/Taipei"] },
|
||||
"Ulaanbaatar Standard Time": { "iana": ["Asia/Ulaanbaatar"] },
|
||||
"Aus Central W. Standard Time": { "iana": ["Australia/Eucla"] },
|
||||
"Transbaikal Standard Time": { "iana": ["Asia/Chita"] },
|
||||
"Tokyo Standard Time": { "iana": ["Asia/Tokyo"] },
|
||||
"North Korea Standard Time": { "iana": ["Asia/Pyongyang"] },
|
||||
"Korea Standard Time": { "iana": ["Asia/Seoul"] },
|
||||
"Yakutsk Standard Time": { "iana": ["Asia/Yakutsk"] },
|
||||
"Cen. Australia Standard Time": { "iana": ["Australia/Adelaide"] },
|
||||
"AUS Central Standard Time": { "iana": ["Australia/Darwin"] },
|
||||
"E. Australia Standard Time": { "iana": ["Australia/Brisbane"] },
|
||||
"AUS Eastern Standard Time": { "iana": ["Australia/Sydney"] },
|
||||
"West Pacific Standard Time": { "iana": ["Pacific/Port_Moresby"] },
|
||||
"Tasmania Standard Time": { "iana": ["Australia/Hobart"] },
|
||||
"Vladivostok Standard Time": { "iana": ["Asia/Vladivostok"] },
|
||||
"Lord Howe Standard Time": { "iana": ["Australia/Lord_Howe"] },
|
||||
"Bougainville Standard Time": { "iana": ["Pacific/Bougainville"] },
|
||||
"Russia Time Zone 10": { "iana": ["Asia/Srednekolymsk"] },
|
||||
"Magadan Standard Time": { "iana": ["Asia/Magadan"] },
|
||||
"Norfolk Standard Time": { "iana": ["Pacific/Norfolk"] },
|
||||
"Sakhalin Standard Time": { "iana": ["Asia/Sakhalin"] },
|
||||
"Central Pacific Standard Time": { "iana": ["Pacific/Guadalcanal"] },
|
||||
"Russia Time Zone 11": { "iana": ["Asia/Kamchatka"] },
|
||||
"New Zealand Standard Time": { "iana": ["Pacific/Auckland"] },
|
||||
"UTC+12": { "iana": ["Etc/GMT-12"] },
|
||||
"Fiji Standard Time": { "iana": ["Pacific/Fiji"] },
|
||||
"Chatham Islands Standard Time": { "iana": ["Pacific/Chatham"] },
|
||||
"UTC+13": { "iana": ["Etc/GMT-13"] },
|
||||
"Tonga Standard Time": { "iana": ["Pacific/Tongatapu"] },
|
||||
"Samoa Standard Time": { "iana": ["Pacific/Apia"] },
|
||||
"Line Islands Standard Time": { "iana": ["Pacific/Kiritimati"] },
|
||||
"(UTC-12:00) International Date Line West": { "iana": ["Etc/GMT+12"] },
|
||||
"(UTC-11:00) Midway Island, Samoa": { "iana": ["Pacific/Apia"] },
|
||||
"(UTC-10:00) Hawaii": { "iana": ["Pacific/Honolulu"] },
|
||||
"(UTC-09:00) Alaska": { "iana": ["America/Anchorage"] },
|
||||
"(UTC-08:00) Pacific Time (US & Canada); Tijuana": { "iana": ["America/Los_Angeles"] },
|
||||
"(UTC-08:00) Pacific Time (US and Canada); Tijuana": { "iana": ["America/Los_Angeles"] },
|
||||
"(UTC-07:00) Mountain Time (US & Canada)": { "iana": ["America/Denver"] },
|
||||
"(UTC-07:00) Mountain Time (US and Canada)": { "iana": ["America/Denver"] },
|
||||
"(UTC-07:00) Chihuahua, La Paz, Mazatlan": { "iana": [null] },
|
||||
"(UTC-07:00) Arizona": { "iana": ["America/Phoenix"] },
|
||||
"(UTC-06:00) Central Time (US & Canada)": { "iana": ["America/Chicago"] },
|
||||
"(UTC-06:00) Central Time (US and Canada)": { "iana": ["America/Chicago"] },
|
||||
"(UTC-06:00) Saskatchewan": { "iana": ["America/Regina"] },
|
||||
"(UTC-06:00) Guadalajara, Mexico City, Monterrey": { "iana": [null] },
|
||||
"(UTC-06:00) Central America": { "iana": ["America/Guatemala"] },
|
||||
"(UTC-05:00) Eastern Time (US & Canada)": { "iana": ["America/New_York"] },
|
||||
"(UTC-05:00) Eastern Time (US and Canada)": { "iana": ["America/New_York"] },
|
||||
"(UTC-05:00) Indiana (East)": { "iana": ["America/Indianapolis"] },
|
||||
"(UTC-05:00) Bogota, Lima, Quito": { "iana": ["America/Bogota"] },
|
||||
"(UTC-04:00) Atlantic Time (Canada)": { "iana": ["America/Halifax"] },
|
||||
"(UTC-04:00) Georgetown, La Paz, San Juan": { "iana": ["America/La_Paz"] },
|
||||
"(UTC-04:00) Santiago": { "iana": ["America/Santiago"] },
|
||||
"(UTC-03:30) Newfoundland": { "iana": [null] },
|
||||
"(UTC-03:00) Brasilia": { "iana": ["America/Sao_Paulo"] },
|
||||
"(UTC-03:00) Georgetown": { "iana": ["America/Cayenne"] },
|
||||
"(UTC-03:00) Greenland": { "iana": ["America/Godthab"] },
|
||||
"(UTC-02:00) Mid-Atlantic": { "iana": [null] },
|
||||
"(UTC-01:00) Azores": { "iana": ["Atlantic/Azores"] },
|
||||
"(UTC-01:00) Cape Verde Islands": { "iana": ["Atlantic/Cape_Verde"] },
|
||||
"(UTC) Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London": { "iana": [null] },
|
||||
"(UTC) Monrovia, Reykjavik": { "iana": ["Atlantic/Reykjavik"] },
|
||||
"(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague": { "iana": ["Europe/Budapest"] },
|
||||
"(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb": { "iana": ["Europe/Warsaw"] },
|
||||
"(UTC+01:00) Brussels, Copenhagen, Madrid, Paris": { "iana": ["Europe/Paris"] },
|
||||
"(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna": { "iana": ["Europe/Berlin"] },
|
||||
"(UTC+01:00) West Central Africa": { "iana": ["Africa/Lagos"] },
|
||||
"(UTC+02:00) Minsk": { "iana": ["Europe/Chisinau"] },
|
||||
"(UTC+02:00) Cairo": { "iana": ["Africa/Cairo"] },
|
||||
"(UTC+02:00) Helsinki, Kiev, Riga, Sofia, Tallinn, Vilnius": { "iana": ["Europe/Kiev"] },
|
||||
"(UTC+02:00) Athens, Bucharest, Istanbul": { "iana": ["Europe/Bucharest"] },
|
||||
"(UTC+02:00) Jerusalem": { "iana": ["Asia/Jerusalem"] },
|
||||
"(UTC+02:00) Harare, Pretoria": { "iana": ["Africa/Johannesburg"] },
|
||||
"(UTC+03:00) Moscow, St. Petersburg, Volgograd": { "iana": ["Europe/Moscow"] },
|
||||
"(UTC+03:00) Kuwait, Riyadh": { "iana": ["Asia/Riyadh"] },
|
||||
"(UTC+03:00) Nairobi": { "iana": ["Africa/Nairobi"] },
|
||||
"(UTC+03:00) Baghdad": { "iana": ["Asia/Baghdad"] },
|
||||
"(UTC+03:30) Tehran": { "iana": ["Asia/Tehran"] },
|
||||
"(UTC+04:00) Abu Dhabi, Muscat": { "iana": ["Asia/Dubai"] },
|
||||
"(UTC+04:00) Baku, Tbilisi, Yerevan": { "iana": ["Asia/Yerevan"] },
|
||||
"(UTC+04:30) Kabul": { "iana": [null] },
|
||||
"(UTC+05:00) Ekaterinburg": { "iana": ["Asia/Yekaterinburg"] },
|
||||
"(UTC+05:00) Tashkent": { "iana": ["Asia/Tashkent"] },
|
||||
"(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi": { "iana": ["Asia/Calcutta"] },
|
||||
"(UTC+05:45) Kathmandu": { "iana": ["Asia/Katmandu"] },
|
||||
"(UTC+06:00) Astana, Dhaka": { "iana": ["Asia/Almaty"] },
|
||||
"(UTC+06:00) Sri Jayawardenepura": { "iana": ["Asia/Colombo"] },
|
||||
"(UTC+06:00) Almaty, Novosibirsk": { "iana": ["Asia/Novosibirsk"] },
|
||||
"(UTC+06:30) Yangon (Rangoon)": { "iana": ["Asia/Rangoon"] },
|
||||
"(UTC+07:00) Bangkok, Hanoi, Jakarta": { "iana": ["Asia/Bangkok"] },
|
||||
"(UTC+07:00) Krasnoyarsk": { "iana": ["Asia/Krasnoyarsk"] },
|
||||
"(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi": { "iana": ["Asia/Shanghai"] },
|
||||
"(UTC+08:00) Kuala Lumpur, Singapore": { "iana": ["Asia/Singapore"] },
|
||||
"(UTC+08:00) Taipei": { "iana": ["Asia/Taipei"] },
|
||||
"(UTC+08:00) Perth": { "iana": ["Australia/Perth"] },
|
||||
"(UTC+08:00) Irkutsk, Ulaanbaatar": { "iana": ["Asia/Irkutsk"] },
|
||||
"(UTC+09:00) Seoul": { "iana": ["Asia/Seoul"] },
|
||||
"(UTC+09:00) Osaka, Sapporo, Tokyo": { "iana": ["Asia/Tokyo"] },
|
||||
"(UTC+09:00) Yakutsk": { "iana": ["Asia/Yakutsk"] },
|
||||
"(UTC+09:30) Darwin": { "iana": ["Australia/Darwin"] },
|
||||
"(UTC+09:30) Adelaide": { "iana": ["Australia/Adelaide"] },
|
||||
"(UTC+10:00) Canberra, Melbourne, Sydney": { "iana": ["Australia/Sydney"] },
|
||||
"(GMT+10:00) Canberra, Melbourne, Sydney": { "iana": ["Australia/Sydney"] },
|
||||
"(UTC+10:00) Brisbane": { "iana": ["Australia/Brisbane"] },
|
||||
"(UTC+10:00) Hobart": { "iana": ["Australia/Hobart"] },
|
||||
"(UTC+10:00) Vladivostok": { "iana": ["Asia/Vladivostok"] },
|
||||
"(UTC+10:00) Guam, Port Moresby": { "iana": ["Pacific/Port_Moresby"] },
|
||||
"(UTC+11:00) Magadan, Solomon Islands, New Caledonia": { "iana": ["Pacific/Guadalcanal"] },
|
||||
"(UTC+12:00) Fiji, Kamchatka, Marshall Is.": { "iana": [null] },
|
||||
"(UTC+12:00) Auckland, Wellington": { "iana": ["Pacific/Auckland"] },
|
||||
"(UTC+13:00) Nuku'alofa": { "iana": ["Pacific/Tongatapu"] },
|
||||
"(UTC-03:00) Buenos Aires": { "iana": ["America/Buenos_Aires"] },
|
||||
"(UTC+02:00) Beirut": { "iana": ["Asia/Beirut"] },
|
||||
"(UTC+02:00) Amman": { "iana": ["Asia/Amman"] },
|
||||
"(UTC-06:00) Guadalajara, Mexico City, Monterrey - New": { "iana": ["America/Mexico_City"] },
|
||||
"(UTC-07:00) Chihuahua, La Paz, Mazatlan - New": { "iana": ["America/Chihuahua"] },
|
||||
"(UTC-08:00) Tijuana, Baja California": { "iana": ["America/Tijuana"] },
|
||||
"(UTC+02:00) Windhoek": { "iana": ["Africa/Windhoek"] },
|
||||
"(UTC+03:00) Tbilisi": { "iana": ["Asia/Tbilisi"] },
|
||||
"(UTC-04:00) Manaus": { "iana": ["America/Cuiaba"] },
|
||||
"(UTC-03:00) Montevideo": { "iana": ["America/Montevideo"] },
|
||||
"(UTC+04:00) Yerevan": { "iana": [null] },
|
||||
"(UTC-04:30) Caracas": { "iana": ["America/Caracas"] },
|
||||
"(UTC) Casablanca": { "iana": ["Africa/Casablanca"] },
|
||||
"(UTC+05:00) Islamabad, Karachi": { "iana": ["Asia/Karachi"] },
|
||||
"(UTC+04:00) Port Louis": { "iana": ["Indian/Mauritius"] },
|
||||
"(UTC) Coordinated Universal Time": { "iana": ["Etc/GMT"] },
|
||||
"(UTC-04:00) Asuncion": { "iana": ["America/Asuncion"] },
|
||||
"(UTC+12:00) Petropavlovsk-Kamchatsky": { "iana": [null] }
|
||||
}
|
||||
6
modules/default/clock/README.md
Normal file
6
modules/default/clock/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Module: Clock
|
||||
|
||||
The `clock` module is one of the default modules of the MagicMirror.
|
||||
This module displays the current date and time. The information will be updated realtime.
|
||||
|
||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/clock.html).
|
||||
360
modules/default/clock/clock.js
Normal file
360
modules/default/clock/clock.js
Normal file
@@ -0,0 +1,360 @@
|
||||
/* global SunCalc */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: Clock
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
Module.register("clock", {
|
||||
// Module config defaults.
|
||||
defaults: {
|
||||
displayType: "digital", // options: digital, analog, both
|
||||
|
||||
timeFormat: config.timeFormat,
|
||||
displaySeconds: true,
|
||||
showPeriod: true,
|
||||
showPeriodUpper: false,
|
||||
clockBold: false,
|
||||
showDate: true,
|
||||
showWeek: false,
|
||||
dateFormat: "dddd, LL",
|
||||
|
||||
/* specific to the analog clock */
|
||||
analogSize: "200px",
|
||||
analogFace: "simple", // options: 'none', 'simple', 'face-###' (where ### is 001 to 012 inclusive)
|
||||
analogPlacement: "bottom", // options: 'top', 'bottom', 'left', 'right'
|
||||
analogShowDate: "top", // options: false, 'top', or 'bottom'
|
||||
secondsColor: "#888888",
|
||||
timezone: null,
|
||||
|
||||
showSunTimes: false,
|
||||
showMoonTimes: false,
|
||||
lat: 47.630539,
|
||||
lon: -122.344147
|
||||
},
|
||||
// Define required scripts.
|
||||
getScripts: function () {
|
||||
return ["moment.js", "moment-timezone.js", "suncalc.js"];
|
||||
},
|
||||
// Define styles.
|
||||
getStyles: function () {
|
||||
return ["clock_styles.css"];
|
||||
},
|
||||
// Define start sequence.
|
||||
start: function () {
|
||||
Log.info("Starting module: " + this.name);
|
||||
|
||||
// Schedule update interval.
|
||||
var self = this;
|
||||
self.second = moment().second();
|
||||
self.minute = moment().minute();
|
||||
|
||||
//Calculate how many ms should pass until next update depending on if seconds is displayed or not
|
||||
var delayCalculator = function (reducedSeconds) {
|
||||
var EXTRA_DELAY = 50; //Deliberate imperceptable delay to prevent off-by-one timekeeping errors
|
||||
|
||||
if (self.config.displaySeconds) {
|
||||
return 1000 - moment().milliseconds() + EXTRA_DELAY;
|
||||
} else {
|
||||
return (60 - reducedSeconds) * 1000 - moment().milliseconds() + EXTRA_DELAY;
|
||||
}
|
||||
};
|
||||
|
||||
//A recursive timeout function instead of interval to avoid drifting
|
||||
var notificationTimer = function () {
|
||||
self.updateDom();
|
||||
|
||||
//If seconds is displayed CLOCK_SECOND-notification should be sent (but not when CLOCK_MINUTE-notification is sent)
|
||||
if (self.config.displaySeconds) {
|
||||
self.second = moment().second();
|
||||
if (self.second !== 0) {
|
||||
self.sendNotification("CLOCK_SECOND", self.second);
|
||||
setTimeout(notificationTimer, delayCalculator(0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//If minute changed or seconds isn't displayed send CLOCK_MINUTE-notification
|
||||
self.minute = moment().minute();
|
||||
self.sendNotification("CLOCK_MINUTE", self.minute);
|
||||
setTimeout(notificationTimer, delayCalculator(0));
|
||||
};
|
||||
|
||||
//Set the initial timeout with the amount of seconds elapsed as reducedSeconds so it will trigger when the minute changes
|
||||
setTimeout(notificationTimer, delayCalculator(self.second));
|
||||
|
||||
// Set locale.
|
||||
moment.locale(config.language);
|
||||
},
|
||||
// Override dom generator.
|
||||
getDom: function () {
|
||||
var wrapper = document.createElement("div");
|
||||
|
||||
/************************************
|
||||
* Create wrappers for DIGITAL clock
|
||||
*/
|
||||
|
||||
var dateWrapper = document.createElement("div");
|
||||
var timeWrapper = document.createElement("div");
|
||||
var secondsWrapper = document.createElement("sup");
|
||||
var periodWrapper = document.createElement("span");
|
||||
var sunWrapper = document.createElement("div");
|
||||
var moonWrapper = document.createElement("div");
|
||||
var weekWrapper = document.createElement("div");
|
||||
// Style Wrappers
|
||||
dateWrapper.className = "date normal medium";
|
||||
timeWrapper.className = "time bright large light";
|
||||
secondsWrapper.className = "dimmed";
|
||||
sunWrapper.className = "sun dimmed small";
|
||||
moonWrapper.className = "moon dimmed small";
|
||||
weekWrapper.className = "week dimmed medium";
|
||||
|
||||
// Set content of wrappers.
|
||||
// The moment().format("h") method has a bug on the Raspberry Pi.
|
||||
// So we need to generate the timestring manually.
|
||||
// See issue: https://github.com/MichMich/MagicMirror/issues/181
|
||||
var timeString;
|
||||
var now = moment();
|
||||
this.lastDisplayedMinute = now.minute();
|
||||
if (this.config.timezone) {
|
||||
now.tz(this.config.timezone);
|
||||
}
|
||||
|
||||
var hourSymbol = "HH";
|
||||
if (this.config.timeFormat !== 24) {
|
||||
hourSymbol = "h";
|
||||
}
|
||||
|
||||
if (this.config.clockBold === true) {
|
||||
timeString = now.format(hourSymbol + '[<span class="bold">]mm[</span>]');
|
||||
} else {
|
||||
timeString = now.format(hourSymbol + ":mm");
|
||||
}
|
||||
|
||||
if (this.config.showDate) {
|
||||
dateWrapper.innerHTML = now.format(this.config.dateFormat);
|
||||
}
|
||||
if (this.config.showWeek) {
|
||||
weekWrapper.innerHTML = this.translate("WEEK", { weekNumber: now.week() });
|
||||
}
|
||||
timeWrapper.innerHTML = timeString;
|
||||
secondsWrapper.innerHTML = now.format("ss");
|
||||
if (this.config.showPeriodUpper) {
|
||||
periodWrapper.innerHTML = now.format("A");
|
||||
} else {
|
||||
periodWrapper.innerHTML = now.format("a");
|
||||
}
|
||||
if (this.config.displaySeconds) {
|
||||
timeWrapper.appendChild(secondsWrapper);
|
||||
}
|
||||
if (this.config.showPeriod && this.config.timeFormat !== 24) {
|
||||
timeWrapper.appendChild(periodWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the time according to the config
|
||||
*
|
||||
* @param {object} config The config of the module
|
||||
* @param {object} time time to format
|
||||
* @returns {string} The formatted time string
|
||||
*/
|
||||
function formatTime(config, time) {
|
||||
var formatString = hourSymbol + ":mm";
|
||||
if (config.showPeriod && config.timeFormat !== 24) {
|
||||
formatString += config.showPeriodUpper ? "A" : "a";
|
||||
}
|
||||
return moment(time).format(formatString);
|
||||
}
|
||||
|
||||
if (this.config.showSunTimes) {
|
||||
const sunTimes = SunCalc.getTimes(now, this.config.lat, this.config.lon);
|
||||
const isVisible = now.isBetween(sunTimes.sunrise, sunTimes.sunset);
|
||||
var nextEvent;
|
||||
if (now.isBefore(sunTimes.sunrise)) {
|
||||
nextEvent = sunTimes.sunrise;
|
||||
} else if (now.isBefore(sunTimes.sunset)) {
|
||||
nextEvent = sunTimes.sunset;
|
||||
} else {
|
||||
const tomorrowSunTimes = SunCalc.getTimes(now.clone().add(1, "day"), this.config.lat, this.config.lon);
|
||||
nextEvent = tomorrowSunTimes.sunrise;
|
||||
}
|
||||
const untilNextEvent = moment.duration(moment(nextEvent).diff(now));
|
||||
const untilNextEventString = untilNextEvent.hours() + "h " + untilNextEvent.minutes() + "m";
|
||||
sunWrapper.innerHTML =
|
||||
'<span class="' +
|
||||
(isVisible ? "bright" : "") +
|
||||
'"><i class="fa fa-sun-o" aria-hidden="true"></i> ' +
|
||||
untilNextEventString +
|
||||
"</span>" +
|
||||
'<span><i class="fa fa-arrow-up" aria-hidden="true"></i> ' +
|
||||
formatTime(this.config, sunTimes.sunrise) +
|
||||
"</span>" +
|
||||
'<span><i class="fa fa-arrow-down" aria-hidden="true"></i> ' +
|
||||
formatTime(this.config, sunTimes.sunset) +
|
||||
"</span>";
|
||||
}
|
||||
if (this.config.showMoonTimes) {
|
||||
const moonIllumination = SunCalc.getMoonIllumination(now.toDate());
|
||||
const moonTimes = SunCalc.getMoonTimes(now, this.config.lat, this.config.lon);
|
||||
const moonRise = moonTimes.rise;
|
||||
var moonSet;
|
||||
if (moment(moonTimes.set).isAfter(moonTimes.rise)) {
|
||||
moonSet = moonTimes.set;
|
||||
} else {
|
||||
const nextMoonTimes = SunCalc.getMoonTimes(now.clone().add(1, "day"), this.config.lat, this.config.lon);
|
||||
moonSet = nextMoonTimes.set;
|
||||
}
|
||||
const isVisible = now.isBetween(moonRise, moonSet) || moonTimes.alwaysUp === true;
|
||||
const illuminatedFractionString = Math.round(moonIllumination.fraction * 100) + "%";
|
||||
moonWrapper.innerHTML =
|
||||
'<span class="' +
|
||||
(isVisible ? "bright" : "") +
|
||||
'"><i class="fa fa-moon-o" aria-hidden="true"></i> ' +
|
||||
illuminatedFractionString +
|
||||
"</span>" +
|
||||
'<span><i class="fa fa-arrow-up" aria-hidden="true"></i> ' +
|
||||
(moonRise ? formatTime(this.config, moonRise) : "...") +
|
||||
"</span>" +
|
||||
'<span><i class="fa fa-arrow-down" aria-hidden="true"></i> ' +
|
||||
(moonSet ? formatTime(this.config, moonSet) : "...") +
|
||||
"</span>";
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
* Create wrappers for ANALOG clock, only if specified in config
|
||||
*/
|
||||
|
||||
if (this.config.displayType !== "digital") {
|
||||
// If it isn't 'digital', then an 'analog' clock was also requested
|
||||
|
||||
// Calculate the degree offset for each hand of the clock
|
||||
if (this.config.timezone) {
|
||||
now.tz(this.config.timezone);
|
||||
}
|
||||
var second = now.seconds() * 6,
|
||||
minute = now.minute() * 6 + second / 60,
|
||||
hour = ((now.hours() % 12) / 12) * 360 + 90 + minute / 12;
|
||||
|
||||
// Create wrappers
|
||||
var clockCircle = document.createElement("div");
|
||||
clockCircle.className = "clockCircle";
|
||||
clockCircle.style.width = this.config.analogSize;
|
||||
clockCircle.style.height = this.config.analogSize;
|
||||
|
||||
if (this.config.analogFace !== "" && this.config.analogFace !== "simple" && this.config.analogFace !== "none") {
|
||||
clockCircle.style.background = "url(" + this.data.path + "faces/" + this.config.analogFace + ".svg)";
|
||||
clockCircle.style.backgroundSize = "100%";
|
||||
|
||||
// The following line solves issue: https://github.com/MichMich/MagicMirror/issues/611
|
||||
// clockCircle.style.border = "1px solid black";
|
||||
clockCircle.style.border = "rgba(0, 0, 0, 0.1)"; //Updated fix for Issue 611 where non-black backgrounds are used
|
||||
} else if (this.config.analogFace !== "none") {
|
||||
clockCircle.style.border = "2px solid white";
|
||||
}
|
||||
var clockFace = document.createElement("div");
|
||||
clockFace.className = "clockFace";
|
||||
|
||||
var clockHour = document.createElement("div");
|
||||
clockHour.id = "clockHour";
|
||||
clockHour.style.transform = "rotate(" + hour + "deg)";
|
||||
clockHour.className = "clockHour";
|
||||
var clockMinute = document.createElement("div");
|
||||
clockMinute.id = "clockMinute";
|
||||
clockMinute.style.transform = "rotate(" + minute + "deg)";
|
||||
clockMinute.className = "clockMinute";
|
||||
|
||||
// Combine analog wrappers
|
||||
clockFace.appendChild(clockHour);
|
||||
clockFace.appendChild(clockMinute);
|
||||
|
||||
if (this.config.displaySeconds) {
|
||||
var clockSecond = document.createElement("div");
|
||||
clockSecond.id = "clockSecond";
|
||||
clockSecond.style.transform = "rotate(" + second + "deg)";
|
||||
clockSecond.className = "clockSecond";
|
||||
clockSecond.style.backgroundColor = this.config.secondsColor;
|
||||
clockFace.appendChild(clockSecond);
|
||||
}
|
||||
clockCircle.appendChild(clockFace);
|
||||
}
|
||||
|
||||
/*******************************************
|
||||
* Combine wrappers, check for .displayType
|
||||
*/
|
||||
|
||||
if (this.config.displayType === "digital") {
|
||||
// Display only a digital clock
|
||||
wrapper.appendChild(dateWrapper);
|
||||
wrapper.appendChild(timeWrapper);
|
||||
wrapper.appendChild(sunWrapper);
|
||||
wrapper.appendChild(moonWrapper);
|
||||
wrapper.appendChild(weekWrapper);
|
||||
} else if (this.config.displayType === "analog") {
|
||||
// Display only an analog clock
|
||||
|
||||
if (this.config.showWeek) {
|
||||
weekWrapper.style.paddingBottom = "15px";
|
||||
} else {
|
||||
dateWrapper.style.paddingBottom = "15px";
|
||||
}
|
||||
|
||||
if (this.config.analogShowDate === "top") {
|
||||
wrapper.appendChild(dateWrapper);
|
||||
wrapper.appendChild(weekWrapper);
|
||||
wrapper.appendChild(clockCircle);
|
||||
} else if (this.config.analogShowDate === "bottom") {
|
||||
wrapper.appendChild(clockCircle);
|
||||
wrapper.appendChild(dateWrapper);
|
||||
wrapper.appendChild(weekWrapper);
|
||||
} else {
|
||||
wrapper.appendChild(clockCircle);
|
||||
}
|
||||
} else {
|
||||
// Both clocks have been configured, check position
|
||||
var placement = this.config.analogPlacement;
|
||||
|
||||
var analogWrapper = document.createElement("div");
|
||||
analogWrapper.id = "analog";
|
||||
analogWrapper.style.cssFloat = "none";
|
||||
analogWrapper.appendChild(clockCircle);
|
||||
|
||||
var digitalWrapper = document.createElement("div");
|
||||
digitalWrapper.id = "digital";
|
||||
digitalWrapper.style.cssFloat = "none";
|
||||
digitalWrapper.appendChild(dateWrapper);
|
||||
digitalWrapper.appendChild(timeWrapper);
|
||||
digitalWrapper.appendChild(sunWrapper);
|
||||
digitalWrapper.appendChild(moonWrapper);
|
||||
digitalWrapper.appendChild(weekWrapper);
|
||||
|
||||
var appendClocks = function (condition, pos1, pos2) {
|
||||
var padding = [0, 0, 0, 0];
|
||||
padding[placement === condition ? pos1 : pos2] = "20px";
|
||||
analogWrapper.style.padding = padding.join(" ");
|
||||
if (placement === condition) {
|
||||
wrapper.appendChild(analogWrapper);
|
||||
wrapper.appendChild(digitalWrapper);
|
||||
} else {
|
||||
wrapper.appendChild(digitalWrapper);
|
||||
wrapper.appendChild(analogWrapper);
|
||||
}
|
||||
};
|
||||
|
||||
if (placement === "left" || placement === "right") {
|
||||
digitalWrapper.style.display = "inline-block";
|
||||
digitalWrapper.style.verticalAlign = "top";
|
||||
analogWrapper.style.display = "inline-block";
|
||||
|
||||
appendClocks("left", 1, 3);
|
||||
} else {
|
||||
digitalWrapper.style.textAlign = "center";
|
||||
|
||||
appendClocks("top", 2, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the wrapper to the dom.
|
||||
return wrapper;
|
||||
}
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user