mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-06-27 08:50:59 -04:00
Run prettier over ALL files once
No other changes done in this commit
This commit is contained in:
parent
3a5a29efc0
commit
abb5dc5739
160 changed files with 2369 additions and 2210 deletions
5
.github/CONTRIBUTING.md
vendored
5
.github/CONTRIBUTING.md
vendored
|
@ -1,5 +1,4 @@
|
|||
Contribution Policy for MagicMirror²
|
||||
====================================
|
||||
# Contribution Policy for MagicMirror²
|
||||
|
||||
Thanks for contributing to MagicMirror²!
|
||||
|
||||
|
@ -30,7 +29,7 @@ Problems installing or configuring your MagicMirror? Check out: [https://forum.m
|
|||
|
||||
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, Windows, Mac, Linux, System V UNIX).
|
||||
|
||||
**Node Version**: Make sure it's version 0.12.13 or later.
|
||||
|
||||
|
|
11
.github/ISSUE_TEMPLATE.md
vendored
11
.github/ISSUE_TEMPLATE.md
vendored
|
@ -1,28 +1,33 @@
|
|||
## I'm not sure if this is a bug
|
||||
|
||||
If you're not sure if it's a real bug or if it's just you, please open a topic on the forum: [https://forum.magicmirror.builders/category/15/bug-hunt](https://forum.magicmirror.builders/category/15/bug-hunt)
|
||||
|
||||
## I'm having troubles installing or configuring MagicMirror
|
||||
|
||||
Problems installing or configuring your MagicMirror? Check out: [https://forum.magicmirror.builders/category/10/troubleshooting](https://forum.magicmirror.builders/category/10/troubleshooting)
|
||||
|
||||
## I found a bug in the MagicMirror installer
|
||||
|
||||
If you are facing an issue or found a bug while trying to install MagicMirror via the installer please report it in the respective GitHub repository:
|
||||
[https://github.com/sdetweil/MagicMirror_scripts](https://github.com/sdetweil/MagicMirror_scripts)
|
||||
|
||||
## I found a bug in the MagicMirror Docker image
|
||||
|
||||
If you are facing an issue or found a bug while running MagicMirror inside a Docker container please create an issue in the GitHub repository of the MagicMirror Docker image:
|
||||
[https://github.com/bastilimbach/docker-MagicMirror](https://github.com/bastilimbach/docker-MagicMirror)
|
||||
|
||||
---
|
||||
|
||||
## I found a bug in MagicMirror
|
||||
Please make sure to only submit reproducible issues. You can safely remove everything above the dividing line.
|
||||
|
||||
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, Windows, Mac, Linux, System V UNIX).
|
||||
|
||||
**Node Version**: Make sure it's version 8 or later.
|
||||
|
||||
**MagicMirror Version**: Please let us now which version of MagicMirror you are running. It can be found in the `package.log` file.
|
||||
**MagicMirror Version**: Please let us now which version of MagicMirror you are running. It can be found in the `package.log` file.
|
||||
|
||||
**Description**: Provide a detailed description about the issue and include specific details to help us understand the problem. Adding screenshots will help describing the problem.
|
||||
|
||||
|
|
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
@ -7,8 +7,7 @@ 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.
|
||||
- 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.
|
||||
|
|
84
CHANGELOG.md
84
CHANGELOG.md
|
@ -7,24 +7,27 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||
|
||||
## [2.12.0] - Unreleased (Develop Branch)
|
||||
|
||||
*This release is scheduled to be released on 2020-07-01.*
|
||||
_This release is scheduled to be released on 2020-07-01._
|
||||
|
||||
### Added
|
||||
|
||||
- Added prettier for an even cleaner codebase
|
||||
|
||||
### 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
|
||||
- Replaced insecure links with https ones
|
||||
- Cleaned up all "no-undef" warnings from eslint
|
||||
|
||||
### 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)
|
||||
|
||||
|
@ -37,11 +40,13 @@ In the past years the project has grown a lot. This came with a huge downside: p
|
|||
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".
|
||||
|
@ -56,6 +61,7 @@ For more information regarding this major change, please check issue [#1860](htt
|
|||
- 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)
|
||||
|
@ -66,6 +72,7 @@ For more information regarding this major change, please check issue [#1860](htt
|
|||
- 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:
|
||||
|
@ -79,6 +86,7 @@ For more information regarding this major change, please check issue [#1860](htt
|
|||
## [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
|
||||
|
@ -88,12 +96,14 @@ 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.
|
||||
|
@ -102,6 +112,7 @@ Special thanks to @sdetweil for all his great contributions!
|
|||
- 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.
|
||||
|
@ -114,18 +125,21 @@ 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`. If you are having issues running Electron, make sure your [Raspbian is up to date](https://www.raspberrypi.org/documentation/raspbian/updating.md).
|
||||
|
||||
### Added
|
||||
|
||||
- Spanish translation for "PRECIP".
|
||||
- Adding a Malay (Malaysian) translation for MagicMirror².
|
||||
- Add test check URLs of vendors 200 and 404 HTTP CODE.
|
||||
- Add tests for new weather module and helper to stub ajax requests.
|
||||
|
||||
### Updated
|
||||
|
||||
- Updatenotification module: Display update notification for a limited (configurable) time.
|
||||
- Enabled e2e/vendor_spec.js tests.
|
||||
- The css/custom.css will be renamed after the next release. We've added into `run-start.sh` an instruction by GIT to ignore with `--skip-worktree` and `rm --cached`. [#1540](https://github.com/MichMich/MagicMirror/issues/1540)
|
||||
- Disable sending of notification CLOCK_SECOND when displaySeconds is false.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Updatenotification module: Properly handle race conditions, prevent crash.
|
||||
- Send `NEWS_FEED` notification also for the first news messages which are shown.
|
||||
- Fixed issue where weather module would not refresh data after a network or API outage. [#1722](https://github.com/MichMich/MagicMirror/issues/1722)
|
||||
|
@ -137,6 +151,7 @@ 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`. 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”
|
||||
|
@ -156,6 +171,7 @@ Special thanks to @sdetweil for all his great contributions!
|
|||
- Added to `newsfeed.js`: in order to design the news article better with css, three more class-names were introduced: newsfeed-desc, newsfeed-desc, newsfeed-desc
|
||||
|
||||
### Updated
|
||||
|
||||
- English translation for "Feels" to "Feels like"
|
||||
- Fixed the example calendar url in `config.js.sample`
|
||||
- Update `ical.js` to solve various calendar issues.
|
||||
|
@ -163,6 +179,7 @@ Special thanks to @sdetweil for all his great contributions!
|
|||
- Only update clock once per minute when seconds aren't shown
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed uncaught exception, race condition on module update
|
||||
- Fixed issue [#1696](https://github.com/MichMich/MagicMirror/issues/1696), some ical files start date to not parse to date type
|
||||
- Allowance HTML5 autoplay-policy (policy is changed from Chrome 66 updates)
|
||||
|
@ -174,6 +191,7 @@ Special thanks to @sdetweil for all his great contributions!
|
|||
- 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
|
||||
|
@ -190,6 +208,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
|
||||
|
@ -203,12 +222,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).
|
||||
|
@ -217,7 +238,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)
|
||||
|
@ -226,6 +247,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).
|
||||
|
@ -243,11 +265,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
|
||||
|
@ -262,6 +286,7 @@ 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 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
|
||||
|
@ -269,15 +294,17 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
|
|||
- Bug in newsfeed when `removeStartTags` is used on the description [#1478](https://github.com/MichMich/MagicMirror/issues/1478)
|
||||
|
||||
### Updated
|
||||
|
||||
- The default calendar setting `showEnd` is changed to `false`.
|
||||
|
||||
### Changed
|
||||
- The Weather Forecast module by default displays the ° symbol after every numeric value to be consistent with the Current Weather module.
|
||||
|
||||
- The Weather Forecast module by default displays the ° symbol after every numeric value to be consistent with the Current Weather module.
|
||||
|
||||
## [2.5.0] - 2018-10-01
|
||||
|
||||
### Added
|
||||
|
||||
- Romanian translation for "Feels"
|
||||
- Support multi-line compliments
|
||||
- Simplified Chinese translation for "Feels"
|
||||
|
@ -292,6 +319,7 @@ 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 close dates to be absolute, if no configured in the config.js - module Calendar
|
||||
|
@ -337,11 +365,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.
|
||||
|
@ -354,6 +384,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
|
||||
|
@ -361,6 +392,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
|
||||
|
@ -381,6 +413,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.
|
||||
|
@ -388,6 +421,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.
|
||||
|
@ -405,6 +439,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
|
||||
|
@ -412,10 +447,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
|
||||
|
||||
- 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.
|
||||
|
@ -429,6 +466,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
|
||||
|
@ -439,9 +477,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.
|
||||
|
@ -458,6 +498,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.
|
||||
|
@ -466,6 +507,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'.
|
||||
|
@ -476,11 +518,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.
|
||||
|
@ -499,11 +543,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
|
||||
|
@ -517,6 +563,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.
|
||||
|
@ -532,8 +579,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.
|
||||
|
@ -574,6 +622,7 @@ 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.
|
||||
|
@ -590,6 +639,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.
|
||||
|
@ -620,6 +670,7 @@ A huge, huge, huge thanks to user @fewieden for all his hard work on the new `we
|
|||
- 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.
|
||||
|
@ -630,6 +681,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.
|
||||
|
@ -640,6 +692,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)
|
||||
|
@ -647,11 +700,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.
|
||||
|
@ -659,6 +714,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.
|
||||
|
@ -666,6 +722,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'.
|
||||
|
@ -674,55 +731,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: [https://michaelteeuw.nl/post/83916869600/magic-mirror-part-vi-production-of-the](https://michaelteeuw.nl/post/83916869600/magic-mirror-part-vi-production-of-the)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
The MIT License (MIT)
|
||||
=====================
|
||||
# The MIT License (MIT)
|
||||
|
||||
Copyright © 2016-2019 Michael Teeuw
|
||||
|
||||
|
|
|
@ -14,9 +14,11 @@
|
|||
MagicMirror² focuses on a modular plugin system and uses [Electron](https://www.electronjs.org/) as an application wrapper. So no more web server or browser installs necessary!
|
||||
|
||||
## Documentation
|
||||
|
||||
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).
|
||||
|
||||
## Links
|
||||
|
||||
- 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)
|
||||
|
@ -28,7 +30,6 @@ For the full documentation including **[installation instructions](https://docs.
|
|||
|
||||
Contributions of all kinds are welcome, not only in the form of code but also with regards bug reports and documentation. For the full contribution guidelines, check out: [https://docs.magicmirror.builders/getting-started/contributing.html](https://docs.magicmirror.builders/getting-started/contributing.html)
|
||||
|
||||
|
||||
## Enjoying MagicMirror? Consider a donation!
|
||||
|
||||
MagicMirror² is opensource and free. That doesn't mean we don't need any money.
|
||||
|
|
|
@ -32,16 +32,16 @@
|
|||
var 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}`));
|
||||
});
|
||||
});
|
||||
|
@ -96,7 +96,6 @@
|
|||
console.log(`There something wrong. The clientonly is not running code ${code}`);
|
||||
}
|
||||
});
|
||||
|
||||
})
|
||||
.catch(function (reason) {
|
||||
fail(`Unable to connect to server: (${reason})`);
|
||||
|
@ -104,4 +103,4 @@
|
|||
} else {
|
||||
fail();
|
||||
}
|
||||
}());
|
||||
})();
|
||||
|
|
|
@ -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.10.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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,21 +2,14 @@
|
|||
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");
|
||||
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"),
|
||||
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");
|
||||
}
|
||||
|
||||
|
@ -24,10 +17,7 @@
|
|||
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"),
|
||||
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");
|
||||
}
|
||||
|
||||
|
@ -35,10 +25,7 @@
|
|||
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"),
|
||||
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");
|
||||
}
|
||||
|
||||
|
@ -46,42 +33,26 @@
|
|||
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");
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
}
|
||||
|
|
44
js/app.js
44
js/app.js
|
@ -44,7 +44,7 @@ process.on("uncaughtException", function (err) {
|
|||
|
||||
/* App - The core app.
|
||||
*/
|
||||
var App = function() {
|
||||
var App = function () {
|
||||
var nodeHelpers = [];
|
||||
|
||||
/* loadConfig(callback)
|
||||
|
@ -53,14 +53,14 @@ var App = function() {
|
|||
*
|
||||
* argument callback function - The callback function.
|
||||
*/
|
||||
var loadConfig = function(callback) {
|
||||
var loadConfig = function (callback) {
|
||||
console.log("Loading config ...");
|
||||
var defaults = require(__dirname + "/defaults.js");
|
||||
|
||||
// For this check proposed to TestSuite
|
||||
// https://forum.magicmirror.builders/topic/1456/test-suite-for-magicmirror/8
|
||||
var configFilename = path.resolve(global.root_path + "/config/config.js");
|
||||
if (typeof(global.configuration_file) !== "undefined") {
|
||||
if (typeof global.configuration_file !== "undefined") {
|
||||
configFilename = path.resolve(global.configuration_file);
|
||||
}
|
||||
|
||||
|
@ -82,23 +82,19 @@ var App = function() {
|
|||
}
|
||||
};
|
||||
|
||||
var checkDeprecatedOptions = function(userConfig) {
|
||||
var checkDeprecatedOptions = function (userConfig) {
|
||||
var deprecated = require(global.root_path + "/js/deprecated.js");
|
||||
var deprecatedOptions = deprecated.configs;
|
||||
|
||||
var usedDeprecated = [];
|
||||
|
||||
deprecatedOptions.forEach(function(option) {
|
||||
deprecatedOptions.forEach(function (option) {
|
||||
if (userConfig.hasOwnProperty(option)) {
|
||||
usedDeprecated.push(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.")
|
||||
);
|
||||
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."));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -107,8 +103,7 @@ var App = function() {
|
|||
*
|
||||
* argument module string - The name of the module (including subpath).
|
||||
*/
|
||||
var loadModule = function(module, callback) {
|
||||
|
||||
var loadModule = function (module, callback) {
|
||||
var elements = module.split("/");
|
||||
var moduleName = elements[elements.length - 1];
|
||||
var moduleFolder = __dirname + "/../modules/" + module;
|
||||
|
@ -156,13 +151,13 @@ var App = function() {
|
|||
*
|
||||
* argument module string - The name of the module (including subpath).
|
||||
*/
|
||||
var loadModules = function(modules, callback) {
|
||||
var loadModules = function (modules, callback) {
|
||||
console.log("Loading module helpers ...");
|
||||
|
||||
var loadNextModule = function() {
|
||||
var loadNextModule = function () {
|
||||
if (modules.length > 0) {
|
||||
var nextModule = modules[0];
|
||||
loadModule(nextModule, function() {
|
||||
loadModule(nextModule, function () {
|
||||
modules = modules.slice(1);
|
||||
loadNextModule();
|
||||
});
|
||||
|
@ -205,9 +200,8 @@ var App = function() {
|
|||
*
|
||||
* argument callback function - The callback function.
|
||||
*/
|
||||
this.start = function(callback) {
|
||||
|
||||
loadConfig(function(c) {
|
||||
this.start = function (callback) {
|
||||
loadConfig(function (c) {
|
||||
config = c;
|
||||
|
||||
var modules = [];
|
||||
|
@ -219,8 +213,8 @@ var App = function() {
|
|||
}
|
||||
}
|
||||
|
||||
loadModules(modules, function() {
|
||||
var server = new Server(config, function(app, io) {
|
||||
loadModules(modules, function () {
|
||||
var server = new Server(config, function (app, io) {
|
||||
console.log("Server started ...");
|
||||
|
||||
for (var h in nodeHelpers) {
|
||||
|
@ -245,7 +239,7 @@ var App = function() {
|
|||
* This calls each node_helper's STOP() function, if it exists.
|
||||
* Added to fix #1056
|
||||
*/
|
||||
this.stop = function() {
|
||||
this.stop = function () {
|
||||
for (var h in nodeHelpers) {
|
||||
var nodeHelper = nodeHelpers[h];
|
||||
if (typeof nodeHelper.stop === "function") {
|
||||
|
@ -262,7 +256,9 @@ var App = function() {
|
|||
*/
|
||||
process.on("SIGINT", () => {
|
||||
console.log("[SIGINT] Received. Shutting down server...");
|
||||
setTimeout(() => { process.exit(0); }, 3000); // Force quit after 3 seconds
|
||||
setTimeout(() => {
|
||||
process.exit(0);
|
||||
}, 3000); // Force quit after 3 seconds
|
||||
this.stop();
|
||||
process.exit(0);
|
||||
});
|
||||
|
@ -271,7 +267,9 @@ var App = function() {
|
|||
*/
|
||||
process.on("SIGTERM", () => {
|
||||
console.log("[SIGTERM] Received. Shutting down server...");
|
||||
setTimeout(() => { process.exit(0); }, 3000); // Force quit after 3 seconds
|
||||
setTimeout(() => {
|
||||
process.exit(0);
|
||||
}, 3000); // Force quit after 3 seconds
|
||||
this.stop();
|
||||
process.exit(0);
|
||||
});
|
||||
|
|
|
@ -53,13 +53,15 @@ function checkConfigFile() {
|
|||
console.info(Utils.colors.info("Checking file... "), configFileName);
|
||||
// I'm not sure if all ever is utf-8
|
||||
fs.readFile(configFileName, "utf-8", function (err, data) {
|
||||
if (err) { throw err; }
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
const messages = linter.verify(data, config);
|
||||
if (messages.length === 0) {
|
||||
console.log("Your configuration file doesn't contain syntax errors :)");
|
||||
return true;
|
||||
} else {
|
||||
messages.forEach(error => {
|
||||
messages.forEach((error) => {
|
||||
console.log("Line", error.line, "col", error.column, error.message);
|
||||
});
|
||||
}
|
||||
|
|
38
js/class.js
38
js/class.js
|
@ -9,10 +9,14 @@
|
|||
*/
|
||||
(function () {
|
||||
var initializing = false;
|
||||
var fnTest = /xyz/.test(function () { xyz; }) ? /\b_super\b/ : /.*/;
|
||||
var 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) {
|
||||
|
@ -32,23 +36,25 @@
|
|||
// Copy the properties over onto the new prototype
|
||||
for (var name in prop) {
|
||||
// Check if we're overwriting an existing function
|
||||
prototype[name] = typeof prop[name] === "function" &&
|
||||
typeof _super[name] === "function" && fnTest.test(prop[name]) ? (function (name, fn) {
|
||||
return function () {
|
||||
var tmp = this._super;
|
||||
prototype[name] =
|
||||
typeof prop[name] === "function" && typeof _super[name] === "function" && fnTest.test(prop[name])
|
||||
? (function (name, fn) {
|
||||
return function () {
|
||||
var tmp = this._super;
|
||||
|
||||
// Add a new ._super() method that is the same method
|
||||
// but on the super-class
|
||||
this._super = _super[name];
|
||||
// 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
|
||||
var 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
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*/
|
||||
var address = "localhost";
|
||||
var port = 8080;
|
||||
if (typeof(mmPort) !== "undefined") {
|
||||
if (typeof mmPort !== "undefined") {
|
||||
port = mmPort;
|
||||
}
|
||||
var defaults = {
|
||||
|
@ -68,14 +68,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;
|
||||
}
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
*/
|
||||
|
||||
var deprecated = {
|
||||
configs: ["kioskmode"],
|
||||
configs: ["kioskmode"]
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = deprecated;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = deprecated;
|
||||
}
|
||||
|
|
|
@ -62,21 +62,21 @@ function createWindow() {
|
|||
}
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
@ -85,17 +85,17 @@ function createWindow() {
|
|||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
app.on("ready", function() {
|
||||
app.on("ready", function () {
|
||||
console.log("Launching application.");
|
||||
createWindow();
|
||||
});
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on("window-all-closed", function() {
|
||||
app.on("window-all-closed", function () {
|
||||
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) {
|
||||
|
@ -112,7 +112,9 @@ app.on("activate", function() {
|
|||
app.on("before-quit", (event) => {
|
||||
console.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);
|
||||
});
|
||||
|
@ -120,7 +122,7 @@ app.on("before-quit", (event) => {
|
|||
// 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) {
|
||||
core.start(function (c) {
|
||||
config = c;
|
||||
});
|
||||
}
|
||||
|
|
114
js/loader.js
114
js/loader.js
|
@ -6,8 +6,7 @@
|
|||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
var Loader = (function() {
|
||||
|
||||
var Loader = (function () {
|
||||
/* Create helper variables */
|
||||
|
||||
var loadedModuleFiles = [];
|
||||
|
@ -19,14 +18,13 @@ var Loader = (function() {
|
|||
/* loadModules()
|
||||
* Loops thru all modules and requests load for every module.
|
||||
*/
|
||||
var loadModules = function() {
|
||||
|
||||
var loadModules = function () {
|
||||
var moduleData = getModuleData();
|
||||
|
||||
var loadNextModule = function() {
|
||||
var loadNextModule = function () {
|
||||
if (moduleData.length > 0) {
|
||||
var nextModule = moduleData[0];
|
||||
loadModule(nextModule, function() {
|
||||
loadModule(nextModule, function () {
|
||||
moduleData = moduleData.slice(1);
|
||||
loadNextModule();
|
||||
});
|
||||
|
@ -35,11 +33,10 @@ 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();
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -49,7 +46,7 @@ var Loader = (function() {
|
|||
/* startModules()
|
||||
* Loops thru all modules and requests start for every module.
|
||||
*/
|
||||
var startModules = function() {
|
||||
var startModules = function () {
|
||||
for (var m in moduleObjects) {
|
||||
var module = moduleObjects[m];
|
||||
module.start();
|
||||
|
@ -64,7 +61,7 @@ var Loader = (function() {
|
|||
*
|
||||
* return array - module data as configured in config
|
||||
*/
|
||||
var getAllModules = function() {
|
||||
var getAllModules = function () {
|
||||
return config.modules;
|
||||
};
|
||||
|
||||
|
@ -73,7 +70,7 @@ var Loader = (function() {
|
|||
*
|
||||
* return array - Module information.
|
||||
*/
|
||||
var getModuleData = function() {
|
||||
var getModuleData = function () {
|
||||
var modules = getAllModules();
|
||||
var moduleFiles = [];
|
||||
|
||||
|
@ -97,12 +94,12 @@ var Loader = (function() {
|
|||
index: m,
|
||||
identifier: "module_" + m + "_" + module,
|
||||
name: moduleName,
|
||||
path: moduleFolder + "/" ,
|
||||
path: moduleFolder + "/",
|
||||
file: moduleName + ".js",
|
||||
position: moduleData.position,
|
||||
header: moduleData.header,
|
||||
config: moduleData.config,
|
||||
classes: (typeof moduleData.classes !== "undefined") ? moduleData.classes + " " + module : module
|
||||
classes: typeof moduleData.classes !== "undefined" ? moduleData.classes + " " + module : module
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -115,13 +112,13 @@ var Loader = (function() {
|
|||
* argument callback function - Function called when done.
|
||||
* argument module object - Information about the module we want to load.
|
||||
*/
|
||||
var loadModule = function(module, callback) {
|
||||
var loadModule = function (module, callback) {
|
||||
var url = module.path + "/" + module.file;
|
||||
|
||||
var afterLoad = function() {
|
||||
var afterLoad = function () {
|
||||
var moduleObject = Module.create(module.name);
|
||||
if (moduleObject) {
|
||||
bootstrapModule(module, moduleObject, function() {
|
||||
bootstrapModule(module, moduleObject, function () {
|
||||
callback();
|
||||
});
|
||||
} else {
|
||||
|
@ -132,7 +129,7 @@ var Loader = (function() {
|
|||
if (loadedModuleFiles.indexOf(url) !== -1) {
|
||||
afterLoad();
|
||||
} else {
|
||||
loadFile(url, function() {
|
||||
loadFile(url, function () {
|
||||
loadedModuleFiles.push(url);
|
||||
afterLoad();
|
||||
});
|
||||
|
@ -146,16 +143,16 @@ var Loader = (function() {
|
|||
* argument mObj object - Modules instance.
|
||||
* argument callback function - Function called when done.
|
||||
*/
|
||||
var bootstrapModule = function(module, mObj, callback) {
|
||||
var 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();
|
||||
|
@ -170,52 +167,58 @@ var Loader = (function() {
|
|||
* argument fileName string - Path of the file we want to load.
|
||||
* argument callback function - Function called when done.
|
||||
*/
|
||||
var loadFile = function(fileName, callback) {
|
||||
|
||||
var loadFile = function (fileName, callback) {
|
||||
var extension = fileName.slice((Math.max(0, fileName.lastIndexOf(".")) || Infinity) + 1);
|
||||
|
||||
switch (extension.toLowerCase()) {
|
||||
case "js":
|
||||
Log.log("Load script: " + fileName);
|
||||
var script = document.createElement("script");
|
||||
script.type = "text/javascript";
|
||||
script.src = fileName;
|
||||
script.onload = function() {
|
||||
if (typeof callback === "function") {callback();}
|
||||
};
|
||||
script.onerror = function() {
|
||||
console.error("Error on loading script:", fileName);
|
||||
if (typeof callback === "function") {callback();}
|
||||
};
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
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("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();
|
||||
},
|
||||
|
||||
|
@ -227,8 +230,7 @@ var Loader = (function() {
|
|||
* argument module Module Object - the module that calls the loadFile function.
|
||||
* argument callback function - 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();
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const Log = (function() {
|
||||
const Log = (function () {
|
||||
return {
|
||||
info: Function.prototype.bind.call(console.info, console),
|
||||
log: Function.prototype.bind.call(console.log, 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),
|
||||
|
|
139
js/main.js
139
js/main.js
|
@ -6,8 +6,7 @@
|
|||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
var MM = (function() {
|
||||
|
||||
var MM = (function () {
|
||||
var modules = [];
|
||||
|
||||
/* Private Methods */
|
||||
|
@ -16,10 +15,10 @@ var MM = (function() {
|
|||
* Create dom objects for all modules that
|
||||
* are configured for a specific position.
|
||||
*/
|
||||
var createDomObjects = function() {
|
||||
var createDomObjects = function () {
|
||||
var domCreationPromises = [];
|
||||
|
||||
modules.forEach(function(module) {
|
||||
modules.forEach(function (module) {
|
||||
if (typeof module.data.position !== "string") {
|
||||
return;
|
||||
}
|
||||
|
@ -52,14 +51,16 @@ var MM = (function() {
|
|||
|
||||
var 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");
|
||||
});
|
||||
};
|
||||
|
@ -69,8 +70,8 @@ var MM = (function() {
|
|||
*
|
||||
* argument position string - The name of the position.
|
||||
*/
|
||||
var selectWrapper = function(position) {
|
||||
var classes = position.replace("_"," ");
|
||||
var selectWrapper = function (position) {
|
||||
var classes = position.replace("_", " ");
|
||||
var parentWrapper = document.getElementsByClassName(classes);
|
||||
if (parentWrapper.length > 0) {
|
||||
var wrapper = parentWrapper[0].getElementsByClassName("container");
|
||||
|
@ -88,7 +89,7 @@ var MM = (function() {
|
|||
* argument sender Module - The module that sent the notification.
|
||||
* argument sendTo Module - The module to send the notification to. (optional)
|
||||
*/
|
||||
var sendNotification = function(notification, payload, sender, sendTo) {
|
||||
var sendNotification = function (notification, payload, sender, sendTo) {
|
||||
for (var m in modules) {
|
||||
var module = modules[m];
|
||||
if (module !== sender && (!sendTo || module === sendTo)) {
|
||||
|
@ -105,8 +106,8 @@ var MM = (function() {
|
|||
*
|
||||
* return Promise - Resolved when the dom is fully updated.
|
||||
*/
|
||||
var updateDom = function(module, speed) {
|
||||
return new Promise(function(resolve) {
|
||||
var updateDom = function (module, speed) {
|
||||
return new Promise(function (resolve) {
|
||||
var newContentPromise = module.getDom();
|
||||
var newHeader = module.getHeader();
|
||||
|
||||
|
@ -115,11 +116,13 @@ var MM = (function() {
|
|||
newContentPromise = Promise.resolve(newContentPromise);
|
||||
}
|
||||
|
||||
newContentPromise.then(function(newContent) {
|
||||
var updatePromise = updateDomWithContent(module, speed, newHeader, newContent);
|
||||
newContentPromise
|
||||
.then(function (newContent) {
|
||||
var updatePromise = updateDomWithContent(module, speed, newHeader, newContent);
|
||||
|
||||
updatePromise.then(resolve).catch(Log.error);
|
||||
}).catch(Log.error);
|
||||
updatePromise.then(resolve).catch(Log.error);
|
||||
})
|
||||
.catch(Log.error);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -133,8 +136,8 @@ var MM = (function() {
|
|||
*
|
||||
* return Promise - Resolved when the module dom has been updated.
|
||||
*/
|
||||
var updateDomWithContent = function(module, speed, newHeader, newContent) {
|
||||
return new Promise(function(resolve) {
|
||||
var updateDomWithContent = function (module, speed, newHeader, newContent) {
|
||||
return new Promise(function (resolve) {
|
||||
if (module.hidden || !speed) {
|
||||
updateModuleContent(module, newHeader, newContent);
|
||||
resolve();
|
||||
|
@ -152,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);
|
||||
|
@ -171,7 +174,7 @@ var MM = (function() {
|
|||
*
|
||||
* return bool - Does the module need an update?
|
||||
*/
|
||||
var moduleNeedsUpdate = function(module, newHeader, newContent) {
|
||||
var moduleNeedsUpdate = function (module, newHeader, newContent) {
|
||||
var moduleWrapper = document.getElementById(module.identifier);
|
||||
if (moduleWrapper === null) {
|
||||
return false;
|
||||
|
@ -201,9 +204,11 @@ var MM = (function() {
|
|||
* argument newHeader String - The new header that is generated.
|
||||
* argument newContent Domobject - The new content that is generated.
|
||||
*/
|
||||
var updateModuleContent = function(module, newHeader, newContent) {
|
||||
var updateModuleContent = function (module, newHeader, newContent) {
|
||||
var moduleWrapper = document.getElementById(module.identifier);
|
||||
if (moduleWrapper === null) {return;}
|
||||
if (moduleWrapper === null) {
|
||||
return;
|
||||
}
|
||||
var headerWrapper = moduleWrapper.getElementsByClassName("module-header");
|
||||
var contentWrapper = moduleWrapper.getElementsByClassName("module-content");
|
||||
|
||||
|
@ -221,7 +226,7 @@ var MM = (function() {
|
|||
* argument speed Number - The speed of the hide animation.
|
||||
* argument callback function - Called when the animation is done.
|
||||
*/
|
||||
var hideModule = function(module, speed, callback, options) {
|
||||
var hideModule = function (module, speed, callback, options) {
|
||||
options = options || {};
|
||||
|
||||
// set lockString if set in options.
|
||||
|
@ -238,7 +243,7 @@ var MM = (function() {
|
|||
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
|
||||
|
@ -247,11 +252,15 @@ 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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -262,13 +271,13 @@ var MM = (function() {
|
|||
* argument speed Number - The speed of the show animation.
|
||||
* argument callback function - Called when the animation is done.
|
||||
*/
|
||||
var showModule = function(module, speed, callback, options) {
|
||||
var showModule = function (module, speed, callback, options) {
|
||||
options = options || {};
|
||||
|
||||
// remove lockString if set in options.
|
||||
if (options.lockString) {
|
||||
var index = module.lockStrings.indexOf(options.lockString);
|
||||
if ( index !== -1) {
|
||||
if (index !== -1) {
|
||||
module.lockStrings.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
@ -301,12 +310,16 @@ var MM = (function() {
|
|||
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(); }
|
||||
if (typeof callback === "function") {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -321,15 +334,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 updateWrapperStates = function () {
|
||||
var positions = ["top_bar", "top_left", "top_center", "top_right", "upper_third", "middle_center", "lower_third", "bottom_left", "bottom_center", "bottom_right", "bottom_bar", "fullscreen_above", "fullscreen_below"];
|
||||
|
||||
positions.forEach(function(position) {
|
||||
positions.forEach(function (position) {
|
||||
var wrapper = selectWrapper(position);
|
||||
var moduleWrappers = wrapper.getElementsByClassName("module");
|
||||
|
||||
var showWrapper = false;
|
||||
Array.prototype.forEach.call(moduleWrappers, function(moduleWrapper) {
|
||||
Array.prototype.forEach.call(moduleWrappers, function (moduleWrapper) {
|
||||
if (moduleWrapper.style.position === "" || moduleWrapper.style.position === "static") {
|
||||
showWrapper = true;
|
||||
}
|
||||
|
@ -342,7 +355,7 @@ var MM = (function() {
|
|||
/* loadConfig()
|
||||
* Loads the core config and combines it with de system defaults.
|
||||
*/
|
||||
var loadConfig = function() {
|
||||
var loadConfig = function () {
|
||||
// FIXME: Think about how to pass config around without breaking tests
|
||||
/* eslint-disable */
|
||||
if (typeof config === "undefined") {
|
||||
|
@ -360,8 +373,7 @@ var MM = (function() {
|
|||
*
|
||||
* argument modules array - Array of modules.
|
||||
*/
|
||||
var setSelectionMethodsForModules = function(modules) {
|
||||
|
||||
var setSelectionMethodsForModules = function (modules) {
|
||||
/* withClass(className)
|
||||
* calls modulesByClass to filter modules with the specified classes.
|
||||
*
|
||||
|
@ -369,7 +381,7 @@ var MM = (function() {
|
|||
*
|
||||
* return array - Filtered collection of modules.
|
||||
*/
|
||||
var withClass = function(className) {
|
||||
var withClass = function (className) {
|
||||
return modulesByClass(className, true);
|
||||
};
|
||||
|
||||
|
@ -380,7 +392,7 @@ var MM = (function() {
|
|||
*
|
||||
* return array - Filtered collection of modules.
|
||||
*/
|
||||
var exceptWithClass = function(className) {
|
||||
var exceptWithClass = function (className) {
|
||||
return modulesByClass(className, false);
|
||||
};
|
||||
|
||||
|
@ -392,13 +404,13 @@ var MM = (function() {
|
|||
*
|
||||
* return array - Filtered collection of modules.
|
||||
*/
|
||||
var modulesByClass = function(className, include) {
|
||||
var modulesByClass = function (className, include) {
|
||||
var searchClasses = className;
|
||||
if (typeof className === "string") {
|
||||
searchClasses = className.split(" ");
|
||||
}
|
||||
|
||||
var newModules = modules.filter(function(module) {
|
||||
var newModules = modules.filter(function (module) {
|
||||
var classes = module.data.classes.toLowerCase().split(" ");
|
||||
|
||||
for (var c in searchClasses) {
|
||||
|
@ -422,8 +434,8 @@ var MM = (function() {
|
|||
*
|
||||
* return array - Filtered collection of modules.
|
||||
*/
|
||||
var exceptModule = function(module) {
|
||||
var newModules = modules.filter(function(mod) {
|
||||
var exceptModule = function (module) {
|
||||
var newModules = modules.filter(function (mod) {
|
||||
return mod.identifier !== module.identifier;
|
||||
});
|
||||
|
||||
|
@ -436,16 +448,24 @@ var MM = (function() {
|
|||
*
|
||||
* argument callback function - The function to execute with the module as an argument.
|
||||
*/
|
||||
var enumerate = function(callback) {
|
||||
modules.map(function(module) {
|
||||
var enumerate = function (callback) {
|
||||
modules.map(function (module) {
|
||||
callback(module);
|
||||
});
|
||||
};
|
||||
|
||||
if (typeof modules.withClass === "undefined") { Object.defineProperty(modules, "withClass", {value: withClass, enumerable: false}); }
|
||||
if (typeof modules.exceptWithClass === "undefined") { Object.defineProperty(modules, "exceptWithClass", {value: exceptWithClass, enumerable: false}); }
|
||||
if (typeof modules.exceptModule === "undefined") { Object.defineProperty(modules, "exceptModule", {value: exceptModule, enumerable: false}); }
|
||||
if (typeof modules.enumerate === "undefined") { Object.defineProperty(modules, "enumerate", {value: enumerate, enumerable: false}); }
|
||||
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 {
|
||||
|
@ -454,7 +474,7 @@ var MM = (function() {
|
|||
/* init()
|
||||
* Main init method.
|
||||
*/
|
||||
init: function() {
|
||||
init: function () {
|
||||
Log.info("Initializing MagicMirror.");
|
||||
loadConfig();
|
||||
Translator.loadCoreTranslations(config.language);
|
||||
|
@ -466,7 +486,7 @@ var MM = (function() {
|
|||
*
|
||||
* argument moduleObjects array<Module> - All module instances.
|
||||
*/
|
||||
modulesStarted: function(moduleObjects) {
|
||||
modulesStarted: function (moduleObjects) {
|
||||
modules = [];
|
||||
for (var m in moduleObjects) {
|
||||
var module = moduleObjects[m];
|
||||
|
@ -486,7 +506,7 @@ var MM = (function() {
|
|||
* argument payload mixed - The payload of the notification.
|
||||
* argument sender Module - 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;
|
||||
|
@ -512,7 +532,7 @@ var MM = (function() {
|
|||
* argument module Module - The module that needs an update.
|
||||
* argument speed Number - The number of microseconds for the animation. (optional)
|
||||
*/
|
||||
updateDom: function(module, speed) {
|
||||
updateDom: function (module, speed) {
|
||||
if (!(module instanceof Module)) {
|
||||
Log.error("updateDom: Sender should be a module.");
|
||||
return;
|
||||
|
@ -527,7 +547,7 @@ var MM = (function() {
|
|||
*
|
||||
* return array - A collection of all modules currently active.
|
||||
*/
|
||||
getModules: function() {
|
||||
getModules: function () {
|
||||
setSelectionMethodsForModules(modules);
|
||||
return modules;
|
||||
},
|
||||
|
@ -540,7 +560,7 @@ var MM = (function() {
|
|||
* argument callback function - Called when the animation is done.
|
||||
* argument options object - 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);
|
||||
},
|
||||
|
@ -553,18 +573,17 @@ var MM = (function() {
|
|||
* argument callback function - Called when the animation is done.
|
||||
* argument options object - Optional settings for the hide 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");
|
||||
|
|
67
js/module.js
67
js/module.js
|
@ -7,7 +7,6 @@
|
|||
* MIT Licensed.
|
||||
*/
|
||||
var Module = Class.extend({
|
||||
|
||||
/*********************************************************
|
||||
* All methods (and properties) below can be subclassed. *
|
||||
*********************************************************/
|
||||
|
@ -80,7 +79,7 @@ var Module = Class.extend({
|
|||
*/
|
||||
getDom: function () {
|
||||
var self = this;
|
||||
return new Promise(function(resolve) {
|
||||
return new Promise(function (resolve) {
|
||||
var div = document.createElement("div");
|
||||
var template = self.getTemplate();
|
||||
var templateData = self.getTemplateData();
|
||||
|
@ -126,7 +125,7 @@ var Module = Class.extend({
|
|||
* return 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()
|
||||
|
@ -161,18 +160,18 @@ var Module = Class.extend({
|
|||
|
||||
* @returns 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) {
|
||||
this._nunjucksEnvironment.addFilter("translate", function (str) {
|
||||
return self.translate(str);
|
||||
});
|
||||
|
||||
|
@ -313,7 +312,9 @@ var Module = Class.extend({
|
|||
|
||||
// The variable `first` will contain the first
|
||||
// defined translation after the following line.
|
||||
for (var first in translations) { break; }
|
||||
for (var first in translations) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (translations) {
|
||||
var translationFile = translations[lang] || undefined;
|
||||
|
@ -337,11 +338,11 @@ var Module = Class.extend({
|
|||
* 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)
|
||||
* argument defaultValueOrVariables string/object - The default value or variables for translating. (Optional)
|
||||
* argument defaultValue string - The default value with variables. (Optional)
|
||||
*/
|
||||
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 || "";
|
||||
|
@ -386,17 +387,22 @@ var Module = Class.extend({
|
|||
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(
|
||||
self,
|
||||
speed,
|
||||
function () {
|
||||
self.suspend();
|
||||
callback();
|
||||
},
|
||||
options
|
||||
);
|
||||
},
|
||||
|
||||
/* showModule(module, speed, callback)
|
||||
|
@ -409,24 +415,28 @@ var Module = Class.extend({
|
|||
show: 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.showModule(this, speed, function () {
|
||||
self.resume();
|
||||
callback;
|
||||
}, options);
|
||||
MM.showModule(
|
||||
this,
|
||||
speed,
|
||||
function () {
|
||||
self.resume();
|
||||
callback;
|
||||
},
|
||||
options
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Module.definitions = {};
|
||||
|
||||
Module.create = function (name) {
|
||||
|
||||
// Make sure module definition is available.
|
||||
if (!Module.definitions[name]) {
|
||||
return;
|
||||
|
@ -442,11 +452,11 @@ Module.create = function (name) {
|
|||
};
|
||||
|
||||
/* cmpVersions(a,b)
|
||||
* Compare two semantic version numbers and return the difference.
|
||||
*
|
||||
* argument a string - Version number a.
|
||||
* argument a string - Version number 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+)+$/;
|
||||
|
@ -464,7 +474,6 @@ function cmpVersions(a, b) {
|
|||
}
|
||||
|
||||
Module.register = function (name, moduleDefinition) {
|
||||
|
||||
if (moduleDefinition.requiresVersion) {
|
||||
Log.log("Check MagicMirror version for module '" + name + "' - Minimum version: " + moduleDefinition.requiresVersion + " - Current version: " + window.version);
|
||||
if (cmpVersions(window.version, moduleDefinition.requiresVersion) >= 0) {
|
||||
|
|
|
@ -8,16 +8,16 @@ const Class = require("./class.js");
|
|||
const express = require("express");
|
||||
|
||||
var NodeHelper = Class.extend({
|
||||
init: function() {
|
||||
init: function () {
|
||||
console.log("Initializing new module helper ...");
|
||||
},
|
||||
|
||||
loaded: function(callback) {
|
||||
loaded: function (callback) {
|
||||
console.log("Module helper loaded: " + this.name);
|
||||
callback();
|
||||
},
|
||||
|
||||
start: function() {
|
||||
start: function () {
|
||||
console.log("Starting module helper: " + this.name);
|
||||
},
|
||||
|
||||
|
@ -27,7 +27,7 @@ var NodeHelper = Class.extend({
|
|||
* gracefully exit the module.
|
||||
*
|
||||
*/
|
||||
stop: function() {
|
||||
stop: function () {
|
||||
console.log("Stopping module helper: " + this.name);
|
||||
},
|
||||
|
||||
|
@ -37,7 +37,7 @@ var NodeHelper = Class.extend({
|
|||
* argument notification string - The identifier of the notification.
|
||||
* argument payload mixed - The payload of the notification.
|
||||
*/
|
||||
socketNotificationReceived: function(notification, payload) {
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
console.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload);
|
||||
},
|
||||
|
||||
|
@ -46,7 +46,7 @@ var NodeHelper = Class.extend({
|
|||
*
|
||||
* argument name string - Module name.
|
||||
*/
|
||||
setName: function(name) {
|
||||
setName: function (name) {
|
||||
this.name = name;
|
||||
},
|
||||
|
||||
|
@ -55,7 +55,7 @@ var NodeHelper = Class.extend({
|
|||
*
|
||||
* argument path string - Module path.
|
||||
*/
|
||||
setPath: function(path) {
|
||||
setPath: function (path) {
|
||||
this.path = path;
|
||||
},
|
||||
|
||||
|
@ -65,7 +65,7 @@ var NodeHelper = Class.extend({
|
|||
* argument notification string - The identifier of the notification.
|
||||
* argument payload mixed - The payload of the notification.
|
||||
*/
|
||||
sendSocketNotification: function(notification, payload) {
|
||||
sendSocketNotification: function (notification, payload) {
|
||||
this.io.of(this.name).emit(notification, payload);
|
||||
},
|
||||
|
||||
|
@ -75,7 +75,7 @@ var NodeHelper = Class.extend({
|
|||
*
|
||||
* argument app Express app - The Express app object.
|
||||
*/
|
||||
setExpressApp: function(app) {
|
||||
setExpressApp: function (app) {
|
||||
this.expressApp = app;
|
||||
|
||||
var publicPath = this.path + "/public";
|
||||
|
@ -88,16 +88,16 @@ var NodeHelper = Class.extend({
|
|||
*
|
||||
* argument io Socket.io - The Socket io object.
|
||||
*/
|
||||
setSocketIO: function(io) {
|
||||
setSocketIO: function (io) {
|
||||
var self = this;
|
||||
self.io = io;
|
||||
|
||||
console.log("Connecting socket for: " + this.name);
|
||||
var namespace = this.name;
|
||||
io.of(namespace).on("connection", function(socket) {
|
||||
io.of(namespace).on("connection", function (socket) {
|
||||
// add a catch all event.
|
||||
var onevent = socket.onevent;
|
||||
socket.onevent = function(packet) {
|
||||
socket.onevent = function (packet) {
|
||||
var args = packet.data || [];
|
||||
onevent.call(this, packet); // original call
|
||||
packet.data = ["*"].concat(args);
|
||||
|
@ -105,7 +105,7 @@ var NodeHelper = Class.extend({
|
|||
};
|
||||
|
||||
// register catch all.
|
||||
socket.on("*", function(notification, payload) {
|
||||
socket.on("*", function (notification, payload) {
|
||||
if (notification !== "*") {
|
||||
//console.log('received message in namespace: ' + namespace);
|
||||
self.socketNotificationReceived(notification, payload);
|
||||
|
@ -115,9 +115,11 @@ var NodeHelper = Class.extend({
|
|||
}
|
||||
});
|
||||
|
||||
NodeHelper.create = function(moduleDefinition) {
|
||||
NodeHelper.create = function (moduleDefinition) {
|
||||
return NodeHelper.extend(moduleDefinition);
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = NodeHelper;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = NodeHelper;
|
||||
}
|
||||
|
|
21
js/server.js
21
js/server.js
|
@ -13,21 +13,20 @@ var fs = require("fs");
|
|||
var helmet = require("helmet");
|
||||
var Utils = require(__dirname + "/utils.js");
|
||||
|
||||
var Server = function(config, callback) {
|
||||
|
||||
var Server = function (config, callback) {
|
||||
var port = config.port;
|
||||
if (process.env.MM_PORT) {
|
||||
port = process.env.MM_PORT;
|
||||
}
|
||||
|
||||
var server = null;
|
||||
if(config.useHttps){
|
||||
if (config.useHttps) {
|
||||
var options = {
|
||||
key: fs.readFileSync(config.httpsPrivateKey),
|
||||
cert: fs.readFileSync(config.httpsCertificate)
|
||||
};
|
||||
server = require("https").Server(options, app);
|
||||
}else{
|
||||
} else {
|
||||
server = require("http").Server(app);
|
||||
}
|
||||
var io = require("socket.io")(server);
|
||||
|
@ -40,8 +39,8 @@ var Server = function(config, callback) {
|
|||
console.info(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) {
|
||||
var result = ipfilter(config.ipWhitelist, { mode: config.ipWhitelist.length === 0 ? "deny" : "allow", log: false })(req, res, function (err) {
|
||||
if (err === undefined) {
|
||||
return next();
|
||||
}
|
||||
|
@ -59,20 +58,20 @@ var Server = function(config, callback) {
|
|||
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) {
|
||||
var html = fs.readFileSync(path.resolve(global.root_path + "/index.html"), { encoding: "utf8" });
|
||||
html = html.replace("#VERSION#", global.version);
|
||||
|
||||
var configFile = "config/config.js";
|
||||
if (typeof(global.configuration_file) !== "undefined") {
|
||||
if (typeof global.configuration_file !== "undefined") {
|
||||
configFile = global.configuration_file;
|
||||
}
|
||||
html = html.replace("#CONFIG_FILE#", configFile);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
var MMSocket = function(moduleName) {
|
||||
var MMSocket = function (moduleName) {
|
||||
var self = this;
|
||||
|
||||
if (typeof moduleName !== "string") {
|
||||
|
@ -17,16 +17,16 @@ var MMSocket = function(moduleName) {
|
|||
|
||||
// Private Methods
|
||||
var base = "/";
|
||||
if ((typeof config !== "undefined") && (typeof config.basePath !== "undefined")) {
|
||||
if (typeof config !== "undefined" && typeof config.basePath !== "undefined") {
|
||||
base = config.basePath;
|
||||
}
|
||||
self.socket = io("/" + self.moduleName, {
|
||||
path: base + "socket.io"
|
||||
});
|
||||
var notificationCallback = function() {};
|
||||
var notificationCallback = function () {};
|
||||
|
||||
var onevent = self.socket.onevent;
|
||||
self.socket.onevent = function(packet) {
|
||||
self.socket.onevent = function (packet) {
|
||||
var args = packet.data || [];
|
||||
onevent.call(this, packet); // original call
|
||||
packet.data = ["*"].concat(args);
|
||||
|
@ -34,18 +34,18 @@ var MMSocket = function(moduleName) {
|
|||
};
|
||||
|
||||
// register catch all.
|
||||
self.socket.on("*", function(notification, payload) {
|
||||
self.socket.on("*", function (notification, payload) {
|
||||
if (notification !== "*") {
|
||||
notificationCallback(notification, payload);
|
||||
}
|
||||
});
|
||||
|
||||
// Public Methods
|
||||
this.setNotificationCallback = function(callback) {
|
||||
this.setNotificationCallback = function (callback) {
|
||||
notificationCallback = callback;
|
||||
};
|
||||
|
||||
this.sendNotification = function(notification, payload) {
|
||||
this.sendNotification = function (notification, payload) {
|
||||
if (typeof payload === "undefined") {
|
||||
payload = {};
|
||||
}
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
* By Christopher Fenner https://github.com/CFenner
|
||||
* MIT Licensed.
|
||||
*/
|
||||
var Translator = (function() {
|
||||
|
||||
var Translator = (function () {
|
||||
/* loadJSON(file, callback)
|
||||
* Load a JSON file via XHR.
|
||||
*
|
||||
|
@ -62,7 +61,7 @@ var Translator = (function() {
|
|||
currentChar = str[i];
|
||||
nextChar = str[i + 1];
|
||||
|
||||
if (!insideComment && currentChar === "\"") {
|
||||
if (!insideComment && currentChar === '"') {
|
||||
var escaped = str[i - 1] === "\\" && str[i - 2] !== "\\";
|
||||
if (!escaped) {
|
||||
insideString = !insideString;
|
||||
|
@ -119,7 +118,7 @@ var Translator = (function() {
|
|||
* argument key string - The key of the text to translate.
|
||||
* argument variables - The variables to use within the translation template (optional)
|
||||
*/
|
||||
translate: function(module, key, variables) {
|
||||
translate: function (module, key, variables) {
|
||||
variables = variables || {}; //Empty object by default
|
||||
|
||||
// Combines template and variables like:
|
||||
|
@ -127,18 +126,18 @@ var Translator = (function() {
|
|||
// variables: {timeToWait: "2 hours", work: "painting"}
|
||||
// to: "Please wait for 2 hours before continuing with painting."
|
||||
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 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);
|
||||
}
|
||||
|
@ -169,7 +168,7 @@ var Translator = (function() {
|
|||
* argument isFallback boolean - Flag to indicate fallback translations.
|
||||
* argument callback function - Function called when done.
|
||||
*/
|
||||
load: function(module, file, isFallback, callback) {
|
||||
load: function (module, file, isFallback, callback) {
|
||||
if (!isFallback) {
|
||||
Log.log(module.name + " - Load translation: " + file);
|
||||
} else {
|
||||
|
@ -177,8 +176,8 @@ var Translator = (function() {
|
|||
}
|
||||
|
||||
var self = this;
|
||||
if(!this.translationsFallback[module.name]) {
|
||||
loadJSON(module.file(file), function(json) {
|
||||
if (!this.translationsFallback[module.name]) {
|
||||
loadJSON(module.file(file), function (json) {
|
||||
if (!isFallback) {
|
||||
self.translations[module.name] = json;
|
||||
} else {
|
||||
|
@ -196,12 +195,12 @@ var Translator = (function() {
|
|||
*
|
||||
* argument lang String - The language identifier of the core language.
|
||||
*/
|
||||
loadCoreTranslations: function(lang) {
|
||||
loadCoreTranslations: function (lang) {
|
||||
var self = this;
|
||||
|
||||
if (lang in translations) {
|
||||
Log.log("Loading core translation file: " + translations[lang]);
|
||||
loadJSON(translations[lang], function(translations) {
|
||||
loadJSON(translations[lang], function (translations) {
|
||||
self.coreTranslations = translations;
|
||||
});
|
||||
} else {
|
||||
|
@ -215,19 +214,21 @@ var Translator = (function() {
|
|||
* Load the core translations fallback.
|
||||
* The first language defined in translations.js will be used.
|
||||
*/
|
||||
loadCoreTranslationsFallback: function() {
|
||||
loadCoreTranslationsFallback: function () {
|
||||
var self = this;
|
||||
|
||||
// The variable `first` will contain the first
|
||||
// defined translation after the following line.
|
||||
for (var first in translations) {break;}
|
||||
for (var first in translations) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
Log.log("Loading core translation fallback file: " + translations[first]);
|
||||
loadJSON(translations[first], function(translations) {
|
||||
loadJSON(translations[first], function (translations) {
|
||||
self.coreTranslationsFallback = translations;
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -14,4 +14,6 @@ var Utils = {
|
|||
}
|
||||
};
|
||||
|
||||
if (typeof module !== "undefined") {module.exports = Utils;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = Utils;
|
||||
}
|
||||
|
|
|
@ -6,8 +6,5 @@
|
|||
"module": "commonjs",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"exclude": [
|
||||
"modules",
|
||||
"node_modules"
|
||||
]
|
||||
"exclude": ["modules", "node_modules"]
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Module: Alert
|
||||
|
||||
The alert module is one of the default modules of the MagicMirror. This module displays notifications from other modules.
|
||||
|
||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/alert.html).
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* By Paul-Vincent Roll https://paulvincentroll.com/
|
||||
* MIT Licensed.
|
||||
*/
|
||||
Module.register("alert",{
|
||||
Module.register("alert", {
|
||||
defaults: {
|
||||
// scale|slide|genie|jelly|flip|bouncyflip|exploader
|
||||
effect: "slide",
|
||||
|
@ -17,31 +17,33 @@ Module.register("alert",{
|
|||
//Position
|
||||
position: "center",
|
||||
//shown at startup
|
||||
welcome_message: false,
|
||||
welcome_message: false
|
||||
},
|
||||
getScripts: function() {
|
||||
getScripts: function () {
|
||||
return ["notificationFx.js"];
|
||||
},
|
||||
getStyles: function() {
|
||||
getStyles: function () {
|
||||
return ["notificationFx.css", "font-awesome.css"];
|
||||
},
|
||||
// Define required translations.
|
||||
getTranslations: function() {
|
||||
getTranslations: function () {
|
||||
return {
|
||||
en: "translations/en.json",
|
||||
de: "translations/de.json",
|
||||
nl: "translations/nl.json",
|
||||
nl: "translations/nl.json"
|
||||
};
|
||||
},
|
||||
show_notification: function(message) {
|
||||
if (this.config.effect === "slide") {this.config.effect = this.config.effect + "-" + this.config.position;}
|
||||
show_notification: function (message) {
|
||||
if (this.config.effect === "slide") {
|
||||
this.config.effect = this.config.effect + "-" + this.config.position;
|
||||
}
|
||||
let msg = "";
|
||||
if (message.title) {
|
||||
msg += "<span class='thin dimmed medium'>" + message.title + "</span>";
|
||||
}
|
||||
if (message.message){
|
||||
if (msg !== ""){
|
||||
msg+= "<br />";
|
||||
if (message.message) {
|
||||
if (msg !== "") {
|
||||
msg += "<br />";
|
||||
}
|
||||
msg += "<span class='light bright small'>" + message.message + "</span>";
|
||||
}
|
||||
|
@ -53,22 +55,26 @@ Module.register("alert",{
|
|||
ttl: message.timer !== undefined ? message.timer : this.config.display_time
|
||||
}).show();
|
||||
},
|
||||
show_alert: function(params, sender) {
|
||||
show_alert: function (params, sender) {
|
||||
let image = "";
|
||||
//Set standard params if not provided by module
|
||||
if (typeof params.timer === "undefined") { params.timer = null; }
|
||||
if (typeof params.imageHeight === "undefined") { params.imageHeight = "80px"; }
|
||||
if (typeof params.timer === "undefined") {
|
||||
params.timer = null;
|
||||
}
|
||||
if (typeof params.imageHeight === "undefined") {
|
||||
params.imageHeight = "80px";
|
||||
}
|
||||
if (typeof params.imageUrl === "undefined" && typeof params.imageFA === "undefined") {
|
||||
params.imageUrl = null;
|
||||
} else if (typeof params.imageFA === "undefined"){
|
||||
image = "<img src='" + (params.imageUrl).toString() + "' height='" + (params.imageHeight).toString() + "' style='margin-bottom: 10px;'/><br />";
|
||||
} else if (typeof params.imageUrl === "undefined"){
|
||||
image = "<span class='bright " + "fa fa-" + params.imageFA + "' style='margin-bottom: 10px;font-size:" + (params.imageHeight).toString() + ";'/></span><br />";
|
||||
} else if (typeof params.imageFA === "undefined") {
|
||||
image = "<img src='" + params.imageUrl.toString() + "' height='" + params.imageHeight.toString() + "' style='margin-bottom: 10px;'/><br />";
|
||||
} else if (typeof params.imageUrl === "undefined") {
|
||||
image = "<span class='bright " + "fa fa-" + params.imageFA + "' style='margin-bottom: 10px;font-size:" + params.imageHeight.toString() + ";'/></span><br />";
|
||||
}
|
||||
//Create overlay
|
||||
const overlay = document.createElement("div");
|
||||
overlay.id = "overlay";
|
||||
overlay.innerHTML += "<div class=\"black_overlay\"></div>";
|
||||
overlay.innerHTML += '<div class="black_overlay"></div>';
|
||||
document.body.insertBefore(overlay, document.body.firstChild);
|
||||
|
||||
//If module already has an open alert close it
|
||||
|
@ -82,7 +88,7 @@ Module.register("alert",{
|
|||
message += "<span class='light dimmed medium'>" + params.title + "</span>";
|
||||
}
|
||||
if (params.message) {
|
||||
if (message !== ""){
|
||||
if (message !== "") {
|
||||
message += "<br />";
|
||||
}
|
||||
|
||||
|
@ -104,9 +110,8 @@ Module.register("alert",{
|
|||
this.hide_alert(sender);
|
||||
}, params.timer);
|
||||
}
|
||||
|
||||
},
|
||||
hide_alert: function(sender) {
|
||||
hide_alert: function (sender) {
|
||||
//Dismiss alert and remove from this.alerts
|
||||
if (this.alerts[sender.name]) {
|
||||
this.alerts[sender.name].dismiss();
|
||||
|
@ -116,18 +121,25 @@ Module.register("alert",{
|
|||
overlay.parentNode.removeChild(overlay);
|
||||
}
|
||||
},
|
||||
setPosition: function(pos) {
|
||||
setPosition: function (pos) {
|
||||
//Add css to body depending on the set position for notifications
|
||||
const sheet = document.createElement("style");
|
||||
if (pos === "center") {sheet.innerHTML = ".ns-box {margin-left: auto; margin-right: auto;text-align: center;}";}
|
||||
if (pos === "right") {sheet.innerHTML = ".ns-box {margin-left: auto;text-align: right;}";}
|
||||
if (pos === "left") {sheet.innerHTML = ".ns-box {margin-right: auto;text-align: left;}";}
|
||||
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: function (notification, payload, sender) {
|
||||
if (notification === "SHOW_ALERT") {
|
||||
if (typeof payload.type === "undefined") { payload.type = "alert"; }
|
||||
if (typeof payload.type === "undefined") {
|
||||
payload.type = "alert";
|
||||
}
|
||||
if (payload.type === "alert") {
|
||||
this.show_alert(payload, sender);
|
||||
} else if (payload.type === "notification") {
|
||||
|
@ -137,15 +149,14 @@ Module.register("alert",{
|
|||
this.hide_alert(sender);
|
||||
}
|
||||
},
|
||||
start: function() {
|
||||
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});
|
||||
if (this.config.welcome_message === true) {
|
||||
this.show_notification({ title: this.translate("sysTitle"), message: this.translate("welcome") });
|
||||
} else {
|
||||
this.show_notification({ title: this.translate("sysTitle"), message: this.config.welcome_message });
|
||||
}
|
||||
}
|
||||
Log.info("Starting module: " + this.name);
|
||||
|
|
|
@ -235,8 +235,12 @@
|
|||
}
|
||||
|
||||
@keyframes animFade {
|
||||
0% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animJelly {
|
||||
|
|
|
@ -10,8 +10,7 @@
|
|||
* Copyright 2014, Codrops
|
||||
* https://tympanus.net/codrops/
|
||||
*/
|
||||
(function(window) {
|
||||
|
||||
(function (window) {
|
||||
/**
|
||||
* extend obj function
|
||||
*/
|
||||
|
@ -58,19 +57,23 @@
|
|||
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
|
||||
*/
|
||||
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;
|
||||
let strinner = "<div class=\"ns-box-inner\">";
|
||||
let strinner = '<div class="ns-box-inner">';
|
||||
strinner += this.options.message;
|
||||
strinner += "</div>";
|
||||
this.ntf.innerHTML = strinner;
|
||||
|
@ -94,15 +97,17 @@
|
|||
/**
|
||||
* init events
|
||||
*/
|
||||
NotificationFx.prototype._initEvents = function() {
|
||||
NotificationFx.prototype._initEvents = function () {
|
||||
// dismiss notification by tapping on it if someone has a touchscreen
|
||||
this.ntf.querySelector(".ns-box-inner").addEventListener("click", () => { this.dismiss(); });
|
||||
this.ntf.querySelector(".ns-box-inner").addEventListener("click", () => {
|
||||
this.dismiss();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* show the notification
|
||||
*/
|
||||
NotificationFx.prototype.show = function() {
|
||||
NotificationFx.prototype.show = function () {
|
||||
this.active = true;
|
||||
this.ntf.classList.remove("ns-hide");
|
||||
this.ntf.classList.add("ns-show");
|
||||
|
@ -112,7 +117,7 @@
|
|||
/**
|
||||
* dismiss the notification
|
||||
*/
|
||||
NotificationFx.prototype.dismiss = function() {
|
||||
NotificationFx.prototype.dismiss = function () {
|
||||
this.active = false;
|
||||
clearTimeout(this.dismissttl);
|
||||
this.ntf.classList.remove("ns-show");
|
||||
|
@ -125,7 +130,9 @@
|
|||
|
||||
// after animation ends remove ntf from the DOM
|
||||
const onEndAnimationFn = (ev) => {
|
||||
if (ev.target !== this.ntf) {return false;}
|
||||
if (ev.target !== this.ntf) {
|
||||
return false;
|
||||
}
|
||||
this.ntf.removeEventListener("animationend", onEndAnimationFn);
|
||||
|
||||
if (ev.target.parentNode === this.options.wrapper) {
|
||||
|
@ -140,5 +147,4 @@
|
|||
* add to global namespace
|
||||
*/
|
||||
window.NotificationFx = NotificationFx;
|
||||
|
||||
})(window);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"sysTitle": "MagicMirror нотификация",
|
||||
"welcome": "Добре дошли, стартирането беше успешно"
|
||||
"sysTitle": "MagicMirror нотификация",
|
||||
"welcome": "Добре дошли, стартирането беше успешно"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"sysTitle": "MagicMirror Notifikation",
|
||||
"welcome": "Velkommen, modulet er succesfuldt startet!"
|
||||
"sysTitle": "MagicMirror Notifikation",
|
||||
"welcome": "Velkommen, modulet er succesfuldt startet!"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"sysTitle": "MagicMirror Benachrichtigung",
|
||||
"welcome": "Willkommen, Start war erfolgreich!"
|
||||
"sysTitle": "MagicMirror Benachrichtigung",
|
||||
"welcome": "Willkommen, Start war erfolgreich!"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"sysTitle": "MagicMirror Notification",
|
||||
"welcome": "Welcome, start was successful!"
|
||||
"sysTitle": "MagicMirror Notification",
|
||||
"welcome": "Welcome, start was successful!"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"sysTitle": "MagicMirror Notificaciones",
|
||||
"welcome": "Bienvenido, ¡se iniciado correctamente!"
|
||||
"sysTitle": "MagicMirror Notificaciones",
|
||||
"welcome": "Bienvenido, ¡se iniciado correctamente!"
|
||||
}
|
||||
|
|
|
@ -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!"
|
||||
}
|
||||
|
|
|
@ -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!"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"sysTitle": "MagicMirror Notificatie",
|
||||
"welcome": "Welkom, Succesvol gestart!"
|
||||
"sysTitle": "MagicMirror Notificatie",
|
||||
"welcome": "Welkom, Succesvol gestart!"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"sysTitle": "MagicMirror Уведомление",
|
||||
"welcome": "Добро пожаловать, старт был успешным!"
|
||||
"sysTitle": "MagicMirror Уведомление",
|
||||
"welcome": "Добро пожаловать, старт был успешным!"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# 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.
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
* MIT Licensed.
|
||||
*/
|
||||
Module.register("calendar", {
|
||||
|
||||
// Define module defaults
|
||||
defaults: {
|
||||
maximumEntries: 10, // Total Maximum Entries
|
||||
|
@ -39,8 +38,8 @@ Module.register("calendar", {
|
|||
calendars: [
|
||||
{
|
||||
symbol: "calendar",
|
||||
url: "https://www.calendarlabs.com/templates/ical/US-Holidays.ics",
|
||||
},
|
||||
url: "https://www.calendarlabs.com/templates/ical/US-Holidays.ics"
|
||||
}
|
||||
],
|
||||
titleReplace: {
|
||||
"De verjaardag van ": "",
|
||||
|
@ -85,7 +84,7 @@ Module.register("calendar", {
|
|||
var calendarConfig = {
|
||||
maximumEntries: calendar.maximumEntries,
|
||||
maximumNumberOfDays: calendar.maximumNumberOfDays,
|
||||
broadcastPastEvents: calendar.broadcastPastEvents,
|
||||
broadcastPastEvents: calendar.broadcastPastEvents
|
||||
};
|
||||
if (calendar.symbolClass === "undefined" || calendar.symbolClass === null) {
|
||||
calendarConfig.symbolClass = "";
|
||||
|
@ -98,7 +97,7 @@ Module.register("calendar", {
|
|||
}
|
||||
|
||||
// we check user and password here for backwards compatibility with old configs
|
||||
if(calendar.user && calendar.pass) {
|
||||
if (calendar.user && calendar.pass) {
|
||||
Log.warn("Deprecation warning: Please update your calendar authentication configuration.");
|
||||
Log.warn("https://github.com/MichMich/MagicMirror/tree/v2.1.2/modules/default/calendar#calendar-authentication-options");
|
||||
calendar.auth = {
|
||||
|
@ -112,7 +111,7 @@ Module.register("calendar", {
|
|||
// Trigger ADD_CALENDAR every fetchInterval to make sure there is always a calendar
|
||||
// fetcher running on the server side.
|
||||
var self = this;
|
||||
setInterval(function() {
|
||||
setInterval(function () {
|
||||
self.addCalendar(calendar.url, calendar.auth, calendarConfig);
|
||||
}, self.config.fetchInterval);
|
||||
}
|
||||
|
@ -144,13 +143,12 @@ Module.register("calendar", {
|
|||
|
||||
// Override dom generator.
|
||||
getDom: function () {
|
||||
|
||||
var events = this.createEventList();
|
||||
var wrapper = document.createElement("table");
|
||||
wrapper.className = this.config.tableClass;
|
||||
|
||||
if (events.length === 0) {
|
||||
wrapper.innerHTML = (this.loaded) ? this.translate("EMPTY") : this.translate("LOADING");
|
||||
wrapper.innerHTML = this.loaded ? this.translate("EMPTY") : this.translate("LOADING");
|
||||
wrapper.className = this.config.tableClass + " dimmed";
|
||||
return wrapper;
|
||||
}
|
||||
|
@ -169,8 +167,8 @@ Module.register("calendar", {
|
|||
for (var e in events) {
|
||||
var event = events[e];
|
||||
var dateAsString = moment(event.startDate, "x").format(this.config.dateFormat);
|
||||
if(this.config.timeFormat === "dateheaders"){
|
||||
if(lastSeenDate !== dateAsString){
|
||||
if (this.config.timeFormat === "dateheaders") {
|
||||
if (lastSeenDate !== dateAsString) {
|
||||
var dateRow = document.createElement("tr");
|
||||
dateRow.className = "normal";
|
||||
var dateCell = document.createElement("td");
|
||||
|
@ -181,9 +179,10 @@ Module.register("calendar", {
|
|||
dateRow.appendChild(dateCell);
|
||||
wrapper.appendChild(dateRow);
|
||||
|
||||
if (e >= startFade) { //fading
|
||||
if (e >= startFade) {
|
||||
//fading
|
||||
currentFadeStep = e - startFade;
|
||||
dateRow.style.opacity = 1 - (1 / fadeSteps * currentFadeStep);
|
||||
dateRow.style.opacity = 1 - (1 / fadeSteps) * currentFadeStep;
|
||||
}
|
||||
|
||||
lastSeenDate = dateAsString;
|
||||
|
@ -209,20 +208,20 @@ Module.register("calendar", {
|
|||
symbolWrapper.className = "symbol align-right " + symbolClass;
|
||||
|
||||
var symbols = this.symbolsForUrl(event.url);
|
||||
if(typeof symbols === "string") {
|
||||
if (typeof symbols === "string") {
|
||||
symbols = [symbols];
|
||||
}
|
||||
|
||||
for(var i = 0; i < symbols.length; i++) {
|
||||
for (var i = 0; i < symbols.length; i++) {
|
||||
var symbol = document.createElement("span");
|
||||
symbol.className = "fa fa-fw fa-" + symbols[i];
|
||||
if(i > 0){
|
||||
if (i > 0) {
|
||||
symbol.style.paddingLeft = "5px";
|
||||
}
|
||||
symbolWrapper.appendChild(symbol);
|
||||
}
|
||||
eventWrapper.appendChild(symbolWrapper);
|
||||
} else if(this.config.timeFormat === "dateheaders"){
|
||||
} else if (this.config.timeFormat === "dateheaders") {
|
||||
var blankCell = document.createElement("td");
|
||||
blankCell.innerHTML = " ";
|
||||
eventWrapper.appendChild(blankCell);
|
||||
|
@ -232,7 +231,6 @@ Module.register("calendar", {
|
|||
repeatingCountTitle = "";
|
||||
|
||||
if (this.config.displayRepeatingCountTitle && event.firstYear !== undefined) {
|
||||
|
||||
repeatingCountTitle = this.countTitleForUrl(event.url);
|
||||
|
||||
if (repeatingCountTitle !== "") {
|
||||
|
@ -255,12 +253,10 @@ Module.register("calendar", {
|
|||
|
||||
var timeWrapper;
|
||||
|
||||
if(this.config.timeFormat === "dateheaders"){
|
||||
|
||||
if (this.config.timeFormat === "dateheaders") {
|
||||
if (event.fullDayEvent) {
|
||||
titleWrapper.colSpan = "2";
|
||||
titleWrapper.align = "left";
|
||||
|
||||
} else {
|
||||
timeWrapper = document.createElement("td");
|
||||
timeWrapper.className = "time light " + this.timeClassForUrl(event.url);
|
||||
|
@ -298,14 +294,14 @@ Module.register("calendar", {
|
|||
}
|
||||
} else {
|
||||
/* Check to see if the user displays absolute or relative dates with their events
|
||||
* Also check to see if an event is happening within an 'urgency' time frameElement
|
||||
* For example, if the user set an .urgency of 7 days, those events that fall within that
|
||||
* time frame will be displayed with 'in xxx' time format or moment.fromNow()
|
||||
*
|
||||
* Note: this needs to be put in its own function, as the whole thing repeats again verbatim
|
||||
*/
|
||||
* Also check to see if an event is happening within an 'urgency' time frameElement
|
||||
* For example, if the user set an .urgency of 7 days, those events that fall within that
|
||||
* time frame will be displayed with 'in xxx' time format or moment.fromNow()
|
||||
*
|
||||
* Note: this needs to be put in its own function, as the whole thing repeats again verbatim
|
||||
*/
|
||||
if (this.config.timeFormat === "absolute") {
|
||||
if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * oneDay))) {
|
||||
if (this.config.urgency > 1 && event.startDate - now < this.config.urgency * oneDay) {
|
||||
// This event falls within the config.urgency period that the user has set
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").from(moment().format("YYYYMMDD")));
|
||||
} else {
|
||||
|
@ -315,9 +311,9 @@ Module.register("calendar", {
|
|||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").from(moment().format("YYYYMMDD")));
|
||||
}
|
||||
}
|
||||
if(this.config.showEnd){
|
||||
timeWrapper.innerHTML += "-" ;
|
||||
timeWrapper.innerHTML += this.capFirst(moment(event.endDate , "x").format(this.config.fullDayEventDateFormat));
|
||||
if (this.config.showEnd) {
|
||||
timeWrapper.innerHTML += "-";
|
||||
timeWrapper.innerHTML += this.capFirst(moment(event.endDate, "x").format(this.config.fullDayEventDateFormat));
|
||||
}
|
||||
} else {
|
||||
if (event.startDate >= new Date()) {
|
||||
|
@ -327,7 +323,7 @@ Module.register("calendar", {
|
|||
// If event is within 6 hour, display 'in xxx' time format or moment.fromNow()
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
||||
} else {
|
||||
if(this.config.timeFormat === "absolute" && !this.config.nextDaysRelative) {
|
||||
if (this.config.timeFormat === "absolute" && !this.config.nextDaysRelative) {
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.dateFormat));
|
||||
} else {
|
||||
// Otherwise just say 'Today/Tomorrow at such-n-such time'
|
||||
|
@ -336,14 +332,14 @@ Module.register("calendar", {
|
|||
}
|
||||
} else {
|
||||
/* Check to see if the user displays absolute or relative dates with their events
|
||||
* Also check to see if an event is happening within an 'urgency' time frameElement
|
||||
* For example, if the user set an .urgency of 7 days, those events that fall within that
|
||||
* time frame will be displayed with 'in xxx' time format or moment.fromNow()
|
||||
*
|
||||
* Note: this needs to be put in its own function, as the whole thing repeats again verbatim
|
||||
*/
|
||||
* Also check to see if an event is happening within an 'urgency' time frameElement
|
||||
* For example, if the user set an .urgency of 7 days, those events that fall within that
|
||||
* time frame will be displayed with 'in xxx' time format or moment.fromNow()
|
||||
*
|
||||
* Note: this needs to be put in its own function, as the whole thing repeats again verbatim
|
||||
*/
|
||||
if (this.config.timeFormat === "absolute") {
|
||||
if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * oneDay))) {
|
||||
if (this.config.urgency > 1 && event.startDate - now < this.config.urgency * oneDay) {
|
||||
// This event falls within the config.urgency period that the user has set
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
||||
} else {
|
||||
|
@ -364,7 +360,6 @@ Module.register("calendar", {
|
|||
if (this.config.showEnd) {
|
||||
timeWrapper.innerHTML += "-";
|
||||
timeWrapper.innerHTML += this.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat));
|
||||
|
||||
}
|
||||
}
|
||||
//timeWrapper.innerHTML += ' - '+ moment(event.startDate,'x').format('lll');
|
||||
|
@ -378,7 +373,7 @@ Module.register("calendar", {
|
|||
// Create fade effect.
|
||||
if (e >= startFade) {
|
||||
currentFadeStep = e - startFade;
|
||||
eventWrapper.style.opacity = 1 - (1 / fadeSteps * currentFadeStep);
|
||||
eventWrapper.style.opacity = 1 - (1 / fadeSteps) * currentFadeStep;
|
||||
}
|
||||
|
||||
if (this.config.showLocation) {
|
||||
|
@ -401,7 +396,7 @@ Module.register("calendar", {
|
|||
|
||||
if (e >= startFade) {
|
||||
currentFadeStep = e - startFade;
|
||||
locationRow.style.opacity = 1 - (1 / fadeSteps * currentFadeStep);
|
||||
locationRow.style.opacity = 1 - (1 / fadeSteps) * currentFadeStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -418,17 +413,17 @@ Module.register("calendar", {
|
|||
* @param {number} timeFormat Specifies either 12 or 24 hour time format
|
||||
* @returns {moment.LocaleSpecification}
|
||||
*/
|
||||
getLocaleSpecification: function(timeFormat) {
|
||||
getLocaleSpecification: function (timeFormat) {
|
||||
switch (timeFormat) {
|
||||
case 12: {
|
||||
return { longDateFormat: {LT: "h:mm A"} };
|
||||
}
|
||||
case 24: {
|
||||
return { longDateFormat: {LT: "HH:mm"} };
|
||||
}
|
||||
default: {
|
||||
return { longDateFormat: {LT: moment.localeData().longDateFormat("LT")} };
|
||||
}
|
||||
case 12: {
|
||||
return { longDateFormat: { LT: "h:mm A" } };
|
||||
}
|
||||
case 24: {
|
||||
return { longDateFormat: { LT: "HH:mm" } };
|
||||
}
|
||||
default: {
|
||||
return { longDateFormat: { LT: moment.localeData().longDateFormat("LT") } };
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -464,37 +459,37 @@ Module.register("calendar", {
|
|||
var calendar = this.calendarData[c];
|
||||
for (var e in calendar) {
|
||||
var event = JSON.parse(JSON.stringify(calendar[e])); // clone object
|
||||
if(event.endDate < now) {
|
||||
if (event.endDate < now) {
|
||||
continue;
|
||||
}
|
||||
if(this.config.hidePrivate) {
|
||||
if(event.class === "PRIVATE") {
|
||||
if (this.config.hidePrivate) {
|
||||
if (event.class === "PRIVATE") {
|
||||
// do not add the current event, skip it
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(this.config.hideOngoing) {
|
||||
if(event.startDate < now) {
|
||||
if (this.config.hideOngoing) {
|
||||
if (event.startDate < now) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(this.listContainsEvent(events,event)){
|
||||
if (this.listContainsEvent(events, event)) {
|
||||
continue;
|
||||
}
|
||||
event.url = c;
|
||||
event.today = event.startDate >= today && event.startDate < (today + 24 * 60 * 60 * 1000);
|
||||
event.today = event.startDate >= today && event.startDate < today + 24 * 60 * 60 * 1000;
|
||||
|
||||
/* if sliceMultiDayEvents is set to true, multiday events (events exceeding at least one midnight) are sliced into days,
|
||||
* otherwise, esp. in dateheaders mode it is not clear how long these events are.
|
||||
*/
|
||||
var maxCount = Math.ceil(((event.endDate - 1) - moment(event.startDate, "x").endOf("day").format("x"))/(1000*60*60*24)) + 1;
|
||||
* otherwise, esp. in dateheaders mode it is not clear how long these events are.
|
||||
*/
|
||||
var maxCount = Math.ceil((event.endDate - 1 - moment(event.startDate, "x").endOf("day").format("x")) / (1000 * 60 * 60 * 24)) + 1;
|
||||
if (this.config.sliceMultiDayEvents && maxCount > 1) {
|
||||
var splitEvents = [];
|
||||
var midnight = moment(event.startDate, "x").clone().startOf("day").add(1, "day").format("x");
|
||||
var count = 1;
|
||||
while (event.endDate > midnight) {
|
||||
var thisEvent = JSON.parse(JSON.stringify(event)); // clone object
|
||||
thisEvent.today = thisEvent.startDate >= today && thisEvent.startDate < (today + 24 * 60 * 60 * 1000);
|
||||
thisEvent.today = thisEvent.startDate >= today && thisEvent.startDate < today + 24 * 60 * 60 * 1000;
|
||||
thisEvent.endDate = midnight;
|
||||
thisEvent.title += " (" + count + "/" + maxCount + ")";
|
||||
splitEvents.push(thisEvent);
|
||||
|
@ -504,11 +499,11 @@ Module.register("calendar", {
|
|||
midnight = moment(midnight, "x").add(1, "day").format("x"); // next day
|
||||
}
|
||||
// Last day
|
||||
event.title += " ("+count+"/"+maxCount+")";
|
||||
event.title += " (" + count + "/" + maxCount + ")";
|
||||
splitEvents.push(event);
|
||||
|
||||
for (event of splitEvents) {
|
||||
if ((event.endDate > now) && (event.endDate <= future)) {
|
||||
if (event.endDate > now && event.endDate <= future) {
|
||||
events.push(event);
|
||||
}
|
||||
}
|
||||
|
@ -524,9 +519,9 @@ Module.register("calendar", {
|
|||
return events.slice(0, this.config.maximumEntries);
|
||||
},
|
||||
|
||||
listContainsEvent: function(eventList, event){
|
||||
for(var evt of eventList){
|
||||
if(evt.title === event.title && parseInt(evt.startDate) === parseInt(event.startDate)){
|
||||
listContainsEvent: function (eventList, event) {
|
||||
for (var evt of eventList) {
|
||||
if (evt.title === event.title && parseInt(evt.startDate) === parseInt(event.startDate)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -549,7 +544,7 @@ Module.register("calendar", {
|
|||
titleClass: calendarConfig.titleClass,
|
||||
timeClass: calendarConfig.timeClass,
|
||||
auth: auth,
|
||||
broadcastPastEvents: calendarConfig.broadcastPastEvents || this.config.broadcastPastEvents,
|
||||
broadcastPastEvents: calendarConfig.broadcastPastEvents || this.config.broadcastPastEvents
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -676,8 +671,9 @@ Module.register("calendar", {
|
|||
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
var word = words[i];
|
||||
if (currentLine.length + word.length < (typeof maxLength === "number" ? maxLength : 25) - 1) { // max - 1 to account for a space
|
||||
currentLine += (word + " ");
|
||||
if (currentLine.length + word.length < (typeof maxLength === "number" ? maxLength : 25) - 1) {
|
||||
// max - 1 to account for a space
|
||||
currentLine += word + " ";
|
||||
} else {
|
||||
line++;
|
||||
if (line > maxTitleLines - 1) {
|
||||
|
@ -688,9 +684,9 @@ Module.register("calendar", {
|
|||
}
|
||||
|
||||
if (currentLine.length > 0) {
|
||||
temp += (currentLine + "<br>" + word + " ");
|
||||
temp += currentLine + "<br>" + word + " ";
|
||||
} else {
|
||||
temp += (word + "<br>");
|
||||
temp += word + "<br>";
|
||||
}
|
||||
currentLine = "";
|
||||
}
|
||||
|
@ -758,11 +754,10 @@ Module.register("calendar", {
|
|||
}
|
||||
}
|
||||
|
||||
eventList.sort(function(a,b) {
|
||||
eventList.sort(function (a, b) {
|
||||
return a.startDate - b.startDate;
|
||||
});
|
||||
|
||||
this.sendNotification("CALENDAR_EVENTS", eventList);
|
||||
|
||||
}
|
||||
});
|
||||
|
|
|
@ -8,44 +8,42 @@
|
|||
const ical = require("./vendor/ical.js");
|
||||
const moment = require("moment");
|
||||
|
||||
var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, includePastEvents) {
|
||||
var CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, includePastEvents) {
|
||||
var self = this;
|
||||
|
||||
var reloadTimer = null;
|
||||
var events = [];
|
||||
|
||||
var fetchFailedCallback = function() {};
|
||||
var eventsReceivedCallback = function() {};
|
||||
var fetchFailedCallback = function () {};
|
||||
var eventsReceivedCallback = function () {};
|
||||
|
||||
/* fetchCalendar()
|
||||
* Initiates calendar fetch.
|
||||
*/
|
||||
var fetchCalendar = function() {
|
||||
|
||||
var fetchCalendar = function () {
|
||||
clearTimeout(reloadTimer);
|
||||
reloadTimer = null;
|
||||
|
||||
var 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/)"
|
||||
"User-Agent": "Mozilla/5.0 (Node.js " + nodeVersion + ") MagicMirror/" + global.version + " (https://github.com/MichMich/MagicMirror/)"
|
||||
},
|
||||
gzip: true
|
||||
};
|
||||
|
||||
if (auth) {
|
||||
if(auth.method === "bearer"){
|
||||
if (auth.method === "bearer") {
|
||||
opts.auth = {
|
||||
bearer: auth.pass
|
||||
};
|
||||
|
||||
} else {
|
||||
opts.auth = {
|
||||
user: auth.user,
|
||||
pass: auth.pass
|
||||
};
|
||||
|
||||
if(auth.method === "digest"){
|
||||
if (auth.method === "digest") {
|
||||
opts.auth.sendImmediately = false;
|
||||
} else {
|
||||
opts.auth.sendImmediately = true;
|
||||
|
@ -53,7 +51,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
}
|
||||
}
|
||||
|
||||
ical.fromURL(url, opts, function(err, data) {
|
||||
ical.fromURL(url, opts, function (err, data) {
|
||||
if (err) {
|
||||
fetchFailedCallback(self, err);
|
||||
scheduleTimer();
|
||||
|
@ -64,17 +62,19 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
var 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 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]));
|
||||
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 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) {
|
||||
|
@ -91,13 +91,12 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
}
|
||||
|
||||
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") {
|
||||
var dur=moment.duration(event.duration);
|
||||
} else if (typeof event.duration !== "undefined") {
|
||||
var dur = moment.duration(event.duration);
|
||||
endDate = startDate.clone().add(dur);
|
||||
} else {
|
||||
if (!isFacebookBirthday) {
|
||||
|
@ -175,8 +174,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
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){
|
||||
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);
|
||||
}
|
||||
|
@ -187,7 +185,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
var pastLocal = moment(past).subtract(past.getTimezoneOffset(), "minutes").toDate();
|
||||
var futureLocal = moment(future).subtract(future.getTimezoneOffset(), "minutes").toDate();
|
||||
var datesLocal = rule.between(pastLocal, futureLocal, true, limitFunction);
|
||||
var dates = datesLocal.map(function(dateLocal) {
|
||||
var dates = datesLocal.map(function (dateLocal) {
|
||||
var date = moment(dateLocal).add(dateLocal.getTimezoneOffset(), "minutes").toDate();
|
||||
return date;
|
||||
});
|
||||
|
@ -199,17 +197,14 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
// 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)
|
||||
{
|
||||
if (event.recurrences !== undefined) {
|
||||
var pastMoment = moment(past);
|
||||
var futureMoment = moment(future);
|
||||
|
||||
for (var r in event.recurrences)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (moment(new Date(r)).isBetween(pastMoment, futureMoment) !== true) {
|
||||
dates.push(new Date(r));
|
||||
}
|
||||
}
|
||||
|
@ -221,7 +216,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
// 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 dateKey = date.toISOString().substring(0, 10);
|
||||
var curEvent = event;
|
||||
var showRecurrence = true;
|
||||
|
||||
|
@ -234,16 +229,14 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
@ -265,7 +258,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
showRecurrence = false;
|
||||
}
|
||||
|
||||
if ((showRecurrence === true) && (addedEvents < maximumEntries)) {
|
||||
if (showRecurrence === true && addedEvents < maximumEntries) {
|
||||
addedEvents++;
|
||||
newEvents.push({
|
||||
title: recurrenceTitle,
|
||||
|
@ -284,7 +277,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
} else {
|
||||
// console.log("Single event ...");
|
||||
// Single event.
|
||||
var fullDayEvent = (isFacebookBirthday) ? true : isFullDayEvent(event);
|
||||
var fullDayEvent = isFacebookBirthday ? true : isFullDayEvent(event);
|
||||
|
||||
if (includePastEvents) {
|
||||
if (endDate < past) {
|
||||
|
@ -329,12 +322,11 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
geo: geo,
|
||||
description: description
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newEvents.sort(function(a, b) {
|
||||
newEvents.sort(function (a, b) {
|
||||
return a.startDate - b.startDate;
|
||||
});
|
||||
|
||||
|
@ -350,10 +342,10 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
/* scheduleTimer()
|
||||
* Schedule the timer for the next update.
|
||||
*/
|
||||
var scheduleTimer = function() {
|
||||
var scheduleTimer = function () {
|
||||
//console.log('Schedule update timer.');
|
||||
clearTimeout(reloadTimer);
|
||||
reloadTimer = setTimeout(function() {
|
||||
reloadTimer = setTimeout(function () {
|
||||
fetchCalendar();
|
||||
}, reloadInterval);
|
||||
};
|
||||
|
@ -365,7 +357,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
*
|
||||
* return bool - The event is a fullday event.
|
||||
*/
|
||||
var isFullDayEvent = function(event) {
|
||||
var isFullDayEvent = function (event) {
|
||||
if (event.start.length === 8 || event.start.dateOnly) {
|
||||
return true;
|
||||
}
|
||||
|
@ -373,7 +365,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
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) {
|
||||
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;
|
||||
}
|
||||
|
@ -390,7 +382,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
*
|
||||
* return bool - The event should be filtered out
|
||||
*/
|
||||
var timeFilterApplies = function(now, endDate, filter) {
|
||||
var timeFilterApplies = function (now, endDate, filter) {
|
||||
if (filter) {
|
||||
var until = filter.split(" "),
|
||||
value = parseInt(until[0]),
|
||||
|
@ -404,16 +396,16 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
};
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
* 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;
|
||||
title = typeof event.summary.val !== "undefined" ? event.summary.val : event.summary;
|
||||
} else if (event.description) {
|
||||
title = event.description;
|
||||
}
|
||||
|
@ -442,14 +434,14 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
/* startFetch()
|
||||
* Initiate fetchCalendar();
|
||||
*/
|
||||
this.startFetch = function() {
|
||||
this.startFetch = function () {
|
||||
fetchCalendar();
|
||||
};
|
||||
|
||||
/* broadcastItems()
|
||||
* Broadcast the existing events.
|
||||
*/
|
||||
this.broadcastEvents = function() {
|
||||
this.broadcastEvents = function () {
|
||||
//console.log('Broadcasting ' + events.length + ' events.');
|
||||
eventsReceivedCallback(self);
|
||||
};
|
||||
|
@ -459,7 +451,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
*
|
||||
* argument callback function - The on success callback.
|
||||
*/
|
||||
this.onReceive = function(callback) {
|
||||
this.onReceive = function (callback) {
|
||||
eventsReceivedCallback = callback;
|
||||
};
|
||||
|
||||
|
@ -468,7 +460,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
*
|
||||
* argument callback function - The on error callback.
|
||||
*/
|
||||
this.onError = function(callback) {
|
||||
this.onError = function (callback) {
|
||||
fetchFailedCallback = callback;
|
||||
};
|
||||
|
||||
|
@ -477,7 +469,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
*
|
||||
* return string - The url of this fetcher.
|
||||
*/
|
||||
this.url = function() {
|
||||
this.url = function () {
|
||||
return url;
|
||||
};
|
||||
|
||||
|
@ -486,7 +478,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
|||
*
|
||||
* return array - The current available events for this fetcher.
|
||||
*/
|
||||
this.events = function() {
|
||||
this.events = function () {
|
||||
return events;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -24,12 +24,12 @@ console.log("Create fetcher ...");
|
|||
|
||||
var fetcher = new CalendarFetcher(url, fetchInterval, [], maximumEntries, maximumNumberOfDays, auth);
|
||||
|
||||
fetcher.onReceive(function(fetcher) {
|
||||
fetcher.onReceive(function (fetcher) {
|
||||
console.log(fetcher.events());
|
||||
console.log("------------------------------------------------------------");
|
||||
});
|
||||
|
||||
fetcher.onError(function(fetcher, error) {
|
||||
fetcher.onError(function (fetcher, error) {
|
||||
console.log("Fetcher error:");
|
||||
console.log(error);
|
||||
});
|
||||
|
|
|
@ -11,17 +11,16 @@ var CalendarFetcher = require("./calendarfetcher.js");
|
|||
|
||||
module.exports = NodeHelper.create({
|
||||
// Override start method.
|
||||
start: function() {
|
||||
start: function () {
|
||||
var events = [];
|
||||
|
||||
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);
|
||||
|
@ -36,11 +35,11 @@ module.exports = NodeHelper.create({
|
|||
* attribute reloadInterval number - Reload interval in milliseconds.
|
||||
*/
|
||||
|
||||
createFetcher: function(url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents) {
|
||||
createFetcher: function (url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents) {
|
||||
var self = this;
|
||||
|
||||
if (!validUrl.isUri(url)) {
|
||||
self.sendSocketNotification("INCORRECT_URL", {url: url});
|
||||
self.sendSocketNotification("INCORRECT_URL", { url: url });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -49,7 +48,7 @@ module.exports = NodeHelper.create({
|
|||
console.log("Create new calendar fetcher for url: " + url + " - Interval: " + fetchInterval);
|
||||
fetcher = new CalendarFetcher(url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents);
|
||||
|
||||
fetcher.onReceive(function(fetcher) {
|
||||
fetcher.onReceive(function (fetcher) {
|
||||
//console.log('Broadcast events.');
|
||||
//console.log(fetcher.events());
|
||||
|
||||
|
@ -59,7 +58,7 @@ module.exports = NodeHelper.create({
|
|||
});
|
||||
});
|
||||
|
||||
fetcher.onError(function(fetcher, error) {
|
||||
fetcher.onError(function (fetcher, error) {
|
||||
console.error("Calendar Error. Could not fetch calendar: ", fetcher.url(), error);
|
||||
self.sendSocketNotification("FETCH_ERROR", {
|
||||
url: fetcher.url(),
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Module: Clock
|
||||
|
||||
The `clock` module is one of the default modules of the MagicMirror.
|
||||
This module displays the current date and time. The information will be updated realtime.
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
Module.register("clock",{
|
||||
Module.register("clock", {
|
||||
// Module config defaults.
|
||||
defaults: {
|
||||
displayType: "digital", // options: digital, analog, both
|
||||
|
@ -31,18 +31,18 @@ Module.register("clock",{
|
|||
showSunTimes: false,
|
||||
showMoonTimes: false,
|
||||
lat: 47.630539,
|
||||
lon: -122.344147,
|
||||
lon: -122.344147
|
||||
},
|
||||
// Define required scripts.
|
||||
getScripts: function() {
|
||||
getScripts: function () {
|
||||
return ["moment.js", "moment-timezone.js", "suncalc.js"];
|
||||
},
|
||||
// Define styles.
|
||||
getStyles: function() {
|
||||
getStyles: function () {
|
||||
return ["clock_styles.css"];
|
||||
},
|
||||
// Define start sequence.
|
||||
start: function() {
|
||||
start: function () {
|
||||
Log.info("Starting module: " + this.name);
|
||||
|
||||
// Schedule update interval.
|
||||
|
@ -51,16 +51,16 @@ Module.register("clock",{
|
|||
self.minute = moment().minute();
|
||||
|
||||
//Calculate how many ms should pass until next update depending on if seconds is displayed or not
|
||||
var delayCalculator = function(reducedSeconds) {
|
||||
var delayCalculator = function (reducedSeconds) {
|
||||
if (self.config.displaySeconds) {
|
||||
return 1000 - moment().milliseconds();
|
||||
} else {
|
||||
return ((60 - reducedSeconds) * 1000) - moment().milliseconds();
|
||||
return (60 - reducedSeconds) * 1000 - moment().milliseconds();
|
||||
}
|
||||
};
|
||||
|
||||
//A recursive timeout function instead of interval to avoid drifting
|
||||
var notificationTimer = function() {
|
||||
var notificationTimer = function () {
|
||||
self.updateDom();
|
||||
|
||||
//If seconds is displayed CLOCK_SECOND-notification should be sent (but not when CLOCK_MINUTE-notification is sent)
|
||||
|
@ -84,11 +84,9 @@ Module.register("clock",{
|
|||
|
||||
// Set locale.
|
||||
moment.locale(config.language);
|
||||
|
||||
},
|
||||
// Override dom generator.
|
||||
getDom: function() {
|
||||
|
||||
getDom: function () {
|
||||
var wrapper = document.createElement("div");
|
||||
|
||||
/************************************
|
||||
|
@ -127,12 +125,12 @@ Module.register("clock",{
|
|||
}
|
||||
|
||||
if (this.config.clockBold === true) {
|
||||
timeString = now.format(hourSymbol + "[<span class=\"bold\">]mm[</span>]");
|
||||
timeString = now.format(hourSymbol + '[<span class="bold">]mm[</span>]');
|
||||
} else {
|
||||
timeString = now.format(hourSymbol + ":mm");
|
||||
}
|
||||
|
||||
if(this.config.showDate){
|
||||
if (this.config.showDate) {
|
||||
dateWrapper.innerHTML = now.format(this.config.dateFormat);
|
||||
}
|
||||
if (this.config.showWeek) {
|
||||
|
@ -173,9 +171,18 @@ Module.register("clock",{
|
|||
}
|
||||
const untilNextEvent = moment.duration(moment(nextEvent).diff(now));
|
||||
const untilNextEventString = untilNextEvent.hours() + "h " + untilNextEvent.minutes() + "m";
|
||||
sunWrapper.innerHTML = "<span class=\"" + (isVisible ? "bright" : "") + "\"><i class=\"fa fa-sun-o\" aria-hidden=\"true\"></i> " + untilNextEventString + "</span>" +
|
||||
"<span><i class=\"fa fa-arrow-up\" aria-hidden=\"true\"></i>" + formatTime(this.config, sunTimes.sunrise) + "</span>" +
|
||||
"<span><i class=\"fa fa-arrow-down\" aria-hidden=\"true\"></i>" + formatTime(this.config, sunTimes.sunset) + "</span>";
|
||||
sunWrapper.innerHTML =
|
||||
'<span class="' +
|
||||
(isVisible ? "bright" : "") +
|
||||
'"><i class="fa fa-sun-o" aria-hidden="true"></i> ' +
|
||||
untilNextEventString +
|
||||
"</span>" +
|
||||
'<span><i class="fa fa-arrow-up" aria-hidden="true"></i>' +
|
||||
formatTime(this.config, sunTimes.sunrise) +
|
||||
"</span>" +
|
||||
'<span><i class="fa fa-arrow-down" aria-hidden="true"></i>' +
|
||||
formatTime(this.config, sunTimes.sunset) +
|
||||
"</span>";
|
||||
}
|
||||
if (this.config.showMoonTimes) {
|
||||
const moonIllumination = SunCalc.getMoonIllumination(now.toDate());
|
||||
|
@ -190,9 +197,18 @@ Module.register("clock",{
|
|||
}
|
||||
const isVisible = now.isBetween(moonRise, moonSet) || moonTimes.alwaysUp === true;
|
||||
const illuminatedFractionString = Math.round(moonIllumination.fraction * 100) + "%";
|
||||
moonWrapper.innerHTML = "<span class=\"" + (isVisible ? "bright" : "") + "\"><i class=\"fa fa-moon-o\" aria-hidden=\"true\"></i> " + illuminatedFractionString + "</span>" +
|
||||
"<span><i class=\"fa fa-arrow-up\" aria-hidden=\"true\"></i> " + (moonRise ? formatTime(this.config, moonRise) : "...") + "</span>"+
|
||||
"<span><i class=\"fa fa-arrow-down\" aria-hidden=\"true\"></i> " + (moonSet ? formatTime(this.config, moonSet) : "...") + "</span>";
|
||||
moonWrapper.innerHTML =
|
||||
'<span class="' +
|
||||
(isVisible ? "bright" : "") +
|
||||
'"><i class="fa fa-moon-o" aria-hidden="true"></i> ' +
|
||||
illuminatedFractionString +
|
||||
"</span>" +
|
||||
'<span><i class="fa fa-arrow-up" aria-hidden="true"></i> ' +
|
||||
(moonRise ? formatTime(this.config, moonRise) : "...") +
|
||||
"</span>" +
|
||||
'<span><i class="fa fa-arrow-down" aria-hidden="true"></i> ' +
|
||||
(moonSet ? formatTime(this.config, moonSet) : "...") +
|
||||
"</span>";
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
|
@ -206,7 +222,7 @@ Module.register("clock",{
|
|||
if (this.config.timezone) {
|
||||
now.tz(this.config.timezone);
|
||||
}
|
||||
var second = now.seconds() * 6,
|
||||
var second = now.seconds() * 6,
|
||||
minute = now.minute() * 6 + second / 60,
|
||||
hour = ((now.hours() % 12) / 12) * 360 + 90 + minute / 12;
|
||||
|
||||
|
@ -217,13 +233,12 @@ Module.register("clock",{
|
|||
clockCircle.style.height = this.config.analogSize;
|
||||
|
||||
if (this.config.analogFace !== "" && this.config.analogFace !== "simple" && this.config.analogFace !== "none") {
|
||||
clockCircle.style.background = "url("+ this.data.path + "faces/" + this.config.analogFace + ".svg)";
|
||||
clockCircle.style.background = "url(" + this.data.path + "faces/" + this.config.analogFace + ".svg)";
|
||||
clockCircle.style.backgroundSize = "100%";
|
||||
|
||||
// The following line solves issue: https://github.com/MichMich/MagicMirror/issues/611
|
||||
// clockCircle.style.border = "1px solid black";
|
||||
clockCircle.style.border = "rgba(0, 0, 0, 0.1)"; //Updated fix for Issue 611 where non-black backgrounds are used
|
||||
|
||||
} else if (this.config.analogFace !== "none") {
|
||||
clockCircle.style.border = "2px solid white";
|
||||
}
|
||||
|
@ -303,9 +318,9 @@ Module.register("clock",{
|
|||
digitalWrapper.appendChild(moonWrapper);
|
||||
digitalWrapper.appendChild(weekWrapper);
|
||||
|
||||
var appendClocks = function(condition, pos1, pos2) {
|
||||
var padding = [0,0,0,0];
|
||||
padding[(placement === condition) ? pos1 : pos2] = "20px";
|
||||
var appendClocks = function (condition, pos1, pos2) {
|
||||
var padding = [0, 0, 0, 0];
|
||||
padding[placement === condition ? pos1 : pos2] = "20px";
|
||||
analogWrapper.style.padding = padding.join(" ");
|
||||
if (placement === condition) {
|
||||
wrapper.appendChild(analogWrapper);
|
||||
|
|
|
@ -1,72 +1,72 @@
|
|||
.clockCircle {
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
.clockFace {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.clockFace::after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
margin: -3px 0 0 -3px;
|
||||
background: white;
|
||||
border-radius: 3px;
|
||||
content: "";
|
||||
display: block;
|
||||
}
|
||||
|
||||
.clockHour {
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -2px 0 -2px -25%; /* numbers much match negative length & thickness */
|
||||
padding: 2px 0 2px 25%; /* indicator length & thickness */
|
||||
background: white;
|
||||
transform-origin: 100% 50%;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
|
||||
.clockMinute {
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -35% -2px 0; /* numbers must match negative length & thickness */
|
||||
padding: 35% 2px 0; /* indicator length & thickness */
|
||||
background: white;
|
||||
transform-origin: 50% 100%;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
|
||||
.clockSecond {
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -38% -1px 0 0; /* numbers must match negative length & thickness */
|
||||
padding: 38% 1px 0 0; /* indicator length & thickness */
|
||||
background: #888;
|
||||
transform-origin: 50% 100%;
|
||||
}
|
||||
|
||||
.module.clock .sun,
|
||||
.module.clock .moon {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.module.clock .sun > *,
|
||||
.module.clock .moon > * {
|
||||
flex: 1;
|
||||
}
|
||||
.clockCircle {
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
.clockFace {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.clockFace::after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
margin: -3px 0 0 -3px;
|
||||
background: white;
|
||||
border-radius: 3px;
|
||||
content: "";
|
||||
display: block;
|
||||
}
|
||||
|
||||
.clockHour {
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -2px 0 -2px -25%; /* numbers much match negative length & thickness */
|
||||
padding: 2px 0 2px 25%; /* indicator length & thickness */
|
||||
background: white;
|
||||
transform-origin: 100% 50%;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
|
||||
.clockMinute {
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -35% -2px 0; /* numbers must match negative length & thickness */
|
||||
padding: 35% 2px 0; /* indicator length & thickness */
|
||||
background: white;
|
||||
transform-origin: 50% 100%;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
|
||||
.clockSecond {
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -38% -1px 0 0; /* numbers must match negative length & thickness */
|
||||
padding: 38% 1px 0 0; /* indicator length & thickness */
|
||||
background: #888;
|
||||
transform-origin: 50% 100%;
|
||||
}
|
||||
|
||||
.module.clock .sun,
|
||||
.module.clock .moon {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.module.clock .sun > *,
|
||||
.module.clock .moon > * {
|
||||
flex: 1;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Module: Compliments
|
||||
|
||||
The `compliments` module is one of the default modules of the MagicMirror.
|
||||
This module displays a random compliment.
|
||||
|
||||
|
|
|
@ -5,31 +5,14 @@
|
|||
* MIT Licensed.
|
||||
*/
|
||||
Module.register("compliments", {
|
||||
|
||||
// Module config defaults.
|
||||
defaults: {
|
||||
compliments: {
|
||||
anytime: [
|
||||
"Hey there sexy!"
|
||||
],
|
||||
morning: [
|
||||
"Good morning, handsome!",
|
||||
"Enjoy your day!",
|
||||
"How was your sleep?"
|
||||
],
|
||||
afternoon: [
|
||||
"Hello, beauty!",
|
||||
"You look sexy!",
|
||||
"Looking good today!"
|
||||
],
|
||||
evening: [
|
||||
"Wow, you look hot!",
|
||||
"You look nice!",
|
||||
"Hi, sexy!"
|
||||
],
|
||||
"....-01-01": [
|
||||
"Happy new year!"
|
||||
]
|
||||
anytime: ["Hey there sexy!"],
|
||||
morning: ["Good morning, handsome!", "Enjoy your day!", "How was your sleep?"],
|
||||
afternoon: ["Hello, beauty!", "You look sexy!", "Looking good today!"],
|
||||
evening: ["Wow, you look hot!", "You look nice!", "Hi, sexy!"],
|
||||
"....-01-01": ["Happy new year!"]
|
||||
},
|
||||
updateInterval: 30000,
|
||||
remoteFile: null,
|
||||
|
@ -41,31 +24,31 @@ Module.register("compliments", {
|
|||
random: true,
|
||||
mockDate: null
|
||||
},
|
||||
lastIndexUsed:-1,
|
||||
lastIndexUsed: -1,
|
||||
// Set currentweather from module
|
||||
currentWeatherType: "",
|
||||
|
||||
// Define required scripts.
|
||||
getScripts: function() {
|
||||
getScripts: function () {
|
||||
return ["moment.js"];
|
||||
},
|
||||
|
||||
// Define start sequence.
|
||||
start: function() {
|
||||
start: function () {
|
||||
Log.info("Starting module: " + this.name);
|
||||
|
||||
this.lastComplimentIndex = -1;
|
||||
|
||||
var self = this;
|
||||
if (this.config.remoteFile !== null) {
|
||||
this.complimentFile(function(response) {
|
||||
this.complimentFile(function (response) {
|
||||
self.config.compliments = JSON.parse(response);
|
||||
self.updateDom();
|
||||
});
|
||||
}
|
||||
|
||||
// Schedule update timer.
|
||||
setInterval(function() {
|
||||
setInterval(function () {
|
||||
self.updateDom(self.config.fadeSpeed);
|
||||
}, this.config.updateInterval);
|
||||
},
|
||||
|
@ -77,12 +60,12 @@ Module.register("compliments", {
|
|||
*
|
||||
* return Number - Random index.
|
||||
*/
|
||||
randomIndex: function(compliments) {
|
||||
randomIndex: function (compliments) {
|
||||
if (compliments.length === 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var generate = function() {
|
||||
var generate = function () {
|
||||
return Math.floor(Math.random() * compliments.length);
|
||||
};
|
||||
|
||||
|
@ -102,7 +85,7 @@ Module.register("compliments", {
|
|||
*
|
||||
* return compliments Array<String> - Array with compliments for the time of the day.
|
||||
*/
|
||||
complimentArray: function() {
|
||||
complimentArray: function () {
|
||||
var hour = moment().hour();
|
||||
var date = this.config.mockDate ? this.config.mockDate : moment().format("YYYY-MM-DD");
|
||||
var compliments;
|
||||
|
@ -111,7 +94,7 @@ Module.register("compliments", {
|
|||
compliments = this.config.compliments.morning.slice(0);
|
||||
} else if (hour >= this.config.afternoonStartTime && hour < this.config.afternoonEndTime && this.config.compliments.hasOwnProperty("afternoon")) {
|
||||
compliments = this.config.compliments.afternoon.slice(0);
|
||||
} else if(this.config.compliments.hasOwnProperty("evening")) {
|
||||
} else if (this.config.compliments.hasOwnProperty("evening")) {
|
||||
compliments = this.config.compliments.evening.slice(0);
|
||||
}
|
||||
|
||||
|
@ -137,13 +120,13 @@ Module.register("compliments", {
|
|||
/* complimentFile(callback)
|
||||
* Retrieve a file from the local filesystem
|
||||
*/
|
||||
complimentFile: function(callback) {
|
||||
complimentFile: function (callback) {
|
||||
var xobj = new XMLHttpRequest(),
|
||||
isRemote = this.config.remoteFile.indexOf("http://") === 0 || this.config.remoteFile.indexOf("https://") === 0,
|
||||
path = isRemote ? this.config.remoteFile : this.file(this.config.remoteFile);
|
||||
xobj.overrideMimeType("application/json");
|
||||
xobj.open("GET", path, true);
|
||||
xobj.onreadystatechange = function() {
|
||||
xobj.onreadystatechange = function () {
|
||||
if (xobj.readyState === 4 && xobj.status === 200) {
|
||||
callback(xobj.responseText);
|
||||
}
|
||||
|
@ -156,27 +139,26 @@ Module.register("compliments", {
|
|||
*
|
||||
* return compliment string - A compliment.
|
||||
*/
|
||||
randomCompliment: function() {
|
||||
randomCompliment: function () {
|
||||
// get the current time of day compliments list
|
||||
var compliments = this.complimentArray();
|
||||
// variable for index to next message to display
|
||||
let index = 0;
|
||||
// are we randomizing
|
||||
if(this.config.random){
|
||||
if (this.config.random) {
|
||||
// yes
|
||||
index = this.randomIndex(compliments);
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
// no, sequential
|
||||
// if doing sequential, don't fall off the end
|
||||
index = (this.lastIndexUsed >= (compliments.length-1))?0: ++this.lastIndexUsed;
|
||||
index = this.lastIndexUsed >= compliments.length - 1 ? 0 : ++this.lastIndexUsed;
|
||||
}
|
||||
|
||||
return compliments[index] || "";
|
||||
},
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function() {
|
||||
getDom: function () {
|
||||
var wrapper = document.createElement("div");
|
||||
wrapper.className = this.config.classes ? this.config.classes : "thin xlarge bright pre-line";
|
||||
// get the compliment text
|
||||
|
@ -186,7 +168,7 @@ Module.register("compliments", {
|
|||
// create a span to hold it all
|
||||
var compliment = document.createElement("span");
|
||||
// process all the parts of the compliment text
|
||||
for (var part of parts){
|
||||
for (var part of parts) {
|
||||
// create a text element for each part
|
||||
compliment.appendChild(document.createTextNode(part));
|
||||
// add a break `
|
||||
|
@ -200,7 +182,7 @@ Module.register("compliments", {
|
|||
},
|
||||
|
||||
// From data currentweather set weather type
|
||||
setCurrentWeatherType: function(data) {
|
||||
setCurrentWeatherType: function (data) {
|
||||
var weatherIconTable = {
|
||||
"01d": "day_sunny",
|
||||
"02d": "day_cloudy",
|
||||
|
@ -225,10 +207,9 @@ Module.register("compliments", {
|
|||
},
|
||||
|
||||
// Override notification handler.
|
||||
notificationReceived: function(notification, payload, sender) {
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
if (notification === "CURRENTWEATHER_DATA") {
|
||||
this.setCurrentWeatherType(payload.data);
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Module: Current Weather
|
||||
|
||||
The `currentweather` module is one of the default modules of the MagicMirror.
|
||||
This module displays the current weather, including the windspeed, the sunset or sunrise time, the temperature and an icon to display the current conditions.
|
||||
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
Module.register("currentweather",{
|
||||
|
||||
Module.register("currentweather", {
|
||||
// Default module config.
|
||||
defaults: {
|
||||
location: false,
|
||||
|
@ -63,7 +62,7 @@ Module.register("currentweather",{
|
|||
"11n": "wi-night-thunderstorm",
|
||||
"13n": "wi-night-snow",
|
||||
"50n": "wi-night-alt-cloudy-windy"
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
// create a variable for the first upcoming calendar event. Used if no location is specified.
|
||||
|
@ -73,17 +72,17 @@ Module.register("currentweather",{
|
|||
fetchedLocationName: "",
|
||||
|
||||
// Define required scripts.
|
||||
getScripts: function() {
|
||||
getScripts: function () {
|
||||
return ["moment.js"];
|
||||
},
|
||||
|
||||
// Define required scripts.
|
||||
getStyles: function() {
|
||||
getStyles: function () {
|
||||
return ["weather-icons.css", "currentweather.css"];
|
||||
},
|
||||
|
||||
// Define required translations.
|
||||
getTranslations: function() {
|
||||
getTranslations: function () {
|
||||
// The translations for the default modules are defined in the core translation files.
|
||||
// Therefor we can just return false. Otherwise we should have returned a dictionary.
|
||||
// If you're trying to build your own module including translations, check out the documentation.
|
||||
|
@ -91,7 +90,7 @@ Module.register("currentweather",{
|
|||
},
|
||||
|
||||
// Define start sequence.
|
||||
start: function() {
|
||||
start: function () {
|
||||
Log.info("Starting module: " + this.name);
|
||||
|
||||
// Set locale.
|
||||
|
@ -109,13 +108,11 @@ Module.register("currentweather",{
|
|||
this.feelsLike = null;
|
||||
this.loaded = false;
|
||||
this.scheduleUpdate(this.config.initialLoadDelay);
|
||||
|
||||
},
|
||||
|
||||
// add extra information of current weather
|
||||
// windDirection, humidity, sunrise and sunset
|
||||
addExtraInfoWeather: function(wrapper) {
|
||||
|
||||
addExtraInfoWeather: function (wrapper) {
|
||||
var small = document.createElement("div");
|
||||
small.className = "normal medium";
|
||||
|
||||
|
@ -130,8 +127,8 @@ Module.register("currentweather",{
|
|||
if (this.config.showWindDirection) {
|
||||
var windDirection = document.createElement("sup");
|
||||
if (this.config.showWindDirectionAsArrow) {
|
||||
if(this.windDeg !== null) {
|
||||
windDirection.innerHTML = " <i class=\"fa fa-long-arrow-down\" style=\"transform:rotate("+this.windDeg+"deg);\"></i> ";
|
||||
if (this.windDeg !== null) {
|
||||
windDirection.innerHTML = ' <i class="fa fa-long-arrow-down" style="transform:rotate(' + this.windDeg + 'deg);"></i> ';
|
||||
}
|
||||
} else {
|
||||
windDirection.innerHTML = " " + this.translate(this.windDirection);
|
||||
|
@ -170,7 +167,7 @@ Module.register("currentweather",{
|
|||
},
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function() {
|
||||
getDom: function () {
|
||||
var wrapper = document.createElement("div");
|
||||
wrapper.className = this.config.tableClass;
|
||||
|
||||
|
@ -197,17 +194,17 @@ Module.register("currentweather",{
|
|||
if (this.config.units === "metric" || this.config.units === "imperial") {
|
||||
degreeLabel += "°";
|
||||
}
|
||||
if(this.config.degreeLabel) {
|
||||
switch(this.config.units) {
|
||||
case "metric":
|
||||
degreeLabel += "C";
|
||||
break;
|
||||
case "imperial":
|
||||
degreeLabel += "F";
|
||||
break;
|
||||
case "default":
|
||||
degreeLabel += "K";
|
||||
break;
|
||||
if (this.config.degreeLabel) {
|
||||
switch (this.config.units) {
|
||||
case "metric":
|
||||
degreeLabel += "C";
|
||||
break;
|
||||
case "imperial":
|
||||
degreeLabel += "F";
|
||||
break;
|
||||
case "default":
|
||||
degreeLabel += "K";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,7 +247,7 @@ Module.register("currentweather",{
|
|||
|
||||
wrapper.appendChild(large);
|
||||
|
||||
if (this.config.showFeelsLike && this.config.onlyTemp === false){
|
||||
if (this.config.showFeelsLike && this.config.onlyTemp === false) {
|
||||
var small = document.createElement("div");
|
||||
small.className = "normal medium";
|
||||
|
||||
|
@ -266,7 +263,7 @@ Module.register("currentweather",{
|
|||
},
|
||||
|
||||
// Override getHeader method.
|
||||
getHeader: function() {
|
||||
getHeader: function () {
|
||||
if (this.config.appendLocationNameToHeader && this.data.header !== undefined) {
|
||||
return this.data.header + " " + this.fetchedLocationName;
|
||||
}
|
||||
|
@ -279,10 +276,10 @@ Module.register("currentweather",{
|
|||
},
|
||||
|
||||
// Override notification handler.
|
||||
notificationReceived: function(notification, payload, sender) {
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
if (notification === "DOM_OBJECTS_CREATED") {
|
||||
if (this.config.appendLocationNameToHeader) {
|
||||
this.hide(0, {lockString: this.identifier});
|
||||
this.hide(0, { lockString: this.identifier });
|
||||
}
|
||||
}
|
||||
if (notification === "CALENDAR_EVENTS") {
|
||||
|
@ -314,7 +311,7 @@ Module.register("currentweather",{
|
|||
* Requests new data from openweather.org.
|
||||
* Calls processWeather on succesfull response.
|
||||
*/
|
||||
updateWeather: function() {
|
||||
updateWeather: function () {
|
||||
if (this.config.appid === "") {
|
||||
Log.error("CurrentWeather: APPID not set!");
|
||||
return;
|
||||
|
@ -326,7 +323,7 @@ Module.register("currentweather",{
|
|||
|
||||
var weatherRequest = new XMLHttpRequest();
|
||||
weatherRequest.open("GET", url, true);
|
||||
weatherRequest.onreadystatechange = function() {
|
||||
weatherRequest.onreadystatechange = function () {
|
||||
if (this.readyState === 4) {
|
||||
if (this.status === 200) {
|
||||
self.processWeather(JSON.parse(this.response));
|
||||
|
@ -340,7 +337,7 @@ Module.register("currentweather",{
|
|||
}
|
||||
|
||||
if (retry) {
|
||||
self.scheduleUpdate((self.loaded) ? -1 : self.config.retryDelay);
|
||||
self.scheduleUpdate(self.loaded ? -1 : self.config.retryDelay);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -352,18 +349,18 @@ Module.register("currentweather",{
|
|||
*
|
||||
* return String - URL params.
|
||||
*/
|
||||
getParams: function() {
|
||||
getParams: function () {
|
||||
var params = "?";
|
||||
if(this.config.locationID) {
|
||||
if (this.config.locationID) {
|
||||
params += "id=" + this.config.locationID;
|
||||
} else if(this.config.location) {
|
||||
} else if (this.config.location) {
|
||||
params += "q=" + this.config.location;
|
||||
} else if (this.firstEvent && this.firstEvent.geo) {
|
||||
params += "lat=" + this.firstEvent.geo.lat + "&lon=" + this.firstEvent.geo.lon;
|
||||
} else if (this.firstEvent && this.firstEvent.location) {
|
||||
params += "q=" + this.firstEvent.location;
|
||||
} else {
|
||||
this.hide(this.config.animationSpeed, {lockString:this.identifier});
|
||||
this.hide(this.config.animationSpeed, { lockString: this.identifier });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -379,8 +376,7 @@ Module.register("currentweather",{
|
|||
*
|
||||
* argument data object - Weather information received form openweather.org.
|
||||
*/
|
||||
processWeather: function(data) {
|
||||
|
||||
processWeather: function (data) {
|
||||
if (!data || !data.main || typeof data.main.temp === "undefined") {
|
||||
// Did not receive usable new data.
|
||||
// Maybe this needs a better check?
|
||||
|
@ -392,7 +388,7 @@ Module.register("currentweather",{
|
|||
this.fetchedLocationName = data.name;
|
||||
this.feelsLike = 0;
|
||||
|
||||
if (this.config.useBeaufort){
|
||||
if (this.config.useBeaufort) {
|
||||
this.windSpeed = this.ms2Beaufort(this.roundValue(data.wind.speed));
|
||||
} else if (this.config.useKMPHwind) {
|
||||
this.windSpeed = parseFloat((data.wind.speed * 60 * 60) / 1000).toFixed(0);
|
||||
|
@ -404,50 +400,59 @@ Module.register("currentweather",{
|
|||
var windInMph = parseFloat(data.wind.speed * 2.23694);
|
||||
|
||||
var tempInF = 0;
|
||||
switch (this.config.units){
|
||||
case "metric": tempInF = 1.8 * this.temperature + 32;
|
||||
break;
|
||||
case "imperial": tempInF = this.temperature;
|
||||
break;
|
||||
case "default":
|
||||
tempInF = 1.8 * (this.temperature - 273.15) + 32;
|
||||
break;
|
||||
switch (this.config.units) {
|
||||
case "metric":
|
||||
tempInF = 1.8 * this.temperature + 32;
|
||||
break;
|
||||
case "imperial":
|
||||
tempInF = this.temperature;
|
||||
break;
|
||||
case "default":
|
||||
tempInF = 1.8 * (this.temperature - 273.15) + 32;
|
||||
break;
|
||||
}
|
||||
|
||||
if (windInMph > 3 && tempInF < 50){
|
||||
if (windInMph > 3 && tempInF < 50) {
|
||||
// windchill
|
||||
var windChillInF = Math.round(35.74+0.6215*tempInF-35.75*Math.pow(windInMph,0.16)+0.4275*tempInF*Math.pow(windInMph,0.16));
|
||||
var windChillInC = (windChillInF - 32) * (5/9);
|
||||
var windChillInF = Math.round(35.74 + 0.6215 * tempInF - 35.75 * Math.pow(windInMph, 0.16) + 0.4275 * tempInF * Math.pow(windInMph, 0.16));
|
||||
var windChillInC = (windChillInF - 32) * (5 / 9);
|
||||
// this.feelsLike = windChillInC.toFixed(0);
|
||||
|
||||
switch (this.config.units){
|
||||
case "metric": this.feelsLike = windChillInC.toFixed(0);
|
||||
break;
|
||||
case "imperial": this.feelsLike = windChillInF.toFixed(0);
|
||||
break;
|
||||
case "default":
|
||||
this.feelsLike = (windChillInC + 273.15).toFixed(0);
|
||||
break;
|
||||
switch (this.config.units) {
|
||||
case "metric":
|
||||
this.feelsLike = windChillInC.toFixed(0);
|
||||
break;
|
||||
case "imperial":
|
||||
this.feelsLike = windChillInF.toFixed(0);
|
||||
break;
|
||||
case "default":
|
||||
this.feelsLike = (windChillInC + 273.15).toFixed(0);
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (tempInF > 80 && this.humidity > 40){
|
||||
} else if (tempInF > 80 && this.humidity > 40) {
|
||||
// heat index
|
||||
var Hindex = -42.379 + 2.04901523*tempInF + 10.14333127*this.humidity
|
||||
- 0.22475541*tempInF*this.humidity - 6.83783*Math.pow(10,-3)*tempInF*tempInF
|
||||
- 5.481717*Math.pow(10,-2)*this.humidity*this.humidity
|
||||
+ 1.22874*Math.pow(10,-3)*tempInF*tempInF*this.humidity
|
||||
+ 8.5282*Math.pow(10,-4)*tempInF*this.humidity*this.humidity
|
||||
- 1.99*Math.pow(10,-6)*tempInF*tempInF*this.humidity*this.humidity;
|
||||
var Hindex =
|
||||
-42.379 +
|
||||
2.04901523 * tempInF +
|
||||
10.14333127 * this.humidity -
|
||||
0.22475541 * tempInF * this.humidity -
|
||||
6.83783 * Math.pow(10, -3) * tempInF * tempInF -
|
||||
5.481717 * Math.pow(10, -2) * this.humidity * this.humidity +
|
||||
1.22874 * Math.pow(10, -3) * tempInF * tempInF * this.humidity +
|
||||
8.5282 * Math.pow(10, -4) * tempInF * this.humidity * this.humidity -
|
||||
1.99 * Math.pow(10, -6) * tempInF * tempInF * this.humidity * this.humidity;
|
||||
|
||||
switch (this.config.units){
|
||||
case "metric": this.feelsLike = parseFloat((Hindex - 32) / 1.8).toFixed(0);
|
||||
break;
|
||||
case "imperial": this.feelsLike = Hindex.toFixed(0);
|
||||
break;
|
||||
case "default":
|
||||
var tc = parseFloat((Hindex - 32) / 1.8) + 273.15;
|
||||
this.feelsLike = tc.toFixed(0);
|
||||
break;
|
||||
switch (this.config.units) {
|
||||
case "metric":
|
||||
this.feelsLike = parseFloat((Hindex - 32) / 1.8).toFixed(0);
|
||||
break;
|
||||
case "imperial":
|
||||
this.feelsLike = Hindex.toFixed(0);
|
||||
break;
|
||||
case "default":
|
||||
var tc = parseFloat((Hindex - 32) / 1.8) + 273.15;
|
||||
this.feelsLike = tc.toFixed(0);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this.feelsLike = parseFloat(this.temperature).toFixed(0);
|
||||
|
@ -464,7 +469,7 @@ Module.register("currentweather",{
|
|||
// The moment().format('h') method has a bug on the Raspberry Pi.
|
||||
// So we need to generate the timestring manually.
|
||||
// See issue: https://github.com/MichMich/MagicMirror/issues/181
|
||||
var sunriseSunsetDateObject = (sunrise < now && sunset > now) ? sunset : sunrise;
|
||||
var sunriseSunsetDateObject = sunrise < now && sunset > now ? sunset : sunrise;
|
||||
var timeString = moment(sunriseSunsetDateObject).format("HH:mm");
|
||||
if (this.config.timeFormat !== 24) {
|
||||
//var hours = sunriseSunsetDateObject.getHours() % 12 || 12;
|
||||
|
@ -483,12 +488,12 @@ Module.register("currentweather",{
|
|||
}
|
||||
|
||||
this.sunriseSunsetTime = timeString;
|
||||
this.sunriseSunsetIcon = (sunrise < now && sunset > now) ? "wi-sunset" : "wi-sunrise";
|
||||
this.sunriseSunsetIcon = sunrise < now && sunset > now ? "wi-sunset" : "wi-sunrise";
|
||||
|
||||
this.show(this.config.animationSpeed, {lockString:this.identifier});
|
||||
this.show(this.config.animationSpeed, { lockString: this.identifier });
|
||||
this.loaded = true;
|
||||
this.updateDom(this.config.animationSpeed);
|
||||
this.sendNotification("CURRENTWEATHER_DATA", {data: data});
|
||||
this.sendNotification("CURRENTWEATHER_DATA", { data: data });
|
||||
},
|
||||
|
||||
/* scheduleUpdate()
|
||||
|
@ -496,14 +501,14 @@ Module.register("currentweather",{
|
|||
*
|
||||
* argument delay number - Milliseconds before next update. If empty, this.config.updateInterval is used.
|
||||
*/
|
||||
scheduleUpdate: function(delay) {
|
||||
scheduleUpdate: function (delay) {
|
||||
var nextLoad = this.config.updateInterval;
|
||||
if (typeof delay !== "undefined" && delay >= 0) {
|
||||
nextLoad = delay;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
self.updateWeather();
|
||||
}, nextLoad);
|
||||
},
|
||||
|
@ -519,8 +524,8 @@ Module.register("currentweather",{
|
|||
*
|
||||
* return number - Windspeed in beaufort.
|
||||
*/
|
||||
ms2Beaufort: function(ms) {
|
||||
var kmh = ms * 60 * 60 / 1000;
|
||||
ms2Beaufort: function (ms) {
|
||||
var kmh = (ms * 60 * 60) / 1000;
|
||||
var speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000];
|
||||
for (var beaufort in speeds) {
|
||||
var speed = speeds[beaufort];
|
||||
|
@ -531,8 +536,8 @@ Module.register("currentweather",{
|
|||
return 12;
|
||||
},
|
||||
|
||||
deg2Cardinal: function(deg) {
|
||||
if (deg>11.25 && deg<=33.75){
|
||||
deg2Cardinal: function (deg) {
|
||||
if (deg > 11.25 && deg <= 33.75) {
|
||||
return "NNE";
|
||||
} else if (deg > 33.75 && deg <= 56.25) {
|
||||
return "NE";
|
||||
|
@ -574,9 +579,8 @@ Module.register("currentweather",{
|
|||
*
|
||||
* return string - Rounded Temperature.
|
||||
*/
|
||||
roundValue: function(temperature) {
|
||||
roundValue: function (temperature) {
|
||||
var decimals = this.config.roundTemp ? 0 : 1;
|
||||
return parseFloat(temperature).toFixed(decimals);
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -7,18 +7,9 @@
|
|||
|
||||
// Modules listed below can be loaded without the 'default/' prefix. Omitting the default folder name.
|
||||
|
||||
var defaultModules = [
|
||||
"alert",
|
||||
"calendar",
|
||||
"clock",
|
||||
"compliments",
|
||||
"currentweather",
|
||||
"helloworld",
|
||||
"newsfeed",
|
||||
"weatherforecast",
|
||||
"updatenotification",
|
||||
"weather"
|
||||
];
|
||||
var defaultModules = ["alert", "calendar", "clock", "compliments", "currentweather", "helloworld", "newsfeed", "weatherforecast", "updatenotification", "weather"];
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = defaultModules;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = defaultModules;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Module: Hello World
|
||||
|
||||
The `helloworld` module is one of the default modules of the MagicMirror. It is a simple way to display a static text on the mirror.
|
||||
|
||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/helloworld.html).
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
Module.register("helloworld",{
|
||||
|
||||
Module.register("helloworld", {
|
||||
// Default module config.
|
||||
defaults: {
|
||||
text: "Hello World!"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Module: News Feed
|
||||
The `newsfeed ` module is one of the default modules of the MagicMirror.
|
||||
This module displays news headlines based on an RSS feed. Scrolling through news headlines happens time-based (````updateInterval````), but can also be controlled by sending news feed specific notifications to the module.
|
||||
|
||||
The `newsfeed` module is one of the default modules of the MagicMirror.
|
||||
This module displays news headlines based on an RSS feed. Scrolling through news headlines happens time-based (`updateInterval`), but can also be controlled by sending news feed specific notifications to the module.
|
||||
|
||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/newsfeed.html).
|
||||
|
|
|
@ -17,7 +17,7 @@ var iconv = require("iconv-lite");
|
|||
* attribute logFeedWarnings boolean - Log warnings when there is an error parsing a news article.
|
||||
*/
|
||||
|
||||
var Fetcher = function(url, reloadInterval, encoding, logFeedWarnings) {
|
||||
var Fetcher = function (url, reloadInterval, encoding, logFeedWarnings) {
|
||||
var self = this;
|
||||
if (reloadInterval < 1000) {
|
||||
reloadInterval = 1000;
|
||||
|
@ -26,8 +26,8 @@ var Fetcher = function(url, reloadInterval, encoding, logFeedWarnings) {
|
|||
var reloadTimer = null;
|
||||
var items = [];
|
||||
|
||||
var fetchFailedCallback = function() {};
|
||||
var itemsReceivedCallback = function() {};
|
||||
var fetchFailedCallback = function () {};
|
||||
var itemsReceivedCallback = function () {};
|
||||
|
||||
/* private methods */
|
||||
|
||||
|
@ -35,32 +35,29 @@ var Fetcher = function(url, reloadInterval, encoding, logFeedWarnings) {
|
|||
* Request the new items.
|
||||
*/
|
||||
|
||||
var fetchNews = function() {
|
||||
var fetchNews = function () {
|
||||
clearTimeout(reloadTimer);
|
||||
reloadTimer = null;
|
||||
items = [];
|
||||
|
||||
var parser = new FeedMe();
|
||||
|
||||
parser.on("item", function(item) {
|
||||
|
||||
parser.on("item", function (item) {
|
||||
var title = item.title;
|
||||
var description = item.description || item.summary || item.content || "";
|
||||
var pubdate = item.pubdate || item.published || item.updated || item["dc:date"];
|
||||
var url = item.url || item.link || "";
|
||||
|
||||
if (title && pubdate) {
|
||||
|
||||
var regex = /(<([^>]+)>)/ig;
|
||||
var regex = /(<([^>]+)>)/gi;
|
||||
description = description.toString().replace(regex, "");
|
||||
|
||||
items.push({
|
||||
title: title,
|
||||
description: description,
|
||||
pubdate: pubdate,
|
||||
url: url,
|
||||
url: url
|
||||
});
|
||||
|
||||
} else if (logFeedWarnings) {
|
||||
console.log("Can't parse feed item:");
|
||||
console.log(item);
|
||||
|
@ -70,39 +67,37 @@ var Fetcher = function(url, reloadInterval, encoding, logFeedWarnings) {
|
|||
}
|
||||
});
|
||||
|
||||
parser.on("end", function() {
|
||||
parser.on("end", function () {
|
||||
//console.log("end parsing - " + url);
|
||||
self.broadcastItems();
|
||||
scheduleTimer();
|
||||
});
|
||||
|
||||
parser.on("error", function(error) {
|
||||
parser.on("error", function (error) {
|
||||
fetchFailedCallback(self, error);
|
||||
scheduleTimer();
|
||||
});
|
||||
|
||||
var nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
|
||||
var headers = {"User-Agent": "Mozilla/5.0 (Node.js "+ nodeVersion + ") MagicMirror/" + global.version + " (https://github.com/MichMich/MagicMirror/)",
|
||||
"Cache-Control": "max-age=0, no-cache, no-store, must-revalidate",
|
||||
"Pragma": "no-cache"};
|
||||
var headers = { "User-Agent": "Mozilla/5.0 (Node.js " + nodeVersion + ") MagicMirror/" + global.version + " (https://github.com/MichMich/MagicMirror/)", "Cache-Control": "max-age=0, no-cache, no-store, must-revalidate", Pragma: "no-cache" };
|
||||
|
||||
request({uri: url, encoding: null, headers: headers})
|
||||
.on("error", function(error) {
|
||||
request({ uri: url, encoding: null, headers: headers })
|
||||
.on("error", function (error) {
|
||||
fetchFailedCallback(self, error);
|
||||
scheduleTimer();
|
||||
})
|
||||
.pipe(iconv.decodeStream(encoding)).pipe(parser);
|
||||
|
||||
.pipe(iconv.decodeStream(encoding))
|
||||
.pipe(parser);
|
||||
};
|
||||
|
||||
/* scheduleTimer()
|
||||
* Schedule the timer for the next update.
|
||||
*/
|
||||
|
||||
var scheduleTimer = function() {
|
||||
var scheduleTimer = function () {
|
||||
//console.log('Schedule update timer.');
|
||||
clearTimeout(reloadTimer);
|
||||
reloadTimer = setTimeout(function() {
|
||||
reloadTimer = setTimeout(function () {
|
||||
fetchNews();
|
||||
}, reloadInterval);
|
||||
};
|
||||
|
@ -114,7 +109,7 @@ var Fetcher = function(url, reloadInterval, encoding, logFeedWarnings) {
|
|||
*
|
||||
* attribute interval number - Interval for the update in milliseconds.
|
||||
*/
|
||||
this.setReloadInterval = function(interval) {
|
||||
this.setReloadInterval = function (interval) {
|
||||
if (interval > 1000 && interval < reloadInterval) {
|
||||
reloadInterval = interval;
|
||||
}
|
||||
|
@ -123,14 +118,14 @@ var Fetcher = function(url, reloadInterval, encoding, logFeedWarnings) {
|
|||
/* startFetch()
|
||||
* Initiate fetchNews();
|
||||
*/
|
||||
this.startFetch = function() {
|
||||
this.startFetch = function () {
|
||||
fetchNews();
|
||||
};
|
||||
|
||||
/* broadcastItems()
|
||||
* Broadcast the existing items.
|
||||
*/
|
||||
this.broadcastItems = function() {
|
||||
this.broadcastItems = function () {
|
||||
if (items.length <= 0) {
|
||||
//console.log('No items to broadcast yet.');
|
||||
return;
|
||||
|
@ -139,19 +134,19 @@ var Fetcher = function(url, reloadInterval, encoding, logFeedWarnings) {
|
|||
itemsReceivedCallback(self);
|
||||
};
|
||||
|
||||
this.onReceive = function(callback) {
|
||||
this.onReceive = function (callback) {
|
||||
itemsReceivedCallback = callback;
|
||||
};
|
||||
|
||||
this.onError = function(callback) {
|
||||
this.onError = function (callback) {
|
||||
fetchFailedCallback = callback;
|
||||
};
|
||||
|
||||
this.url = function() {
|
||||
this.url = function () {
|
||||
return url;
|
||||
};
|
||||
|
||||
this.items = function() {
|
||||
this.items = function () {
|
||||
return items;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
Module.register("newsfeed",{
|
||||
|
||||
Module.register("newsfeed", {
|
||||
// Default module config.
|
||||
defaults: {
|
||||
feeds: [
|
||||
|
@ -41,12 +40,12 @@ Module.register("newsfeed",{
|
|||
},
|
||||
|
||||
// Define required scripts.
|
||||
getScripts: function() {
|
||||
getScripts: function () {
|
||||
return ["moment.js"];
|
||||
},
|
||||
|
||||
// Define required translations.
|
||||
getTranslations: function() {
|
||||
getTranslations: function () {
|
||||
// The translations for the default modules are defined in the core translation files.
|
||||
// Therefor we can just return false. Otherwise we should have returned a dictionary.
|
||||
// If you're trying to build your own module including translations, check out the documentation.
|
||||
|
@ -54,7 +53,7 @@ Module.register("newsfeed",{
|
|||
},
|
||||
|
||||
// Define start sequence.
|
||||
start: function() {
|
||||
start: function () {
|
||||
Log.info("Starting module: " + this.name);
|
||||
|
||||
// Set locale.
|
||||
|
@ -71,7 +70,7 @@ Module.register("newsfeed",{
|
|||
},
|
||||
|
||||
// Override socket notification handler.
|
||||
socketNotificationReceived: function(notification, payload) {
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
if (notification === "NEWS_ITEMS") {
|
||||
this.generateFeed(payload);
|
||||
|
||||
|
@ -84,7 +83,7 @@ Module.register("newsfeed",{
|
|||
},
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function() {
|
||||
getDom: function () {
|
||||
var wrapper = document.createElement("div");
|
||||
|
||||
if (this.config.feedUrl) {
|
||||
|
@ -98,7 +97,6 @@ Module.register("newsfeed",{
|
|||
}
|
||||
|
||||
if (this.newsItems.length > 0) {
|
||||
|
||||
// this.config.showFullArticle is a run-time configuration, triggered by optional notifications
|
||||
if (!this.config.showFullArticle && (this.config.showSourceTitle || this.config.showPublishDate)) {
|
||||
var sourceAndTimestamp = document.createElement("div");
|
||||
|
@ -113,7 +111,7 @@ Module.register("newsfeed",{
|
|||
if (this.config.showPublishDate) {
|
||||
sourceAndTimestamp.innerHTML += moment(new Date(this.newsItems[this.activeItem].pubdate)).fromNow();
|
||||
}
|
||||
if (this.config.showSourceTitle && this.newsItems[this.activeItem].sourceTitle !== "" || this.config.showPublishDate) {
|
||||
if ((this.config.showSourceTitle && this.newsItems[this.activeItem].sourceTitle !== "") || this.config.showPublishDate) {
|
||||
sourceAndTimestamp.innerHTML += ":";
|
||||
}
|
||||
|
||||
|
@ -123,47 +121,42 @@ Module.register("newsfeed",{
|
|||
//Remove selected tags from the beginning of rss feed items (title or description)
|
||||
|
||||
if (this.config.removeStartTags === "title" || this.config.removeStartTags === "both") {
|
||||
|
||||
for (let f=0; f<this.config.startTags.length;f++) {
|
||||
if (this.newsItems[this.activeItem].title.slice(0,this.config.startTags[f].length) === this.config.startTags[f]) {
|
||||
this.newsItems[this.activeItem].title = this.newsItems[this.activeItem].title.slice(this.config.startTags[f].length,this.newsItems[this.activeItem].title.length);
|
||||
for (let f = 0; f < this.config.startTags.length; f++) {
|
||||
if (this.newsItems[this.activeItem].title.slice(0, this.config.startTags[f].length) === this.config.startTags[f]) {
|
||||
this.newsItems[this.activeItem].title = this.newsItems[this.activeItem].title.slice(this.config.startTags[f].length, this.newsItems[this.activeItem].title.length);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (this.config.removeStartTags === "description" || this.config.removeStartTags === "both") {
|
||||
|
||||
if (this.isShowingDescription) {
|
||||
for (let f=0; f<this.config.startTags.length;f++) {
|
||||
if (this.newsItems[this.activeItem].description.slice(0,this.config.startTags[f].length) === this.config.startTags[f]) {
|
||||
this.newsItems[this.activeItem].description = this.newsItems[this.activeItem].description.slice(this.config.startTags[f].length,this.newsItems[this.activeItem].description.length);
|
||||
for (let f = 0; f < this.config.startTags.length; f++) {
|
||||
if (this.newsItems[this.activeItem].description.slice(0, this.config.startTags[f].length) === this.config.startTags[f]) {
|
||||
this.newsItems[this.activeItem].description = this.newsItems[this.activeItem].description.slice(this.config.startTags[f].length, this.newsItems[this.activeItem].description.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Remove selected tags from the end of rss feed items (title or description)
|
||||
|
||||
if (this.config.removeEndTags) {
|
||||
for (let f=0; f<this.config.endTags.length;f++) {
|
||||
if (this.newsItems[this.activeItem].title.slice(-this.config.endTags[f].length)===this.config.endTags[f]) {
|
||||
this.newsItems[this.activeItem].title = this.newsItems[this.activeItem].title.slice(0,-this.config.endTags[f].length);
|
||||
for (let f = 0; f < this.config.endTags.length; f++) {
|
||||
if (this.newsItems[this.activeItem].title.slice(-this.config.endTags[f].length) === this.config.endTags[f]) {
|
||||
this.newsItems[this.activeItem].title = this.newsItems[this.activeItem].title.slice(0, -this.config.endTags[f].length);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isShowingDescription) {
|
||||
for (let f=0; f<this.config.endTags.length;f++) {
|
||||
if (this.newsItems[this.activeItem].description.slice(-this.config.endTags[f].length)===this.config.endTags[f]) {
|
||||
this.newsItems[this.activeItem].description = this.newsItems[this.activeItem].description.slice(0,-this.config.endTags[f].length);
|
||||
for (let f = 0; f < this.config.endTags.length; f++) {
|
||||
if (this.newsItems[this.activeItem].description.slice(-this.config.endTags[f].length) === this.config.endTags[f]) {
|
||||
this.newsItems[this.activeItem].description = this.newsItems[this.activeItem].description.slice(0, -this.config.endTags[f].length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!this.config.showFullArticle){
|
||||
if (!this.config.showFullArticle) {
|
||||
var title = document.createElement("div");
|
||||
title.className = "newsfeed-title bright medium light" + (!this.config.wrapTitle ? " no-wrap" : "");
|
||||
title.innerHTML = this.newsItems[this.activeItem].title;
|
||||
|
@ -174,7 +167,7 @@ Module.register("newsfeed",{
|
|||
var description = document.createElement("div");
|
||||
description.className = "newsfeed-desc small light" + (!this.config.wrapDescription ? " no-wrap" : "");
|
||||
var txtDesc = this.newsItems[this.activeItem].description;
|
||||
description.innerHTML = (this.config.truncDescription ? (txtDesc.length > this.config.lengthDescription ? txtDesc.substring(0, this.config.lengthDescription) + "..." : txtDesc) : txtDesc);
|
||||
description.innerHTML = this.config.truncDescription ? (txtDesc.length > this.config.lengthDescription ? txtDesc.substring(0, this.config.lengthDescription) + "..." : txtDesc) : txtDesc;
|
||||
wrapper.appendChild(description);
|
||||
}
|
||||
|
||||
|
@ -196,7 +189,6 @@ Module.register("newsfeed",{
|
|||
if (this.config.hideLoading) {
|
||||
this.show();
|
||||
}
|
||||
|
||||
} else {
|
||||
if (this.config.hideLoading) {
|
||||
this.hide();
|
||||
|
@ -209,14 +201,14 @@ Module.register("newsfeed",{
|
|||
return wrapper;
|
||||
},
|
||||
|
||||
getActiveItemURL: function() {
|
||||
getActiveItemURL: function () {
|
||||
return typeof this.newsItems[this.activeItem].url === "string" ? this.newsItems[this.activeItem].url : this.newsItems[this.activeItem].url.href;
|
||||
},
|
||||
|
||||
/* registerFeeds()
|
||||
* registers the feeds to be used by the backend.
|
||||
*/
|
||||
registerFeeds: function() {
|
||||
registerFeeds: function () {
|
||||
for (var f in this.config.feeds) {
|
||||
var feed = this.config.feeds[f];
|
||||
this.sendSocketNotification("ADD_FEED", {
|
||||
|
@ -231,7 +223,7 @@ Module.register("newsfeed",{
|
|||
*
|
||||
* attribute feeds object - An object with feeds returned by the node helper.
|
||||
*/
|
||||
generateFeed: function(feeds) {
|
||||
generateFeed: function (feeds) {
|
||||
var newsItems = [];
|
||||
for (var feed in feeds) {
|
||||
var feedItems = feeds[feed];
|
||||
|
@ -239,24 +231,24 @@ Module.register("newsfeed",{
|
|||
for (var i in feedItems) {
|
||||
var item = feedItems[i];
|
||||
item.sourceTitle = this.titleForFeed(feed);
|
||||
if (!(this.config.ignoreOldItems && ((Date.now() - new Date(item.pubdate)) > this.config.ignoreOlderThan))) {
|
||||
if (!(this.config.ignoreOldItems && Date.now() - new Date(item.pubdate) > this.config.ignoreOlderThan)) {
|
||||
newsItems.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
newsItems.sort(function(a,b) {
|
||||
newsItems.sort(function (a, b) {
|
||||
var dateA = new Date(a.pubdate);
|
||||
var dateB = new Date(b.pubdate);
|
||||
return dateB - dateA;
|
||||
});
|
||||
if(this.config.maxNewsItems > 0) {
|
||||
if (this.config.maxNewsItems > 0) {
|
||||
newsItems = newsItems.slice(0, this.config.maxNewsItems);
|
||||
}
|
||||
|
||||
if(this.config.prohibitedWords.length > 0) {
|
||||
newsItems = newsItems.filter(function(value){
|
||||
for (var i=0; i < this.config.prohibitedWords.length; i++) {
|
||||
if (this.config.prohibitedWords.length > 0) {
|
||||
newsItems = newsItems.filter(function (value) {
|
||||
for (var i = 0; i < this.config.prohibitedWords.length; i++) {
|
||||
if (value["title"].toLowerCase().indexOf(this.config.prohibitedWords[i].toLowerCase()) > -1) {
|
||||
return false;
|
||||
}
|
||||
|
@ -267,8 +259,8 @@ Module.register("newsfeed",{
|
|||
|
||||
// get updated news items and broadcast them
|
||||
var updatedItems = [];
|
||||
newsItems.forEach(value => {
|
||||
if (this.newsItems.findIndex(value1 => value1 === value) === -1) {
|
||||
newsItems.forEach((value) => {
|
||||
if (this.newsItems.findIndex((value1) => value1 === value) === -1) {
|
||||
// Add item to updated items list
|
||||
updatedItems.push(value);
|
||||
}
|
||||
|
@ -276,7 +268,7 @@ Module.register("newsfeed",{
|
|||
|
||||
// check if updated items exist, if so and if we should broadcast these updates, then lets do so
|
||||
if (this.config.broadcastNewsUpdates && updatedItems.length > 0) {
|
||||
this.sendNotification("NEWS_FEED_UPDATE", {items: updatedItems});
|
||||
this.sendNotification("NEWS_FEED_UPDATE", { items: updatedItems });
|
||||
}
|
||||
|
||||
this.newsItems = newsItems;
|
||||
|
@ -289,7 +281,7 @@ Module.register("newsfeed",{
|
|||
*
|
||||
* returns bool
|
||||
*/
|
||||
subscribedToFeed: function(feedUrl) {
|
||||
subscribedToFeed: function (feedUrl) {
|
||||
for (var f in this.config.feeds) {
|
||||
var feed = this.config.feeds[f];
|
||||
if (feed.url === feedUrl) {
|
||||
|
@ -306,7 +298,7 @@ Module.register("newsfeed",{
|
|||
*
|
||||
* returns string
|
||||
*/
|
||||
titleForFeed: function(feedUrl) {
|
||||
titleForFeed: function (feedUrl) {
|
||||
for (var f in this.config.feeds) {
|
||||
var feed = this.config.feeds[f];
|
||||
if (feed.url === feedUrl) {
|
||||
|
@ -319,23 +311,23 @@ Module.register("newsfeed",{
|
|||
/* scheduleUpdateInterval()
|
||||
* Schedule visual update.
|
||||
*/
|
||||
scheduleUpdateInterval: function() {
|
||||
scheduleUpdateInterval: function () {
|
||||
var self = this;
|
||||
|
||||
self.updateDom(self.config.animationSpeed);
|
||||
|
||||
// Broadcast NewsFeed if needed
|
||||
if (self.config.broadcastNewsFeeds) {
|
||||
self.sendNotification("NEWS_FEED", {items: self.newsItems});
|
||||
self.sendNotification("NEWS_FEED", { items: self.newsItems });
|
||||
}
|
||||
|
||||
this.timer = setInterval(function() {
|
||||
this.timer = setInterval(function () {
|
||||
self.activeItem++;
|
||||
self.updateDom(self.config.animationSpeed);
|
||||
|
||||
// Broadcast NewsFeed if needed
|
||||
if (self.config.broadcastNewsFeeds) {
|
||||
self.sendNotification("NEWS_FEED", {items: self.newsItems});
|
||||
self.sendNotification("NEWS_FEED", { items: self.newsItems });
|
||||
}
|
||||
}, this.config.updateInterval);
|
||||
},
|
||||
|
@ -347,25 +339,25 @@ Module.register("newsfeed",{
|
|||
*
|
||||
* return string - Capitalized output string.
|
||||
*/
|
||||
capitalizeFirstLetter: function(string) {
|
||||
capitalizeFirstLetter: function (string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
},
|
||||
|
||||
resetDescrOrFullArticleAndTimer: function() {
|
||||
resetDescrOrFullArticleAndTimer: function () {
|
||||
this.isShowingDescription = this.config.showDescription;
|
||||
this.config.showFullArticle = false;
|
||||
this.scrollPosition = 0;
|
||||
// reset bottom bar alignment
|
||||
document.getElementsByClassName("region bottom bar")[0].style.bottom = "0";
|
||||
document.getElementsByClassName("region bottom bar")[0].style.top = "inherit";
|
||||
if(!this.timer){
|
||||
if (!this.timer) {
|
||||
this.scheduleUpdateInterval();
|
||||
}
|
||||
},
|
||||
|
||||
notificationReceived: function(notification, payload, sender) {
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
var before = this.activeItem;
|
||||
if(notification === "ARTICLE_NEXT"){
|
||||
if (notification === "ARTICLE_NEXT") {
|
||||
this.activeItem++;
|
||||
if (this.activeItem >= this.newsItems.length) {
|
||||
this.activeItem = 0;
|
||||
|
@ -373,7 +365,7 @@ Module.register("newsfeed",{
|
|||
this.resetDescrOrFullArticleAndTimer();
|
||||
Log.info(this.name + " - going from article #" + before + " to #" + this.activeItem + " (of " + this.newsItems.length + ")");
|
||||
this.updateDom(100);
|
||||
} else if(notification === "ARTICLE_PREVIOUS"){
|
||||
} else if (notification === "ARTICLE_PREVIOUS") {
|
||||
this.activeItem--;
|
||||
if (this.activeItem < 0) {
|
||||
this.activeItem = this.newsItems.length - 1;
|
||||
|
@ -383,51 +375,50 @@ Module.register("newsfeed",{
|
|||
this.updateDom(100);
|
||||
}
|
||||
// if "more details" is received the first time: show article summary, on second time show full article
|
||||
else if(notification === "ARTICLE_MORE_DETAILS"){
|
||||
else if (notification === "ARTICLE_MORE_DETAILS") {
|
||||
// full article is already showing, so scrolling down
|
||||
if(this.config.showFullArticle === true){
|
||||
if (this.config.showFullArticle === true) {
|
||||
this.scrollPosition += this.config.scrollLength;
|
||||
window.scrollTo(0, this.scrollPosition);
|
||||
Log.info(this.name + " - scrolling down");
|
||||
Log.info(this.name + " - ARTICLE_MORE_DETAILS, scroll position: " + this.config.scrollLength);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.showFullArticle();
|
||||
}
|
||||
} else if(notification === "ARTICLE_SCROLL_UP"){
|
||||
if(this.config.showFullArticle === true){
|
||||
} else if (notification === "ARTICLE_SCROLL_UP") {
|
||||
if (this.config.showFullArticle === true) {
|
||||
this.scrollPosition -= this.config.scrollLength;
|
||||
window.scrollTo(0, this.scrollPosition);
|
||||
Log.info(this.name + " - scrolling up");
|
||||
Log.info(this.name + " - ARTICLE_SCROLL_UP, scroll position: " + this.config.scrollLength);
|
||||
}
|
||||
} else if(notification === "ARTICLE_LESS_DETAILS"){
|
||||
} else if (notification === "ARTICLE_LESS_DETAILS") {
|
||||
this.resetDescrOrFullArticleAndTimer();
|
||||
Log.info(this.name + " - showing only article titles again");
|
||||
this.updateDom(100);
|
||||
} else if (notification === "ARTICLE_TOGGLE_FULL"){
|
||||
if (this.config.showFullArticle){
|
||||
} else if (notification === "ARTICLE_TOGGLE_FULL") {
|
||||
if (this.config.showFullArticle) {
|
||||
this.activeItem++;
|
||||
this.resetDescrOrFullArticleAndTimer();
|
||||
} else {
|
||||
this.showFullArticle();
|
||||
}
|
||||
} else if (notification === "ARTICLE_INFO_REQUEST"){
|
||||
} else if (notification === "ARTICLE_INFO_REQUEST") {
|
||||
this.sendNotification("ARTICLE_INFO_RESPONSE", {
|
||||
title: this.newsItems[this.activeItem].title,
|
||||
title: this.newsItems[this.activeItem].title,
|
||||
source: this.newsItems[this.activeItem].sourceTitle,
|
||||
date: this.newsItems[this.activeItem].pubdate,
|
||||
desc: this.newsItems[this.activeItem].description,
|
||||
url: this.getActiveItemURL()
|
||||
date: this.newsItems[this.activeItem].pubdate,
|
||||
desc: this.newsItems[this.activeItem].description,
|
||||
url: this.getActiveItemURL()
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
showFullArticle: function() {
|
||||
showFullArticle: function () {
|
||||
this.isShowingDescription = !this.isShowingDescription;
|
||||
this.config.showFullArticle = !this.isShowingDescription;
|
||||
// make bottom bar align to top to allow scrolling
|
||||
if(this.config.showFullArticle === true){
|
||||
if (this.config.showFullArticle === true) {
|
||||
document.getElementsByClassName("region bottom bar")[0].style.bottom = "inherit";
|
||||
document.getElementsByClassName("region bottom bar")[0].style.top = "-90px";
|
||||
}
|
||||
|
|
|
@ -11,13 +11,13 @@ var Fetcher = require("./fetcher.js");
|
|||
|
||||
module.exports = NodeHelper.create({
|
||||
// Subclass start method.
|
||||
start: function() {
|
||||
start: function () {
|
||||
console.log("Starting module: " + this.name);
|
||||
this.fetchers = [];
|
||||
},
|
||||
|
||||
// Subclass socketNotificationReceived received.
|
||||
socketNotificationReceived: function(notification, payload) {
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
if (notification === "ADD_FEED") {
|
||||
this.createFetcher(payload.feed, payload.config);
|
||||
return;
|
||||
|
@ -31,7 +31,7 @@ module.exports = NodeHelper.create({
|
|||
* attribute feed object - A feed object.
|
||||
* attribute config object - A configuration object containing reload interval in milliseconds.
|
||||
*/
|
||||
createFetcher: function(feed, config) {
|
||||
createFetcher: function (feed, config) {
|
||||
var self = this;
|
||||
|
||||
var url = feed.url || "";
|
||||
|
@ -48,11 +48,11 @@ module.exports = NodeHelper.create({
|
|||
console.log("Create new news fetcher for url: " + url + " - Interval: " + reloadInterval);
|
||||
fetcher = new Fetcher(url, reloadInterval, encoding, config.logFeedWarnings);
|
||||
|
||||
fetcher.onReceive(function(fetcher) {
|
||||
fetcher.onReceive(function (fetcher) {
|
||||
self.broadcastFeeds();
|
||||
});
|
||||
|
||||
fetcher.onError(function(fetcher, error) {
|
||||
fetcher.onError(function (fetcher, error) {
|
||||
self.sendSocketNotification("FETCH_ERROR", {
|
||||
url: fetcher.url(),
|
||||
error: error
|
||||
|
@ -74,7 +74,7 @@ module.exports = NodeHelper.create({
|
|||
* Creates an object with all feed items of the different registered feeds,
|
||||
* and broadcasts these using sendSocketNotification.
|
||||
*/
|
||||
broadcastFeeds: function() {
|
||||
broadcastFeeds: function () {
|
||||
var feeds = {};
|
||||
for (var f in this.fetchers) {
|
||||
feeds[f] = this.fetchers[f].items();
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"configuration_changed": "Die Konfigurationsoptionen für das Newsfeed-Modul haben sich geändert. \nBitte überprüfen Sie die Dokumentation."
|
||||
}
|
||||
"configuration_changed": "Die Konfigurationsoptionen für das Newsfeed-Modul haben sich geändert. \nBitte überprüfen Sie die Dokumentation."
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"configuration_changed": "The configuration options for the newsfeed module have changed.\nPlease check the documentation."
|
||||
}
|
||||
"configuration_changed": "The configuration options for the newsfeed module have changed.\nPlease check the documentation."
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"configuration_changed": "Las opciones de configuración para el módulo de suministro de noticias han cambiado. \nVerifique la documentación."
|
||||
}
|
||||
"configuration_changed": "Las opciones de configuración para el módulo de suministro de noticias han cambiado. \nVerifique la documentación."
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"configuration_changed": "Les options de configuration du module newsfeed ont changé. \nVeuillez consulter la documentation."
|
||||
}
|
||||
"configuration_changed": "Les options de configuration du module newsfeed ont changé. \nVeuillez consulter la documentation."
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Module: Update Notification
|
||||
|
||||
The `updatenotification` module is one of the default modules of the MagicMirror.
|
||||
This will display a message whenever a new version of the MagicMirror application is available.
|
||||
|
||||
|
|
|
@ -6,21 +6,18 @@ var defaultModules = require(__dirname + "/../defaultmodules.js");
|
|||
var NodeHelper = require("node_helper");
|
||||
|
||||
module.exports = NodeHelper.create({
|
||||
|
||||
config: {},
|
||||
|
||||
updateTimer: null,
|
||||
updateProcessStarted: false,
|
||||
|
||||
start: function () {
|
||||
},
|
||||
|
||||
configureModules: function(modules) {
|
||||
start: function () {},
|
||||
|
||||
configureModules: function (modules) {
|
||||
// Push MagicMirror itself , biggest chance it'll show up last in UI and isn't overwritten
|
||||
// others will be added in front
|
||||
// this method returns promises so we can't wait for every one to resolve before continuing
|
||||
simpleGits.push({"module": "default", "git": SimpleGit(path.normalize(__dirname + "/../../../"))});
|
||||
simpleGits.push({ module: "default", git: SimpleGit(path.normalize(__dirname + "/../../../")) });
|
||||
|
||||
var promises = [];
|
||||
|
||||
|
@ -33,7 +30,7 @@ module.exports = NodeHelper.create({
|
|||
//console.log("checking git for module="+moduleName)
|
||||
let stat = fs.statSync(path.join(moduleFolder, ".git"));
|
||||
promises.push(this.resolveRemote(moduleName, moduleFolder));
|
||||
} catch(err) {
|
||||
} catch (err) {
|
||||
// Error when directory .git doesn't exist
|
||||
// This module is not managed with git, skip
|
||||
continue;
|
||||
|
@ -47,7 +44,7 @@ module.exports = NodeHelper.create({
|
|||
socketNotificationReceived: function (notification, payload) {
|
||||
if (notification === "CONFIG") {
|
||||
this.config = payload;
|
||||
} else if(notification === "MODULES") {
|
||||
} else if (notification === "MODULES") {
|
||||
// if this is the 1st time thru the update check process
|
||||
if (!this.updateProcessStarted) {
|
||||
this.updateProcessStarted = true;
|
||||
|
@ -56,7 +53,7 @@ module.exports = NodeHelper.create({
|
|||
}
|
||||
},
|
||||
|
||||
resolveRemote: function(moduleName, moduleFolder) {
|
||||
resolveRemote: function (moduleName, moduleFolder) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var git = SimpleGit(moduleFolder);
|
||||
git.getRemotes(true, (err, remotes) => {
|
||||
|
@ -65,19 +62,19 @@ module.exports = NodeHelper.create({
|
|||
return resolve();
|
||||
}
|
||||
// Folder has .git and has at least one git remote, watch this folder
|
||||
simpleGits.unshift({"module": moduleName, "git": git});
|
||||
simpleGits.unshift({ module: moduleName, git: git });
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
performFetch: function() {
|
||||
performFetch: function () {
|
||||
var self = this;
|
||||
simpleGits.forEach((sg) => {
|
||||
sg.git.fetch().status((err, data) => {
|
||||
data.module = sg.module;
|
||||
if (!err) {
|
||||
sg.git.log({"-1": null}, (err, data2) => {
|
||||
sg.git.log({ "-1": null }, (err, data2) => {
|
||||
if (!err && data2.latest && "hash" in data2.latest) {
|
||||
data.hash = data2.latest.hash;
|
||||
self.sendSocketNotification("STATUS", data);
|
||||
|
@ -90,19 +87,19 @@ module.exports = NodeHelper.create({
|
|||
this.scheduleNextFetch(this.config.updateInterval);
|
||||
},
|
||||
|
||||
scheduleNextFetch: function(delay) {
|
||||
scheduleNextFetch: function (delay) {
|
||||
if (delay < 60 * 1000) {
|
||||
delay = 60 * 1000;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
clearTimeout(this.updateTimer);
|
||||
this.updateTimer = setTimeout(function() {
|
||||
this.updateTimer = setTimeout(function () {
|
||||
self.performFetch();
|
||||
}, delay);
|
||||
},
|
||||
|
||||
ignoreUpdateChecking: function(moduleName) {
|
||||
ignoreUpdateChecking: function (moduleName) {
|
||||
// Should not check for updates for default modules
|
||||
if (defaultModules.indexOf(moduleName) >= 0) {
|
||||
return true;
|
||||
|
@ -116,5 +113,4 @@ module.exports = NodeHelper.create({
|
|||
// The rest of the modules that passes should check for updates
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
* MIT Licensed.
|
||||
*/
|
||||
Module.register("updatenotification", {
|
||||
|
||||
defaults: {
|
||||
updateInterval: 10 * 60 * 1000, // every 10 minutes
|
||||
refreshInterval: 24 * 60 * 60 * 1000, // one day
|
||||
|
@ -18,7 +17,10 @@ Module.register("updatenotification", {
|
|||
start: function () {
|
||||
var self = this;
|
||||
Log.log("Start updatenotification");
|
||||
setInterval( () => { self.moduleList = {};self.updateDom(2); } , self.config.refreshInterval);
|
||||
setInterval(() => {
|
||||
self.moduleList = {};
|
||||
self.updateDom(2);
|
||||
}, self.config.refreshInterval);
|
||||
},
|
||||
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
|
@ -39,16 +41,15 @@ Module.register("updatenotification", {
|
|||
var self = this;
|
||||
if (payload && payload.behind > 0) {
|
||||
// if we haven't seen info for this module
|
||||
if(this.moduleList[payload.module] === undefined){
|
||||
if (this.moduleList[payload.module] === undefined) {
|
||||
// save it
|
||||
this.moduleList[payload.module]=payload;
|
||||
this.moduleList[payload.module] = payload;
|
||||
self.updateDom(2);
|
||||
}
|
||||
//self.show(1000, { lockString: self.identifier });
|
||||
|
||||
} else if (payload && payload.behind === 0){
|
||||
} else if (payload && payload.behind === 0) {
|
||||
// if the module WAS in the list, but shouldn't be
|
||||
if(this.moduleList[payload.module] !== undefined){
|
||||
if (this.moduleList[payload.module] !== undefined) {
|
||||
// remove it
|
||||
delete this.moduleList[payload.module];
|
||||
self.updateDom(2);
|
||||
|
@ -56,23 +57,18 @@ Module.register("updatenotification", {
|
|||
}
|
||||
},
|
||||
|
||||
diffLink: function(module, text) {
|
||||
diffLink: function (module, text) {
|
||||
var localRef = module.hash;
|
||||
var remoteRef = module.tracking.replace(/.*\//, "");
|
||||
return "<a href=\"https://github.com/MichMich/MagicMirror/compare/"+localRef+"..."+remoteRef+"\" "+
|
||||
"class=\"xsmall dimmed\" "+
|
||||
"style=\"text-decoration: none;\" "+
|
||||
"target=\"_blank\" >" +
|
||||
text +
|
||||
"</a>";
|
||||
return '<a href="https://github.com/MichMich/MagicMirror/compare/' + localRef + "..." + remoteRef + '" ' + 'class="xsmall dimmed" ' + 'style="text-decoration: none;" ' + 'target="_blank" >' + text + "</a>";
|
||||
},
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function () {
|
||||
var wrapper = document.createElement("div");
|
||||
if(this.suspended === false){
|
||||
if (this.suspended === false) {
|
||||
// process the hash of module info found
|
||||
for(var key of Object.keys(this.moduleList)){
|
||||
for (var key of Object.keys(this.moduleList)) {
|
||||
let m = this.moduleList[key];
|
||||
|
||||
var message = document.createElement("div");
|
||||
|
@ -93,7 +89,7 @@ Module.register("updatenotification", {
|
|||
var text = document.createElement("span");
|
||||
if (m.module === "default") {
|
||||
text.innerHTML = this.translate("UPDATE_NOTIFICATION");
|
||||
subtextHtml = this.diffLink(m,subtextHtml);
|
||||
subtextHtml = this.diffLink(m, subtextHtml);
|
||||
} else {
|
||||
text.innerHTML = this.translate("UPDATE_NOTIFICATION_MODULE", {
|
||||
MODULE_NAME: m.module
|
||||
|
@ -112,11 +108,11 @@ Module.register("updatenotification", {
|
|||
return wrapper;
|
||||
},
|
||||
|
||||
suspend: function() {
|
||||
this.suspended=true;
|
||||
suspend: function () {
|
||||
this.suspended = true;
|
||||
},
|
||||
resume: function() {
|
||||
this.suspended=false;
|
||||
resume: function () {
|
||||
this.suspended = false;
|
||||
this.updateDom(2);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -15,15 +15,15 @@ Table of Contents:
|
|||
|
||||
This is the script in which the weather provider will be defined. In its most simple form, the weather provider must implement the following:
|
||||
|
||||
````javascript
|
||||
```javascript
|
||||
WeatherProvider.register("yourprovider", {
|
||||
providerName: "YourProvider",
|
||||
providerName: "YourProvider",
|
||||
|
||||
fetchCurrentWeather() {},
|
||||
fetchCurrentWeather() {},
|
||||
|
||||
fetchWeatherForecast() {}
|
||||
fetchWeatherForecast() {}
|
||||
});
|
||||
````
|
||||
```
|
||||
|
||||
### Weather provider methods to implement
|
||||
|
||||
|
@ -89,24 +89,24 @@ A convenience function to make requests. It returns a promise.
|
|||
|
||||
### WeatherObject
|
||||
|
||||
| Property | Type | Value/Unit |
|
||||
| --- | --- | --- |
|
||||
| units | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
|
||||
| tempUnits | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
|
||||
| windUnits | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
|
||||
| date | `object` | [Moment.js](https://momentjs.com/) object of the time/date. |
|
||||
| windSpeed |`number` | Metric: `meter/second` <br> Imperial: `miles/hour` |
|
||||
| windDirection |`number` | Direction of the wind in degrees. |
|
||||
| sunrise |`object` | [Moment.js](https://momentjs.com/) object of sunrise. |
|
||||
| sunset |`object` | [Moment.js](https://momentjs.com/) object of sunset. |
|
||||
| temperature | `number` | Current temperature |
|
||||
| minTemperature | `number` | Lowest temperature of the day. |
|
||||
| maxTemperature | `number` | Highest temperature of the day. |
|
||||
| weatherType | `string` | Icon name of the weather type. <br> Possible values: [WeatherIcons](https://www.npmjs.com/package/weathericons) |
|
||||
| humidity | `number` | Percentage of humidity |
|
||||
| rain | `number` | Metric: `millimeters` <br> Imperial: `inches` |
|
||||
| snow | `number` | Metric: `millimeters` <br> Imperial: `inches` |
|
||||
| precipitation | `number` | Metric: `millimeters` <br> Imperial: `inches` <br> UK Met Office provider: `percent` |
|
||||
| Property | Type | Value/Unit |
|
||||
| -------------- | -------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| units | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
|
||||
| tempUnits | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
|
||||
| windUnits | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
|
||||
| date | `object` | [Moment.js](https://momentjs.com/) object of the time/date. |
|
||||
| windSpeed | `number` | Metric: `meter/second` <br> Imperial: `miles/hour` |
|
||||
| windDirection | `number` | Direction of the wind in degrees. |
|
||||
| sunrise | `object` | [Moment.js](https://momentjs.com/) object of sunrise. |
|
||||
| sunset | `object` | [Moment.js](https://momentjs.com/) object of sunset. |
|
||||
| temperature | `number` | Current temperature |
|
||||
| minTemperature | `number` | Lowest temperature of the day. |
|
||||
| maxTemperature | `number` | Highest temperature of the day. |
|
||||
| weatherType | `string` | Icon name of the weather type. <br> Possible values: [WeatherIcons](https://www.npmjs.com/package/weathericons) |
|
||||
| humidity | `number` | Percentage of humidity |
|
||||
| rain | `number` | Metric: `millimeters` <br> Imperial: `inches` |
|
||||
| snow | `number` | Metric: `millimeters` <br> Imperial: `inches` |
|
||||
| precipitation | `number` | Metric: `millimeters` <br> Imperial: `inches` <br> UK Met Office provider: `percent` |
|
||||
|
||||
#### Current weather
|
||||
|
||||
|
|
|
@ -22,15 +22,16 @@ WeatherProvider.register("darksky", {
|
|||
|
||||
fetchCurrentWeather() {
|
||||
this.fetchData(this.getUrl())
|
||||
.then(data => {
|
||||
if(!data || !data.currently || typeof data.currently.temperature === "undefined") {
|
||||
.then((data) => {
|
||||
if (!data || !data.currently || typeof data.currently.temperature === "undefined") {
|
||||
// No usable data?
|
||||
return;
|
||||
}
|
||||
|
||||
const currentWeather = this.generateWeatherDayFromCurrentWeather(data);
|
||||
this.setCurrentWeather(currentWeather);
|
||||
}).catch(function(request) {
|
||||
})
|
||||
.catch(function (request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable());
|
||||
|
@ -38,15 +39,16 @@ WeatherProvider.register("darksky", {
|
|||
|
||||
fetchWeatherForecast() {
|
||||
this.fetchData(this.getUrl())
|
||||
.then(data => {
|
||||
if(!data || !data.daily || !data.daily.data.length) {
|
||||
.then((data) => {
|
||||
if (!data || !data.daily || !data.daily.data.length) {
|
||||
// No usable data?
|
||||
return;
|
||||
}
|
||||
|
||||
const forecast = this.generateWeatherObjectsFromForecast(data.daily.data);
|
||||
this.setWeatherForecast(forecast);
|
||||
}).catch(function(request) {
|
||||
})
|
||||
.catch(function (request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable());
|
||||
|
@ -109,12 +111,12 @@ WeatherProvider.register("darksky", {
|
|||
const weatherTypes = {
|
||||
"clear-day": "day-sunny",
|
||||
"clear-night": "night-clear",
|
||||
"rain": "rain",
|
||||
"snow": "snow",
|
||||
"sleet": "snow",
|
||||
"wind": "wind",
|
||||
"fog": "fog",
|
||||
"cloudy": "cloudy",
|
||||
rain: "rain",
|
||||
snow: "snow",
|
||||
sleet: "snow",
|
||||
wind: "wind",
|
||||
fog: "fog",
|
||||
cloudy: "cloudy",
|
||||
"partly-cloudy-day": "day-cloudy",
|
||||
"partly-cloudy-night": "night-cloudy"
|
||||
};
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
* This class is the blueprint for a weather provider.
|
||||
*/
|
||||
WeatherProvider.register("openweathermap", {
|
||||
|
||||
// Set the name of the provider.
|
||||
// This isn't strictly necessary, since it will fallback to the provider identifier
|
||||
// But for debugging (and future alerts) it would be nice to have the real name.
|
||||
|
@ -18,7 +17,7 @@ WeatherProvider.register("openweathermap", {
|
|||
// Overwrite the fetchCurrentWeather method.
|
||||
fetchCurrentWeather() {
|
||||
this.fetchData(this.getUrl())
|
||||
.then(data => {
|
||||
.then((data) => {
|
||||
if (!data || !data.main || typeof data.main.temp === "undefined") {
|
||||
// Did not receive usable new data.
|
||||
// Maybe this needs a better check?
|
||||
|
@ -30,7 +29,7 @@ WeatherProvider.register("openweathermap", {
|
|||
const currentWeather = this.generateWeatherObjectFromCurrentWeather(data);
|
||||
this.setCurrentWeather(currentWeather);
|
||||
})
|
||||
.catch(function(request) {
|
||||
.catch(function (request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable());
|
||||
|
@ -39,7 +38,7 @@ WeatherProvider.register("openweathermap", {
|
|||
// Overwrite the fetchCurrentWeather method.
|
||||
fetchWeatherForecast() {
|
||||
this.fetchData(this.getUrl())
|
||||
.then(data => {
|
||||
.then((data) => {
|
||||
if (!data || !data.list || !data.list.length) {
|
||||
// Did not receive usable new data.
|
||||
// Maybe this needs a better check?
|
||||
|
@ -51,7 +50,7 @@ WeatherProvider.register("openweathermap", {
|
|||
const forecast = this.generateWeatherObjectsFromForecast(data.list);
|
||||
this.setWeatherForecast(forecast);
|
||||
})
|
||||
.catch(function(request) {
|
||||
.catch(function (request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable());
|
||||
|
@ -86,7 +85,6 @@ WeatherProvider.register("openweathermap", {
|
|||
* Generate WeatherObjects based on forecast information
|
||||
*/
|
||||
generateWeatherObjectsFromForecast(forecasts) {
|
||||
|
||||
if (this.config.weatherEndpoint === "/forecast") {
|
||||
return this.fetchForecastHourly(forecasts);
|
||||
} else if (this.config.weatherEndpoint === "/forecast/daily") {
|
||||
|
@ -113,7 +111,6 @@ WeatherProvider.register("openweathermap", {
|
|||
let weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
|
||||
|
||||
for (const forecast of forecasts) {
|
||||
|
||||
if (date !== moment(forecast.dt, "X").format("YYYY-MM-DD")) {
|
||||
// calculate minimum/maximum temperature, specify rain amount
|
||||
weather.minTemperature = Math.min.apply(null, minTemp);
|
||||
|
@ -139,7 +136,6 @@ WeatherProvider.register("openweathermap", {
|
|||
|
||||
// If the first value of today is later than 17:00, we have an icon at least!
|
||||
weather.weatherType = this.convertWeatherType(forecast.weather[0].icon);
|
||||
|
||||
}
|
||||
|
||||
if (moment(forecast.dt, "X").format("H") >= 8 && moment(forecast.dt, "X").format("H") <= 17) {
|
||||
|
@ -260,16 +256,16 @@ WeatherProvider.register("openweathermap", {
|
|||
*/
|
||||
getParams() {
|
||||
let params = "?";
|
||||
if(this.config.locationID) {
|
||||
if (this.config.locationID) {
|
||||
params += "id=" + this.config.locationID;
|
||||
} else if(this.config.location) {
|
||||
} else if (this.config.location) {
|
||||
params += "q=" + this.config.location;
|
||||
} else if (this.firstEvent && this.firstEvent.geo) {
|
||||
params += "lat=" + this.firstEvent.geo.lat + "&lon=" + this.firstEvent.geo.lon;
|
||||
} else if (this.firstEvent && this.firstEvent.location) {
|
||||
params += "q=" + this.firstEvent.location;
|
||||
} else {
|
||||
this.hide(this.config.animationSpeed, {lockString:this.identifier});
|
||||
this.hide(this.config.animationSpeed, { lockString: this.identifier });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
* This class is a provider for UK Met Office Datapoint.
|
||||
*/
|
||||
WeatherProvider.register("ukmetoffice", {
|
||||
|
||||
// Set the name of the provider.
|
||||
// This isn't strictly necessary, since it will fallback to the provider identifier
|
||||
// But for debugging (and future alerts) it would be nice to have the real name.
|
||||
|
@ -23,7 +22,7 @@ WeatherProvider.register("ukmetoffice", {
|
|||
// Overwrite the fetchCurrentWeather method.
|
||||
fetchCurrentWeather() {
|
||||
this.fetchData(this.getUrl("3hourly"))
|
||||
.then(data => {
|
||||
.then((data) => {
|
||||
if (!data || !data.SiteRep || !data.SiteRep.DV || !data.SiteRep.DV.Location || !data.SiteRep.DV.Location.Period || data.SiteRep.DV.Location.Period.length === 0) {
|
||||
// Did not receive usable new data.
|
||||
// Maybe this needs a better check?
|
||||
|
@ -35,7 +34,7 @@ WeatherProvider.register("ukmetoffice", {
|
|||
const currentWeather = this.generateWeatherObjectFromCurrentWeather(data);
|
||||
this.setCurrentWeather(currentWeather);
|
||||
})
|
||||
.catch(function(request) {
|
||||
.catch(function (request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable());
|
||||
|
@ -44,7 +43,7 @@ WeatherProvider.register("ukmetoffice", {
|
|||
// Overwrite the fetchCurrentWeather method.
|
||||
fetchWeatherForecast() {
|
||||
this.fetchData(this.getUrl("daily"))
|
||||
.then(data => {
|
||||
.then((data) => {
|
||||
if (!data || !data.SiteRep || !data.SiteRep.DV || !data.SiteRep.DV.Location || !data.SiteRep.DV.Location.Period || data.SiteRep.DV.Location.Period.length === 0) {
|
||||
// Did not receive usable new data.
|
||||
// Maybe this needs a better check?
|
||||
|
@ -56,7 +55,7 @@ WeatherProvider.register("ukmetoffice", {
|
|||
const forecast = this.generateWeatherObjectsFromForecast(data);
|
||||
this.setWeatherForecast(forecast);
|
||||
})
|
||||
.catch(function(request) {
|
||||
.catch(function (request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable());
|
||||
|
@ -83,18 +82,17 @@ WeatherProvider.register("ukmetoffice", {
|
|||
|
||||
// loop round each of the (5) periods, look for today (the first period may be yesterday)
|
||||
for (var i in currentWeatherData.SiteRep.DV.Location.Period) {
|
||||
let periodDate = moment.utc(currentWeatherData.SiteRep.DV.Location.Period[i].value.substr(0,10), "YYYY-MM-DD");
|
||||
let periodDate = moment.utc(currentWeatherData.SiteRep.DV.Location.Period[i].value.substr(0, 10), "YYYY-MM-DD");
|
||||
|
||||
// ignore if period is before today
|
||||
if (periodDate.isSameOrAfter(moment.utc().startOf("day"))) {
|
||||
|
||||
// check this is the period we want, after today the diff will be -ve
|
||||
if (moment().diff(periodDate, "minutes") > 0) {
|
||||
// loop round the reports looking for the one we are in
|
||||
// $ value specifies the time in minutes-of-the-day: 0, 180, 360,...1260
|
||||
for (var j in currentWeatherData.SiteRep.DV.Location.Period[i].Rep){
|
||||
for (var j in currentWeatherData.SiteRep.DV.Location.Period[i].Rep) {
|
||||
let p = currentWeatherData.SiteRep.DV.Location.Period[i].Rep[j].$;
|
||||
if (timeInMins >= p && timeInMins-180 < p) {
|
||||
if (timeInMins >= p && timeInMins - 180 < p) {
|
||||
// finally got the one we want, so populate weather object
|
||||
currentWeather.humidity = currentWeatherData.SiteRep.DV.Location.Period[i].Rep[j].H;
|
||||
currentWeather.temperature = this.convertTemp(currentWeatherData.SiteRep.DV.Location.Period[i].Rep[j].T);
|
||||
|
@ -121,7 +119,6 @@ WeatherProvider.register("ukmetoffice", {
|
|||
* Generate WeatherObjects based on forecast information
|
||||
*/
|
||||
generateWeatherObjectsFromForecast(forecasts) {
|
||||
|
||||
const days = [];
|
||||
|
||||
// loop round the (5) periods getting the data
|
||||
|
@ -131,12 +128,12 @@ WeatherProvider.register("ukmetoffice", {
|
|||
|
||||
// data times are always UTC
|
||||
const dateStr = forecasts.SiteRep.DV.Location.Period[j].value;
|
||||
let periodDate = moment.utc(dateStr.substr(0,10), "YYYY-MM-DD");
|
||||
let periodDate = moment.utc(dateStr.substr(0, 10), "YYYY-MM-DD");
|
||||
|
||||
// ignore if period is before today
|
||||
if (periodDate.isSameOrAfter(moment.utc().startOf("day"))) {
|
||||
// populate the weather object
|
||||
weather.date = moment.utc(dateStr.substr(0,10), "YYYY-MM-DD");
|
||||
weather.date = moment.utc(dateStr.substr(0, 10), "YYYY-MM-DD");
|
||||
weather.minTemperature = this.convertTemp(forecasts.SiteRep.DV.Location.Period[j].Rep[1].Nm);
|
||||
weather.maxTemperature = this.convertTemp(forecasts.SiteRep.DV.Location.Period[j].Rep[0].Dm);
|
||||
weather.weatherType = this.convertWeatherType(forecasts.SiteRep.DV.Location.Period[j].Rep[0].W);
|
||||
|
@ -207,7 +204,7 @@ WeatherProvider.register("ukmetoffice", {
|
|||
* Convert temp (from degrees C) if required
|
||||
*/
|
||||
convertTemp(tempInC) {
|
||||
return this.tempUnits === "imperial" ? tempInC * 9 / 5 + 32 : tempInC;
|
||||
return this.tempUnits === "imperial" ? (tempInC * 9) / 5 + 32 : tempInC;
|
||||
},
|
||||
|
||||
/*
|
||||
|
@ -222,22 +219,22 @@ WeatherProvider.register("ukmetoffice", {
|
|||
*/
|
||||
convertWindDirection(windDirection) {
|
||||
const windCardinals = {
|
||||
"N": 0,
|
||||
"NNE": 22,
|
||||
"NE": 45,
|
||||
"ENE": 67,
|
||||
"E": 90,
|
||||
"ESE": 112,
|
||||
"SE": 135,
|
||||
"SSE": 157,
|
||||
"S": 180,
|
||||
"SSW": 202,
|
||||
"SW": 225,
|
||||
"WSW": 247,
|
||||
"W": 270,
|
||||
"WNW": 292,
|
||||
"NW": 315,
|
||||
"NNW": 337
|
||||
N: 0,
|
||||
NNE: 22,
|
||||
NE: 45,
|
||||
ENE: 67,
|
||||
E: 90,
|
||||
ESE: 112,
|
||||
SE: 135,
|
||||
SSE: 157,
|
||||
S: 180,
|
||||
SSW: 202,
|
||||
SW: 225,
|
||||
WSW: 247,
|
||||
W: 270,
|
||||
WNW: 292,
|
||||
NW: 315,
|
||||
NNW: 337
|
||||
};
|
||||
|
||||
return windCardinals.hasOwnProperty(windDirection) ? windCardinals[windDirection] : null;
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
* Since it is free, there are some items missing - like sunrise, sunset, humidity, etc.
|
||||
*/
|
||||
WeatherProvider.register("weathergov", {
|
||||
|
||||
// Set the name of the provider.
|
||||
// This isn't strictly necessary, since it will fallback to the provider identifier
|
||||
// But for debugging (and future alerts) it would be nice to have the real name.
|
||||
|
@ -21,7 +20,7 @@ WeatherProvider.register("weathergov", {
|
|||
// Overwrite the fetchCurrentWeather method.
|
||||
fetchCurrentWeather() {
|
||||
this.fetchData(this.getUrl())
|
||||
.then(data => {
|
||||
.then((data) => {
|
||||
if (!data || !data.properties || !data.properties.periods || !data.properties.periods.length) {
|
||||
// Did not receive usable new data.
|
||||
// Maybe this needs a better check?
|
||||
|
@ -31,7 +30,7 @@ WeatherProvider.register("weathergov", {
|
|||
const currentWeather = this.generateWeatherObjectFromCurrentWeather(data.properties.periods[0]);
|
||||
this.setCurrentWeather(currentWeather);
|
||||
})
|
||||
.catch(function(request) {
|
||||
.catch(function (request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable());
|
||||
|
@ -40,7 +39,7 @@ WeatherProvider.register("weathergov", {
|
|||
// Overwrite the fetchCurrentWeather method.
|
||||
fetchWeatherForecast() {
|
||||
this.fetchData(this.getUrl())
|
||||
.then(data => {
|
||||
.then((data) => {
|
||||
if (!data || !data.properties || !data.properties.periods || !data.properties.periods.length) {
|
||||
// Did not receive usable new data.
|
||||
// Maybe this needs a better check?
|
||||
|
@ -50,7 +49,7 @@ WeatherProvider.register("weathergov", {
|
|||
const forecast = this.generateWeatherObjectsFromForecast(data.properties.periods);
|
||||
this.setWeatherForecast(forecast);
|
||||
})
|
||||
.catch(function(request) {
|
||||
.catch(function (request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable());
|
||||
|
@ -105,9 +104,7 @@ WeatherProvider.register("weathergov", {
|
|||
weather.precipitation = 0;
|
||||
|
||||
for (const forecast of forecasts) {
|
||||
|
||||
if (date !== moment(forecast.startTime).format("YYYY-MM-DD")) {
|
||||
|
||||
// calculate minimum/maximum temperature, specify rain amount
|
||||
weather.minTemperature = Math.min.apply(null, minTemp);
|
||||
weather.maxTemperature = Math.max.apply(null, maxTemp);
|
||||
|
@ -240,22 +237,22 @@ WeatherProvider.register("weathergov", {
|
|||
*/
|
||||
convertWindDirection(windDirection) {
|
||||
const windCardinals = {
|
||||
"N": 0,
|
||||
"NNE": 22,
|
||||
"NE": 45,
|
||||
"ENE": 67,
|
||||
"E": 90,
|
||||
"ESE": 112,
|
||||
"SE": 135,
|
||||
"SSE": 157,
|
||||
"S": 180,
|
||||
"SSW": 202,
|
||||
"SW": 225,
|
||||
"WSW": 247,
|
||||
"W": 270,
|
||||
"WNW": 292,
|
||||
"NW": 315,
|
||||
"NNW": 337
|
||||
N: 0,
|
||||
NNE: 22,
|
||||
NE: 45,
|
||||
ENE: 67,
|
||||
E: 90,
|
||||
ESE: 112,
|
||||
SE: 135,
|
||||
SSE: 157,
|
||||
S: 180,
|
||||
SSW: 202,
|
||||
SW: 225,
|
||||
WSW: 247,
|
||||
W: 270,
|
||||
WNW: 292,
|
||||
NW: 315,
|
||||
NNW: 337
|
||||
};
|
||||
|
||||
return windCardinals.hasOwnProperty(windDirection) ? windCardinals[windDirection] : null;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
Module.register("weather",{
|
||||
Module.register("weather", {
|
||||
// Default module config.
|
||||
defaults: {
|
||||
weatherProvider: "openweathermap",
|
||||
|
@ -60,23 +60,17 @@ Module.register("weather",{
|
|||
weatherProvider: null,
|
||||
|
||||
// Define required scripts.
|
||||
getStyles: function() {
|
||||
getStyles: function () {
|
||||
return ["font-awesome.css", "weather-icons.css", "weather.css"];
|
||||
},
|
||||
|
||||
// Return the scripts that are necessary for the weather module.
|
||||
getScripts: function () {
|
||||
return [
|
||||
"moment.js",
|
||||
"weatherprovider.js",
|
||||
"weatherobject.js",
|
||||
"suncalc.js",
|
||||
this.file("providers/" + this.config.weatherProvider.toLowerCase() + ".js")
|
||||
];
|
||||
return ["moment.js", "weatherprovider.js", "weatherobject.js", "suncalc.js", this.file("providers/" + this.config.weatherProvider.toLowerCase() + ".js")];
|
||||
},
|
||||
|
||||
// Override getHeader method.
|
||||
getHeader: function() {
|
||||
getHeader: function () {
|
||||
if (this.config.appendLocationNameToHeader && this.data.header !== undefined && this.weatherProvider) {
|
||||
return this.data.header + " " + this.weatherProvider.fetchedLocation();
|
||||
}
|
||||
|
@ -102,7 +96,7 @@ Module.register("weather",{
|
|||
},
|
||||
|
||||
// Override notification handler.
|
||||
notificationReceived: function(notification, payload, sender) {
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
if (notification === "CALENDAR_EVENTS") {
|
||||
var senderClasses = sender.data.classes.toLowerCase().split(" ");
|
||||
if (senderClasses.indexOf(this.config.calendarClass.toLowerCase()) !== -1) {
|
||||
|
@ -145,13 +139,13 @@ Module.register("weather",{
|
|||
},
|
||||
|
||||
// What to do when the weather provider has new information available?
|
||||
updateAvailable: function() {
|
||||
updateAvailable: function () {
|
||||
Log.log("New weather information available.");
|
||||
this.updateDom(0);
|
||||
this.scheduleUpdate();
|
||||
},
|
||||
|
||||
scheduleUpdate: function(delay = null) {
|
||||
scheduleUpdate: function (delay = null) {
|
||||
var nextLoad = this.config.updateInterval;
|
||||
if (delay !== null && delay >= 0) {
|
||||
nextLoad = delay;
|
||||
|
@ -166,88 +160,106 @@ Module.register("weather",{
|
|||
}, nextLoad);
|
||||
},
|
||||
|
||||
roundValue: function(temperature) {
|
||||
roundValue: function (temperature) {
|
||||
var decimals = this.config.roundTemp ? 0 : 1;
|
||||
return parseFloat(temperature).toFixed(decimals);
|
||||
},
|
||||
|
||||
addFilters() {
|
||||
this.nunjucksEnvironment().addFilter("formatTime", function(date) {
|
||||
date = moment(date);
|
||||
this.nunjucksEnvironment().addFilter(
|
||||
"formatTime",
|
||||
function (date) {
|
||||
date = moment(date);
|
||||
|
||||
if (this.config.timeFormat !== 24) {
|
||||
if (this.config.showPeriod) {
|
||||
if (this.config.showPeriodUpper) {
|
||||
return date.format("h:mm A");
|
||||
if (this.config.timeFormat !== 24) {
|
||||
if (this.config.showPeriod) {
|
||||
if (this.config.showPeriodUpper) {
|
||||
return date.format("h:mm A");
|
||||
} else {
|
||||
return date.format("h:mm a");
|
||||
}
|
||||
} else {
|
||||
return date.format("h:mm a");
|
||||
}
|
||||
} else {
|
||||
return date.format("h:mm");
|
||||
}
|
||||
}
|
||||
|
||||
return date.format("HH:mm");
|
||||
}.bind(this));
|
||||
|
||||
this.nunjucksEnvironment().addFilter("unit", function (value, type) {
|
||||
if (type === "temperature") {
|
||||
if (this.config.tempUnits === "metric" || this.config.tempUnits === "imperial") {
|
||||
value += "°";
|
||||
}
|
||||
if (this.config.degreeLabel) {
|
||||
if (this.config.tempUnits === "metric") {
|
||||
value += "C";
|
||||
} else if (this.config.tempUnits === "imperial") {
|
||||
value += "F";
|
||||
} else {
|
||||
value += "K";
|
||||
return date.format("h:mm");
|
||||
}
|
||||
}
|
||||
} else if (type === "precip") {
|
||||
if (isNaN(value) || value === 0 || value.toFixed(2) === "0.00") {
|
||||
value = "";
|
||||
} else {
|
||||
if (this.config.weatherProvider === "ukmetoffice") {
|
||||
value += "%";
|
||||
} else {
|
||||
value = `${value.toFixed(2)} ${this.config.units === "imperial" ? "in" : "mm"}`;
|
||||
|
||||
return date.format("HH:mm");
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.nunjucksEnvironment().addFilter(
|
||||
"unit",
|
||||
function (value, type) {
|
||||
if (type === "temperature") {
|
||||
if (this.config.tempUnits === "metric" || this.config.tempUnits === "imperial") {
|
||||
value += "°";
|
||||
}
|
||||
if (this.config.degreeLabel) {
|
||||
if (this.config.tempUnits === "metric") {
|
||||
value += "C";
|
||||
} else if (this.config.tempUnits === "imperial") {
|
||||
value += "F";
|
||||
} else {
|
||||
value += "K";
|
||||
}
|
||||
}
|
||||
} else if (type === "precip") {
|
||||
if (isNaN(value) || value === 0 || value.toFixed(2) === "0.00") {
|
||||
value = "";
|
||||
} else {
|
||||
if (this.config.weatherProvider === "ukmetoffice") {
|
||||
value += "%";
|
||||
} else {
|
||||
value = `${value.toFixed(2)} ${this.config.units === "imperial" ? "in" : "mm"}`;
|
||||
}
|
||||
}
|
||||
} else if (type === "humidity") {
|
||||
value += "%";
|
||||
}
|
||||
} else if (type === "humidity") {
|
||||
value += "%";
|
||||
}
|
||||
|
||||
return value;
|
||||
}.bind(this));
|
||||
return value;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.nunjucksEnvironment().addFilter("roundValue", function(value) {
|
||||
return this.roundValue(value);
|
||||
}.bind(this));
|
||||
this.nunjucksEnvironment().addFilter(
|
||||
"roundValue",
|
||||
function (value) {
|
||||
return this.roundValue(value);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.nunjucksEnvironment().addFilter("decimalSymbol", function(value) {
|
||||
return value.toString().replace(/\./g, this.config.decimalSymbol);
|
||||
}.bind(this));
|
||||
this.nunjucksEnvironment().addFilter(
|
||||
"decimalSymbol",
|
||||
function (value) {
|
||||
return value.toString().replace(/\./g, this.config.decimalSymbol);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.nunjucksEnvironment().addFilter("calcNumSteps", function(forecast) {
|
||||
return Math.min(forecast.length, this.config.maxNumberOfDays);
|
||||
}.bind(this));
|
||||
this.nunjucksEnvironment().addFilter(
|
||||
"calcNumSteps",
|
||||
function (forecast) {
|
||||
return Math.min(forecast.length, this.config.maxNumberOfDays);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.nunjucksEnvironment().addFilter("opacity", function(currentStep, numSteps) {
|
||||
if (this.config.fade && this.config.fadePoint < 1) {
|
||||
if (this.config.fadePoint < 0) {
|
||||
this.config.fadePoint = 0;
|
||||
}
|
||||
var startingPoint = numSteps * this.config.fadePoint;
|
||||
var numFadesteps = numSteps - startingPoint;
|
||||
if (currentStep >= startingPoint) {
|
||||
return 1 - (currentStep - startingPoint) / numFadesteps;
|
||||
this.nunjucksEnvironment().addFilter(
|
||||
"opacity",
|
||||
function (currentStep, numSteps) {
|
||||
if (this.config.fade && this.config.fadePoint < 1) {
|
||||
if (this.config.fadePoint < 0) {
|
||||
this.config.fadePoint = 0;
|
||||
}
|
||||
var startingPoint = numSteps * this.config.fadePoint;
|
||||
var numFadesteps = numSteps - startingPoint;
|
||||
if (currentStep >= startingPoint) {
|
||||
return 1 - (currentStep - startingPoint) / numFadesteps;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
*/
|
||||
class WeatherObject {
|
||||
constructor(units, tempUnits, windUnits) {
|
||||
|
||||
this.units = units;
|
||||
this.tempUnits = tempUnits;
|
||||
this.windUnits = windUnits;
|
||||
|
@ -29,11 +28,10 @@ class WeatherObject {
|
|||
this.snow = null;
|
||||
this.precipitation = null;
|
||||
this.feelsLikeTemp = null;
|
||||
|
||||
}
|
||||
|
||||
cardinalWindDirection() {
|
||||
if (this.windDirection > 11.25 && this.windDirection <= 33.75){
|
||||
if (this.windDirection > 11.25 && this.windDirection <= 33.75) {
|
||||
return "NNE";
|
||||
} else if (this.windDirection > 33.75 && this.windDirection <= 56.25) {
|
||||
return "NE";
|
||||
|
@ -69,7 +67,7 @@ class WeatherObject {
|
|||
}
|
||||
|
||||
beaufortWindSpeed() {
|
||||
const windInKmh = (this.windUnits === "imperial") ? this.windSpeed * 1.609344 : this.windSpeed * 60 * 60 / 1000;
|
||||
const windInKmh = this.windUnits === "imperial" ? this.windSpeed * 1.609344 : (this.windSpeed * 60 * 60) / 1000;
|
||||
const speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000];
|
||||
for (const [index, speed] of speeds.entries()) {
|
||||
if (speed > windInKmh) {
|
||||
|
@ -87,21 +85,25 @@ class WeatherObject {
|
|||
if (this.feelsLikeTemp) {
|
||||
return this.feelsLikeTemp;
|
||||
}
|
||||
const windInMph = (this.windUnits === "imperial") ? this.windSpeed : this.windSpeed * 2.23694;
|
||||
const tempInF = this.tempUnits === "imperial" ? this.temperature : this.temperature * 9 / 5 + 32;
|
||||
const windInMph = this.windUnits === "imperial" ? this.windSpeed : this.windSpeed * 2.23694;
|
||||
const tempInF = this.tempUnits === "imperial" ? this.temperature : (this.temperature * 9) / 5 + 32;
|
||||
let feelsLike = tempInF;
|
||||
|
||||
if (windInMph > 3 && tempInF < 50) {
|
||||
feelsLike = Math.round(35.74 + 0.6215 * tempInF - 35.75 * Math.pow(windInMph, 0.16) + 0.4275 * tempInF * Math.pow(windInMph, 0.16));
|
||||
} else if (tempInF > 80 && this.humidity > 40) {
|
||||
feelsLike = -42.379 + 2.04901523 * tempInF + 10.14333127 * this.humidity
|
||||
- 0.22475541 * tempInF * this.humidity - 6.83783 * Math.pow(10, -3) * tempInF * tempInF
|
||||
- 5.481717 * Math.pow(10, -2) * this.humidity * this.humidity
|
||||
+ 1.22874 * Math.pow(10, -3) * tempInF * tempInF * this.humidity
|
||||
+ 8.5282 * Math.pow(10, -4) * tempInF * this.humidity * this.humidity
|
||||
- 1.99 * Math.pow(10, -6) * tempInF * tempInF * this.humidity * this.humidity;
|
||||
feelsLike =
|
||||
-42.379 +
|
||||
2.04901523 * tempInF +
|
||||
10.14333127 * this.humidity -
|
||||
0.22475541 * tempInF * this.humidity -
|
||||
6.83783 * Math.pow(10, -3) * tempInF * tempInF -
|
||||
5.481717 * Math.pow(10, -2) * this.humidity * this.humidity +
|
||||
1.22874 * Math.pow(10, -3) * tempInF * tempInF * this.humidity +
|
||||
8.5282 * Math.pow(10, -4) * tempInF * this.humidity * this.humidity -
|
||||
1.99 * Math.pow(10, -6) * tempInF * tempInF * this.humidity * this.humidity;
|
||||
}
|
||||
|
||||
return this.tempUnits === "imperial" ? feelsLike : (feelsLike - 32) * 5 / 9;
|
||||
return this.tempUnits === "imperial" ? feelsLike : ((feelsLike - 32) * 5) / 9;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,77 +28,77 @@ var WeatherProvider = Class.extend({
|
|||
// All the following methods can be overwritten, although most are good as they are.
|
||||
|
||||
// Called when a weather provider is initialized.
|
||||
init: function(config) {
|
||||
init: function (config) {
|
||||
this.config = config;
|
||||
Log.info(`Weather provider: ${this.providerName} initialized.`);
|
||||
},
|
||||
|
||||
// Called to set the config, this config is the same as the weather module's config.
|
||||
setConfig: function(config) {
|
||||
setConfig: function (config) {
|
||||
this.config = config;
|
||||
Log.info(`Weather provider: ${this.providerName} config set.`, this.config);
|
||||
},
|
||||
|
||||
// Called when the weather provider is about to start.
|
||||
start: function() {
|
||||
start: function () {
|
||||
Log.info(`Weather provider: ${this.providerName} started.`);
|
||||
},
|
||||
|
||||
// This method should start the API request to fetch the current weather.
|
||||
// This method should definitely be overwritten in the provider.
|
||||
fetchCurrentWeather: function() {
|
||||
fetchCurrentWeather: function () {
|
||||
Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchCurrentWeather method.`);
|
||||
},
|
||||
|
||||
// This method should start the API request to fetch the weather forecast.
|
||||
// This method should definitely be overwritten in the provider.
|
||||
fetchWeatherForecast: function() {
|
||||
fetchWeatherForecast: function () {
|
||||
Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchWeatherForecast method.`);
|
||||
},
|
||||
|
||||
// This returns a WeatherDay object for the current weather.
|
||||
currentWeather: function() {
|
||||
currentWeather: function () {
|
||||
return this.currentWeatherObject;
|
||||
},
|
||||
|
||||
// This returns an array of WeatherDay objects for the weather forecast.
|
||||
weatherForecast: function() {
|
||||
weatherForecast: function () {
|
||||
return this.weatherForecastArray;
|
||||
},
|
||||
|
||||
// This returns the name of the fetched location or an empty string.
|
||||
fetchedLocation: function() {
|
||||
fetchedLocation: function () {
|
||||
return this.fetchedLocationName || "";
|
||||
},
|
||||
|
||||
// Set the currentWeather and notify the delegate that new information is available.
|
||||
setCurrentWeather: function(currentWeatherObject) {
|
||||
setCurrentWeather: function (currentWeatherObject) {
|
||||
// We should check here if we are passing a WeatherDay
|
||||
this.currentWeatherObject = currentWeatherObject;
|
||||
},
|
||||
|
||||
// Set the weatherForecastArray and notify the delegate that new information is available.
|
||||
setWeatherForecast: function(weatherForecastArray) {
|
||||
setWeatherForecast: function (weatherForecastArray) {
|
||||
// We should check here if we are passing a WeatherDay
|
||||
this.weatherForecastArray = weatherForecastArray;
|
||||
},
|
||||
|
||||
// Set the fetched location name.
|
||||
setFetchedLocation: function(name) {
|
||||
setFetchedLocation: function (name) {
|
||||
this.fetchedLocationName = name;
|
||||
},
|
||||
|
||||
// Notify the delegate that new weather is available.
|
||||
updateAvailable: function() {
|
||||
updateAvailable: function () {
|
||||
this.delegate.updateAvailable(this);
|
||||
},
|
||||
|
||||
// A convenience function to make requests. It returns a promise.
|
||||
fetchData: function(url, method = "GET", data = null) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
fetchData: function (url, method = "GET", data = null) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.open(method, url, true);
|
||||
request.onreadystatechange = function() {
|
||||
request.onreadystatechange = function () {
|
||||
if (this.readyState === 4) {
|
||||
if (this.status === 200) {
|
||||
resolve(JSON.parse(this.response));
|
||||
|
@ -120,14 +120,14 @@ WeatherProvider.providers = [];
|
|||
/**
|
||||
* Static method to register a new weather provider.
|
||||
*/
|
||||
WeatherProvider.register = function(providerIdentifier, providerDetails) {
|
||||
WeatherProvider.register = function (providerIdentifier, providerDetails) {
|
||||
WeatherProvider.providers[providerIdentifier.toLowerCase()] = WeatherProvider.extend(providerDetails);
|
||||
};
|
||||
|
||||
/**
|
||||
* Static method to initialize a new weather provider.
|
||||
*/
|
||||
WeatherProvider.initialize = function(providerIdentifier, delegate) {
|
||||
WeatherProvider.initialize = function (providerIdentifier, delegate) {
|
||||
providerIdentifier = providerIdentifier.toLowerCase();
|
||||
|
||||
var provider = new WeatherProvider.providers[providerIdentifier]();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Module: Weather Forecast
|
||||
|
||||
The `weatherforecast` module is one of the default modules of the MagicMirror.
|
||||
This module displays the weather forecast for the coming week, including an an icon to display the current conditions, the minimum temperature and the maximum temperature.
|
||||
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
Module.register("weatherforecast",{
|
||||
|
||||
Module.register("weatherforecast", {
|
||||
// Default module config.
|
||||
defaults: {
|
||||
location: false,
|
||||
|
@ -56,7 +55,7 @@ Module.register("weatherforecast",{
|
|||
"11n": "wi-night-thunderstorm",
|
||||
"13n": "wi-night-snow",
|
||||
"50n": "wi-night-alt-cloudy-windy"
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
// create a variable for the first upcoming calendar event. Used if no location is specified.
|
||||
|
@ -66,17 +65,17 @@ Module.register("weatherforecast",{
|
|||
fetchedLocationName: "",
|
||||
|
||||
// Define required scripts.
|
||||
getScripts: function() {
|
||||
getScripts: function () {
|
||||
return ["moment.js"];
|
||||
},
|
||||
|
||||
// Define required scripts.
|
||||
getStyles: function() {
|
||||
getStyles: function () {
|
||||
return ["weather-icons.css", "weatherforecast.css"];
|
||||
},
|
||||
|
||||
// Define required translations.
|
||||
getTranslations: function() {
|
||||
getTranslations: function () {
|
||||
// The translations for the default modules are defined in the core translation files.
|
||||
// Therefor we can just return false. Otherwise we should have returned a dictionary.
|
||||
// If you're trying to build your own module including translations, check out the documentation.
|
||||
|
@ -84,7 +83,7 @@ Module.register("weatherforecast",{
|
|||
},
|
||||
|
||||
// Define start sequence.
|
||||
start: function() {
|
||||
start: function () {
|
||||
Log.info("Starting module: " + this.name);
|
||||
|
||||
// Set locale.
|
||||
|
@ -95,11 +94,10 @@ Module.register("weatherforecast",{
|
|||
this.scheduleUpdate(this.config.initialLoadDelay);
|
||||
|
||||
this.updateTimer = null;
|
||||
|
||||
},
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function() {
|
||||
getDom: function () {
|
||||
var wrapper = document.createElement("div");
|
||||
|
||||
if (this.config.appid === "") {
|
||||
|
@ -143,17 +141,17 @@ Module.register("weatherforecast",{
|
|||
if (this.config.units === "metric" || this.config.units === "imperial") {
|
||||
degreeLabel += "°";
|
||||
}
|
||||
if(this.config.scale) {
|
||||
switch(this.config.units) {
|
||||
case "metric":
|
||||
degreeLabel += "C";
|
||||
break;
|
||||
case "imperial":
|
||||
degreeLabel += "F";
|
||||
break;
|
||||
case "default":
|
||||
degreeLabel = "K";
|
||||
break;
|
||||
if (this.config.scale) {
|
||||
switch (this.config.units) {
|
||||
case "metric":
|
||||
degreeLabel += "C";
|
||||
break;
|
||||
case "imperial":
|
||||
degreeLabel += "F";
|
||||
break;
|
||||
case "default":
|
||||
degreeLabel = "K";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,7 +174,7 @@ Module.register("weatherforecast",{
|
|||
if (isNaN(forecast.rain)) {
|
||||
rainCell.innerHTML = "";
|
||||
} else {
|
||||
if(config.units !== "imperial") {
|
||||
if (config.units !== "imperial") {
|
||||
rainCell.innerHTML = parseFloat(forecast.rain).toFixed(1).replace(".", this.config.decimalSymbol) + " mm";
|
||||
} else {
|
||||
rainCell.innerHTML = (parseFloat(forecast.rain) / 25.4).toFixed(2).replace(".", this.config.decimalSymbol) + " in";
|
||||
|
@ -194,7 +192,7 @@ Module.register("weatherforecast",{
|
|||
var steps = this.forecast.length - startingPoint;
|
||||
if (f >= startingPoint) {
|
||||
var currentStep = f - startingPoint;
|
||||
row.style.opacity = 1 - (1 / steps * currentStep);
|
||||
row.style.opacity = 1 - (1 / steps) * currentStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -203,7 +201,7 @@ Module.register("weatherforecast",{
|
|||
},
|
||||
|
||||
// Override getHeader method.
|
||||
getHeader: function() {
|
||||
getHeader: function () {
|
||||
if (this.config.appendLocationNameToHeader) {
|
||||
return this.data.header + " " + this.fetchedLocationName;
|
||||
}
|
||||
|
@ -212,10 +210,10 @@ Module.register("weatherforecast",{
|
|||
},
|
||||
|
||||
// Override notification handler.
|
||||
notificationReceived: function(notification, payload, sender) {
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
if (notification === "DOM_OBJECTS_CREATED") {
|
||||
if (this.config.appendLocationNameToHeader) {
|
||||
this.hide(0, {lockString: this.identifier});
|
||||
this.hide(0, { lockString: this.identifier });
|
||||
}
|
||||
}
|
||||
if (notification === "CALENDAR_EVENTS") {
|
||||
|
@ -239,7 +237,7 @@ Module.register("weatherforecast",{
|
|||
* Requests new data from openweather.org.
|
||||
* Calls processWeather on successful response.
|
||||
*/
|
||||
updateWeather: function() {
|
||||
updateWeather: function () {
|
||||
if (this.config.appid === "") {
|
||||
Log.error("WeatherForecast: APPID not set!");
|
||||
return;
|
||||
|
@ -251,7 +249,7 @@ Module.register("weatherforecast",{
|
|||
|
||||
var weatherRequest = new XMLHttpRequest();
|
||||
weatherRequest.open("GET", url, true);
|
||||
weatherRequest.onreadystatechange = function() {
|
||||
weatherRequest.onreadystatechange = function () {
|
||||
if (this.readyState === 4) {
|
||||
if (this.status === 200) {
|
||||
self.processWeather(JSON.parse(this.response));
|
||||
|
@ -269,7 +267,7 @@ Module.register("weatherforecast",{
|
|||
}
|
||||
|
||||
if (retry) {
|
||||
self.scheduleUpdate((self.loaded) ? -1 : self.config.retryDelay);
|
||||
self.scheduleUpdate(self.loaded ? -1 : self.config.retryDelay);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -281,18 +279,18 @@ Module.register("weatherforecast",{
|
|||
*
|
||||
* return String - URL params.
|
||||
*/
|
||||
getParams: function() {
|
||||
getParams: function () {
|
||||
var params = "?";
|
||||
if(this.config.locationID) {
|
||||
if (this.config.locationID) {
|
||||
params += "id=" + this.config.locationID;
|
||||
} else if(this.config.location) {
|
||||
} else if (this.config.location) {
|
||||
params += "q=" + this.config.location;
|
||||
} else if (this.firstEvent && this.firstEvent.geo) {
|
||||
params += "lat=" + this.firstEvent.geo.lat + "&lon=" + this.firstEvent.geo.lon;
|
||||
} else if (this.firstEvent && this.firstEvent.location) {
|
||||
params += "q=" + this.firstEvent.location;
|
||||
} else {
|
||||
this.hide(this.config.animationSpeed, {lockString:this.identifier});
|
||||
this.hide(this.config.animationSpeed, { lockString: this.identifier });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -310,9 +308,9 @@ Module.register("weatherforecast",{
|
|||
* from openweather.org
|
||||
*
|
||||
*/
|
||||
parserDataWeather: function(data) {
|
||||
parserDataWeather: function (data) {
|
||||
if (data.hasOwnProperty("main")) {
|
||||
data["temp"] = {"min": data.main.temp_min, "max": data.main.temp_max};
|
||||
data["temp"] = { min: data.main.temp_min, max: data.main.temp_max };
|
||||
}
|
||||
return data;
|
||||
},
|
||||
|
@ -322,7 +320,7 @@ Module.register("weatherforecast",{
|
|||
*
|
||||
* argument data object - Weather information received form openweather.org.
|
||||
*/
|
||||
processWeather: function(data) {
|
||||
processWeather: function (data) {
|
||||
this.fetchedLocationName = data.city.name + ", " + data.city.country;
|
||||
|
||||
this.forecast = [];
|
||||
|
@ -330,13 +328,12 @@ Module.register("weatherforecast",{
|
|||
var forecastData = {};
|
||||
|
||||
for (var i = 0, count = data.list.length; i < count; i++) {
|
||||
|
||||
var forecast = data.list[i];
|
||||
this.parserDataWeather(forecast); // hack issue #1017
|
||||
|
||||
var day;
|
||||
var hour;
|
||||
if(forecast.dt_txt) {
|
||||
if (forecast.dt_txt) {
|
||||
day = moment(forecast.dt_txt, "YYYY-MM-DD hh:mm:ss").format("ddd");
|
||||
hour = moment(forecast.dt_txt, "YYYY-MM-DD hh:mm:ss").format("H");
|
||||
} else {
|
||||
|
@ -375,7 +372,7 @@ Module.register("weatherforecast",{
|
|||
}
|
||||
|
||||
//Log.log(this.forecast);
|
||||
this.show(this.config.animationSpeed, {lockString:this.identifier});
|
||||
this.show(this.config.animationSpeed, { lockString: this.identifier });
|
||||
this.loaded = true;
|
||||
this.updateDom(this.config.animationSpeed);
|
||||
},
|
||||
|
@ -385,7 +382,7 @@ Module.register("weatherforecast",{
|
|||
*
|
||||
* argument delay number - Milliseconds before next update. If empty, this.config.updateInterval is used.
|
||||
*/
|
||||
scheduleUpdate: function(delay) {
|
||||
scheduleUpdate: function (delay) {
|
||||
var nextLoad = this.config.updateInterval;
|
||||
if (typeof delay !== "undefined" && delay >= 0) {
|
||||
nextLoad = delay;
|
||||
|
@ -393,7 +390,7 @@ Module.register("weatherforecast",{
|
|||
|
||||
var self = this;
|
||||
clearTimeout(this.updateTimer);
|
||||
this.updateTimer = setTimeout(function() {
|
||||
this.updateTimer = setTimeout(function () {
|
||||
self.updateWeather();
|
||||
}, nextLoad);
|
||||
},
|
||||
|
@ -409,8 +406,8 @@ Module.register("weatherforecast",{
|
|||
*
|
||||
* return number - Windspeed in beaufort.
|
||||
*/
|
||||
ms2Beaufort: function(ms) {
|
||||
var kmh = ms * 60 * 60 / 1000;
|
||||
ms2Beaufort: function (ms) {
|
||||
var kmh = (ms * 60 * 60) / 1000;
|
||||
var speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000];
|
||||
for (var beaufort in speeds) {
|
||||
var speed = speeds[beaufort];
|
||||
|
@ -428,7 +425,7 @@ Module.register("weatherforecast",{
|
|||
*
|
||||
* return string - Rounded Temperature.
|
||||
*/
|
||||
roundValue: function(temperature) {
|
||||
roundValue: function (temperature) {
|
||||
var decimals = this.config.roundTemp ? 0 : 1;
|
||||
return parseFloat(temperature).toFixed(decimals);
|
||||
},
|
||||
|
@ -440,16 +437,16 @@ Module.register("weatherforecast",{
|
|||
* That object has a property "3h" which contains the amount of rain since the previous forecast in the list.
|
||||
* This code finds all forecasts that is for the same day and sums the amount of rain and returns that.
|
||||
*/
|
||||
processRain: function(forecast, allForecasts) {
|
||||
processRain: function (forecast, allForecasts) {
|
||||
//If the amount of rain actually is a number, return it
|
||||
if (!isNaN(forecast.rain)) {
|
||||
return forecast.rain;
|
||||
}
|
||||
|
||||
//Find all forecasts that is for the same day
|
||||
var checkDateTime = (forecast.dt_txt) ? moment(forecast.dt_txt, "YYYY-MM-DD hh:mm:ss") : moment(forecast.dt, "X");
|
||||
var daysForecasts = allForecasts.filter(function(item) {
|
||||
var itemDateTime = (item.dt_txt) ? moment(item.dt_txt, "YYYY-MM-DD hh:mm:ss") : moment(item.dt, "X");
|
||||
var checkDateTime = forecast.dt_txt ? moment(forecast.dt_txt, "YYYY-MM-DD hh:mm:ss") : moment(forecast.dt, "X");
|
||||
var daysForecasts = allForecasts.filter(function (item) {
|
||||
var itemDateTime = item.dt_txt ? moment(item.dt_txt, "YYYY-MM-DD hh:mm:ss") : moment(item.dt, "X");
|
||||
return itemDateTime.isSame(checkDateTime, "day") && item.rain instanceof Object;
|
||||
});
|
||||
|
||||
|
@ -459,10 +456,12 @@ Module.register("weatherforecast",{
|
|||
}
|
||||
|
||||
//Summarize all the rain from the matching days
|
||||
return daysForecasts.map(function(item) {
|
||||
return Object.values(item.rain)[0];
|
||||
}).reduce(function(a, b) {
|
||||
return a + b;
|
||||
}, 0);
|
||||
return daysForecasts
|
||||
.map(function (item) {
|
||||
return Object.values(item.rain)[0];
|
||||
})
|
||||
.reduce(function (a, b) {
|
||||
return a + b;
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
var app = require("../js/app.js");
|
||||
app.start(function(config) {
|
||||
app.start(function (config) {
|
||||
var bindAddress = config.address ? config.address : "localhost";
|
||||
var httpType = config.useHttps ? "https" : "http";
|
||||
console.log("\nReady to go! Please point your browser to: " + httpType + "://" + bindAddress + ":" + config.port);
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
{
|
||||
// Escaped
|
||||
"FOO\"BAR": "Today",
|
||||
// Escaped
|
||||
"FOO\"BAR": "Today",
|
||||
|
||||
/*
|
||||
* The following lines
|
||||
* represent cardinal directions
|
||||
*/
|
||||
"N": "N",
|
||||
"E": "E",
|
||||
"S": "S",
|
||||
"W": "W"
|
||||
/*
|
||||
* The following lines
|
||||
* represent cardinal directions
|
||||
*/
|
||||
"N": "N",
|
||||
"E": "E",
|
||||
"S": "S",
|
||||
"W": "W"
|
||||
}
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
{
|
||||
"LOADING": "Loading …",
|
||||
"LOADING": "Loading …",
|
||||
|
||||
"TODAY": "Today",
|
||||
"TOMORROW": "Tomorrow",
|
||||
"DAYAFTERTOMORROW": "In 2 days",
|
||||
"RUNNING": "Ends in",
|
||||
"EMPTY": "No upcoming events.",
|
||||
"TODAY": "Today",
|
||||
"TOMORROW": "Tomorrow",
|
||||
"DAYAFTERTOMORROW": "In 2 days",
|
||||
"RUNNING": "Ends in",
|
||||
"EMPTY": "No upcoming events.",
|
||||
|
||||
"WEEK": "Week {weekNumber}",
|
||||
"WEEK": "Week {weekNumber}",
|
||||
|
||||
"N": "N",
|
||||
"NNE": "NNE",
|
||||
"NE": "NE",
|
||||
"ENE": "ENE",
|
||||
"E": "E",
|
||||
"ESE": "ESE",
|
||||
"SE": "SE",
|
||||
"SSE": "SSE",
|
||||
"S": "S",
|
||||
"SSW": "SSW",
|
||||
"SW": "SW",
|
||||
"WSW": "WSW",
|
||||
"W": "W",
|
||||
"WNW": "WNW",
|
||||
"NW": "NW",
|
||||
"NNW": "NNW",
|
||||
"N": "N",
|
||||
"NNE": "NNE",
|
||||
"NE": "NE",
|
||||
"ENE": "ENE",
|
||||
"E": "E",
|
||||
"ESE": "ESE",
|
||||
"SE": "SE",
|
||||
"SSE": "SSE",
|
||||
"S": "S",
|
||||
"SSW": "SSW",
|
||||
"SW": "SW",
|
||||
"WSW": "WSW",
|
||||
"W": "W",
|
||||
"WNW": "WNW",
|
||||
"NW": "NW",
|
||||
"NNW": "NNW",
|
||||
|
||||
"UPDATE_NOTIFICATION": "MagicMirror² update available.",
|
||||
"UPDATE_NOTIFICATION_MODULE": "Update available for MODULE_NAME module.",
|
||||
"UPDATE_INFO_SINGLE": "The current installation is COMMIT_COUNT commit behind on the BRANCH_NAME branch.",
|
||||
"UPDATE_INFO_MULTIPLE": "The current installation is COMMIT_COUNT commits behind on the BRANCH_NAME branch."
|
||||
"UPDATE_NOTIFICATION": "MagicMirror² update available.",
|
||||
"UPDATE_NOTIFICATION_MODULE": "Update available for MODULE_NAME module.",
|
||||
"UPDATE_INFO_SINGLE": "The current installation is COMMIT_COUNT commit behind on the BRANCH_NAME branch.",
|
||||
"UPDATE_INFO_MULTIPLE": "The current installation is COMMIT_COUNT commits behind on the BRANCH_NAME branch."
|
||||
}
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
{
|
||||
"LOADING": "Loading …",
|
||||
"LOADING": "Loading …",
|
||||
|
||||
"TODAY": "Today",
|
||||
"TOMORROW": "Tomorrow",
|
||||
"DAYAFTERTOMORROW": "In 2 days",
|
||||
"RUNNING": "Ends in",
|
||||
"EMPTY": "No upcoming events.",
|
||||
"TODAY": "Today",
|
||||
"TOMORROW": "Tomorrow",
|
||||
"DAYAFTERTOMORROW": "In 2 days",
|
||||
"RUNNING": "Ends in",
|
||||
"EMPTY": "No upcoming events.",
|
||||
|
||||
"WEEK": "Week {weekNumber}",
|
||||
"WEEK": "Week {weekNumber}",
|
||||
|
||||
"N": "N",
|
||||
"NNE": "NNE",
|
||||
"NE": "NE",
|
||||
"ENE": "ENE",
|
||||
"E": "E",
|
||||
"ESE": "ESE",
|
||||
"SE": "SE",
|
||||
"SSE": "SSE",
|
||||
"S": "S",
|
||||
"SSW": "SSW",
|
||||
"SW": "SW",
|
||||
"WSW": "WSW",
|
||||
"W": "W",
|
||||
"WNW": "WNW",
|
||||
"NW": "NW",
|
||||
"NNW": "NNW",
|
||||
"N": "N",
|
||||
"NNE": "NNE",
|
||||
"NE": "NE",
|
||||
"ENE": "ENE",
|
||||
"E": "E",
|
||||
"ESE": "ESE",
|
||||
"SE": "SE",
|
||||
"SSE": "SSE",
|
||||
"S": "S",
|
||||
"SSW": "SSW",
|
||||
"SW": "SW",
|
||||
"WSW": "WSW",
|
||||
"W": "W",
|
||||
"WNW": "WNW",
|
||||
"NW": "NW",
|
||||
"NNW": "NNW",
|
||||
|
||||
"UPDATE_NOTIFICATION": "MagicMirror² update available.",
|
||||
"UPDATE_NOTIFICATION_MODULE": "Update available for MODULE_NAME module.",
|
||||
"UPDATE_INFO_SINGLE": "The current installation is COMMIT_COUNT commit behind on the BRANCH_NAME branch.",
|
||||
"UPDATE_INFO_MULTIPLE": "The current installation is COMMIT_COUNT commits behind on the BRANCH_NAME branch."
|
||||
"UPDATE_NOTIFICATION": "MagicMirror² update available.",
|
||||
"UPDATE_NOTIFICATION_MODULE": "Update available for MODULE_NAME module.",
|
||||
"UPDATE_INFO_SINGLE": "The current installation is COMMIT_COUNT commit behind on the BRANCH_NAME branch.",
|
||||
"UPDATE_INFO_MULTIPLE": "The current installation is COMMIT_COUNT commits behind on the BRANCH_NAME branch."
|
||||
}
|
||||
|
|
|
@ -13,13 +13,14 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
]
|
||||
modules: []
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,13 +13,14 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
]
|
||||
modules: []
|
||||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -38,4 +38,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -39,4 +39,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -34,4 +34,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -41,4 +41,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -36,4 +36,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -26,4 +26,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -26,4 +26,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -29,4 +29,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -29,4 +29,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -29,4 +29,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -26,4 +26,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -26,4 +26,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -29,4 +29,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -32,4 +32,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -27,7 +27,6 @@ var config = {
|
|||
afternoon: [],
|
||||
evening: [],
|
||||
anytime: ["Anytime here"]
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,4 +34,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -14,8 +14,8 @@ let config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -28,9 +28,7 @@ let config = {
|
|||
morning: [],
|
||||
afternoon: [],
|
||||
evening: [],
|
||||
"....-01-01": [
|
||||
"Happy new year!"
|
||||
]
|
||||
"....-01-01": ["Happy new year!"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,4 +36,6 @@ let config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -24,7 +24,6 @@ var config = {
|
|||
config: {
|
||||
compliments: {
|
||||
anytime: ["Anytime here"]
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,4 +31,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -23,15 +23,9 @@ var config = {
|
|||
position: "middle_center",
|
||||
config: {
|
||||
compliments: {
|
||||
morning: [
|
||||
"Hi", "Good Morning", "Morning test"
|
||||
],
|
||||
afternoon: [
|
||||
"Hello", "Good Afternoon", "Afternoon test"
|
||||
],
|
||||
evening: [
|
||||
"Hello There", "Good Evening", "Evening test"
|
||||
]
|
||||
morning: ["Hi", "Good Morning", "Morning test"],
|
||||
afternoon: ["Hello", "Good Afternoon", "Afternoon test"],
|
||||
evening: ["Hello There", "Good Evening", "Evening test"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,4 +33,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -29,4 +29,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ var config = {
|
|||
units: "metric",
|
||||
electronOptions: {
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
},
|
||||
nodeIntegration: true
|
||||
}
|
||||
},
|
||||
|
||||
modules: [
|
||||
|
@ -26,4 +26,6 @@ var config = {
|
|||
};
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = config;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = config;
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue