Compare commits

...

1597 Commits

Author SHA1 Message Date
Michael Teeuw
a6cbc9f0ef Merge pull request #2761 from MichMich/develop
release v2.18.0
2022-01-01 19:31:41 +01:00
Michael Teeuw
be073daaf2 Merge pull request #2760 from MichMich/prepare-release-2.18.0
Prepare-release-2.18.0
2022-01-01 19:24:05 +01:00
Michael Teeuw
4c919a7489 Prepare 2.18.0. 2022-01-01 19:17:35 +01:00
Michael Teeuw
7a7ed48063 Merge pull request #2754 from kolbyjack/broadcast-custom-symbols 2021-12-29 09:14:00 +01:00
Michael Teeuw
01010fe795 Merge pull request #2753 from khassel/calendar-test 2021-12-29 09:13:11 +01:00
Karsten Hassel
32382dc461 Merge branch 'develop' into calendar-test 2021-12-28 13:45:58 +01:00
Jon Kolb
bf83341fb9 Merge branch 'develop' into broadcast-custom-symbols 2021-12-28 07:33:22 -05:00
Michael Teeuw
c4d2a7b409 Merge pull request #2751 from KristjanESPERANTO/patch-1
Add End-to-End Testing + Format
2021-12-28 13:23:54 +01:00
Michael Teeuw
4ad832dcdd Merge pull request #2755 from sdetweil/newcal 2021-12-27 17:59:50 +01:00
sam detweiler
b35d25eda4 Merge branch 'develop' into newcal 2021-12-27 08:29:09 -06:00
Michael Teeuw
da51a5512f Merge pull request #2756 from sdetweil/fixhttps 2021-12-27 10:19:34 +01:00
Sam Detweiler
710d51bf32 Merge branch 'fixhttps' of https://github.com/sdetweil/MagicMirror into fixhttps 2021-12-26 17:54:32 +01:00
Sam Detweiler
446bb229bc fix changelog 2021-12-26 17:53:28 +01:00
sam detweiler
c605023bad Merge branch 'develop' into fixhttps 2021-12-26 10:46:12 -06:00
Sam Detweiler
28d866c001 enable useHTTP for full electron version 2021-12-26 17:43:27 +01:00
Sam Detweiler
95b587381b add changelog entry 2021-12-26 16:43:14 +01:00
Sam Detweiler
fc14431147 fix date correction comparison for fulldate, just less than, not less or equal 2021-12-26 16:40:17 +01:00
Karsten Hassel
5d8f2f5da1 iterate over timezones 2021-12-26 16:27:40 +01:00
Jon Kolb
cf428dc1f7 Add CHANGELOG entry 2021-12-26 09:44:04 -05:00
Jon Kolb
c579989ded Perform customEvent symbols replacement in symbolsForEvent 2021-12-26 09:25:28 -05:00
Karsten Hassel
a954a62762 added test for calendar recurring event with checks the correct date displayed (related to #2752) 2021-12-26 14:30:38 +01:00
Kristjan Esperanto
855860c00c Update CONTRIBUTING.md 2021-12-26 06:54:46 +01:00
Kristjan Esperanto
58c48b1b21 End-to-End Testing + Format 2021-12-25 21:58:30 +01:00
Michael Teeuw
512c392386 Merge pull request #2748 from sdetweil/calfix 2021-12-24 18:00:06 +01:00
Sam Detweiler
c5ed70dbfc past prettier 2021-12-25 02:26:11 +11:00
Sam Detweiler
f75ca0c399 after prettier 2021-12-25 02:22:42 +11:00
sam detweiler
75c2977daf Merge branch 'develop' into calfix 2021-12-24 09:17:25 -06:00
Sam Detweiler
b3ef4b40c5 fix calendar across timezone, showEnd/FullDay 2021-12-25 02:10:48 +11:00
Michael Teeuw
bd5664cf8e Merge pull request #2746 from khassel/node-version-update 2021-12-23 18:56:50 +01:00
Karsten Hassel
272219eb61 Merge branch 'develop' into node-version-update 2021-12-23 18:48:40 +01:00
Michael Teeuw
acbcb47739 Merge pull request #2739 from rico24/develop
Added missing translations (NL)
2021-12-23 10:27:53 +01:00
Michael Teeuw
672575e427 Merge pull request #2747 from khassel/electron-tests
fix electron tests
2021-12-23 10:27:42 +01:00
Michael Teeuw
be0b3b1d63 Merge pull request #2745 from KristjanESPERANTO/shebang
Add missing shebang
2021-12-23 10:27:30 +01:00
Karsten Hassel
c88d25b4ee update node versions in templates and github workflows 2021-12-22 22:35:55 +01:00
Karsten Hassel
9b83862a96 fixed electron tests with retry, update dependencies 2021-12-22 22:31:39 +01:00
Kristjan SCHMIDT
21c7f0da6e Added missing shebang 2021-12-22 21:00:13 +01:00
Michael Teeuw
dca5759569 Merge pull request #2744 from MichMich/patch-use-https-for-shields
Use https for shield links.
2021-12-22 09:13:27 +01:00
Michael Teeuw
0abf87bfa2 Use https for links and images. 2021-12-22 09:08:43 +01:00
rico24
6b8b37843b Added missing translations 2021-12-21 20:50:11 +01:00
Michael Teeuw
fa0b83f056 Merge pull request #2738 from MichMich/patch-replace-broken-shields
Replace broken shields.
2021-12-21 20:36:21 +01:00
Michael Teeuw
a706fa3590 Add changelog. 2021-12-21 20:31:13 +01:00
Michael Teeuw
852aa3d260 Remove flacky badge. 2021-12-21 20:30:09 +01:00
Michael Teeuw
c4edcaad87 Replace broken shields. 2021-12-21 20:12:05 +01:00
Michael Teeuw
9d0cab01d5 Merge pull request #2736 from rejas/issue_2712
Allow dangerouslyDisablingAutoEscaping in newsfeed items
2021-12-21 19:44:03 +01:00
Michael Teeuw
c3e507234d Merge pull request #2737 from rejas/RelativeFullDayEvents
Resubmit of "Relative full day events"
2021-12-21 19:42:47 +01:00
rejas
405ec82dd0 Run prettier 2021-12-21 15:22:52 +01:00
rejas
9c4c77fe84 Merge branch 'develop' into RelativeFullDayEvents 2021-12-21 15:19:36 +01:00
rejas
831afdf9e7 Update CHANGELOG 2021-12-21 13:35:38 +01:00
rejas
1996efb183 Add new variable dangerouslyDisableAutoEscaping 2021-12-21 13:31:39 +01:00
rejas
19bb2a0238 Add safe option to newsfeed templates 2021-12-21 08:13:00 +01:00
Michael Teeuw
af6cf70558 Merge pull request #2734 from rejas/issue_2731 2021-12-20 19:41:31 +01:00
rejas
9b57e6049e Update CHANGELOG 2021-12-20 13:37:07 +01:00
rejas
f54c06fb94 Add test for old items 2021-12-20 13:35:50 +01:00
rejas
2107e7c427 Show empty message when no newsfeed items are available 2021-12-20 13:29:56 +01:00
Michael Teeuw
eae277f165 Merge pull request #2730 from khassel/newsfeed-user-agent 2021-12-14 05:59:57 +01:00
Karsten Hassel
a9c4ad2895 update User-Agent-Header in calendarfetcher 2021-12-13 23:00:07 +01:00
Karsten Hassel
e88ba1ab1c update dependencies 2021-12-13 21:34:32 +01:00
Karsten Hassel
677dcb87ef fixed User-Agent-Header for newsfeed module (#2729) 2021-12-13 21:24:37 +01:00
Michael Teeuw
7bb217636e Merge pull request #2723 from MariusVaice/patch-1
Added lithuanian language to translations.js
2021-12-13 20:42:31 +01:00
MariusVaice
abc16e98eb Update CHANGELOG.md 2021-12-03 17:29:48 +02:00
MariusVaice
3b1405609e Added lithuanian language 2021-12-03 17:28:59 +02:00
Michael Teeuw
b0d3518c1d Merge pull request #2716 from khassel/revert-node-ical
Revert node ical update
2021-11-29 19:49:26 +01:00
Karsten Hassel
aed005618e revert node-ical update 2021-11-29 00:21:00 +01:00
Michael Teeuw
559970c95d Merge pull request #2715 from khassel/playwright 2021-11-28 21:25:30 +01:00
Karsten Hassel
f6f3aa11ea add CHANGELOG.md 2021-11-27 23:50:49 +01:00
Karsten Hassel
472c91f022 update dependencies 2021-11-27 23:10:04 +01:00
Karsten Hassel
4a7f498683 replace spectron with playwright, rewrite electron tests 2021-11-27 23:00:02 +01:00
Michael Teeuw
2b87d6ec01 Merge pull request #2699 from khassel/test-updates 2021-11-06 15:12:00 +01:00
AmpioRosso
1af282a7a1 Don't display 'in xxx' for all day events when using relative times 2021-11-06 15:03:08 +01:00
AmpioRosso
46b63f52fe Updated changelog.md, fix for issue #2424 2021-10-24 18:12:05 +02:00
AmpioRosso
01977005fb Issue #2424 - Don't display start time of full date events when using relative dates. Also use the fullDateEventDateFormat when showing full day events more than a week out 2021-10-24 18:02:19 +02:00
Karsten Hassel
b1a46b365b increase load delay for modules tests 2021-10-22 22:49:10 +02:00
Karsten Hassel
3a0a02d3ba Merge branch 'develop' into test-updates
# Conflicts:
#	CHANGELOG.md
2021-10-20 21:22:12 +02:00
Karsten Hassel
1acbef5bfa increase testTimeout, minor changes tests e2e 2021-10-20 21:05:19 +02:00
Michael Teeuw
66b2ba07bb Merge pull request #2697 from jupadin/fixTimeOffset 2021-10-20 15:20:22 +02:00
Julian Dinter
4777538103 Updated CHANGELOG. 2021-10-20 15:11:41 +02:00
Julian Dinter
31d31fc3d3 [Fix] Fixed time zone calculation error on starting date of events with different time zones. 2021-10-20 15:05:40 +02:00
Michael Teeuw
21f606c6ba Merge pull request #2694 from rejas/codecov2 2021-10-17 20:04:55 +02:00
veeck
6508ec2d17 Update CHANGELOG 2021-10-17 19:09:43 +02:00
veeck
c623ca9fe0 Update other actions and dependencies 2021-10-17 19:02:22 +02:00
veeck
90deaf564f Update codecov action before the old one gets deprecated 2021-10-17 18:56:18 +02:00
Michael Teeuw
e2d2cf67fa Merge pull request #2693 from rejas/patch-1 2021-10-17 18:51:20 +02:00
Veeck
8ee73a11bb Update CHANGELOG
add credits where credits due :-)
2021-10-17 18:39:06 +02:00
Michael Teeuw
b5b01be373 Merge pull request #2691 from rejas/issue_2638 2021-10-17 17:07:12 +02:00
veeck
730f2eb0b8 Add fix for chaotic newsfeed display after network connection loss 2021-10-17 16:01:01 +02:00
Michael Teeuw
4576754de2 Merge pull request #2687 from khassel/weather-e2e 2021-10-16 03:15:16 +02:00
Karsten Hassel
0fb9e0bc89 increase test delay for alert module test 2021-10-16 00:25:31 +02:00
Karsten Hassel
000c34ef73 update dependencies 2021-10-16 00:18:52 +02:00
Karsten Hassel
0ec80a7791 move weather-test to e2e 2021-10-16 00:05:12 +02:00
Michael Teeuw
e9650285bd Merge pull request #2686 from fewieden/clean-up-alert-module
Clean up alert module
2021-10-15 09:08:23 +02:00
Felix Wiedenbach
d0cc0a4034 move notificationReceived 2021-10-15 06:50:54 +02:00
Felix Wiedenbach
f9c4a3a9c0 updated CHANGELOG.md 2021-10-15 06:44:58 +02:00
Felix Wiedenbach
15fd2021bb blur modules instead of black overlay 2021-10-15 06:43:53 +02:00
Felix Wiedenbach
75cf1d610e migrate manual DOM creation for alerts to nunjuck template 2021-10-15 06:31:07 +02:00
Felix Wiedenbach
8f5ee9466a move notificationFx styles into styles directory 2021-10-15 06:26:23 +02:00
Felix Wiedenbach
467720f1c4 migrate manual DOM creation for notifications to nunjuck template 2021-10-15 06:23:50 +02:00
Felix Wiedenbach
026e624e23 use alert as default in notificationReceived 2021-10-15 06:17:03 +02:00
Felix Wiedenbach
460a9fc5f7 move notification effect override into start function 2021-10-15 06:14:45 +02:00
Felix Wiedenbach
3695e64ce9 migrate set position DOM manipulation to css 2021-10-15 06:11:24 +02:00
Felix Wiedenbach
b3bb829e4d fix missing translation files 2021-10-15 06:05:06 +02:00
Felix Wiedenbach
1b9f8c23d2 shorthand camelcase functions 2021-10-15 06:03:51 +02:00
Michael Teeuw
5ee5fd7332 Cleanup Changelog. 2021-10-07 09:09:16 +02:00
Michael Teeuw
18d94b9a26 Merge pull request #2682 from MMRIZE/ko
fix language Code kr->ko and add missed translation
2021-10-07 09:07:33 +02:00
eouia
3ae9686b2b modify CHANGELOG.md 2021-10-06 21:32:31 +02:00
eouia
bbe4ef8497 fix Language ko 2021-10-06 15:58:16 +02:00
eouia
a2fd354dc9 fix ko language 2021-10-06 15:56:33 +02:00
Seongnoh Sean Yi
a7202078ce Merge branch 'develop' into ko 2021-10-06 15:44:31 +02:00
eouia
d8940a9cea Merge branch 'ko' of https://github.com/MMRIZE/MagicMirror into ko 2021-10-06 15:42:13 +02:00
eouia
96611333bf fix translations.js 2021-10-06 15:42:01 +02:00
Michael Teeuw
5074123f57 Merge pull request #2679 from fewieden/clean-up-updatenotifications-and-nunjuck-templating
Clean up updatenotifications and nunjuck templating
2021-10-06 15:24:02 +02:00
Michael Teeuw
5bfbd3eaa0 Merge branch 'develop' into clean-up-updatenotifications-and-nunjuck-templating 2021-10-06 15:21:54 +02:00
Michael Teeuw
7fdeed14f5 Merge pull request #2680 from rejas/issue_2678 2021-10-05 10:31:18 +02:00
veeck
04a9ca92b5 Update dependencies 2021-10-05 10:24:42 +02:00
Michael Teeuw
016a1e9adb Merge pull request #2683 from khassel/calendar-test 2021-10-05 08:30:23 +02:00
Karsten Hassel
1d12e57606 move calendar tests from category electron to e2e 2021-10-04 23:04:28 +02:00
Seongnoh Sean Yi
cb753f5371 Merge branch 'develop' into ko 2021-10-04 11:16:33 +02:00
eouia
7a9f9b2705 edit CHANGELOG 2021-10-04 11:15:06 +02:00
eouia
6d4b4dd9fc fix language Code kr->ko and add missed translation 2021-10-04 11:05:33 +02:00
rejas
3d9c92fb63 Update CHANGELOG 2021-10-02 23:05:10 +02:00
rejas
d4168f6b5d Use feels_like data from openweathermap instead of calculating it 2021-10-02 22:15:21 +02:00
Felix Wiedenbach
1751cabb9d remove obsolete snapshot 2021-10-02 14:56:13 +02:00
Felix Wiedenbach
b0e3b6414a bump ecmascript to 2018 for eslint 2021-10-02 14:42:49 +02:00
Felix Wiedenbach
51967ed9f5 shorthand function 2021-10-02 14:18:58 +02:00
Felix Wiedenbach
fc06f13c30 Merge branch 'develop' into clean-up-updatenotifications-and-nunjuck-templating
# Conflicts:
#	CHANGELOG.md
2021-10-02 14:15:54 +02:00
Felix Wiedenbach
b094707324 remove mocking from implementation and use jest to mock git cli instead 2021-10-02 14:08:16 +02:00
Michael Teeuw
e4a0a517d5 Merge 2.17.1 2021-10-01 18:58:26 +02:00
Michael Teeuw
222a5f3779 Merge pull request #2674 from rejas/issue_2671_master
Update electron to fix certificate errors
2021-10-01 18:48:07 +02:00
veeck
72f7106086 Update version 2021-10-01 18:31:29 +02:00
veeck
33387b60cc Update CHANGELOG 2021-10-01 18:30:06 +02:00
veeck
83cc18f648 Update electron to fix certificate errors 2021-10-01 18:27:29 +02:00
Michael Teeuw
1735ca57d5 Fix Formatting 2021-10-01 16:03:29 +02:00
Michael Teeuw
a49459b253 Prepare 2.18.0 Dev Branch 2021-10-01 15:25:54 +02:00
Michael Teeuw
5a4fbbf48a Merge pull request #2672 from MichMich/develop
Release 2.17.0
2021-10-01 15:17:49 +02:00
Michael Teeuw
f7465679c0 Prepare 2.17.0 Release 2021-10-01 15:06:26 +02:00
Michael Teeuw
2a5299ebcb Merge pull request #2670 from khassel/new-e2e 2021-10-01 14:41:44 +02:00
Karsten Hassel
b3bddb2c99 fix without_modules.js 2021-09-29 22:01:35 +02:00
Karsten Hassel
997aec8cc2 remove default values from test configs 2021-09-29 21:12:03 +02:00
Karsten Hassel
c67320f185 fix logger.js 2021-09-28 22:08:21 +02:00
Karsten Hassel
8224a6ac35 add custom.css before testing 2021-09-28 21:38:30 +02:00
Karsten Hassel
abcee8aa56 fix global-setup.js 2021-09-28 21:23:59 +02:00
Karsten Hassel
23c6b44921 Merge branch 'develop' into new-e2e
# Conflicts:
#	CHANGELOG.md
2021-09-28 21:12:04 +02:00
Karsten Hassel
1034171e91 add CHANGELOG 2021-09-28 21:10:24 +02:00
Michael Teeuw
bf49f79e6e Merge pull request #2669 from rejas/cleanup 2021-09-28 13:34:08 +02:00
veeck
f7f24dbdfe Update CHANGELOGE 2021-09-28 12:15:07 +02:00
veeck
31ec848aec Update dependencies 2021-09-28 12:14:58 +02:00
veeck
9eb08420b6 Add proper names to github action steps 2021-09-28 12:14:39 +02:00
veeck
eb6d8d4f83 Remove unused file 2021-09-28 12:14:16 +02:00
Michael Teeuw
e10f620cf9 Merge pull request #2664 from khassel/test-setup 2021-09-28 11:51:19 +02:00
Karsten Hassel
f750436b64 update module clock_es 2021-09-26 22:09:41 +02:00
Karsten Hassel
a4a8504558 update module clock 2021-09-26 22:02:29 +02:00
Karsten Hassel
385e5aabaa prettier 2021-09-26 00:29:03 +02:00
Karsten Hassel
d831315e20 update module newsfeed 2021-09-26 00:18:22 +02:00
Karsten Hassel
e0906f3462 update module helloworld 2021-09-25 23:52:38 +02:00
Karsten Hassel
6595b6a44f refactor global-setup.js 2021-09-25 23:45:34 +02:00
Karsten Hassel
0183d7a080 update modules alert and compliments 2021-09-25 23:37:37 +02:00
Karsten Hassel
89e803ee42 update without_modules.js and env_spec.js 2021-09-25 22:12:53 +02:00
Karsten Hassel
c0ce52abe3 change getDocument, delay needed, now 2 tests moved 2021-09-25 00:01:41 +02:00
Karsten Hassel
a1c7f20990 fix logger.js, move jsdom in startApplication 2021-09-24 21:30:51 +02:00
Karsten Hassel
0ef6f89d44 fix server.close() issue 2021-09-24 00:30:00 +02:00
Karsten Hassel
54b04962a8 snapshot 2021-09-23 22:52:32 +02:00
Karsten Hassel
60f8de282d snapshot 2021-09-23 00:05:30 +02:00
karsten13
b4350278a0 first tests 2021-09-21 23:48:29 +02:00
Felix Wiedenbach
332e429a41 updated CHANGELOG.md 2021-09-18 04:02:07 +02:00
Felix Wiedenbach
7be25c21ed use nunjuck template, es6, removed unused config option 2021-09-18 03:57:45 +02:00
Felix Wiedenbach
1dfa5d383c nunjuck template for updatenotification 2021-09-18 03:55:32 +02:00
Felix Wiedenbach
c2d2c278e0 fixed updatenotification async/await issues and more es6 2021-09-18 03:53:40 +02:00
Felix Wiedenbach
75a57829c2 fixed git helper async/await issues, template strings, clean up 2021-09-18 03:50:53 +02:00
Karsten Hassel
c3c5307624 fix basic-auth.js 2021-09-17 20:03:57 +02:00
Karsten Hassel
879d585f2e update dependencies, add CHANGELOG 2021-09-16 23:16:38 +02:00
Karsten Hassel
9969fede35 refactor e2e 2021-09-16 23:02:17 +02:00
Karsten Hassel
c15b31b374 close server 2021-09-15 21:23:43 +02:00
Karsten Hassel
a3a6c33b32 move translations_spec to e2e 2021-09-13 23:57:18 +02:00
Karsten Hassel
236bf6e0fc silence logger for tests, use modulePaths for e2e 2021-09-13 23:55:41 +02:00
Karsten Hassel
974de179e0 refactor tests in 3 categories unit, e2e and electron 2021-09-13 23:07:34 +02:00
Michael Teeuw
60e03777f3 Merge pull request #2662 from rejas/prettier 2021-09-11 16:10:40 +02:00
Michael Teeuw
05f6b2510f Merge pull request #2661 from khassel/mock_git 2021-09-11 16:09:04 +02:00
rejas
f3274977f5 Ignore config dir 2021-09-11 11:55:51 +02:00
rejas
cf7fb1a3b9 Update CHANGELOG 2021-09-11 11:20:00 +02:00
rejas
12457a87d4 Test on latest version of node 16 again 2021-09-11 11:17:12 +02:00
rejas
9a8de7db80 Run prettier again 2021-09-11 11:16:09 +02:00
rejas
68e02a528e Dont ignore everything when running prettier 2021-09-11 11:15:33 +02:00
Karsten Hassel
277055f44e added tests for updatenotification 2021-09-10 21:12:27 +02:00
Michael Teeuw
c3fc745c7e Merge pull request #2655 from khassel/logger
change use of logger.js in jest tests
2021-09-10 09:01:10 +02:00
Karsten Hassel
8901ed219d update dependencies 2021-09-09 23:56:30 +02:00
Karsten Hassel
d7c70dc021 fix logger.js 2021-09-09 23:30:36 +02:00
Karsten Hassel
53c789bff9 again logger.js due to problem with e2e test 2021-09-09 21:54:20 +02:00
Karsten Hassel
eb63745664 update tests in updatenotification_spec.js due to problems after merging PR's 2021-09-09 21:29:28 +02:00
Karsten Hassel
91d72e48ad Merge branch 'develop' into logger 2021-09-09 21:18:28 +02:00
Karsten Hassel
1dcda63192 add logger.js to jests moduleNameMapper 2021-09-09 21:17:16 +02:00
Karsten Hassel
3ea6544f77 remove logger special 2021-09-09 21:12:55 +02:00
Karsten Hassel
d12a587f11 Merge branch 'updatenotification' into logger 2021-09-09 20:51:58 +02:00
Karsten Hassel
2b147bb98b do logger mocking in logger.js, remove sandbox stuff from unit tests 2021-09-09 20:50:35 +02:00
Michael Teeuw
6529eaaf9a Merge pull request #2650 from rejas/weather 2021-09-09 20:30:37 +02:00
Michael Teeuw
a68aa148b8 Merge pull request #2651 from khassel/updatenotification 2021-09-09 20:30:15 +02:00
Karsten Hassel
98942d6f9c rename to GitHelper 2021-09-09 19:51:07 +02:00
Karsten Hassel
690efc0aff fix test 2021-09-09 00:48:39 +02:00
Karsten Hassel
627cfa1dff hold node v16 at v16.8 due to errors in e2e tests with v16.9 2021-09-09 00:31:24 +02:00
Karsten Hassel
99aca932db update dependencies 2021-09-09 00:24:13 +02:00
Karsten Hassel
dd43f35bbe add unit tests 2021-09-09 00:03:28 +02:00
Karsten Hassel
093988e136 extract git stuff in own class 2021-09-06 23:55:32 +02:00
rejas
087a472765 Fix tomezone test error as reported by @khassel 2021-09-06 21:15:23 +02:00
Karsten Hassel
ce13d7f98b added comments, get hash only for mm 2021-09-05 23:39:23 +02:00
Karsten Hassel
b1fc766908 update dependencies 2021-09-04 23:17:54 +02:00
Karsten Hassel
22384342db fix update notification, remove simple-git 2021-09-04 22:55:03 +02:00
rejas
badce5146a Update CHANGELOG 2021-09-04 22:54:58 +02:00
rejas
0bf3ff9c17 Refer to new docs page for development documentation 2021-09-04 22:49:40 +02:00
Karsten Hassel
860840c367 dirty working snapshot 2021-09-04 19:08:18 +02:00
rejas
221b6325f6 Cleanup some docs in the weather modules 2021-09-04 13:50:21 +02:00
Michael Teeuw
06389e35f9 Merge pull request #2648 from rejas/weather_cleanup
Add common methods to weatherobject
2021-09-03 11:08:43 +02:00
veeck
a7756cec13 Inline some functions 2021-09-02 20:37:41 +02:00
veeck
9ee11654a6 Update jsdocs 2021-09-02 20:35:43 +02:00
veeck
a273266e5e Remove useless returns and now unused jsdoc variables 2021-09-02 20:35:25 +02:00
rejas
e2158716d6 Revert "Update dependencies"
This reverts commit f49312ed13.
2021-09-01 20:50:43 +02:00
rejas
c132206543 Use new method in ukmetofficedatahub provider 2021-09-01 20:02:45 +02:00
rejas
f49312ed13 Update dependencies 2021-09-01 11:02:06 +02:00
rejas
a9f69f07e6 Update CHANGELOG 2021-08-31 23:43:33 +02:00
rejas
d7429a4812 Add eslint rule for === vs == and fix its occurence 2021-08-31 23:39:40 +02:00
rejas
be76d5ce9a Use new method in smhi provider 2021-08-31 23:34:22 +02:00
rejas
f2bc10c5c0 Add tests for new methods 2021-08-31 23:32:05 +02:00
rejas
43eb760bce Use isDayTime method 2021-08-31 22:01:30 +02:00
rejas
a7684e3e9f Add common method for determining if it is daytime 2021-08-31 21:55:43 +02:00
rejas
8949aa3bec Add common method for updating suntimes 2021-08-31 21:41:27 +02:00
Michael Teeuw
e40ddd4b69 Merge pull request #2643 from MMRIZE/develop
Add custom switches for electron mainWindow
2021-08-31 13:12:52 +02:00
Seongnoh Sean Yi
17637fb1f6 Merge branch 'develop' into develop 2021-08-31 10:50:05 +02:00
Michael Teeuw
f71defe958 Merge pull request #2641 from rejas/issue_2462
Actually use showTime parameter in clock module
2021-08-31 09:22:32 +02:00
Michael Teeuw
b8d6a6da1f Merge pull request #2645 from khassel/black_cursor
electron: disable black cursor on start
2021-08-31 09:21:39 +02:00
Karsten Hassel
fbc886b21c run prettier ... 2021-08-30 19:55:26 +02:00
Karsten Hassel
8879fb55de disable black cursor on start 2021-08-30 19:47:26 +02:00
rejas
ed316e8bf3 Try fixing test 2021-08-30 16:38:59 +02:00
eouia
45529f7de9 Add custom switches for electron mainWindow 2021-08-30 11:38:40 +02:00
eouia
dbdff38d2e Add custom switches for electron mainWindow 2021-08-30 11:32:24 +02:00
rejas
21c3179e03 Update dependencies 2021-08-29 20:07:29 +02:00
rejas
c05d93aed8 Update CHANGELOG 2021-08-29 20:05:42 +02:00
rejas
6225abb010 Fix showTime parameter 2021-08-29 20:00:55 +02:00
rejas
c41fff8f5c Add test for showTime parameter 2021-08-29 20:00:42 +02:00
Michael Teeuw
8589d9c482 Merge pull request #2634 from jupadin/fixTimeOffset 2021-08-29 14:03:07 +02:00
Michael Teeuw
7f264953af Merge pull request #2635 from rejas/clock_layout_fix 2021-08-29 14:01:02 +02:00
Julian Dinter
cfff2ad72b Changed comment regarding "ical.js" and "node-ical". 2021-08-25 17:28:37 +02:00
rejas
c0258b352e Update dependencies 2021-08-22 22:45:08 +02:00
rejas
3e1b051ec3 Fix layout of digital clock
ALignment was always set to center. Instead it now takes the positions alignment (left/center/right)
2021-08-22 22:34:11 +02:00
Julian Dinter
b34bb87d7a Added fix to CHANGELOG. 2021-08-22 14:27:18 +02:00
Julian Dinter
83b8cc6729 Ran npm run lint:prettier.. 2021-08-22 14:23:10 +02:00
Julian Dinter
878c0be727 [Fix] start time of calendar event gets corrected by time zone offset. 2021-08-22 14:16:02 +02:00
Julian Dinter
e7f06f5c0c Removed duplicated and thus superfluous debug messages. 2021-08-22 14:06:24 +02:00
Julian Dinter
a1fc38c5fe Prettified and added debug messages. 2021-08-22 13:57:24 +02:00
Michael Teeuw
ff0ab24000 Merge pull request #2631 from apiontek/develop 2021-08-15 23:26:35 +02:00
Michael Teeuw
56a10d192d Merge pull request #2627 from rejas/clock 2021-08-15 23:26:07 +02:00
Adam Piontek
1a8413d8f0 update weathergov provider to try fetching not just current but also forecast when API URLs available 2021-08-10 18:54:34 -04:00
rejas
934b156ebb Update CHANGELOG 2021-08-07 13:33:11 +02:00
rejas
f9639d9705 Refactor clock to allow finer placement of analog clock 2021-08-07 13:30:55 +02:00
Michael Teeuw
4c345c4f33 Merge pull request #2625 from rejas/jsdoc 2021-08-05 19:18:30 +02:00
rejas
490151267a Print warnings about jsdoc during testing too 2021-08-05 16:41:10 +02:00
rejas
3d19a08cc7 Update CHANGELOG 2021-08-05 16:40:49 +02:00
rejas
385c4c32f9 Update dependencies 2021-08-05 16:39:14 +02:00
rejas
3a5052c871 Final jsdoc comments 2021-08-05 16:38:57 +02:00
rejas
f84f590f1d Start filling last gaps of jsdoc 2021-08-03 08:25:57 +02:00
Michael Teeuw
5b9eba7819 Merge pull request #2621 from rejas/issue_2620 2021-08-02 18:55:35 +02:00
rejas
cd18794fca Update jsdocs 2021-08-01 09:53:28 +02:00
rejas
ae3d552ad7 Update dependencies 2021-08-01 09:52:04 +02:00
rejas
be5f71f4a7 Update CHANGELOG 2021-08-01 09:40:44 +02:00
rejas
745a5f0376 Move ignoreToday logic into template to fix undefined forecast 2021-08-01 09:39:07 +02:00
Michael Teeuw
99114b2a61 Merge pull request #2614 from khassel/update_templates 2021-07-21 08:30:40 +02:00
Karsten Hassel
df9bd2b0f9 Merge branch 'develop' into update_templates 2021-07-20 22:33:02 +02:00
Michael Teeuw
e194b559ac Merge pull request #2613 from rejas/cleanup 2021-07-20 21:06:05 +02:00
Karsten Hassel
af5344dccd update github templates 2021-07-14 21:25:12 +02:00
rejas
2d7b8121d7 Update CHANGELOG 2021-07-14 16:03:19 +02:00
rejas
0297450702 Remove eslint rules that now pass 2021-07-14 15:21:03 +02:00
rejas
6b17f6aa28 Final var conversions 2021-07-14 15:06:23 +02:00
rejas
8a7abfe42d Update dependencies 2021-07-14 15:05:43 +02:00
veeck
dd5041395c Run stylelint over all css files 2021-07-14 10:41:29 +02:00
veeck
36d6a5bc15 Start cleaning up some jsdoc 2021-07-14 10:41:29 +02:00
rejas
2619f92d09 More var -> let/const conversions 2021-07-14 10:41:29 +02:00
rejas
53720ae8ae Fix some eslint issues in the tests 2021-07-14 10:41:29 +02:00
veeck
bcff953fbb Fix warning in weather provider 2021-07-14 10:41:29 +02:00
veeck
bcc0cc599d Fix == usages 2021-07-14 10:41:29 +02:00
veeck
a1e3fed312 Disable eslint checks in deprecated weather modules 2021-07-14 10:41:29 +02:00
veeck
399dca2ef9 Make eslint complain about var usage 2021-07-14 10:41:29 +02:00
veeck
2e44e1626d Remove unused variables 2021-07-14 10:41:29 +02:00
veeck
39aa2dfe01 Run linter over all files again 2021-07-14 10:41:29 +02:00
veeck
099929c677 Actually test all js and css files when lint script is run 2021-07-14 10:41:29 +02:00
rejas
af5d132410 Rename global version variable 2021-07-14 10:41:29 +02:00
Michael Teeuw
79acbc3a98 Merge pull request #2611 from khassel/test_config
refactor test configs
2021-07-14 10:40:27 +02:00
Karsten Hassel
e75e4e2284 use process.cwd() for correct path 2021-07-14 00:12:59 +02:00
Karsten Hassel
9aa0af4f9c factory again 2021-07-05 22:07:33 +02:00
Michael Teeuw
50e272efba Fix release date. 2021-07-05 19:54:31 +02:00
karsten13
209e049893 run prettier 2021-07-05 19:51:18 +02:00
Karsten Hassel
bbb3accf0c use factory 2021-07-05 19:45:58 +02:00
Karsten Hassel
179989aa42 add missing comments 2021-07-05 19:21:39 +02:00
Karsten Hassel
2881d19d43 remove env.js 2021-07-05 18:27:14 +02:00
Karsten Hassel
7cfc3458ec fix tests 2021-07-05 18:08:56 +02:00
karsten13
659e1da79d run prettier 2021-07-05 00:40:01 +02:00
Karsten Hassel
a7ae79493d refactor test config 2021-07-05 00:35:28 +02:00
Michael Teeuw
8b484ee707 Merge pull request #2609 from khassel/electron13 2021-07-04 21:39:31 +02:00
Karsten Hassel
d617d4aa09 update package-lock.json 2021-07-04 20:54:54 +02:00
Karsten Hassel
99c04648b4 update CHANGELOG.md 2021-07-04 19:35:36 +02:00
karsten13
f945d50c0d run prettier 2021-07-04 19:21:17 +02:00
Karsten Hassel
e9fabd59ed contextIsolation: false 2021-07-04 18:30:43 +02:00
Karsten Hassel
ad13de3588 update electron to v13 2021-07-04 17:46:22 +02:00
Michael Teeuw
aad8141e27 Prepare v.2.17.0-develop 2021-07-01 14:34:02 +02:00
Michael Teeuw
26a76f80d6 Merge pull request #2607 from MichMich/develop
Release v2.16.0
2021-07-01 14:30:00 +02:00
Michael Teeuw
53ead2087f Prepare v2.16.0 2021-07-01 14:08:10 +02:00
Michael Teeuw
faee811d67 Merge pull request #2605 from rico24/patch-1
Update nl.json
2021-06-29 09:56:35 +02:00
Michael Teeuw
b9c739df1f Fix prettier issues. 2021-06-29 09:49:13 +02:00
rico24
6e124842e8 Merge branch 'develop' into patch-1 2021-06-28 22:38:10 +02:00
rico24
7a5928ea24 Update CHANGELOG.md 2021-06-28 21:03:05 +02:00
Michael Teeuw
7fdf7de11c Merge pull request #2600 from njwilliams/njw/openweathermap-onecall-fix 2021-06-25 11:32:49 +02:00
Michael Teeuw
b75eedb84e Merge pull request #2604 from khassel/update_deps 2021-06-25 11:01:08 +02:00
rico24
3b92ae49a9 Update nl.json
Added the missing variables based on the en.json file.
2021-06-25 10:58:05 +02:00
Nick Williams
775d1091db typo when not using /onecall - was getting forecast from wrong attr 2021-06-24 21:39:32 -04:00
Karsten Hassel
1f77b491fc update dependencies 2021-06-24 23:01:29 +02:00
Michael Teeuw
eff2fd7cc0 Merge pull request #2603 from khassel/dev_console_test 2021-06-24 08:38:49 +02:00
Karsten Hassel
58d2a0d874 Merge branch 'develop' into dev_console_test
# Conflicts:
#	CHANGELOG.md
2021-06-23 23:14:19 +02:00
Michael Teeuw
ea9def997a Merge pull request #2602 from khassel/jest 2021-06-22 08:31:05 +02:00
Karsten Hassel
a222c58047 use --forceExit running jest, define timeouts for github workflows 2021-06-21 22:27:36 +02:00
Michael Teeuw
39a838c2ab Merge pull request #2601 from khassel/jest 2021-06-20 14:45:41 +02:00
Karsten Hassel
cfc0bcd5ad workaround for dev_console test using getWindowCount 2021-06-18 21:39:55 +02:00
Karsten Hassel
3418c9b50f revert changes to dev_console test (has impact on other tests), add CHANGELOG 2021-06-18 12:42:13 +02:00
Nick Williams
e686611890 update One Call API results for openweathermap
The results from the /onecall endpoint were not
being parsed correctly in current and forecast mode - it was
assuming the current/forecast endpoint API, and the return
datasets are different. The effect was that the module
would simply display "Loading..." when in /onecall mode, since
it has no way of displaying error status (ideally, it should,
but leave that for another day)
2021-06-17 19:30:56 -04:00
karsten13
ebb5dee1fc run prettier 2021-06-17 23:19:57 +02:00
Karsten Hassel
d9edaffd9c reset changes on js/logger.js, mock logger.js in global_vars tests, workaround for failing dev_console test 2021-06-17 22:50:26 +02:00
Michael Teeuw
cbe7b1a5b9 Merge pull request #2599 from khassel/jest 2021-06-17 07:06:36 +02:00
Karsten Hassel
e758fd4093 add CHANGELOG 2021-06-16 20:54:03 +02:00
Karsten Hassel
14a99a3b25 Merge branch 'develop' into jest 2021-06-16 20:53:02 +02:00
Karsten Hassel
1ba67506a0 fix logger.js after jest changes 2021-06-16 20:51:32 +02:00
Michael Teeuw
2a6ca5d5ac Merge pull request #2581 from Crazylegstoo/envCanadaFix
Fix to Environment Canada weather provider
2021-06-16 10:07:48 +02:00
Michael Teeuw
aa12e6495a Merge pull request #2593 from khassel/jest
replace mocha with jest
2021-06-16 10:06:32 +02:00
Karsten Hassel
9269848f66 Merge branch 'develop' into jest
# Conflicts:
#	CHANGELOG.md
2021-06-15 23:32:22 +02:00
Karsten Hassel
a71e61cd30 use short if in weatherforecast.js, add comment to logger.js 2021-06-15 23:29:07 +02:00
Michael Teeuw
8be4604c97 Merge pull request #2580 from r3wald/feature/newsfeed-show-as-list
feature: show newsfeed as list
2021-06-15 11:14:15 +02:00
Michael Teeuw
a7bba903f5 Merge branch 'develop' into envCanadaFix 2021-06-15 11:13:28 +02:00
Karsten Hassel
1d8af5835d Merge branch 'develop' into jest
# Conflicts:
#	package-lock.json
#	package.json
2021-06-14 23:21:08 +02:00
Karsten Hassel
189c01fc74 update moduleNameMapper 2021-06-14 22:45:21 +02:00
Karsten Hassel
e3a5bbf661 use expect where missing 2021-06-14 21:07:38 +02:00
Karsten Hassel
ee23c5f72c update clock tests, remove testSequencer 2021-06-14 19:21:21 +02:00
Karsten Hassel
a2083be76b test order 2021-06-14 00:45:40 +02:00
Michael Teeuw
a1c4be83d6 Merge pull request #2578 from rejas/docs 2021-06-13 19:27:24 +02:00
Karsten Hassel
d2fde2bfc8 fix segment faults in node16 on ubuntu 2021-06-12 23:53:04 +02:00
Karsten Hassel
e8956b0b55 update package-lock.json 2021-06-12 22:52:05 +02:00
Karsten Hassel
2af4009a93 change missing translations log, disable console.log in unit tests 2021-06-12 22:26:02 +02:00
Karsten Hassel
a5f7c946cc jest setup changes, increase setMaxListeners, disable console.log in tests/unit/global_vars 2021-06-12 21:26:08 +02:00
Karsten Hassel
298542b531 enable missing translation messages 2021-06-11 23:42:24 +02:00
Karsten Hassel
fca6707a29 add eslint-plugin-jest 2021-06-11 23:11:30 +02:00
Karsten Hassel
10d3a284e9 fix js lint warnings 2021-06-11 23:10:41 +02:00
karsten13
1a244726aa run prettier 2021-06-11 22:24:21 +02:00
Karsten Hassel
044935a164 add jest options 2021-06-11 22:18:05 +02:00
Karsten Hassel
99e5edf2c5 add CHANGELOG.md 2021-06-11 01:00:05 +02:00
Karsten Hassel
b26270bd13 fix helloworld_spec tests 2021-06-11 00:36:53 +02:00
Karsten Hassel
65a8cb9ddb remove unused references 2021-06-11 00:32:54 +02:00
Karsten Hassel
ba4b976e80 test config 2021-06-10 00:24:08 +02:00
veeck
297ae1dbaf Try updating dependencies 2021-06-09 13:09:28 +02:00
veeck
214614f740 Cleanup jsdoc via eslint 2021-06-09 12:49:57 +02:00
Karsten Hassel
0e14d3d6e8 snapshot e2e 2021-06-09 00:19:43 +02:00
Karsten Hassel
67011c0c32 snapshot e2e 2021-06-08 00:47:40 +02:00
Karsten Hassel
16bbb42b8d remove chai from unit tests 2021-06-08 00:47:15 +02:00
Kevin G
b85ac91e6c Corrected bullet for Updated list
Wording/location corrected for update to WEATHER module
2021-06-07 09:28:33 -04:00
Karsten Hassel
66759a33fa unit tests 2021-06-07 00:16:53 +02:00
Kevin G
bf5e83861c Highlight new custom precipitation unit-of-measure for WEATHER
With the new Environment Canada provider in WEATHER, it is now possible for provider to set a customized precipitation unit-of-measure
2021-06-05 11:39:21 -04:00
Robert Ewald
d56a6fb06f disabled by default. 2021-06-03 12:45:08 +02:00
Robert Ewald
5e7aa8e16d changelog updated. 2021-06-03 12:42:39 +02:00
Robert Ewald
bace0ad339 indentation in template fixed. 2021-06-03 12:38:54 +02:00
Robert Ewald
6014eaf8eb Merge remote-tracking branch 'upstream/develop' into feature/newsfeed-show-as-list 2021-06-03 12:27:26 +02:00
Robert Ewald
3e96e8b3f5 newsfeed: showAsList implemented. 2021-06-03 11:40:18 +02:00
Kevin G
95d1b8a6d0 Corrected formating 2021-06-02 16:25:53 -04:00
Kevin G
0ecb66c99e Fixed precipitation unit conversion logic
Found an error that precipitation amount was not being calculated correctly when config.js is asking for Imperial units. This has been fixed.
2021-06-02 16:15:44 -04:00
Kevin G
af52b91799 Fix to precipitation logic
Found a really dumb error I made that broke compatibility with OpenWeatherMap hourly forecast under certain conditions. This is now fixed.
2021-06-02 11:43:56 -04:00
veeck
3c50d6c30a Update CHANGELOG 2021-05-29 16:23:53 +02:00
veeck
2722c72c43 Update issue template 2021-05-29 16:23:41 +02:00
rejas
d5ab3101c6 Update links to config documentation 2021-05-29 16:23:41 +02:00
Michael Teeuw
32df76bdff Merge pull request #2577 from rejas/prettier 2021-05-29 16:22:52 +02:00
rejas
68a06c3d1d Update CHANGELOG 2021-05-29 16:13:24 +02:00
rejas
1b42dc779b Run prettier again 2021-05-29 16:11:39 +02:00
rejas
cedffd40f2 Lint all files, exclude others 2021-05-29 16:11:25 +02:00
Michael Teeuw
cdc8db4837 Merge pull request #2576 from rejas/calendar_fix 2021-05-29 14:02:13 +02:00
Michael Teeuw
a0ee23d84e Merge pull request #2568 from Crazylegstoo/envcanada 2021-05-29 14:01:04 +02:00
Michael Teeuw
49d2d8c9d0 Merge pull request #2574 from daniel-windsor/forecase-ignore-today 2021-05-29 13:59:55 +02:00
rejas
63620aa811 Update CHANGELOG 2021-05-29 13:33:41 +02:00
rejas
d5b11a1dba Update dependencies 2021-05-29 13:32:24 +02:00
rejas
4a63af0490 Set error to null once the events are coming again 2021-05-29 13:22:31 +02:00
Michael Teeuw
a68019293f Merge pull request #2567 from khassel/fix_husky 2021-05-27 07:14:56 +02:00
Karsten Hassel
6b1c91f0dd Merge branch 'develop' into fix_husky
# Conflicts:
#	CHANGELOG.md
2021-05-26 20:20:55 +02:00
Michael Teeuw
ea93785581 Merge pull request #2552 from rejas/error_handling 2021-05-26 20:12:17 +02:00
Karsten Hassel
32819c4fd5 Merge pull request #1 from rejas/fix_pretty_quick_too
Fix call to pretty quick, make husky script executable
2021-05-26 19:49:36 +02:00
Daniel Windsor
fc5a438cdc Add flag that removes today entry in forecast 2021-05-26 21:45:24 +12:00
veeck
57fe94f945 Fix call to pretty quick, make husky script executable 2021-05-26 11:39:29 +02:00
Robert Ewald
aa3a3bdf16 server data for newsfeed list. 2021-05-25 09:29:48 +02:00
Kevin G
db89da3daa Ran prettier 2021-05-21 12:11:58 -04:00
Kevin G
d6ba5796ce ran prettier... 2021-05-21 11:48:56 -04:00
Kevin G
3968743b28 ran prettier... 2021-05-21 11:45:54 -04:00
Kevin G
20c6226b84 Update for new weather provider 2021-05-21 10:20:16 -04:00
Kevin G
463ce394fe ran prettier... 2021-05-21 10:13:39 -04:00
Kevin G
1faefebe42 Formatting correction for min/max display 2021-05-21 10:06:54 -04:00
Karsten Hassel
e2c9339ec4 fix husky setup in prod environment 2021-05-19 20:58:29 +02:00
rejas
4b1c7da171 Update jsdoc 2021-05-19 11:12:56 +02:00
rejas
bdfd6e5e9f Fix calendar test 2021-05-19 11:12:56 +02:00
rejas
4c8508b0a9 Fix newsfeed test 2021-05-19 11:12:56 +02:00
veeck
06b3f92963 Refaktor calendar error once again for better messaging to the user 2021-05-19 11:12:56 +02:00
veeck
d43a57af36 Refaktor newsfeed error once again for better messaging to the user 2021-05-19 11:12:56 +02:00
rejas
aeefe28710 Update jsdoc 2021-05-19 11:12:56 +02:00
rejas
e9de961a23 Cleanup prohibited words filter code and test data 2021-05-19 11:12:56 +02:00
rejas
dcec778e02 Update CHANGELOG 2021-05-19 11:12:56 +02:00
rejas
b212641069 Move checkFetchStatus into NodelHelper 2021-05-19 11:12:42 +02:00
rejas
90aa50bb11 Add fetcher_helper for calendar and newsfeed 2021-05-19 11:12:42 +02:00
rejas
a6879e853b Fix tests 2021-05-19 11:12:42 +02:00
rejas
37fab7ac63 Update error handling for newsfeed and calendar 2021-05-19 11:12:42 +02:00
Michael Teeuw
8b01ae08c5 Merge pull request #2566 from khassel/update_deps 2021-05-19 11:07:43 +02:00
Michael Teeuw
b1cdf42790 Merge pull request #2565 from rejas/css_gap 2021-05-19 11:07:10 +02:00
Karsten Hassel
974968d238 update CHANGELOG.md 2021-05-18 20:44:43 +02:00
karsten13
bf467cbba5 add package-lock.json 2021-05-18 20:42:06 +02:00
Karsten Hassel
536aa2e96e update dependencies and migrate husky to v6 2021-05-18 20:36:56 +02:00
rejas
3d84344b75 Revert "Update dependencies, run linter"
This reverts commit 1054ba3b1e.
2021-05-14 22:06:55 +02:00
rejas
1054ba3b1e Update dependencies, run linter 2021-05-14 21:46:18 +02:00
rejas
aa8ddb9a92 Update CHANGELOG 2021-05-14 21:20:33 +02:00
rejas
fcfe57e5e2 Add more properties for finer body margins 2021-05-14 17:42:23 +02:00
Kevin G
c4fd4e0317 New provider - Environment Canada
Added a new provider that pulls weather data - current, forecast, and hourly - from Environment Canada (Canadian gov weather service). This provider supports Canadian locations only. Documentation will be provided that outlines specific behaviours of this provider and required 'weather' modules parms for Env Canada.
2021-05-13 11:12:30 -04:00
Kevin G
3c76933824 Edit Update
Formatting
2021-05-13 11:00:43 -04:00
Kevin G
fa83819bee Edit update
Formatting
2021-05-13 10:59:45 -04:00
Kevin G
b65ae88879 Edit update
Formatting
2021-05-13 10:58:08 -04:00
Kevin G
96db21f9bf Updates to support Environment Canada provider
Code updates to support a new weatherobject element called precipitationUnits. For the 'forecast' and 'hourly' UI, the weather module will use this new element if it is not null (i.e. a provider has pushed some value into this object element). If the element is null, then default 'units' processing will still occur. This allows a provider to have a customn unit of measure for precipitation - e.g. Env Canada will use mm for rain and cm for snow.
2021-05-13 10:56:30 -04:00
veeck
6d356ff770 Introduce font-size-small property and use it in calendar too (clean it up too) 2021-05-08 18:29:53 +02:00
veeck
21790b32bf Use hex notation instead of verbose name like everywhere else 2021-05-08 18:07:38 +02:00
rejas
159f3d0aa2 Replace deprecated symbol with modern solution 2021-05-08 18:05:30 +02:00
veeck
012a7b0678 Convert one px to rem 2021-05-08 18:04:14 +02:00
veeck
6595c85671 Remove useless(?) margins 2021-05-08 18:04:14 +02:00
veeck
cf5c0464fe Use custom property for gaps between modules 2021-05-08 18:04:14 +02:00
Michael Teeuw
e31450f731 Merge pull request #2560 from foundations-design/contribute 2021-05-08 08:50:00 +02:00
Michael Teeuw
3653984a95 Merge pull request #2562 from rejas/config_check 2021-05-08 08:49:10 +02:00
Michael Teeuw
0c0b856c37 Merge pull request #2561 from khassel/develop 2021-05-08 08:48:43 +02:00
Michael Teeuw
69f1b153ea Merge pull request #2559 from rejas/issue_2547 2021-05-08 08:46:44 +02:00
veeck
43ba4bd00e Fix calendar debug 2021-05-07 12:28:55 +02:00
veeck
bf5edcaac6 Fix failing config check when es6 notation is used 2021-05-07 12:23:29 +02:00
Karsten Hassel
ac51709211 update dependencies in package.json, require node >= v12, remove rrule and rrule-alt 2021-05-04 21:49:16 +02:00
Earl Man
e1a578e819 add myself to CHANGELOG.md 2021-05-04 09:25:36 -05:00
rejas
591c9e53b0 Update CHANGELOG 2021-05-02 15:07:49 +02:00
rejas
87d543eb3a Finish test case 2021-05-02 15:07:02 +02:00
rejas
40c1521591 Add test data 2021-05-02 14:55:45 +02:00
Michael Teeuw
b31c2a6264 Add contributors list.
The full list will be checked during release.
2021-04-29 10:32:20 +02:00
Michael Teeuw
66a42f13f1 Merge pull request #2546 from rejas/move 2021-04-27 16:27:55 +02:00
veeck
8de6ebbbd1 Update dependencies 2021-04-27 13:14:27 +02:00
rejas
649de694ed Update CHANGELOG 2021-04-27 13:14:27 +02:00
veeck
8a52fde8fc Move codecov.yml into github dir to other configs 2021-04-27 13:14:07 +02:00
rejas
fb8bd657de Move weatherforecast mock data in better suited directory 2021-04-27 13:14:07 +02:00
Michael Teeuw
b04a0a6b61 Merge pull request #2556 from khassel/updatenotification 2021-04-27 07:21:06 +02:00
Michael Teeuw
f29c911a0f Merge pull request #2557 from khassel/node16 2021-04-27 07:20:31 +02:00
Karsten Hassel
bd908123c2 replace node v10 with v16 in github workflow 2021-04-26 22:29:23 +02:00
Karsten Hassel
cbdb0b67ab fix updatenotification.js increasing timeout 2021-04-26 22:24:07 +02:00
Michael Teeuw
aa3848f420 Merge pull request #2544 from rejas/es6_conversion 2021-04-18 18:14:25 +02:00
rejas
6cf0748172 Update CHANGELOG 2021-04-18 18:07:26 +02:00
rejas
11122d3f81 Use es6 notation in clientonly 2021-04-18 15:36:00 +02:00
rejas
2dea9398f2 Use es6 notation in updatenotification module 2021-04-18 15:35:41 +02:00
rejas
de93b3294f Use es6 notation in main.js 2021-04-18 15:29:10 +02:00
rejas
7accb84eb9 Use es6 notation in config sample 2021-04-18 15:06:28 +02:00
rejas
ea90ed04d6 Use es6 notation in vendor 2021-04-18 15:06:21 +02:00
rejas
838eed2630 Use es6 notation in weatherprovider 2021-04-18 15:06:06 +02:00
rejas
376b65c749 Use es6 notation in serveronly 2021-04-18 14:52:04 +02:00
rejas
3b4432cb00 Use es6 notation in defaults 2021-04-18 14:51:57 +02:00
rejas
7bc71029de Use es6 notation in socketclient 2021-04-18 14:51:50 +02:00
rejas
d736dd92be Use es6 notation in tests 2021-04-18 14:51:28 +02:00
rejas
6eba8d681c Use es6 notation in module 2021-04-17 16:29:38 +02:00
rejas
5fe654c19d Use es6 notation in loader 2021-04-17 16:29:38 +02:00
rejas
dd366f35a8 Use es6 notation in test configs 2021-04-17 16:29:38 +02:00
rejas
2ababa521d Use es6 notation in weather module and ukmet provider 2021-04-17 16:29:38 +02:00
rejas
bda8f26511 Use es6 notation in compliments module and cleanup jsdoc 2021-04-17 16:29:38 +02:00
rejas
7f1a3df25b Use es6 notation in font tests 2021-04-17 16:29:38 +02:00
rejas
ef2ff50089 Use es6 notation in clock module 2021-04-17 16:29:38 +02:00
rejas
0b3964c827 Use es6 notation in helloworld tests 2021-04-17 16:29:38 +02:00
Michael Teeuw
ccf5bb9342 Merge pull request #2540 from rejas/patch-1 2021-04-17 15:52:38 +02:00
Michael Teeuw
4303882c6a Merge pull request #2542 from rejas/codecov 2021-04-17 15:50:49 +02:00
Michael Teeuw
c34028d549 Merge pull request #2541 from rejas/css 2021-04-17 15:50:18 +02:00
rejas
8e76cdcb57 Revert "Remove some tests to see if it still errors"
This reverts commit 0abebc1e32.
2021-04-17 13:26:16 +02:00
rejas
5eb66106b9 Rename yaml file 2021-04-17 13:17:24 +02:00
rejas
0abebc1e32 Remove some tests to see if it still errors 2021-04-17 13:05:06 +02:00
rejas
552e82f44d Update CHANGELOG 2021-04-17 12:35:46 +02:00
rejas
ada40e36db Add codecov yaml to set informational mode 2021-04-17 12:35:10 +02:00
rejas
480f734a06 Update CHANGELOG, undo accidental commit in clock.js 2021-04-17 12:20:25 +02:00
rejas
20bee9c334 Update dependencies 2021-04-17 11:19:39 +02:00
rejas
de8267f41e Fix css comment style 2021-04-17 10:27:36 +02:00
rejas
7bfaf07980 Use custom properties colors more often 2021-04-17 09:50:12 +02:00
rejas
a42fa8e9f9 Adjust default css to match styles from befor custom properties merge 2021-04-17 09:38:33 +02:00
Veeck
5facad683a Add link to subforum to README
As propose din #2539
2021-04-17 06:46:30 +02:00
Michael Teeuw
fd1913a72e Merge pull request #2538 from codac/master 2021-04-15 16:16:51 +02:00
Michael Teeuw
54c98b4250 Merge pull request #2537 from rejas/custom-properties 2021-04-15 16:15:27 +02:00
config
5d99baac21 Fixing fetch option httpsAgent to agent in calendar module (#466) 2021-04-15 14:51:33 +02:00
veeck
cb95bdf6d7 Undo script changes, Update CHANGELOG 2021-04-14 17:03:09 +02:00
veeck
25e803abfc Update custom.css.sample 2021-04-14 16:56:29 +02:00
veeck
7c6073e4ef Undo README changes 2021-04-14 16:45:38 +02:00
veeck
09bcbe8dfc Update dependencies and lock files 2021-04-14 16:45:07 +02:00
veeck
e0a9c7d0bb Merge branch 'develop' into contribute 2021-04-14 16:42:54 +02:00
Michael Teeuw
ac27d05fd5 Merge pull request #2532 from ezeholz/develop
Alert Module not recognizing multiple alerts
2021-04-14 16:17:45 +02:00
Ezequiel Holzweissig
d6ab56252f Merge branch 'develop' into develop 2021-04-13 20:56:32 -03:00
Michael Teeuw
dab178ed50 Merge pull request #2536 from B1gG/develop
Develop
2021-04-13 14:38:17 +02:00
B1gG
6ed50b6a75 updating changelog 2021-04-13 07:39:14 +01:00
B1gG
ee559ec650 to use dateFormat when timeFormat is relative 2021-04-13 07:29:11 +01:00
Michael Teeuw
4310238418 Merge pull request #2527 from rejas/patch-1 2021-04-12 21:03:27 +02:00
veeck
10c47a6c38 Really run prettier 2021-04-12 19:33:23 +02:00
veeck
4b8043086e Merge branch 'develop' into patch-1 2021-04-12 19:28:51 +02:00
Michael Teeuw
fd952b88bf Merge pull request #2535 from rejas/issue_2530 2021-04-12 19:23:07 +02:00
Ezequiel Holzweissig
e262d463c5 Merge branch 'develop' into develop 2021-04-12 14:20:33 -03:00
veeck
b02bce2510 Merge branch 'develop' into issue_2530 2021-04-12 19:11:24 +02:00
Michael Teeuw
58094531c0 Merge pull request #2534 from KristjanESPERANTO/develop 2021-04-12 19:08:36 +02:00
Michael Teeuw
d937e3ca7c Merge pull request #2533 from rejas/alert_test 2021-04-12 19:07:55 +02:00
Kristjan SCHMIDT
9c58413209 Put decimalSymbol at the end of the row like in the Current part 2021-04-12 18:34:33 +02:00
rejas
db65ff12d7 Update CHANGELOG 2021-04-11 22:49:26 +02:00
rejas
f93b819ea6 Fix missing await calls in tests 2021-04-11 22:42:21 +02:00
rejas
e6fea297e5 Add test for decimalSymbol in weatherforecast 2021-04-11 22:25:17 +02:00
Kristjan SCHMIDT
5c2a0e5634 Fix decimalSymbol for forcast 2021-04-11 22:25:15 +02:00
rejas
71aa21a82d Update dependencies 2021-04-11 21:07:26 +02:00
rejas
23821360c7 Update CHANGELOG 2021-04-11 21:03:35 +02:00
rejas
79f5b938f5 Add first test for alert module 2021-04-11 20:59:53 +02:00
Michael Teeuw
f8769fcc2a Merge pull request #2531 from KristjanESPERANTO/patch-1 2021-04-11 12:45:23 +02:00
Ezequiel Holzweissig
a3ed24c766 Prettifier 2021-04-10 20:38:33 -03:00
Ezequiel Holzweissig
82727b825c Fix alerts
Fixes #2522
2021-04-10 20:15:32 -03:00
Kristjan Esperanto
16e98496af Remove duplicate codecov badge
The second badge doesn't make sense, does it?
2021-04-10 23:55:11 +02:00
rejas
62896ce1a3 Merge branch 'develop' into patch-1 2021-04-10 20:37:47 +02:00
Michael Teeuw
7ea5b1ecbf Merge pull request #2526 from FrancoisRmn/add-some-translations 2021-04-10 20:36:11 +02:00
rejas
eecc95f8fb Run linter 2021-04-10 20:34:27 +02:00
FrancoisRmn
85808d85c4 add some translations 2021-04-10 17:56:47 +02:00
Gerardo Gonzalez
12cc670642 Merge branch 'develop' into patch-1 2021-04-10 14:04:53 +01:00
Michael Teeuw
13fcb55df6 Merge pull request #2518 from oemel09/hide-newsfeed-description 2021-04-10 14:56:49 +02:00
oemel09
a4dfd15888 Fixes tests for newsfeed description 2021-04-10 09:13:14 +02:00
Gerardo Gonzalez
7fbd326298 Update CHANGELOG.md
updating the changelog for the fix Fix wrong treatment of `appendLocationNameToHeader` when using `ukmetofficedatahub`
2021-04-10 01:30:12 +01:00
Gerardo Gonzalez
331d147d50 Update ukmetofficedatahub.js
There is a wrong treatment to appendLocationNameToHeader.
The location should be always returned and leave the weater.js to decide if is included or not in the header.
2021-04-10 01:20:52 +01:00
oemel09
acdcdc55bc Adjusts parenthesis in template to be consistent 2021-04-09 08:07:37 +02:00
oemel09
fdb0c0acb3 Adds test to check that newsfeed description does not show up 2021-04-09 08:05:19 +02:00
Earl Man
564bf47fb2 update sample css 2021-04-08 14:56:01 -05:00
Earl Man
eca35b2371 change --base to --font-size 2021-04-08 14:51:07 -05:00
Earl Man
f97d2e2644 revert 'dimming' method 2021-04-08 14:48:23 -05:00
Earl Man
fdd6659139 delete useless file 2021-04-08 14:39:21 -05:00
Earl Man
0d698fb659 Merge remote-tracking branch 'upstream/develop' into contribute 2021-04-08 14:35:39 -05:00
Earl Man
c68b39dda8 Merge branch 'css-properties' 2021-04-08 14:24:39 -05:00
oemel09
cb67286bc3 Adds test to validate that description is shown 2021-04-08 17:00:15 +02:00
oemel09
a49962b8de Moves validation into template 2021-04-08 17:00:15 +02:00
oemel09
256d5ae14f Rebases onto develop 2021-04-08 16:59:46 +02:00
oemel09
799ee8bcfa Check showDescription setting for newsfeed module 2021-04-08 16:59:03 +02:00
Michael Teeuw
fe8a317ef9 Merge pull request #2514 from jupadin/log-consistency
Log consistency
2021-04-08 11:04:11 +02:00
Michael Teeuw
b6d6ee45e0 Fix linter issue. 2021-04-08 10:40:31 +02:00
Michael Teeuw
0151466a28 Merge branch 'develop' into log-consistency 2021-04-08 10:37:18 +02:00
earlman
3588875e28 Update .gitignore 2021-04-04 13:52:18 -05:00
earlman
e8031aec39 add original main.css bak 2021-04-04 13:34:52 -05:00
earlman
1cabe107e6 run prettier 2021-04-04 13:28:56 -05:00
earlman
ba0c59744b update CHANGELOG.md 2021-04-04 13:28:34 -05:00
earlman
50c702c00a adjust new opacities 2021-04-04 13:24:59 -05:00
earlman
ff8c2fe227 add custom.css.sample 2021-04-04 13:22:33 -05:00
earlman
f9c78a5263 use variables for fonts 2021-04-04 13:18:33 -05:00
earlman
d46784a4d5 base font sizes on rem and var(--base) 2021-04-04 13:05:41 -05:00
earlman
ff43d082f2 base line-height on scale 2021-04-04 12:53:35 -05:00
earlman
83801736d7 add color variables 2021-04-04 12:38:17 -05:00
earlman
e16986cf71 Merge branch 'develop' of https://github.com/MichMich/MagicMirror into contribute 2021-04-04 11:55:03 -05:00
earlman
7ad12d954c add original main.css bak 2021-04-04 11:51:04 -05:00
Julian Dinter
fb74fadec2 Run 'npm run lint:prettier'. 2021-04-04 15:37:47 +02:00
Julian Dinter
c00bdf910e Clean up code by adding comments. 2021-04-04 15:22:12 +02:00
Julian Dinter
50cc1c56e5 Consistent log text. 2021-04-04 15:17:42 +02:00
Julian Dinter
0006099758 Changed log type of starting function of updatenotification module. 2021-04-04 14:52:24 +02:00
earlman
162dcec331 Merge branch 'master' of https://github.com/foundations-design/WhiteLight into develop 2021-04-03 11:15:33 -05:00
earlman
3e8bd022e2 add custom variables 2021-04-02 11:28:22 -05:00
earlman
01e5936671 install valid-url 2021-04-02 10:11:38 -05:00
Michael Teeuw
d85e1c70d6 Merge pull request #2512 from jupadin/log-consistency 2021-04-02 12:56:28 +02:00
Julian Dinter
f329770194 Added fix in CHANGELOG. 2021-04-02 12:42:31 +02:00
Julian Dinter
08194925cb Changed log type of starting function of calendar module. 2021-04-02 12:26:34 +02:00
earlman
2b7aa3e810 use yarn 2021-04-01 15:59:48 -05:00
earlman
549417f106 yarn 2021-04-01 15:00:12 -05:00
earlman
f0f370cc7d add *.env to .gitignore 2021-04-01 15:00:06 -05:00
earlman
b842241f8c fix broken start scripts & yarn 2021-04-01 14:08:48 -05:00
earlman
93aa4b7440 update README.md 2021-04-01 14:03:38 -05:00
Michael Teeuw
9e2eef7818 Merge pull request #2511 from khassel/update-node-ical 2021-04-01 20:12:12 +02:00
Karsten Hassel
b29b65851e Bump node-ical to v0.13.0 (now last runtime dependency using deprecated request package is removed). 2021-04-01 20:02:08 +02:00
Michael Teeuw
a6829bff4f Prepare develop: v2.16.0-develop 2021-04-01 14:36:50 +02:00
Michael Teeuw
491f5aa776 Merge pull request #2510 from MichMich/develop
Release v2.15.0
2021-04-01 14:23:02 +02:00
Michael Teeuw
d401e59031 Prepare for release: v2.15.0 2021-04-01 14:09:08 +02:00
Michael Teeuw
63ce69ce82 Merge pull request #2506 from rejas/fix_self 2021-03-28 11:28:13 +02:00
veeck
f15972c823 Update CHANGELOG
just to please the action and my inner OCD ;-)
2021-03-28 10:50:26 +02:00
veeck
b210fcdcf3 Update dependencies 2021-03-28 10:48:21 +02:00
veeck
8ea37a5a45 Fix left over self reference 2021-03-28 10:48:12 +02:00
Michael Teeuw
daf98c36fc Merge pull request #2504 from rejas/translation 2021-03-26 07:08:50 +01:00
rejas
6a0e6eb84e Fix styling issue 2021-03-25 17:35:36 +01:00
rejas
7bcdd2a42c Update CHANGELOG 2021-03-25 17:30:27 +01:00
rejas
670a760404 Use some es6 notation 2021-03-25 17:29:27 +01:00
veeck
a99de1ad13 Sort translation entries consistently 2021-03-25 16:43:27 +01:00
Michael Teeuw
108fe2082d Merge pull request #2503 from sdetweil/fixnews 2021-03-25 08:31:50 +01:00
Sam Detweiler
e67dc38890 fix newsreader template 2021-03-24 15:06:12 -05:00
sam detweiler
20a87656ff Merge pull request #4 from MichMich/develop
sync Develop
2021-03-24 15:03:29 -05:00
Michael Teeuw
70c3b68e67 Add FUNDING.yml 2021-03-24 16:20:36 +01:00
Michael Teeuw
bfcb7dc601 Merge pull request #2500 from sdetweil/daylight_fullday
fix fullday recurring event where start date is before 2007
2021-03-23 15:56:53 +01:00
Michael Teeuw
39e16221fb Merge pull request #2498 from rejas/md
Update markdown files for github
2021-03-23 15:56:34 +01:00
Sam Detweiler
d11e4e5c92 refix #2483 , recurring FULL Day event, start date before 2007 2021-03-23 08:44:14 -05:00
sam detweiler
cccaa1f33d Merge pull request #3 from MichMich/develop
sync Develop
2021-03-23 08:39:03 -05:00
rejas
37d3aa25b1 Update CHANGELOG 2021-03-23 09:55:35 +01:00
rejas
ebfe96d31d Update CONTRIBUTING guidelines 2021-03-23 09:47:55 +01:00
rejas
e902b8c52f Update PR template 2021-03-23 08:15:33 +01:00
veeck
201b36d62c Update README 2021-03-23 07:55:06 +01:00
Michael Teeuw
7b29070516 Merge pull request #2495 from qu1que/develop 2021-03-22 06:50:04 +01:00
qu1que
9ed2e4d557 Update translations.js
added galician language
2021-03-21 22:59:13 +01:00
qu1que
38544f2368 Update CHANGELOG.md
Added galician language
2021-03-21 22:58:09 +01:00
qu1que
df39408411 Create gl.json
gl.json for Galician language
2021-03-21 22:57:23 +01:00
Michael Teeuw
2e01f988f5 Merge pull request #2489 from sdetweil/fixday2 2021-03-19 16:54:44 +01:00
Sam Detweiler
2b940c9cfb fix formatting 2021-03-19 10:42:14 -05:00
Sam Detweiler
b6f4737ecc fix 2488, west of UTC day shift 2021-03-19 10:32:04 -05:00
Michael Teeuw
89d6473052 Merge pull request #2484 from rejas/newsfeed_cleanup 2021-03-18 22:49:20 +01:00
rejas
99d838f9cd Remove console log 2021-03-18 16:55:54 +01:00
rejas
f3bdddfaa9 Final es6 notation stuff 2021-03-18 16:55:54 +01:00
rejas
87f952c87b Update CHANGELOG 2021-03-18 16:55:54 +01:00
rejas
639305ecc8 Add test for prohibited words 2021-03-18 16:55:54 +01:00
rejas
a70bb517e1 Use es6 notation in newsfeed module 2021-03-18 16:55:54 +01:00
rejas
e4f671c898 Update error to use translatable text 2021-03-18 16:55:54 +01:00
rejas
a269b5cd93 Show invalid url error on UI, add test case 2021-03-18 16:55:54 +01:00
Michael Teeuw
2ec275957f Merge pull request #2486 from sdetweil/fixdaylight 2021-03-17 13:52:20 +01:00
Sam Detweiler
4f9fc032e5 fix for issue 2483, calendar shows wrong date, recurring start before 2007 2021-03-17 07:36:03 -05:00
sam detweiler
5c9dbccc10 Merge pull request #2 from MichMich/develop
synch
2021-03-17 07:33:20 -05:00
Michael Teeuw
b2cf470ec9 Merge pull request #2482 from rejas/valid_url 2021-03-16 20:06:25 +01:00
veeck
bceb181b47 Update CHANGELOG 2021-03-16 19:28:38 +01:00
veeck
1a314b741a Remove valid-url from dependencies 2021-03-16 19:27:56 +01:00
rejas
7635dea3e9 Replace valid-url library by standard node method 2021-03-16 19:25:23 +01:00
Michael Teeuw
90112d1a7d Merge pull request #2481 from rejas/fix_basic_auth 2021-03-15 21:03:02 +01:00
rejas
ad0cf12e53 Update dependencies 2021-03-15 12:50:41 +01:00
rejas
a53029f11e Use es6 notation in basic auth server 2021-03-15 12:36:58 +01:00
rejas
848529f9f4 Update CHANGELOG
so the github action is quiet ;-)
2021-03-14 21:12:36 +01:00
rejas
b5dc91fd07 Remove now unnecessary require 2021-03-14 21:04:38 +01:00
rejas
0a2b939514 Undo husky upgrade 2021-03-14 19:43:00 +01:00
rejas
52584f36d7 Fix base64 encoding for basic auth in calendar 2021-03-14 19:38:03 +01:00
Michael Teeuw
30c7a24fc2 Merge pull request #2376 from rejas/calendar_refactor 2021-03-14 12:55:24 +01:00
veeck
0643a103ac Update dependencies 2021-03-14 10:40:14 +01:00
rejas
71bd45dfd4 Fix errors introduced after latest rebase 2021-03-14 10:40:14 +01:00
rejas
85c9d3b331 More es6 notations 2021-03-14 10:40:14 +01:00
rejas
5e6cbeb9ba Convert some code to es6 2021-03-14 10:40:14 +01:00
rejas
3d4429d418 Remove copypasted-function that doesnt exist 2021-03-14 10:40:14 +01:00
rejas
d3d64d3ca0 Cleanups 2021-03-14 10:40:14 +01:00
rejas
6de983aeb2 Update CHANGELOG 2021-03-14 10:40:14 +01:00
rejas
05c3a5bf83 Remove self variable 2021-03-14 10:40:14 +01:00
rejas
c2f5d038de Move filter function into utils class too 2021-03-14 09:03:12 +01:00
rejas
0ac5032db9 Move filter function into seperate method 2021-03-14 08:59:50 +01:00
rejas
9b93066cbe Remove unused variable 2021-03-14 08:53:40 +01:00
rejas
bc60ae21c4 Cleanup node_helper to look more like the one from newsfeed module 2021-03-14 08:53:40 +01:00
rejas
0b37ed072c Refactor fetcher methods into util class 2021-03-14 08:53:40 +01:00
Michael Teeuw
57174f09b9 Merge pull request #2479 from buxxi/updatenotification-async-timeout 2021-03-14 07:23:47 +01:00
buxxi
61057d1a25 fix package-lock merge conflict 2021-03-13 22:58:30 +01:00
buxxi
03964d6f68 Merge with upstream 2021-03-13 22:56:14 +01:00
Michael Teeuw
7515fc10d1 Merge pull request #2478 from thomasrockhu/codecov-badge
Add Codecov badge to README
2021-03-13 19:22:34 +01:00
Michael Teeuw
6583f05858 Merge pull request #2477 from PostLogical/openweathermaplatlon
Allowing openweathermap config lat and lon to function without onecall, overrides locationID and location since more specific.
2021-03-13 19:20:59 +01:00
Michael Teeuw
2b1dbbde68 Merge pull request #2475 from MystaraTheGreat/master
Added hiddenOnStartup flag to module config
2021-03-13 19:20:29 +01:00
Michael Teeuw
49c95a9f46 Merge pull request #2474 from khassel/node-fetch
Node fetch
2021-03-13 19:19:22 +01:00
Michael Teeuw
a6214c8da3 Merge pull request #2473 from khassel/remove-ical
remove ical
2021-03-13 19:18:35 +01:00
buxxi
d2b3414ac6 changelog for zombie processes and fixing linting error 2021-03-13 12:02:56 +01:00
buxxi
401a6f3417 Updating simple-git and using timeout when checking for module updates 2021-03-13 11:58:33 +01:00
buxxi
3ed223a550 Refactoring update notification to use async/await 2021-03-13 11:12:08 +01:00
Tom Hu
47bd48e0a3 Add Codecov badge to README 2021-03-12 23:36:24 -05:00
Robby Griffin
cdd1853369 Fix weather module openweathermap not loading if lat and lon set without onecall. Lat and Lon take precedence over LocationID and Location. 2021-03-11 12:48:41 -05:00
MystaraTheGreat
8f2980c23d Fixed unnecessarily verbose way of looping thanks to @rejas :) 2021-03-07 20:34:26 +00:00
MystaraTheGreat
b72556b9a9 Merge branch 'master' of https://github.com/MystaraTheGreat/MagicMirror 2021-03-07 20:14:36 +00:00
MystaraTheGreat
49be3cbd6b Changed var to let as requested 2021-03-07 20:13:57 +00:00
MystaraTheGreat
fbb0c59b4e Merge branch 'develop' into master 2021-03-07 13:16:18 +00:00
MystaraTheGreat
d83e696a8d Changed variable name to appease tester 2021-03-07 11:23:52 +00:00
MystaraTheGreat
a467d900c9 Changed iterative variable from m to n to appease tester 2021-03-07 11:20:19 +00:00
MystaraTheGreat
96be8d6fea Updated CHANGELOG.md 2021-03-07 11:14:23 +00:00
MystaraTheGreat
8b1ce26fa6 Added hiddenOnStartup flag to module configuration options to cause a module to be iniitally hidden after starting up 2021-03-07 11:05:29 +00:00
Karsten Hassel
514b9453f8 removed getFetcher function 2021-03-06 21:19:14 +01:00
Karsten Hassel
fa0f997928 replace request with node-fetch 2021-03-05 22:17:55 +01:00
Karsten Hassel
504e7cadd7 getFetcher 2021-03-05 00:13:32 +01:00
Karsten Hassel
c80aeff945 fixes calendarfetcher 2021-03-04 21:12:53 +01:00
Karsten Hassel
454206c803 remove ical 2021-03-03 21:41:48 +01:00
Michael Teeuw
e4f47178fc Merge pull request #2466 from rejas/electron_update
Enable contextIsolation in Electron
2021-03-03 10:43:10 +01:00
Karsten Hassel
1f9109f8e4 snapshot 2021-03-03 00:09:32 +01:00
Karsten Hassel
f09c54184a moved tests to fetch, run prettier 2021-03-02 21:26:25 +01:00
Karsten Hassel
92a35692f2 snapshot 2021-03-02 00:40:55 +01:00
Karsten Hassel
53e300bd31 snapshot 2021-03-02 00:17:13 +01:00
veeck
e8be6ad4f1 Update CHANGELOG 2021-02-27 14:01:10 +01:00
rejas
01b9ecb339 Update dependencies 2021-02-27 13:58:59 +01:00
rejas
6ff85ebe54 Enable contextIsolation in electron
Fixes warning when starting MM
2021-02-27 13:19:16 +01:00
Michael Teeuw
ea6eebd809 Merge pull request #2439 from fewieden/feature/add-error-to-callback
Added error to callback
2021-02-23 14:18:07 +01:00
Michael Teeuw
b2a21b937d Change error string. 2021-02-23 14:11:54 +01:00
Michael Teeuw
13010ecaee Merge branch 'develop' into feature/add-error-to-callback 2021-02-23 14:10:35 +01:00
Michael Teeuw
2e2e157017 Merge pull request #2458 from codac/patch-2
This is supposed to make self signed certs work with the calendar module
2021-02-23 14:09:13 +01:00
Michael Teeuw
e23e33852e Merge pull request #2464 from fewieden/feature/expose-logger-for-modules
exposed logger as node module
2021-02-23 14:08:07 +01:00
config
5b270b84b4 linted calendarfetcher.js 2021-02-21 19:56:02 +01:00
Krouty
9094240024 Merge branch 'develop' into patch-2 2021-02-21 11:48:47 +01:00
Krouty
1db0dbf52b Added support for self-signed certificates
Added support for self-signed certificates
2021-02-21 11:45:15 +01:00
Krouty
beb5faef8b Added support for self-signed certificates
Added support for self-signed certificates
2021-02-21 11:32:03 +01:00
Krouty
c6aff8b7dc Added suppoert for self-signed certificates
Added support for self-signed certificates
2021-02-21 11:29:19 +01:00
Krouty
1aacc37c83 Added selfSignedCert as a parameter
Added selfSignedCert as a parameter, so that it can be enabled only when it is required.
2021-02-21 10:29:40 +01:00
Felix Wiedenbach
b18d98f5ea exposed logger as node module 2021-02-18 19:14:53 +01:00
Michael Teeuw
09ddd3d925 Merge pull request #2461 from fewieden/feature/add-locale 2021-02-16 23:55:51 +01:00
Felix Wiedenbach
e2b4823e43 added locale to sample config 2021-02-16 22:06:53 +01:00
Krouty
04491ac5ac This is supposed to make self signed certs work with the calendar module
Many people use Own-/Nextcloud together witht he https protocol. This is supposed to make self-signed certificates work with the calendar module and fix the issue #466.
2021-02-13 17:13:00 +01:00
Michael Teeuw
e3bee5aae7 Merge pull request #2454 from rejas/cleanup_tests 2021-02-13 17:00:58 +01:00
Michael Teeuw
89152e537e Merge pull request #2452 from TheDuffman85/develop 2021-02-13 17:00:07 +01:00
Felix Wiedenbach
652e1a528f Merge branch 'develop' into feature/add-error-to-callback
# Conflicts:
#	CHANGELOG.md
2021-02-13 08:43:23 +01:00
Felix Wiedenbach
9d85baee37 move error callback into options and rename it 2021-02-13 08:29:13 +01:00
rejas
bdc4ed4d86 Add specific change-port test and fix the others 2021-02-12 08:45:54 +01:00
rejas
35f3b315a2 Update CHANGELOG 2021-02-11 21:22:56 +01:00
rejas
efec49bb47 Use es6 notations 2021-02-11 21:22:03 +01:00
rejas
68bc77c81e Cleanup global-setup code 2021-02-11 21:21:08 +01:00
veeck
dbea348779 Cleanup port usage in tests 2021-02-11 21:20:34 +01:00
rejas
b46160bcbc Simplify weather-compliments test, increase timeWaitout for it 2021-02-11 21:20:16 +01:00
TheDuffman85
ddb06ca214 Added translation for today and tomorrow
I had missread the documentation of moment.js. We've to provide the translation for today and tomorrow ourselves. For the translation I use the standard MM² translation mechanism.
2021-02-10 14:24:59 +01:00
Michael Teeuw
ef2fd16b69 Merge pull request #2448 from khassel/electron11
fix e2e tests after spectron update
2021-02-10 10:01:57 +01:00
Michael Teeuw
d874ad9988 Merge pull request #2451 from TheDuffman85/develop 2021-02-09 17:41:05 +01:00
TheDuffman85
57dc349675 Added a new parameter to hide time portion on relative times 2021-02-09 16:06:21 +01:00
TheDuffman85
120f0299b2 Added a new parameter to hide time portion on relative times
Added a new parameter hideTime with default value false. This parameter hides the time portion on relative times.
2021-02-09 16:05:38 +01:00
Michael Teeuw
b5b6df5e48 Merge pull request #2450 from TheDuffman85/develop 2021-02-09 15:37:47 +01:00
TheDuffman85
5ffd20b843 Removed unnecessary tabs 2021-02-09 15:21:14 +01:00
TheDuffman85
3dacce6675 Respect parameter ColoredSymbolOnly also for custom events 2021-02-09 13:05:11 +01:00
TheDuffman85
8e4ba4fe93 Respect parameter ColoredSymbolOnly also for custom events 2021-02-09 13:01:57 +01:00
Michael Teeuw
37d488760f Merge pull request #2449 from rejas/update_jsdoc 2021-02-07 09:09:14 +01:00
veeck
7cdeceedf1 Update lock file 2021-02-07 08:47:18 +01:00
Karsten Hassel
7ba76020d8 fix e2e tests after spectron update 2021-02-07 00:20:30 +01:00
rejas
aab1b97653 Update CHANGELOG 2021-02-06 22:57:31 +01:00
rejas
221eadcc20 Make function callbacks and returns more readable 2021-02-06 22:55:59 +01:00
rejas
4a11cdc564 Update dependencies 2021-02-06 22:48:24 +01:00
rejas
b9333134c7 Fix lint script 2021-02-06 22:21:08 +01:00
veeck
8c83dc9494 Cleanup jsdoc 2021-02-06 22:20:49 +01:00
Felix Wiedenbach
1ed721fb15 updated changelog 2021-02-06 21:32:57 +01:00
Felix Wiedenbach
88ed5ed373 add error separate callback 2021-02-06 21:22:13 +01:00
Felix Wiedenbach
84995c9252 Merge branch 'develop' into feature/add-error-to-callback
# Conflicts:
#	CHANGELOG.md
2021-02-06 11:29:17 +01:00
Michael Teeuw
6fadc76fe3 Merge pull request #2447 from rejas/warn_dom
Warn dom
2021-02-06 11:01:21 +01:00
Michael Teeuw
d240986fdc Merge branch 'develop' into warn_dom 2021-02-06 11:01:08 +01:00
Michael Teeuw
afc73920ad Merge pull request #2443 from khassel/electron11 2021-02-06 10:58:38 +01:00
rejas
16615c3da2 Update CHANGELOG 2021-02-05 22:40:44 +01:00
rejas
88c80973f1 Dont updateDOM when the module is not displayed 2021-02-05 22:40:05 +01:00
Karsten Hassel
e322be2624 Merge branch 'develop' into electron11 2021-01-30 22:58:35 +01:00
Karsten Hassel
059b87bbb4 bump electron to v11 2021-01-30 21:58:49 +01:00
Michael Teeuw
911675f995 Merge pull request #2442 from fewieden/fix/translation-fallbacks
Fix/translation fallbacks
2021-01-30 20:21:39 +01:00
Felix Wiedenbach
e76fe5e25a updated changelog 2021-01-29 22:42:57 +01:00
Felix Wiedenbach
308774c2a6 remove callback hell 2021-01-29 22:34:12 +01:00
Felix Wiedenbach
db24f20289 cleaned up function and added test in case no file should be loaded 2021-01-29 22:25:49 +01:00
Felix Wiedenbach
94bb8e6c03 added sinon, tests for module.loadTranslations 2021-01-29 22:13:44 +01:00
Felix Wiedenbach
41da6f455a use error object for callback to include stack trace 2021-01-28 21:23:48 +01:00
Felix Wiedenbach
d2a7a3b0bb more error like message 2021-01-28 21:13:17 +01:00
Felix Wiedenbach
afbdacf136 updated changelog 2021-01-28 07:46:23 +01:00
Felix Wiedenbach
2324579057 add error to module show callback 2021-01-28 07:33:02 +01:00
Michael Teeuw
3c357f057b Merge pull request #2438 from khassel/fix_socket_v2 2021-01-27 21:15:01 +01:00
Karsten Hassel
5116a2fc82 fix socket.io backward compatibility with socket v2 clients 2021-01-27 20:52:50 +01:00
Michael Teeuw
c0ddc020d5 Merge pull request #2433 from buxxi/deprecate-old-weather 2021-01-24 11:04:13 +01:00
buxxi
0683734d5a Make a sane default for weatherEndpoint based on the type 2021-01-24 10:32:43 +01:00
buxxi
6cbd267384 Merge branch 'develop' into deprecate-old-weather 2021-01-24 10:22:39 +01:00
Michael Teeuw
925113fa20 Merge pull request #2432 from buxxi/weather-provider-hourly 2021-01-23 16:56:03 +01:00
buxxi
3696d45e94 Merge branch 'develop' into deprecate-old-weather 2021-01-23 13:56:13 +01:00
Michael Teeuw
cb3ae66652 Merge pull request #2430 from EdgardosReis/patch-4 2021-01-23 13:31:27 +01:00
buxxi
948b6c8de8 deprecate module currentweather and weatherforecast 2021-01-23 13:12:56 +01:00
buxxi
3c4d7a33e0 Fixing code style issue with no return before default 2021-01-23 12:07:10 +01:00
buxxi
5a421220c9 Updating readme and changelog and fixing typo in method 2021-01-23 11:40:02 +01:00
buxxi
41508931be Moving default values regarding specific providers into the providers themselves 2021-01-23 11:21:56 +01:00
buxxi
e2cfa24686 make weatherprovider have a method for hourly fetching instead of a generic weatherData 2021-01-23 10:45:55 +01:00
buxxi
d48113f2d9 Moving openweathermap specific check for hourly into its provider and make invalid types fail nicer 2021-01-23 10:13:41 +01:00
Edgar dos Reis
16c5bddbb7 Update CHANGELOG.md
updated changelog
2021-01-22 14:07:40 +00:00
Edgar dos Reis
ef7556f6d3 Update pt.json
Added MODULE_CONFIG_CHANGED and PRECIP translations.
2021-01-22 13:02:13 +00:00
Michael Teeuw
a3cb0b7b96 Merge pull request #2428 from rejas/update_defaults
Update documentation and help screen about invalid config files
2021-01-21 15:11:39 +01:00
Michael Teeuw
4d28688f30 Merge branch 'develop' into update_defaults 2021-01-21 15:11:31 +01:00
Michael Teeuw
01ff00fa31 Merge pull request #2427 from dannoh/Issue-TranslateEncodedHtml
Issue translate encoded html
2021-01-21 15:10:28 +01:00
Michael Teeuw
78190d9c7f Merge branch 'develop' into Issue-TranslateEncodedHtml 2021-01-21 15:10:14 +01:00
Michael Teeuw
b1565e4047 Merge pull request #2423 from rejas/add_start_dev
Added start:dev command to npm scripts
2021-01-21 15:08:52 +01:00
Michael Teeuw
db220b7861 Merge branch 'develop' into add_start_dev 2021-01-21 15:08:44 +01:00
Michael Teeuw
eaf27b837e Merge pull request #2422 from buxxi/develop
Refactoring newsfeed-module to use templates instead of dom-generation
2021-01-21 15:07:22 +01:00
Michael Teeuw
74410344af Merge pull request #2421 from klaernie/no-empty-subdirs
prevent empty path components for module main scripts
2021-01-21 15:07:07 +01:00
Michael Teeuw
998f64f983 Merge branch 'develop' into no-empty-subdirs 2021-01-21 15:06:57 +01:00
Michael Teeuw
684dfb643b Merge pull request #2420 from rejas/issue_2416
Add new Notification CURRENTWEATHER_TYPE
2021-01-21 15:05:09 +01:00
Michael Teeuw
314c3cb516 Merge pull request #2417 from ashishtank/Issue2221
Fixed Unit test case error for #2221
2021-01-21 15:04:33 +01:00
veeck
58939bfd8c Update documentation and help screen about invalid config files 2021-01-20 22:44:37 +01:00
Dan Forsyth
33592b3c0e Removed |safe from translates in the default module templates 2021-01-20 14:06:00 -05:00
Dan Forsyth
ad9c2549bc Removed |safe from translates in the default module templates 2021-01-20 13:47:32 -05:00
Dan Forsyth
5152e0b114 Updated changelog 2021-01-20 07:36:26 -05:00
Dan Forsyth
ca48663efd Merge remote-tracking branch 'origin/develop' into Issue-TranslateEncodedHtml 2021-01-20 07:31:11 -05:00
Dan Forsyth
b520b4c37a Marked all translated strings as safe before passing them to the nunjuck template 2021-01-20 07:16:09 -05:00
veeck
90f07295b1 Add extra check for currentweather 2021-01-17 15:00:34 +01:00
veeck
3895c18466 Add test case 2021-01-17 14:57:06 +01:00
veeck
2b6a9fc5bb Update dependencies 2021-01-17 12:41:42 +01:00
rejas
052f0b8709 Added start:dev script 2021-01-16 22:23:55 +01:00
buxxi
a5bb9d962d Fixing eslint issues 2021-01-16 14:06:07 +01:00
buxxi
2d9d28aa0f updating changelog with newsfeed changes 2021-01-16 13:47:16 +01:00
buxxi
69c053a94f refactoring newsfeed to use templates instead of generating dom in the code 2021-01-16 13:37:18 +01:00
buxxi
aaaf1f660c refactoring newsfeed, moving hiding of module while loading away from dom-generation 2021-01-16 12:26:38 +01:00
buxxi
8538d83520 Moving newsfeed styling from js to a new css file 2021-01-16 11:52:55 +01:00
buxxi
132c98b767 refactoring newsfeed, moving tag stripping to loading instead of presentation logic 2021-01-16 11:10:53 +01:00
rejas
42cac81953 Fix tests 2021-01-15 23:12:44 +01:00
Andre Klärner
3ee4bd65c6 prevent empty path components for module main scripts
The module.path component has by definition in line 97 a trailing slash.
Hence adding another is unneeded, but results in an additional folder in the inspector.
2021-01-15 23:09:07 +01:00
rejas
fcc7e80bf9 Update CHANGELOG 2021-01-15 21:49:12 +01:00
rejas
e0d43a4c1e Add new Event CURRENTWEATHER_TYPE
- send it from the weather and currentweather module
- use it in the compliments module directly instead of the data
2021-01-15 21:47:14 +01:00
Ashish Tank
4966d6c920 Fixed Unit test case error for #2221 2021-01-14 19:10:04 +01:00
Michael Teeuw
1fd506f25d Merge pull request #2414 from ashishtank/FeelsLikeCleanup
Feels like translation code cleanup
2021-01-13 09:50:11 +01:00
Michael Teeuw
a99698d1a9 Merge pull request #2413 from ashishtank/Issue2221
Issue 2221 Weather module - Always displays night icons because of fixed day start and end time
2021-01-12 16:14:06 +01:00
Ashish Tank
774b86c7dc Code cleanup for feels like translation 2021-01-10 17:37:10 +01:00
Ashish Tank
2c3e8533c7 Issue #2221 - Weather forecast always shows night icons in day time 2021-01-10 16:24:46 +01:00
ashishtank
3eda8af671 Merge pull request #9 from MichMich/develop
Develop
2021-01-10 16:03:49 +01:00
Michael Teeuw
aa61874848 Merge pull request #2411 from khassel/fix_cors
fix socket.io cors errors
2021-01-10 10:13:16 +01:00
Karsten Hassel
2deab31187 fix socket.io cors errors, see breaking change since socket.io v3 https://socket.io/docs/v3/handling-cors/ 2021-01-09 23:20:36 +01:00
Michael Teeuw
b177a56fa2 Fix wrong placement of changelog item. 2021-01-07 12:45:03 +01:00
Michael Teeuw
6f0f75cf27 Merge pull request #2388 from drewski3420/no_negative_zero
No negative zero
2021-01-07 12:41:30 +01:00
Michael Teeuw
39bb2eb9b0 Add Changelog. 2021-01-07 11:53:21 +01:00
Michael Teeuw
7b36bb025a Improve readabiliy. 2021-01-07 11:51:10 +01:00
Michael Teeuw
aa9a1b7af2 Add CodeCov badge to Readme. 2021-01-06 11:03:15 +01:00
Michael Teeuw
caf3552d6b Merge pull request #2401 from rejas/github_codecov_action
Add github action for uploading codecov results
2021-01-06 10:05:50 +01:00
veeck
6e9897f7fc Remove now unnecessary file 2021-01-06 09:33:06 +01:00
veeck
fa9258761e Change on-trigger 2021-01-06 09:33:06 +01:00
veeck
003e948899 Use codecov action instead of bash command 2021-01-06 09:33:06 +01:00
veeck
9cd998f219 Update CHANGELOG 2021-01-06 09:33:06 +01:00
rejas
d75b894d9a Add lcov reporter 2021-01-06 09:33:06 +01:00
rejas
5a3d3b76a7 Cleanups 2021-01-06 09:33:06 +01:00
rejas
5bc2c207db Add codecov github action 2021-01-06 09:33:06 +01:00
Michael Teeuw
612cf25878 Merge pull request #2407 from fewieden/feature/update-node-js-code
update node js code
2021-01-06 09:01:42 +01:00
Felix Wiedenbach
5ae4912b45 added changelog entry 2021-01-05 20:08:18 +01:00
Felix Wiedenbach
a9a70fd2e9 linting and fix defualt module test 2021-01-05 20:04:02 +01:00
Felix Wiedenbach
f90856808b fix config file path 2021-01-05 19:39:31 +01:00
Felix Wiedenbach
7dbcaa83bc clean up deprecated 2021-01-05 19:36:20 +01:00
Felix Wiedenbach
38f10b6e3e clean up app 2021-01-05 19:35:11 +01:00
Felix Wiedenbach
9c8fa06ce1 clean up config checker 2021-01-05 19:01:59 +01:00
Felix Wiedenbach
4efe04774c clean up electron 2021-01-05 18:48:55 +01:00
Felix Wiedenbach
5d60534dc9 clean up node helper 2021-01-05 18:44:36 +01:00
Felix Wiedenbach
ac141a4316 clean up server 2021-01-05 18:37:16 +01:00
Felix Wiedenbach
d22064c6f4 clean up utils 2021-01-05 18:37:01 +01:00
Michael Teeuw
189721ebba Merge pull request #2406 from rejas/danger
Remove now unused danger library
2021-01-05 16:54:55 +01:00
Michael Teeuw
7aa9c63dba Merge pull request #2405 from rejas/markdown
Update markdown for 2021
2021-01-05 16:53:46 +01:00
veeck
16e894e300 Update CHANGELOG 2021-01-05 15:07:40 +01:00
veeck
d466705ec0 Fix eslint error due to now js files being in root anymore 2021-01-05 15:05:50 +01:00
veeck
0e97d863ce Remove now unused danger library 2021-01-05 14:43:41 +01:00
veeck
5de64d2ae8 Added engine fields into package json 2021-01-05 09:54:03 +01:00
veeck
ced0398e49 Update markdowns 2021-01-05 09:53:47 +01:00
Michael Teeuw
d4b57924a7 Merge pull request #2404 from rejas/issue_2402
Update default log levels
2021-01-04 13:52:37 +01:00
veeck
d2def2bea3 Update CHANGELOG 2021-01-04 10:03:35 +01:00
veeck
e65bb84f9f Add default values for log levels 2021-01-04 10:01:56 +01:00
Michael Teeuw
69b0aa6118 Prepare v2.15.0-develop 2021-01-01 19:27:07 +01:00
Michael Teeuw
10dc315f3b Merge pull request #2400 from MichMich/develop
Release 2.14.0
2021-01-01 19:16:03 +01:00
Michael Teeuw
0f1457b5d7 Prepare release 2.14.0 - Fix Version 2021-01-01 19:08:19 +01:00
Michael Teeuw
090873d4c2 Prepare release 2.14.0 2021-01-01 19:07:14 +01:00
Michael Teeuw
da00c168ae Merge pull request #2398 from fewieden/patch-2
Highlight required version mismatch
2021-01-01 15:59:45 +01:00
fewieden
8286d5a06e Highlight required version mismatch 2021-01-01 14:44:39 +01:00
fewieden
dc5fb978a7 Update CHANGELOG.md 2021-01-01 14:43:16 +01:00
Michael Teeuw
a4ab0cbe09 Merge pull request #2397 from ashishtank/develop
Added support for variables in nunjucks templates for translate filter
2020-12-31 19:57:03 +01:00
Ashish Tank
4a341b381e Added support for variables in nunjucks templates for translate filter 2020-12-31 18:58:21 +01:00
ashishtank
3fa98bc1aa Merge pull request #8 from MichMich/develop
Develop
2020-12-31 18:51:38 +01:00
Michael Teeuw
87e2e87ce6 Merge pull request #2395 from ashishtank/develop
Added support for optional DEGREE position in "FEELS" label
2020-12-31 16:44:25 +01:00
Michael Teeuw
c25b6dc16c Merge pull request #2392 from sdetweil/fix-update
Fix simple-git version for unhandle promise rejection
2020-12-31 16:43:26 +01:00
sam detweiler
6a786aa090 Merge branch 'develop' into fix-update 2020-12-31 09:35:29 -06:00
Michael Teeuw
a97d87bce8 Merge pull request #2387 from bugsounet/patch-1
No Text Select for Touch Screen use
2020-12-31 16:29:14 +01:00
Ashish Tank
fba91329f1 Added support optional support for DEGREE position in FEELS translation 2020-12-31 16:28:00 +01:00
Michael Teeuw
353cc3b00f Merge pull request #2393 from MikeBishop/translator_falsy_vars
Permit substituting a falsy value in translator variables
2020-12-31 16:27:43 +01:00
ashishtank
057ef63586 Merge pull request #7 from MichMich/develop
Develop
2020-12-31 16:15:26 +01:00
Mike Bishop
c9fb38981e CHANGELOG 2020-12-31 10:11:43 -05:00
Mike Bishop
9ea7de8b44 Permit substituting a falsy value in translator variables 2020-12-31 10:10:01 -05:00
Sam Detweiler
020c8ccd2a fix update notification 2383 2020-12-31 09:08:23 -06:00
Sam Detweiler
4d09abe725 fix node-ical version 0.12.7 for bad rrule throw 2020-12-31 07:40:40 -06:00
Sam Detweiler
5079e30caf Merge branch 'develop' of https://github.com/MichMich/MagicMirror into develop 2020-12-31 07:36:37 -06:00
drewski3420@gmail.com
e51f6597ed This change prevents returning '-0' (negative zero) when roundTemp is true. 2020-12-30 09:03:19 -05:00
drewski3420@gmail.com
e80a65a3cd This change prevents returning '-0' (negative zero) when roundTemp is true 2020-12-30 08:51:07 -05:00
Bugsounet - Cédric
3e5c1a278b Update CHANGELOG.md 2020-12-30 11:38:28 +01:00
Bugsounet - Cédric
4ded60874f No Text Select for Touch Screen use 2020-12-30 11:34:09 +01:00
drewski3420@gmail.com
7c3675c9e1 This change prevents returning '-0' (negative zero) when roundTemp is true. 2020-12-29 21:07:14 -05:00
Michael Teeuw
ff1a843de6 Merge pull request #2386 from khassel/fix_modules_array
removes undefined objects from modules array, see issue #2382
2020-12-29 23:01:25 +01:00
Karsten Hassel
e6cefcf948 Merge branch 'develop' into fix_modules_array
# Conflicts:
#	CHANGELOG.md
2020-12-29 22:24:19 +01:00
Michael Teeuw
a857412f13 Merge pull request #2385 from rejas/missing_function_call
Add missing function call in module.js
2020-12-29 22:15:55 +01:00
Karsten Hassel
e507f95b2a added suggestion from @rejas 2020-12-29 21:45:35 +01:00
Karsten Hassel
61cf92c67a removes undefined objects from modules array, see issue #2382 2020-12-29 21:14:42 +01:00
rejas
7a4eddc592 Adjust some log levels 2020-12-29 18:50:27 +01:00
rejas
3fcbf15915 Update CHANGELOG 2020-12-29 18:50:10 +01:00
rejas
efafb1c28a Cleanup jsdoc 2020-12-29 18:48:45 +01:00
rejas
67bedf8648 Add missing function () 2020-12-29 18:38:38 +01:00
Michael Teeuw
c1d35b0f91 Merge pull request #2384 from rejas/log_level_sample
Update config sample
2020-12-29 16:23:39 +01:00
Michael Teeuw
d3a715bd6b Merge pull request #2381 from sdetweil/node-ical-again
change node-ical version again
2020-12-29 16:22:52 +01:00
Michael Teeuw
28b52cd24f Merge pull request #2379 from sdetweil/fix-package
Fix package.json electron dependency
2020-12-29 16:22:33 +01:00
Michael Teeuw
46e77f805c Merge pull request #2377 from buxxi/gitignore-merge
Merging config/.gitignore with .gitignore
2020-12-29 16:21:47 +01:00
buxxi
dd23db0ad8 Running prettier for CHANGELOG 2020-12-29 12:43:40 +01:00
rejas
30cf7f8afe Update CHANGELOG 2020-12-29 10:23:57 +01:00
rejas
7802e0bb88 Set locationID everywhere on config sample 2020-12-29 10:23:12 +01:00
rejas
07e75b8550 Adjust log level in sample 2020-12-29 10:21:50 +01:00
Sam Detweiler
1e9fad8278 Merge branch 'develop' of https://github.com/MichMich/MagicMirror into develop 2020-12-28 12:21:48 -06:00
Sam Detweiler
0975826457 update node-ical version 2020-12-28 08:24:19 -06:00
Sam Detweiler
e5ff320591 fix package.json for optional dependency 2020-12-28 08:15:40 -06:00
Sam Detweiler
02c1e47749 Merge branch 'develop' of https://github.com/MichMich/MagicMirror into fix-package 2020-12-28 08:13:50 -06:00
buxxi
a50824eeee Merging config/.gitignore with .gitignore 2020-12-28 11:50:01 +01:00
Michael Teeuw
0c3f9f4ed9 Merge pull request #2372 from sdetweil/update_node-ical
update node-ical to 0.12.4
2020-12-23 17:51:42 +01:00
Sam Detweiler
6b12601c6f update node-ical to 0.12.4 2020-12-23 08:57:22 -06:00
Sam Detweiler
225bece44e update node-ical to 0.12.4 2020-12-23 08:53:45 -06:00
Michael Teeuw
a5a5e73196 Fix Prettier issue. 2020-12-21 13:59:03 +01:00
Michael Teeuw
f8085ed78f Update CHANGELOG.md 2020-12-21 13:56:19 +01:00
Michael Teeuw
4e6e84c637 Replace Badge 2020-12-21 13:33:03 +01:00
Michael Teeuw
846212798c Update node-ci.js.yml 2020-12-21 13:29:10 +01:00
Michael Teeuw
571f95eb2f Merge pull request #2370 from MichMich/fix-changelog
Fix changelog special character issues.
2020-12-21 13:19:26 +01:00
Michael Teeuw
202eeea33f Fix changelog special character issues. 2020-12-21 13:16:54 +01:00
Michael Teeuw
af212057db Merge pull request #2369 from MichMich/enforce-changelog
Create enforce-changelog.yml
2020-12-21 13:07:17 +01:00
Michael Teeuw
0fd0fea7e7 Update enforce-changelog.yml 2020-12-21 13:06:39 +01:00
Michael Teeuw
eb6ef3c8ff Update enforce-changelog.yml 2020-12-21 13:02:40 +01:00
Michael Teeuw
cdb8d35cf6 Create enforce-changelog.yml 2020-12-21 12:36:59 +01:00
Michael Teeuw
684dcdcef3 Merge pull request #2224 from ashishtank/develop
Issue 2221 - Weather module always shows night icons for locale other then english
2020-12-21 11:33:05 +01:00
Michael Teeuw
155351f5dd Merge pull request #2226 from rejas/update_dependencies
Update dependencies incl ini
2020-12-21 11:32:52 +01:00
Michael Teeuw
0419e06e7c Merge pull request #2361 from buxxi/smhi-provider
Adding SMHI as a provider for the weather module
2020-12-21 11:29:20 +01:00
Michael Teeuw
2ea38bd9a4 Stupid commit to check github actions. 2020-12-21 11:27:12 +01:00
Michael Teeuw
30db9c30c8 Stupid commit to check github actions. 2020-12-21 11:25:36 +01:00
Michael Teeuw
a8ef594dab Stupid commit to check github actions. 2020-12-21 11:23:02 +01:00
Michael Teeuw
9858d5b495 Merge pull request #2360 from rejas/issue_2228
Hide alert overlay when dismissed manually
2020-12-21 11:20:55 +01:00
Michael Teeuw
20bd85b676 Delete danger-ci.yml
Unfortunately Danger Actions currently can't work with PR's:
https://github.com/PrismJS/prism/issues/2627
2020-12-21 11:20:34 +01:00
Michael Teeuw
83ec8ca24f Restore. 2020-12-21 11:13:41 +01:00
Michael Teeuw
9622d02230 Yolo. 2020-12-21 11:11:48 +01:00
Michael Teeuw
b72d0ed37e Add ENV variable. 2020-12-21 11:02:14 +01:00
Michael Teeuw
f966e504a5 Cleanup. 2020-12-21 10:59:55 +01:00
Michael Teeuw
a0366e794b Stupid commit to check github actions. 2020-12-21 10:57:18 +01:00
Michael Teeuw
3c3ce24397 Cleanup Danger-CI 2020-12-21 10:51:51 +01:00
Michael Teeuw
30290f3eb2 Merge pull request #2367 from MichMich/danger-ci
Danger ci
2020-12-21 10:47:27 +01:00
Michael Teeuw
616431e04b Disable Transpile. 2020-12-21 10:27:06 +01:00
Michael Teeuw
d0aeb90f0a Merge pull request #2365 from MichMich/ci-test
Replace Travis with Github Actions
2020-12-21 10:24:26 +01:00
Michael Teeuw
86220fa721 Update danger-ci.yml 2020-12-21 10:22:25 +01:00
Michael Teeuw
0f58a56a07 Add env variable. 2020-12-21 09:47:36 +01:00
Michael Teeuw
29451562e3 Create danger-ci.yml 2020-12-21 09:37:08 +01:00
Michael Teeuw
911687af2a Remove Travis 2020-12-21 09:28:26 +01:00
Michael Teeuw
1c9c33b87b Merge branch 'develop' into ci-test 2020-12-21 09:16:52 +01:00
Michael Teeuw
f485462af1 Merge pull request #2364 from khassel/ci-test
test github actions
2020-12-21 08:25:40 +01:00
Karsten Hassel
39d7ceb017 test github actions 2020-12-20 22:49:41 +01:00
Michael Teeuw
8251792a0d Update .prettierignore 2020-12-20 20:04:06 +01:00
Michael Teeuw
54ac450f92 Update and rename node.js.yml to node-ci.js.yml 2020-12-20 20:00:54 +01:00
Michael Teeuw
299e4a497f Create node.js.yml 2020-12-20 19:58:06 +01:00
buxxi
3f851c1fd6 Adding SMHI as a provider for the weather module 2020-12-19 11:13:46 +01:00
veeck
8cf16f1049 Update changelog 2020-12-17 18:32:01 +01:00
veeck
b373aa6250 Hide alert overlay when dismissed manually 2020-12-17 18:31:18 +01:00
veeck
2f4b8cd642 Update dependencies incl ini 2020-12-11 11:09:52 +01:00
Ashish Tank
85b6df3738 Issue #2221 2020-12-09 15:27:36 +01:00
Ashish Tank
c675421a6a #Issue 2221 2020-12-09 12:08:20 +01:00
ashishtank
a7b571e5d0 Merge pull request #6 from MichMich/develop
Develop
2020-12-09 12:04:15 +01:00
Ashish Tank
b3a9b7ef0e Merge branch 'develop' of https://github.com/ashishtank/MagicMirror into develop
Pull from github
2020-12-09 11:59:30 +01:00
Ashish Tank
e984893853 Issue #2221 - Night icons are always shown for locale other then english 2020-12-09 11:58:47 +01:00
Michael Teeuw
3024319027 Merge pull request #2222 from khassel/update_dependencies
update depencencies
2020-12-08 16:36:06 +01:00
Michael Teeuw
21ba652413 Regenerate Package.lock 2020-12-08 16:33:57 +01:00
Michael Teeuw
e4b8cd92f2 Merge branch 'develop' into update_dependencies 2020-12-08 16:31:47 +01:00
Michael Teeuw
1a4a9f6501 Merge pull request #2190 from Sub028/develop
Fix windspeed convertion error in ukmetoffice weather provider
2020-12-08 16:28:02 +01:00
Michael Teeuw
e950cdaf32 Prettier fixes. 2020-12-08 16:20:48 +01:00
Michael Teeuw
f97be2f8f3 Fix prettier issue. 2020-12-08 16:07:11 +01:00
Michael Teeuw
be0c8f4f16 Prettier fix. 2020-12-08 16:01:19 +01:00
Michael Teeuw
46fd2de315 Merge branch 'develop' into develop 2020-12-08 15:53:21 +01:00
Michael Teeuw
72f0d77865 Merge pull request #2217 from sdetweil/cal-again2
Cal again2
2020-12-08 15:51:56 +01:00
Michael Teeuw
d43679d59e Merge pull request #2185 from Sub028/master
Weather config enhancement
2020-12-08 15:51:41 +01:00
Michael Teeuw
ce46fb5384 Fix Prettier Issue 2020-12-08 15:43:41 +01:00
Michael Teeuw
43b33cb6de Fix prettier issue. 2020-12-08 15:42:01 +01:00
Michael Teeuw
0344399253 Fix prettier issue. 2020-12-08 15:40:46 +01:00
Michael Teeuw
3a9b154cb2 Fix code style issue. 2020-12-08 15:38:07 +01:00
Michael Teeuw
1074fbacfe Merge pull request #2215 from Alvinger/limitDays
New option "limitDays" - limit the number of discrete days to be displayed
2020-12-08 15:24:04 +01:00
Michael Teeuw
00ff3ab380 Merge branch 'develop' into limitDays 2020-12-08 15:23:54 +01:00
Michael Teeuw
db874a011c Merge pull request #2214 from Alvinger/customEvents
Custom events - Use custom symbol/color based on keyword in title
2020-12-08 15:21:39 +01:00
Michael Teeuw
35a2839a2f Merge branch 'develop' into customEvents 2020-12-08 15:21:29 +01:00
Michael Teeuw
cdb9b9bb87 Merge branch 'develop' into cal-again2 2020-12-08 15:17:45 +01:00
Michael Teeuw
87a3e4d440 Merge branch 'develop' into limitDays 2020-12-08 15:16:21 +01:00
Michael Teeuw
284bed677e Merge pull request #2209 from jakobsarwary1/patch-1
Create ps.json
2020-12-08 15:13:59 +01:00
Michael Teeuw
667be460e5 Merge pull request #2210 from rejas/issue-2022_catch-ical-parsing-errors
Catch errors when parsing calendar data with ical
2020-12-08 15:13:42 +01:00
Michael Teeuw
c49386bb58 Merge branch 'develop' into issue-2022_catch-ical-parsing-errors 2020-12-08 15:13:34 +01:00
Michael Teeuw
adb50a9623 Merge pull request #2208 from rejas/issue-2199_console-debug
Add timestamp to Log.debug
2020-12-08 15:12:39 +01:00
Michael Teeuw
ac1d2372f4 Merge branch 'develop' into issue-2199_console-debug 2020-12-08 15:12:32 +01:00
Michael Teeuw
f54690c829 Merge pull request #2206 from Alvinger/calendar-enhance
Calendar fixes and updates
2020-12-08 15:11:32 +01:00
Michael Teeuw
053f9c34aa Merge pull request #2205 from marvai-vgtu/patch-1
Update lt.json
2020-12-08 15:11:03 +01:00
Michael Teeuw
e7dd2b4aee Merge pull request #2187 from AndyPoms/weatherbit
Add support for Weatherbit in the Weather Module
2020-12-08 15:10:45 +01:00
sam detweiler
6f82f9e01b update changelog 2020-12-07 07:27:40 -06:00
sam detweiler
d531730dc1 Merge pull request #1 from MichMich/develop
sync Develop
2020-12-07 07:08:30 -06:00
Karsten Hassel
00bdf6aaa6 update depencencies 2020-12-04 22:42:56 +01:00
Johan Alvinger
97f3514677 Bugfix after Travis CI error (redeclaring variables) 2020-11-30 16:48:07 +01:00
sam detweiler
137facf95a Merge branch 'develop' into cal-again2 2020-11-28 19:23:15 -06:00
Sam Detweiler
8afba3a5c4 fix between.from to use now, instead of yesterday for non-full day events, unless includePastEvents:true 2020-11-28 19:19:52 -06:00
Sam Detweiler
9c5383dc37 fix between.from to use now, instead of yesterday for non-full day events, unless includePastEvents:true 2020-11-28 19:13:56 -06:00
Johan Alvinger
260bc9664e Fixed variable redeclaration 2020-11-28 14:05:35 +01:00
Johan Alvinger
655ca83356 New option "limitDays" - limit the number of discreet days to be displayed 2020-11-28 14:00:38 +01:00
Johan Alvinger
472bf1665c New option "limitDays" - limit the number of discreet days to be displayed 2020-11-28 13:59:13 +01:00
Johan Alvinger
db129cc19b Added "customEvents" to changelog 2020-11-28 13:19:04 +01:00
Johan Alvinger
b735f8a524 New option "customEvents"
Use custom symbol and/or color based on keyword in event titles
2020-11-28 13:17:14 +01:00
Johan Alvinger
1e34764588 coloredEvents should also color the symbol if that is displayed 2020-11-26 17:14:59 +01:00
Johan Alvinger
99aaae491c Reverted changes to test case for calendar 2020-11-25 23:35:01 +01:00
Johan Alvinger
f288581c69 Reverted changes to test case for calendar 2020-11-25 23:31:26 +01:00
Johan Alvinger
3c5d50bce9 Include all past events (if broadcastPastEvents set) and up to maximumEntries of current or upcoming events 2020-11-25 23:29:10 +01:00
Johan Alvinger
a01f08391b Removed test on maximumEntries 2020-11-25 22:45:38 +01:00
Johan Alvinger
51a1399bca Change custom calendar test to not include past events 2020-11-25 22:36:00 +01:00
Johan Alvinger
b735cb96a0 Fetch maximumEntries of current events (and all past events if
broadcastPastEvents is true)
2020-11-25 21:59:58 +01:00
Johan Alvinger
d00c25e107 Fetch maximumEntries of current events (and all past events if broadcastPastEvents is true) 2020-11-25 21:53:34 +01:00
Johan Alvinger
8a5e87b116 All events from the beginning of today were fetched but we only want
the ones that are ongoing or upcoming.
2020-11-24 23:17:26 +01:00
Johan Alvinger
2779d19d5c All events from the beginning of today were fetched but we only want the ones that are ongoing or upcoming. 2020-11-24 23:06:41 +01:00
rejas
38d4a8b198 Update CHANGELOG 2020-11-24 21:33:14 +01:00
rejas
ccf98c0c22 Surround ical parsing with try/catch to catch unknown bugs 2020-11-24 21:32:16 +01:00
Jakob Sarwary
bd0d91d1b4 Create ps.json
ISO 639-1 Language Code: ps
ISO 639-2 Language Code: pus
English name of Language: Pushto; Pashto
French name of Language: pachto

Pashto, sometimes spelled Pukhto or Pakhto, is an Eastern Iranian language of the Indo-European family. It is known in Persian literature as Afghani. Speakers of the language are called Pashtuns or Pukhtuns/Pakhtuns. Pashto and Dari are the two official languages of Afghanistan.
Native speakers: 40-60 million

https://g.co/kgs/y4xaLh
2020-11-24 20:20:07 +01:00
Johan Alvinger
056f3a6ccb limitDays and coloredEvents are now only module-wide options, not per calendar. 2020-11-24 15:41:28 +01:00
Johan Alvinger
20a50f8382 Reverted changes in custom.js for testing 2020-11-24 14:57:51 +01:00
Johan Alvinger
21284e7795 Reverted change in calendarfetcher so events are limited to maximumEntries 2020-11-24 14:52:08 +01:00
Johan Alvinger
e0ceed5a63 Correct error in custom.js in calendar tests 2020-11-24 12:52:21 +01:00
veeck
958a2ee6ec Update CHANGELOG 2020-11-24 09:56:54 +01:00
veeck
ea264cb15e Update console-stamp to latest version and configure it 2020-11-24 09:54:59 +01:00
Johan Alvinger
d8f19e631c Fixed variable declarations to pass Travic CI check 2020-11-24 01:20:19 +01:00
Johan Alvinger
ce5c0ed5ba Fixed typo in condition 2020-11-24 00:37:21 +01:00
Johan Alvinger
839ca9ecfb Lines that were commented out has been removed 2020-11-24 00:23:55 +01:00
Johan Alvinger
720bc12c00 Changelog updated 2020-11-24 00:13:07 +01:00
Johan Alvinger
1ba845fb06 Make calendarfetcher return all events, not just a slice 2020-11-23 21:53:20 +01:00
Johan Alvinger
e86fa9d24a Revised handling of timeFormat "absolute" and "relative".
Added new option "coloredEvents" which contain an array with keyword and color for coloring events matching keyword
2020-11-23 21:48:34 +01:00
marvai-vgtu
e348a61085 Update CHANGELOG.md 2020-11-22 14:02:21 +02:00
marvai-vgtu
2f70366299 Update CHANGELOG.md 2020-11-22 14:00:41 +02:00
marvai-vgtu
c466b20558 Update lt.json 2020-11-22 13:56:24 +02:00
marvai-vgtu
021f8d25a5 Update lt.json
Geographic translation changes with "FEELS" change
2020-11-22 13:54:31 +02:00
Johan Alvinger
a19c3a43d8 New option "limitDays" that will limit the number of days displayed. 2020-11-21 18:03:34 +01:00
Andrew
819923e66d Merge branch 'develop' into weatherbit 2020-11-20 15:09:34 -05:00
Michael Teeuw
6c3100e250 Merge pull request #2202 from sdetweil/cal-again
fix full day events east of UTC start time and duration
2020-11-20 20:39:59 +01:00
Andrew
1065eda47f Update CHANGELOG.md 2020-11-20 14:37:02 -05:00
Andrew
afd676a958 Merge branch 'develop' into weatherbit 2020-11-16 18:29:11 -05:00
Sam Detweiler
12405b66d0 fix full date start time and duration, east of UTC, make getCorrection more understandable with variable name changes 2020-11-16 13:49:44 -06:00
Sam Detweiler
ecd0b6fa83 Merge branch 'develop' of https://github.com/MichMich/MagicMirror into cal-again 2020-11-16 09:50:09 -06:00
Sam Detweiler
3a8587378c fix full date start time and duration, east of UTC, make getCorrection more understandable with variable name changes 2020-11-16 10:32:29 -05:00
Michael Teeuw
a05c08ed48 Merge pull request #2196 from ashishtank/develop
Added Hindi and Gujarati translations
2020-11-16 16:29:59 +01:00
Sam Detweiler
469a90787b fix full date start time and duration, east of UTC, make getCorrection more understandable with variable name changes 2020-11-16 10:23:33 -05:00
Sam Detweiler
9e5a9b5ced fix full date start time and duration, east of UTC, make getCorrection more understandable with variable name changes 2020-11-16 10:17:48 -05:00
Michael Teeuw
f311ba3f7c Merge branch 'develop' into develop 2020-11-15 20:14:37 +01:00
Michael Teeuw
fc68321bd5 Merge branch 'develop' into develop 2020-11-15 20:13:58 +01:00
Michael Teeuw
8a4173d0ad Merge pull request #2175 from jakemulley/packages
Update npm packages and resolve breaking changes from packages
2020-11-15 20:06:40 +01:00
Michael Teeuw
90af31cb2f Merge branch 'develop' into develop 2020-11-15 20:03:10 +01:00
Michael Teeuw
61bcd9337b Merge pull request #2193 from mirontoli/develop
Add Chuvash translation
2020-11-15 20:01:40 +01:00
Michael Teeuw
7944045893 Merge branch 'develop' into weatherbit 2020-11-15 19:59:12 +01:00
Michael Teeuw
2c3f83c70d Merge pull request #2186 from rejas/use_debug
Use Log.debug where applicable
2020-11-15 19:58:06 +01:00
Andrew
92ab705ff5 Update weatherbit.js
Post npm lint:prettier
2020-11-14 18:32:18 -05:00
Andrew
2951f0c40c Update weatherbit.js 2020-11-14 18:28:20 -05:00
Andrew
8a23bccb70 Update CHANGELOG.md 2020-11-14 18:20:02 -05:00
Ashish Tank
aa6ad01fb9 Change log 2020-11-11 16:12:03 +01:00
Ashish Tank
ef325896c5 Merge branch 'develop' of https://github.com/ashishtank/MagicMirror into develop
# Conflicts:
#	CHANGELOG.md
2020-11-11 16:03:10 +01:00
Ashish Tank
a270c73d7c Hindi and Gujarati Language 2020-11-11 16:01:03 +01:00
ashishtank
16feda895d Merge pull request #5 from MichMich/develop
Develop merge
2020-11-11 14:46:18 +01:00
Anatoly Mironov
874a50d142 update changelog for Chuvash translation addition 2020-11-10 23:09:50 +01:00
Anatoly Mironov
ae32645470 improve Chuvash translation 2020-11-10 23:05:28 +01:00
Anatoly Mironov
d4412fe06f Add Chuvash 2020-11-10 23:01:08 +01:00
Anatoly Mironov
b42c05fb56 Create cv.json 2020-11-10 23:00:04 +01:00
Jake Mulley
8d0da61bd4 Allow node-ical minor version upgrades again 2020-11-10 20:55:33 +00:00
Jake Mulley
8b8be261cd Update packages 2020-11-10 20:40:18 +00:00
Jake Mulley
2b6ceed897 Sync package-lock.json 2020-11-08 21:27:47 +00:00
Jake Mulley
8abca801a2 Revert node-ical to 0.12.2 2020-11-08 21:22:11 +00:00
Jake Mulley
1eae4425d8 Merge branch 'develop' into packages 2020-11-08 21:21:31 +00:00
Jake Mulley
a6386bd60e Update npm packages and resolve package changes 2020-11-08 21:18:19 +00:00
Aurélien Veillard
1460f002ab Add new parameter "useKmh" to weather module
Add new parameter "useKmh" to weather module for displaying wind speed as km/h instead of m/s when windUnits is set to metric.
2020-11-08 14:01:02 +01:00
Aurélien Veillard
0d6f736c2c Fix windspeed convertion error in ukmetoffice weather provider
Fix windspeed convertion error from mph to m/s in ukmetoffice weather provider (#2189)
2020-11-08 12:37:21 +01:00
Andrew
da88e11d04 Add files via upload
Adds support for Weatherbit.io

API Key needed, based on existing classes for Dark Sky and Weather.gov
2020-11-07 20:15:54 -05:00
rejas
0a58767563 Use Log.debug where applicable 2020-11-07 11:59:01 +01:00
Aurélien Veillard
198525f2ce Weather config enhancement
Add new parameter 'useKmh' to display wind speed in km/h instead of m/s.
2020-11-07 09:54:13 +01:00
Michael Teeuw
3220702034 Merge pull request #2178 from sdetweil/fix-cal2
Fix calendar when no DTEND record in event
2020-11-06 21:48:59 +01:00
Michael Teeuw
262711a127 Merge pull request #2184 from rejas/debug_log_level
Add new log level "debug"
2020-11-06 21:47:41 +01:00
veeck
078438442a Cleanup some log levels 2020-11-06 11:47:29 +01:00
veeck
5ac20d65ac Add new log level "debug" for such a purpose 2020-11-06 11:47:09 +01:00
Sam Detweiler
5d2f706c32 fix time offset calcs for any timezone 2020-11-01 09:05:33 -06:00
Sam Detweiler
844a59d880 one more offset only test 2020-10-28 13:10:24 -05:00
Sam Detweiler
82c742f964 fix prettier problems, was run on commit before 2020-10-28 16:55:53 +01:00
Sam Detweiler
b73cfd8a60 fix time adjustment routine 2020-10-28 16:42:20 +01:00
Sam Detweiler
8466ff0c1a change debug logging from console. to Log. 2020-10-28 09:42:00 -05:00
Sam Detweiler
1e0fc7eb0d add package-lock for test 2020-10-28 09:38:31 -05:00
Sam Detweiler
74b0c3ea57 update node-ical dependency version 2020-10-28 09:32:38 -05:00
Sam Detweiler
a735275864 fix full day recurring events east of UTC for RRULE/Luxon confusion, also update windows zones to include old offset based strings (UTC+05:00) US Eastern and Canada 2020-10-28 09:31:05 -05:00
Sam Detweiler
8a1d46deb4 fix fullday event compare operator 2020-10-26 12:52:32 -05:00
Sam Detweiler
b624aeec45 fix the fullDayEvent checker function 2020-10-26 12:41:03 -05:00
Sam Detweiler
e83b4d7d42 fix calendar when event has no DTEND record 2020-10-26 09:55:35 -05:00
Michael Teeuw
da302fce32 Merge pull request #2158 from flopp999/patch-2
Weather - forecast change day of week
2020-10-20 09:34:16 +02:00
flopp999
e09fec56e2 Update weather_spec.js
try to change the default to Today and Tomorrow
2020-10-20 07:26:39 +02:00
sam detweiler
568f952573 Merge pull request #3 from MichMich/develop
sync
2020-10-16 14:47:40 -05:00
Michael Teeuw
fc1e488391 Merge pull request #2169 from bluemanos/patch-1
A space after icon of sunrise and sunset
2020-10-16 13:21:43 +02:00
Szymon Bluma
5781c258e0 A space after icon of sunrise and sunset 2020-10-16 13:10:02 +02:00
Michael Teeuw
13c542aeda Merge pull request #2167 from MichMich/fix-greek-translation
Fix greek translation. Rename GR to EL. (Fixes: #2155)
2020-10-15 09:39:50 +02:00
Michael Teeuw
02148f68e5 Rename greek translation. (#2155) 2020-10-15 09:26:56 +02:00
Michael Teeuw
3c84002abd Fix linter issues. 2020-10-15 09:20:44 +02:00
sam detweiler
01aa57e493 Merge pull request #2 from MichMich/develop
resynch after revert
2020-10-13 10:13:47 -05:00
Michael Teeuw
af09b4214a Merge pull request #2165 from MichMich/revert-2161-develop
Revert "Fixes the un-hide problem with currentwether and weatherforcast modules."
2020-10-13 16:15:16 +02:00
Michael Teeuw
43b6c71205 Revert "Fixes the un-hide problem with currentwether and weatherforcast modules." 2020-10-13 16:14:52 +02:00
sam detweiler
480e1adfbf Merge pull request #1 from MichMich/develop
synch fork
2020-10-13 08:51:10 -05:00
Michael Teeuw
8c04712784 Merge pull request #2162 from sdetweil/fixfetch
Fix calendar subsequent fetch timing with multiple calendar entries
2020-10-13 09:13:28 +02:00
Michael Teeuw
0c61ba8f2d Merge pull request #2161 from Snille/develop
Fixes the un-hide problem with currentwether and weatherforcast modules.
2020-10-13 09:13:05 +02:00
Sam Detweiler
485f662d75 revert fetcher filtering results 2020-10-12 10:12:47 -05:00
Sam Detweiler
26caeec0c1 Merge branch 'fixfetch' of https://github.com/sdetweil/MagicMirror into fixfetch 2020-10-12 09:02:15 -05:00
Sam Detweiler
4a7cb88a3e typo, remove dead code, comments, fix recurring refresh, let fetcher handle it 2020-10-12 09:01:50 -05:00
flopp999
d11696015d Update forecast.njk 2020-10-12 09:10:51 +02:00
flopp999
3b76ca4f9b Update forecast.njk 2020-10-12 09:09:42 +02:00
sam detweiler
6f3239d514 Merge branch 'develop' into fixfetch 2020-10-11 22:46:31 -05:00
Sam Detweiler
e8f60d39de fix subsequent calendar fetcher timing 2020-10-11 22:43:11 -05:00
Sam Detweiler
a3bad8aec4 fix subsequent calendar fetcher timing 2020-10-11 22:39:42 -05:00
Erik Pettersson
644aa26b75 Update CHANGELOG.md
Added the "currentwather" and "watherforcast" update show fixes information.
2020-10-11 21:41:59 +02:00
Erik Pettersson
ecd9828afc Makes the module stay hidden without a lock.
When this module is hidden from another module, it un-hides itself when it updates itself. With this change it stays hidden, as it should. :)
2020-10-11 21:37:00 +02:00
Erik Pettersson
7462d61e16 Makes the module stay hidden without a lock.
When this module is hidden from another module, it un-hides itself when it updates itself. With this change it stays hidden, as it should. :)
2020-10-11 21:35:42 +02:00
Ashish Tank
b95ef7250d Merge branch 'develop' of https://github.com/ashishtank/MagicMirror into develop 2020-10-11 15:43:03 +02:00
flopp999
569dec1b0b Update CHANGELOG.md 2020-10-11 09:42:09 +02:00
flopp999
1bc0270d7b Update forecast.njk
change name of day to today and tomorrow
2020-10-11 09:36:52 +02:00
ashishtank
ebfeebc40b Merge pull request #4 from MichMich/develop
Merge from Develop
2020-10-07 15:58:47 +02:00
Michael Teeuw
ec80b25087 Merge pull request #2150 from sdetweil/fixparse
add error handler to json parsing of translation files
2020-10-06 10:23:46 +02:00
Michael Teeuw
7a2278d7b6 Merge branch 'develop' into fixparse 2020-10-06 10:23:34 +02:00
Michael Teeuw
28b93b70fa Merge pull request #2152 from sdetweil/new_cal
update calendar handler code for rrule and daylight/standard time adjustments
2020-10-06 10:19:58 +02:00
Michael Teeuw
0431f45190 Merge branch 'develop' into new_cal 2020-10-06 10:16:08 +02:00
Michael Teeuw
96ce444061 Merge pull request #2154 from sdetweil/fixical
fix node-ical version
2020-10-06 10:15:10 +02:00
sam detweiler
32776e7ca3 Merge branch 'develop' into fixparse 2020-10-05 13:11:55 -05:00
sam detweiler
35d516ad4d Merge branch 'develop' into new_cal 2020-10-05 13:11:11 -05:00
sam detweiler
e6a17d27b9 Merge branch 'develop' into fixical 2020-10-05 13:10:25 -05:00
Sam Detweiler
52edec1b64 fix wrong node-ical version requested, package.json 2020-10-05 12:59:30 -05:00
Sam Detweiler
6440d2289c fix translation files with comments crashing UI 2020-10-05 12:58:10 -05:00
Sam Detweiler
11afadcea8 fix RRULE bad date and add Windows Timezone name support 2020-10-05 12:56:25 -05:00
Sam Detweiler
2e981987f2 resolve conflict 2020-10-05 11:06:21 -05:00
Sam Detweiler
69efca0bdb use Log.error instead of console.log for error message, shows in output 2020-10-05 10:45:07 -05:00
Sam Detweiler
eefb92367e fix node-ical version 2020-10-05 10:02:29 -05:00
Sam Detweiler
8fa96c2836 add error handler to json parsing of translation files 2020-10-04 12:13:08 -05:00
Sam Detweiler
04dae52a6c correct daylight/standard adjustments, windows timezones and east of london rrule bug 2020-10-04 12:10:19 -05:00
Ashish Tank
37c9da1351 Merge branch 'develop' of https://github.com/ashishtank/MagicMirror into develop 2020-10-02 22:01:50 +02:00
ashishtank
86d7ec6270 Merge pull request #3 from MichMich/develop
Sync with Develop
2020-10-02 18:06:24 +02:00
Ashish Tank
82f1e0fe32 Added Hindi and Gujarati languages 2020-10-02 17:51:41 +02:00
Michael Teeuw
6ec0aa8894 Prepare 2.14.0-develop 2020-10-01 12:19:57 +02:00
Michael Teeuw
3dbe8bfbbf Merge pull request #2143 from MichMich/develop
Release 2.13.0
2020-10-01 12:13:38 +02:00
Michael Teeuw
20d82bab78 Prepare 2.13.0 2020-10-01 11:55:18 +02:00
Michael Teeuw
60f6123b64 Merge pull request #2139 from sdetweil/develop
restore ical for 3rd party modules
2020-09-29 17:45:49 +02:00
Sam Detweiler
74887a58f6 remove regression on event duration 2020-09-29 10:33:51 -05:00
Sam Detweiler
76821d16fc restore ical for 3rd party modules 2020-09-24 10:13:25 -05:00
Michael Teeuw
233a6b5e90 Merge pull request #2137 from rejas/md
Update Changelog and Licsense file
2020-09-22 16:16:22 +02:00
Michael Teeuw
a1d9337c81 Merge pull request #2136 from sdetweil/develop
fix package.json
2020-09-22 16:16:08 +02:00
rejas
e3fec6c3e4 Update Changelog and License 2020-09-22 15:47:16 +02:00
Sam Detweiler
5de88b4cc1 remove added lodash 2020-09-22 08:40:40 -05:00
Sam Detweiler
af5e6655e0 fix pull 2020-09-22 08:38:15 -05:00
Sam Detweiler
a8716799c9 fix dependency versioning error 2020-09-22 08:33:21 -05:00
Michael Teeuw
1672027091 Merge pull request #2135 from sdetweil/develop
update to node-ical, handle MS timezones and timezones in general in cal entries
2020-09-22 14:55:27 +02:00
sam detweiler
934ac886cb Merge branch 'develop' into develop 2020-09-22 07:49:19 -05:00
sam detweiler
1b47c4d16f add package-lock 2020-09-22 07:31:02 -05:00
sam detweiler
e8fd906aa1 move calendar to node-ical for ms timezones and timezones in general 2020-09-22 07:25:48 -05:00
Michael Teeuw
57b6ea1297 Merge pull request #2134 from bugsounet/patch-1
New ConfigMerge code purpose
2020-09-22 10:54:35 +02:00
bugsounet
102ff15a99 Checking formatting...
All matched files use Prettier code style!
2020-09-22 00:26:24 +02:00
Cédric
19148cfc84 Update loader.js 2020-09-22 00:04:05 +02:00
Cédric
97c2bab58a apply ConfigMerge() if configDeepMerge: true 2020-09-21 23:21:43 +02:00
Cédric
1f473228ce New purpose for configMerge() 2020-09-21 23:17:41 +02:00
Cédric
340a8e4176 (test react for in my repo) 2020-09-21 22:36:54 +02:00
Michael Teeuw
67201a52cc Merge pull request #2016 from Travelbacon/develop
Changed "Gevoelstemperatuur" to "Voelt als". This is shorter and save…
2020-09-21 12:48:09 +02:00
Michael Teeuw
30e164fe0a Fix merge conflict. 2020-09-21 12:21:08 +02:00
Michael Teeuw
cfcb20a468 Merge pull request #2133 from rejas/dependencies
Cleanup dependencies
2020-09-21 12:18:37 +02:00
Joris
24eac7ef41 For the mergne 2020-09-20 20:57:20 +02:00
ashishtank
94162f1b45 Merge pull request #1 from MichMich/develop
Catching up on 19th Sep 2020
2020-09-19 17:20:35 +02:00
rejas
01c19efc0c Update CHANGELOG 2020-09-19 12:30:17 +02:00
rejas
e4f2a8a23b Update dependencies 2020-09-19 12:25:11 +02:00
rejas
89b33d2c62 Move lodash to devDependencies sicne its used only in testing 2020-09-19 10:50:06 +02:00
rejas
3ced35a2f8 Dont generally use latest versions of dependencies 2020-09-19 10:49:59 +02:00
Michael Teeuw
fd4576b234 Merge pull request #2081 from bryanzzhu/bryanzzhu-weather
adds current, hourly, and daily forecasts to the Weather module (OpenWeatherMap One Call API)
2020-09-18 12:28:20 +02:00
Michael Teeuw
812ad829c9 Merge pull request #2125 from MichMich/dependabot/npm_and_yarn/node-fetch-2.6.1
Bump node-fetch from 2.6.0 to 2.6.1
2020-09-18 12:27:57 +02:00
Michael Teeuw
207a9ba723 Merge branch 'develop' into bryanzzhu-weather 2020-09-18 12:15:44 +02:00
Michael Teeuw
f9bf17d701 Merge pull request #2105 from rejas/tests
Cleanup clock tests
2020-09-18 12:12:29 +02:00
dependabot[bot]
43b96ccd1e Bump node-fetch from 2.6.0 to 2.6.1
Bumps [node-fetch](https://github.com/bitinn/node-fetch) from 2.6.0 to 2.6.1.
- [Release notes](https://github.com/bitinn/node-fetch/releases)
- [Changelog](https://github.com/node-fetch/node-fetch/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/bitinn/node-fetch/compare/v2.6.0...v2.6.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-10 22:35:52 +00:00
rejas
14e8cdd9b2 Update Changelog 2020-09-02 13:42:35 +02:00
rejas
259068b860 Remove unused dependency, update eslint 2020-09-02 13:42:35 +02:00
rejas
a1a4192835 Fix clock test, remove now unneeded dependency 2020-09-02 13:42:35 +02:00
rejas
45f09dcc8c Add test for analog clock face 2020-09-02 13:42:35 +02:00
Michael Teeuw
15c3f11f4a Merge pull request #2121 from sdetweil/develop
fix full day recurring events not showing
2020-09-02 09:04:08 +02:00
Michael Teeuw
1a21027850 Fix Prettier issues. 2020-09-02 09:03:46 +02:00
Michael Teeuw
8427a9fc68 Merge pull request #2103 from rejas/issue_1985
Fix "undefined" in weather modules header.
2020-09-02 08:59:53 +02:00
Sam Detweiler
f09b89f975 fix , prettier not run 2020-09-01 15:13:42 -05:00
sam detweiler
7bec84f767 fix formatting, prettier did not run 2020-09-01 21:00:14 +01:00
sam detweiler
8f4cbcf817 Merge branch 'develop' of https://github.com/sdetweil/MagicMirror into develop 2020-09-01 14:41:09 +01:00
sam detweiler
c3382274a2 remove old master branch code 2020-09-01 14:40:07 +01:00
sam detweiler
493055f861 Merge branch 'develop' into develop 2020-09-01 08:15:24 -05:00
sam detweiler
11fbbd49f3 add changelog 2020-09-01 14:10:25 +01:00
sam detweiler
8ce37d53cd fix recurring full date events 2020-09-01 14:04:35 +01:00
sam detweiler
baa4012872 Merge pull request #98 from MichMich/master
catch up
2020-08-31 19:15:08 -05:00
rejas
16c5eba2be Update Changelog 2020-08-13 21:51:18 +02:00
rejas
ec08cb32aa Set visibility of header more explicitly 2020-08-13 21:51:18 +02:00
rejas
86fb1b938b Check for undefined header data in weather modules 2020-08-13 21:51:18 +02:00
rejas
5aa7097a6e Add test for displaying the header 2020-08-13 21:51:18 +02:00
rejas
fd3f520e95 Fix typos 2020-08-13 21:51:18 +02:00
rejas
49ff92892f Warn user if he uses the sample value in the config for the apiid 2020-08-13 21:51:18 +02:00
Michael Teeuw
904f5a2656 Merge pull request #2112 from rejas/issue_2109
Fix config check after merge of prettier
2020-08-13 21:44:08 +02:00
Michael Teeuw
bb105dd832 Merge pull request #2101 from rejas/jsdoc
Cleanup jsdoc comments
2020-08-13 21:43:27 +02:00
Michael Teeuw
fd934c0514 Merge pull request #2100 from Bee-Mar/develop
Added --dry-run to fetch call within updatednotification module
2020-08-13 21:42:38 +02:00
rejas
66609428a2 Remove thrown Errors and add some color to the ouput 2020-08-08 22:16:27 +02:00
rejas
0056e0bc6d Update CHANGELOG 2020-08-07 09:58:55 +02:00
rejas
056b66a764 Dont use the prettier based config for verifying the syntax 2020-08-07 09:57:06 +02:00
rejas
3438a5a374 Cleanup newsfeed jsdoc 2020-08-03 11:36:29 +02:00
rejas
9f3806dabf Update eslint jsdoc plugin 2020-08-03 11:20:11 +02:00
rejas
1d4d5cc4e7 Cleanup calendar jsdoc 2020-08-03 11:19:54 +02:00
Bryan Zhu
3901543697 readded processing for numEntries option that I accidentally took out 2020-08-02 00:22:19 -04:00
Bryan Zhu
a4d73e2a67 amended code according to pull request reviews 2020-08-01 17:39:58 -04:00
Bryan Zhu
f6854f58ff ran prettier on weather module code 2020-08-01 14:03:48 -04:00
Bryan Zhu
ad7f7d2890 Merge branch 'bryanzzhu-weather' of https://github.com/bryanzzhu/MagicMirror into bryanzzhu-weather 2020-08-01 13:33:21 -04:00
Bryan Zhu
498db63cac Merge branch 'develop' of https://github.com/MichMich/MagicMirror into develop 2020-08-01 13:32:29 -04:00
rejas
05659820d0 Minor cleanups 2020-08-01 17:06:56 +02:00
rejas
02779ef725 Cleanup main jsdoc 2020-08-01 17:06:46 +02:00
rejas
935c9b6a42 Cleanup loader jsdoc 2020-08-01 16:38:42 +02:00
rejas
522f7644a3 Add typedef for Module, use it in other jsdocs 2020-08-01 16:31:42 +02:00
Bryan Zhu
b1a67d1fc5 integrated onecall usage into existing types 2020-08-01 02:59:08 -04:00
rejas
0674c0aff6 Updated changelog 2020-07-30 13:00:02 +02:00
rejas
c8664d5952 Cleanup weatherprovider jsdoc 2020-07-30 12:58:35 +02:00
rejas
8ce1e2c956 Cleanup calendar jsdoc 2020-07-30 12:54:39 +02:00
rejas
90cca28e01 Cleanup clock jsdoc 2020-07-30 09:36:01 +02:00
rejas
e489b7101c Cleanup notificationFx jsdoc 2020-07-30 09:33:19 +02:00
rejas
eee64c8064 Cleanup clientonly jsdoc 2020-07-29 22:13:19 +02:00
Brandon Marlowe
a0d4e8dafc updated changelog 2020-07-28 23:55:39 -04:00
Brandon Marlowe
e6b94df06d Merge branch 'mmpm-integration' of github.com:Bee-Mar/MagicMirror into develop 2020-07-28 23:53:03 -04:00
Brandon Marlowe
6f1c7d6253 added --dry-run option to prevent updatenotification module from consuming fetch result 2020-07-28 23:51:38 -04:00
rejas
2d5a19b676 Cleanup module jsdoc 2020-07-28 17:29:21 +02:00
rejas
d1e8dded68 Cleanup class jsdoc 2020-07-28 17:29:16 +02:00
rejas
9888f66c84 Cleanup check_config jsdoc 2020-07-28 17:29:10 +02:00
rejas
5ec51d0ccc Cleanup app jsdoc 2020-07-28 16:49:02 +02:00
rejas
79f9331073 Cleanup translator jsdoc 2020-07-27 21:37:08 +02:00
rejas
43bcf4ab98 Run eslint over files, see what gets fixed automatically and clean up 2020-07-27 14:25:43 +02:00
rejas
f4eae72c48 Install eslint jsdoc plugin 2020-07-27 13:10:07 +02:00
Michael Teeuw
9d14d3e5b7 Merge pull request #2095 from rejas/issue_2072_take_2
Fix clock face-006
2020-07-20 19:27:55 +02:00
rejas
26d3141489 Fix face-006 too 2020-07-20 19:13:12 +02:00
Michael Teeuw
5c44f51d6d Merge pull request #2079 from rejas/issue_2023
Added config option to calendar-icons for recurring- and fullday-events
2020-07-19 20:41:22 +02:00
rejas
50f3f32ba8 Add tests for custom icons 2020-07-19 11:54:03 +02:00
rejas
8fa858ca8c Cleanup test descriptions 2020-07-18 22:16:44 +02:00
rejas
8b1d1671f7 Add test for changing the calendar symbol 2020-07-18 21:10:36 +02:00
rejas
73aa35ea2c Pass maximumEntries for each calender to fetcher
Fixes failing test
2020-07-18 17:51:21 +02:00
rejas
a391445e5f Add test setup for custom calendar configuration 2020-07-18 13:58:05 +02:00
rejas
530c5d416a Remove now unused test data 2020-07-18 11:32:01 +02:00
rejas
319a13c0ef Remove orphaned ical entry 2020-07-17 18:20:48 +02:00
rejas
ec2fedd797 Make sure default symbol is first from right-to-left 2020-07-17 18:00:27 +02:00
rejas
7489d19784 Merge calendar symbols 2020-07-17 18:00:27 +02:00
rejas
3b5a0e8d66 Added config option to calendar-icons for recurring- and fullday-events 2020-07-17 18:00:27 +02:00
Michael Teeuw
29ed63286c Merge pull request #2089 from rejas/newsfeed
Cleaned up newsfeed module code
2020-07-17 16:28:44 +02:00
Michael Teeuw
108da4b6d4 Merge pull request #2084 from easyas314/wxGov
Wx gov
2020-07-17 16:26:20 +02:00
Michael Teeuw
a00a1c64d1 Merge pull request #2083 from sthuber90/backward-browser-compatability
Backward browser compatability
2020-07-17 16:25:35 +02:00
Michael Teeuw
275825cfe1 Merge pull request #2082 from rejas/istanbul
Add test coverage tool Istanbul
2020-07-17 16:25:00 +02:00
Michael Teeuw
cd2fce56a6 Merge branch 'develop' into istanbul 2020-07-17 16:23:59 +02:00
Michael Teeuw
cab850fb35 Merge pull request #2080 from larryare/develop
Add lithuanian translation
2020-07-17 16:21:19 +02:00
Michael Teeuw
045dc600b0 Merge branch 'develop' into develop 2020-07-17 16:21:12 +02:00
Michael Teeuw
1b199c1682 Merge pull request #2077 from oemel09/fix-maxNumberOfDays-2018
Fixes getting only full day forecasts
2020-07-17 16:18:57 +02:00
Michael Teeuw
936fa637ec Merge pull request #2078 from cjbrunner/cbrunner/weatherforecast-openweather-onecall-api-support
weatherforecast OpenWeather onecall api support
2020-07-17 16:18:17 +02:00
rejas
b9ccb7a892 Add new test to see if coverage improves
it does :-)

  calendar.js                       |    8.97 |     9.57 |   11.54 |     8.7 | 63-413,446-659,690-693,699,733-771

->

  calendar.js                       |    9.97 |    10.64 |   11.54 |     9.7 | 63-413,446-659,699,733-771
2020-07-15 13:15:03 +02:00
rejas
95b33be3cf Update vendor dependencies 2020-07-15 12:14:48 +02:00
rejas
cde27b89c2 Update CHANGELOG 2020-07-14 07:51:19 +02:00
rejas
476e52e004 Use let/const and arrow functions 2020-07-12 13:21:36 +02:00
rejas
8dc88fe64b Remove unused helper function and its unit test 2020-07-12 10:32:16 +02:00
rejas
d00da790af Move configuration change warning into general translation 2020-07-12 08:25:07 +02:00
rejas
c61fac75e4 Rename Newsfeed Fetcher for clarity 2020-07-12 08:10:43 +02:00
easyas314
e949af6fd5 revert package.json 2020-07-11 16:13:31 -04:00
easyas314
c1e38b917e update CHANGELOG 2020-07-11 15:06:24 -04:00
easyas314
1244121216 change weathergov.js for multi-URL api 2020-07-11 15:01:16 -04:00
easyas314
46543daa13 Merge branch 'wxGov' of github.com:easyas314/MagicMirror into wxGov 2020-07-11 14:56:35 -04:00
Brad Simmons
6277384bf9 Merge pull request #3 from MichMich/develop
Merge MM/Develop
2020-07-11 14:54:22 -04:00
Stephan Huber
17549fed9c Update CHANGELOG.md 2020-07-11 08:25:01 +02:00
Stephan Huber
377e42affc fix display style for older browsers 2020-07-10 23:12:17 +02:00
Stephan Huber
0ac5d56865 must be const for backward compatibility 2020-07-10 23:03:36 +02:00
rejas
b59ee6ad7e Update dependencies 2020-07-09 22:43:08 +02:00
rejas
2d7c5a827f Cleanup test expectation 2020-07-09 20:44:52 +02:00
Veeck
85c32ef843 Use const in test requires 2020-07-09 20:30:43 +02:00
rejas
dc089d7db9 Remove stripCommentsFromJson code and test
Since we dont allow comments in the json anymore anyway
2020-07-09 20:30:26 +02:00
rejas
d570f910f8 Added test coverage with istanbul 2020-07-09 20:29:38 +02:00
Bryan Zhu
8f2731911b added changelog entry 2020-07-08 23:12:01 -04:00
Bryan Zhu
9d22420027 Merge branch 'develop' of https://github.com/MichMich/MagicMirror into bryanzzhu-weather 2020-07-08 23:06:29 -04:00
larryare
029da3791b Update CHANGELOG.md
Added lithuanian language entry.
2020-07-08 23:57:57 +03:00
Laurynas Kerezius
caa51b1029 Add lithuanian language 2020-07-08 20:52:24 +00:00
Chris Brunner
2973a03d40 Prettier... 2020-07-08 10:11:12 -07:00
Chris Brunner
679d464f4c Update chagelog 2020-07-08 09:53:50 -07:00
Chris Brunner
93c0787efd Add support for OpenWeather onecall API 2020-07-08 09:48:03 -07:00
oemel09
a1708a1469 Fixes getting only full day forecasts 2020-07-08 10:19:49 +02:00
Michael Teeuw
862bf78e63 Merge pull request #2071 from chamakura/develop
Fixing double conversion of time between UTC and local timezone
2020-07-05 22:06:59 +02:00
Michael Teeuw
8c01df50a2 Merge pull request #2073 from rejas/issue_2072
Fix incorrect namespace links in svg clockfaces
2020-07-05 12:42:42 +02:00
rejas
921932920d Fix incorrect namespace links in svg clockfaces
Fixes #2072
2020-07-05 11:58:37 +02:00
chamakura
d021c9b4ae Removing debug log statements 2020-07-04 20:23:09 -07:00
chamakura
309a18e9c0 Fixing double conversion of time between UTC and local timezone 2020-07-04 20:12:34 -07:00
Michael Teeuw
73322c96a6 Merge pull request #2070 from rejas/fix_log_level
Fix log level
2020-07-04 22:10:16 +02:00
rejas
121e7db330 Add CHANGELOG entry 2020-07-04 22:03:13 +02:00
rejas
9f5e1b59fb Set logLevel after loading config 2020-07-04 22:02:39 +02:00
Michael Teeuw
3282ed4fea Add TODO 2020-07-04 21:49:14 +02:00
Michael Teeuw
c7d9192f18 Merge pull request #2060 from bryanzzhu/bryanzzhu-weather-patch
fixed minor typo (apiKey vs. appid) in weather.js
2020-07-04 21:47:52 +02:00
Michael Teeuw
bd0f707aed Add changelog. 2020-07-04 21:40:31 +02:00
Michael Teeuw
8b30634ebe Merge branch 'develop' into pr/bryanzzhu/2060 2020-07-04 21:38:29 +02:00
Michael Teeuw
ca8acb14f1 Merge pull request #2057 from wolfen351/wolfen351-patch-1
Correct calendar display - account for current timezone
2020-07-04 21:34:32 +02:00
Michael Teeuw
26b8f8d0d5 Merge branch 'develop' into pr/bryanzzhu/2060 2020-07-04 21:33:50 +02:00
Michael Teeuw
a066153556 Add updated changelog. 2020-07-04 21:26:22 +02:00
Michael Teeuw
2ee7131c28 Merge branch 'develop' into pr/wolfen351/2057 2020-07-04 21:24:48 +02:00
Michael Teeuw
cb1f65732e Merge branch 'develop' into pr/wolfen351/2057 2020-07-04 21:15:14 +02:00
Michael Teeuw
37237d9c10 Merge pull request #2066 from oemel09/fix-maxNumberOfDays-2018
Adjusts maxNumberOfDays depending on the endpoint
2020-07-03 22:02:26 +02:00
Michael Teeuw
92cc41dec6 Fix linting issue. 2020-07-03 21:53:18 +02:00
oemel09
246dc663c4 Applies suggestions from prettier, floor instead of ceil 2020-07-02 22:22:04 +02:00
oemel09
4ed3235590 Adds fix to changelog 2020-07-02 20:56:48 +02:00
oemel09
0939e405b4 Adjusts maxNumberOfDays depending on the endpoint 2020-07-02 20:54:20 +02:00
Bryan Zhu
584c51ef56 Merge branch 'develop' of https://github.com/MichMich/MagicMirror into bryanzzhu-weather 2020-07-01 14:49:05 -04:00
Michael Teeuw
26ed05ba3a Prepare 2.13.0-develop 2020-07-01 20:07:12 +02:00
Michael Teeuw
018cb91526 Merge pull request #2064 from MichMich/develop
Release 2.12.0
2020-07-01 20:04:36 +02:00
Michael Teeuw
e16db2233f Fix contributor list. 2020-07-01 19:52:38 +02:00
Michael Teeuw
5d90a08011 Prepare release 2.12.0 2020-07-01 19:47:09 +02:00
Michael Teeuw
2d3849a671 Merge pull request #2061 from ZoneMR/patch-1
Fix #2030 - Clock can be off by a minute
2020-07-01 19:38:11 +02:00
Michael Teeuw
e0dcdc2110 Merge pull request #2063 from lyubomirv/develop
Updated the BG translation
2020-07-01 19:31:08 +02:00
Michael Teeuw
22fcee2a16 Merge branch 'develop' into pr/ZoneMR/2061 2020-07-01 19:29:41 +02:00
Michael Teeuw
8c0141367b Fix EOF issue. 2020-07-01 19:25:00 +02:00
Bryan Zhu
5bb72cfed8 Merge branch 'develop' of https://github.com/MichMich/MagicMirror into bryanzzhu-weather 2020-07-01 08:38:46 -04:00
Michael Teeuw
6d829fa575 Merge pull request #2059 from bryanzzhu/bryanzzhu-currentweather
added config option to hide sunrise/sunset in Current Weather module
2020-07-01 13:16:48 +02:00
Bryan Zhu
85ed1b85ae added functional current, hourly, and daily forecasts via OpenWeatherMap One Call API 2020-07-01 05:08:04 -04:00
Lyubomir Vasilev
c9d0bd2d7f Updated the BG translation 2020-07-01 10:08:41 +03:00
ZoneMR
ea31d34649 Fix #2030 - Clock can be off by a minute
Set minute/second in our model based on the actual time from moment() rather than our own attempt to track the time - which can drift or fail to respond to time changes.

Also, schedule time refreshes to happen 50ms after the minute/second is expected to change - preventing premature fires and rapid re-firings of notifications due to accuracy limits in setTimeout
2020-06-30 19:51:25 +01:00
Bryan Zhu
1d2f929d3f Rename wdataHourly.njk to wdatahourly.njk 2020-06-30 13:21:50 -04:00
Bryan Zhu
ffbf0804d9 added default values for lat and lon 2020-06-30 12:41:14 -04:00
Bryan Zhu
ca0b89ecd3 backtracked apiKey change to appid 2020-06-30 12:36:23 -04:00
Bryan Zhu
dc9d6f6b79 fixed minor typo (apiKey vs. appid) 2020-06-30 12:29:00 -04:00
Bryan Zhu
f73520559e typo and bug fixes 2020-06-30 12:06:16 -04:00
Bryan Zhu
a4df38d963 fixed config parameter typo 2020-06-30 04:18:03 -04:00
Bryan Zhu
4a162543f6 added OpenWeatherMap One Call API function to default Weather module, added wDataHourly type 2020-06-30 02:40:41 -04:00
Bryan Zhu
7a3ea37798 added config option to hide sunrise/sunset in Current Weather module 2020-06-29 12:44:24 -04:00
Michael Teeuw
fdd389d6b7 Merge pull request #2058 from XBCreepinJesus/develop
UK Met Office DataHub weather provider
2020-06-28 13:40:17 +02:00
CreepinJesus
b91fccc0e3 Update metofficedatahub.js 2020-06-28 12:09:16 +01:00
CreepinJesus
d911b075ab Met Office DataHub weather provider 2020-06-28 11:13:44 +01:00
CreepinJesus
4339cdd8a4 Included check for Met Office DataHub provider.
The new Met Office provider also returns precipitation as a probability percentage.
2020-06-28 11:00:10 +01:00
CreepinJesus
dd32d3a492 New Met Office provider
This is a provider for the Met Office's new DataHub API (which will eventually replace their current DataPoint service).
2020-06-28 10:57:19 +01:00
wolfen351
d5caadd906 Correct calendar display - account for current timezone 2020-06-27 19:43:09 +12:00
Michael Teeuw
c4bc3e2687 Merge pull request #2054 from MichMich/revert-pr-2021
Revert PR #2021
2020-06-24 12:36:28 +02:00
Michael Teeuw
2c5909a138 Update CHANGELOG.md 2020-06-24 09:50:25 +02:00
Michael Teeuw
42c13fa584 Update compliments.js 2020-06-24 09:49:14 +02:00
Michael Teeuw
3b442d4bfc Merge pull request #2048 from rejas/issue_1926
Updated ical library to latest version
2020-06-22 18:19:18 +02:00
rejas
9831f81b6e Merge branch 'develop' into issue_1926 2020-06-20 20:43:09 +02:00
Michael Teeuw
d3282506c9 Merge pull request #2052 from chamakura/develop
Bug fix to correctly handle the logic for 'maxEntries' Issue #2050
2020-06-20 20:35:32 +02:00
chamakura
2afff6c432 Updating files to the latest versions from 'develop' branch 2020-06-20 11:18:37 -07:00
chamakura
be3616abe2 Bug fix to correctly handle the logic for 'maxEntries' Issue #2050 2020-06-20 11:01:37 -07:00
rejas
daa6f5051c Use object.entries to iterate over data 2020-06-20 09:01:35 +02:00
rejas
1e5bd98f02 Make calendar debuger use const stuff and add npm script for it 2020-06-20 08:45:46 +02:00
rejas
7e5bfa8dd2 Fix slice parameter type 2020-06-20 08:45:22 +02:00
rejas
53363b0618 Simplify return call to make a better diff 2020-06-20 08:45:03 +02:00
rejas
b2f71a2ce1 Update dependencies 2020-06-20 08:33:19 +02:00
rejas
5d4a575919 Undo switch to fetch, use request like ical did 2020-06-20 08:32:54 +02:00
rejas
7d521ed3ce More var -> let/const conversions 2020-06-18 21:54:51 +02:00
rejas
bb9ad3daa9 Use some const/let instead of var 2020-06-17 21:37:49 +02:00
rejas
442f270ee0 Update CHANGELOG 2020-06-17 21:24:50 +02:00
rejas
7ab74c6cc9 Remove old ical version 2020-06-17 21:17:35 +02:00
rejas
6d60baa2d6 Install latest ical version and use it 2020-06-17 21:17:26 +02:00
Michael Teeuw
6d3308621f Merge pull request #2017 from rejas/config_logger
Make logger a little more configurable
2020-06-14 14:33:06 +02:00
Michael Teeuw
fd49be1d9b Remove unreachable code. 2020-06-14 14:14:30 +02:00
Michael Teeuw
c40a5648dd Merge branch 'develop' into config_logger 2020-06-14 14:02:48 +02:00
Michael Teeuw
d9a8d2627a Merge pull request #2046 from rejas/issue_1928
Throw error when check_config fails
2020-06-14 14:00:19 +02:00
rejas
c7a88e2f12 Throw error when check_config fails 2020-06-14 11:04:11 +02:00
easyas314
9d2d170c2d add additional URLs per API 2020-06-13 11:58:06 -04:00
easyas314
df3aa22c59 current appears to be working 2020-06-04 23:35:32 -04:00
easyas314
a4aabfcdae add the lock file 2020-06-04 19:26:09 -04:00
easyas314
ce99e70bf9 tweak package; my wxgov.js 2020-06-04 19:25:13 -04:00
rejas
963b1aa6b1 Final cleanups I think 2020-06-02 15:05:31 +02:00
rejas
008ac2876b More console -> Logger conversions 2020-06-02 15:05:31 +02:00
rejas
2330b166f6 Totall forgot we need a changelog entry 2020-06-02 15:04:58 +02:00
rejas
23c0e01565 Use logger in node_helpers 2020-06-02 15:04:58 +02:00
Veeck
13073bc98d Lint stuff 2020-06-02 15:03:59 +02:00
Veeck
8c319903dd Cleanup outcommented logging 2020-06-02 15:03:59 +02:00
Veeck
2334cbd78a User logger in checkconfig script 2020-06-02 15:03:59 +02:00
rejas
0cae954f80 Use Log in loader too 2020-06-02 15:03:59 +02:00
rejas
c60446a015 Fix tests 2020-06-02 15:03:59 +02:00
Veeck
d0c6a4ee6d Make logger configurable 2020-06-02 15:03:59 +02:00
rejas
f2d03a511e User logger in node files 2020-06-02 15:03:59 +02:00
rejas
367233c318 Add console-stamp to node-logger 2020-06-02 15:03:59 +02:00
rejas
9461c1692a Add node/browser wrapper around logger 2020-06-02 15:03:59 +02:00
Michael Teeuw
3b32605b5e Merge pull request #2036 from DarthBrento/develop
Fix #1109 - multiple calendar instances with different config
2020-06-02 10:16:09 +02:00
Michael Teeuw
4d21f8d022 Merge branch 'develop' into develop 2020-06-02 09:17:43 +02:00
Michael Teeuw
aac67570d4 Merge pull request #2035 from Ekristoffe/develop
Fix for #2018
2020-06-02 09:17:03 +02:00
Michael Teeuw
5f2c465274 Merge pull request #2032 from radokristof/weather-module
[weather] Ability to hide sun details
2020-06-02 09:15:31 +02:00
Michael Teeuw
fdaa0bc876 Merge pull request #2031 from radokristof/calendar-module
[calendar] Use wrapEvents to also truncate the location
2020-06-02 09:13:42 +02:00
Kristof Rado
a692d6be09 Reworked titleTransform. 2020-06-01 22:25:07 +02:00
Kristof Rado
efbb9648c4 Introduce new function for location title shortening. 2020-06-01 20:23:59 +02:00
Kristof Rado
3d73153e59 Revert "Renamed function"
This reverts commit 6aa0a4a4
2020-06-01 20:19:03 +02:00
DarthBrento
8fa2256fb0 linted 2020-06-01 17:19:41 +02:00
DarthBrento
4fe974e7a8 Check identifier for type, too 2020-06-01 13:31:46 +02:00
DarthBrento
21f76a8f27 Attach identifier to socket notifications to allow multiple instances 2020-06-01 13:14:54 +02:00
DarthBrento
aeb287fa1d Attach identifier to socket notifications to allow multiple instances 2020-06-01 13:12:54 +02:00
DarthBrento
1405e8821c Update calendar.js 2020-06-01 00:38:05 +02:00
DarthBrento
37e31bac5b Update node_helper.js 2020-06-01 00:36:47 +02:00
DarthBrento
d31b696846 updated changelog 2020-06-01 00:24:13 +02:00
Chris
cc01c1f0db Update weatherforecast.js 2020-05-31 11:57:53 +09:00
Chris
4a7076e01c Prettier correction 2020-05-31 11:46:50 +09:00
Chris
d306bb25dc Update CHANGELOG.md 2020-05-31 00:20:42 +09:00
Chris
457c80fe76 Correct #2018
Weather forecast need the maxNumberOfDays as argument &cnt=**
The minimum is 1 and the maximum is 17.
2020-05-31 00:15:54 +09:00
Kristof Rado
bb972f8449 Updated CHANGELOG.md 2020-05-30 00:11:14 +02:00
Kristof Rado
e6ef64968b Disable Sunrise/Sunset in Config option 2020-05-30 00:06:20 +02:00
Kristof Rado
6f3b87cfd1 Merge branch 'develop' into weather-module 2020-05-29 23:56:09 +02:00
Kristof Rado
f449feb3f8 Updated CHANGELOG.md 2020-05-29 23:53:07 +02:00
Kristof Rado
b179c8e2b7 Merge branch 'develop' into calendar-module 2020-05-29 23:50:24 +02:00
Kristof Rado
6aa0a4a47f Renamed function 2020-05-29 23:50:01 +02:00
Kristof Rado
766140f483 Ability to hide sun details 2020-05-28 10:57:01 +02:00
Kristof Rado
52aa8b868a Truncate event title 2020-05-28 10:09:34 +02:00
Michael Teeuw
8a3a4d6fae Merge pull request #2027 from rejas/fix_travis_error
Fix travis error
2020-05-25 20:18:37 +02:00
Veeck
94f212a411 Fix travis error due to merge before prettier run 2020-05-25 20:00:59 +02:00
Michael Teeuw
4e1dce70a3 Merge pull request #2013 from rejas/prettier
Add Prettier for an even cleaner code-experience
2020-05-25 18:46:36 +02:00
Michael Teeuw
bbf48a0dd0 Merge pull request #2021 from ndom91/update-compliments-module-advice-api
Update Compliments Module - Add Advice API
2020-05-24 13:47:30 +02:00
ndo@ndo3
a35e8f3315 update: spaces -> tabs 2020-05-14 15:00:09 +02:00
ndo@ndo3
205de7233e add: advice api to compliments module 2020-05-14 14:56:38 +02:00
Veeck
abb5dc5739 Run prettier over ALL files once
No other changes done in this commit
2020-05-11 22:22:32 +02:00
Veeck
3a5a29efc0 Add pretty-quick 2020-05-11 21:59:45 +02:00
GeorgeTravelbacon
9d249406e3 Forgot CHANGELOG.MD 2020-05-10 17:07:34 +02:00
Joris
42d9e7a090 Changed "Gevoelstemperatuur" to "Voelt als". This is shorter and saves mirror space. 2020-05-10 15:18:24 +02:00
rejas
c202c0d705 Add prettier, configs and editorconfig 2020-05-07 14:09:22 +02:00
Michael Teeuw
8a1f9b7de6 Merge pull request #2012 from rejas/issue_2011
Use correct object in browser context
2020-05-07 09:02:17 +02:00
Veeck
18820c383d Use correct object in browser context 2020-05-06 22:19:07 +02:00
Michael Teeuw
b38b879ee3 Merge pull request #2010 from rejas/no_undef_and_more
Cleanup eslints no-undef warnings Part 2
2020-05-05 15:16:27 +02:00
Veeck
7fc7d626bc Add Module to globals, cleanup comments 2020-05-05 14:55:15 +02:00
Veeck
c8a0f1d0de Fix some more undefs that popped up out of nowhere 2020-05-05 14:54:49 +02:00
Michael Teeuw
c8aedc6f29 Merge pull request #1997 from andrezibaia/patch-1
Literal translation (not used in Portuguese)
2020-05-05 14:32:45 +02:00
Michael Teeuw
1e7ce8bb5c Merge pull request #2007 from rejas/no_undef
Cleanup eslints no-undef warnings
2020-05-05 14:31:41 +02:00
Veeck
5386ca1592 Merge branch 'develop' into no_undef 2020-05-05 14:20:24 +02:00
Michael Teeuw
08ed019426 Merge branch 'develop' of https://github.com/MichMich/MagicMirror into develop 2020-05-05 14:18:14 +02:00
Michael Teeuw
55c6a5aa27 remove unused file 2020-05-05 14:17:46 +02:00
rejas
bd6bbf864a Update dependencies 2020-05-03 20:52:58 +02:00
rejas
703335c2f1 Update changelog 2020-05-03 20:50:52 +02:00
rejas
c04fa496bf Second round of undef fixes 2020-05-03 18:59:26 +02:00
rejas
b9d19cfcb4 First round of undef fixes 2020-05-03 12:40:48 +02:00
rejas
d8f093b226 Cleanup some globals 2020-05-02 10:33:24 +02:00
rejas
63a2d83d26 Start checking for undef 2020-05-02 10:32:53 +02:00
Michael Teeuw
7e42e98cb6 Merge pull request #2002 from rejas/https
Replace insecure links with https
2020-04-30 09:19:17 +02:00
rejas
93deebe923 Update changelog 2020-04-28 23:09:29 +02:00
rejas
1eecf4b2c7 Fix docs, grunt is removed 2020-04-28 23:07:14 +02:00
rejas
e7fc4ef1e7 Replace unsecure links with https ones 2020-04-28 23:05:28 +02:00
Michael Teeuw
3c54d4af15 Merge pull request #1996 from rejas/eslint_recommend
Update eslint config to use eslint:recommended
2020-04-23 10:23:40 +02:00
andrezibaia
ebfbf0e1f8 Literal translation (not used in Portuguese) 2020-04-23 04:41:05 +02:00
rejas
14aa4036e0 Some spelling and doc fixes 2020-04-22 22:09:31 +02:00
Veeck
0c60d54c3f Add eqeqeq rule to not confuse == with === 2020-04-21 15:13:06 +02:00
Veeck
e510d279a2 Cleanup some more header comments 2020-04-21 14:58:17 +02:00
Veeck
be6f1f9c4a Move eslint to dependencies, update some devdependcies 2020-04-21 14:41:34 +02:00
rejas
b1fb177e4b Undo "fix" in main.js that broke the tests 2020-04-21 12:38:29 +02:00
Veeck
ec187fe109 Update changelog and commen tags 2020-04-21 12:23:50 +02:00
Veeck
9ec329b7ae Final fixes for eslint:recommended 2020-04-21 12:23:50 +02:00
rejas
d08bd4e866 Fix lots of warnings 2020-04-21 12:23:50 +02:00
rejas
941d5d7cd9 Fix mixed tabs and whitespace errors 2020-04-21 12:23:50 +02:00
rejas
ef8d85773c Run automatic fix 2020-04-21 12:23:50 +02:00
rejas
e668d488b4 Use eslint:recommend 2020-04-21 12:23:50 +02:00
Michael Teeuw
d4eb5facfd Merge pull request #1992 from rejas/update_fonts
Update font package
2020-04-21 11:55:08 +02:00
Veeck
a1285b120b Remove some other prefixed styles since we are at it 2020-04-20 12:13:13 +02:00
Veeck
8650876e2b Remove yarn.locks 2020-04-20 12:10:58 +02:00
Veeck
81ab12c89c Update changelog 2020-04-20 12:10:58 +02:00
Veeck
4ddb8cec85 Update roboto fonts, remove ttf entries 2020-04-20 12:10:58 +02:00
Michael Teeuw
711c566498 Merge pull request #1995 from rejas/cleanup_alert
Cleanup default alert module
2020-04-20 12:06:18 +02:00
Veeck
cc5336900c Update dependencies 2020-04-20 11:34:24 +02:00
Veeck
291a8f5c1f Fix warnings for jsonlint 2020-04-20 11:34:16 +02:00
Veeck
8c0e98ed43 Ignore some other files when linting markdown too 2020-04-20 11:30:05 +02:00
Veeck
7a976c0239 Fix tests, use non-spread object extend version again 2020-04-20 11:24:03 +02:00
Veeck
fb557b9130 Update ignored files for eslint 2020-04-20 11:15:11 +02:00
Veeck
b56a930f60 Update changelog 2020-04-20 10:59:04 +02:00
Veeck
5819ef346b Rename stylesheet for clarity that its about the notificationFX 2020-04-20 10:58:27 +02:00
Veeck
b3dcc82c71 Remove old webkit prefix styles 2020-04-20 10:52:53 +02:00
Veeck
06e8308dc2 Replace old js files with modern code 2020-04-20 10:51:27 +02:00
Michael Teeuw
c8a9c9b84e Merge pull request #1991 from rejas/cleanup_config_check
Cleanup config check script
2020-04-19 09:10:41 +02:00
Veeck
e85b49c573 Update changelog 2020-04-19 08:22:48 +02:00
Veeck
c7c6dc4e67 Move config check into js folder, cleanup var usage 2020-04-19 07:55:56 +02:00
Michael Teeuw
172d668416 Merge pull request #1963 from AndreKoepke/feature/client_https
add https support for clientonly-mode
2020-04-15 09:57:53 +02:00
AndreKoepke
b651dc845b no single quotes 2020-04-14 14:10:29 +02:00
Michael Teeuw
c755f44153 Merge pull request #1981 from rejas/remove_grunt
Remove grunt, use non-grunt linters instead
2020-04-10 21:55:41 +02:00
rejas
ed28ce1874 Update changelog and lock 2020-04-10 16:12:51 +02:00
rejas
194af0e985 Run linter and npm audit 2020-04-10 15:09:08 +02:00
rejas
ab3015df6b Remove rest of grunt calls 2020-04-10 14:43:58 +02:00
rejas
f36df159e0 Replace grunt-jsonlint with jsonlint 2020-04-10 12:24:08 +02:00
rejas
61462cf57e Replace grunt-eslint with eslint 2020-04-10 07:48:28 +02:00
rejas
427d186c86 Replace grunt-markdownlint with markdownlint-cli 2020-04-10 07:48:28 +02:00
rejas
84e9c47a67 Replace grunt-stylelint with stylelint 2020-04-10 07:48:28 +02:00
rejas
18989d593a Replace grunt-yamllint with yaml-lint 2020-04-10 07:48:28 +02:00
Michael Teeuw
fc624359c5 Merge pull request #1980 from khassel/socketclient_fix
socketclient.js: add backward compatibility for old module code
2020-04-09 13:50:38 +02:00
Karsten Hassel
1914573634 fixed again ... 2020-04-07 20:26:09 +02:00
Karsten Hassel
1fd36458a6 update CHANGELOG.md 2020-04-07 19:42:16 +02:00
Karsten Hassel
37ee0e568f socketclient.js: add backward compatibility for old module code 2020-04-07 19:35:21 +02:00
Michael Teeuw
3f8363a5b2 Merge pull request #1976 from Legion2/develop
Added basename config
2020-04-06 21:55:10 +02:00
Leon Kiefer
e6c0011789 renamed basename to basePath 2020-04-06 21:29:55 +02:00
Leon Kiefer
eb37a495a2 changed headings in Changelog 2020-04-05 23:18:00 +02:00
Leon Kiefer
54542f7f07 added basename config
use basename in socket.io path fix #1973
2020-04-05 23:00:38 +02:00
AndreKoepke
8e38910dd8 Merge branch 'develop' into feature/client_https 2020-04-03 14:22:41 +02:00
Andre
31b3f778fc no single quotes 2020-04-03 12:37:33 +02:00
Michael Teeuw
ca3275757b Fix release date. 2020-04-01 15:40:14 +02:00
Michael Teeuw
501a314597 Start of 2.12.0-develop 2020-04-01 12:28:40 +02:00
Michael Teeuw
447c0bffdc Merge pull request #1968 from MichMich/develop
Release 2.11.0
2020-04-01 12:21:33 +02:00
Michael Teeuw
46df59d77a Prepare release 2.11.0 2020-04-01 11:52:39 +02:00
Michael Teeuw
8c85e240b7 Merge pull request #1967 from MichMich/rejas-date
Rejas date
2020-04-01 11:15:31 +02:00
Michael Teeuw
2464d01891 Fix date test. 2020-04-01 10:57:50 +02:00
Michael Teeuw
66642a19c5 Merge branch 'date' of https://github.com/rejas/MagicMirror into rejas-date 2020-04-01 10:08:33 +02:00
Michael Teeuw
f7f4e92e0a Merge branch 'develop' of https://github.com/MichMich/MagicMirror into develop 2020-04-01 09:58:55 +02:00
Michael Teeuw
2674bf22d8 Let's use sensible defaults. :) 2020-04-01 09:44:22 +02:00
Andre
351e0f3a0b add https support 2020-03-28 12:37:50 +01:00
rejas
ff7dc95e93 Implement suggestions from Michael 2020-03-28 09:24:30 +01:00
rejas
4e4d3418b3 LInt mockdate helper 2020-03-25 06:53:09 +01:00
rejas
3659b5b5c9 Update mockdate info 2020-03-25 06:40:23 +01:00
rejas
65e1b60fb7 Fix error when no compliments are set 2020-03-25 06:40:23 +01:00
rejas
e2427fe299 Try out a mockdate class 2020-03-25 06:40:23 +01:00
rejas
b08f882324 Add (failing) test for new date field 2020-03-25 06:40:23 +01:00
rejas
2a31ece0c6 Refactor code 2020-03-25 06:40:23 +01:00
rejas
cafa4211a6 Update changelog and add another birtday 2020-03-25 06:40:23 +01:00
rejas
82b50d3059 Add basic compliment on a specific day of the year 2020-03-25 06:39:24 +01:00
Michael Teeuw
39cb582e07 Merge pull request #1960 from effelle/develop
Update pt-br.json
2020-03-24 21:16:41 +01:00
Michael Teeuw
fe6645a420 Merge pull request #1955 from rejas/jshint
Remove jshint dependency
2020-03-24 21:16:12 +01:00
Michael Teeuw
a49816f4eb Merge pull request #1948 from rejas/lint
Run linter tests on travis (was: Run linter before commit)
2020-03-24 21:15:41 +01:00
Michael Teeuw
7ac97f854c Merge pull request #1937 from Legion2/develop
use relative path for socket.io
2020-03-24 21:13:36 +01:00
Federico Leoni
8f203852ca Update CHANGELOG.md 2020-03-24 10:06:12 -03:00
Federico Leoni
c342f7bce6 Update pt-br.json 2020-03-23 14:55:36 -03:00
Michael Teeuw
fe1c5df9d5 Merge pull request #1958 from bugsounet/master
issue #1956
2020-03-19 19:17:03 +01:00
bugsounet
8aa7a55559 issue #1956 2020-03-19 19:03:25 +01:00
rejas
e9461586cb Update changelog 2020-03-15 21:25:11 +01:00
rejas
a91f2de26a Remove jshint dependency, use eslint for config verification 2020-03-15 20:38:52 +01:00
rejas
5a4ae99283 Add no-multi-spaces rule to eslint and run it 2020-03-15 15:49:34 +01:00
rejas
437d030502 Remove some dead files, fix stylelint error 2020-03-11 12:44:44 +01:00
rejas
f3d45eff69 Fix js error but add a stylelint error 2020-03-11 11:43:45 +01:00
rejas
f22e39e22b Add switch to auto fix eslint and stylelint issues 2020-03-11 11:29:59 +01:00
rejas
7470a3b813 Dont fix eslint errors automatically 2020-03-11 10:48:43 +01:00
rejas
0b2e836e3d Commit a faulty js to see if travis complains 2020-03-11 09:01:21 +01:00
rejas
c3d57eef4f Remove husky again, move lint as test to travis 2020-03-11 08:52:25 +01:00
rejas
ca2571b438 Update changelog 2020-03-08 16:31:34 +01:00
rejas
6c926b8876 Add husky for pre-commit hook 2020-03-08 16:25:26 +01:00
rejas
5517a913d4 Run linter manually 2020-03-08 16:20:54 +01:00
Michael Teeuw
ef7720ff06 Merge pull request #1939 from dtreskunov/dtreskunov/fix-moon-illumination-percent-calc
fix floating point bug in moon illumination calc
2020-02-27 16:35:38 +01:00
Michael Teeuw
daa13b4b30 Merge pull request #1941 from bibaldo/bibaldo-currentweather
Add new default config to 'current weather' module
2020-02-27 16:34:26 +01:00
Michael Teeuw
9d4e237c09 Merge pull request #1938 from tosbaha/develop
Turkish translation update
2020-02-27 16:32:40 +01:00
Michael Teeuw
883c169ef7 Merge branch 'develop' into develop 2020-02-27 16:31:10 +01:00
Michael Teeuw
2b2b07f6b4 Merge pull request #1936 from buxxi/develop
Using promises to resolve which modules has a git remote
2020-02-27 16:30:23 +01:00
Jose Forte
f338b68f36 Update CHANGELOG.md 2020-02-21 10:31:40 -03:00
Jose Forte
c877b5fc70 Add new default config to 'current weather' module 2020-02-21 10:17:05 -03:00
Leon Kiefer
6aeec81072 removed additional slash from the socket.io path 2020-02-19 20:58:58 +01:00
Denis Treskunov
3f78c664eb fix floating point bug in moon illumination calc 2020-02-19 09:31:02 -08:00
mustafa
3b7df1d5c2 Changelog updated 2020-02-19 15:25:46 +03:00
mustafa
b6fc063c75 Turkish translation updated 2020-02-19 15:21:25 +03:00
Leon Kiefer
7b56817ae6 use relative path for socket.io fix #1934 2020-02-18 17:09:25 +01:00
buxxi
75c8c3f50b Using promises to resolve which modules has a git remote so the modules wont be skipped on the first check 2020-02-17 22:56:06 +01:00
Michael Teeuw
be07ff2129 Merge pull request #1923 from wast/patch-1
Fix Croatian translation for Calendar module word FEELS.
2020-02-12 16:34:45 +01:00
Michael Teeuw
56846b258c Merge branch 'develop' into patch-1 2020-02-12 16:34:33 +01:00
Michael Teeuw
f67310c8fa Merge pull request #1927 from fewieden/fix/weather-tests
1840 fix weather tests
2020-02-12 16:33:31 +01:00
Michael Teeuw
3dff3a1d4a Merge branch 'develop' into fix/weather-tests 2020-02-12 16:33:19 +01:00
Michael Teeuw
142d30b1c6 Merge pull request #1929 from buxxi/develop
Adding support for ignoring update check for certain modules
2020-02-11 19:21:46 +01:00
buxxi
c925e88475 Adding support for ignoring update check for certain modules 2020-02-11 18:13:39 +01:00
fewieden
56392e41d0 Add 1840 to changelog 2020-02-10 22:05:43 +01:00
fewieden
cd7b6450c6 Run tests also on lts and last stable version of nodejs 2020-02-10 21:51:29 +01:00
Felix Wiedenbach
de6a9f5811 fix timing issue in weather tests => returning response by url instead of index 2020-02-10 18:56:55 +01:00
Michael Teeuw
d7295948fd Merge pull request #1925 from dtreskunov/dtreskunov/fix-clock-module-moon-widget
fix bugs in showMoonTimes in clock module
2020-02-10 07:40:10 +01:00
Denis Treskunov
f4c3e412a2 fix bugs in showMoonTimes in clock module
1. as reported on https://github.com/MichMich/MagicMirror/pull/1885,
toLocaleString is not supported on old iPad Safari

2. SunCalc.getMoonTimes returns moonRise/moonSet times for the given date,
so moonRise can be after moonSet. We want to display the most relevant
times, which are today's moonRise and the next moonSet.
2020-02-09 22:03:35 -08:00
Stjepan
0d461e14d9 Merge pull request #1 from wast/patch-2
Update CHANGELOG.md
2020-02-09 10:26:54 +01:00
Stjepan
845c2571fe Update CHANGELOG.md
Fix FEELS translation in Croatian
2020-02-09 10:24:20 +01:00
Stjepan
d5c02d1031 Update hr.json
Fix FEELS translation in Croatian.
2020-02-09 10:22:41 +01:00
Michael Teeuw
30e93a9372 Merge pull request #1915 from normankong/develop
Support HTTPS Magicmirror (resubmit)
2020-02-06 10:10:06 +01:00
Michael Teeuw
e5cdcd190c Merge pull request #1918 from sergge1/develop
to make logic of parameter name to its actual behavior
2020-02-06 10:09:22 +01:00
sergge1
271f1cd71b to make logic of name to actual behaviour
hideTemp = false -> temp shold appear at the MM
hideTemp === true -> should not appear.
This is the logic. Worked vise versa, just corrected in the code
2020-02-06 00:04:48 +02:00
Michael Teeuw
4abb790e12 Merge pull request #1916 from d-Rickyy-b/log-date-timestamp
Log date timestamp
2020-02-05 12:43:26 +01:00
Michael Teeuw
89c9d4dfc4 Merge branch 'develop' into develop 2020-02-05 12:42:43 +01:00
karenorman
5511c15921 Update description in config.js.sample 2020-02-05 10:23:49 +08:00
Rico
e160f9a0f6 docs: add info about changed timestamp in changelog 2020-02-03 21:43:45 +01:00
Rico
65ea341ed0 feat: use date in log timestamp
I find it quite hard to look something up in the logs, when there is no date in the log file. The time itself is not sufficient for debugging, hence I suggest also logging the date.
2020-02-03 21:39:46 +01:00
Michael Teeuw
561827e896 Remove logging. 2020-02-01 19:12:05 +01:00
Michael Teeuw
1315aceb44 Remove duplicate 2.11 entry. 2020-02-01 17:41:49 +01:00
Michael Teeuw
19afd89df5 Fix Typo 2020-02-01 16:48:35 +01:00
Michael Teeuw
cbcc893f64 Restore MM.sh to keep PM2 working. 2020-02-01 15:16:23 +01:00
Michael Teeuw
a41bae3132 Cleanup 2020-02-01 15:05:26 +01:00
Michael Teeuw
8ef8388c32 Set default DISPLAY in package.json. 2020-02-01 15:02:47 +01:00
Michael Teeuw
e2d4a0fde8 Improve Feedback. 2020-02-01 14:52:52 +01:00
Michael Teeuw
65b1d61295 Improve feedback. 2020-02-01 14:43:43 +01:00
Michael Teeuw
1b38e73eb2 Fix install scripts. 2020-02-01 14:41:46 +01:00
Michael Teeuw
e2afee3275 Add feedback. 2020-02-01 14:39:50 +01:00
Michael Teeuw
7f2ecbd04f Cleanup. 2020-02-01 14:32:38 +01:00
Michael Teeuw
21dbaa1a03 Move DISPLAY default to electron script. 2020-02-01 13:59:13 +01:00
Michael Teeuw
09c1ea992c Update start methods. 2020-02-01 13:56:15 +01:00
karenorman
c8c327b6ab Add HTTPS support 2020-02-01 20:46:26 +08:00
Michael Teeuw
3bb801f579 Merge pull request #1914 from kblankenship1989/fix-iso-string
fix: add missing +1 for month
2020-02-01 04:57:12 +01:00
Kurtis Blankenship
ca49d8adb3 fix: correcting syntax 2020-01-31 20:46:43 -06:00
Kurtis Blankenship
88b15797a0 fix: add missing +1 for month 2020-01-31 20:35:46 -06:00
Michael Teeuw
28ebbd229c Merge pull request #1910 from sergge1/develop
added Ukrainian (uk) translation
2020-01-31 19:51:48 +01:00
sergge1
d7f6f46805 2.11.0 - added Ukrainian translation 2020-01-31 11:28:00 +02:00
sergge1
c41d6965ab added Ukrainian translation 2020-01-31 11:18:43 +02:00
sergge1
602119fa41 Ukrainian translation added 2020-01-31 11:17:11 +02:00
Michael Teeuw
4c2a9b47f5 Merge pull request #1906 from mmphego/develop
Grammar fixes on config.js.sample file
2020-01-26 16:46:48 +01:00
Michael Teeuw
5a26fefd6b Merge pull request #1903 from roramirez/not-log-error-vendor-test
Revert error logging introduced in 1e97b5c
2020-01-26 16:46:06 +01:00
Mpho Mphego
85d26f7320 Grammar fixes to config.
This PR does the following:

- [x] Corrects grammar on `config.js.sample`
2020-01-26 14:10:24 +02:00
Michael Teeuw
9487af1852 Merge pull request #1901 from kolbyjack/fix-97e5d92-regression
Fix regression causing 'undefined' to show up when config.hideTemp is…
2020-01-24 17:46:45 +01:00
Jon Kolb
a5b09d7846 Update CHANGELOG.md 2020-01-24 10:51:24 -05:00
Jon Kolb
6ba5056c96 Fix regression causing 'undefined' to show up when config.hideTemp is false 2020-01-24 10:42:45 -05:00
Michael Teeuw
50b4d05ef5 Merge pull request #1899 from MichMich/Skip-weather
Skip weather tests for now
2020-01-23 19:56:23 +01:00
Michael Teeuw
13ebe6d0e1 Check method 2020-01-23 19:52:07 +01:00
Michael Teeuw
95dedf0d80 Skip weather tests for now 2020-01-23 19:38:15 +01:00
Michael Teeuw
89a9368166 Merge pull request #1896 from sejka/patch-1
adds PRECIP polish translation
2020-01-23 19:29:43 +01:00
Michael Teeuw
c10e6159da Merge pull request #1895 from adi-miller/heb
Add Hebrew translation
2020-01-23 19:29:26 +01:00
Michael Teeuw
c30693b8a8 Merge pull request #1894 from roramirez/mode-permission-changelog
Revert permission mode for CHANGELOG
2020-01-23 19:28:59 +01:00
Michael Teeuw
7fc4f61182 Merge pull request #1892 from roramirez/upgrade-electron-6
Upgrade to Electron 6:
2020-01-23 19:28:37 +01:00
Karol Sejka
b44ab88927 adds PRECIP polish translation 2020-01-23 02:24:47 +01:00
adimi
2ca81f965a Add hebrew translation 2020-01-22 22:03:34 +02:00
Michael Teeuw
9f4dc1e382 Merge pull request #1887 from dtreskunov/dtreskunov/sun-and-moon
fix calculation of duration until next sunrise
2020-01-22 15:23:01 +01:00
Michael Teeuw
5ec9704e7f Merge pull request #1893 from roramirez/vendor-fix-test
Fix the vendor_spec test:
2020-01-22 15:22:29 +01:00
Denis Treskunov
7ad0cfd5f6 update CHANGELOG.md 2020-01-21 19:16:42 -08:00
Denis Treskunov
ac8f679dfd Merge remote-tracking branch 'upstream/develop' into dtreskunov/sun-and-moon 2020-01-21 19:13:51 -08:00
Rodrigo Ramírez Norambuena
a7ee2ef3a6 Upgrade to Electron 6:
New version of Electron has enable by default sandbox
http://www.atom.pe/docs/api/sandbox-option/

There was some issues to migrate a new version of Electron for
MagicMirror. Using the new version in Travis CI was failing at this
time. The problem is because the testing runner is a Docker enviroment

The issue experimented is the same topic mentioned here:
 - https://github.com/electron/electron/issues/17972
 - https://github.com/electron-userland/spectron/issues/443

The fix for to all of this is to set the `--no-sandbox` mode in CI
testing https://electronjs.org/docs/all#--no-sandbox

This change use the feature to set and disable Sandbox using
by enviroment variable `ELECTRON_DISABLE_SANDBOX=1`
https://github.com/electron/electron/pull/16576

This change has reference #1800
2020-01-20 22:19:10 +00:00
Rodrigo Ramírez Norambuena
6e7edd9824 Revert error logging introduced in 1e97b5c 2020-01-19 08:50:37 -03:00
Rodrigo Ramírez Norambuena
81b310086f Revert permission mode for CHANGELOG
This change revert change of permission mode for file CHANGELOG.md
introduced in commit a619fc4fef
2020-01-19 04:40:05 -03:00
Rodrigo Ramírez Norambuena
cce57c7229 Fix the vendor_spec test:
This change set after startApplication the configuration to run this
test.

The previous statement when e2e are running using --recursive the
MM_CONFIG_FILE was setting by the test running before this one.
2020-01-19 04:06:21 -03:00
Denis Treskunov
9cffa3dd67 fix calculation of duration until next sunrise 2020-01-18 17:02:40 -08:00
Michael Teeuw
7b3a59455d Merge pull request #1885 from dtreskunov/dtreskunov/sun-and-moon
add sun/moon rise/set times
2020-01-18 21:29:41 +01:00
Michael Teeuw
fefe5659d3 Merge branch 'develop' into dtreskunov/sun-and-moon 2020-01-18 21:20:35 +01:00
Michael Teeuw
db72e22e61 Fuck it. 2020-01-18 21:17:54 +01:00
Michael Teeuw
a85ed709af Remove newlines. 2020-01-18 21:09:18 +01:00
Michael Teeuw
e24d1a1261 Fix newlines? 2020-01-18 21:02:49 +01:00
Michael Teeuw
d6f1bf18de Merge pull request #1884 from kblankenship1989/fix-timeshift-calendar
fix: Issue #1798 - fixing recurrent calendar events crosstime DST
2020-01-18 19:59:24 +01:00
Michael Teeuw
97e5d923fa Merge pull request #1882 from adi-miller/hide_weather
Added the ability to hide the temp label and weather icon in the `cur…
2020-01-18 19:54:30 +01:00
Denis Treskunov
36762a6e46 add duration until next sunset/sunrise 2020-01-18 09:50:43 -08:00
Denis Treskunov
40a9f9dd85 fix stylelint:simple 2020-01-18 08:45:52 -08:00
Denis Treskunov
85a52523cf show sun/moon as bright rather than bold if obj is visible 2020-01-18 08:43:33 -08:00
Kurtis Blankenship
5ed7974099 Merge branch 'fix-timeshift-calendar' of github.com:kblankenship1989/MagicMirror into fix-timeshift-calendar 2020-01-18 06:38:13 -06:00
Kurtis Blankenship
70ca9ce2e0 fix: fixing lint error 2020-01-18 06:38:01 -06:00
kblankenship1989
af9f555e8f fix: fixing copy / paste error on futureLocal 2020-01-18 06:28:21 -06:00
Denis Treskunov
08b9e7b5b5 add sun/moon rise/set times
Icons become bold when the object is above the horizon. Also shows
percent of moon illumination.
2020-01-17 22:28:24 -08:00
Kurtis Blankenship
84f74c53b5 perf: fix wording on changelog 2020-01-17 23:08:34 -06:00
Kurtis Blankenship
762bae907c perf: updating changelog 2020-01-17 23:04:51 -06:00
Kurtis Blankenship
2b738fa14b Merge branch 'develop' of github.com:MichMich/MagicMirror into fix-timeshift-calendar 2020-01-17 22:54:59 -06:00
Kurtis Blankenship
8aa745471b fix: Issue #1798 - fixing recurrent calendar events crosstime DST 2020-01-17 22:53:14 -06:00
Adi Miller
63950f572a Adding trailing enter to avoid build warning. 2020-01-16 16:47:49 +02:00
Adi Miller
81aab7e86f 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. 2020-01-16 16:26:20 +02:00
Michael Teeuw
1fd5fd4832 Update ISSUE_TEMPLATE.md 2020-01-14 09:20:50 +01:00
Michael Teeuw
ceaf478a84 Merge pull request #1875 from bastilimbach/master
Add different GitHub repos to issue template
2020-01-13 12:29:52 +01:00
Sebastian Limbach
61256f98ca Fix typo 2020-01-13 09:40:38 +01:00
Sebastian Limbach
818ed5d189 Add different GitHub repos
This commit adds the official docker image repo as well as the installation scripts repo to the issue template to avoid the creation of issue which don't belong to the MagicMirror core.
2020-01-12 20:15:04 +01:00
Michael Teeuw
0ca00e5059 Merge pull request #1874 from sdetweil/develop
cleanup installers folder
2020-01-12 17:44:50 +01:00
Sam Detweiler
a2c743167d cleanup installers foldr 2020-01-12 10:19:37 -06:00
Sam Detweiler
786641662c cleanup installers folder 2020-01-12 10:16:59 -06:00
sam detweiler
8380896deb Merge pull request #42 from MichMich/develop
synch with fork source
2020-01-12 10:14:44 -06:00
Michael Teeuw
1152689f34 Remove docs and link to docs.magicmirror.builders. 2020-01-12 13:01:56 +01:00
Michael Teeuw
d2afdc82f1 Merge branch 'master' into develop. 2020-01-10 22:27:44 +01:00
Michael Teeuw
5bf90ae31d Merge pull request #1872 from MichMich/2.10.1-docs
2.10.1 docs
2020-01-10 22:23:18 +01:00
Michael Teeuw
b533e3d3e1 Add install link. 2020-01-10 22:16:19 +01:00
Michael Teeuw
4d9df309c8 Use https. 2020-01-10 22:14:13 +01:00
Michael Teeuw
65126365a6 Add links to documentation site. 2020-01-10 22:11:10 +01:00
Michael Teeuw
0afe403057 Merge pull request #1861 from khassel/develop
fix run-start.sh: if running in docker-container, just start electron…
2020-01-08 16:25:57 +01:00
Michael Teeuw
d500353722 Merge pull request #1871 from shahrukh-alizai/patch-1
Remove the extra two "of" words from Manifesto content.
2020-01-08 16:24:29 +01:00
Shahrukh Khan
eea8b4dfbf Update README.md
Remove extra two "of" words
2020-01-08 18:10:36 +05:00
Karsten Hassel
20a9150ea3 fix run-start.sh: if running in docker-container, just start electron, see #1859 2020-01-03 19:17:50 +01:00
sam detweiler
c51e613568 Merge pull request #34 from MichMich/develop
sync Develop
2020-01-02 09:31:24 -06:00
Michael Teeuw
69a887fb05 Merge pull request #1851 from ZakarFin/fi-translations
Add missing translations for fi
2020-01-02 16:16:00 +01:00
Michael Teeuw
01c1d150c6 Merge branch 'develop' into fi-translations 2020-01-02 16:15:47 +01:00
Michael Teeuw
c51c09348e Merge pull request #1857 from HeikoGr/master
force declaration of public ip adress in config file (ISSUE #1852)
2020-01-02 16:14:14 +01:00
Michael Teeuw
0a4df191a7 Merge branch 'develop' into master 2020-01-02 16:13:30 +01:00
HeikoGr
760995773e Update CHANGELOG.md 2020-01-02 16:04:38 +01:00
HeikoGr
94ff8a9b04 force declaration of public ip adress in config file (ISSUE #1852) 2020-01-02 14:15:24 +00:00
Sami Mäkinen
10d5529f79 Add missing translations for fi 2020-01-02 00:04:59 +02:00
Michael Teeuw
2b511b67c3 Prepare for 2.11.0 2020-01-01 22:21:55 +01:00
sam detweiler
e884cc1f75 Merge pull request #32 from MichMich/develop
sync Develop
2019-12-29 07:33:57 -06:00
sam detweiler
525c235d3b Merge pull request #31 from MichMich/develop
synch to Develop
2019-12-28 15:01:54 -06:00
331 changed files with 29233 additions and 27415 deletions

15
.editorconfig Normal file
View 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

View File

@@ -1,5 +1 @@
vendor/*
!/vendor/vendor.js
!/modules/default/**
!/modules/node_helper
!/modules/node_helper/**
modules/default/calendar/vendor/*

View File

@@ -1,25 +1,31 @@
{
"rules": {
"indent": ["error", "tab"],
"quotes": ["error", "double"],
"semi": ["error"],
"max-len": ["error", 250],
"curly": "error",
"camelcase": ["error", {"properties": "never"}],
"no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 1 }],
"no-trailing-spaces": ["error", {"ignoreComments": false }],
"no-irregular-whitespace": ["error"]
},
"extends": ["eslint:recommended", "plugin:prettier/recommended", "plugin:jsdoc/recommended"],
"plugins": ["prettier", "jsdoc", "jest"],
"env": {
"browser": true,
"node": true,
"es6": true
"es6": true,
"jest/globals": true,
"node": true
},
"globals": {
"config": true,
"Log": true,
"MM": true,
"Module": true,
"moment": true
},
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2017,
"ecmaVersion": 2018,
"ecmaFeatures": {
"globalReturn": true
}
}
},
"rules": {
"prettier/prettier": "error",
"eqeqeq": "error",
"no-prototype-builtins": "off",
"no-unused-vars": "off",
"no-useless-return": "error"
}
}

View File

@@ -1,40 +1,50 @@
Contribution Policy for MagicMirror²
====================================
# 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 both linters, use `grunt` without any arguments.
## Linters
If you wish to run our linters, use `npm run lint` without any arguments.
### JavaScript: Run ESLint
We use [ESLint](http://eslint.org) on our JavaScript files.
We use [ESLint](https://eslint.org) on our JavaScript files.
Our ESLint configuration is in our .eslintrc.json and .eslintignore files.
Our ESLint configuration is in our `.eslintrc.json` and `.eslintignore` files.
To run ESLint, use `grunt eslint`.
To run ESLint, use `npm run lint:js`.
### CSS: Run StyleLint
We use [StyleLint](http://stylelint.io) to lint our CSS. Our configuration is in our .stylelintrc file.
We use [StyleLint](https://stylelint.io) to lint our CSS. Our configuration is in our .stylelintrc file.
To run StyleLint, use `grunt stylelint`.
To run StyleLint, use `npm run lint:css`.
### Submitting Issues
## Testing
We use [Jest](https://jestjs.io) for JavaScript testing.
To run all tests, use `npm run test`.
The specific test commands are defined in `package.json`. So you can also run the specific tests with other commands, e.g. `npm run test:unit` or `npx jest tests/e2e/env_spec.js`.
## 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).
**Platform**: Place your platform here... give us your web browser/Electron version _and_ your hardware (Raspberry Pi 2/3/4, Windows, Mac, Linux, System V UNIX).
**Node Version**: Make sure it's version 0.12.13 or later.
**Node Version**: Make sure it's version 14 or later (recommended is 16).
**MagicMirror Version**: Now that the versions have split, tell us if you are using the PHP version (v1) or the newer JavaScript version (v2).
**MagicMirror Version**: Please let us know which version of MagicMirror you are running. It can be found in the `package.json` 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.

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
github: MichMich
custom: ["https://magicmirror.builders/#donate"]

View File

@@ -1,15 +1,41 @@
Please only submit reproducible issues.
Hello and thank you for opening an issue.
**Please make sure that you have read the following lines before submitting your Issue:**
## 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)
A common problem is that your config file could be invalid. Please run in your MagicMirror directory: `npm run config:check` and see if it reports an error.
## 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 corresponding repository:
- karsten13/magicmirror: [https://gitlab.com/khassel/magicmirror](https://gitlab.com/khassel/magicmirror)
- (deprecated) bastilimbach/docker-magicmirror: [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).
**Platform**: Place your platform here... give us your web browser/Electron version _and_ your hardware (Raspberry Pi 2/3/4, Windows, Mac, Linux, System V UNIX).
**Node Version**: Make sure it's version 0.12.13 or later.
**Node Version**: Make sure it's version 14 or later (recommended is 16).
**MagicMirror Version**: Now that the versions have split, tell us if you are using the PHP version (v1) or the newer JavaScript version (v2).
**MagicMirror Version**: Please let us know which version of MagicMirror you are running. It can be found in the `package.json` 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.

View File

@@ -1,14 +1,25 @@
> Please send your pull requests the develop branch.
> Don't forget to add the change to CHANGELOG.md.
Hello and thank you for wanting to contribute to the MagicMirror project
**Please make sure that you have followed these 4 rules before submitting your Pull Request:**
> 1. Base your pull requests against the `develop` branch.
>
> 2. Include these infos in the description:
>
> - Does the pull request solve a **related** issue?
> - If so, can you reference the issue like this `Fixes #<issue_number>`?
> - What does the pull request accomplish? Use a list if needed.
> - If it includes major visual changes please add screenshots.
>
> 3. Please run `npm run lint:prettier` before submitting so that
> style issues are fixed.
>
> 4. Don't forget to add an entry about your changes to
> the CHANGELOG.md file.
**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.
Thanks again and have a nice day!

6
.github/codecov.yml vendored Normal file
View File

@@ -0,0 +1,6 @@
coverage:
status:
project:
default:
# advanced settings
informational: true

BIN
.github/header.psd vendored

Binary file not shown.

38
.github/workflows/automated-tests.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
# 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: "Run Automated Tests"
on:
push:
branches: [master, develop]
pull_request:
branches: [master, develop]
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
node-version: [14.x, 16.x, 17.x]
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
- name: Install dependencies and run tests
run: |
Xvfb :99 -screen 0 1024x768x16 &
export DISPLAY=:99
npm install
touch css/custom.css
npm run test:prettier
npm run test:js
npm run test:css
npm run test:unit
npm run test:e2e
npm run test:electron

View File

@@ -0,0 +1,29 @@
# This workflow runs the automated test and uploads the coverage results to codecov.io
name: "Run Codecov Tests"
on:
push:
branches: [master, develop]
pull_request:
branches: [master, develop]
jobs:
run-and-upload-coverage-report:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install dependencies and run coverage
run: |
Xvfb :99 -screen 0 1024x768x16 &
export DISPLAY=:99
npm ci
touch css/custom.css
npm run test:coverage
- name: Upload coverage results to codecov
uses: codecov/codecov-action@v2
with:
files: ./coverage/lcov.info
fail_ci_if_error: true

20
.github/workflows/enforce-changelog.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
# This workflow enforces the update of a changelog file on every pull request
name: "Enforce Changelog"
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled]
jobs:
check:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Enforce changelog
uses: dangoslen/changelog-enforcer@v2
with:
changeLogPath: "CHANGELOG.md"
skipLabels: "Skip Changelog"

11
.gitignore vendored
View File

@@ -1,5 +1,4 @@
# Various Node ignoramuses.
logs
*.log
npm-debug.log*
@@ -8,15 +7,16 @@ pids
*.seed
lib-cov
coverage
.grunt
.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/
@@ -54,7 +54,6 @@ Temporary Items
.apdisk
# Various Linux ignoramuses.
.fuse_hidden*
.directory
.Trash-*
@@ -68,6 +67,10 @@ Temporary Items
# 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]
@@ -77,5 +80,3 @@ Temporary Items
*.orig
*.rej
*.bak
!/tests/node_modules/**/*

4
.husky/pre-commit Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run lint:staged

4
.prettierignore Normal file
View File

@@ -0,0 +1,4 @@
/config
/coverage
.nyc_output
package-lock.json

3
.prettierrc.json Normal file
View File

@@ -0,0 +1,3 @@
{
"trailingComma": "none"
}

View File

@@ -1,5 +1,7 @@
{
"extends": "stylelint-config-standard",
"font-family-name-quotes": "double-where-recommended",
"block-no-empty": false
"extends": ["stylelint-prettier/recommended"],
"plugins": ["stylelint-prettier"],
"rules": {
"prettier/prettier": true
}
}

View File

@@ -1,21 +0,0 @@
dist: trusty
language: node_js
node_js:
- "10"
before_install:
- npm i -g npm
before_script:
- yarn danger ci
- npm install grunt-cli -g
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
- sleep 5
script:
- npm run test:e2e
- npm run test:unit
- grunt
after_script:
- npm list
cache:
directories:
- node_modules

508
CHANGELOG.md Executable file → Normal file
View File

@@ -1,21 +1,424 @@
# MagicMirror² Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
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² core.
❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/donate) With your help we can continue to improve the MagicMirror².
## [2.10.0] - 2020-01-01
## [2.18.0] - 2022-01-01
Special thanks to the following contributors: @AmpioRosso, @eouia, @fewieden, @jupadin, @khassel, @kolbyjack, @KristjanESPERANTO, @MariusVaice, @rejas, @rico24 and @sdetweil.
### Added
- Added test for calendar recurring event with checks the correct date displayed (related to #2752).
### Updated
- ESLint version supports now ECMAScript 2018.
- Cleaned up `updatenotification` module and switched to nunjuck template.
- Moved calendar tests from category `electron` to `e2e`.
- Update missed translations for Korean language (ko.json).
- Update missed translations for Dutch language (nl.json).
- Cleaned up `alert` module and switched to nunjuck template.
- Moved weather tests from category `electron` to `e2e`.
- Updated github actions.
- Replace spectron with playwright, update dependencies including electron update to v16.
- Added lithuanian language to translations.js.
- Show info message if newsfeed is empty (fixes #2731).
- Added dangerouslyDisableAutoEscaping config option for newsfeed templates (fixes #2712).
- Added missing shebang to `installers/mm.sh`.
- Node versions in templates and github workflows.
### Fixed
- Fixed wrong file `kr.json` to `ko.json`. Use language code 'ko' instead of 'kr' for Korean language.
- Fixed `feels_like` data from openweathermaps current weather being ignored (#2678).
- Fixed chaotic newsfeed display after network connection loss thanks to @jalibu (#2638).
- Fixed incorrect time zone correction of recurring full day events (#2632 and #2634).
- Fixed e2e tests by increasing testTimeout.
- Revert node-ical update due to missing luxon package.
- Fixed User-Agent-Header for newsfeed and calendar module (#2729).
- Replace broken shields in Readme and use https for links.
- Fixed electron tests with retry.
- Fixed Calendar recurring cross timezone error (add/subtract a day, not just offset hours) (#2632).
- Fixed Calendar showEnd and Full Date overlay (#2629).
- Fixed regression on #2632, #2752.
- Broadcast custom symbols in CALENDAR_EVENTS.
## [2.17.1] - 2021-10-01
### Fixed
- Fixed error when accessing letsencrypt certificates
- Fixed Calendar module enhancement: displaying full events without time (#2424)
## [2.17.0] - 2021-10-01
Special thanks to the following contributors: @apiontek, @eouia, @jupadin, @khassel and @rejas.
### Added
- Added showTime parameter to clock module for enabling/disabling time display in analog clock.
- Added custom electron switches from user config (`config.electronSwitches`).
- Added unit tests for updatenotification module.
### Updated
- Bump electron to v13 (and spectron to v15) and update other dependencies in package.json.
- Refactor test configs, use default test config for all tests.
- Updated github templates.
- Actually test all js and css files when lint script is run.
- Update jsdocs and print warnings during testing too.
- Update weathergov provider to try fetching not just current, but also foreacst, when API URLs available.
- Refactored clock layout.
- Refactored methods from weatherproviders into weatherobject (isDaytime, updateSunTime).
- Use of `logger.js` in jest tests.
- Run prettier over all relevant files.
- Move tests needing electron in new category `electron`, use `server only` mode in `e2e` tests.
- Update dependencies in package.json.
### Fixed
- Fix undefined error with ignoreToday option in weather module (#2620).
- Fix time zone correction in calendar module when the date hour is equal to the time zone correction value (#2632).
- Fix black cursor on startup when using electron.
- Fix update notification not working for own repository (#2644).
## [2.16.0] - 2021-07-01
Special thanks to the following contributors: @210954, @B1gG, @codac, @Crazylegstoo, @daniel, @earlman, @ezeholz, @FrancoisRmn, @jupadin, @khassel, @KristjanESPERANTO, @njwilliams, @oemel09, @r3wald, @rejas, @rico24, Faizan Ahmed.
### Added
- Added French translations for "MODULE_CONFIG_ERROR" and "PRECIP".
- Added German translation for "PRECIP".
- Added Dutch translation for "WEEK", "PRECIP", "MODULE_CONFIG_CHANGED" and "MODULE_CONFIG_ERROR".
- Added first test for Alert module.
- Added support for `dateFormat` when not using `timeFormat: "absolute"`.
- Added custom-properties for colors and fonts for improved styling experience, see `custom.css.sample` file.
- Added custom-properties for gaps around body and between modules.
- Added test case for recurring calendar events.
- Added new Environment Canada provider for default WEATHER module (weather data for Canadian locations only).
- Added list view for newsfeed module.
- Added dev dependency jest, switching from mocha to jest.
### Updated
- Bump node-ical to v0.13.0 (now last runtime dependency using deprecated `request` package is removed).
- Use codecov in informational mode.
- Refactor code into es6 where possible (e.g. var -> let/const).
- Use node v16 in github workflow (replacing node v10).
- Moved some files into better suited directories.
- Update dependencies in package.json, require node >= v12, remove `rrule-alt` and `rrule`.
- Update dependencies in package.json and migrate husky to v6, fix husky setup in prod environment.
- Cleaned up error handling in newsfeed and calendar modules for real.
- Updated default WEATHER module such that a provider can optionally set a custom unit-of-measure for precipitation (`weatherObject.precipitationUnits`).
- Update documentation.
- Update jest tests: Reset changes on js/logger.js, mock logger.js in global_vars tests.
- Update dependencies in package.json.
### Removed
- Switching from mocha to jest so removed following dev dependencies: chai, chai-as-promised, mocha, mocha-each, mocha-logger.
### Fixed
- Fix calendar start function logging inconsistency.
- Fix updatenotification start function logging inconsistency.
- Checks and applies the showDescription setting for the newsfeed module again.
- Fix issue with openweathermap not showing current or forecast info when using onecall API.
- Fix tests in weather module and add one for decimalPoint in forecast.
- Fix decimalSymbol in the forecast part of the new weather module (#2530).
- Fix wrong treatment of `appendLocationNameToHeader` when using `ukmetofficedatahub`.
- Fix alert not recognizing multiple alerts (#2522).
- Fix fetch option httpsAgent to agent in calendar module (#466).
- Fix module updatenotification which did not work for repos with many refs (#1907).
- Fix config check failing when encountering let syntax ("Parsing error: Unexpected token config").
- Fix calendar debug check.
- Really run prettier over all files.
- Fix logger.js after jest changes, use --forceExit running jest.
- Workaround for dev_console test using getWindowCount.
## [2.15.0] - 2021-04-01
Special thanks to the following contributors: @EdgardosReis, @MystaraTheGreat, @TheDuffman85, @ashishtank, @buxxi, @codac, @fewieden, @khassel, @klaernie, @qu1que, @rejas, @sdetweil & @thomasrockhu.
**Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`.
### Added
- Added Galician language.
- Added GitHub workflows for automated testing and changelog enforcement.
- Added CodeCov badge to Readme.
- Added CURRENTWEATHER_TYPE notification to currentweather and weather module, use it in compliments module.
- Added `start:dev` command to the npm scripts for starting electron with devTools open.
- Added logging when using deprecated modules weatherforecast or currentweather.
- Added Portuguese translations for "MODULE_CONFIG_CHANGED" and "PRECIP".
- Respect parameter ColoredSymbolOnly also for custom events.
- Added a new parameter to hide time portion on relative times.
- `module.show` has now the option for a callback on error.
- Added locale to sample config file.
- Added support for self-signed certificates for the default calendar module (#466).
- Added hiddenOnStartup flag to module config (#2475).
### Updated
- Updated markdown files for github.
- Cleaned up old code on server side.
- Convert `-0` to `0` when displaying temperature.
- Code cleanup for FEELS like and added {DEGREE} placeholder for FEELSLIKE for each language.
- Converted newsfeed module to use templates.
- Updated documentation and help screen about invalid config files.
- Moving weather provider specific code and configuration into each provider and making hourly part of the interface.
- Bump electron to v11 and enable contextIsolation.
- Don't update the DOM when a module is not displayed.
- Cleaned up jsdoc and tests.
- Exposed logger as node module for easier access for 3rd party modules.
- Replaced deprecated `request` package with `node-fetch` and `digest-fetch`.
- Refactored calendar fetcher.
- Cleaned up newsfeed module.
- Cleaned up translations and translator code.
### Removed
- Removed danger.js library.
- Removed `ical` which was substituted by `node-ical` in release `v2.13.0`. Module developers must install this dependency themselves in the module folder if needed.
- Removed valid-url library.
### Fixed
- Added default log levels to stop calendar log spamming.
- Fix socket.io cors errors, see [breaking change since socket.io v3](https://socket.io/docs/v3/handling-cors/).
- Fix Issue with weather forecast icons due to fixed day start and end time (#2221).
- Fix empty directory for each module's main javascript file in the inspector.
- Fix Issue with weather forecast icons unit tests with different timezones (#2221).
- Fix issue with unencoded characters in translated strings when using nunjuck template (`Loading &hellip;` as an example).
- Fix socket.io backward compatibility with socket v2 clients.
- Fix 3rd party module language loading if language is English.
- Fix e2e tests after spectron update.
- Fix updatenotification creating zombie processes by setting a timeout for the git process.
- Fix weather module openweathermap not loading if lat and lon set without onecall.
- Fix calendar daylight savings offset calculation if recurring start date before 2007.
- Fix calendar time/date adjustment when time with GMT offset is different day (#2488).
- Fix calendar daylight savings offset calculation if recurring FULL DAY start date before 2007 (#2483).
- Fix newsreaders template, for wrong test for nowrap in 2 places (should be if not).
## [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.
- Added 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.
- 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.
### 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 integration.
### 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 than 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` Added option 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`.
- Added 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 the 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.
@@ -24,30 +427,34 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- 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
- 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 rename after the next release. We've add into `run-start.sh` a instruction by GIT to ignore with `--skip-worktree` and `rm --cached`. [#1540](https://github.com/MichMich/MagicMirror/issues/1540)
- 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)
@@ -59,6 +466,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
**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
@@ -78,13 +486,16 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- 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 calender url in `config.js.sample`
- 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
- Update weatherprovider documentation.
### 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)
@@ -96,6 +507,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- 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
@@ -112,6 +524,7 @@ Fixed `package.json` version number.
**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
@@ -125,12 +538,14 @@ Fixed `package.json` version number.
- 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).
@@ -139,7 +554,7 @@ Fixed `package.json` version number.
- 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)
- 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)
@@ -148,6 +563,7 @@ Fixed `package.json` version number.
- 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).
@@ -165,11 +581,13 @@ Fixed `package.json` version number.
**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
@@ -184,22 +602,25 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- Documentation for the existing `scale` option in the Weather Forecast module.
### Fixed
- Allow to parse recurring calendar events where the start date is before 1900
- 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 &deg; symbol after every numeric value to be consistent with the Current Weather module.
- The Weather Forecast module by default displays the &deg; 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"
@@ -214,8 +635,9 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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 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)
@@ -259,11 +681,13 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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.
@@ -276,6 +700,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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
@@ -283,6 +708,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
## [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
@@ -293,7 +719,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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 to scroll in full page article view of default newsfeed module with gesture events from [MMM-Gestures](https://github.com/thobach/MMM-Gestures)
- 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
@@ -303,6 +729,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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.
@@ -310,6 +737,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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.
@@ -327,6 +755,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
## [2.2.1] - 2018-01-01
### Fixed
- Fixed linting errors.
## [2.2.0] - 2018-01-01
@@ -334,10 +763,12 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
**Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`
### Changed
- Calender week is now handled with a variable translation in order to move number language specific.
- 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.
@@ -351,6 +782,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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
@@ -361,9 +793,11 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
**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.
@@ -380,6 +814,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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.
@@ -388,6 +823,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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'.
@@ -398,11 +834,13 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
## [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.
@@ -421,11 +859,13 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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
@@ -439,6 +879,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
**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.
@@ -454,8 +895,9 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- Restructured Test Suite.
### Added
- Added Docker support (Pull Request [#673](https://github.com/MichMich/MagicMirror/pull/673)).
- Calendar-specific support for `maximumEntries`, and ` maximumNumberOfDays`.
- 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.
@@ -496,11 +938,12 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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 is set to imperial, the showRainAmount option of weatherforecast will show the correct unit.
- 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)
@@ -512,6 +955,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
**Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`
### Added
- Finnish translation.
- Danish translation.
- Turkish translation.
@@ -524,7 +968,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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 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.
- 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
@@ -532,16 +976,17 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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 show only current temperature and weather icon.
- 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 abilty set the classes option to compliments module for style and text size of compliments.
- 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.
@@ -552,6 +997,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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.
@@ -562,6 +1008,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
## [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)
@@ -569,11 +1016,13 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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.
@@ -581,6 +1030,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
## [2.0.4] - 2016-08-07
### Added
- Brazilian Portuguese Translation.
- Option to enable Kiosk mode.
- Added ability to start the app with Dev Tools.
@@ -588,6 +1038,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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'.
@@ -596,55 +1047,72 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
- 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: [http://michaelteeuw.nl/post/83916869600/magic-mirror-part-vi-production-of-the](http://michaelteeuw.nl/post/83916869600/magic-mirror-part-vi-production-of-the)
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)

View File

@@ -1,105 +0,0 @@
module.exports = function(grunt) {
require("time-grunt")(grunt);
grunt.initConfig({
pkg: grunt.file.readJSON("package.json"),
eslint: {
options: {
fix: "true",
configFile: ".eslintrc.json"
},
target: [
"js/*.js",
"modules/default/*.js",
"modules/default/*/*.js",
"serveronly/*.js",
"clientonly/*.js",
"*.js",
"tests/**/*.js",
"!modules/default/alert/notificationFx.js",
"!modules/default/alert/modernizr.custom.js",
"!modules/default/alert/classie.js",
"config/*",
"translations/translations.js",
"vendor/vendor.js",
"modules/node_modules/node_helper/index.js"
]
},
stylelint: {
simple: {
options: {
configFile: ".stylelintrc.json"
},
src: [
"css/main.css",
"modules/default/calendar/calendar.css",
"modules/default/clock/clock_styles.css",
"modules/default/currentweather/currentweather.css",
"modules/default/weatherforcast/weatherforcast.css"
]
}
},
jsonlint: {
main: {
src: [
"package.json",
".eslintrc.json",
".stylelintrc.json",
"installers/pm2_MagicMirror.json",
"translations/*.json",
"modules/default/*/translations/*.json",
"vendor/package.json"
],
options: {
reporter: "jshint"
}
}
},
markdownlint: {
all: {
options: {
config: {
"default": true,
"line-length": false,
"blanks-around-headers": false,
"no-duplicate-header": false,
"no-inline-html": false,
"MD010": false,
"MD001": false,
"MD031": false,
"MD040": false,
"MD002": false,
"MD029": false,
"MD041": false,
"MD032": false,
"MD036": false,
"MD037": false,
"MD009": false,
"MD018": false,
"MD012": false,
"MD026": false,
"MD038": false
}
},
src: [
"README.md",
"CHANGELOG.md",
"LICENSE.md",
"modules/README.md",
"modules/default/**/*.md",
"!modules/default/calendar/vendor/ical.js/readme.md"
]
}
},
yamllint: {
all: [
".travis.yml"
]
}
});
grunt.loadNpmTasks("grunt-eslint");
grunt.loadNpmTasks("grunt-stylelint");
grunt.loadNpmTasks("grunt-jsonlint");
grunt.loadNpmTasks("grunt-yamllint");
grunt.loadNpmTasks("grunt-markdownlint");
grunt.registerTask("default", ["eslint", "stylelint", "jsonlint", "markdownlint", "yamllint"]);
};

View File

@@ -1,7 +1,6 @@
The MIT License (MIT)
=====================
# The MIT License (MIT)
Copyright © 2016-2019 Michael Teeuw
Copyright © 2016-2021 Michael Teeuw
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation

243
README.md
View File

@@ -1,212 +1,46 @@
![MagicMirror²: The open source modular smart mirror platform. ](.github/header.png)
<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="http://choosealicense.com/licenses/mit"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
<a href="https://travis-ci.com/MichMich/MagicMirror"><img src="https://travis-ci.com/MichMich/MagicMirror.svg" alt="Travis"></a>
<a href="https://snyk.io/test/github/MichMich/MagicMirror"><img src="https://snyk.io/test/github/MichMich/MagicMirror/badge.svg" alt="Known Vulnerabilities" data-canonical-src="https://snyk.io/test/github/MichMich/MagicMirror" style="max-width:100%;"></a>
<p style="text-align: center">
<a href="https://choosealicense.com/licenses/mit">
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License">
</a>
<img src="https://img.shields.io/github/workflow/status/michmich/magicmirror/Run%20Automated%20Tests" alt="GitHub Actions">
<img src="https://img.shields.io/github/checks-status/michmich/magicmirror/master" alt="Build Status">
<a href="https://codecov.io/gh/MichMich/MagicMirror">
<img src="https://codecov.io/gh/MichMich/MagicMirror/branch/master/graph/badge.svg?token=LEG1KitZR6" alt="CodeCov Status"/>
</a>
<a href="https://github.com/MichMich/MagicMirror">
<img src="https://img.shields.io/github/stars/michmich/magicmirror?style=social">
</a>
</p>
**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](http://michaelteeuw.nl/tagged/magicmirror) with the incredible help of a [growing community of contributors](https://github.com/MichMich/MagicMirror/graphs/contributors).
**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).
MagicMirror² focuses on a modular plugin system and uses [Electron](http://electron.atom.io/) as an application wrapper. So no more web server or browser installs necessary!
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!
## Table Of Contents
## Documentation
- [Table Of Contents](#table-of-contents)
- [Installation](#installation)
- [Raspberry Pi](#raspberry-pi)
- [Automatic Installation (Raspberry Pi only!)](#automatic-installation-raspberry-pi-only)
- [Manual Installation](#manual-installation)
- [Server Only](#server-only)
- [Client Only](#client-only)
- [Docker](#docker)
- [Configuration](#configuration)
- [Raspberry Specific](#raspberry-specific)
- [General](#general)
- [Modules](#modules)
- [Updating](#updating)
- [Community](#community)
- [Contributing Guidelines](#contributing-guidelines)
- [Enjoying MagicMirror? Consider a donation!](#enjoying-magicmirror-consider-a-donation)
- [Manifesto](#manifesto)
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).
## Installation
## Links
### Raspberry Pi
#### Automatic Installation (Raspberry Pi only!)
*Electron*, the app wrapper around MagicMirror², only supports the Raspberry Pi 2/3. The Raspberry Pi 0/1 is currently **not** supported. If you want to run this on a Raspberry Pi 1, use the [server only](#server-only) feature and setup a fullscreen browser yourself. (Yes, people have managed to run MM² also on a Pi0, so if you insist, search in the forums.)
Note that you will need to install the latest full version of Raspbian, **don't use the Lite version**.
Execute the following command on your Raspberry Pi to install MagicMirror²:
```bash
bash -c "$(curl -sL https://raw.githubusercontent.com/MichMich/MagicMirror/master/installers/raspberry.sh)"
```
#### Manual Installation
1. Download and install the latest *Node.js* version:
- `curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -`
- `sudo apt install -y nodejs`
2. Clone the repository and check out the master branch: `git clone https://github.com/MichMich/MagicMirror`
3. Enter the repository: `cd MagicMirror/`
4. Install and run the app with: `npm install && npm start` \
For **Server Only** use: `npm install && node serveronly` .
**:warning: Important!**
- **The installation step for `npm install` will take a very long time**, often with little or no terminal response! \
For the RPi3 this is **~10** minutes and for the Rpi2 **~25** minutes. \
Do not interrupt or you risk getting a :broken_heart: by Raspberry Jam.
Also note that:
- `npm start` does **not** work via SSH. But you can use `DISPLAY=:0 nohup npm start &` instead. \
This starts the mirror on the remote display.
- If you want to debug on Raspberry Pi you can use `npm start dev` which will start MM with *Dev Tools* enabled.
- To access toolbar menu when in mirror mode, hit `ALT` key.
- To toggle the (web) `Developer Tools` from mirror mode, use `CTRL-SHIFT-I` or `ALT` and select `View`.
### Server Only
In some cases, you want to start the application without an actual app window. In this case, you can start MagicMirror² in server only mode by manually running `node serveronly` or using Docker. This will start the server, after which you can open the application in your browser of choice. Detailed description below.
**Important:** Make sure that you whitelist the interface/ip (`ipWhitelist`) in the server config where you want the client to connect to, otherwise it will not be allowed to connect to the server. You also need to set the local host `address` field to `0.0.0.0` in order for the RPi to listen on all interfaces and not only `localhost` (default).
```javascript
var config = {
address: "0.0.0.0", // default is "localhost"
port: 8080, // default
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:172.17.0.1"], // default -- need to add your IP here
...
};
```
### Client Only
This is when you already have a server running remotely and want your RPi to connect as a standalone client to this instance, to show the MM from the server. Then from your RPi, you run it with: `node clientonly --address 192.168.1.5 --port 8080`. (Specify the ip address and port number of the server)
### Docker
MagicMirror² in server only mode can be deployed using [Docker](https://docker.com). After a successful [Docker installation](https://docs.docker.com/engine/installation/) you just need to execute the following command in the shell:
```bash
docker run -d \
--publish 80:8080 \
--restart always \
--volume ~/magic_mirror/config:/opt/magic_mirror/config \
--volume ~/magic_mirror/modules:/opt/magic_mirror/modules \
--name magic_mirror \
bastilimbach/docker-magicmirror
```
To get more information about the available Dockerfile versions and configurations head over to the respective [GitHub repository](https://github.com/bastilimbach/docker-MagicMirror).
## Configuration
### Raspberry Specific
The following wiki links are helpful for the initial configuration of your MagicMirror² operating system:
- [Configuring the Raspberry Pi](https://github.com/MichMich/MagicMirror/wiki/Configuring-the-Raspberry-Pi)
- [Auto Starting MagicMirror](https://github.com/MichMich/MagicMirror/wiki/Auto-Starting-MagicMirror)
### General
1. Copy `/home/pi/MagicMirror/config/config.js.sample` to `/home/pi/MagicMirror/config/config.js`. \
**Note:** If you used the installer script. This step is already done for you.
2. Modify your required settings. \
Note: You can check your configuration running `npm run config:check` in `/home/pi/MagicMirror`.
The following properties can be configured:
| **Option** | **Description** |
| --- | --- |
| `port` | The port on which the MagicMirror² server will run on. The default value is `8080`. |
| `address` | The *interface* ip address on which to accept connections. The default is `localhost`, which would prevent exposing the built-in webserver to machines on the local network. To expose it to other machines, use: `0.0.0.0`. |
| `ipWhitelist` | The list of IPs from which you are allowed to access the MagicMirror². The default value is `["127.0.0.1", "::ffff:127.0.0.1", "::1"]`, which is from `localhost` only. Add your IP when needed. You can also specify IP ranges with subnet masks (`["127.0.0.1", "127.0.0.1/24"]`) or directly with (`["127.0.0.1", ["192.168.0.1", "192.168.0.100"]]`). Set `[]` to allow all IP addresses. For more information see: [follow post ipWhitelist HowTo](https://forum.magicmirror.builders/topic/1326/ipwhitelist-howto) |
| `zoom` | This allows to scale the mirror contents with a given zoom factor. The default value is `1.0`|
| `language` | The language of the interface. (Note: Not all elements will be localized.) Possible values are `en`, `nl`, `ru`, `fr`, etc., but the default value is `en`. |
| `timeFormat` | The form of time notation that will be used. Possible values are `12` or `24`. The default is `24`. |
| `units` | The units that will be used in the default weather modules. Possible values are `metric` or `imperial`. The default is `metric`. |
| `modules` | An array of active modules. **The array must contain objects. See the next table below for more information.** |
| `electronOptions` | An optional array of Electron (browser) options. This allows configuration of e.g. the browser screen size and position (example: `electronOptions: { fullscreen: false, width: 800, height: 600 }`). Kiosk mode can be enabled by setting `kiosk: true`, `autoHideMenuBar: false` and `fullscreen: false`. More options can be found [here](https://github.com/electron/electron/blob/master/docs/api/browser-window.md). |
| `customCss` | The path of the `custom.css` stylesheet. The default is `css/custom.css`. |
Module configuration:
| **Option** | **Description** |
| --- | --- |
| `module` | The name of the module. This can also contain the subfolder. Valid examples include `clock`, `default/calendar` and `custommodules/mymodule`. |
| `position` | The location of the module in which the module will be loaded. Possible values are `top_bar`, `top_left`, `top_center`, `top_right`, `upper_third`, `middle_center`, `lower_third`, `bottom_left`, `bottom_center`, `bottom_right`, `bottom_bar`, `fullscreen_above`, and `fullscreen_below`. This field is optional but most modules require this field to set. Check the documentation of the module for more information. Multiple modules with the same position will be ordered based on the order in the configuration file. |
| `classes` | Additional classes which are passed to the module. The field is optional. |
| `header` | To display a header text above the module, add the header property. This field is optional. |
| `disabled` | Set disabled to `true` to skip creating the module. This field is optional. |
| `config` | An object with the module configuration properties. Check the documentation of the module for more information. This field is optional, unless the module requires extra configuration. |
## Modules
The following modules are installed by default.
- [**Clock**](modules/default/clock)
- [**Calendar**](modules/default/calendar)
- [**Current Weather**](modules/default/currentweather)
- [**Weather Forecast**](modules/default/weatherforecast)
- [**News Feed**](modules/default/newsfeed)
- [**Compliments**](modules/default/compliments)
- [**Hello World**](modules/default/helloworld)
- [**Alert**](modules/default/alert)
For more available modules, check out out the wiki page [MagicMirror² 3rd Party Modules](https://github.com/MichMich/MagicMirror/wiki/3rd-party-modules). If you want to build your own modules, check out the [MagicMirror² Module Development Documentation](modules) and don't forget to add it to the wiki and the [forum](https://forum.magicmirror.builders/category/7/showcase)!
## Updating
If you want to update your MagicMirror² to the latest version, use your terminal to go to your Magic Mirror folder and type the following command:
```
bash -c "$(curl -sL https://raw.githubusercontent.com/MichMich/MagicMirror/master/installers/upgrade-script.sh)"
```
This will do a test run
If the test update looks good then run this command
```
bash -c "$(curl -sL https://raw.githubusercontent.com/MichMich/MagicMirror/master/installers/upgrade-script.sh)" apply
```
If there are changes you have made, they will be listed, and u will have the opportunity to save your work
The script will also update the dependencies of any active modules
If there are update issues, please come to the forums for help
## Community
The community around the MagicMirror² is constantly growing. We even have a [forum](https://forum.magicmirror.builders) now where you can share your ideas, ask questions, help others and get inspired by other builders. We would love to see you there!
- 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)
- Technical discussions: https://forum.magicmirror.builders/category/11/core-system
- 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)
## Contributing Guidelines
Contributions of all kinds are welcome, not only in the form of code but also with regards bug reports and documentation.
Contributions of all kinds are welcome, not only in the form of code but also with regards to
Please keep the following in mind:
- **Bug Reports**: Make sure you're running the latest version. If the issue(s) still persist: please open a clearly documented issue with a clear title.
- **Minor Bug Fixes**: Please send a pull request with a clear explanation of the issue or a link to the issue it solves.
- **Major Bug Fixes**: please discuss your approach in an GitHub issue before you start to alter a big part of the code.
- **New Features**: please please discuss in a GitHub issue before you start to alter a big part of the code. Without discussion upfront, the pull request will not be accepted / merged.
Thanks for your help in making MagicMirror² better!
- bug reports
- documentation
- translations
For the full contribution guidelines, check out: [https://docs.magicmirror.builders/getting-started/contributing.html](https://docs.magicmirror.builders/getting-started/contributing.html)
## Enjoying MagicMirror? Consider a donation!
@@ -217,23 +51,6 @@ If we receive enough donations we might even be able to free up some working hou
To donate, please follow [this](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=G5D8E9MR5DTD2&source=url) link.
## Manifesto
A real Manifesto is still to be written. Till then, Michael's response on [one of the repository issues](https://github.com/MichMich/MagicMirror/issues/1174) gives a great summary:
> "... I started this project as an ultimate starter project for Raspberry Pi enthusiasts. As a matter of fact, for most of the contributors, the MagicMirror project is the first open source project they ever contributed to. This is one of the reasons why the MagicMirror project is featured in several RasPi magazines.
>
>The project has a lot of opportunities for improvement. We could use a powerful framework like Vue to ramp up the development speed. We could use SASS for better/easier css implementations. We could make it an NPM installable package. And as you say, we could bundle it up. The big downside of of of these changes is that it over complicates things: a user no longer will be able to open just one file and make a small modification and see how it works out.
>
>Of course, a bundled version can be complimentary to the regular un-bundled version. And I'm sure a lot of (new) users will opt for the bundled version. But this means those users won't be motivated to take a peek under the hood. They will just remain 'users'. They won't become contributors, and worse: they won't be motivated to take their first steps in software development.
>
>And to be honest: motivating curious users to step out of their comfort zone and take those first steps is what drives me in this project. Therefore my ultimate goal is this project is to keep it as accessible as possible."
>
> ~ Michael Teeuw
<p align="center">
<br>
<p style="text-align: center">
<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>

View File

@@ -1,18 +1,24 @@
/* jshint esversion: 6 */
"use strict";
// Use separate scope to prevent global scope pollution
(function () {
var config = {};
const config = {};
// Helper function to get server address/hostname from either the commandline or env
/**
* Helper function to get server address/hostname from either the commandline or env
*/
function getServerAddress() {
// Helper function to get command line parameters
// Assumes that a cmdline parameter is defined with `--key [value]`
/**
* 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;
const index = process.argv.indexOf(`--${key}`);
const value = index > -1 ? process.argv[index + 1] : undefined;
return value !== undefined ? String(value) : defaultValue;
}
@@ -20,37 +26,52 @@
["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 reqested url
// 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 = "";
let configData = "";
// Gather incoming data
response.on("data", function(chunk) {
response.on("data", function (chunk) {
configData += chunk;
});
// Resolve promise at the end of the HTTP/HTTPS stream
response.on("end", function() {
response.on("end", function () {
resolve(JSON.parse(configData));
});
});
request.on("error", function(error) {
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'");
console.log("Usage: 'node clientonly --address 192.168.1.10 --port 8080 [--use-tls]'");
}
process.exit(code);
}
@@ -58,16 +79,18 @@
getServerAddress();
(config.address && config.port) || fail();
const 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(`http://${config.address}:${config.port}/config/`)
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 };
const env = Object.create(process.env);
const options = { env: env };
configReturn.address = config.address;
configReturn.port = config.port;
configReturn.tls = config.tls;
env.config = JSON.stringify(configReturn);
// Spawn electron application
@@ -93,7 +116,6 @@
console.log(`There something wrong. The clientonly is not running code ${code}`);
}
});
})
.catch(function (reason) {
fail(`Unable to connect to server: (${reason})`);
@@ -101,4 +123,4 @@
} else {
fail();
}
}());
})();

2
config/.gitignore vendored
View File

@@ -1,2 +0,0 @@
*
!config.js.sample

View File

@@ -1,35 +1,42 @@
/* Magic Mirror Config Sample
*
* By Michael Teeuw http://michaelteeuw.nl
* By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed.
*
* For more information how you can configurate this file
* See https://github.com/MichMich/MagicMirror#configuration
*
* For more information on how you can configure this file
* see https://docs.magicmirror.builders/getting-started/configuration.html#general
* and https://docs.magicmirror.builders/modules/configuration.html
*/
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, is "localhost"
let 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,
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"],
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",
locale: "en-US",
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
// 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",
@@ -50,7 +57,8 @@ var config = {
calendars: [
{
symbol: "calendar-check",
url: "webcal://www.calendarlabs.com/ical-calendar/ics/76/US_Holidays.ics" }
url: "webcal://www.calendarlabs.com/ical-calendar/ics/76/US_Holidays.ics"
}
]
}
},
@@ -59,22 +67,26 @@ var config = {
position: "lower_third"
},
{
module: "currentweather",
module: "weather",
position: "top_right",
config: {
weatherProvider: "openweathermap",
type: "current",
location: "New York",
locationID: "", //ID from http://bulk.openweathermap.org/sample/city.list.json.gz; unzip the gz file and find your city
appid: "YOUR_OPENWEATHER_API_KEY"
locationID: "5128581", //ID from http://bulk.openweathermap.org/sample/city.list.json.gz; unzip the gz file and find your city
apiKey: "YOUR_OPENWEATHER_API_KEY"
}
},
{
module: "weatherforecast",
module: "weather",
position: "top_right",
header: "Weather Forecast",
config: {
weatherProvider: "openweathermap",
type: "forecast",
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"
locationID: "5128581", //ID from http://bulk.openweathermap.org/sample/city.list.json.gz; unzip the gz file and find your city
apiKey: "YOUR_OPENWEATHER_API_KEY"
}
},
{
@@ -84,7 +96,7 @@ var config = {
feeds: [
{
title: "New York Times",
url: "http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml"
url: "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml"
}
],
showSourceTitle: true,
@@ -94,7 +106,6 @@ var config = {
}
},
]
};
/*************** DO NOT EDIT THE LINE BELOW ***************/

31
css/custom.css.sample Normal file
View File

@@ -0,0 +1,31 @@
/* Magic Mirror Custom CSS Sample
*
* Change color and fonts here.
*
* Beware that properties cannot be unitless, so for example write '--gap-body: 0px;' instead of just '--gap-body: 0;'
*
* MIT Licensed.
*/
/* Uncomment and adjust accordingly if you want to import another font from the google-fonts-api: */
/* @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@100;300;400;700&display=swap'); */
:root {
--color-text: #999;
--color-text-dimmed: #666;
--color-text-bright: #fff;
--color-background: black;
--font-primary: "Roboto Condensed";
--font-secondary: "Roboto";
--font-size: 20px;
--font-size-small: 0.75rem;
--gap-body-top: 60px;
--gap-body-right: 60px;
--gap-body-bottom: 60px;
--gap-body-left: 60px;
--gap-modules: 30px;
}

View File

@@ -1,7 +1,29 @@
:root {
--color-text: #999;
--color-text-dimmed: #666;
--color-text-bright: #fff;
--color-background: #000;
--font-primary: "Roboto Condensed";
--font-secondary: "Roboto";
--font-size: 20px;
--font-size-small: 0.75rem;
--gap-body-top: 60px;
--gap-body-right: 60px;
--gap-body-bottom: 60px;
--gap-body-left: 60px;
--gap-modules: 30px;
}
html {
cursor: none;
overflow: hidden;
background: #000;
background: var(--color-background);
user-select: none;
font-size: var(--font-size);
}
::-webkit-scrollbar {
@@ -9,16 +31,15 @@ html {
}
body {
margin: 60px;
margin: var(--gap-body-top) var(--gap-body-right) var(--gap-body-bottom) var(--gap-body-left);
position: absolute;
height: calc(100% - 120px);
width: calc(100% - 120px);
background: #000;
color: #aaa;
font-family: "Roboto Condensed", sans-serif;
height: calc(100% - var(--gap-body-top) - var(--gap-body-bottom));
width: calc(100% - var(--gap-body-right) - var(--gap-body-left));
background: var(--color-background);
color: var(--color-text);
font-family: var(--font-primary), sans-serif;
font-weight: 400;
font-size: 2em;
line-height: 1.5em;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
@@ -27,60 +48,60 @@ body {
*/
.dimmed {
color: #666;
color: var(--color-text-dimmed);
}
.normal {
color: #999;
color: var(--color-text);
}
.bright {
color: #fff;
color: var(--color-text-bright);
}
.xsmall {
font-size: 15px;
line-height: 20px;
font-size: var(--font-size-small);
line-height: 1.275;
}
.small {
font-size: 20px;
line-height: 25px;
font-size: 1rem;
line-height: 1.25;
}
.medium {
font-size: 30px;
line-height: 35px;
font-size: 1.5rem;
line-height: 1.225;
}
.large {
font-size: 65px;
line-height: 65px;
font-size: 3.25rem;
line-height: 1;
}
.xlarge {
font-size: 75px;
line-height: 75px;
font-size: 3.75rem;
line-height: 1;
letter-spacing: -3px;
}
.thin {
font-family: Roboto, sans-serif;
font-family: var(--font-secondary), sans-serif;
font-weight: 100;
}
.light {
font-family: "Roboto Condensed", sans-serif;
font-family: var(--font-primary), sans-serif;
font-weight: 300;
}
.regular {
font-family: "Roboto Condensed", sans-serif;
font-family: var(--font-primary), sans-serif;
font-weight: 400;
}
.bold {
font-family: "Roboto Condensed", sans-serif;
font-family: var(--font-primary), sans-serif;
font-weight: 700;
}
@@ -94,14 +115,14 @@ body {
header {
text-transform: uppercase;
font-size: 15px;
font-family: "Roboto Condensed", Arial, Helvetica, sans-serif;
font-size: var(--font-size-small);
font-family: var(--font-primary), Arial, Helvetica, sans-serif;
font-weight: 400;
border-bottom: 1px solid #666;
border-bottom: 1px solid var(--color-text-dimmed);
line-height: 15px;
padding-bottom: 5px;
margin-bottom: 10px;
color: #999;
color: var(--color-text);
}
sup {
@@ -114,11 +135,11 @@ sup {
*/
.module {
margin-bottom: 30px;
margin-bottom: var(--gap-modules);
}
.region.bottom .module {
margin-top: 30px;
margin-top: var(--gap-modules);
margin-bottom: 0;
}
@@ -142,10 +163,10 @@ sup {
.region.fullscreen {
position: absolute;
top: -60px;
left: -60px;
right: -60px;
bottom: -60px;
top: calc(-1 * var(--gap-body-top));
left: calc(-1 * var(--gap-body-left));
right: calc(-1 * var(--gap-body-right));
bottom: calc(-1 * var(--gap-body-bottom));
pointer-events: none;
}
@@ -162,25 +183,9 @@ sup {
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%;
-moz-transform: translateX(-50%);
-o-transform: translateX(-50%);
-webkit-transform: translateX(-50%);
-ms-transform: translateX(-50%);
transform: translateX(-50%);
}
@@ -194,10 +199,6 @@ sup {
bottom: 0;
}
.region.bottom .container:empty {
margin-top: 0;
}
.region.bottom.right,
.region.bottom.center,
.region.bottom.left {
@@ -213,10 +214,6 @@ sup {
.region.middle.center {
width: 100%;
text-align: center;
-moz-transform: translateY(-50%);
-o-transform: translateY(-50%);
-webkit-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}

View File

@@ -1,17 +0,0 @@
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.");
}
}

View File

@@ -1,12 +1,26 @@
{
"name": "magicmirror-fonts",
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"roboto-fontface": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.8.0.tgz",
"integrity": "sha512-ZYzRkETgBrdEGzL5JSKimvjI2CX7ioyZCkX2BpcfyjqI+079W0wHAyj5W4rIZMcDSOHgLZtgz1IdDi/vU77KEQ=="
}
}
"name": "magicmirror-fonts",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "magicmirror-fonts",
"license": "MIT",
"dependencies": {
"roboto-fontface": "^0.10.0"
}
},
"node_modules/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=="
}
},
"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=="
}
}
}

View File

@@ -1,15 +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.8.0"
}
"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"
}
}

View File

@@ -2,94 +2,57 @@
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"),
url("node_modules/roboto-fontface/fonts/roboto/Roboto-Thin.ttf") format("truetype");
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"),
url("node_modules/roboto-fontface/fonts/roboto-condensed/Roboto-Condensed-Light.ttf") format("truetype");
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"),
url("node_modules/roboto-fontface/fonts/roboto-condensed/Roboto-Condensed-Regular.ttf") format("truetype");
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"),
url("node_modules/roboto-fontface/fonts/roboto-condensed/Roboto-Condensed-Bold.ttf") format("truetype");
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"),
url("node_modules/roboto-fontface/fonts/roboto/Roboto-Regular.ttf") format("truetype");
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"),
url("node_modules/roboto-fontface/fonts/roboto/Roboto-Medium.ttf") format("truetype");
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"),
url("node_modules/roboto-fontface/fonts/roboto/Roboto-Bold.ttf") format("truetype");
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"),
url("node_modules/roboto-fontface/fonts/roboto/Roboto-Light.ttf") format("truetype");
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");
}

View File

@@ -1,7 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
roboto-fontface@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/roboto-fontface/-/roboto-fontface-0.8.0.tgz#031a83c8f79932801a57d83bf743f37250163499"

View File

@@ -1,55 +1,57 @@
<!DOCTYPE html>
<html>
<head>
<title>MagicMirror²</title>
<meta name="google" content="notranslate" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<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">
<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. -->
<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>
<script type="text/javascript">
window.mmVersion = "#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>

View File

@@ -1,15 +0,0 @@
const config = require('../config/config.js');const fs=require('fs');
for(let m of config.modules){
if(!(m.disabled || false)){
try {
let f=fs.statSync(m.module);
if(f.isDirectory()){
f1=fs.statSync(m.module+'/package.json');
if (f1.isFile()){
console.log(m.module);
}
}
}
catch (ex) {}
}
}

View File

@@ -1,183 +0,0 @@
#!/bin/bash
# Define the tested version of Node.js.
NODE_TESTED="v10.1.0"
NPM_TESTED="V6.0.0"
USER=`whoami`
PM2_FILE=pm2_MagicMirror.json
mac=$(uname -s)
if [ $mac == 'Darwin' ]; then
cmd=greadlink
else
cmd=readlink
fi
if [ -d ~/MagicMirror ]; then
# put the log where the script is located
logdir=$(dirname $($cmd -f "$0"))
# if the script was execute from the web
if [[ $logdir != *"MagicMirror/installers"* ]]; then
# use the MagicMirror/installers folder
cd ~/MagicMirror/installers >/dev/null
logdir=$(pwd)
cd - >/dev/null
fi
logfile=$logdir/pm2_setup.log
echo the log will be saved in $logfile
date +"pm2 setup starting - %a %b %e %H:%M:%S %Z %Y" >>$logfile
echo system is $(uname -a) >> $logfile
if [ "$mac" == "Darwin" ]; then
echo the os is macOS $(sw_vers -productVersion) >> $logfile
else
echo the os is $(lsb_release -a 2>/dev/null) >> $logfile
fi
node_installed=$(which node)
if [ "$node_installed." == "." ]; then
# node not installed
echo Installing node >>$logfile
if [ $mac == 'Darwin' ]; then
brew install node
else
NODE_STABLE_BRANCH="10.x"
curl -sL https://deb.nodesource.com/setup_$NODE_STABLE_BRANCH | sudo -E bash -
sudo apt-get install -y nodejs
fi
fi
node_version=$(node -v)
echo node version $node_version >>$logfile
npm_installed=$(which npm)
if [ "$npm_installed." == "." ]; then
# npm not installed
echo Installing npm >>$logfile
if [ $mac != 'Darwin' ]; then
sudo apt-get install -y npm
fi
fi
# get latest
echo force installing latest npm version via npm >>$logfile
#sudo npm i -g npm
npm_version=$(npm -v)
echo npm version $npm_version >>$logfile
# assume pm2 will be found on the path
pm2cmd=pm2
up=""
if [ $mac == 'Darwin' ]; then
up="--unsafe-perm"
launchctl=launchctl
launchctl_path=$(which $launchctl)
`export PATH=$PATH:${launchctl_path%/$launchctl}`
fi
# check to see if already installed
pm2_installed=$(which $pm2cmd)
if [ "$pm2_installed." != "." ]; then
# does it work?
echo pm2 installed >> $logfile
pm2_fails=$(pm2 list | grep -i -m 1 "App Name" | wc -l )
if [ $pm2_fails != 1 ]; then
# uninstall it
echo pm2 installed, but does not work, uninstalling >> $logfile
sudo npm uninstall $up -g pm2
# force reinstall
pm2_installed=
fi
fi
# in not installed
if [ "$pm2_installed." == "." ]; then
# install it.
echo pm2 not installed, installing >>$logfile
result=$(sudo npm install $up -g pm2)
# if this is a mac
if [ $mac == 'Darwin' ]; then
echo this is a mac, fixup for path >>$logfile
# get the location of pm2 install
# parse the npm install output to get the command
pm2cmd=`echo $result | awk -F - '{print $1}' | tr -d '[:space:]'`
c='/pm2'
# get the path only
echo ${pm2cmd%$c} >installers/pm2path
fi
fi
# remove MagicMirror if defined
$pm2cmd delete MagicMirror >/dev/null 2>&1
cd ~/MagicMirror
echo get the pm2 platform specific startup command >>$logfile
# get the platform specific pm2 startup command
v=$($pm2cmd startup | tail -n 1)
if [ $mac != 'Darwin' ]; then
# check to see if we can get the OS package name (Ubuntu)
if [ $(which lsb_release| wc -l) >0 ]; then
# fix command
# if ubuntu 18.04, pm2 startup gets something wrong
if [ $(lsb_release -r | grep -m1 18.04 | wc -l) > 0 ]; then
v=$(echo $v | sed 's/\/bin/\/bin:\/bin/')
fi
fi
fi
echo startup command = $v >>$logfile
# execute the command returned
$v 2>&1 >>$logfile
echo pm2 startup command done >>$logfile
# is this is mac
# need to fix pm2 startup, only on catalina
if [ $mac == 'Darwin' ]; then
if [ $(sw_vers -productVersion | head -c 6) == '10.15.' ]; then
# only do if the faulty tag is present (pm2 may fix this, before the script is fixed)
if [ $(grep -m 1 UserName /Users/$USER/Library/LaunchAgents/pm2.$USER.plist | wc -l) -eq 1 ]; then
# copy the pm2 startup file config
cp /Users/$USER/Library/LaunchAgents/pm2.$USER.plist .
# edit out the UserName key/value strings
sed -e '/UserName/{N;d;}' pm2.$USER.plist > pm2.$USER.plist.new
# copy the file back
sudo cp pm2.$USER.plist.new /Users/$USER/Library/LaunchAgents/pm2.$USER.plist
fi
fi
fi
# if the user is no pi, we have to fixup the pm2 json file
echo configure the pm2 config file for MagicMirror >>$logfile
if [ "$USER" != "pi" ]; then
echo the user is not pi >>$logfile
# go to the installers folder`
cd installers
# edit the startup script for the right user
echo change mm.sh >>$logfile
if [ ! -e mm_temp.sh ]; then
echo save copy of mm.sh >> $logfile
cp mm.sh mm_temp.sh
fi
if [ $(grep pi mm_temp.sh | wc -l) -gt 0 ]; then
echo change hard coded pi username >> $logfile
sed 's/pi/'$USER'/g' mm_temp.sh >mm.sh
else
echo change relative home path to hard coded path >> $logfile
hf=$(echo $HOME |sed 's/\//\\\//g')
sed 's/\~/'$hf'/g' mm_temp.sh >mm.sh
fi
# edit the pms config file for the right user
echo change $PM2_FILE >>$logfile
sed 's/pi/'$USER'/g' $PM2_FILE > pm2_MagicMirror_new.json
# make sure to use the updated file
PM2_FILE=pm2_MagicMirror_new.json
# if this is a mac
if [ $mac == 'Darwin' ]; then
# copy the path file to the system paths list
sudo cp ./pm2path /etc/paths.d
# change the name of the home path for mac
sed 's/home/Users/g' $PM2_FILE > pm2_MagicMirror_new1.json
# make sure to use the updated file
PM2_FILE=pm2_MagicMirror_new1.json
fi
echo now using this config file $PM2_FILE >>$logfile
# go back one cd level
cd - >/dev/null
fi
echo start MagicMirror via pm2 now >>$logfile
# tell pm2 to start the app defined in the config file
$pm2cmd start $HOME/MagicMirror/installers/$PM2_FILE
# tell pm2 to save that configuration, for start at boot
echo save MagicMirror pm2 config now >>$logfile
$pm2cmd save
date +"pm2 setup completed - %a %b %e %H:%M:%S %Z %Y" >>$logfile
else
echo It appears MagicMirror has not been installed on this system
echo please run the installer, "raspberry.sh" first
fi

View File

@@ -1,2 +1,4 @@
#!/bin/bash
# This file is still here to keep PM2 working on older installations.
cd ~/MagicMirror
DISPLAY=:0 npm start

View File

@@ -1,7 +0,0 @@
{
"apps" : [{
"name" : "MagicMirror",
"script" : "/home/pi/MagicMirror/installers/mm.sh",
"watch" : ["/home/pi/MagicMirror/config/config.js"]
}]
}

View File

@@ -1,2 +0,0 @@
echo "\033[32mMagicMirror installation successful!"
exit 0

View File

@@ -1,569 +0,0 @@
#!/bin/bash
# This is an installer script for MagicMirror2. It works well enough
# that it can detect if you have Node installed, run a binary script
# and then download and run MagicMirror2.
if [ $USER == 'root' ]; then
echo Please logon as a user to execute the MagicMirror installation, not root
exit 1
fi
echo -e "\e[0m"
echo '$$\ $$\ $$\ $$\ $$\ $$\ $$$$$$\'
echo '$$$\ $$$ | \__| $$$\ $$$ |\__| $$ __$$\'
echo '$$$$\ $$$$ | $$$$$$\ $$$$$$\ $$\ $$$$$$$\ $$$$\ $$$$ |$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ \__/ $$ |'
echo '$$\$$\$$ $$ | \____$$\ $$ __$$\ $$ |$$ _____|$$\$$\$$ $$ |$$ |$$ __$$\ $$ __$$\ $$ __$$\ $$ __$$\ $$$$$$ |'
echo '$$ \$$$ $$ | $$$$$$$ |$$ / $$ |$$ |$$ / $$ \$$$ $$ |$$ |$$ | \__|$$ | \__|$$ / $$ |$$ | \__|$$ ____/'
echo '$$ |\$ /$$ |$$ __$$ |$$ | $$ |$$ |$$ | $$ |\$ /$$ |$$ |$$ | $$ | $$ | $$ |$$ | $$ |'
echo '$$ | \_/ $$ |\$$$$$$$ |\$$$$$$$ |$$ |\$$$$$$$\ $$ | \_/ $$ |$$ |$$ | $$ | \$$$$$$ |$$ | $$$$$$$$\'
echo '\__| \__| \_______| \____$$ |\__| \_______|\__| \__|\__|\__| \__| \______/ \__| \________|'
echo ' $$\ $$ |'
echo ' \$$$$$$ |'
echo ' \______/'
echo -e "\e[0m"
doInstall=1
true=1
false=0
# Define the tested version of Node.js.
NODE_TESTED="v10.1.0"
NPM_TESTED="V6.0.0"
USER=`whoami`
PM2_FILE=pm2_MagicMirror.json
force_arch=
pm2setup=$false
trim() {
local var="$*"
# remove leading whitespace characters
var="${var#"${var%%[![:space:]]*}"}"
# remove trailing whitespace characters
var="${var%"${var##*[![:space:]]}"}"
echo -n "$var"
}
mac=$(uname -s)
if [ $mac == 'Darwin' ]; then
echo this is a mac | tee -a $logfile
cmd=greadlink
else
cmd=readlink
fi
# put the log where the script is located
logdir=$(dirname $($cmd -f "$0"))
# if the script was execute from the web
if [[ $logdir != *"MagicMirror/installers"* ]]; then
# use the MagicMirror/installers folder, if setup
if [ -d MagicMirror ]; then
cd ~/MagicMirror/installers >/dev/null
logdir=$(pwd)
cd - >/dev/null
else
# use the users home folder if initial install
logdir=$HOME
fi
fi
logfile=$logdir/install.log
echo install log being saved to $logfile
# Determine which Pi is running.
date +"install starting - %a %b %e %H:%M:%S %Z %Y" >>$logfile
ARM=$(uname -m)
echo installing on $ARM processor system >>$logfile
echo the os is $(lsb_release -a 2>/dev/null) >> $logfile
# Check the Raspberry Pi version.
if [ "$ARM" != "armv7l" ]; then
read -p "this appears not to be a Raspberry Pi 2 or 3, do you want to continue installation (y/N)?" choice
if [[ $choice =~ ^[Nn]$ ]]; then
echo user stopped install on $ARM hardware >>$logfile
echo -e "\e[91mSorry, your Raspberry Pi is not supported."
echo -e "\e[91mPlease run MagicMirror on a Raspberry Pi 2 or 3."
echo -e "\e[91mIf this is a Pi Zero, the setup will configure to run in server only mode wih a local browser."
exit;
fi
#if [ "$ARM" == "armv6l" ]; then
# echo forcing armv71 architecture for pi 0 >>$logfile
# force_arch=-'--arch=armv7l'
# fi
fi
# Define helper methods.
function command_exists () { type "$1" &> /dev/null ;}
function verlte() { [ "$1" = "`echo -e "$1\n$2" | sort -V | head -n1`" ];}
function verlt() { [ "$1" = "$2" ] && return 1 || verlte $1 $2 ;}
# Update before first apt-get
if [ $mac != 'Darwin' ]; then
echo -e "\e[96mUpdating packages ...\e[90m" | tee -a $logfile
upgrade=$false
update=$(sudo apt-get update 2>&1)
echo $update >> $logfile
update_rc=$?
if [ $update_rc -ne 0 ]; then
echo -e "\e[91mUpdate failed, retrying installation ...\e[90m" | tee -a $logfile
if [ $(echo $update | grep "apt-secure" | wc -l) -eq 1 ]; then
update=$(sudo apt-get update --allow-releaseinfo-change 2>&1)
update_rc=$?
echo $update >> $logfile
if [ $update_rc -ne 0 ]; then
echo "second apt-get update failed" $update | tee -a $logfile
exit 1
else
echo "second apt-get update completed ok" >> $logfile
upgrade=$true
fi
fi
else
echo "apt-get update completed ok" >> $logfile
upgrade=$true
fi
if [ $upgrade -eq $true ]; then
upgrade_result=$(sudo apt-get upgrade 2>&1)
upgrade_rc=$?
echo apt upgrade result ="rc=$upgrade_rc $upgrade_result" >> $logfile
fi
# Installing helper tools
echo -e "\e[96mInstalling helper tools ...\e[90m" | tee -a $logfile
sudo apt-get --assume-yes install curl wget git build-essential unzip || exit
fi
# Check if we need to install or upgrade Node.js.
echo -e "\e[96mCheck current Node installation ...\e[0m" | tee -a $logfile
NODE_INSTALL=false
if command_exists node; then
echo -e "\e[0mNode currently installed. Checking version number." | tee -a $logfile
NODE_CURRENT=$(node -v)
if [ "$NODE_CURRENT." == "." ]; then
NODE_CURRENT="V1.0.0"
echo forcing low Node version >> $logfile
fi
echo -e "\e[0mMinimum Node version: \e[1m$NODE_TESTED\e[0m" | tee -a $logfile
echo -e "\e[0mInstalled Node version: \e[1m$NODE_CURRENT\e[0m" | tee -a $logfile
if verlte $NODE_CURRENT $NODE_TESTED; then
echo -e "\e[96mNode should be upgraded.\e[0m" | tee -a $logfile
NODE_INSTALL=true
# Check if a node process is currenlty running.
# If so abort installation.
if pgrep "node" > /dev/null; then
echo -e "\e[91mA Node process is currently running. Can't upgrade." | tee -a $logfile
echo "Please quit all Node processes and restart the installer." | tee -a $logfile
echo $(ps -ef | grep node | grep -v \-\-color) | tee -a $logfile
exit;
fi
else
echo -e "\e[92mNo Node.js upgrade necessary.\e[0m" | tee -a $logfile
fi
else
echo -e "\e[93mNode.js is not installed.\e[0m" | tee -a $logfile
NODE_INSTALL=true
fi
# Install or upgrade node if necessary.
if $NODE_INSTALL; then
echo -e "\e[96mInstalling Node.js ...\e[90m" | tee -a $logfile
# Fetch the latest version of Node.js from the selected branch
# The NODE_STABLE_BRANCH variable will need to be manually adjusted when a new branch is released. (e.g. 7.x)
# Only tested (stable) versions are recommended as newer versions could break MagicMirror.
if [ $mac == 'Darwin' ]; then
brew install node
else
NODE_STABLE_BRANCH="10.x"
# sudo apt-get install --only-upgrade libstdc++6
node_info=$(curl -sL https://deb.nodesource.com/setup_$NODE_STABLE_BRANCH | sudo -E bash - )
echo Node release info = $node_info >> $logfile
if [ "$(echo $node_info | grep "not currently supported")." == "." ]; then
sudo apt-get install -y nodejs
else
echo node $NODE_STABLE_BRANCH version installer not available, doing manually >>$logfile
# no longer supported install
sudo apt-get install -y --only-upgrade libstdc++6 >> $logfile
# have to do it manually
node_vnum=$(echo $NODE_STABLE_BRANCH | awk -F. '{print $1}')
# get the highest release number in the stable branch line for this processor architecture
node_ver=$(curl -sL https://unofficial-builds.nodejs.org/download/release/index.tab | grep $ARM | grep -m 1 v$node_vnum | awk '{print $1}')
echo latest release in the $NODE_STABLE_BRANCH family for $ARM is $node_ver >> $logfile
curl -sL https://unofficial-builds.nodejs.org/download/release/$node_ver/node-$node_ver-linux-$ARM.tar.gz >node_release-$node_ver.tar.gz
cd /usr/local
echo using release tar file = node_release-$node_ver.tar.gz >> $logfile
sudo tar --strip-components 1 -xzf $HOME/node_release-$node_ver.tar.gz
cd - >/dev/null
rm ./node_release-$node_ver.tar.gz
fi
# get the new node version number
new_ver=$(node -v 2>&1)
# if there is a failure to get it due to a missing library
if [ $(echo $new_ver | grep "not found" | wc -l) -ne 0 ]; then
#
sudo apt-get install -y --only-upgrade libstdc++6 >> $logfile
fi
echo node version is $(node -v 2>&1 >>$logfile)
fi
echo -e "\e[92mNode.js installation Done! version=$(node -v)\e[0m" | tee -a $logfile
fi
# Check if we need to install or upgrade npm.
echo -e "\e[96mCheck current NPM installation ...\e[0m" | tee -a $logfile
NPM_INSTALL=false
if command_exists npm; then
echo -e "\e[0mNPM currently installed. Checking version number." | tee -a $logfile
NPM_CURRENT='V'$(npm -v)
echo -e "\e[0mMinimum npm version: \e[1m$NPM_TESTED\e[0m" | tee -a $logfile
echo -e "\e[0mInstalled npm version: \e[1m$NPM_CURRENT\e[0m" | tee -a $logfile
if verlte $NPM_CURRENT $NPM_TESTED; then
echo -e "\e[96mnpm should be upgraded.\e[0m" | tee -a $logfile
NPM_INSTALL=true
# Check if a node process is currently running.
# If so abort installation.
if pgrep "npm" > /dev/null; then
echo -e "\e[91mA npm process is currently running. Can't upgrade." | tee -a $logfile
echo "Please quit all npm processes and restart the installer." | tee -a $logfile
exit;
fi
else
echo -e "\e[92mNo npm upgrade necessary.\e[0m" | tee -a $logfile
fi
else
echo -e "\e[93mnpm is not installed.\e[0m" | tee -a $logfile
NPM_INSTALL=true
fi
# Install or upgrade node if necessary.
if $NPM_INSTALL; then
echo -e "\e[96mInstalling npm ...\e[90m" | tee -a $logfile
# Fetch the latest version of npm from the selected branch
# The NODE_STABLE_BRANCH variable will need to be manually adjusted when a new branch is released. (e.g. 7.x)
# Only tested (stable) versions are recommended as newer versions could break MagicMirror.
#NODE_STABLE_BRANCH="9.x"
#curl -sL https://deb.nodesource.com/setup_$NODE_STABLE_BRANCH | sudo -E bash -
#
# if this is a mac, npm was installed with node
if [ $mac != 'Darwin' ]; then
sudo apt-get install -y npm >>$logfile
fi
# update to the latest.
echo upgrading npm to latest >> $logfile
sudo npm i -g npm >>$logfile
echo -e "\e[92mnpm installation Done! version=V$(npm -v)\e[0m" | tee -a $logfile
fi
# Install MagicMirror
cd ~
if [ $doInstall == 1 ]; then
if [ -d "$HOME/MagicMirror" ] ; then
echo -e "\e[93mIt seems like MagicMirror is already installed." | tee -a $logfile
echo -e "To prevent overwriting, the installer will be aborted." | tee -a $logfile
echo -e "Please rename the \e[1m~/MagicMirror\e[0m\e[93m folder and try again.\e[0m" | tee -a $logfile
echo ""
echo -e "If you want to upgrade your installation run \e[1m\e[97mupgrade-script\e[0m from the ~/MagicMirror/installers directory." | tee -a $logfile
echo ""
exit;
fi
echo -e "\e[96mCloning MagicMirror ...\e[90m" | tee -a $logfile
if git clone --depth=1 https://github.com/MichMich/MagicMirror.git; then
echo -e "\e[92mCloning MagicMirror Done!\e[0m" | tee -a $logfile
else
echo -e "\e[91mUnable to clone MagicMirror." | tee -a $logfile
exit;
fi
cd ~/MagicMirror || exit
if [ $(grep version package.json | awk -F: '{print $2}') == '"2.9.0",' -a $ARM == 'armv6l' ]; then
git fetch https://github.com/MichMich/MagicMirror.git develop >/dev/null 2>&1
git branch develop FETCH_HEAD > /dev/null 2>&1
git checkout develop > /dev/null 2>&1
fi
echo -e "\e[96mInstalling dependencies ...\e[90m" | tee -a $logfile
if npm install $force_arch; then
echo -e "\e[92mDependencies installation Done!\e[0m" | tee -a $logfile
else
echo -e "\e[91mUnable to install dependencies!" | tee -a $logfile
exit;
fi
# Use sample config for start MagicMirror
echo setting up initial config.js | tee -a $logfile
cp config/config.js.sample config/config.js
fi
# Check if plymouth is installed (default with PIXEL desktop environment), then install custom splashscreen.
echo -e "\e[96mCheck plymouth installation ...\e[0m" | tee -a $logfile
if command_exists plymouth; then
THEME_DIR="/usr/share/plymouth/themes"
echo -e "\e[90mSplashscreen: Checking themes directory.\e[0m" | tee -a $logfile
if [ -d $THEME_DIR ]; then
echo -e "\e[90mSplashscreen: Create theme directory if not exists.\e[0m" | tee -a $logfile
if [ ! -d $THEME_DIR/MagicMirror ]; then
sudo mkdir $THEME_DIR/MagicMirror
fi
if sudo cp ~/MagicMirror/splashscreen/splash.png $THEME_DIR/MagicMirror/splash.png && sudo cp ~/MagicMirror/splashscreen/MagicMirror.plymouth $THEME_DIR/MagicMirror/MagicMirror.plymouth && sudo cp ~/MagicMirror/splashscreen/MagicMirror.script $THEME_DIR/MagicMirror/MagicMirror.script; then
echo
if [ "$(which plymouth-set-default-theme)." != "." ]; then
if sudo plymouth-set-default-theme -R MagicMirror; then
echo -e "\e[92mSplashscreen: Changed theme to MagicMirror successfully.\e[0m" | tee -a $logfile
else
echo -e "\e[91mSplashscreen: Couldn't change theme to MagicMirror!\e[0m" | tee -a $logfile
fi
fi
else
echo -e "\e[91mSplashscreen: Copying theme failed!\e[0m" | tee -a $logfile
fi
else
echo -e "\e[91mSplashscreen: Themes folder doesn't exist!\e[0m" | tee -a $logfile
fi
else
echo -e "\e[93mplymouth is not installed.\e[0m" | tee -a $logfile
fi
# Use pm2 control like a service MagicMirror
read -p "Do you want use pm2 for auto starting of your MagicMirror (y/N)?" choice
if [[ $choice =~ ^[Yy]$ ]]; then
echo install and setup pm2 | tee -a $logfile
# assume pm2 will be found on the path
pm2cmd=pm2
# check to see if already installed
pm2_installed=$(which $pm2cmd)
up=""
if [ $mac == 'Darwin' ]; then
up="--unsafe-perm"
launchctl=launchctl
launchctl_path=$(which $launchctl)
`export PATH=$PATH:${launchctl_path%/$launchctl}`
fi
# check to see if already installed
pm2_installed=$(which $pm2cmd)
if [ "$pm2_installed." != "." ]; then
# does it work?
pm2_fails=$(pm2 list | grep -i -m 1 "App Name" | wc -l )
if [ $pm2_fails != 1 ]; then
# uninstall it
echo pm2 installed, but does not work, uninstalling >> $logfile
sudo npm uninstall $up -g pm2 >> $logfile
# force reinstall
pm2_installed=
fi
fi
# if not installed
if [ "$pm2_installed." == "." ]; then
# install it.
echo pm2 not installed, installing >>$logfile
result=$(sudo npm install $up -g pm2 2>&1)
echo pm2 install result $result >>$logfile
# if this is a mac
if [ $mac == 'Darwin' ]; then
echo this is a mac, fixup for path >>$logfile
# get the location of pm2 install
# parse the npm install output to get the command
pm2cmd=`echo $result | awk -F - '{print $1}' | tr -d '[:space:]'`
c='/pm2'
# get the path only
echo ${pm2cmd%$c} >installers/pm2path
fi
fi
echo get the pm2 platform specific startup command >>$logfile
# get the platform specific pm2 startup command
v=$($pm2cmd startup | tail -n 1)
if [ $mac != 'Darwin' ]; then
# check to see if we can get the OS package name (Ubuntu)
if [ $(which lsb_release| wc -l) >0 ]; then
# fix command
# if ubuntu 18.04, pm2 startup gets something wrong
if [ $(lsb_release -r | grep -m1 18.04 | wc -l) > 0 ]; then
v=$(echo $v | sed 's/\/bin/\/bin:\/bin/')
fi
fi
fi
echo startup command = $v >>$logfile
# execute the command returned
$v 2>&1 >>$logfile
echo pm2 startup command done >>$logfile
# is this is mac
# need to fix pm2 startup, only on catalina
if [ $mac == 'Darwin' ];then
if [ $(sw_vers -productVersion | head -c 6) == '10.15.' ]; then
# only do if the faulty tag is present (pm2 may fix this, before the script is fixed)
if [ $(grep -m 1 UserName /Users/$USER/Library/LaunchAgents/pm2.$USER.plist | wc -l) -eq 1 ]; then
# copy the pm2 startup file config
cp /Users/$USER/Library/LaunchAgents/pm2.$USER.plist .
# edit out the UserName key/value strings
sed -e '/UserName/{N;d;}' pm2.$USER.plist > pm2.$USER.plist.new
# copy the file back
sudo cp pm2.$USER.plist.new /Users/$USER/Library/LaunchAgents/pm2.$USER.plist
fi
fi
fi
# if the user is no pi, we have to fixup the pm2 json file
echo configure the pm2 config file for MagicMirror >>$logfile
if [ "$USER" != "pi" ]; then
echo the user is not pi >>$logfile
# go to the installers folder`
cd installers
# edit the startup script for the right user
echo change mm.sh >>$logfile
if [ ! -e mm_temp.sh ]; then
echo save copy of mm.sh >> $logfile
cp mm.sh mm_temp.sh
fi
if [ $(grep pi mm_temp.sh | wc -l) -gt 0 ]; then
echo change hard coded pi username >> $logfile
sed 's/pi/'$USER'/g' mm_temp.sh >mm.sh
else
echo change relative home path to hard coded path >> $logfile
hf=$(echo $HOME |sed 's/\//\\\//g')
sed 's/\~/'$hf'/g' mm_temp.sh >mm.sh
fi
# edit the pms config file for the right user
echo change $PM2_FILE >>$logfile
sed 's/pi/'$USER'/g' $PM2_FILE > pm2_MagicMirror_new.json
# make sure to use the updated file
PM2_FILE=pm2_MagicMirror_new.json
# if this is a mac
if [ $mac == 'Darwin' ]; then
# copy the path file to the system paths list
sudo cp ./pm2path /etc/paths.d
# change the name of the home path for mac
sed 's/home/Users/g' $PM2_FILE > pm2_MagicMirror_new1.json
# make sure to use the updated file
PM2_FILE=pm2_MagicMirror_new1.json
fi
echo now using this config file $PM2_FILE >>$logfile
# go back one cd level
cd - >/dev/null
fi
echo start MagicMirror via pm2 now >>$logfile
# tell pm2 to start the app defined in the config file
$pm2cmd start $HOME/MagicMirror/installers/$PM2_FILE
# tell pm2 to save that configuration, for start at boot
echo save MagicMirror pm2 config now >>$logfile
$pm2cmd save
pm2setup=$true
fi
# Disable Screensaver
choice=n
read -p "Do you want to disable the screen saver? (y/N)?" choice
if [[ $choice =~ ^[Yy]$ ]]; then
# if this is a mac
if [ $mac == 'Darwin' ]; then
# get the current setting
setting=$(defaults -currentHost read com.apple.screensaver idleTime)
# if its on
if [ $setting != 0 ] ; then
# turn it off
echo disable screensaver via mac profile >> $logfile
defaults -currentHost write com.apple.screensaver idleTime 0
else
echo mac profile screen saver already disabled >> $logfile
fi
else
# find out if some screen saver running
# get just the running processes and args
# just want the program name (1st token)
# find the 1st with 'saver' in it (should only be one)
# parse with path char, get the last field ( the actual pgm name)
screen_saver_running=$(ps -A -o args | awk '{print $1}' | grep -m1 [s]aver | awk -F\/ '{print $NF}');
# if we found something
if [ "$screen_saver_running." != "." ]; then
# some screensaver running
case "$screen_saver_running" in
mate-screensaver) echo 'mate screen saver' >>$logfile
#killall mate-screensaver >/dev/null 2>&1
#$ms -d >/dev/null 2>&1
gsettings set org.mate.screensaver lock-enabled false 2>/dev/null
gsettings set org.mate.screensaver idle-activation-enabled false 2>/dev/null
gsettings set org.mate.screensaver lock_delay 0 2>/dev/null
echo " $screen_saver_running disabled" >> $logfile
DISPLAY=:0 mate-screensaver >/dev/null 2>&1 &
;;
gnome-screensaver) echo 'gnome screen saver' >>$logfile
gnome_screensaver-command -d >/dev/null 2>&1
echo " $screen_saver_running disabled" >> $logfile
;;
xscreensaver) echo 'xscreensaver running' | tee -a $logfile
if [ $(grep -m1 'mode:' ~/.xscreensaver | awk '{print $2}') != 'off' ]; then
sed -i 's/$xsetting/mode: off/' ~/.xscreensaver
echo " xscreensaver set to off" >> $logfile
else
echo " xscreensaver already disabled" >> $logfile
fi
;;
gsd-screensaver | gsd-screensaver-proxy)
setting=$(gsettings get org.gnome.desktop.screensaver lock-enabled)
setting1=$(gsettings get org.gnome.desktop.session idle-delay)
if [ "$setting $setting1" != 'false uint32 0' ]; then
echo disable screensaver via gsettings was $setting and $setting1>> $logfile
gsettings set org.gnome.desktop.screensaver lock-enabled false
gsettings set org.gnome.desktop.screensaver idle-activation-enabled false
gsettings set org.gnome.desktop.session idle-delay 0
else
echo gsettings screen saver already disabled >> $logfile
fi
;;
*) echo "some other screensaver $screen_saver_running" found | tee -a $logfile
echo "please configure it manually" | tee -a $logfile
;;
esac
elif [ -e "/etc/lightdm/lightdm.conf" ]; then
# if screen saver NOT already disabled?
if [ $(grep 'xserver-command=X -s 0 -dpms' /etc/lightdm/lightdm.conf | wc -l) == 0 ]; then
echo install screensaver via lightdm.conf >> $logfile
sudo sed -i '/^\[Seat:\*\]/a xserver-command=X -s 0 -dpms' /etc/lightdm/lightdm.conf
else
echo screensaver via lightdm already disabled >> $logfile
fi
elif [ $(which gsettings | wc -l) == 1 ]; then
setting=$(gsettings get org.gnome.desktop.screensaver lock-enabled)
setting1=$(gsettings get org.gnome.desktop.session idle-delay)
if [ "$setting $setting1" != 'false uint32 0' ]; then
echo disable screensaver via gsettings was $setting and $setting1>> $logfile
gsettings set org.gnome.desktop.screensaver lock-enabled false
gsettings set org.gnome.desktop.screensaver idle-activation-enabled false
gsettings set org.gnome.desktop.session idle-delay 0
else
echo gsettings screen saver already disabled >> $logfile
fi
elif [ -d "/etc/xdg/lxsession" ]; then
currently_set=$(grep -m1 '\-dpms' /etc/xdg/lxsession/LXDE-pi/autostart)
if [ "$currently_set." == "." ]; then
echo disable screensaver via lxsession >> $logfile
# turn it off for the future
sudo su -c "echo -e '@xset s noblank\n@xset s off\n@xset -dpms' >> /etc/xdg/lxsession/LXDE-pi/autostart"
# turn it off now
export DISPLAY=:0; xset s noblank;xset s off;xset -dpms
else
echo lxsession screen saver already disabled >> $logfile
fi
else
echo " "
echo -e "unable to disable screen saver, /etc/xdg/lxsession does not exist" | tee -a $logfile
fi
fi
fi
echo " "
if [ $pm2setup -eq $true ]; then
rmessage="pm2 start MagicMirror"
else
rmessage="DISPLAY=:0 npm start"
fi
echo -e "\e[92mWe're ready! Run \e[1m\e[97m$rmessage\e[0m\e[92m from the ~/MagicMirror directory to start your MagicMirror.\e[0m" | tee -a $logfile
echo " "
echo " "
date +"install completed - %a %b %e %H:%M:%S %Z %Y" >>$logfile

View File

@@ -1,101 +0,0 @@
#/bin/bash
logfile=~/screensaver.log
mac=$(uname -s)
if [ $mac == 'Darwin' ]; then
setting=$(defaults -currentHost read com.apple.screensaver idleTime)
if [ $setting != 0 ] ; then
echo disable screensaver via mac profile >> $logfile
defaults -currentHost write com.apple.screensaver idleTime 0
else
echo mac profile screen saver already disabled >> $logfile
fi
else
# find out if some screen saver running
# get just the running processes and args
# just want the program name
# find the 1st with 'saver' in it (should only be one)
# if the process name is a path, parse it and get the last field ( the actual pgm name)
screen_saver_running=$(ps -A -o args | awk '{print $1}' | grep -m1 [s]aver | awk -F\/ '{print $NF}');
# if we found something
if [ "$screen_saver_running." != "." ]; then
# some screensaver running
case "$screen_saver_running" in
mate-screensaver) echo 'mate screen saver' >>$logfile
#killall mate-screensaver >/dev/null 2>&1
#ms=$(which mate-screensaver-command)
#$ms -d >/dev/null 2>&1
gsettings set org.mate.screensaver lock-enabled false 2>/dev/null
gsettings set org.mate.screensaver idle-activation-enabled false 2>/dev/null
gsettings set org.mate.screensaver lock_delay 0 2>/dev/null
echo " $screen_saver_running disabled" >> $logfile
DISPLAY=:0 mate-screensaver >/dev/null 2>&1 &
;;
gnome-screensaver) echo 'gnome screen saver' >>$logfile
gnome_screensaver-command -d >/dev/null 2>&1
echo " $screen_saver_running disabled" >> $logfile
;;
xscreensaver) echo 'xscreensaver running' | tee -a $logfile
if [ $(grep -m1 'mode:' ~/.xscreensaver | awk '{print $2}') != 'off' ]; then
sed -i 's/$xsetting/mode: off/' ~/.xscreensaver
echo " xscreensaver set to off" >> $logfile
else
echo " xscreensaver already disabled" >> $logfile
fi
;;
gsd-screensaver | gsd-screensaver-proxy)
setting=$(gsettings get org.gnome.desktop.screensaver lock-enabled)
setting1=$(gsettings get org.gnome.desktop.session idle-delay)
if [ "$setting $setting1" != 'false uint32 0' ]; then
echo disable screensaver via gsettings was $setting and $setting1>> $logfile
gsettings set org.gnome.desktop.screensaver lock-enabled false
gsettings set org.gnome.desktop.screensaver idle-activation-enabled false
gsettings set org.gnome.desktop.session idle-delay 0
else
echo gsettings screen saver already disabled >> $logfile
fi
;;
*) echo "some other screensaver $screen_saver_running" found | tee -a $logfile
echo "please configure it manually" | tee -a $logfile
;;
esac
elif [ -e "/etc/lightdm/lightdm.conf" ]; then
# if screen saver NOT already disabled?
if [ $(grep 'xserver-command=X -s 0 -dpms' /etc/lightdm/lightdm.conf | wc -l) == 0 ]; then
echo install screensaver via lightdm.conf >> $logfile
sudo sed -i '/^\[Seat:\*\]/a xserver-command=X -s 0 -dpms' /etc/lightdm/lightdm.conf
#sudo cp _myconf /etc/lightdm/lightdm.conf
#rm _myconf >/dev/null
else
echo screensaver via lightdm already disabled >> $logfile
fi
elif [ $(which gsettings | wc -l) == 1 ]; then
setting=$(gsettings get org.gnome.desktop.screensaver lock-enabled)
setting1=$(gsettings get org.gnome.desktop.session idle-delay)
if [ "$setting $setting1" != 'false uint32 0' ]; then
echo disable screensaver via gsettings was $setting and $setting1>> $logfile
gsettings set org.gnome.desktop.screensaver lock-enabled false
gsettings set org.gnome.desktop.screensaver idle-activation-enabled false
gsettings set org.gnome.desktop.session idle-delay 0
else
echo gsettings screen saver already disabled >> $logfile
fi
elif [ -d "/etc/xdg/lxsession" ]; then
currently_set=$(grep -m1 '\-dpms' /etc/xdg/lxsession/LXDE-pi/autostart)
if [ "$currently_set." == "." ]; then
echo disable screensaver via lxsession >> $logfile
# turn it off for the future
sudo su -c "echo -e '@xset s noblank\n@xset s off\n@xset -dpms' >> /etc/xdg/lxsession/LXDE-pi/autostart"
# turn it off now
export DISPLAY=:0; xset s noblank;xset s off;xset -dpms
else
echo lxsession screen saver already disabled >> $logfile
fi
else
echo " "
echo -e "unable to disable screen saver, /etc/xdg/lxsession does not exist" | tee >>$logfile
fi
fi

View File

@@ -1,361 +0,0 @@
#!/bin/bash
# only DO npm installs when flag is set to 1
# test when set to 0
true=1
false=0
doinstalls=$false
force=$false
justActive=$true
test_run=$true
stashed=$false
keyFile=package.json
forced_arch=
git_active_lock='./.git/index.lock'
lf=$'\n'
git_user_name=
git_user_email=
trim() {
local var="$*"
# remove leading whitespace characters
var="${var#"${var%%[![:space:]]*}"}"
# remove trailing whitespace characters
var="${var%"${var##*[![:space:]]}"}"
echo -n "$var"
}
# is this a mac
mac=$(uname -s)
# get the processor architecture
arch=$(uname -m)
if [ $mac == 'Darwin' ]; then
cmd=greadlink
else
cmd=readlink
fi
if [ -d ~/MagicMirror ]; then
# put the log where the script is located
logdir=$(dirname $($cmd -f "$0"))
# if the script was execute from the web
if [[ $logdir != *"MagicMirror/installers"* ]]; then
# use the MagicMirror/installers folder
cd ~/MagicMirror/installers >/dev/null
logdir=$(pwd)
cd - >/dev/null
fi
logfile=$logdir/upgrade.log
echo the log will be $logfile
echo >>$logfile
date +"Upgrade started - %a %b %e %H:%M:%S %Z %Y" >>$logfile
echo system is $(uname -a) >> $logfile
echo the os is $(lsb_release -a) >> $logfile
# because of how its executed from the web, p0 gets overlayed with parm
# check to see if a parm was passed .. easy apply without editing
p0=$0
# if not 'bash', and some parm specified
if [ $0 != 'bash' -a "$1." != "." ]; then
# then executed locally
# get the parm
p0=$1
fi
# lowercase it.. watch out, mac stuff doesn't work with tr, etc
p0=$(echo $p0 | cut -c 1-5 | awk '{print tolower($0)}' )
if [ $p0 == 'apply' ]; then
echo user requested to apply changes >>$logfile
doinstalls=$true
test_run=$false
elif [ $p0 == 'force' ]; then
echo user requested to force apply changes >>$logfile
doinstalls=$true
force=$true
test_run=$false
fi
if [ $test_run == $true ]; then
echo doing test run = true | tee -a $logfile
else
echo doing test run = false | tee -a $logfile
fi
# if we want just the modules listed in config.js now
if [ $justActive == $true ]; then
if [ ! -f ~/MagicMirror/installers/dumpactivemodules.js ]; then
echo downloading dumpactivemodules script >> $logfile
curl -sL https://www.dropbox.com/s/wwe6bfg2lcjmj43/dumpactivemodules.js?dl=0 > ~/MagicMirror/installers/dumpactivemodules.js
fi
fi
echo update log will be in $logfile
# used for parsing the array of module names
SAVEIFS=$IFS # Save current IFS
IFS=$'\n'
echo | tee -a $logfile
# if the git lock file exists and git is not running
if [ -f git_active_lock ]; then
# check to see if git is actually running
git_running=`ps -ef | grep git | grep -v color | grep -v 'grep git' | wc -l`
# if not running
if [ git_running == $false ]; then
# clean up the dangling lock file
echo erasing abandonded git lock file >> $logfile
rm git_active_lock >/dev/null 2>&1
else
# git IS running, we can't proceed
echo it appears another instance of git is running | tee -a $logfile
# if this is an actual run
if [ $doinstalls == $true ]; then
# force it back to test run
doinstalls = $false
test_run=$true
echo forcing test run mode | tee -a $logfile
echo please resolve git running already and start the update again | tee -a $logfile
fi
fi
fi
# change to MagicMirror folder
cd ~/MagicMirror
# save custom.css
cd css
echo "saving custom.css" | tee -a $logfile
cp -p custom.css save_custom.css
cd - >/dev/null
save_alias=$(alias git 2>/dev/null)
lang=$(locale | grep LANGUAGE | awk -F= '{print $2}')
# make sure git respones are in english, so code works
if [ "$lang." != "en_US.UTF-8." ]; then
echo not english or locale not set, set git alias >>$logfile
if [ "$LC_ALL." == "." ]; then
alias git='LANGUAGE=en_US.UTF-8 git' >>$logfile
else
alias git='LC_ALL=en_US.UTF-8 git' >>$logfile
fi
#alias >>$logfile
fi
# get the git remote name
remote=$(git remote 2>/dev/null | awk '{print $1}')
# if remote name set
if [ "$remote." != "." ]; then
echo remote name = $remote >>$logfile
# get the local and remote package.json versions
local_version=$(grep -m1 version package.json | awk -F\" '{print $4}')
remote_version=$(curl -s https://raw.githubusercontent.com/MichMich/MagicMirror/master/package.json | grep -m1 version | awk -F\" '{print $4}')
# only change if they are different
if [ "$local_version." != "$remote_version." -o $force == $true -o $test_run == $true ]; then
echo upgrading from version $local_version to $remote_version | tee -a $logfile
# get the latest upgrade
echo fetching latest revisions | tee -a $logfile
git fetch $remote >/dev/null
rc=$?
echo git fetch rc=$rc >>$logfile
if [ $rc -eq 0 ]; then
# need to get the current branch
current_branch=$(git branch | grep "*" | awk '{print $2}')
echo current branch = $current_branch >>$logfile
git status 2>&1 >>$logfile
# get the names of the files that are different locally
diffs=$(git status 2>&1 | grep modified | awk -F: '{print $2}')
# split names into an array
diffs=($diffs) # split to array $diffs
# if there are different files (array size greater than zero)
if [ ${#diffs[@]} -gt 0 ]; then
package_lock=0
echo there are "${#diffs[@]}" local files that are different than the master repo | tee -a $logfile
echo | tee -a $logfile
for file in "${diffs[@]}"
do
echo "$file" | tee -a $logfile
if [ $(echo $file | grep '\-lock.json$' | wc -l) -eq 1 ]; then
package_lock=$true
fi
done
echo | tee -a $logfile
if [ $package_lock -eq 1 ]; then
echo "any *-lock.json files do not need to be saved"
fi
read -p "do you want to save these files for later (Y/n)?" choice
echo save/restore files selection = $choice >> $logfile
if [[ $choice =~ ^[Yy]$ ]]; then
git_user=$(git config --global --get user.email)
if [ "git_user." == "." ]; then
git_user_name="-c user.name=upgrade_script"
git_user_email="-c user.email=script@upgrade.com"
fi
git git_user_name git_user_email stash >>$logfile
stashed=$true
else
for file in "${diffs[@]}"
do
f="$(trim "$file")"
echo restoring $f from repo >> $logfile
if [ $test_run == $false ]; then
git checkout HEAD -- $f | tee -a $logfile
else
echo skipping restore for $f, doing test run | tee -a $logfile
fi
done
fi
else
echo no files different from github version >> $logfile
fi
# lets test merge, in memory, no changes to working directory or local repo
test_merge_output=$(git merge-tree `git merge-base $current_branch HEAD` HEAD $current_branch | grep "^<<<<<<<\|changed in both")
echo "test merge result rc='$test_merge_output' , if empty, no conflicts" >> $logfile
# if there were no conflicts reported
if [ "$test_merge_output." == "." ]; then
if [ $test_run == $false ]; then
# go ahead and merge now
echo "executing merge, apply specified" >> $logfile
# get the text output of merge
merge_output=$(git merge $remote/$current_branch 2>&1)
# and its return code
merge_result=$?
# make any long line readable
merge_output=$(echo $merge_output | tr '|' '\n'| sed "s/create/\\${lf}create/g" | sed "s/mode\ change/\\${lf}mode\ change/g")
echo -e "merge result rc= $merge_result\n $merge_output">> $logfile
else
echo "skipping merge, only test run" >> $logfile
merge_output=''
merge_result=0
fi
# if no merge errors
if [ $merge_result == 0 ]; then
# some updates applied
if [ "$merge_output." != 'Already up to date.' -o $test_run == $true ]; then
# update any dependencies for base
if [ $doinstalls == $true ]; then
# if this is a pi zero
echo processor architecture is $arch >> $logfile
if [ "$arch" == "armv6l" ]; then
# force to look like pi 2
echo forcing architecture armv7l >>$logfile
forced_arch='--arch=armv7l'
fi
echo "updating MagicMirror runtime, please wait" | tee -a $logfile
npm install $forced_arch 2>&1 | tee -a $logfile
done_update=`date +"completed - %a %b %e %H:%M:%S %Z %Y"`
echo npm install $done_update on base >> $ logfile
fi
# process updates for modules after base changed
cd modules
if [ $justActive == $true ]; then
# get the list of ACTIVE modules with package.json files
mtype=active
modules=$(node ../installers/dumpactivemodules.js)
else
# get the list of INSTALLED modules with package.json files
mtype=installed
modules=$(find -maxdepth 2 -name 'package.json' -printf "%h\n" | cut -d'/' -f2 )
fi
modules=($modules) # split to array $modules
# if the array has entries in it
if [ ${#modules[@]} -gt 0 ]; then
echo >> $logfile
echo "processing dependency changes for $mtype modules with package.json files" | tee -a $logfile
echo
for module in "${modules[@]}"
do
echo "processing for module" $module please wait | tee -a $logfile
echo '----------------------------------' | tee -a $logfile
# change to that directory
cd $module
# process its dependencies
if [ $doinstalls == $true ]; then
npm install $forced_arch 2>&1| tee -a $logfile
else
echo skipped processing for $module, doing test run | tee -a $logfile
fi
# return to modules folder
cd .. >/dev/null
echo "processing complete for module" $module | tee -a $logfile
echo
done
else
echo "no modules found needing npm refresh" | tee -a $logfile
fi
# return to Magic Mirror folder
cd .. >/dev/null
else
echo "no changes detected for modules, skipping " | tee -a $logfile
fi
else
echo there were merge errors | tee -a $logfile
echo $merge_output | tee -a %logfile
echo you should examine and resolve them | tee -a $logfile
echo using the command git log --oneline --decorate | tee -a $logfile
git log --oneline --decorate | tee -a $logfile
fi
else
echo "there are merge conflicts to be resolved, no changes have been applied" | tee -a $logfile
echo $test_merge_output | tee -a $logfile
fi
else
echo "MagicMirror git fetch failed" | tee -a $logfile
fi
else
echo "local version $local_version already same as master $remote_version" | tee -a $logfile
fi
else
echo "Unable to determine upstream git repository" | tee -a $logfile
fi
# should be in MagicMirror base
cd css
# restore custom.css
echo "restoring custom.css" | tee -a $logfile
cp -p save_custom.css custom.css
rm save_custom.css
cd - >/dev/null
if [ "$lang." != "en_US.UTF-8." ]; then
if [ "$save_alias." != "." ]; then
echo restoring git alias >>$logfile
$save_alias >/dev/null
else
echo removing git alias >>$logfile
unalias git >/dev/null
fi
fi
IFS=$SAVEIFS # Restore IFS
if [ $stashed == $true ]; then
if [ $test_run == $true ]; then
echo test run, restoring files stashed | tee -a $logfile
git git_user_name git_user_email stash pop >> $logfile
else
echo we stashed a set of files that appear changed from the latest repo versions. you should review them | tee -a $logfile
git stash show --name-only > installers/stashed_files
echo see installers/stashed_files for the list
echo
echo you can use git checkout "stash@{0}" -- filename to extract one file from the stash
echo
echo or git stash pop to restore them all
echo
echo WARNING..
echo WARNING.. either will overlay the file just installed by the update
echo WARNING..
fi
fi
# return to original folder
cd - >/dev/null
date +"Upgrade ended - %a %b %e %H:%M:%S %Z %Y" >>$logfile
else
echo It appears MagicMirror has not been installed on this system
echo please run the installer, "raspberry.sh" first
fi

251
js/app.js
View File

@@ -1,28 +1,26 @@
/* Magic Mirror
* The Core App (Server)
*
* By Michael Teeuw http://michaelteeuw.nl
* By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed.
*/
var fs = require("fs");
var Server = require(__dirname + "/server.js");
var Utils = require(__dirname + "/utils.js");
var defaultModules = require(__dirname + "/../modules/default/defaultmodules.js");
var path = require("path");
// Alias modules mentioned in package.js under _moduleAliases.
require("module-alias/register");
// add timestamps in front of log messages
require("console-stamp")(console, "HH:MM:ss.l");
const fs = require("fs");
const path = require("path");
const Log = require("logger");
const Server = require(`${__dirname}/server`);
const Utils = require(`${__dirname}/utils`);
const defaultModules = require(`${__dirname}/../modules/default/defaultmodules`);
// Get version number.
global.version = JSON.parse(fs.readFileSync("package.json", "utf8")).version;
console.log("Starting MagicMirror: v" + global.version);
global.version = require(`${__dirname}/../package.json`).version;
Log.log("Starting MagicMirror: v" + global.version);
// global absolute root path
global.root_path = path.resolve(__dirname + "/../");
global.root_path = path.resolve(`${__dirname}/../`);
if (process.env.MM_CONFIG_FILE) {
global.configuration_file = process.env.MM_CONFIG_FILE;
@@ -37,107 +35,104 @@ if (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) {
console.log("Whoops! There was an uncaught exception...");
console.error(err);
console.log("MagicMirror will not quit, but it might be a good idea to check why this happened. Maybe no internet connection?");
console.log("If you think this really is an issue, please open an issue on GitHub: https://github.com/MichMich/MagicMirror/issues");
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");
});
/* App - The core app.
/**
* The core app.
*
* @class
*/
var App = function() {
var nodeHelpers = [];
function App() {
let nodeHelpers = [];
let httpServer;
/* loadConfig(callback)
* Loads the config file. combines it with the defaults,
* and runs the callback with the found config as argument.
/**
* Loads the config file. Combines it with the defaults, and runs the
* callback with the found config as argument.
*
* argument callback function - The callback function.
* @param {Function} callback Function to be called after loading the config
*/
var loadConfig = function(callback) {
console.log("Loading config ...");
var defaults = require(__dirname + "/defaults.js");
function loadConfig(callback) {
Log.log("Loading config ...");
const defaults = require(`${__dirname}/defaults`);
// 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);
}
const configFilename = path.resolve(global.configuration_file || `${global.root_path}/config/config.js`);
try {
fs.accessSync(configFilename, fs.F_OK);
var c = require(configFilename);
const c = require(configFilename);
checkDeprecatedOptions(c);
var config = Object.assign(defaults, c);
const config = Object.assign(defaults, c);
callback(config);
} catch (e) {
if (e.code === "ENOENT") {
console.error(Utils.colors.error("WARNING! Could not find config file. Please create one. Starting with default configuration."));
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) {
console.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));
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 {
console.error(Utils.colors.error("WARNING! Could not load config file. Starting with default configuration. Error found: " + e));
Log.error(Utils.colors.error(`WARNING! Could not load config file. Starting with default configuration. Error found: ${e}`));
}
callback(defaults);
}
};
}
var checkDeprecatedOptions = function(userConfig) {
var deprecated = require(global.root_path + "/js/deprecated.js");
var deprecatedOptions = deprecated.configs;
/**
* 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
*/
function checkDeprecatedOptions(userConfig) {
const deprecated = require(`${global.root_path}/js/deprecated`);
const deprecatedOptions = deprecated.configs;
var usedDeprecated = [];
deprecatedOptions.forEach(function(option) {
if (userConfig.hasOwnProperty(option)) {
usedDeprecated.push(option);
}
});
const usedDeprecated = deprecatedOptions.filter((option) => userConfig.hasOwnProperty(option));
if (usedDeprecated.length > 0) {
console.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.")
);
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.`));
}
};
}
/* loadModule(module)
/**
* Loads a specific module.
*
* argument module string - The name of the module (including subpath).
* @param {string} module The name of the module (including subpath).
* @param {Function} callback Function to be called after loading
*/
var loadModule = function(module, callback) {
function loadModule(module, callback) {
const elements = module.split("/");
const moduleName = elements[elements.length - 1];
let moduleFolder = `${__dirname}/../modules/${module}`;
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;
if (defaultModules.includes(moduleName)) {
moduleFolder = `${__dirname}/../modules/default/${module}`;
}
var helperPath = moduleFolder + "/node_helper.js";
const helperPath = `${moduleFolder}/node_helper.js`;
var loadModule = true;
let loadHelper = true;
try {
fs.accessSync(helperPath, fs.R_OK);
} catch (e) {
loadModule = false;
console.log("No helper found for module: " + moduleName + ".");
loadHelper = false;
Log.log(`No helper found for module: ${moduleName}.`);
}
if (loadModule) {
var Module = require(helperPath);
var m = new Module();
if (loadHelper) {
const Module = require(helperPath);
let m = new Module();
if (m.requiresVersion) {
console.log("Check MagicMirror version for node helper '" + moduleName + "' - Minimum version: " + m.requiresVersion + " - Current version: " + global.version);
Log.log(`Check MagicMirror version for node helper '${moduleName}' - Minimum version: ${m.requiresVersion} - Current version: ${global.version}`);
if (cmpVersions(global.version, m.requiresVersion) >= 0) {
console.log("Version is ok!");
Log.log("Version is ok!");
} else {
console.log("Version is incorrect. Skip module: '" + moduleName + "'");
Log.warn(`Version is incorrect. Skip module: '${moduleName}'`);
return;
}
}
@@ -150,45 +145,51 @@ var App = function() {
} else {
callback();
}
};
}
/* loadModules(modules)
/**
* Loads all modules.
*
* argument module string - The name of the module (including subpath).
* @param {Module[]} modules All modules to be loaded
* @param {Function} callback Function to be called after loading
*/
var loadModules = function(modules, callback) {
console.log("Loading module helpers ...");
function loadModules(modules, callback) {
Log.log("Loading module helpers ...");
var loadNextModule = function() {
/**
*
*/
function loadNextModule() {
if (modules.length > 0) {
var nextModule = modules[0];
loadModule(nextModule, function() {
const nextModule = modules[0];
loadModule(nextModule, function () {
modules = modules.slice(1);
loadNextModule();
});
} else {
// All modules are loaded
console.log("All module helpers loaded.");
Log.log("All module helpers loaded.");
callback();
}
};
}
loadNextModule();
};
}
/* cmpVersions(a,b)
/**
* Compare two semantic version numbers and return the difference.
*
* argument a string - Version number a.
* argument a string - Version number b.
* @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);
let i, diff;
const regExStrip0 = /(\.0+)+$/;
const segmentsA = a.replace(regExStrip0, "").split(".");
const segmentsB = b.replace(regExStrip0, "").split(".");
const l = Math.min(segmentsA.length, segmentsB.length);
for (i = 0; i < l; i++) {
diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10);
@@ -199,39 +200,39 @@ var App = function() {
return segmentsA.length - segmentsB.length;
}
/* start(callback)
* This methods starts 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.
/**
* Start the core app.
*
* argument callback function - The callback function.
* 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) {
this.start = function (callback) {
loadConfig(function (c) {
config = c;
var modules = [];
Log.setLogLevel(config.logLevel);
for (var m in config.modules) {
var module = config.modules[m];
if (modules.indexOf(module.module) === -1 && !module.disabled) {
let modules = [];
for (const module of config.modules) {
if (!modules.includes(module.module) && !module.disabled) {
modules.push(module.module);
}
}
loadModules(modules, function() {
var server = new Server(config, function(app, io) {
console.log("Server started ...");
loadModules(modules, function () {
httpServer = new Server(config, function (app, io) {
Log.log("Server started ...");
for (var h in nodeHelpers) {
var nodeHelper = nodeHelpers[h];
for (let nodeHelper of nodeHelpers) {
nodeHelper.setExpressApp(app);
nodeHelper.setSocketIO(io);
nodeHelper.start();
}
console.log("Sockets connected & modules started ...");
Log.log("Sockets connected & modules started ...");
if (typeof callback === "function") {
callback(config);
@@ -241,41 +242,49 @@ var App = function() {
});
};
/* stop()
* This methods stops the core app.
* This calls each node_helper's STOP() function, if it exists.
/**
* 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];
this.stop = function () {
for (const nodeHelper of nodeHelpers) {
if (typeof nodeHelper.stop === "function") {
nodeHelper.stop();
}
}
httpServer.close();
};
/* Listen for SIGINT signal and call stop() function.
/**
* 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", () => {
console.log("[SIGINT] Received. Shutting down server...");
setTimeout(() => { process.exit(0); }, 3000); // Force quit after 3 seconds
Log.log("[SIGINT] Received. Shutting down server...");
setTimeout(() => {
process.exit(0);
}, 3000); // Force quit after 3 seconds
this.stop();
process.exit(0);
});
/* We also need to listen to SIGTERM signals so we stop everything when we are asked to stop by the OS.
/**
* Listen to SIGTERM signals so we can stop everything when we
* are asked to stop by the OS.
*/
process.on("SIGTERM", () => {
console.log("[SIGTERM] Received. Shutting down server...");
setTimeout(() => { process.exit(0); }, 3000); // Force quit after 3 seconds
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();

73
js/check_config.js Normal file
View File

@@ -0,0 +1,73 @@
/* 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!
return path.resolve(process.env.MM_CONFIG_FILE || `${rootPath}/config/config.js`);
}
/**
* 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
const configFile = fs.readFileSync(configFileName, "utf-8");
// Explicitly tell linter that he might encounter es6 syntax ("let config = {...}")
const errors = linter.verify(configFile, {
env: {
es6: true
}
});
if (errors.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 :("));
for (const error of errors) {
Log.error(`Line ${error.line} column ${error.column}: ${error.message}`);
}
}
}
checkConfigFile();

View File

@@ -1,54 +1,65 @@
/* global Class, xyz */
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* By John Resig https://johnresig.com/
*
* Inspired by base2 and Prototype
*
* MIT Licensed.
*/
// Inspired by base2 and Prototype
(function () {
var initializing = false;
var fnTest = /xyz/.test(function () { xyz; }) ? /\b_super\b/ : /.*/;
let initializing = false;
const fnTest = /xyz/.test(function () {
xyz;
})
? /\b_super\b/
: /.*/;
// The base Class implementation (does nothing)
this.Class = function () { };
this.Class = function () {};
// Create a new Class that inherits from this class
Class.extend = function (prop) {
var _super = this.prototype;
let _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
const prototype = new this();
initializing = false;
// Make a copy of all prototype properties, to prevent reference issues.
for (var name in prototype) {
prototype[name] = cloneObject(prototype[name]);
for (const p in prototype) {
prototype[p] = cloneObject(prototype[p]);
}
// Copy the properties over onto the new prototype
for (var name in prop) {
for (const 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;
prototype[name] =
typeof prop[name] === "function" && typeof _super[name] === "function" && fnTest.test(prop[name])
? (function (name, fn) {
return function () {
const tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// 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;
// The method only need to be bound temporarily, so we
// remove it when we're done executing
const ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) : prop[name];
return ret;
};
})(name, prop[name])
: prop[name];
}
// The dummy class constructor
/**
* The dummy class constructor
*/
function Class() {
// All construction is actually done in the init method
if (!initializing && this.init) {
@@ -69,15 +80,19 @@
};
})();
//Define the clone method for later use.
//Helper Method
/**
* 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) {
const temp = obj.constructor(); // give temp the original obj's constructor
for (const key in obj) {
temp[key] = cloneObject(obj[key]);
if (key === "lockStrings") {

View File

@@ -1,25 +1,26 @@
/* exported defaults */
/* global mmPort */
/* Magic Mirror
* Config Defauls
* Config Defaults
*
* By Michael Teeuw http://michaelteeuw.nl
* By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed.
*/
var port = 8080;
var address = "localhost";
if (typeof(mmPort) !== "undefined") {
const address = "localhost";
let port = 8080;
if (typeof mmPort !== "undefined") {
port = mmPort;
}
var defaults = {
const defaults = {
address: address,
port: port,
basePath: "/",
kioskmode: false,
electronOptions: {},
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
language: "en",
logLevel: ["INFO", "LOG", "WARN", "ERROR"],
timeFormat: 24,
units: "metric",
zoom: 1,
@@ -42,7 +43,7 @@ var defaults = {
module: "helloworld",
position: "middle_center",
config: {
text: "Please create a config file."
text: "Please create a config file or check the existing one for errors."
}
},
{
@@ -58,7 +59,7 @@ var defaults = {
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."
text: "If you get this message while your config file is already created,<br>" + "it probably contains an error. To validate your config file run in your MagicMirror directory<br>" + "<pre>npm run config:check</pre>"
}
},
{
@@ -68,14 +69,16 @@ var defaults = {
config: {
text: "www.michaelteeuw.nl"
}
},
}
],
paths: {
modules: "modules",
vendor: "vendor"
},
}
};
/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {module.exports = defaults;}
if (typeof module !== "undefined") {
module.exports = defaults;
}

View File

@@ -1,14 +1,11 @@
/* Magic Mirror Deprecated Config Options List
*
* By Michael Teeuw http://michaelteeuw.nl
* By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed.
*
* Olex S. original idea this deprecated option
*/
var deprecated = {
configs: ["kioskmode"],
module.exports = {
configs: ["kioskmode"]
};
/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {module.exports = deprecated;}

View File

@@ -1,12 +1,11 @@
/* jshint esversion: 6 */
"use strict";
const electron = require("electron");
const core = require(__dirname + "/app.js");
const core = require("./app.js");
const Log = require("logger");
// Config
var config = process.env.config ? JSON.parse(process.env.config) : {};
let config = process.env.config ? JSON.parse(process.env.config) : {};
// Module to control application life.
const app = electron.app;
// Module to create native browser window.
@@ -16,15 +15,20 @@ const BrowserWindow = electron.BrowserWindow;
// 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 = {
let electronSwitchesDefaults = ["autoplay-policy", "no-user-gesture-required"];
app.commandLine.appendSwitch(...new Set(electronSwitchesDefaults, config.electronSwitches));
let electronOptionsDefaults = {
width: 800,
height: 600,
x: 0,
y: 0,
darkTheme: true,
webPreferences: {
contextIsolation: true,
nodeIntegration: false,
zoomFactor: config.zoom
},
@@ -40,37 +44,55 @@ function createWindow() {
electronOptionsDefaults.autoHideMenuBar = true;
}
var electronOptions = Object.assign({}, electronOptionsDefaults, config.electronOptions);
const 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 address = (config.address === void 0) | (config.address === "") ? (config.address = "localhost") : config.address;
mainWindow.loadURL(`http://${address}:${config.port}`);
let prefix;
if ((config["tls"] !== null && config["tls"]) || config.useHttps) {
prefix = "https://";
} else {
prefix = "http://";
}
let 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")) {
if (process.env.JEST_WORKER_ID !== undefined) {
// if we are running with jest
const devtools = new BrowserWindow(electronOptions);
mainWindow.webContents.setDevToolsWebContents(devtools.webContents);
}
mainWindow.webContents.openDevTools();
}
// simulate mouse move to hide black cursor on start
mainWindow.webContents.on("dom-ready", (event) => {
mainWindow.webContents.sendInputEvent({ type: "mouseMove", x: 0, y: 0 });
});
// Set responders for window events.
mainWindow.on("closed", function() {
mainWindow.on("closed", function () {
mainWindow = null;
});
if (config.kioskmode) {
mainWindow.on("blur", function() {
mainWindow.on("blur", function () {
mainWindow.focus();
});
mainWindow.on("leave-full-screen", function() {
mainWindow.on("leave-full-screen", function () {
mainWindow.setFullScreen(true);
});
mainWindow.on("resize", function() {
setTimeout(function() {
mainWindow.on("resize", function () {
setTimeout(function () {
mainWindow.reload();
}, 1000);
});
@@ -79,17 +101,22 @@ function createWindow() {
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on("ready", function() {
console.log("Launching application.");
app.on("ready", function () {
Log.log("Launching application.");
createWindow();
});
// Quit when all windows are closed.
app.on("window-all-closed", function() {
createWindow();
app.on("window-all-closed", function () {
if (process.env.JEST_WORKER_ID !== undefined) {
// if we are running with jest
app.quit();
} else {
createWindow();
}
});
app.on("activate", function() {
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) {
@@ -104,17 +131,26 @@ app.on("activate", function() {
* core.stop() is called by process.on("SIGINT"... in `app.js`
*/
app.on("before-quit", (event) => {
console.log("Shutting down server...");
Log.log("Shutting down server...");
event.preventDefault();
setTimeout(() => { process.exit(0); }, 3000); // Force-quit after 3 seconds.
setTimeout(() => {
process.exit(0);
}, 3000); // Force-quit after 3 seconds.
core.stop();
process.exit(0);
});
/* handle errors from self signed certificates */
app.on("certificate-error", (event, webContents, url, error, certificate, callback) => {
event.preventDefault();
callback(true);
});
// 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) {
if (["localhost", "127.0.0.1", "::1", "::ffff:127.0.0.1", undefined].includes(config.address)) {
core.start(function (c) {
config = c;
});
}

View File

@@ -1,32 +1,30 @@
/* global config, vendor, MM, Log, Module */
/* global defaultModules, vendor */
/* Magic Mirror
* Module and File loaders.
*
* By Michael Teeuw http://michaelteeuw.nl
* By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed.
*/
var Loader = (function() {
const Loader = (function () {
/* Create helper variables */
var loadedModuleFiles = [];
var loadedFiles = [];
var moduleObjects = [];
const loadedModuleFiles = [];
const loadedFiles = [];
const moduleObjects = [];
/* Private Methods */
/* loadModules()
/**
* Loops thru all modules and requests load for every module.
*/
var loadModules = function() {
const loadModules = function () {
let moduleData = getModuleData();
var moduleData = getModuleData();
var loadNextModule = function() {
const loadNextModule = function () {
if (moduleData.length > 0) {
var nextModule = moduleData[0];
loadModule(nextModule, function() {
const nextModule = moduleData[0];
loadModule(nextModule, function () {
moduleData = moduleData.slice(1);
loadNextModule();
});
@@ -35,93 +33,100 @@ var Loader = (function() {
// This is done after all the modules so we can
// overwrite all the defined styles.
loadFile(config.customCss, function() {
loadFile(config.customCss, function () {
// custom.css loaded. Start all modules.
startModules();
});
}
};
loadNextModule();
};
/* startModules()
/**
* Loops thru all modules and requests start for every module.
*/
var startModules = function() {
for (var m in moduleObjects) {
var module = moduleObjects[m];
const startModules = function () {
for (const module of moduleObjects) {
module.start();
}
// Notify core of loaded modules.
MM.modulesStarted(moduleObjects);
// Starting modules also hides any modules that have requested to be initially hidden
for (const thisModule of moduleObjects) {
if (thisModule.data.hiddenOnStartup) {
Log.info("Initially hiding " + thisModule.name);
thisModule.hide();
}
}
};
/* getAllModules()
/**
* Retrieve list of all modules.
*
* return array - module data as configured in config
* @returns {object[]} module data as configured in config
*/
var getAllModules = function() {
const getAllModules = function () {
return config.modules;
};
/* getModuleData()
/**
* Generate array with module information including module paths.
*
* return array - Module information.
* @returns {object[]} Module information.
*/
var getModuleData = function() {
var modules = getAllModules();
var moduleFiles = [];
const getModuleData = function () {
const modules = getAllModules();
const moduleFiles = [];
for (var m in modules) {
var moduleData = modules[m];
var module = moduleData.module;
modules.forEach(function (moduleData, index) {
const module = moduleData.module;
var elements = module.split("/");
var moduleName = elements[elements.length - 1];
var moduleFolder = config.paths.modules + "/" + module;
const elements = module.split("/");
const moduleName = elements[elements.length - 1];
let moduleFolder = config.paths.modules + "/" + module;
if (defaultModules.indexOf(moduleName) !== -1) {
moduleFolder = config.paths.modules + "/default/" + module;
moduleFolder = config.paths.modules + "/default/" + module;
}
if (moduleData.disabled === true) {
continue;
return;
}
moduleFiles.push({
index: m,
identifier: "module_" + m + "_" + module,
index: index,
identifier: "module_" + index + "_" + module,
name: moduleName,
path: moduleFolder + "/" ,
path: moduleFolder + "/",
file: moduleName + ".js",
position: moduleData.position,
hiddenOnStartup: moduleData.hiddenOnStartup,
header: moduleData.header,
configDeepMerge: typeof moduleData.configDeepMerge === "boolean" ? moduleData.configDeepMerge : false,
config: moduleData.config,
classes: (typeof moduleData.classes !== "undefined") ? moduleData.classes + " " + module : module
classes: typeof moduleData.classes !== "undefined" ? moduleData.classes + " " + module : module
});
}
});
return moduleFiles;
};
/* loadModule(module)
* Load modules via ajax request and create module objects.
/**
* Load modules via ajax request and create module objects.s
*
* argument callback function - Function called when done.
* argument module object - Information about the module we want to load.
* @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;
const loadModule = function (module, callback) {
const url = module.path + module.file;
var afterLoad = function() {
var moduleObject = Module.create(module.name);
const afterLoad = function () {
const moduleObject = Module.create(module.name);
if (moduleObject) {
bootstrapModule(module, moduleObject, function() {
bootstrapModule(module, moduleObject, function () {
callback();
});
} else {
@@ -132,30 +137,30 @@ var Loader = (function() {
if (loadedModuleFiles.indexOf(url) !== -1) {
afterLoad();
} else {
loadFile(url, function() {
loadFile(url, function () {
loadedModuleFiles.push(url);
afterLoad();
});
}
};
/* bootstrapModule(module, mObj)
/**
* Bootstrap modules by setting the module data and loading the scripts & styles.
*
* argument module object - Information about the module we want to load.
* argument mObj object - Modules instance.
* argument callback function - Function called when done.
* @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) {
const bootstrapModule = function (module, mObj, callback) {
Log.info("Bootstrapping module: " + module.name);
mObj.setData(module);
mObj.loadScripts(function() {
mObj.loadScripts(function () {
Log.log("Scripts loaded for: " + module.name);
mObj.loadStyles(function() {
mObj.loadStyles(function () {
Log.log("Styles loaded for: " + module.name);
mObj.loadTranslations(function() {
mObj.loadTranslations(function () {
Log.log("Translations loaded for: " + module.name);
moduleObjects.push(mObj);
callback();
@@ -164,71 +169,77 @@ var Loader = (function() {
});
};
/* loadFile(fileName)
/**
* Load a script or stylesheet by adding it to the dom.
*
* argument fileName string - Path of the file we want to load.
* argument callback function - Function called when done.
* @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);
const loadFile = function (fileName, callback) {
const extension = fileName.slice((Math.max(0, fileName.lastIndexOf(".")) || Infinity) + 1);
let script, stylesheet;
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() {
console.error("Error on loading script:", fileName);
if (typeof callback === "function") {callback();}
};
case "js":
Log.log("Load script: " + fileName);
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() {
console.error("Error on loading stylesheet:", fileName);
if (typeof callback === "function") {callback();}
};
document.getElementsByTagName("body")[0].appendChild(script);
break;
case "css":
Log.log("Load stylesheet: " + fileName);
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;
document.getElementsByTagName("head")[0].appendChild(stylesheet);
break;
}
};
/* Public Methods */
return {
/* loadModules()
/**
* Load all modules as defined in the config.
*/
loadModules: function() {
loadModules: function () {
loadModules();
},
/* loadFile()
/**
* Load a file (script or stylesheet).
* Prevent double loading and search for files in the vendor folder.
*
* argument fileName string - Path of the file we want to load.
* argument module Module Object - the module that calls the loadFile function.
* argument callback function - Function called when done.
* @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) {
loadFile: function (fileName, module, callback) {
if (loadedFiles.indexOf(fileName.toLowerCase()) !== -1) {
Log.log("File already loaded: " + fileName);
callback();

View File

@@ -1,27 +1,79 @@
/* global console */
/* exported Log */
/* Magic Mirror
* Logger
* Log
*
* By Michael Teeuw http://michaelteeuw.nl
* 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") {
if (process.env.JEST_WORKER_ID === undefined) {
// 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) {
let logLevel;
let enableLog;
if (typeof exports === "object") {
// in nodejs and not running with jest
enableLog = process.env.JEST_WORKER_ID === undefined;
} else {
// in browser and not running with jsdom
enableLog = typeof window === "object" && window.name !== "jsdom";
}
// 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.
if (enableLog) {
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)
};
var Log = (function() {
return {
info: Function.prototype.bind.call(console.info, console),
log: Function.prototype.bind.call(console.log, console),
error: Function.prototype.bind.call(console.error, console),
warn: Function.prototype.bind.call(console.warn, 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 () {};
}
});
}
};
} else {
logLevel = {
debug: function () {},
log: function () {},
info: function () {},
warn: function () {},
error: function () {},
group: function () {},
groupCollapsed: function () {},
groupEnd: function () {},
time: function () {},
timeEnd: function () {},
timeStamp: function () {}
};
logLevel.setLogLevel = function () {};
}
return logLevel;
});

View File

@@ -1,34 +1,30 @@
/* global Log, Loader, Module, config, defaults */
/* jshint -W020, esversion: 6 */
/* global Loader, defaults, Translator */
/* Magic Mirror
* Main System
*
* By Michael Teeuw http://michaelteeuw.nl
* By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed.
*/
var MM = (function() {
var modules = [];
const MM = (function () {
let modules = [];
/* Private Methods */
/* createDomObjects()
* Create dom objects for all modules that
* are configured for a specific position.
/**
* Create dom objects for all modules that are configured for a specific position.
*/
var createDomObjects = function() {
var domCreationPromises = [];
const createDomObjects = function () {
const domCreationPromises = [];
modules.forEach(function(module) {
modules.forEach(function (module) {
if (typeof module.data.position !== "string") {
return;
}
var wrapper = selectWrapper(module.data.position);
const wrapper = selectWrapper(module.data.position);
var dom = document.createElement("div");
const dom = document.createElement("div");
dom.id = module.identifier;
dom.className = module.name;
@@ -39,104 +35,109 @@ var MM = (function() {
dom.opacity = 0;
wrapper.appendChild(dom);
var moduleHeader = document.createElement("header");
const 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;";
moduleHeader.style.display = "none;";
} else {
moduleHeader.style.display = "block;";
}
var moduleContent = document.createElement("div");
const moduleContent = document.createElement("div");
moduleContent.className = "module-content";
dom.appendChild(moduleContent);
var domCreationPromise = updateDom(module, 0);
const domCreationPromise = updateDom(module, 0);
domCreationPromises.push(domCreationPromise);
domCreationPromise.then(function() {
sendNotification("MODULE_DOM_CREATED", null, null, module);
}).catch(Log.error);
domCreationPromise
.then(function () {
sendNotification("MODULE_DOM_CREATED", null, null, module);
})
.catch(Log.error);
});
updateWrapperStates();
Promise.all(domCreationPromises).then(function() {
Promise.all(domCreationPromises).then(function () {
sendNotification("DOM_OBJECTS_CREATED");
});
};
/* selectWrapper(position)
/**
* Select the wrapper dom object for a specific position.
*
* argument position string - The name of the 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);
const selectWrapper = function (position) {
const classes = position.replace("_", " ");
const parentWrapper = document.getElementsByClassName(classes);
if (parentWrapper.length > 0) {
var wrapper = parentWrapper[0].getElementsByClassName("container");
const wrapper = parentWrapper[0].getElementsByClassName("container");
if (wrapper.length > 0) {
return wrapper[0];
}
}
};
/* sendNotification(notification, payload, sender)
/**
* Send a notification to all modules.
*
* argument notification string - The identifier of the notification.
* argument payload mixed - The payload of the notification.
* argument sender Module - The module that sent the notification.
* argument sendTo Module - The module to send the notification to. (optional)
* @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];
const sendNotification = function (notification, payload, sender, sendTo) {
for (const m in modules) {
const module = modules[m];
if (module !== sender && (!sendTo || module === sendTo)) {
module.notificationReceived(notification, payload, sender);
}
}
};
/* updateDom(module, speed)
/**
* Update the dom for a specific module.
*
* argument module Module - The module that needs an update.
* argument speed Number - The number of microseconds for the animation. (optional)
*
* return Promise - Resolved when the dom is fully updated.
* @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();
const updateDom = function (module, speed) {
return new Promise(function (resolve) {
const newHeader = module.getHeader();
let newContentPromise = module.getDom();
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);
newContentPromise
.then(function (newContent) {
const updatePromise = updateDomWithContent(module, speed, newHeader, newContent);
updatePromise.then(resolve).catch(Log.error);
}).catch(Log.error);
updatePromise.then(resolve).catch(Log.error);
})
.catch(Log.error);
});
};
/* updateDomWithContent(module, speed, newHeader, newContent)
/**
* Update the dom with the specified content
*
* argument module Module - The module that needs an update.
* argument speed Number - The number of microseconds for the animation. (optional)
* argument newHeader String - The new header that is generated.
* argument newContent Domobject - The new content that is generated.
*
* return Promise - Resolved when the module dom has been updated.
* @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) {
const updateDomWithContent = function (module, speed, newHeader, newContent) {
return new Promise(function (resolve) {
if (module.hidden || !speed) {
updateModuleContent(module, newHeader, newContent);
resolve();
@@ -154,7 +155,7 @@ var MM = (function() {
return;
}
hideModule(module, speed / 2, function() {
hideModule(module, speed / 2, function () {
updateModuleContent(module, newHeader, newContent);
if (!module.hidden) {
showModule(module, speed / 2);
@@ -164,66 +165,72 @@ var MM = (function() {
});
};
/* moduleNeedsUpdate(module, newContent)
/**
* Check if the content has changed.
*
* argument module Module - The module to check.
* argument newHeader String - The new header that is generated.
* argument newContent Domobject - The new content that is generated.
*
* return bool - Does the module need an update?
* @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);
const moduleNeedsUpdate = function (module, newHeader, newContent) {
const moduleWrapper = document.getElementById(module.identifier);
if (moduleWrapper === null) {
return false;
}
var contentWrapper = moduleWrapper.getElementsByClassName("module-content");
var headerWrapper = moduleWrapper.getElementsByClassName("module-header");
const contentWrapper = moduleWrapper.getElementsByClassName("module-content");
const headerWrapper = moduleWrapper.getElementsByClassName("module-header");
var headerNeedsUpdate = false;
var contentNeedsUpdate = false;
let headerNeedsUpdate = false;
let contentNeedsUpdate;
if (headerWrapper.length > 0) {
headerNeedsUpdate = newHeader !== headerWrapper[0].innerHTML;
}
var tempContentWrapper = document.createElement("div");
const tempContentWrapper = document.createElement("div");
tempContentWrapper.appendChild(newContent);
contentNeedsUpdate = tempContentWrapper.innerHTML !== contentWrapper[0].innerHTML;
return headerNeedsUpdate || contentNeedsUpdate;
};
/* moduleNeedsUpdate(module, newContent)
/**
* Update the content of a module on screen.
*
* argument module Module - The module to check.
* argument newHeader String - The new header that is generated.
* argument newContent Domobject - The new content that is generated.
* @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");
const updateModuleContent = function (module, newHeader, newContent) {
const moduleWrapper = document.getElementById(module.identifier);
if (moduleWrapper === null) {
return;
}
const headerWrapper = moduleWrapper.getElementsByClassName("module-header");
const contentWrapper = moduleWrapper.getElementsByClassName("module-content");
contentWrapper[0].innerHTML = "";
contentWrapper[0].appendChild(newContent);
headerWrapper[0].innerHTML = newHeader;
headerWrapper[0].style = headerWrapper.length > 0 && newHeader ? undefined : "display: none;";
if (headerWrapper.length > 0 && newHeader) {
headerWrapper[0].style.display = "block";
} else {
headerWrapper[0].style.display = "none";
}
};
/* hideModule(module, speed, callback)
/**
* Hide the module.
*
* argument module Module - The module to hide.
* argument speed Number - The speed of the hide animation.
* argument callback function - Called when the animation is done.
* @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) {
const hideModule = function (module, speed, callback, options) {
options = options || {};
// set lockString if set in options.
@@ -234,13 +241,13 @@ var MM = (function() {
}
}
var moduleWrapper = document.getElementById(module.identifier);
const 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() {
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
@@ -249,28 +256,33 @@ var MM = (function() {
updateWrapperStates();
if (typeof callback === "function") { callback(); }
if (typeof callback === "function") {
callback();
}
}, speed);
} else {
// invoke callback even if no content, issue 1308
if (typeof callback === "function") { callback(); }
if (typeof callback === "function") {
callback();
}
}
};
/* showModule(module, speed, callback)
/**
* Show the module.
*
* argument module Module - The module to show.
* argument speed Number - The speed of the show animation.
* argument callback function - Called when the animation is done.
* @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) {
const 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) {
const index = module.lockStrings.indexOf(options.lockString);
if (index !== -1) {
module.lockStrings.splice(index, 1);
}
}
@@ -279,6 +291,9 @@ var MM = (function() {
// Otherwise cancel show action.
if (module.lockStrings.length !== 0 && options.force !== true) {
Log.log("Will not show " + module.name + ". LockStrings active: " + module.lockStrings.join(","));
if (typeof options.onError === "function") {
options.onError(new Error("LOCK_STRING_ACTIVE"));
}
return;
}
@@ -290,7 +305,7 @@ var MM = (function() {
module.lockStrings = [];
}
var moduleWrapper = document.getElementById(module.identifier);
const moduleWrapper = document.getElementById(module.identifier);
if (moduleWrapper !== null) {
moduleWrapper.style.transition = "opacity " + speed / 1000 + "s";
// Restore the position. See hideModule() for more info.
@@ -299,18 +314,24 @@ var MM = (function() {
updateWrapperStates();
// Waiting for DOM-changes done in updateWrapperStates before we can start the animation.
var dummy = moduleWrapper.parentElement.parentElement.offsetHeight;
const dummy = moduleWrapper.parentElement.parentElement.offsetHeight;
moduleWrapper.style.opacity = 1;
clearTimeout(module.showHideTimer);
module.showHideTimer = setTimeout(function() {
if (typeof callback === "function") { callback(); }
module.showHideTimer = setTimeout(function () {
if (typeof callback === "function") {
callback();
}
}, speed);
} else {
// invoke callback
if (typeof callback === "function") {
callback();
}
}
};
/* updateWrapperStates()
/**
* 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.
@@ -321,15 +342,15 @@ var MM = (function() {
* 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"];
const updateWrapperStates = function () {
const 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");
positions.forEach(function (position) {
const wrapper = selectWrapper(position);
const moduleWrappers = wrapper.getElementsByClassName("module");
var showWrapper = false;
Array.prototype.forEach.call(moduleWrappers, function(moduleWrapper) {
let showWrapper = false;
Array.prototype.forEach.call(moduleWrappers, function (moduleWrapper) {
if (moduleWrapper.style.position === "" || moduleWrapper.style.position === "static") {
showWrapper = true;
}
@@ -339,10 +360,12 @@ var MM = (function() {
});
};
/* loadConfig()
* Loads the core config and combines it with de system defaults.
/**
* Loads the core config and combines it with the system defaults.
*/
var loadConfig = function() {
const 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.");
@@ -350,56 +373,52 @@ var MM = (function() {
}
config = Object.assign({}, defaults, config);
/* eslint-enable */
};
/* setSelectionMethodsForModules()
/**
* Adds special selectors on a collection of modules.
*
* argument modules array - Array of modules.
* @param {Module[]} modules Array of modules.
*/
var setSelectionMethodsForModules = function(modules) {
/* withClass(className)
* calls modulesByClass to filter modules with the specified classes.
const setSelectionMethodsForModules = function (modules) {
/**
* Filter modules with the specified classes.
*
* argument className string/array - one or multiple classnames. (array or space divided)
*
* return array - Filtered collection of modules.
* @param {string|string[]} className one or multiple classnames (array or space divided).
* @returns {Module[]} Filtered collection of modules.
*/
var withClass = function(className) {
const withClass = function (className) {
return modulesByClass(className, true);
};
/* exceptWithClass(className)
* calls modulesByClass to filter modules without the specified classes.
/**
* Filter modules without the specified classes.
*
* argument className string/array - one or multiple classnames. (array or space divided)
*
* return array - Filtered collection of modules.
* @param {string|string[]} className one or multiple classnames (array or space divided).
* @returns {Module[]} Filtered collection of modules.
*/
var exceptWithClass = function(className) {
const exceptWithClass = function (className) {
return modulesByClass(className, false);
};
/* modulesByClass(className, include)
* filters a collection of modules based on classname(s).
/**
* Filters a collection of modules based on classname(s).
*
* argument className string/array - one or multiple classnames. (array or space divided)
* argument include boolean - if the filter should include or exclude the modules with the specific classes.
*
* return array - Filtered collection of modules.
* @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;
const modulesByClass = function (className, include) {
let searchClasses = className;
if (typeof className === "string") {
searchClasses = className.split(" ");
}
var newModules = modules.filter(function(module) {
var classes = module.data.classes.toLowerCase().split(" ");
const newModules = modules.filter(function (module) {
const classes = module.data.classes.toLowerCase().split(" ");
for (var c in searchClasses) {
var searchClass = searchClasses[c];
for (const searchClass of searchClasses) {
if (classes.indexOf(searchClass.toLowerCase()) !== -1) {
return include;
}
@@ -412,15 +431,14 @@ var MM = (function() {
return newModules;
};
/* exceptModule(module)
/**
* Removes a module instance from the collection.
*
* argument module Module object - The module instance to remove from the collection.
*
* return array - Filtered collection of modules.
* @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) {
const exceptModule = function (module) {
const newModules = modules.filter(function (mod) {
return mod.identifier !== module.identifier;
});
@@ -428,47 +446,55 @@ var MM = (function() {
return newModules;
};
/* enumerate(callback)
/**
* Walks thru a collection of modules and executes the callback with the module as an argument.
*
* argument callback function - The function to execute 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) {
const 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}); }
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 */
/* init()
/**
* Main init method.
*/
init: function() {
init: function () {
Log.info("Initializing MagicMirror.");
loadConfig();
Log.setLogLevel(config.logLevel);
Translator.loadCoreTranslations(config.language);
Loader.loadModules();
},
/* modulesStarted(moduleObjects)
/**
* Gets called when all modules are started.
*
* argument moduleObjects array<Module> - All module instances.
* @param {Module[]} moduleObjects All module instances.
*/
modulesStarted: function(moduleObjects) {
modulesStarted: function (moduleObjects) {
modules = [];
for (var m in moduleObjects) {
var module = moduleObjects[m];
modules[module.data.index] = module;
}
moduleObjects.forEach((module) => modules.push(module));
Log.info("All modules started!");
sendNotification("ALL_MODULES_STARTED");
@@ -476,14 +502,14 @@ var MM = (function() {
createDomObjects();
},
/* sendNotification(notification, payload, sender)
/**
* Send a notification to all modules.
*
* argument notification string - The identifier of the notification.
* argument payload mixed - The payload of the notification.
* argument sender Module - The module that sent the notification.
* @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) {
sendNotification: function (notification, payload, sender) {
if (arguments.length < 3) {
Log.error("sendNotification: Missing arguments.");
return;
@@ -503,74 +529,78 @@ var MM = (function() {
sendNotification(notification, payload, sender);
},
/* updateDom(module, speed)
/**
* Update the dom for a specific module.
*
* argument module Module - The module that needs an update.
* argument speed Number - The number of microseconds for the animation. (optional)
* @param {Module} module The module that needs an update.
* @param {number} [speed] The number of microseconds for the animation.
*/
updateDom: function(module, speed) {
updateDom: function (module, speed) {
if (!(module instanceof Module)) {
Log.error("updateDom: Sender should be a module.");
return;
}
if (!module.data.position) {
Log.warn("module tries to update the DOM without being displayed.");
return;
}
// Further implementation is done in the private method.
updateDom(module, speed);
},
/* getModules(module, speed)
/**
* Returns a collection of all modules currently active.
*
* return array - A collection of all modules currently active.
* @returns {Module[]} A collection of all modules currently active.
*/
getModules: function() {
getModules: function () {
setSelectionMethodsForModules(modules);
return modules;
},
/* hideModule(module, speed, callback)
/**
* Hide the module.
*
* argument module Module - The module hide.
* argument speed Number - The speed of the hide animation.
* argument callback function - Called when the animation is done.
* argument options object - Optional settings for the hide method.
* @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) {
hideModule: function (module, speed, callback, options) {
module.hidden = true;
hideModule(module, speed, callback, options);
},
/* showModule(module, speed, callback)
/**
* Show the module.
*
* argument module Module - The module show.
* argument speed Number - The speed of the show animation.
* argument callback function - Called when the animation is done.
* argument options object - Optional settings for the hide method.
* @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) {
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) {
(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];
const output = Object(target);
for (let index = 1; index < arguments.length; index++) {
const source = arguments[index];
if (source !== undefined && source !== null) {
for (var nextKey in source) {
for (const nextKey in source) {
if (source.hasOwnProperty(nextKey)) {
output[nextKey] = source[nextKey];
}

View File

@@ -1,15 +1,13 @@
/* global Log, Class, Loader, Class , MM */
/* exported Module */
/* global Class, cloneObject, Loader, MMSocket, nunjucks, Translator */
/* Magic Mirror
* Module Blueprint.
* @typedef {Object} Module
*
* By Michael Teeuw http://michaelteeuw.nl
* By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed.
*/
var Module = Class.extend({
const Module = Class.extend({
/*********************************************************
* All methods (and properties) below can be subclassed. *
*********************************************************/
@@ -32,65 +30,66 @@ var Module = Class.extend({
// Use the nunjucksEnvironment() to get it.
_nunjucksEnvironment: null,
/* init()
* Is called when the module is instantiated.
/**
* Called when the module is instantiated.
*/
init: function () {
//Log.log(this.defaults);
},
/* start()
* Is called when the module is started.
/**
* Called when the module is started.
*/
start: function () {
Log.info("Starting module: " + this.name);
},
/* getScripts()
/**
* Returns a list of scripts the module requires to be loaded.
*
* return Array<String> - An array with filenames.
* @returns {string[]} An array with filenames.
*/
getScripts: function () {
return [];
},
/* getStyles()
/**
* Returns a list of stylesheets the module requires to be loaded.
*
* return Array<String> - An array with filenames.
* @returns {string[]} An array with filenames.
*/
getStyles: function () {
return [];
},
/* getTranslations()
/**
* Returns a map of translation files the module requires to be loaded.
*
* return Map<String, String> - A map with langKeys and filenames.
* return Map<String, String> -
*
* @returns {*} A map with langKeys and filenames.
*/
getTranslations: function () {
return false;
},
/* getDom()
* This method generates the dom which needs to be displayed. This method is called by the Magic Mirror core.
/**
* 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.
*
* return DomObject | Promise - The dom or a promise with the dom to display.
* @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();
return new Promise((resolve) => {
const div = document.createElement("div");
const template = this.getTemplate();
const templateData = this.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) {
this.nunjucksEnvironment().render(template, templateData, function (err, res) {
if (err) {
Log.error(err);
}
@@ -101,105 +100,103 @@ var Module = Class.extend({
});
} else {
// the template is a template string.
div.innerHTML = self.nunjucksEnvironment().renderString(template, templateData);
div.innerHTML = this.nunjucksEnvironment().renderString(template, templateData);
resolve(div);
}
});
},
/* getHeader()
* This method generates the header string which needs to be displayed if a user has a header configured for this module.
/**
* 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.
*
* return string - The header to display above the header.
* @returns {string} The header to display above the header.
*/
getHeader: function () {
return this.data.header;
},
/* getTemplate()
* This method returns the template for the module which is used by the default getDom implementation.
/**
* 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.
*
* return string - The template string of filename.
* @returns {string} The template string of filename.
*/
getTemplate: function () {
return "<div class=\"normal\">" + this.name + "</div><div class=\"small dimmed\">" + this.identifier + "</div>";
return '<div class="normal">' + this.name + '</div><div class="small dimmed">' + this.identifier + "</div>";
},
/* getTemplateData()
* This method returns the data to be used in the template.
/**
* Returns the data to be used in the template.
* This method needs to be subclassed if the module wants to use a custom data.
*
* return Object
* @returns {object} The data for the template
*/
getTemplateData: function () {
return {};
},
/* notificationReceived(notification, payload, sender)
* This method is called when a notification arrives.
* This method is called by the Magic Mirror core.
/**
* Called by the Magic Mirror core when a notification arrives.
*
* argument notification string - The identifier of the notification.
* argument payload mixed - The payload of the notification.
* argument sender Module - The module that sent the notification.
* @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);
// Log.log(this.name + " received a module notification: " + notification + " from sender: " + sender.name);
} else {
Log.log(this.name + " received a system notification: " + notification);
// Log.log(this.name + " received a system notification: " + notification);
}
},
/** nunjucksEnvironment()
/**
* Returns the nunjucks environment for the current module.
* The environment is checked in the _nunjucksEnvironment instance variable.
* @returns Nunjucks Environment
*
* @returns {object} The Nunjucks Environment
*/
nunjucksEnvironment: function() {
nunjucksEnvironment: function () {
if (this._nunjucksEnvironment !== null) {
return this._nunjucksEnvironment;
}
var self = this;
this._nunjucksEnvironment = new nunjucks.Environment(new nunjucks.WebLoader(this.file(""), {async: true}), {
this._nunjucksEnvironment = new nunjucks.Environment(new nunjucks.WebLoader(this.file(""), { async: true }), {
trimBlocks: true,
lstripBlocks: true
});
this._nunjucksEnvironment.addFilter("translate", function(str) {
return self.translate(str);
this._nunjucksEnvironment.addFilter("translate", (str, variables) => {
return nunjucks.runtime.markSafe(this.translate(str, variables));
});
return this._nunjucksEnvironment;
},
/* socketNotificationReceived(notification, payload)
* This method is called when a socket notification arrives.
/**
* Called when a socket notification arrives.
*
* argument notification string - The identifier of the notification.
* argument payload mixed - The payload of the notification.
* @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);
},
/* suspend()
* This method is called when a module is hidden.
/**
* Called when the module is hidden.
*/
suspend: function () {
Log.log(this.name + " is suspended.");
},
/* resume()
* This method is called when a module is shown.
/**
* Called when the module is shown.
*/
resume: function () {
Log.log(this.name + " is resumed.");
@@ -209,10 +206,10 @@ var Module = Class.extend({
* The methods below don"t need subclassing. *
*********************************************/
/* setData(data)
/**
* Set the module data.
*
* argument data object - Module data.
* @param {object} data The module data
*/
setData: function (data) {
this.data = data;
@@ -220,78 +217,78 @@ var Module = Class.extend({
this.identifier = data.identifier;
this.hidden = false;
this.setConfig(data.config);
this.setConfig(data.config, data.configDeepMerge);
},
/* setConfig(config)
/**
* Set the module config and combine it with the module defaults.
*
* argument config object - Module config.
* @param {object} config The combined module config.
* @param {boolean} deep Merge module config in deep.
*/
setConfig: function (config) {
this.config = Object.assign({}, this.defaults, config);
setConfig: function (config, deep) {
this.config = deep ? configMerge({}, this.defaults, config) : Object.assign({}, this.defaults, config);
},
/* socket()
* Returns a socket object. If it doesn't exist, it"s created.
/**
* 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 = this._socket = new MMSocket(this.name);
this._socket = new MMSocket(this.name);
}
var self = this;
this._socket.setNotificationCallback(function (notification, payload) {
self.socketNotificationReceived(notification, payload);
this._socket.setNotificationCallback((notification, payload) => {
this.socketNotificationReceived(notification, payload);
});
return this._socket;
},
/* file(file)
/**
* Retrieve the path to a module file.
*
* argument file string - Filename.
*
* return string - File path.
* @param {string} file Filename
* @returns {string} the file path
*/
file: function (file) {
return (this.data.path + "/" + file).replace("//", "/");
},
/* loadStyles()
/**
* Load all required stylesheets by requesting the MM object to load the files.
*
* argument callback function - Function called when done.
* @param {Function} callback Function called when done.
*/
loadStyles: function (callback) {
this.loadDependencies("getStyles", callback);
},
/* loadScripts()
/**
* Load all required scripts by requesting the MM object to load the files.
*
* argument callback function - Function called when done.
* @param {Function} callback Function called when done.
*/
loadScripts: function (callback) {
this.loadDependencies("getScripts", callback);
},
/* loadDependencies(funcName, callback)
/**
* Helper method to load all dependencies.
*
* argument funcName string - Function name to call to get scripts or styles.
* argument callback function - Function called when done.
* @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]();
let dependencies = this[funcName]();
var loadNextDependency = function () {
const loadNextDependency = () => {
if (dependencies.length > 0) {
var nextDependency = dependencies[0];
Loader.loadFile(nextDependency, self, function () {
const nextDependency = dependencies[0];
Loader.loadFile(nextDependency, this, () => {
dependencies = dependencies.slice(1);
loadNextDependency();
});
@@ -303,176 +300,237 @@ var Module = Class.extend({
loadNextDependency();
},
/* loadScripts()
* Load all required scripts by requesting the MM object to load the files.
/**
* Load all translations.
*
* argument callback function - Function called when done.
* @param {Function} callback Function called when done.
*/
loadTranslations: function (callback) {
var self = this;
var translations = this.getTranslations();
var lang = config.language.toLowerCase();
loadTranslations(callback) {
const translations = this.getTranslations() || {};
const language = config.language.toLowerCase();
// The variable `first` will contain the first
// defined translation after the following line.
for (var first in translations) { break; }
const languages = Object.keys(translations);
const fallbackLanguage = languages[0];
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 {
if (languages.length === 0) {
callback();
return;
}
const translationFile = translations[language];
const translationsFallbackFile = translations[fallbackLanguage];
if (!translationFile) {
Translator.load(this, translationsFallbackFile, true, callback);
return;
}
Translator.load(this, translationFile, false, () => {
if (translationFile !== translationsFallbackFile) {
Translator.load(this, translationsFallbackFile, true, callback);
} else {
callback();
}
});
},
/* translate(key, defaultValueOrVariables, defaultValue)
/**
* Request the translation for a given key with optional variables and default value.
*
* argument key string - The key of the string to translate
* argument defaultValueOrVariables string/object - The default value or variables for translating. (Optional)
* argument defaultValue string - The default value with variables. (Optional)
* @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") {
if (typeof defaultValueOrVariables === "object") {
return Translator.translate(this, key, defaultValueOrVariables) || defaultValue || "";
}
return Translator.translate(this, key) || defaultValueOrVariables || "";
},
/* updateDom(speed)
/**
* Request an (animated) update of the module.
*
* argument speed Number - The speed of the animation. (Optional)
* @param {number} [speed] The speed of the animation.
*/
updateDom: function (speed) {
MM.updateDom(this, speed);
},
/* sendNotification(notification, payload)
/**
* Send a notification to all modules.
*
* argument notification string - The identifier of the notification.
* argument payload mixed - The payload of the notification.
* @param {string} notification The identifier of the notification.
* @param {*} payload The payload of the notification.
*/
sendNotification: function (notification, payload) {
MM.sendNotification(notification, payload, this);
},
/* 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.
* @param {string} notification The identifier of the notification.
* @param {*} payload The payload of the notification.
*/
sendSocketNotification: function (notification, payload) {
this.socket().sendNotification(notification, payload);
},
/* hideModule(module, speed, callback)
/**
* Hide this module.
*
* argument speed Number - The speed of the hide animation.
* argument callback function - Called when the animation is done.
* argument options object - Optional settings for the hide method.
* @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 = function () {};
}
callback = callback || function () { };
callback = callback || function () {};
options = options || {};
var self = this;
MM.hideModule(self, speed, function () {
self.suspend();
callback();
}, options);
MM.hideModule(
this,
speed,
() => {
this.suspend();
callback();
},
options
);
},
/* showModule(module, speed, callback)
/**
* Show this module.
*
* argument speed Number - The speed of the show animation.
* argument callback function - Called when the animation is done.
* argument options object - Optional settings for the hide method.
* @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 = function () {};
}
callback = callback || function () { };
callback = callback || function () {};
options = options || {};
this.resume();
MM.showModule(this, speed, callback, options);
MM.showModule(
this,
speed,
() => {
this.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) {
const stack = Array.prototype.slice.call(arguments, 1);
let item, 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);
const moduleDefinition = Module.definitions[name];
const clonedDefinition = cloneObject(moduleDefinition);
// Note that we clone the definition. Otherwise the objects are shared, which gives problems.
var ModuleClass = Module.extend(clonedDefinition);
const ModuleClass = Module.extend(clonedDefinition);
return new ModuleClass();
};
/* cmpVersions(a,b)
* Compare two semantic version numbers and return the difference.
*
* argument a string - Version number a.
* argument a string - Version number b.
*/
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;
}
Module.register = function (name, moduleDefinition) {
if (moduleDefinition.requiresVersion) {
Log.log("Check MagicMirror version for module '" + name + "' - Minimum version: " + moduleDefinition.requiresVersion + " - Current version: " + version);
if (cmpVersions(version, moduleDefinition.requiresVersion) >= 0) {
Log.log("Check MagicMirror version for module '" + name + "' - Minimum version: " + moduleDefinition.requiresVersion + " - Current version: " + window.mmVersion);
if (cmpVersions(window.mmVersion, moduleDefinition.requiresVersion) >= 0) {
Log.log("Version is ok!");
} else {
Log.log("Version is incorrect. Skip module: '" + name + "'");
Log.warn("Version is incorrect. Skip module: '" + name + "'");
return;
}
}
Log.log("Module registered: " + name);
Module.definitions[name] = moduleDefinition;
};
window.Module = Module;
/**
* 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) {
const regExStrip0 = /(\.0+)+$/;
const segmentsA = a.replace(regExStrip0, "").split(".");
const segmentsB = b.replace(regExStrip0, "").split(".");
const l = Math.min(segmentsA.length, segmentsB.length);
for (let i = 0; i < l; i++) {
let diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10);
if (diff) {
return diff;
}
}
return segmentsA.length - segmentsB.length;
}

View File

@@ -1,26 +1,25 @@
/* Magic Mirror
* Node Helper Superclass
*
* By Michael Teeuw http://michaelteeuw.nl
* By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed.
*/
const Class = require("./class.js");
const Log = require("logger");
const express = require("express");
var Class = require("./class.js");
var express = require("express");
var path = require("path");
NodeHelper = Class.extend({
init: function() {
console.log("Initializing new module helper ...");
const NodeHelper = Class.extend({
init() {
Log.log("Initializing new module helper ...");
},
loaded: function(callback) {
console.log("Module helper loaded: " + this.name);
loaded(callback) {
Log.log(`Module helper loaded: ${this.name}`);
callback();
},
start: function() {
console.log("Starting module helper: " + this.name);
start() {
Log.log(`Starting module helper: ${this.name}`);
},
/* stop()
@@ -29,8 +28,8 @@ NodeHelper = Class.extend({
* gracefully exit the module.
*
*/
stop: function() {
console.log("Stopping module helper: " + this.name);
stop() {
Log.log(`Stopping module helper: ${this.name}`);
},
/* socketNotificationReceived(notification, payload)
@@ -39,8 +38,8 @@ NodeHelper = Class.extend({
* argument notification string - The identifier of the notification.
* argument payload mixed - The payload of the notification.
*/
socketNotificationReceived: function(notification, payload) {
console.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload);
socketNotificationReceived(notification, payload) {
Log.log(`${this.name} received a socket notification: ${notification} - Payload: ${payload}`);
},
/* setName(name)
@@ -48,7 +47,7 @@ NodeHelper = Class.extend({
*
* argument name string - Module name.
*/
setName: function(name) {
setName(name) {
this.name = name;
},
@@ -57,7 +56,7 @@ NodeHelper = Class.extend({
*
* argument path string - Module path.
*/
setPath: function(path) {
setPath(path) {
this.path = path;
},
@@ -67,7 +66,7 @@ NodeHelper = Class.extend({
* argument notification string - The identifier of the notification.
* argument payload mixed - The payload of the notification.
*/
sendSocketNotification: function(notification, payload) {
sendSocketNotification(notification, payload) {
this.io.of(this.name).emit(notification, payload);
},
@@ -77,11 +76,10 @@ NodeHelper = Class.extend({
*
* argument app Express app - The Express app object.
*/
setExpressApp: function(app) {
setExpressApp(app) {
this.expressApp = app;
var publicPath = this.path + "/public";
app.use("/" + this.name, express.static(publicPath));
app.use(`/${this.name}`, express.static(`${this.path}/public`));
},
/* setSocketIO(io)
@@ -90,35 +88,58 @@ NodeHelper = Class.extend({
*
* argument io Socket.io - The Socket io object.
*/
setSocketIO: function(io) {
var self = this;
self.io = io;
setSocketIO(io) {
this.io = io;
console.log("Connecting socket for: " + this.name);
var namespace = this.name;
io.of(namespace).on("connection", function(socket) {
Log.log(`Connecting socket for: ${this.name}`);
io.of(this.name).on("connection", (socket) => {
// add a catch all event.
var onevent = socket.onevent;
socket.onevent = function(packet) {
var args = packet.data || [];
onevent.call(this, packet); // original call
const onevent = socket.onevent;
socket.onevent = function (packet) {
const args = packet.data || [];
onevent.call(this, packet); // original call
packet.data = ["*"].concat(args);
onevent.call(this, packet); // additional call to catch-all
onevent.call(this, packet); // additional call to catch-all
};
// register catch all.
socket.on("*", function(notification, payload) {
socket.on("*", (notification, payload) => {
if (notification !== "*") {
//console.log('received message in namespace: ' + namespace);
self.socketNotificationReceived(notification, payload);
this.socketNotificationReceived(notification, payload);
}
});
});
}
});
NodeHelper.create = function(moduleDefinition) {
NodeHelper.checkFetchStatus = function (response) {
// response.status >= 200 && response.status < 300
if (response.ok) {
return response;
} else {
throw Error(response.statusText);
}
};
/**
* Look at the specified error and return an appropriate error type, that
* can be translated to a detailed error message
*
* @param {Error} error the error from fetching something
* @returns {string} the string of the detailed error message in the translations
*/
NodeHelper.checkFetchError = function (error) {
let error_type = "MODULE_ERROR_UNSPECIFIED";
if (error.code === "EAI_AGAIN") {
error_type = "MODULE_ERROR_NO_CONNECTION";
} else if (error.message === "Unauthorized") {
error_type = "MODULE_ERROR_UNAUTHORIZED";
}
return error_type;
};
NodeHelper.create = function (moduleDefinition) {
return NodeHelper.extend(moduleDefinition);
};

View File

@@ -1,69 +1,96 @@
/* Magic Mirror
* Server
*
* By Michael Teeuw http://michaelteeuw.nl
* By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed.
*/
const express = require("express");
const app = require("express")();
const path = require("path");
const ipfilter = require("express-ipfilter").IpFilter;
const fs = require("fs");
const helmet = require("helmet");
var express = require("express");
var app = require("express")();
var server = require("http").Server(app);
var io = require("socket.io")(server);
var path = require("path");
var ipfilter = require("express-ipfilter").IpFilter;
var fs = require("fs");
var helmet = require("helmet");
var Utils = require(__dirname + "/utils.js");
const Log = require("logger");
const Utils = require("./utils.js");
var Server = function(config, callback) {
/**
* Server
*
* @param {object} config The MM config
* @param {Function} callback Function called when done.
* @class
*/
function Server(config, callback) {
const port = process.env.MM_PORT || config.port;
const serverSockets = new Set();
var port = config.port;
if (process.env.MM_PORT) {
port = process.env.MM_PORT;
let server = null;
if (config.useHttps) {
const options = {
key: fs.readFileSync(config.httpsPrivateKey),
cert: fs.readFileSync(config.httpsCertificate)
};
server = require("https").Server(options, app);
} else {
server = require("http").Server(app);
}
const io = require("socket.io")(server, {
cors: {
origin: /.*$/,
credentials: true
},
allowEIO3: true
});
console.log("Starting server on port " + port + " ... ");
server.on("connection", (socket) => {
serverSockets.add(socket);
socket.on("close", () => {
serverSockets.delete(socket);
});
});
server.listen(port, config.address ? config.address : "localhost");
Log.log(`Starting server on port ${port} ... `);
server.listen(port, config.address || "localhost");
if (config.ipWhitelist instanceof Array && config.ipWhitelist.length === 0) {
console.info(Utils.colors.warn("You're using a full whitelist configuration to allow for all IPs"));
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) {
app.use(function (req, res, next) {
ipfilter(config.ipWhitelist, { mode: config.ipWhitelist.length === 0 ? "deny" : "allow", log: false })(req, res, function (err) {
if (err === undefined) {
return next();
}
console.log(err.message);
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());
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];
const directories = ["/config", "/css", "/fonts", "/modules", "/vendor", "/translations", "/tests/configs"];
for (const directory of directories) {
app.use(directory, express.static(path.resolve(global.root_path + directory)));
}
app.get("/version", function(req,res) {
app.get("/version", function (req, res) {
res.send(global.version);
});
app.get("/config", function(req,res) {
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"});
app.get("/", function (req, res) {
let html = fs.readFileSync(path.resolve(`${global.root_path}/index.html`), { encoding: "utf8" });
html = html.replace("#VERSION#", global.version);
configFile = "config/config.js";
if (typeof(global.configuration_file) !== "undefined") {
configFile = global.configuration_file;
let configFile = "config/config.js";
if (typeof global.configuration_file !== "undefined") {
configFile = global.configuration_file;
}
html = html.replace("#CONFIG_FILE#", configFile);
@@ -73,6 +100,13 @@ var Server = function(config, callback) {
if (typeof callback === "function") {
callback(app, io);
}
};
this.close = function () {
for (const socket of serverSockets.values()) {
socket.destroy();
}
server.close();
};
}
module.exports = Server;

View File

@@ -1,35 +0,0 @@
/* exported Log */
/* Magic Mirror
* Socket Connection
*
* By Michael Teeuw http://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;
self.socket = io("http://localhost:8080");
self.socket.on("notification", function(data) {
MM.sendNotification(data.notification, data.payload, Socket);
});
return {
sendMessage: function(notification, payload, sender) {
Log.log("Send socket message: " + notification);
self.socket.emit("notification", {
notification: notification,
sender: sender,
payload: payload
});
}
};
};

View File

@@ -1,40 +1,53 @@
var MMSocket = function(moduleName) {
var self = this;
/* global io */
/* Magic Mirror
* TODO add description
*
* By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed.
*/
const MMSocket = function (moduleName) {
if (typeof moduleName !== "string") {
throw new Error("Please set the module name for the MMSocket.");
}
self.moduleName = moduleName;
this.moduleName = moduleName;
// Private Methods
self.socket = io("/" + self.moduleName);
var notificationCallback = function() {};
let base = "/";
if (typeof config !== "undefined" && typeof config.basePath !== "undefined") {
base = config.basePath;
}
this.socket = io("/" + this.moduleName, {
path: base + "socket.io"
});
var onevent = self.socket.onevent;
self.socket.onevent = function(packet) {
var args = packet.data || [];
onevent.call(this, packet); // original call
let notificationCallback = function () {};
const onevent = this.socket.onevent;
this.socket.onevent = (packet) => {
const args = packet.data || [];
onevent.call(this.socket, packet); // original call
packet.data = ["*"].concat(args);
onevent.call(this, packet); // additional call to catch-all
onevent.call(this.socket, packet); // additional call to catch-all
};
// register catch all.
self.socket.on("*", function(notification, payload) {
this.socket.on("*", (notification, payload) => {
if (notification !== "*") {
notificationCallback(notification, payload);
}
});
// Public Methods
this.setNotificationCallback = function(callback) {
this.setNotificationCallback = (callback) => {
notificationCallback = callback;
};
this.sendNotification = function(notification, payload) {
this.sendNotification = (notification, payload) => {
if (typeof payload === "undefined") {
payload = {};
}
self.socket.emit(notification, payload);
this.socket.emit(notification, payload);
};
};

View File

@@ -1,143 +1,78 @@
/* exported Translator */
/* global translations */
/* Magic Mirror
* Translator (l10n)
*
* By Christopher Fenner http://github.com/CFenner
* By Christopher Fenner https://github.com/CFenner
* MIT Licensed.
*/
var Translator = (function() {
/* loadJSON(file, callback)
const Translator = (function () {
/**
* Load a JSON file via XHR.
*
* argument file string - Path of the file we want to load.
* argument callback function - Function called when done.
* @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();
const xhr = new XMLHttpRequest();
xhr.overrideMimeType("application/json");
xhr.open("GET", file, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
callback(JSON.parse(stripComments(xhr.responseText)));
// 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);
}
/* loadJSON(str, options)
* Remove any commenting from a json file so it can be parsed.
*
* argument str string - The string that contains json with comments.
* argument opts function - Strip options.
*
* return the stripped string.
*/
function stripComments(str, opts) {
// strip comments copied from: https://github.com/sindresorhus/strip-json-comments
var singleComment = 1;
var multiComment = 2;
function stripWithoutWhitespace() {
return "";
}
function stripWithWhitespace(str, start, end) {
return str.slice(start, end).replace(/\S/g, " ");
}
opts = opts || {};
var currentChar;
var nextChar;
var insideString = false;
var insideComment = false;
var offset = 0;
var ret = "";
var strip = opts.whitespace === false ? stripWithoutWhitespace : stripWithWhitespace;
for (var i = 0; i < str.length; i++) {
currentChar = str[i];
nextChar = str[i + 1];
if (!insideComment && currentChar === "\"") {
var escaped = str[i - 1] === "\\" && str[i - 2] !== "\\";
if (!escaped) {
insideString = !insideString;
}
}
if (insideString) {
continue;
}
if (!insideComment && currentChar + nextChar === "//") {
ret += str.slice(offset, i);
offset = i;
insideComment = singleComment;
i++;
} else if (insideComment === singleComment && currentChar + nextChar === "\r\n") {
i++;
insideComment = false;
ret += strip(str, offset, i);
offset = i;
continue;
} else if (insideComment === singleComment && currentChar === "\n") {
insideComment = false;
ret += strip(str, offset, i);
offset = i;
} else if (!insideComment && currentChar + nextChar === "/*") {
ret += str.slice(offset, i);
offset = i;
insideComment = multiComment;
i++;
continue;
} else if (insideComment === multiComment && currentChar + nextChar === "*/") {
i++;
insideComment = false;
ret += strip(str, offset, i + 1);
offset = i + 1;
continue;
}
}
return ret + (insideComment ? strip(str.substr(offset)) : str.substr(offset));
}
return {
coreTranslations: {},
coreTranslationsFallback: {},
translations: {},
translationsFallback: {},
/* translate(module, key, variables)
/**
* Load a translation for a given key for a given module.
*
* argument module Module - The module to load the translation for.
* argument key string - The key of the text to translate.
* argument variables - The variables to use within the translation template (optional)
* @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) {
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."
/**
* 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]") {
if (Object.prototype.toString.call(template) !== "[object String]") {
return template;
}
if(variables.fallback && !template.match(new RegExp("\{.+\}"))) {
if (variables.fallback && !template.match(new RegExp("{.+}"))) {
template = variables.fallback;
}
return template.replace(new RegExp("\{([^\}]+)\}", "g"), function(_unused, varName){
return variables[varName] || "{"+varName+"}";
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]) {
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);
}
@@ -160,73 +95,61 @@ var Translator = (function() {
return key;
},
/* load(module, file, isFallback, callback)
/**
* Load a translation file (json) and remember the data.
*
* argument module Module - The module to load the translation file for.
* argument file string - Path of the file we want to load.
* argument isFallback boolean - Flag to indicate fallback translations.
* argument callback function - Function called when done.
* @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);
load(module, file, isFallback, callback) {
Log.log(`${module.name} - Load translation${isFallback && " fallback"}: ${file}`);
if (this.translationsFallback[module.name]) {
callback();
return;
}
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 {
loadJSON(module.file(file), (json) => {
const property = isFallback ? "translationsFallback" : "translations";
this[property][module.name] = json;
callback();
}
});
},
/* loadCoreTranslations(lang)
/**
* Load the core translations.
*
* argument lang String - The language identifier of the core language.
* @param {string} lang The language identifier of the core language.
*/
loadCoreTranslations: function(lang) {
var self = this;
loadCoreTranslations: function (lang) {
if (lang in translations) {
Log.log("Loading core translation file: " + translations[lang]);
loadJSON(translations[lang], function(translations) {
self.coreTranslations = translations;
loadJSON(translations[lang], (translations) => {
this.coreTranslations = translations;
});
} else {
Log.log("Configured language not found in core translations.");
}
self.loadCoreTranslationsFallback();
this.loadCoreTranslationsFallback();
},
/* 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;}
loadCoreTranslationsFallback: function () {
let first = Object.keys(translations)[0];
if (first) {
Log.log("Loading core translation fallback file: " + translations[first]);
loadJSON(translations[first], function(translations) {
self.coreTranslationsFallback = translations;
loadJSON(translations[first], (translations) => {
this.coreTranslationsFallback = translations;
});
}
},
}
};
})();
window.Translator = Translator;

View File

@@ -1,19 +1,16 @@
/* exported Utils */
/* Magic Mirror
* Utils
*
* By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
* MIT Licensed.
*/
const colors = require("colors/safe");
var colors = require("colors/safe");
var Utils = {
module.exports = {
colors: {
warn: colors.yellow,
error: colors.red,
info: colors.blue
info: colors.blue,
pass: colors.green
}
};
if (typeof module !== "undefined") {module.exports = Utils;}

View File

@@ -6,8 +6,5 @@
"module": "commonjs",
"allowSyntheticDefaultImports": true
},
"exclude": [
"modules",
"node_modules"
]
"exclude": ["modules", "node_modules"]
}

View File

@@ -1,16 +1,16 @@
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,
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: {
@@ -18,14 +18,14 @@ export declare const Module: {
};
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,
};
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;
};

View File

@@ -1,721 +0,0 @@
# MagicMirror² Module Development Documentation
This document describes the way to develop your own MagicMirror² modules.
Table of Contents:
- Module structure
- Files
- The Core module file: modulename.js
- Available module instance properties
- Subclassable module methods
- Module instance methods
- Visibility locking
- The Node Helper: node_helper.js
- Available module instance properties
- Subclassable module methods
- Module instance methods
- MagicMirror Helper Methods
- Module Selection
- MagicMirror Logger
---
## General Advice
As MagicMirror has gained huge popularity, so has the number of available modules. For new users and developers alike, it is very time consuming to navigate around the various repositories in order to find out what exactly a certain modules does, how it looks and what it depends on. Unfortunately, this information is rarely available, nor easily obtained without having to install it first.
Therefore **we highly recommend you to include the following information in your README file.**
- A high quality screenshot of your working module
- A short, one sentence, clear description what it does (duh!)
- What external API's it depends upon, including web links to those
- Whether the API/request require a key and the user limitations of those. (Is it free?)
Surely this also help you get better recognition and feedback for your work.
## Module structure
All modules are loaded in the `modules` folder. The default modules are grouped together in the `modules/default` folder. Your module should be placed in a subfolder of `modules`. Note that any file or folder your create in the `modules` folder will be ignored by git, allowing you to upgrade the MagicMirror² without the loss of your files.
A module can be placed in one single folder. Or multiple modules can be grouped in a subfolder. Note that name of the module must be unique. Even when a module with a similar name is placed in a different folder, they can't be loaded at the same time.
### Files
- **modulename/modulename.js** - This is your core module script.
- **modulename/node_helper.js** - This is an optional helper that will be loaded by the node script. The node helper and module script can communicate with each other using an integrated socket system.
- **modulename/public** - Any files in this folder can be accessed via the browser on `/modulename/filename.ext`.
- **modulename/anyfileorfolder** Any other file or folder in the module folder can be used by the core module script. For example: *modulename/css/modulename.css* would be a good path for your additional module styles.
## The Core module file: modulename.js
This is the script in which the module will be defined. This script is required in order for the module to be used. In it's most simple form, the core module file must contain:
````javascript
Module.register("modulename",{});
````
Of course, the above module would not do anything fancy, so it's good to look at one of the simplest modules: **helloworld**:
````javascript
//helloworld.js:
Module.register("helloworld",{
// Default module config.
defaults: {
text: "Hello World!"
},
// Override dom generator.
getDom: function() {
var wrapper = document.createElement("div");
wrapper.innerHTML = this.config.text;
return wrapper;
}
});
````
As you can see, the `Module.register()` method takes two arguments: the name of the module and an object with the module properties.
### Available module instance properties
After the module is initialized, the module instance has a few available module properties:
| Instance Property | Type | Description |
|:----------------- |:---- |:----------- |
| `this.name` | String | The name of the module. |
| `this.identifier` | String | This is a unique identifier for the module instance. |
| `this.hidden` | Boolean | This represents if the module is currently hidden (faded away). |
| `this.config` | Boolean | The configuration of the module instance as set in the user's `config.js` file. This config will also contain the module's defaults if these properties are not over-written by the user config. |
| `this.data` | Object | The data object contain additional metadata about the module instance. (See below) |
The `this.data` data object contain the following metadata:
- `data.classes` - The classes which are added to the module dom wrapper.
- `data.file` - The filename of the core module file.
- `data.path` - The path of the module folder.
- `data.header` - The header added to the module.
- `data.position` - The position in which the instance will be shown.
#### `defaults: {}`
Any properties defined in the defaults object, will be merged with the module config as defined in the user's config.js file. This is the best place to set your modules' configuration defaults. Any of the module configuration properties can be accessed using `this.config.propertyName`, but more about that later.
#### `requiresVersion:`
*Introduced in version: 2.1.0.*
A string that defines the minimum version of the MagicMirror framework. If it is set, the system compares the required version with the users version. If the version of the user is out of date, it won't run the module. Make sure to also set this value in the Node helper.
**Note:** Since this check is introduced in version 2.1.0, this check will not be run in older versions. Keep this in mind if you get issue reports on your module.
Example:
````javascript
requiresVersion: "2.1.0",
````
### Subclassable module methods
#### `init()`
This method is called when a module gets instantiated. In most cases you do not need to subclass this method.
#### `loaded(callback)`
*Introduced in version: 2.1.1.*
This method is called when a module is loaded. Subsequent modules in the config are not yet loaded. The `callback` function MUST be called when the module is done loading. In most cases you do not need to subclass this method.
**Example:**
````javascript
loaded: function(callback) {
this.finishLoading();
Log.log(this.name + ' is loaded!');
callback();
}
````
#### `start()`
This method is called when all modules are loaded and the system is ready to boot up. Keep in mind that the dom object for the module is not yet created. The start method is a perfect place to define any additional module properties:
**Example:**
````javascript
start: function() {
this.mySpecialProperty = "So much wow!";
Log.log(this.name + ' is started!');
}
````
#### `getScripts()`
**Should return: Array**
The getScripts method is called to request any additional scripts that need to be loaded. This method should therefore return an array with strings. If you want to return a full path to a file in the module folder, use the `this.file('filename.js')` method. In all cases the loader will only load a file once. It even checks if the file is available in the default vendor folder.
**Example:**
````javascript
getScripts: function() {
return [
'script.js', // will try to load it from the vendor folder, otherwise it will load is from the module folder.
'moment.js', // this file is available in the vendor folder, so it doesn't need to be available in the module folder.
this.file('anotherfile.js'), // this file will be loaded straight from the module folder.
'https://code.jquery.com/jquery-2.2.3.min.js', // this file will be loaded from the jquery servers.
]
}
````
**Note:** If a file can not be loaded, the boot up of the mirror will stall. Therefore, it's advised not to use any external urls.
#### `getStyles()`
**Should return: Array**
The getStyles method is called to request any additional stylesheets that need to be loaded. This method should therefore return an array with strings. If you want to return a full path to a file in the module folder, use the `this.file('filename.css')` method. In all cases the loader will only load a file once. It even checks if the file is available in the default vendor folder.
**Example:**
````javascript
getStyles: function() {
return [
'script.css', // will try to load it from the vendor folder, otherwise it will load is from the module folder.
'font-awesome.css', // this file is available in the vendor folder, so it doesn't need to be available in the module folder.
this.file('anotherfile.css'), // this file will be loaded straight from the module folder.
'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css', // this file will be loaded from the bootstrapcdn servers.
]
}
````
**Note:** If a file can not be loaded, the boot up of the mirror will stall. Therefore, it's advised not to use any external URLs.
#### `getTranslations()`
**Should return: Dictionary**
The getTranslations method is called to request translation files that need to be loaded. This method should therefore return a dictionary with the files to load, identified by the country's short name.
If the module does not have any module specific translations, the function can just be omitted or return `false`.
**Example:**
````javascript
getTranslations: function() {
return {
en: "translations/en.json",
de: "translations/de.json"
}
}
````
#### `getDom()`
**Should return:** Dom Object
Whenever the MagicMirror needs to update the information on screen (because it starts, or because your module asked a refresh using `this.updateDom()`), the system calls the getDom method. This method should therefore return a dom object.
**Example:**
````javascript
getDom: function() {
var wrapper = document.createElement("div");
wrapper.innerHTML = 'Hello world!';
return wrapper;
}
````
#### `getHeader()`
**Should return:** String
Whenever the MagicMirror needs to update the information on screen (because it starts, or because your module asked a refresh using `this.updateDom()`), the system calls the getHeader method to retrieve the module's header. This method should therefor return a string. If this method is not subclassed, this function will return the user's configured header.
If you want to use the original user's configured header, reference `this.data.header`.
**NOTE:** If the user did not configure a default header, no header will be displayed and thus this method will not be called.
**Example:**
````javascript
getHeader: function() {
return this.data.header + ' Foo Bar';
}
````
#### `notificationReceived(notification, payload, sender)`
That MagicMirror core has the ability to send notifications to modules. Or even better: the modules have the possibility to send notifications to other modules. When this module is called, it has 3 arguments:
- `notification` - String - The notification identifier.
- `payload` - AnyType - The payload of a notification.
- `sender` - Module - The sender of the notification. If this argument is `undefined`, the sender of the notification is the core system.
**Example:**
````javascript
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);
}
}
````
**Note:** the system sends three notifications when starting up. These notifications could come in handy!
- `ALL_MODULES_STARTED` - All modules are started. You can now send notifications to other modules.
- `DOM_OBJECTS_CREATED` - All dom objects are created. The system is now ready to perform visual changes.
- `MODULE_DOM_CREATED` - This module's dom has been fully loaded. You can now access your module's dom objects.
#### `socketNotificationReceived: function(notification, payload)`
When using a node_helper, the node helper can send your module notifications. When this module is called, it has 2 arguments:
- `notification` - String - The notification identifier.
- `payload` - AnyType - The payload of a notification.
**Note 1:** When a node helper sends a notification, all modules of that module type receive the same notifications. <br>
**Note 2:** The socket connection is established as soon as the module sends its first message using [sendSocketNotification](#thissendsocketnotificationnotification-payload).
**Example:**
````javascript
socketNotificationReceived: function(notification, payload) {
Log.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload);
},
````
#### `suspend()`
When a module is hidden (using the `module.hide()` method), the `suspend()` method will be called. By subclassing this method you can perform tasks like halting the update timers.
#### `resume()`
When a module is requested to be shown (using the `module.show()` method), the `resume()` method will be called. By subclassing this method you can perform tasks restarting the update timers.
### Module instance methods
Each module instance has some handy methods which can be helpful building your module.
#### `this.file(filename)`
***filename* String** - The name of the file you want to create the path for.<br>
**Returns String**
If you want to create a path to a file in your module folder, use the `file()` method. It returns the path to the filename given as the attribute. Is method comes in handy when configuring the [getScripts](#getscripts) and [getStyles](#getstyles) methods.
#### `this.updateDom(speed)`
***speed* Number** - Optional. Animation speed in milliseconds.<br>
Whenever your module need to be updated, call the `updateDom(speed)` method. It requests the MagicMirror core to update its dom object. If you define the speed, the content update will be animated, but only if the content will really change.
As an example: the clock modules calls this method every second:
````javascript
...
start: function() {
var self = this;
setInterval(function() {
self.updateDom(); // no speed defined, so it updates instantly.
}, 1000); //perform every 1000 milliseconds.
},
...
````
#### `this.sendNotification(notification, payload)`
***notification* String** - The notification identifier.<br>
***payload* AnyType** - Optional. A notification payload.<br>
If you want to send a notification to all other modules, use the `sendNotification(notification, payload)`. All other modules will receive the message via the [notificationReceived](#notificationreceivednotification-payload-sender) method. In that case, the sender is automatically set to the instance calling the sendNotification method.
**Example:**
````javascript
this.sendNotification('MYMODULE_READY_FOR_ACTION', {foo:bar});
````
#### `this.sendSocketNotification(notification, payload)`
***notification* String** - The notification identifier.<br>
***payload* AnyType** - Optional. A notification payload.<br>
If you want to send a notification to the node_helper, use the `sendSocketNotification(notification, payload)`. Only the node_helper of this module will receive the socket notification.
**Example:**
````javascript
this.sendSocketNotification('SET_CONFIG', this.config);
````
#### `this.hide(speed, callback, options)`
***speed* Number** - Optional (Required when setting callback or options), The speed of the hide animation in milliseconds.
***callback* Function** - Optional, The callback after the hide animation is finished.
***options* Function** - Optional, Object with additional options for the hide action (see below). (*Introduced in version: 2.1.0.*)
To hide a module, you can call the `hide(speed, callback)` method. You can call the hide method on the module instance itself using `this.hide()`, but of course you can also hide another module using `anOtherModule.hide()`.
Possible configurable options:
- `lockString` - String - When setting lock string, the module can not be shown without passing the correct lockstring. This way (multiple) modules can prevent a module from showing. It's considered best practice to use your modules identifier as the locksString: `this.identifier`. See *visibility locking* below.
**Note 1:** If the hide animation is cancelled, for instance because the show method is called before the hide animation was finished, the callback will not be called.<br>
**Note 2:** If the hide animation is hijacked (an other method calls hide on the same module), the callback will not be called.<br>
**Note 3:** If the dom is not yet created, the hide method won't work. Wait for the `DOM_OBJECTS_CREATED` [notification](#notificationreceivednotification-payload-sender).
#### `this.show(speed, callback, options)`
***speed* Number** - Optional (Required when setting callback or options), The speed of the show animation in milliseconds.
***callback* Function** - Optional, The callback after the show animation is finished.
***options* Function** - Optional, Object with additional options for the show action (see below). (*Introduced in version: 2.1.0.*)
To show a module, you can call the `show(speed, callback)` method. You can call the show method on the module instance itself using `this.show()`, but of course you can also show another module using `anOtherModule.show()`.
Possible configurable options:
- `lockString` - String - When setting lock string, the module can not be shown without passing the correct lockstring. This way (multiple) modules can prevent a module from showing. See *visibility locking* below.
- `force` - Boolean - When setting the force tag to `true`, the locking mechanism will be overwritten. Use this option with caution. It's considered best practice to let the usage of the force option be use- configurable. See *visibility locking* below.
**Note 1:** If the show animation is canceled, for instance because the hide method is called before the show animation was finished, the callback will not be called.<br>
**Note 2:** If the show animation is hijacked (an other method calls show on the same module), the callback will not be called.<br>
**Note 3:** If the dom is not yet created, the show method won't work. Wait for the `DOM_OBJECTS_CREATED` [notification](#notificationreceivednotification-payload-sender).
#### Visibility locking
(*Introduced in version: 2.1.0.*)
Visibility locking helps the module system to prevent unwanted hide/show actions. The following scenario explains the concept:
**Module B asks module A to hide:**
````javascript
moduleA.hide(0, {lockString: "module_b_identifier"});
````
Module A is now hidden, and has an lock array with the following strings:
````javascript
moduleA.lockStrings == ["module_b_identifier"]
moduleA.hidden == true
````
**Module C asks module A to hide:**
````javascript
moduleA.hide(0, {lockString: "module_c_identifier"});
````
Module A is now hidden, and has an lock array with the following strings:
````javascript
moduleA.lockStrings == ["module_b_identifier", "module_c_identifier"]
moduleA.hidden == true
````
**Module B asks module A to show:**
````javascript
moduleA.show(0, {lockString: "module_b_identifier"});
````
The lockString will be removed from moduleAs locks array, but since there still is an other lock string available, the module remains hidden:
````javascript
moduleA.lockStrings == ["module_c_identifier"]
moduleA.hidden == true
````
**Module C asks module A to show:**
````javascript
moduleA.show(0, {lockString: "module_c_identifier"});
````
The lockString will be removed from moduleAs locks array, and since this will result in an empty lock array, the module will be visible:
````javascript
moduleA.lockStrings == []
moduleA.hidden == false
````
**Note:** The locking mechanism can be overwritten by using the force tag:
````javascript
moduleA.show(0, {force: true});
````
This will reset the lockstring array, and will show the module.
````javascript
moduleA.lockStrings == []
moduleA.hidden == false
````
Use this `force` method with caution. See `show()` method for more information.
#### `this.translate(identifier)`
***identifier* String** - Identifier of the string that should be translated.
The Magic Mirror contains a convenience wrapper for `l18n`. You can use this to automatically serve different translations for your modules based on the user's `language` configuration.
If no translation is found, a fallback will be used. The fallback sequence is as follows:
- 1. Translation as defined in module translation file of the user's preferred language.
- 2. Translation as defined in core translation file of the user's preferred language.
- 3. Translation as defined in module translation file of the fallback language (the first defined module translation file).
- 4. Translation as defined in core translation file of the fallback language (the first defined core translation file).
- 5. The key (identifier) of the translation.
When adding translations to your module, it's a good idea to see if an appropriate translation is already available in the [core translation files](https://github.com/MichMich/MagicMirror/tree/master/translations). This way, your module can benefit from the existing translations.
**Example:**
````javascript
this.translate("INFO") //Will return a translated string for the identifier INFO
````
**Example json file:**
````javascript
{
"INFO": "Really important information!"
}
````
**Note:** although comments are officially not supported in JSON files, MagicMirror allows it by stripping the comments before parsing the JSON file. Comments in translation files could help other translators.
##### `this.translate(identifier, variables)`
***identifier* String** - Identifier of the string that should be translated.
***variables* Object** - Object of variables to be used in translation.
This improved and backwards compatible way to handle translations behaves like the normal translation function and follows the rules described above. It's recommended to use this new format for translating everywhere. It allows translator to change the word order in the sentence to be translated.
**Example:**
````javascript
var timeUntilEnd = moment(event.endDate, "x").fromNow(true);
this.translate("RUNNING", { "timeUntilEnd": timeUntilEnd) }); // Will return a translated string for the identifier RUNNING, replacing `{timeUntilEnd}` with the contents of the variable `timeUntilEnd` in the order that translator intended.
````
**Example English .json file:**
````javascript
{
"RUNNING": "Ends in {timeUntilEnd}",
}
````
**Example Finnish .json file:**
````javascript
{
"RUNNING": "Päättyy {timeUntilEnd} päästä",
}
````
**Note:** The *variables* Object has an special case called `fallback`. It's used to support old translations in translation files that do not have the variables in them. If you are upgrading an old module that had translations that did not support the word order, it is recommended to have the fallback layout.
**Example:**
````javascript
var timeUntilEnd = moment(event.endDate, "x").fromNow(true);
this.translate("RUNNING", {
"fallback": this.translate("RUNNING") + " {timeUntilEnd}"
"timeUntilEnd": timeUntilEnd
)}); // Will return a translated string for the identifier RUNNING, replacing `{timeUntilEnd}` with the contents of the variable `timeUntilEnd` in the order that translator intended. (has a fallback)
````
**Example Swedish .json file that does not have the variable in it:**
````javascript
{
"RUNNING": "Slutar",
}
````
In this case the `translate`-function will not find any variables in the translation, will look for `fallback` variable and use that if possible to create the translation.
## The Node Helper: node_helper.js
The node helper is a Node.js script that is able to do some backend task to support your module. For every module type, only one node helper instance will be created. For example: if your MagicMirror uses two calendar modules, there will be only one calendar node helper instantiated.
**Note:** Because there is only one node helper per module type, there is no default config available within your module. It's your task to send the desired config from your module to your node helper.
In it's most simple form, the node_helper.js file must contain:
````javascript
var NodeHelper = require("node_helper");
module.exports = NodeHelper.create({});
````
Of course, the above helper would not do anything useful. So with the information above, you should be able to make it a bit more sophisticated.
### Available module instance properties
#### `this.name`
**String**
The name of the module
#### `this.path`
**String**
The path of the module
#### `this.expressApp`
**Express App Instance**
This is a link to the express instance. It will allow you to define extra routes.
**Example:**
````javascript
start: function() {
this.expressApp.get('/foobar', function (req, res) {
res.send('GET request to /foobar');
});
}
````
**Note:** By default, a public path to your module's public folder will be created:
````javascript
this.expressApp.use("/" + this.name, express.static(this.path + "/public"));
````
#### `this.io`
**Socket IO Instance**
This is a link to the IO instance. It will allow you to do some Socket.IO magic. In most cases you won't need this, since the Node Helper has a few convenience methods to make this simple.
#### `requiresVersion:`
*Introduced in version: 2.1.0.*
A string that defines the minimum version of the MagicMirror framework. If it is set, the system compares the required version with the users version. If the version of the user is out of date, it won't run the module.
**Note:** Since this check is introduced in version 2.1.0, this check will not be run in older versions. Keep this in mind if you get issue reports on your module.
Example:
````javascript
requiresVersion: "2.1.0",
````
### Subclassable module methods
#### `init()`
This method is called when a node helper gets instantiated. In most cases you do not need to subclass this method.
#### `start()`
This method is called when all node helpers are loaded and the system is ready to boot up. The start method is a perfect place to define any additional module properties:
**Example:**
````javascript
start: function() {
this.mySpecialProperty = "So much wow!";
Log.log(this.name + ' is started!');
}
````
#### `stop()`
This method is called when the MagicMirror server receives a `SIGINT` command and is shutting down. This method should include any commands needed to close any open connections, stop any sub-processes and gracefully exit the module.
**Example:**
````javascript
stop: function() {
console.log("Shutting down MyModule");
this.connection.close();
}
````
#### `socketNotificationReceived: function(notification, payload)`
With this method, your node helper can receive notifications from your modules. When this method is called, it has 2 arguments:
- `notification` - String - The notification identifier.
- `payload` - AnyType - The payload of a notification.
**Note:** The socket connection is established as soon as the module sends its first message using [sendSocketNotification](thissendsocketnotificationnotification-payload).
**Example:**
````javascript
socketNotificationReceived: function(notification, payload) {
Log.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload);
},
````
### Module instance methods
Each node helper has some handy methods which can be helpful building your module.
#### `this.sendSocketNotification(notification, payload)`
***notification* String** - The notification identifier.<br>
***payload* AnyType** - Optional. A notification payload.<br>
If you want to send a notification to all your modules, use the `sendSocketNotification(notification, payload)`. Only the module of your module type will receive the socket notification.
**Note:** Since all instances of your module will receive the notifications, it's your task to make sure the right module responds to your messages.
**Example:**
````javascript
this.sendSocketNotification('SET_CONFIG', this.config);
````
## MagicMirror Helper Methods
The core Magic Mirror object: `MM` has some handy method that will help you in controlling your and other modules. Most of the `MM` methods are available via convenience methods on the Module instance.
### Module selection
The only additional method available for your module, is the feature to retrieve references to other modules. This can be used to hide and show other modules.
#### `MM.getModules()`
**Returns Array** - An array with module instances.<br>
To make a selection of all currently loaded module instances, run the `MM.getModules()` method. It will return an array with all currently loaded module instances. The returned array has a lot of filtering methods. See below for more info.
**Note:** This method returns an empty array if not all modules are started yet. Wait for the `ALL_MODULES_STARTED` [notification](#notificationreceivednotification-payload-sender).
##### `.withClass(classnames)`
***classnames* String or Array** - The class names on which you want to filter.
**Returns Array** - An array with module instances.<br>
If you want to make a selection based on one or more class names, use the withClass method on a result of the `MM.getModules()` method. The argument of the `withClass(classname)` method can be an array, or space separated string.
**Examples:**
````javascript
var modules = MM.getModules().withClass('classname');
var modules = MM.getModules().withClass('classname1 classname2');
var modules = MM.getModules().withClass(['classname1','classname2']);
````
##### `.exceptWithClass(classnames)`
***classnames* String or Array** - The class names of the modules you want to remove from the results.
**Returns Array** - An array with module instances.<br>
If you to remove some modules from a selection based on a classname, use the exceptWithClass method on a result of the `MM.getModules()` method. The argument of the `exceptWithClass(classname)` method can be an array, or space separated string.
**Examples:**
````javascript
var modules = MM.getModules().exceptWithClass('classname');
var modules = MM.getModules().exceptWithClass('classname1 classname2');
var modules = MM.getModules().exceptWithClass(['classname1','classname2']);
````
##### `.exceptModule(module)`
***module* Module Object** - The reference to a module you want to remove from the results.
**Returns Array** - An array with module instances.<br>
If you to remove a specific module instance from a selection based on a classname, use the exceptWithClass method on a result of the `MM.getModules()` method. This can be helpful if you want to select all module instances except the instance of your module.
**Examples:**
````javascript
var modules = MM.getModules().exceptModule(this);
````
Of course, you can combine all of the above filters:
**Example:**
````javascript
var modules = MM.getModules().withClass('classname1').exceptwithClass('classname2').exceptModule(aModule);
````
##### `.enumerate(callback)`
***callback* Function(module)** - The callback run on every instance.
If you want to perform an action on all selected modules, you can use the `enumerate` function:
````javascript
MM.getModules().enumerate(function(module) {
Log.log(module.name);
});
````
**Example:**
To hide all modules except the your module instance, you could write something like:
````javascript
Module.register("modulename",{
//...
notificationReceived: function(notification, payload, sender) {
if (notification === 'DOM_OBJECTS_CREATED') {
MM.getModules().exceptModule(this).enumerate(function(module) {
module.hide(1000, function() {
//Module hidden.
});
});
}
},
//...
});
````
## MagicMirror Logger
The Magic Mirror contains a convenience wrapper for logging. Currently, this logger is a simple proxy to the original `console.log` methods. But it might get additional features in the future. The Loggers is currently only available in the core module file (not in the node_helper).
**Examples:**
````javascript
Log.info('error');
Log.log('log');
Log.error('info');
````

View File

@@ -1,65 +1,5 @@
# Module: Alert
The alert module is one of the default modules of the MagicMirror. This module displays notifications from other modules.
## Usage
To use this module, add it to the modules array in the config/config.js file:
```
modules: [
{
module: "alert",
config: {
// The config property is optional.
// See 'Configuration options' for more information.
}
}
]
```
## Configuration options
The following properties can be configured:
| Option | Description
| ----------------- | -----------
| `effect` | The animation effect to use for notifications. <br><br> **Possible values:** `scale` `slide` `genie` `jelly` `flip` `exploader` `bouncyflip` <br> **Default value:** `slide`
| `alert_effect` | The animation effect to use for alerts. <br><br> **Possible values:** `scale` `slide` `genie` `jelly` `flip` `exploader` `bouncyflip` <br> **Default value:** `jelly`
| `display_time` | Time a notification is displayed in milliseconds. <br><br> **Possible values:** `int` <br> **Default value:** `3500`
| `position` | Position where the notifications should be displayed. <br><br> **Possible values:** `left` `center` `right` <br> **Default value:** `center`
| `welcome_message` | Message shown at startup. <br><br> **Possible values:** `string` `false` <br> **Default value:** `false` (no message at startup)
## Developer notes
For notifications use:
```
self.sendNotification("SHOW_ALERT", {type: "notification"});
```
For alerts use:
```
self.sendNotification("SHOW_ALERT", {});
```
### Notification params
| Option | Description
| ------------------ | -----------
| `title` | The title of the notification. <br><br> **Possible values:** `text` or `html`
| `message` | The message of the notification. <br><br> **Possible values:** `text` or `html`
| `timer` (optional) | How long the notification should stay visible in ms. <br> If absent, the default `display_time` is used. <br> **Possible values:** `int` `float`
### Alert params
| Option | Description
| ----------------------------------------------- | -----------
| `title` | The title of the alert. <br><br> **Possible values:** `text` or `html`
| `message` | The message of the alert. <br><br> **Possible values:** `text` or `html`
| `imageUrl` (optional) | Image to show in the alert <br><br> **Possible values:** `url` `path` <br> **Default value:** `none`
| `imageFA` (optional) | Font Awesome icon to show in the alert <br><br> **Possible values:** See [Font Awsome](http://fontawesome.io/icons/) website. <br> **Default value:** `none`
| `imageHeight` (optional even with imageUrl set) | Height of the image <br><br> **Possible values:** `intpx` <br> **Default value:** `80px`
| `timer` (optional) | How long the alert should stay visible in ms. <br> **Important:** If you do not use the `timer`, it is your duty to hide the alert by using `self.sendNotification("HIDE_ALERT");`! <br><br>**Possible values:** `int` `float` <br> **Default value:** `none`
## Open Source Licenses
### [NotificationStyles](https://github.com/codrops/NotificationStyles)
See [tympanus.net](http://tympanus.net/codrops/licensing/) for license.
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/alert.html).

View File

@@ -1,155 +1,146 @@
/* global Module */
/* global NotificationFx */
/* Magic Mirror
* Module: alert
*
* By Paul-Vincent Roll http://paulvincentroll.com
* By Paul-Vincent Roll https://paulvincentroll.com/
* MIT Licensed.
*/
Module.register("alert", {
alerts: {},
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
effect: "slide", // scale|slide|genie|jelly|flip|bouncyflip|exploader
alert_effect: "jelly", // scale|slide|genie|jelly|flip|bouncyflip|exploader
display_time: 3500, // time a notification is displayed in seconds
position: "center",
//shown at startup
welcome_message: false,
welcome_message: false // shown at startup
},
getScripts: function() {
return ["classie.js", "modernizr.custom.js", "notificationFx.js"];
getScripts() {
return ["notificationFx.js"];
},
getStyles: function() {
return ["ns-default.css", "font-awesome.css"];
getStyles() {
return ["font-awesome.css", this.file(`./styles/notificationFx.css`), this.file(`./styles/${this.config.position}.css`)];
},
// Define required translations.
getTranslations: function() {
getTranslations() {
return {
en: "translations/en.json",
bg: "translations/bg.json",
da: "translations/da.json",
de: "translations/de.json",
en: "translations/en.json",
es: "translations/es.json",
fr: "translations/fr.json",
hu: "translations/hu.json",
nl: "translations/nl.json",
ru: "translations/ru.json"
};
},
show_notification: function(message) {
if (this.config.effect === "slide") {this.config.effect = this.config.effect + "-" + this.config.position;}
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();
getTemplate(type) {
return `templates/${type}.njk`;
},
show_alert: function(params, sender) {
var self = this;
//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;
image = "";
} 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
var 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);
start() {
Log.info(`Starting module: ${this.name}`);
if (this.config.effect === "slide") {
this.config.effect = `${this.config.effect}-${this.config.position}`;
}
//Display title and message only if they are provided in notification parameters
var 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,
al_no: "ns-alert"
});
//Show alert
this.alerts[sender.name].show();
//Add timer to dismiss alert and overlay
if (params.timer) {
setTimeout(function() {
self.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
var overlay = document.getElementById("overlay");
overlay.parentNode.removeChild(overlay);
if (this.config.welcome_message) {
const message = this.config.welcome_message === true ? this.translate("welcome") : this.config.welcome_message;
this.showNotification({ title: this.translate("sysTitle"), message });
}
},
setPosition: function(pos) {
//Add css to body depending on the set position for notifications
var 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) {
notificationReceived(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);
if (payload.type === "notification") {
this.showNotification(payload);
} else {
this.showAlert(payload, sender);
}
} else if (notification === "HIDE_ALERT") {
this.hide_alert(sender);
this.hideAlert(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});
async showNotification(notification) {
const message = await this.renderMessage("notification", notification);
new NotificationFx({
message,
layout: "growl",
effect: this.config.effect,
ttl: notification.timer || this.config.display_time
}).show();
},
async showAlert(alert, sender) {
// If module already has an open alert close it
if (this.alerts[sender.name]) {
this.hideAlert(sender, false);
}
// Add overlay
if (!Object.keys(this.alerts).length) {
this.toggleBlur(true);
}
const message = await this.renderMessage("alert", alert);
// Store alert in this.alerts
this.alerts[sender.name] = new NotificationFx({
message,
effect: this.config.alert_effect,
ttl: alert.timer,
onClose: () => this.hideAlert(sender),
al_no: "ns-alert"
});
// Show alert
this.alerts[sender.name].show();
// Add timer to dismiss alert and overlay
if (alert.timer) {
setTimeout(() => {
this.hideAlert(sender);
}, alert.timer);
}
},
hideAlert(sender, close = true) {
// Dismiss alert and remove from this.alerts
if (this.alerts[sender.name]) {
this.alerts[sender.name].dismiss(close);
delete this.alerts[sender.name];
// Remove overlay
if (!Object.keys(this.alerts).length) {
this.toggleBlur(false);
}
}
Log.info("Starting module: " + this.name);
},
renderMessage(type, data) {
return new Promise((resolve) => {
this.nunjucksEnvironment().render(this.getTemplate(type), data, function (err, res) {
if (err) {
Log.error("Failed to render alert", err);
}
resolve(res);
});
});
},
toggleBlur(add = false) {
const method = add ? "add" : "remove";
const modules = document.querySelectorAll(".module");
for (const module of modules) {
module.classList[method]("alert-blur");
}
}
});

View File

@@ -1,79 +0,0 @@
/*!
* classie - class helper functions
* from bonzo https://github.com/ded/bonzo
*
* classie.has( elem, 'my-class' ) -> true/false
* classie.add( elem, 'my-new-class' )
* classie.remove( elem, 'my-unwanted-class' )
* classie.toggle( elem, 'my-class' )
*/
// jscs:disable
/*jshint browser: true, strict: true, undef: true */
/*global define: false */
(function(window) {
"use strict";
// class helper functions from bonzo https://github.com/ded/bonzo
function classReg(className) {
return new RegExp("(^|\\s+)" + className + "(\\s+|$)");
}
// classList support for class management
// altho to be fair, the api sucks because it won't accept multiple classes at once
var hasClass, addClass, removeClass;
if ("classList" in document.documentElement) {
hasClass = function(elem, c) {
return elem.classList.contains(c);
};
addClass = function(elem, c) {
elem.classList.add(c);
};
removeClass = function(elem, c) {
elem.classList.remove(c);
};
} else {
hasClass = function(elem, c) {
return classReg(c).test(elem.className);
};
addClass = function(elem, c) {
if (!hasClass(elem, c)) {
elem.className = elem.className + " " + c;
}
};
removeClass = function(elem, c) {
elem.className = elem.className.replace(classReg(c), " ");
};
}
function toggleClass(elem, c) {
var fn = hasClass(elem, c) ? removeClass : addClass;
fn(elem, c);
}
var classie = {
// full names
hasClass: hasClass,
addClass: addClass,
removeClass: removeClass,
toggleClass: toggleClass,
// short names
has: hasClass,
add: addClass,
remove: removeClass,
toggle: toggleClass
};
// transport
if (typeof define === "function" && define.amd) {
// AMD
define(classie);
} else {
// browser global
window.classie = classie;
}
})(window);

File diff suppressed because one or more lines are too long

View File

@@ -1,34 +1,25 @@
/**
* Based on work by
*
* notificationFx.js v1.0.0
* http://www.codrops.com
* https://tympanus.net/codrops/
*
* Licensed under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
* https://opensource.org/licenses/mit-license.php
*
* Copyright 2014, Codrops
* http://www.codrops.com
* https://tympanus.net/codrops/
*/
// jscs:disable
;(function(window) {
"use strict";
var docElem = window.document.documentElement,
support = {animations: Modernizr.cssanimations},
animEndEventNames = {
"WebkitAnimation": "webkitAnimationEnd",
"OAnimation": "oAnimationEnd",
"msAnimation": "MSAnimationEnd",
"animation": "animationend"
},
// animation end event name
animEndEventName = animEndEventNames[ Modernizr.prefixed("animation") ];
(function (window) {
/**
* extend obj function
* 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 (var key in b) {
for (let key in b) {
if (b.hasOwnProperty(key)) {
a[key] = b[key];
}
@@ -37,7 +28,10 @@
}
/**
* NotificationFx function
* NotificationFx constructor
*
* @param {object} options The configuration options
* @class
*/
function NotificationFx(options) {
this.options = extend({}, this.options);
@@ -70,19 +64,22 @@
ttl: 6000,
al_no: "ns-box",
// callbacks
onClose: function() { return false; },
onOpen: function() { return false; }
onClose: function () {
return false;
},
onOpen: function () {
return false;
}
};
/**
* init function
* initialize and cache some vars
* Initialize and cache some vars
*/
NotificationFx.prototype._init = function() {
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;
var strinner = "<div class=\"ns-box-inner\">";
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;
@@ -91,13 +88,12 @@
this.options.wrapper.insertBefore(this.ntf, this.options.wrapper.nextSibling);
// dismiss after [options.ttl]ms
var self = this;
if (this.options.ttl) {
this.dismissttl = setTimeout(function() {
if (self.active) {
self.dismiss();
}
}, this.options.ttl);
this.dismissttl = setTimeout(() => {
if (this.active) {
this.dismiss();
}
}, this.options.ttl);
}
// init events
@@ -105,61 +101,58 @@
};
/**
* init events
* Init events
*/
NotificationFx.prototype._initEvents = function() {
var self = this;
NotificationFx.prototype._initEvents = function () {
// dismiss notification by tapping on it if someone has a touchscreen
this.ntf.querySelector(".ns-box-inner").addEventListener("click", function() { self.dismiss(); });
this.ntf.querySelector(".ns-box-inner").addEventListener("click", () => {
this.dismiss();
});
};
/**
* show the notification
* Show the notification
*/
NotificationFx.prototype.show = function() {
NotificationFx.prototype.show = function () {
this.active = true;
classie.remove(this.ntf, "ns-hide");
classie.add(this.ntf, "ns-show");
this.ntf.classList.remove("ns-hide");
this.ntf.classList.add("ns-show");
this.options.onOpen();
};
/**
* dismiss the notification
* Dismiss the notification
*
* @param {boolean} [close] call the onClose callback at the end
*/
NotificationFx.prototype.dismiss = function() {
var self = this;
NotificationFx.prototype.dismiss = function (close = true) {
this.active = false;
clearTimeout(this.dismissttl);
classie.remove(this.ntf, "ns-show");
setTimeout(function() {
classie.add(self.ntf, "ns-hide");
this.ntf.classList.remove("ns-show");
setTimeout(() => {
this.ntf.classList.add("ns-hide");
// callback
self.options.onClose();
if (close) this.options.onClose();
}, 25);
// after animation ends remove ntf from the DOM
var onEndAnimationFn = function(ev) {
if (support.animations) {
if (ev.target !== self.ntf) return false;
this.removeEventListener(animEndEventName, onEndAnimationFn);
const onEndAnimationFn = (ev) => {
if (ev.target !== this.ntf) {
return false;
}
this.ntf.removeEventListener("animationend", onEndAnimationFn);
if (this.parentNode === self.options.wrapper) {
self.options.wrapper.removeChild(this);
if (ev.target.parentNode === this.options.wrapper) {
this.options.wrapper.removeChild(this.ntf);
}
};
if (support.animations) {
this.ntf.addEventListener(animEndEventName, onEndAnimationFn);
} else {
onEndAnimationFn();
}
this.ntf.addEventListener("animationend", onEndAnimationFn);
};
/**
* add to global namespace
* Add to global namespace
*/
window.NotificationFx = NotificationFx;
})(window);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
.ns-box {
margin-left: auto;
margin-right: auto;
text-align: center;
}

View File

@@ -0,0 +1,4 @@
.ns-box {
margin-right: auto;
text-align: left;
}

View File

@@ -0,0 +1,928 @@
/* 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;
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: var(--color-text-dimmed);
}
.ns-alert {
border-style: solid;
border-color: var(--color-text-bright);
padding: 17px;
line-height: 1.4;
margin-bottom: 10px;
z-index: 3;
color: var(--color-text-bright);
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;
}
.alert-blur {
filter: blur(2px) brightness(50%);
}
[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);
}
}

View File

@@ -0,0 +1,4 @@
.ns-box {
margin-left: auto;
text-align: right;
}

View File

@@ -0,0 +1,18 @@
{% if imageUrl or imageFA %}
{% set imageHeight = imageHeight if imageHeight else "80px" %}
{% if imageUrl %}
<img src="{{ imageUrl }}" height="{{ imageHeight }}" style="margin-bottom: 10px;"/>
{% else %}
<span class="bright fa fa-{{ imageFA }}" style='margin-bottom: 10px; font-size: {{ imageHeight }};'/></span>
{% endif %}
<br/>
{% endif %}
{% if title %}
<span class="thin dimmed medium">{{ title }}</span>
{% endif %}
{% if message %}
{% if title %}
<br/>
{% endif %}
<span class="light bright small">{{ message }}</span>
{% endif %}

View File

@@ -0,0 +1,9 @@
{% if title %}
<span class="thin dimmed medium">{{ title }}</span>
{% endif %}
{% if message %}
{% if title %}
<br/>
{% endif %}
<span class="light bright small">{{ message }}</span>
{% endif %}

View File

@@ -1,4 +1,4 @@
{
"sysTitle": "MagicMirror нотификация",
"welcome": "Добре дошли, стартирането беше успешно"
"sysTitle": "MagicMirror нотификация",
"welcome": "Добре дошли, стартирането беше успешно"
}

View File

@@ -1,4 +1,4 @@
{
"sysTitle": "MagicMirror Notifikation",
"welcome": "Velkommen, modulet er succesfuldt startet!"
"sysTitle": "MagicMirror Notifikation",
"welcome": "Velkommen, modulet er succesfuldt startet!"
}

View File

@@ -1,4 +1,4 @@
{
"sysTitle": "MagicMirror Benachrichtigung",
"welcome": "Willkommen, Start war erfolgreich!"
"sysTitle": "MagicMirror Benachrichtigung",
"welcome": "Willkommen, Start war erfolgreich!"
}

View File

@@ -1,4 +1,4 @@
{
"sysTitle": "MagicMirror Notification",
"welcome": "Welcome, start was successful!"
"sysTitle": "MagicMirror Notification",
"welcome": "Welcome, start was successful!"
}

View File

@@ -1,4 +1,4 @@
{
"sysTitle": "MagicMirror Notificaciones",
"welcome": "Bienvenido, ¡se iniciado correctamente!"
"sysTitle": "MagicMirror Notificaciones",
"welcome": "Bienvenido, ¡se iniciado correctamente!"
}

View File

@@ -1,4 +1,4 @@
{
"sysTitle": "MagicMirror Notification",
"welcome": "Bienvenue, le démarrage a été un succès!"
"sysTitle": "MagicMirror Notification",
"welcome": "Bienvenue, le démarrage a été un succès!"
}

View File

@@ -1,4 +1,4 @@
{
"sysTitle": "MagicMirror értesítés",
"welcome": "Üdvözöljük, indulás sikeres!"
"sysTitle": "MagicMirror értesítés",
"welcome": "Üdvözöljük, indulás sikeres!"
}

View File

@@ -1,4 +1,4 @@
{
"sysTitle": "MagicMirror Notificatie",
"welcome": "Welkom, Succesvol gestart!"
"sysTitle": "MagicMirror Notificatie",
"welcome": "Welkom, Succesvol gestart!"
}

View File

@@ -1,4 +1,4 @@
{
"sysTitle": "MagicMirror Уведомление",
"welcome": "Добро пожаловать, старт был успешным!"
"sysTitle": "MagicMirror Уведомление",
"welcome": "Добро пожаловать, старт был успешным!"
}

View File

@@ -1,108 +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.
## Using the module
To use this module, add it to the modules array in the `config/config.js` file:
````javascript
modules: [
{
module: "calendar",
position: "top_left", // This can be any of the regions. Best results in left or right regions.
config: {
// The config property is optional.
// If no config is set, an example calendar is shown.
// See 'Configuration options' for more information.
}
}
]
````
## Configuration options
The following properties can be configured:
| Option | Description
| ---------------------------- | -----------
| `maximumEntries` | The maximum number of events shown. / **Possible values:** `0` - `100` <br> **Default value:** `10`
| `maximumNumberOfDays` | The maximum number of days in the future. <br><br> **Default value:** `365`
| `displaySymbol` | Display a symbol in front of an entry. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
| `defaultSymbol` | The default symbol. <br><br> **Possible values:** See [Font Awsome](http://fontawesome.io/icons/) website. <br> **Default value:** `calendar`
| `showLocation` | Whether to show event locations. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
| `maxTitleLength` | The maximum title length. <br><br> **Possible values:** `10` - `50` <br> **Default value:** `25`
| `wrapEvents` | Wrap event titles to multiple lines. Breaks lines at the length defined by `maxTitleLength`. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
| `maxTitleLines` | The maximum number of lines a title will wrap vertically before being cut (Only enabled if `wrapEvents` is also enabled). <br><br> **Possible values:** `0` - `10` <br> **Default value:** `3`
| `fetchInterval` | How often does the content needs to be fetched? (Milliseconds) <br><br> **Possible values:** `1000` - `86400000` <br> **Default value:** `300000` (5 minutes)
| `animationSpeed` | Speed of the update animation. (Milliseconds) <br><br> **Possible values:** `0` - `5000` <br> **Default value:** `2000` (2 seconds)
| `fade` | Fade the future events to black. (Gradient) <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
| `fadePoint` | Where to start fade? <br><br> **Possible values:** `0` (top of the list) - `1` (bottom of list) <br> **Default value:** `0.25`
| `tableClass` | Name of the classes issued from `main.css`. <br><br> **Possible values:** xsmall, small, medium, large, xlarge. <br> **Default value:** _small._
| `calendars` | The list of calendars. <br><br> **Possible values:** An array, see _calendar configuration_ below. <br> **Default value:** _An example calendar._
| `titleReplace` | An object of textual replacements applied to the tile of the event. This allow to remove or replace certains words in the title. <br><br> **Example:** `{'Birthday of ' : '', 'foo':'bar'}` <br> **Default value:** `{ "De verjaardag van ": "", "'s birthday": "" }`
| `displayRepeatingCountTitle` | Show count title for yearly repeating events (e.g. "X. Birthday", "X. Anniversary") <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
| `dateFormat` | Format to use for the date of events (when using absolute dates) <br><br> **Possible values:** See [Moment.js formats](http://momentjs.com/docs/#/parsing/string-format/) <br> **Default value:** `MMM Do` (e.g. Jan 18th)
| `dateEndFormat` | Format to use for the end time of events <br><br> **Possible values:** See [Moment.js formats](http://momentjs.com/docs/#/parsing/string-format/) <br> **Default value:** `HH:mm` (e.g. 16:30)
| `showEnd` | Show end time of events <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
| `fullDayEventDateFormat` | Format to use for the date of full day events (when using absolute dates) <br><br> **Possible values:** See [Moment.js formats](http://momentjs.com/docs/#/parsing/string-format/) <br> **Default value:** `MMM Do` (e.g. Jan 18th)
| `timeFormat` | Display event times as absolute dates, or relative time, or using absolute date headers with times for each event next to it <br><br> **Possible values:** `absolute` or `relative` or `dateheaders` <br> **Default value:** `relative`
| `showEnd` | Display the end of a date as well <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
| `getRelative` | How much time (in hours) should be left until calendar events start getting relative? <br><br> **Possible values:** `0` (events stay absolute) - `48` (48 hours before the event starts) <br> **Default value:** `6`
| `urgency` | When using a timeFormat of `absolute`, the `urgency` setting allows you to display events within a specific time frame as `relative`. This allows events within a certain time frame to be displayed as relative (in xx days) while others are displayed as absolute dates <br><br> **Possible values:** a positive integer representing the number of days for which you want a relative date, for example `7` (for 7 days) <br><br> **Default value:** `7`
| `broadcastEvents` | If this property is set to true, the calendar will broadcast all the events to all other modules with the notification message: `CALENDAR_EVENTS`. The event objects are stored in an array and contain the following fields: `title`, `startDate`, `endDate`, `fullDayEvent`, `location` and `geo`. <br><br> **Possible values:** `true`, `false` <br><br> **Default value:** `true`
| `hidePrivate` | Hides private calendar events. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
| `hideOngoing` | Hides calendar events that have already started. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
| `excludedEvents` | An array of words / phrases from event titles that will be excluded from being shown. <br><br>Additionally advanced filter objects can be passed in. Below is the configuration for the advance filtering object.<br>**Required**<br>`filterBy` - string used to determine if filter is applied.<br>**Optional**<br>`until` - Time before an event to display it Ex: [`'3 days'`, `'2 months'`, `'1 week'`]<br>`caseSensitive` - By default, excludedEvents are case insensitive, set this to true to enforce case sensitivity<br>`regex` - set to `true` if filterBy is a regex. For those not familiar with regex it is used for pattern matching, please see [here](https://regexr.com/) for more info.<br><br> **Example:** `['Birthday', 'Hide This Event', {filterBy: 'Payment', until: '6 days', caseSensitive: true}, {filterBy: '^[0-9]{1,}.*', regex: true}]` <br> **Default value:** `[]`
| `broadcastPastEvents` | If this is set to true, events from the past `maximumNumberOfDays` will be included in event broadcasts <br> **Default value:** `false`
| `sliceMultiDayEvents` | If this is set to true, events exceeding at least one midnight will be sliced into separate events including a counter like (1/2). This is especially helpful in "dateheaders" mode. Events will be sliced at midnight, end time for all events but the last will be 23:59 <br> **Default value:** `true`
| `nextDaysRelative ` | If this is set to true, the appointments of today and tomorrow are displayed relatively, even if the timeformat is set to absolute. <br> **Default value:** `false`
### Calendar configuration
The `calendars` property contains an array of the configured calendars.
The `colored` property gives the option for an individual color for each calendar.
The `coloredSymbolOnly` property will apply color to the symbol only, not the whole line. This is only applicable when `colored` is also enabled.
#### Default value:
````javascript
config: {
colored: false,
coloredSymbolOnly: false,
calendars: [
{
url: 'http://www.calendarlabs.com/templates/ical/US-Holidays.ics',
symbol: 'calendar',
auth: {
user: 'username',
pass: 'superstrongpassword',
method: 'basic'
}
},
],
}
````
#### Calendar configuration options:
| Option | Description
| --------------------- | -----------
| `url` | The url of the calendar .ical. This property is required. <br><br> **Possible values:** Any public accessble .ical calendar.
| `symbol` | The symbol to show in front of an event. This property is optional. <br><br> **Possible values:** See [Font Awesome](http://fontawesome.io/icons/) website. To have multiple symbols you can define them in an array e.g. `["calendar", "plane"]`
| `color` | The font color of an event from this calendar. This property should be set if the config is set to colored: true. <br><br> **Possible values:** HEX, RGB or RGBA values (#efefef, rgb(242,242,242), rgba(242,242,242,0.5)).
| `repeatingCountTitle` | The count title for yearly repating events in this calendar. <br><br> **Example:** `'Birthday'`
| `maximumEntries` | The maximum number of events shown. Overrides global setting. **Possible values:** `0` - `100`
| `maximumNumberOfDays` | The maximum number of days in the future. Overrides global setting
| `name` | The name of the calendar. Included in event broadcasts as `calendarName`.
| `auth` | The object containing options for authentication against the calendar.
| `symbolClass` | Add a class to the cell of symbol.
| `titleClass` | Add a class to the title's cell.
| `timeClass` | Add a class to the time's cell.
| `broadcastPastEvents` | Whether to include past events from this calendar. Overrides global setting
#### Calendar authentication options:
| Option | Description
| --------------------- | -----------
| `user` | The username for HTTP authentication.
| `pass` | The password for HTTP authentication. (If you use Bearer authentication, this should be your BearerToken.)
| `method` | Which authentication method should be used. HTTP Basic, Digest and Bearer authentication methods are supported. Basic authentication is used by default if this option is omitted. **Possible values:** `digest`, `basic`, `bearer` **Default value:** `basic`
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/calendar.html).

View File

@@ -1,15 +1,14 @@
.calendar .symbol {
display: flex;
flex-direction: row;
justify-content: flex-end;
padding-left: 0;
padding-right: 10px;
font-size: 80%;
vertical-align: top;
font-size: var(--font-size-small);
}
.calendar .symbol span {
display: inline-block;
-ms-transform: translate(0, 2px); /* IE 9 */
-webkit-transform: translate(0, 2px); /* Safari */
transform: translate(0, 2px);
padding-top: 4px;
}
.calendar .title {

File diff suppressed because it is too large Load Diff

View File

@@ -1,486 +1,156 @@
/* Magic Mirror
* Node Helper: Calendar - CalendarFetcher
*
* By Michael Teeuw http://michaelteeuw.nl
* By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed.
*/
const CalendarUtils = require("./calendarutils");
const Log = require("logger");
const NodeHelper = require("node_helper");
const ical = require("node-ical");
const fetch = require("node-fetch");
const digest = require("digest-fetch");
const https = require("https");
var ical = require("./vendor/ical.js");
var 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
* @param {boolean} selfSignedCert If true, the server certificate is not verified against the list of supplied CAs.
* @class
*/
const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, includePastEvents, selfSignedCert) {
let reloadTimer = null;
let events = [];
var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, includePastEvents) {
var self = this;
let fetchFailedCallback = function () {};
let eventsReceivedCallback = function () {};
var reloadTimer = null;
var events = [];
var fetchFailedCallback = function() {};
var eventsReceivedCallback = function() {};
/* fetchCalendar()
/**
* Initiates calendar fetch.
*/
var fetchCalendar = function() {
const fetchCalendar = () => {
clearTimeout(reloadTimer);
reloadTimer = null;
nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
var opts = {
headers: {
"User-Agent": "Mozilla/5.0 (Node.js "+ nodeVersion + ") MagicMirror/" + global.version + " (https://github.com/MichMich/MagicMirror/)"
},
gzip: true
const nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
let fetcher = null;
let httpsAgent = null;
let headers = {
"User-Agent": "Mozilla/5.0 (Node.js " + nodeVersion + ") MagicMirror/" + global.version
};
if (selfSignedCert) {
httpsAgent = new https.Agent({
rejectUnauthorized: false
});
}
if (auth) {
if(auth.method === "bearer"){
opts.auth = {
bearer: auth.pass
};
if (auth.method === "bearer") {
headers.Authorization = "Bearer " + auth.pass;
} else if (auth.method === "digest") {
fetcher = new digest(auth.user, auth.pass).fetch(url, { headers: headers, agent: httpsAgent });
} else {
opts.auth = {
user: auth.user,
pass: auth.pass
};
if(auth.method === "digest"){
opts.auth.sendImmediately = false;
} else {
opts.auth.sendImmediately = true;
}
headers.Authorization = "Basic " + Buffer.from(auth.user + ":" + auth.pass).toString("base64");
}
}
if (fetcher === null) {
fetcher = fetch(url, { headers: headers, agent: httpsAgent });
}
ical.fromURL(url, opts, function(err, data) {
if (err) {
fetchFailedCallback(self, err);
fetcher
.then(NodeHelper.checkFetchStatus)
.then((response) => response.text())
.then((responseData) => {
let data = [];
try {
data = ical.parseICS(responseData);
Log.debug("parsed data=" + JSON.stringify(data));
events = CalendarUtils.filterEvents(data, {
excludedEvents,
includePastEvents,
maximumEntries,
maximumNumberOfDays
});
} catch (error) {
fetchFailedCallback(this, error);
scheduleTimer();
return;
}
this.broadcastEvents();
scheduleTimer();
})
.catch((error) => {
fetchFailedCallback(this, error);
scheduleTimer();
return;
}
// console.log(data);
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
var limitFunction = function(date, i) {return true;};
var eventDate = function(event, time) {
return (event[time].length === 8) ? moment(event[time], "YYYYMMDD") : moment(new Date(event[time]));
};
for (var e in data) {
var event = data[e];
var now = new Date();
var today = moment().startOf("day").toDate();
var 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.
var past = today;
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.
var isFacebookBirthday = false;
if (typeof event.uid !== "undefined") {
if (event.uid.indexOf("@facebook.com") !== -1) {
isFacebookBirthday = true;
}
}
if (event.type === "VEVENT") {
var startDate = eventDate(event, "start");
var endDate;
if (typeof event.end !== "undefined") {
endDate = eventDate(event, "end");
} else if(typeof event.duration !== "undefined") {
dur=moment.duration(event.duration);
endDate = startDate.clone().add(dur);
} else {
if (!isFacebookBirthday) {
endDate = startDate;
} else {
endDate = moment(startDate).add(1, "days");
}
}
// calculate the duration f the event for use with recurring events.
var duration = parseInt(endDate.format("x")) - parseInt(startDate.format("x"));
if (event.start.length === 8) {
startDate = startDate.startOf("day");
}
var title = getTitleFromEvent(event);
var excluded = false,
dateFilter = null;
for (var f in excludedEvents) {
var 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) {
continue;
}
var location = event.location || false;
var geo = event.geo || false;
var description = event.description || false;
if (typeof event.rrule !== "undefined" && event.rrule !== null && !isFacebookBirthday) {
var rule = event.rrule;
var addedEvents = 0;
// 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.
var dates = rule.between(past, future, true, limitFunction);
// 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)
{
var pastMoment = moment(past);
var futureMoment = moment(future);
for (var 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 (var d in dates) {
var 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 )
var dateKey = date.toISOString().substring(0,10);
var curEvent = event;
var showRecurrence = true;
// Stop parsing this event's recurrences if we've already found maximumEntries worth of recurrences.
// (The logic below would still filter the extras, but the check is simple since we're already tracking the count)
if (addedEvents >= maximumEntries) {
break;
}
startDate = moment(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;
}
endDate = moment(parseInt(startDate.format("x")) + duration, "x");
if (startDate.format("x") == endDate.format("x")) {
endDate = endDate.endOf("day");
}
var 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) && (addedEvents < maximumEntries)) {
addedEvents++;
newEvents.push({
title: recurrenceTitle,
startDate: startDate.format("x"),
endDate: endDate.format("x"),
fullDayEvent: isFullDayEvent(event),
class: event.class,
firstYear: event.start.getFullYear(),
location: location,
geo: geo,
description: description
});
}
}
// end recurring event parsing
} else {
// console.log("Single event ...");
// Single event.
var fullDayEvent = (isFacebookBirthday) ? true : isFullDayEvent(event);
if (includePastEvents) {
if (endDate < past) {
//console.log("Past event is too far in the past. So skip: " + title);
continue;
}
} else {
if (!fullDayEvent && endDate < new Date()) {
//console.log("It's not a fullday event, and it is in the past. So skip: " + title);
continue;
}
if (fullDayEvent && endDate <= today) {
//console.log("It's a fullday event, and it is before today. So skip: " + title);
continue;
}
}
if (startDate > future) {
//console.log("It exceeds the maximumNumberOfDays limit. So skip: " + title);
continue;
}
if (timeFilterApplies(now, endDate, dateFilter)) {
continue;
}
// 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);
}
// Every thing is good. Add it to the list.
newEvents.push({
title: title,
startDate: startDate.format("x"),
endDate: endDate.format("x"),
fullDayEvent: fullDayEvent,
class: event.class,
location: location,
geo: geo,
description: description
});
}
}
}
newEvents.sort(function(a, b) {
return a.startDate - b.startDate;
});
//console.log(newEvents);
events = newEvents.slice(0, maximumEntries);
self.broadcastEvents();
scheduleTimer();
});
};
/* scheduleTimer()
/**
* Schedule the timer for the next update.
*/
var scheduleTimer = function() {
//console.log('Schedule update timer.');
const scheduleTimer = function () {
clearTimeout(reloadTimer);
reloadTimer = setTimeout(function() {
reloadTimer = setTimeout(function () {
fetchCalendar();
}, reloadInterval);
};
/* isFullDayEvent(event)
* Checks if an event is a fullday event.
*
* argument event object - The event object to check.
*
* return bool - The event is a fullday event.
*/
var isFullDayEvent = function(event) {
if (event.start.length === 8 || event.start.dateOnly) {
return true;
}
var start = event.start || 0;
var startDate = new Date(start);
var 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;
};
/* timeFilterApplies()
* Determines if the user defined time filter should apply
*
* argument now Date - Date object using previously created object for consistency
* argument endDate Moment - Moment object representing the event end date
* argument filter string - The time to subtract from the end date to determine if an event should be shown
*
* return bool - The event should be filtered out
*/
var timeFilterApplies = function(now, endDate, filter) {
if (filter) {
var 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;
};
/* getTitleFromEvent(event)
* Gets the title from the event.
*
* argument event object - The event object to check.
*
* return string - The title of the event, or "Event" if no title is found.
*/
var getTitleFromEvent = function (event) {
var 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;
};
var 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 */
/* startFetch()
/**
* Initiate fetchCalendar();
*/
this.startFetch = function() {
this.startFetch = function () {
fetchCalendar();
};
/* broadcastItems()
/**
* Broadcast the existing events.
*/
this.broadcastEvents = function() {
//console.log('Broadcasting ' + events.length + ' events.');
eventsReceivedCallback(self);
this.broadcastEvents = function () {
Log.info("Calendar-Fetcher: Broadcasting " + events.length + " events.");
eventsReceivedCallback(this);
};
/* onReceive(callback)
/**
* Sets the on success callback
*
* argument callback function - The on success callback.
* @param {Function} callback The on success callback.
*/
this.onReceive = function(callback) {
this.onReceive = function (callback) {
eventsReceivedCallback = callback;
};
/* onError(callback)
/**
* Sets the on error callback
*
* argument callback function - The on error callback.
* @param {Function} callback The on error callback.
*/
this.onError = function(callback) {
this.onError = function (callback) {
fetchFailedCallback = callback;
};
/* url()
/**
* Returns the url of this fetcher.
*
* return string - The url of this fetcher.
* @returns {string} The url of this fetcher.
*/
this.url = function() {
this.url = function () {
return url;
};
/* events()
/**
* Returns current available events for this fetcher.
*
* return array - The current available events for this fetcher.
* @returns {object[]} The current available events for this fetcher.
*/
this.events = function() {
this.events = function () {
return events;
};
};

View File

@@ -0,0 +1,621 @@
/* Magic Mirror
* Calendar Util Methods
*
* By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed.
*/
/**
* @external Moment
*/
const moment = require("moment");
const path = require("path");
const zoneTable = require(path.join(__dirname, "windowsZones.json"));
const Log = require("../../../js/logger.js");
const CalendarUtils = {
/**
* Calculate the time correction, either dst/std or full day in cases where
* utc time is day before plus offset
*
* @param {object} event the event which needs adjustement
* @param {Date} date the date on which this event happens
* @returns {number} the necessary adjustment in hours
*/
calculateTimezoneAdjustment: 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 = CalendarUtils.getIanaTZFromMS(event.start.tz);
Log.debug("corrected TZ=" + tz);
// watch out for unregistered windows timezone names
// if we had a successful 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
let es = moment(event.start);
// check for start date prior to start of daylight changing date
if (es.format("YYYY") < 2007) {
es.set("year", 2013); // if so, use a closer date
}
Log.debug("start date/time=" + es.toDate());
start_offset = moment.tz(es, 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;
},
/**
* Filter the events from ical according to the given config
*
* @param {object} data the calendar data from ical
* @param {object} config The configuration object
* @returns {string[]} the filtered events
*/
filterEvents: function (data, config) {
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 CalendarUtils.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]) => {
Log.debug("Processing entry...");
const now = new Date();
const today = moment().startOf("day").toDate();
const future = moment().startOf("day").add(config.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;
if (config.includePastEvents) {
past = moment().startOf("day").subtract(config.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") {
Log.debug("Event:\n" + JSON.stringify(event));
let startDate = eventDate(event, "start");
let endDate;
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());
Log.debug("end:: " + endDate.toDate());
// Calculate the duration of the event for use with recurring events.
let duration = parseInt(endDate.format("x")) - parseInt(startDate.format("x"));
Log.debug("duration: " + duration);
// FIXME: Since the parsed json object from node-ical comes with time information
// this check could be removed (?)
if (event.start.length === 8) {
startDate = startDate.startOf("day");
}
const title = CalendarUtils.getTitleFromEvent(event);
Log.debug("title: " + title);
let excluded = false,
dateFilter = null;
for (let f in config.excludedEvents) {
let filter = config.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 (CalendarUtils.titleFilterApplies(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 (CalendarUtils.isFullDayEvent(event)) {
Log.debug("fullday");
// if full day event, only use the date part of the ranges
pastLocal = pastMoment.toDate();
futureLocal = futureMoment.toDate();
Log.debug("pastLocal: " + pastLocal);
Log.debug("futureLocal: " + futureLocal);
} else {
// if we want past events
if (config.includePastEvents) {
// use the calculated past time for the between from
pastLocal = pastMoment.toDate();
} else {
// otherwise use NOW.. cause we shouldn't use any before now
pastLocal = moment().toDate(); //now
}
futureLocal = futureMoment.toDate(); // future
}
Log.debug("Search for recurring events between: " + pastLocal + " and " + futureLocal);
const dates = rule.between(pastLocal, futureLocal, true, limitFunction);
Log.debug("Title: " + event.summary + ", with 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.
Log.debug("event.recurrences: " + event.recurrences);
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];
// Remove the time information of each date by using its substring, using the following method:
// .toISOString().substring(0,10).
// since the date is given as ISOString with YYYY-MM-DDTHH:MM:SS.SSSZ
// (see https://momentjs.com/docs/#/displaying/as-iso-string/).
const dateKey = date.toISOString().substring(0, 10);
let curEvent = event;
let showRecurrence = true;
// Get the offset of today where we are processing
// This will be the correction, we need to apply.
let nowOffset = new Date().getTimezoneOffset();
// For full day events, the time might be off from RRULE/Luxon problem
// Get time zone offset of the rule calculated event
let dateoffset = date.getTimezoneOffset();
// Reduce the time by the following offset.
Log.debug(" recurring date is " + date + " offset is " + dateoffset);
let dh = moment(date).format("HH");
Log.debug(" recurring date is " + date + " offset is " + dateoffset / 60 + " Hour is " + dh);
if (CalendarUtils.isFullDayEvent(event)) {
Log.debug("Fullday");
// If the offset is negative (east of GMT), where the problem is
if (dateoffset < 0) {
if (dh < Math.abs(dateoffset / 60)) {
// reduce the time by the offset
// Apply the correction to the date/time to get it UTC relative
date = new Date(date.getTime() - Math.abs(24 * 60) * 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 date1 fulldate is " + date);
}
} else {
// if the timezones are the same, correct date if needed
//if (event.start.tz === moment.tz.guess()) {
// if the date hour is less than the offset
if (24 - dh <= Math.abs(dateoffset / 60)) {
// apply the correction to the date/time back to right day
date = new Date(date.getTime() + Math.abs(24 * 60) * 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 date2 fulldate is " + date);
}
//}
}
} else {
// not full day, but luxon can still screw up the date on the rule processing
// we need to correct the date to get back to the right event for
if (dateoffset < 0) {
// if the date hour is less than the offset
if (dh <= Math.abs(dateoffset / 60)) {
// Reduce the time by the offset:
// Apply the correction to the date/time to get it UTC relative
date = new Date(date.getTime() - Math.abs(24 * 60) * 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 date1 is " + date);
}
} else {
// if the timezones are the same, correct date if needed
//if (event.start.tz === moment.tz.guess()) {
// if the date hour is less than the offset
if (24 - dh <= Math.abs(dateoffset / 60)) {
// apply the correction to the date/time back to right day
date = new Date(date.getTime() + Math.abs(24 * 60) * 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 date2 is " + date);
}
//}
}
}
startDate = moment(date);
Log.debug("Corrected startDate: " + startDate.toDate());
let adjustDays = CalendarUtils.calculateTimezoneAdjustment(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 = CalendarUtils.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 (CalendarUtils.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: CalendarUtils.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 : CalendarUtils.isFullDayEvent(event);
// Log.debug("full day event")
if (config.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 (CalendarUtils.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 = CalendarUtils.calculateTimezoneAdjustment(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();
let entries = 0;
let events = [];
for (let ne of newEvents) {
if (moment(ne.endDate, "x").isBefore(now)) {
if (config.includePastEvents) events.push(ne);
continue;
}
entries++;
// If max events has been saved, skip the rest
if (entries > config.maximumEntries) break;
events.push(ne);
}
return events;
},
/**
* Lookup iana tz from windows
*
* @param {string} msTZName the timezone name to lookup
* @returns {string|null} the iana name or null of none is found
*/
getIanaTZFromMS: function (msTZName) {
// Get hash entry
const he = zoneTable[msTZName];
// If found return iana name, else null
return he ? he.iana[0] : null;
},
/**
* 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.
*/
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;
},
/**
* 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
*/
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
*/
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;
},
/**
* Determines if the user defined title filter should apply
*
* @param {string} title the title of the event
* @param {string} filter the string to look for, can be a regex also
* @param {boolean} useRegex true if a regex should be used, otherwise it just looks for the filter as a string
* @param {string} regexFlags flags that should be applied to the regex
* @returns {boolean} True if the title should be filtered out, false otherwise
*/
titleFilterApplies: 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);
}
}
};
if (typeof module !== "undefined") {
module.exports = CalendarUtils;
}

View File

@@ -2,38 +2,40 @@
* 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 http://michaelteeuw.nl
* By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed.
*/
// Alias modules mentioned in package.js under _moduleAliases.
require("module-alias/register");
var CalendarFetcher = require("./calendarfetcher.js");
const CalendarFetcher = require("./calendarfetcher.js");
var url = "https://calendar.google.com/calendar/ical/pkm1t2uedjbp0uvq1o7oj1jouo%40group.calendar.google.com/private-08ba559f89eec70dd74bbd887d0a3598/basic.ics"; // Standard test URL
// var url = "https://www.googleapis.com/calendar/v3/calendars/primary/events/"; // URL for Bearer auth (must be configured in Google OAuth2 first)
var fetchInterval = 60 * 60 * 1000;
var maximumEntries = 10;
var maximumNumberOfDays = 365;
var user = "magicmirror";
var pass = "MyStrongPass";
var broadcastPastEvents = false;
var auth = {
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 ...");
fetcher = new CalendarFetcher(url, fetchInterval, [], maximumEntries, maximumNumberOfDays, auth);
const fetcher = new CalendarFetcher(url, fetchInterval, [], maximumEntries, maximumNumberOfDays, auth);
fetcher.onReceive(function(fetcher) {
fetcher.onReceive(function (fetcher) {
console.log(fetcher.events());
console.log("------------------------------------------------------------");
process.exit(0);
});
fetcher.onError(function(fetcher, error) {
fetcher.onError(function (fetcher, error) {
console.log("Fetcher error:");
console.log(error);
process.exit(1);
});
fetcher.startFetch();

View File

@@ -1,79 +1,88 @@
/* Magic Mirror
* Node Helper: Calendar
*
* By Michael Teeuw http://michaelteeuw.nl
* By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed.
*/
var NodeHelper = require("node_helper");
var validUrl = require("valid-url");
var CalendarFetcher = require("./calendarfetcher.js");
const NodeHelper = require("node_helper");
const CalendarFetcher = require("./calendarfetcher.js");
const Log = require("logger");
module.exports = NodeHelper.create({
// Override start method.
start: function() {
var events = [];
start: function () {
Log.log("Starting node helper for: " + this.name);
this.fetchers = [];
console.log("Starting node helper for: " + this.name);
},
// Override socketNotificationReceived method.
socketNotificationReceived: function(notification, payload) {
socketNotificationReceived: function (notification, payload) {
if (notification === "ADD_CALENDAR") {
//console.log('ADD_CALENDAR: ');
this.createFetcher(payload.url, payload.fetchInterval, payload.excludedEvents, payload.maximumEntries, payload.maximumNumberOfDays, payload.auth, payload.broadcastPastEvents);
this.createFetcher(payload.url, payload.fetchInterval, payload.excludedEvents, payload.maximumEntries, payload.maximumNumberOfDays, payload.auth, payload.broadcastPastEvents, payload.selfSignedCert, payload.id);
}
},
/* createFetcher(url, reloadInterval)
/**
* Creates a fetcher for a new url if it doesn't exist yet.
* Otherwise it reuses the existing one.
*
* attribute url string - URL of the news feed.
* attribute reloadInterval number - Reload interval in milliseconds.
* @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 {boolean} selfSignedCert If true, the server certificate is not verified against the list of supplied CAs.
* @param {string} identifier ID of the module
*/
createFetcher: function(url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents) {
var self = this;
if (!validUrl.isUri(url)) {
self.sendSocketNotification("INCORRECT_URL", {url: url});
createFetcher: function (url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents, selfSignedCert, identifier) {
try {
new URL(url);
} catch (error) {
Log.error("Calendar Error. Malformed calendar url: ", url, error);
this.sendSocketNotification("CALENDAR_ERROR", { error_type: "MODULE_ERROR_MALFORMED_URL" });
return;
}
var fetcher;
if (typeof self.fetchers[url] === "undefined") {
console.log("Create new calendar fetcher for url: " + url + " - Interval: " + fetchInterval);
fetcher = new CalendarFetcher(url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents);
let fetcher;
if (typeof this.fetchers[identifier + url] === "undefined") {
Log.log("Create new calendarfetcher for url: " + url + " - Interval: " + fetchInterval);
fetcher = new CalendarFetcher(url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents, selfSignedCert);
fetcher.onReceive(function(fetcher) {
//console.log('Broadcast events.');
//console.log(fetcher.events());
fetcher.onReceive((fetcher) => {
this.broadcastEvents(fetcher, identifier);
});
self.sendSocketNotification("CALENDAR_EVENTS", {
url: fetcher.url(),
events: fetcher.events()
fetcher.onError((fetcher, error) => {
Log.error("Calendar Error. Could not fetch calendar: ", fetcher.url(), error);
let error_type = NodeHelper.checkFetchError(error);
this.sendSocketNotification("CALENDAR_ERROR", {
id: identifier,
error_type
});
});
fetcher.onError(function(fetcher, error) {
console.error("Calendar Error. Could not fetch calendar: ", fetcher.url(), error);
self.sendSocketNotification("FETCH_ERROR", {
url: fetcher.url(),
error: error
});
});
self.fetchers[url] = fetcher;
this.fetchers[identifier + url] = fetcher;
} else {
//console.log('Use existing news fetcher for url: ' + url);
fetcher = self.fetchers[url];
Log.log("Use existing calendarfetcher for url: " + url);
fetcher = this.fetchers[identifier + url];
fetcher.broadcastEvents();
}
fetcher.startFetch();
},
/**
*
* @param {object} fetcher the fetcher associated with the calendar
* @param {string} identifier the identifier of the calendar
*/
broadcastEvents: function (fetcher, identifier) {
this.sendSocketNotification("CALENDAR_EVENTS", {
id: identifier,
url: fetcher.url(),
events: fetcher.events()
});
}
});

View File

@@ -1,4 +0,0 @@
language: node_js
node_js:
- "8.9"
install: npm install

View File

@@ -1,178 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@@ -1,13 +0,0 @@
Copyright 2012 Peter Braden
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,16 +0,0 @@
'use strict';
const ical = require('ical');
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
ical.fromURL('http://lanyrd.com/topics/nodejs/nodejs.ics', {}, function (err, data) {
for (let k in data) {
if (data.hasOwnProperty(k)) {
var ev = data[k];
if (data[k].type == 'VEVENT') {
console.log(`${ev.summary} is in ${ev.location} on the ${ev.start.getDate()} of ${months[ev.start.getMonth()]} at ${ev.start.toLocaleTimeString('en-GB')}`);
}
}
}
});

View File

@@ -1,118 +0,0 @@
var ical = require('./node-ical')
var moment = require('moment')
var data = ical.parseFile('./examples/example_rrule.ics');
// Complicated example demonstrating how to handle recurrence rules and exceptions.
for (var k in data) {
// When dealing with calendar recurrences, you need a range of dates to query against,
// because otherwise you can get an infinite number of calendar events.
var rangeStart = moment("2017-01-01");
var rangeEnd = moment("2017-12-31");
var event = data[k]
if (event.type === 'VEVENT') {
var title = event.summary;
var startDate = moment(event.start);
var endDate = moment(event.end);
// Calculate the duration of the event for use with recurring events.
var duration = parseInt(endDate.format("x")) - parseInt(startDate.format("x"));
// Simple case - no recurrences, just print out the calendar event.
if (typeof event.rrule === 'undefined')
{
console.log('title:' + title);
console.log('startDate:' + startDate.format('MMMM Do YYYY, h:mm:ss a'));
console.log('endDate:' + endDate.format('MMMM Do YYYY, h:mm:ss a'));
console.log('duration:' + moment.duration(duration).humanize());
console.log();
}
// Complicated case - if an RRULE exists, handle multiple recurrences of the event.
else if (typeof event.rrule !== 'undefined')
{
// For recurring events, get the set of event start dates that fall within the range
// of dates we're looking for.
var dates = event.rrule.between(
rangeStart.toDate(),
rangeEnd.toDate(),
true,
function(date, i) {return true;}
)
// 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. One way to handle this is
// to add *all* recurrence override entries into the set of dates that we check, and then later
// filter out any recurrences that don't actually belong within our range.
if (event.recurrences != undefined)
{
for (var 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(rangeStart, rangeEnd) != true)
{
dates.push(new Date(r));
}
}
}
// Loop through the set of date entries to see which recurrences should be printed.
for(var i in dates) {
var date = dates[i];
var curEvent = event;
var showRecurrence = true;
var curDuration = duration;
startDate = moment(date);
// Use just the date of the recurrence to look up overrides and exceptions (i.e. chop off time information)
var dateLookupKey = date.toISOString().substring(0, 10);
// 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[dateLookupKey] != undefined))
{
// We found an override, so for this recurrence, use a potentially different title, start date, and duration.
curEvent = curEvent.recurrences[dateLookupKey];
startDate = moment(curEvent.start);
curDuration = 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[dateLookupKey] != undefined))
{
// This date is an exception date, which means we should skip it in the recurrence pattern.
showRecurrence = false;
}
// Set the the title and the end date from either the regular event or the recurrence override.
var recurrenceTitle = curEvent.summary;
endDate = moment(parseInt(startDate.format("x")) + curDuration, 'x');
// If this recurrence ends before the start of the date range, or starts after the end of the date range,
// don't process it.
if (endDate.isBefore(rangeStart) || startDate.isAfter(rangeEnd)) {
showRecurrence = false;
}
if (showRecurrence === true) {
console.log('title:' + recurrenceTitle);
console.log('startDate:' + startDate.format('MMMM Do YYYY, h:mm:ss a'));
console.log('endDate:' + endDate.format('MMMM Do YYYY, h:mm:ss a'));
console.log('duration:' + moment.duration(curDuration).humanize());
console.log();
}
}
}
}
}

View File

@@ -1,40 +0,0 @@
BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:ical
X-WR-TIMEZONE:US/Central
X-WR-CALDESC:
BEGIN:VEVENT
UID:98765432-ABCD-DCBB-999A-987765432123
DTSTART;TZID=US/Central:20170601T090000
DTEND;TZID=US/Central:20170601T170000
DTSTAMP:20170727T044436Z
EXDATE;TZID=US/Central:20170706T090000,20170713T090000,20170720T090000,20
170803T090000
LAST-MODIFIED:20170727T044435Z
RRULE:FREQ=WEEKLY;WKST=SU;UNTIL=20170814T045959Z;BYDAY=TH
SEQUENCE:0
SUMMARY:Recurring weekly meeting from June 1 - Aug 14 (except July 6, July 13, July 20, Aug 3)
END:VEVENT
BEGIN:VEVENT
UID:98765432-ABCD-DCBB-999A-987765432123
RECURRENCE-ID;TZID=US/Central:20170629T090000
DTSTART;TZID=US/Central:20170703T090000
DTEND;TZID=US/Central:20170703T120000
DTSTAMP:20170727T044436Z
LAST-MODIFIED:20170216T143445Z
SEQUENCE:0
SUMMARY:Last meeting in June moved to Monday July 3 and shortened to half day
END:VEVENT
BEGIN:VEVENT
UID:12354454-ABCD-DCBB-999A-2349872354897
DTSTART;TZID=US/Central:20171201T130000
DTEND;TZID=US/Central:20171201T150000
DTSTAMP:20170727T044436Z
LAST-MODIFIED:20170727T044435Z
SEQUENCE:0
SUMMARY:Single event on Dec 1
END:VEVENT
END:VCALENDAR

View File

@@ -1,452 +0,0 @@
(function(name, definition) {
/****************
* A tolerant, minimal icalendar parser
* (http://tools.ietf.org/html/rfc5545)
*
* <peterbraden@peterbraden.co.uk>
* **************/
if (typeof module !== 'undefined') {
module.exports = definition();
} else if (typeof define === 'function' && typeof define.amd === 'object'){
define(definition);
} else {
this[name] = definition();
}
}('ical', function(){
// Unescape Text re RFC 4.3.11
var text = function(t){
t = t || "";
return (t
.replace(/\\\,/g, ',')
.replace(/\\\;/g, ';')
.replace(/\\[nN]/g, '\n')
.replace(/\\\\/g, '\\')
)
}
var parseParams = function(p){
var out = {}
for (var i = 0; i<p.length; i++){
if (p[i].indexOf('=') > -1){
var segs = p[i].split('=');
out[segs[0]] = parseValue(segs.slice(1).join('='));
}
}
return out || sp
}
var parseValue = function(val){
if ('TRUE' === val)
return true;
if ('FALSE' === val)
return false;
var number = Number(val);
if (!isNaN(number))
return number;
return val;
}
var storeValParam = function (name) {
return function (val, curr) {
var current = curr[name];
if (Array.isArray(current)) {
current.push(val);
return curr;
}
if (current != null) {
curr[name] = [current, val];
return curr;
}
curr[name] = val;
return curr
}
}
var storeParam = function (name) {
return function (val, params, curr) {
var data;
if (params && params.length && !(params.length == 1 && params[0] === 'CHARSET=utf-8')) {
data = { params: parseParams(params), val: text(val) }
}
else
data = text(val)
return storeValParam(name)(data, curr);
}
}
var addTZ = function (dt, params) {
var p = parseParams(params);
if (params && p){
dt.tz = p.TZID
}
return dt
}
var dateParam = function(name){
return function (val, params, curr) {
var newDate = text(val);
if (params && params[0] === "VALUE=DATE") {
// Just Date
var comps = /^(\d{4})(\d{2})(\d{2})$/.exec(val);
if (comps !== null) {
// No TZ info - assume same timezone as this computer
newDate = new Date(
comps[1],
parseInt(comps[2], 10)-1,
comps[3]
);
newDate = addTZ(newDate, params);
newDate.dateOnly = true;
// Store as string - worst case scenario
return storeValParam(name)(newDate, curr)
}
}
//typical RFC date-time format
var comps = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(Z)?$/.exec(val);
if (comps !== null) {
if (comps[7] == 'Z'){ // GMT
newDate = new Date(Date.UTC(
parseInt(comps[1], 10),
parseInt(comps[2], 10)-1,
parseInt(comps[3], 10),
parseInt(comps[4], 10),
parseInt(comps[5], 10),
parseInt(comps[6], 10 )
));
// TODO add tz
} else {
newDate = new Date(
parseInt(comps[1], 10),
parseInt(comps[2], 10)-1,
parseInt(comps[3], 10),
parseInt(comps[4], 10),
parseInt(comps[5], 10),
parseInt(comps[6], 10)
);
}
newDate = addTZ(newDate, params);
}
// Store as string - worst case scenario
return storeValParam(name)(newDate, curr)
}
}
var geoParam = function(name){
return function(val, params, curr){
storeParam(val, params, curr)
var parts = val.split(';');
curr[name] = {lat:Number(parts[0]), lon:Number(parts[1])};
return curr
}
}
var categoriesParam = function (name) {
var separatorPattern = /\s*,\s*/g;
return function (val, params, curr) {
storeParam(val, params, curr)
if (curr[name] === undefined)
curr[name] = val ? val.split(separatorPattern) : []
else
if (val)
curr[name] = curr[name].concat(val.split(separatorPattern))
return curr
}
}
// EXDATE is an entry that represents exceptions to a recurrence rule (ex: "repeat every day except on 7/4").
// The EXDATE entry itself can also contain a comma-separated list, so we make sure to parse each date out separately.
// There can also be more than one EXDATE entries in a calendar record.
// Since there can be multiple dates, we create an array of them. The index into the array is the ISO string of the date itself, for ease of use.
// i.e. You can check if ((curr.exdate != undefined) && (curr.exdate[date iso string] != undefined)) to see if a date is an exception.
// NOTE: This specifically uses date only, and not time. This is to avoid a few problems:
// 1. The ISO string with time wouldn't work for "floating dates" (dates without timezones).
// ex: "20171225T060000" - this is supposed to mean 6 AM in whatever timezone you're currently in
// 2. Daylight savings time potentially affects the time you would need to look up
// 3. Some EXDATE entries in the wild seem to have times different from the recurrence rule, but are still excluded by calendar programs. Not sure how or why.
// These would fail any sort of sane time lookup, because the time literally doesn't match the event. So we'll ignore time and just use date.
// ex: DTSTART:20170814T140000Z
// RRULE:FREQ=WEEKLY;WKST=SU;INTERVAL=2;BYDAY=MO,TU
// EXDATE:20171219T060000
// Even though "T060000" doesn't match or overlap "T1400000Z", it's still supposed to be excluded? Odd. :(
// TODO: See if this causes any problems with events that recur multiple times a day.
var exdateParam = function (name) {
return function (val, params, curr) {
var separatorPattern = /\s*,\s*/g;
curr[name] = curr[name] || [];
var dates = val ? val.split(separatorPattern) : [];
dates.forEach(function (entry) {
var exdate = new Array();
dateParam(name)(entry, params, exdate);
if (exdate[name])
{
if (typeof exdate[name].toISOString === 'function') {
curr[name][exdate[name].toISOString().substring(0, 10)] = exdate[name];
} else {
console.error("No toISOString function in exdate[name]", exdate[name]);
}
}
}
)
return curr;
}
}
// RECURRENCE-ID is the ID of a specific recurrence within a recurrence rule.
// TODO: It's also possible for it to have a range, like "THISANDPRIOR", "THISANDFUTURE". This isn't currently handled.
var recurrenceParam = function (name) {
return dateParam(name);
}
var addFBType = function (fb, params) {
var p = parseParams(params);
if (params && p){
fb.type = p.FBTYPE || "BUSY"
}
return fb;
}
var freebusyParam = function (name) {
return function(val, params, curr){
var fb = addFBType({}, params);
curr[name] = curr[name] || []
curr[name].push(fb);
storeParam(val, params, fb);
var parts = val.split('/');
['start', 'end'].forEach(function (name, index) {
dateParam(name)(parts[index], params, fb);
});
return curr;
}
}
return {
objectHandlers : {
'BEGIN' : function(component, params, curr, stack){
stack.push(curr)
return {type:component, params:params}
}
, 'END' : function(component, params, curr, stack){
// prevents the need to search the root of the tree for the VCALENDAR object
if (component === "VCALENDAR") {
//scan all high level object in curr and drop all strings
var key,
obj;
for (key in curr) {
if(curr.hasOwnProperty(key)) {
obj = curr[key];
if (typeof obj === 'string') {
delete curr[key];
}
}
}
return curr
}
var par = stack.pop()
if (curr.uid)
{
// If this is the first time we run into this UID, just save it.
if (par[curr.uid] === undefined)
{
par[curr.uid] = curr;
}
else
{
// If we have multiple ical entries with the same UID, it's either going to be a
// modification to a recurrence (RECURRENCE-ID), and/or a significant modification
// to the entry (SEQUENCE).
// TODO: Look into proper sequence logic.
if (curr.recurrenceid === undefined)
{
// If we have the same UID as an existing record, and it *isn't* a specific recurrence ID,
// not quite sure what the correct behaviour should be. For now, just take the new information
// and merge it with the old record by overwriting only the fields that appear in the new record.
var key;
for (key in curr) {
par[curr.uid][key] = curr[key];
}
}
}
// If we have recurrence-id entries, list them as an array of recurrences keyed off of recurrence-id.
// To use - as you're running through the dates of an rrule, you can try looking it up in the recurrences
// array. If it exists, then use the data from the calendar object in the recurrence instead of the parent
// for that day.
// NOTE: Sometimes the RECURRENCE-ID record will show up *before* the record with the RRULE entry. In that
// case, what happens is that the RECURRENCE-ID record ends up becoming both the parent record and an entry
// in the recurrences array, and then when we process the RRULE entry later it overwrites the appropriate
// fields in the parent record.
if (curr.recurrenceid != null)
{
// TODO: Is there ever a case where we have to worry about overwriting an existing entry here?
// Create a copy of the current object to save in our recurrences array. (We *could* just do par = curr,
// except for the case that we get the RECURRENCE-ID record before the RRULE record. In that case, we
// would end up with a shared reference that would cause us to overwrite *both* records at the point
// that we try and fix up the parent record.)
var recurrenceObj = new Object();
var key;
for (key in curr) {
recurrenceObj[key] = curr[key];
}
if (recurrenceObj.recurrences != undefined) {
delete recurrenceObj.recurrences;
}
// If we don't have an array to store recurrences in yet, create it.
if (par[curr.uid].recurrences === undefined) {
par[curr.uid].recurrences = new Array();
}
// Save off our cloned recurrence object into the array, keyed by date but not time.
// We key by date only to avoid timezone and "floating time" problems (where the time isn't associated with a timezone).
// TODO: See if this causes a problem with events that have multiple recurrences per day.
if (typeof curr.recurrenceid.toISOString === 'function') {
par[curr.uid].recurrences[curr.recurrenceid.toISOString().substring(0,10)] = recurrenceObj;
} else {
console.error("No toISOString function in curr.recurrenceid", curr.recurrenceid);
}
}
// One more specific fix - in the case that an RRULE entry shows up after a RECURRENCE-ID entry,
// let's make sure to clear the recurrenceid off the parent field.
if ((par[curr.uid].rrule != undefined) && (par[curr.uid].recurrenceid != undefined))
{
delete par[curr.uid].recurrenceid;
}
}
else
par[Math.random()*100000] = curr // Randomly assign ID : TODO - use true GUID
return par
}
, 'SUMMARY' : storeParam('summary')
, 'DESCRIPTION' : storeParam('description')
, 'URL' : storeParam('url')
, 'UID' : storeParam('uid')
, 'LOCATION' : storeParam('location')
, 'DTSTART' : dateParam('start')
, 'DTEND' : dateParam('end')
, 'EXDATE' : exdateParam('exdate')
,' CLASS' : storeParam('class')
, 'TRANSP' : storeParam('transparency')
, 'GEO' : geoParam('geo')
, 'PERCENT-COMPLETE': storeParam('completion')
, 'COMPLETED': dateParam('completed')
, 'CATEGORIES': categoriesParam('categories')
, 'FREEBUSY': freebusyParam('freebusy')
, 'DTSTAMP': dateParam('dtstamp')
, 'CREATED': dateParam('created')
, 'LAST-MODIFIED': dateParam('lastmodified')
, 'RECURRENCE-ID': recurrenceParam('recurrenceid')
},
handleObject : function(name, val, params, ctx, stack, line){
var self = this
if(self.objectHandlers[name])
return self.objectHandlers[name](val, params, ctx, stack, line)
//handling custom properties
if(name.match(/X\-[\w\-]+/) && stack.length > 0) {
//trimming the leading and perform storeParam
name = name.substring(2);
return (storeParam(name))(val, params, ctx, stack, line);
}
return storeParam(name.toLowerCase())(val, params, ctx);
},
parseICS : function(str){
var self = this
var lines = str.split(/\r?\n/)
var ctx = {}
var stack = []
for (var i = 0, ii = lines.length, l = lines[0]; i<ii; i++, l=lines[i]){
//Unfold : RFC#3.1
while (lines[i+1] && /[ \t]/.test(lines[i+1][0])) {
l += lines[i+1].slice(1)
i += 1
}
var kv = l.split(":")
if (kv.length < 2){
// Invalid line - must have k&v
continue;
}
// Although the spec says that vals with colons should be quote wrapped
// in practise nobody does, so we assume further colons are part of the
// val
var value = kv.slice(1).join(":")
, kp = kv[0].split(";")
, name = kp[0]
, params = kp.slice(1)
ctx = self.handleObject(name, value, params, ctx, stack, l) || {}
}
// type and params are added to the list of items, get rid of them.
delete ctx.type
delete ctx.params
return ctx
}
}
}))

View File

@@ -1,8 +0,0 @@
module.exports = require('./ical')
var node = require('./node-ical')
// Copy node functions across to exports
for (var i in node){
module.exports[i] = node[i]
}

View File

@@ -1,66 +0,0 @@
var ical = require('./ical')
, request = require('request')
, fs = require('fs')
exports.fromURL = function(url, opts, cb){
if (!cb)
return;
request(url, opts, function(err, r, data){
if (err)
{
return cb(err, null);
}
else if (r.statusCode != 200)
{
return cb(r.statusCode + ": " + r.statusMessage, null);
}
cb(undefined, ical.parseICS(data));
})
}
exports.parseFile = function(filename){
return ical.parseICS(fs.readFileSync(filename, 'utf8'))
}
var rrule = require('rrule').RRule
ical.objectHandlers['RRULE'] = function(val, params, curr, stack, line){
curr.rrule = line;
return curr
}
var originalEnd = ical.objectHandlers['END'];
ical.objectHandlers['END'] = function (val, params, curr, stack) {
// Recurrence rules are only valid for VEVENT, VTODO, and VJOURNAL.
// More specifically, we need to filter the VCALENDAR type because we might end up with a defined rrule
// due to the subtypes.
if ((val === "VEVENT") || (val === "VTODO") || (val === "VJOURNAL")) {
if (curr.rrule) {
var rule = curr.rrule.replace('RRULE:', '');
if (rule.indexOf('DTSTART') === -1) {
if (curr.start.length === 8) {
var comps = /^(\d{4})(\d{2})(\d{2})$/.exec(curr.start);
if (comps) {
curr.start = new Date(comps[1], comps[2] - 1, comps[3]);
}
}
if (typeof curr.start.toISOString === 'function') {
try {
rule += ';DTSTART=' + curr.start.toISOString().replace(/[-:]/g, '');
rule = rule.replace(/\.[0-9]{3}/, '');
} catch (error) {
console.error("ERROR when trying to convert to ISOString", error);
}
} else {
console.error("No toISOString function in curr.start", curr.start);
}
}
curr.rrule = rrule.fromString(rule);
}
}
return originalEnd.call(this, val, params, curr, stack);
}

View File

@@ -1,29 +0,0 @@
{
"name": "ical",
"version": "0.5.0",
"main": "index.js",
"description": "A tolerant, minimal icalendar parser",
"keywords": [
"ical",
"ics",
"calendar"
],
"homepage": "https://github.com/peterbraden/ical.js",
"author": "Peter Braden <peterbraden@peterbraden.co.uk> (peterbraden.co.uk)",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "git://github.com/peterbraden/ical.js.git"
},
"dependencies": {
"request": "^2.88.0",
"rrule": "2.4.1"
},
"devDependencies": {
"vows": "0.8.2",
"underscore": "1.9.1"
},
"scripts": {
"test": "./node_modules/vows/bin/vows ./test/test.js"
}
}

View File

@@ -1,62 +0,0 @@
# ical.js #
(Formerly node-ical)
[![Build Status](https://travis-ci.org/peterbraden/ical.js.png)](https://travis-ci.org/peterbraden/ical.js)
A tolerant, minimal icalendar parser for javascript/node
(http://tools.ietf.org/html/rfc5545)
## Install - Node.js ##
ical.js is availble on npm:
npm install ical
## API ##
ical.parseICS(str)
Parses a string with an ICS File
var data = ical.parseFile(filename)
Reads in the specified iCal file, parses it and returns the parsed data
ical.fromURL(url, options, function(err, data) {} )
Use the request library to fetch the specified URL (```opts``` gets passed on to the ```request()``` call), and call the function with the result (either an error or the data).
## Example 1 - Print list of upcoming node conferences (see example.js)
```javascript
'use strict';
const ical = require('ical');
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
ical.fromURL('http://lanyrd.com/topics/nodejs/nodejs.ics', {}, function (err, data) {
for (let k in data) {
if (data.hasOwnProperty(k)) {
var ev = data[k];
if (data[k].type == 'VEVENT') {
console.log(`${ev.summary} is in ${ev.location} on the ${ev.start.getDate()} of ${months[ev.start.getMonth()]} at ${ev.start.toLocaleTimeString('en-GB')}`);
}
}
}
});
```
## Recurrences and Exceptions ##
Calendar events with recurrence rules can be significantly more complicated to handle correctly. There are three parts to handling them:
1. rrule - the recurrence rule specifying the pattern of recurring dates and times for the event.
2. recurrences - an optional array of event data that can override specific occurrences of the event.
3. exdate - an optional array of dates that should be excluded from the recurrence pattern.
See example_rrule.js for an example of handling recurring calendar events.

View File

@@ -1,500 +0,0 @@
/****
* Tests
*
*
***/
process.env.TZ = 'America/San_Francisco';
var ical = require('../index')
var vows = require('vows')
, assert = require('assert')
, _ = require('underscore')
vows.describe('node-ical').addBatch({
'when parsing test1.ics (node conferences schedule from lanyrd.com, modified)': {
topic: function () {
return ical.parseFile('./test/test1.ics')
}
,'we get 9 events': function (topic) {
var events = _.select(_.values(topic), function(x){ return x.type==='VEVENT'})
assert.equal (events.length, 9);
}
,'event 47f6e' : {
topic: function(events){
return _.select(_.values(events),
function(x){
return x.uid ==='47f6ea3f28af2986a2192fa39a91fa7d60d26b76'})[0]
}
,'is in fort lauderdale' : function(topic){
assert.equal(topic.location, "Fort Lauderdale, United States")
}
,'starts Tue, 29 Nov 2011' : function(topic){
assert.equal(topic.start.toDateString(), new Date(2011,10,29).toDateString())
}
}
, 'event 480a' : {
topic: function(events){
return _.select(_.values(events),
function(x){
return x.uid ==='480a3ad48af5ed8965241f14920f90524f533c18'})[0]
}
, 'has a summary (invalid colon handling tolerance)' : function(topic){
assert.equal(topic.summary, '[Async]: Everything Express')
}
, 'has a date only start datetime' : function(topic){
assert.equal(topic.start.dateOnly, true)
}
, 'has a date only end datetime' : function(topic){
assert.equal(topic.end.dateOnly, true)
}
}
, 'event d4c8' :{
topic : function(events){
return _.select(_.values(events),
function(x){
return x.uid === 'd4c826dfb701f611416d69b4df81caf9ff80b03a'})[0]
}
, 'has a start datetime' : function(topic){
assert.equal(topic.start.toDateString(), new Date(Date.UTC(2011, 2, 12, 20, 0, 0)).toDateString())
}
}
, 'event sdfkf09fsd0 (Invalid Date)' :{
topic : function(events){
return _.select(_.values(events),
function(x){
return x.uid === 'sdfkf09fsd0'})[0]
}
, 'has a start datetime' : function(topic){
assert.equal(topic.start, "Next Year")
}
}
}
, 'with test2.ics (testing ical features)' : {
topic: function () {
return ical.parseFile('./test/test2.ics')
}
, 'todo item uid4@host1.com' : {
topic : function(items){
return items['uid4@host1.com']
}
, 'is a VTODO' : function(topic){
assert.equal(topic.type, 'VTODO')
}
}
, 'vfreebusy' : {
topic: function(events) {
return _.select(_.values(events), function(x) {
return x.type === 'VFREEBUSY';
})[0];
}
, 'has a URL' : function(topic) {
assert.equal(topic.url, 'http://www.host.com/calendar/busytime/jsmith.ifb');
}
}
, 'vfreebusy first freebusy' : {
topic: function(events) {
return _.select(_.values(events), function(x) {
return x.type === 'VFREEBUSY';
})[0].freebusy[0];
}
, 'has undefined type defaulting to busy' : function(topic) {
assert.equal(topic.type, "BUSY");
}
, 'has an start datetime' : function(topic) {
assert.equal(topic.start.getFullYear(), 1998);
assert.equal(topic.start.getUTCMonth(), 2);
assert.equal(topic.start.getUTCDate(), 14);
assert.equal(topic.start.getUTCHours(), 23);
assert.equal(topic.start.getUTCMinutes(), 30);
}
, 'has an end datetime' : function(topic) {
assert.equal(topic.end.getFullYear(), 1998);
assert.equal(topic.end.getUTCMonth(), 2);
assert.equal(topic.end.getUTCDate(), 15);
assert.equal(topic.end.getUTCHours(), 00);
assert.equal(topic.end.getUTCMinutes(), 30);
}
}
}
, 'with test3.ics (testing tvcountdown.com)' : {
topic: function() {
return ical.parseFile('./test/test3.ics');
}
, 'event -83' : {
topic: function(events) {
return _.select(_.values(events), function(x) {
return x.uid === '20110505T220000Z-83@tvcountdown.com';
})[0];
}
, 'has a start datetime' : function(topic) {
assert.equal(topic.start.getFullYear(), 2011);
assert.equal(topic.start.getMonth(), 4);
}
, 'has an end datetime' : function(topic) {
assert.equal(topic.end.getFullYear(), 2011);
assert.equal(topic.end.getMonth(), 4);
}
}
}
, 'with test4.ics (testing tripit.com)' : {
topic: function() {
return ical.parseFile('./test/test4.ics');
}
, 'event c32a5...' : {
topic: function(events) {
return _.select(_.values(events), function(x) {
return x.uid === 'c32a5eaba2354bb29e012ec18da827db90550a3b@tripit.com';
})[0];
}
, 'has a start datetime' : function(topic) {
assert.equal(topic.start.getFullYear(), 2011);
assert.equal(topic.start.getMonth(), 09);
assert.equal(topic.start.getDate(), 11);
}
, 'has a summary' : function(topic){
// escaped commas and semicolons should be replaced
assert.equal(topic.summary, 'South San Francisco, CA, October 2011;')
}
, 'has a description' : function(topic){
var desired = 'John Doe is in South San Francisco, CA from Oct 11 ' +
'to Oct 13, 2011\nView and/or edit details in TripIt : http://www.tripit.c' +
'om/trip/show/id/23710889\nTripIt - organize your travel at http://www.trip' +
'it.com\n'
assert.equal(topic.description, desired)
}
, 'has a geolocation' : function(topic){
assert.ok(topic.geo, 'no geo param')
assert.equal(topic.geo.lat, 37.654656)
assert.equal(topic.geo.lon, -122.40775)
}
, 'has transparency' : function(topic){
assert.equal(topic.transparency, 'TRANSPARENT')
}
}
}
, 'with test5.ics (testing meetup.com)' : {
topic: function () {
return ical.parseFile('./test/test5.ics')
}
, 'event nsmxnyppbfc@meetup.com' : {
topic: function(events) {
return _.select(_.values(events), function(x) {
return x.uid === 'event_nsmxnyppbfc@meetup.com';
})[0];
}
, 'has a start' : function(topic){
assert.equal(topic.start.tz, 'America/Phoenix')
assert.equal(topic.start.toISOString(), new Date(2011, 10, 09, 19, 0,0).toISOString())
}
}
}
, 'with test6.ics (testing assembly.org)': {
topic: function () {
return ical.parseFile('./test/test6.ics')
}
, 'event with no ID' : {
topic: function(events) {
return _.select(_.values(events), function(x) {
return x.summary === 'foobar Summer 2011 starts!';
})[0];
}
, 'has a start' : function(topic){
assert.equal(topic.start.toISOString(), new Date(2011, 07, 04, 12, 0,0).toISOString())
}
}
, 'event with rrule' :{
topic: function(events){
return _.select(_.values(events), function(x){
return x.summary === "foobarTV broadcast starts"
})[0];
}
, "Has an RRULE": function(topic){
assert.notEqual(topic.rrule, undefined);
}
, "RRule text": function(topic){
assert.equal(topic.rrule.toText(), "every 5 weeks on Monday, Friday until January 30, 2013")
}
}
}
, 'with test7.ics (testing dtstart of rrule)' :{
topic: function() {
return ical.parseFile('./test/test7.ics');
},
'recurring yearly event (14 july)': {
topic: function(events){
var ev = _.values(events)[0];
return ev.rrule.between(new Date(2013, 0, 1), new Date(2014, 0, 1));
},
'dt start well set': function(topic) {
assert.equal(topic[0].toDateString(), new Date(2013, 6, 14).toDateString());
}
}
}
, "with test 8.ics (VTODO completion)": {
topic: function() {
return ical.parseFile('./test/test8.ics');
},
'grabbing VTODO task': {
topic: function(topic) {
return _.values(topic)[0];
},
'task completed': function(task){
assert.equal(task.completion, 100);
assert.equal(task.completed.toISOString(), new Date(2013, 06, 16, 10, 57, 45).toISOString());
}
}
}
, "with test 9.ics (VEVENT with VALARM)": {
topic: function() {
return ical.parseFile('./test/test9.ics');
},
'grabbing VEVENT task': {
topic: function(topic) {
return _.values(topic)[0];
},
'task completed': function(task){
assert.equal(task.summary, "Event with an alarm");
}
}
}
, 'with test 11.ics (VEVENT with custom properties)': {
topic: function() {
return ical.parseFile('./test10.ics');
},
'grabbing custom properties': {
topic: function(topic) {
}
}
},
'with test10.ics': {
topic: function () {
return ical.parseFile('./test/test10.ics');
},
'when categories present': {
topic: function (t) {return _.values(t)[0]},
'should be a list': function (e) {
assert(e.categories instanceof [].constructor);
},
'should contain individual category values': function (e) {
assert.deepEqual(e.categories, ['cat1', 'cat2', 'cat3']);
}
},
'when categories present with trailing whitespace': {
topic: function (t) {return _.values(t)[1]},
'should contain individual category values without whitespace': function (e) {
assert.deepEqual(e.categories, ['cat1', 'cat2', 'cat3']);
}
},
'when categories present but empty': {
topic: function (t) {return _.values(t)[2]},
'should be an empty list': function (e) {
assert.deepEqual(e.categories, []);
}
},
'when categories present but singular': {
topic: function (t) {return _.values(t)[3]},
'should be a list of single item': function (e) {
assert.deepEqual(e.categories, ['lonely-cat']);
}
},
'when categories present on multiple lines': {
topic: function (t) {return _.values(t)[4]},
'should contain the category values in an array': function (e) {
assert.deepEqual(e.categories, ['cat1', 'cat2', 'cat3']);
}
}
},
'with test11.ics (testing zimbra freebusy)': {
topic: function () {
return ical.parseFile('./test/test11.ics');
},
'freebusy params' : {
topic: function(events) {
return _.values(events)[0];
}
, 'has a URL' : function(topic) {
assert.equal(topic.url, 'http://mail.example.com/yvr-2a@example.com/20140416');
}
, 'has an ORGANIZER' : function(topic) {
assert.equal(topic.organizer, 'mailto:yvr-2a@example.com');
}
, 'has an start datetime' : function(topic) {
assert.equal(topic.start.getFullYear(), 2014);
assert.equal(topic.start.getMonth(), 3);
}
, 'has an end datetime' : function(topic) {
assert.equal(topic.end.getFullYear(), 2014);
assert.equal(topic.end.getMonth(), 6);
}
}
, 'freebusy busy events' : {
topic: function(events) {
return _.select(_.values(events)[0].freebusy, function(x) {
return x.type === 'BUSY';
})[0];
}
, 'has an start datetime' : function(topic) {
assert.equal(topic.start.getFullYear(), 2014);
assert.equal(topic.start.getMonth(), 3);
assert.equal(topic.start.getUTCHours(), 15);
assert.equal(topic.start.getUTCMinutes(), 15);
}
, 'has an end datetime' : function(topic) {
assert.equal(topic.end.getFullYear(), 2014);
assert.equal(topic.end.getMonth(), 3);
assert.equal(topic.end.getUTCHours(), 19);
assert.equal(topic.end.getUTCMinutes(), 00);
}
}
}
, 'with test12.ics (testing recurrences and exdates)': {
topic: function () {
return ical.parseFile('./test/test12.ics')
}
, 'event with rrule': {
topic: function (events) {
return _.select(_.values(events), function (x) {
return x.uid === '0000001';
})[0];
}
, "Has an RRULE": function (topic) {
assert.notEqual(topic.rrule, undefined);
}
, "Has summary Treasure Hunting": function (topic) {
assert.equal(topic.summary, 'Treasure Hunting');
}
, "Has two EXDATES": function (topic) {
assert.notEqual(topic.exdate, undefined);
assert.notEqual(topic.exdate[new Date(2015, 06, 08, 12, 0, 0).toISOString().substring(0, 10)], undefined);
assert.notEqual(topic.exdate[new Date(2015, 06, 10, 12, 0, 0).toISOString().substring(0, 10)], undefined);
}
, "Has a RECURRENCE-ID override": function (topic) {
assert.notEqual(topic.recurrences, undefined);
assert.notEqual(topic.recurrences[new Date(2015, 06, 07, 12, 0, 0).toISOString().substring(0, 10)], undefined);
assert.equal(topic.recurrences[new Date(2015, 06, 07, 12, 0, 0).toISOString().substring(0, 10)].summary, 'More Treasure Hunting');
}
}
}
, 'with test13.ics (testing recurrence-id before rrule)': {
topic: function () {
return ical.parseFile('./test/test13.ics')
}
, 'event with rrule': {
topic: function (events) {
return _.select(_.values(events), function (x) {
return x.uid === '6m2q7kb2l02798oagemrcgm6pk@google.com';
})[0];
}
, "Has an RRULE": function (topic) {
assert.notEqual(topic.rrule, undefined);
}
, "Has summary 'repeated'": function (topic) {
assert.equal(topic.summary, 'repeated');
}
, "Has a RECURRENCE-ID override": function (topic) {
assert.notEqual(topic.recurrences, undefined);
assert.notEqual(topic.recurrences[new Date(2016, 7, 26, 14, 0, 0).toISOString().substring(0, 10)], undefined);
assert.equal(topic.recurrences[new Date(2016, 7, 26, 14, 0, 0).toISOString().substring(0, 10)].summary, 'bla bla');
}
}
}
, 'with test14.ics (testing comma-separated exdates)': {
topic: function () {
return ical.parseFile('./test/test14.ics')
}
, 'event with comma-separated exdate': {
topic: function (events) {
return _.select(_.values(events), function (x) {
return x.uid === '98765432-ABCD-DCBB-999A-987765432123';
})[0];
}
, "Has summary 'Example of comma-separated exdates'": function (topic) {
assert.equal(topic.summary, 'Example of comma-separated exdates');
}
, "Has four comma-separated EXDATES": function (topic) {
assert.notEqual(topic.exdate, undefined);
// Verify the four comma-separated EXDATES are there
assert.notEqual(topic.exdate[new Date(2017, 6, 6, 12, 0, 0).toISOString().substring(0, 10)], undefined);
assert.notEqual(topic.exdate[new Date(2017, 6, 17, 12, 0, 0).toISOString().substring(0, 10)], undefined);
assert.notEqual(topic.exdate[new Date(2017, 6, 20, 12, 0, 0).toISOString().substring(0, 10)], undefined);
assert.notEqual(topic.exdate[new Date(2017, 7, 3, 12, 0, 0).toISOString().substring(0, 10)], undefined);
// Verify an arbitrary date isn't there
assert.equal(topic.exdate[new Date(2017, 4, 5, 12, 0, 0).toISOString().substring(0, 10)], undefined);
}
}
}
, 'with test14.ics (testing exdates with bad times)': {
topic: function () {
return ical.parseFile('./test/test14.ics')
}
, 'event with exdates with bad times': {
topic: function (events) {
return _.select(_.values(events), function (x) {
return x.uid === '1234567-ABCD-ABCD-ABCD-123456789012';
})[0];
}
, "Has summary 'Example of exdate with bad times'": function (topic) {
assert.equal(topic.summary, 'Example of exdate with bad times');
}
, "Has two EXDATES even though they have bad times": function (topic) {
assert.notEqual(topic.exdate, undefined);
// Verify the two EXDATES are there, even though they have bad times
assert.notEqual(topic.exdate[new Date(2017, 11, 18, 12, 0, 0).toISOString().substring(0, 10)], undefined);
assert.notEqual(topic.exdate[new Date(2017, 11, 19, 12, 0, 0).toISOString().substring(0, 10)], undefined);
}
}
}
, 'url request errors': {
topic : function () {
ical.fromURL('http://255.255.255.255/', {}, this.callback);
}
, 'are passed back to the callback' : function (err, result) {
assert.instanceOf(err, Error);
if (!err){
console.log(">E:", err, result)
}
}
}
}).export(module)
//ical.fromURL('http://lanyrd.com/topics/nodejs/nodejs.ics',
// {},
// function(err, data){
// console.log("OUT:", data)
// })

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