Showing preview only (1,137K chars total). Download the full file or copy to clipboard to get everything.
Repository: MagicMirrorOrg/MagicMirror
Branch: master
Commit: b742e839becf
Files: 382
Total size: 1.0 MB
Directory structure:
gitextract__ioz32a6/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── CODE_OF_CONDUCT.md
│ ├── CONTRIBUTING.md
│ ├── FUNDING.yaml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── change_request.yml
│ │ ├── config.yml
│ │ └── feature_request.yml
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── dependabot.yaml
│ └── workflows/
│ ├── automated-tests.yaml
│ ├── dep-review.yaml
│ ├── electron-rebuild.yaml
│ ├── enforce-pullrequest-rules.yaml
│ ├── release-notes.yaml
│ ├── spellcheck.yaml
│ └── stale.yaml
├── .gitignore
├── .husky/
│ └── pre-commit
├── .markdownlint.json
├── .npmrc
├── .prettierignore
├── CHANGELOG.md
├── Collaboration.md
├── LICENSE.md
├── README.md
├── clientonly/
│ └── index.js
├── cspell.config.json
├── css/
│ ├── custom.css.sample
│ ├── font-awesome.css
│ ├── main.css
│ └── roboto.css
├── eslint.config.mjs
├── index.html
├── jest.config.js
├── js/
│ ├── alias-resolver.js
│ ├── animateCSS.js
│ ├── app.js
│ ├── check_config.js
│ ├── class.js
│ ├── defaults.js
│ ├── deprecated.js
│ ├── electron.js
│ ├── ip_access_control.js
│ ├── loader.js
│ ├── logger.js
│ ├── main.js
│ ├── module.js
│ ├── module_functions.js
│ ├── node_helper.js
│ ├── releasenotes.js
│ ├── server.js
│ ├── server_functions.js
│ ├── socketclient.js
│ ├── translator.js
│ ├── utils.js
│ └── vendor.js
├── jsconfig.json
├── module-types.ts
├── modules/
│ └── default/
│ ├── alert/
│ │ ├── README.md
│ │ ├── alert.js
│ │ ├── notificationFx.js
│ │ ├── styles/
│ │ │ ├── center.css
│ │ │ ├── left.css
│ │ │ ├── notificationFx.css
│ │ │ └── right.css
│ │ ├── templates/
│ │ │ ├── alert.njk
│ │ │ └── notification.njk
│ │ └── translations/
│ │ ├── bg.json
│ │ ├── da.json
│ │ ├── de.json
│ │ ├── el.json
│ │ ├── en.json
│ │ ├── eo.json
│ │ ├── es.json
│ │ ├── fr.json
│ │ ├── hu.json
│ │ ├── nl.json
│ │ ├── pt-br.json
│ │ ├── pt.json
│ │ ├── ru.json
│ │ └── th.json
│ ├── calendar/
│ │ ├── README.md
│ │ ├── calendar.css
│ │ ├── calendar.js
│ │ ├── calendarfetcher.js
│ │ ├── calendarfetcherutils.js
│ │ ├── calendarutils.js
│ │ ├── debug.js
│ │ ├── node_helper.js
│ │ └── windowsZones.json
│ ├── clock/
│ │ ├── README.md
│ │ ├── clock.js
│ │ └── clock_styles.css
│ ├── compliments/
│ │ ├── README.md
│ │ └── compliments.js
│ ├── defaultmodules.js
│ ├── helloworld/
│ │ ├── README.md
│ │ ├── helloworld.js
│ │ └── helloworld.njk
│ ├── newsfeed/
│ │ ├── README.md
│ │ ├── fullarticle.njk
│ │ ├── newsfeed.css
│ │ ├── newsfeed.js
│ │ ├── newsfeed.njk
│ │ ├── newsfeedfetcher.js
│ │ ├── node_helper.js
│ │ └── oldconfig.njk
│ ├── updatenotification/
│ │ ├── README.md
│ │ ├── git_helper.js
│ │ ├── node_helper.js
│ │ ├── update_helper.js
│ │ ├── updatenotification.css
│ │ ├── updatenotification.js
│ │ └── updatenotification.njk
│ ├── utils.js
│ └── weather/
│ ├── README.md
│ ├── current.njk
│ ├── forecast.njk
│ ├── hourly.njk
│ ├── providers/
│ │ ├── README.md
│ │ ├── envcanada.js
│ │ ├── openmeteo.js
│ │ ├── openweathermap.js
│ │ ├── overrideWrapper.js
│ │ ├── pirateweather.js
│ │ ├── smhi.js
│ │ ├── ukmetofficedatahub.js
│ │ ├── weatherbit.js
│ │ ├── weatherflow.js
│ │ ├── weathergov.js
│ │ └── yr.js
│ ├── weather.css
│ ├── weather.js
│ ├── weatherobject.js
│ ├── weatherprovider.js
│ └── weatherutils.js
├── package.json
├── prettier.config.mjs
├── serveronly/
│ ├── index.js
│ └── watcher.js
├── stylelint.config.mjs
├── tests/
│ ├── configs/
│ │ ├── customregions.js
│ │ ├── default.js
│ │ ├── empty_ipWhiteList.js
│ │ ├── modules/
│ │ │ ├── alert/
│ │ │ │ ├── welcome_false.js
│ │ │ │ ├── welcome_string.js
│ │ │ │ └── welcome_true.js
│ │ │ ├── calendar/
│ │ │ │ ├── 3_move_first_allday_repeating_event.js
│ │ │ │ ├── auth-default.js
│ │ │ │ ├── bad_rrule.js
│ │ │ │ ├── basic-auth.js
│ │ │ │ ├── berlin_end_of_day_repeating.js
│ │ │ │ ├── berlin_multi.js
│ │ │ │ ├── berlin_whole_day_event_moved_over_dst_change.js
│ │ │ │ ├── changed-port.js
│ │ │ │ ├── chicago-looking-at-ny-recurring.js
│ │ │ │ ├── chicago_late_in_timezone.js
│ │ │ │ ├── countCalendarEvents.js
│ │ │ │ ├── custom.js
│ │ │ │ ├── default.js
│ │ │ │ ├── diff_tz_start_end.js
│ │ │ │ ├── end_of_day_berlin_moved.js
│ │ │ │ ├── event_with_time_over_multiple_days_non_repeating_display_end.js
│ │ │ │ ├── event_with_time_over_multiple_days_non_repeating_no_display_end.js
│ │ │ │ ├── exdate_and_recurrence_together.js
│ │ │ │ ├── exdate_la_at_midnight_dst.js
│ │ │ │ ├── exdate_la_at_midnight_std.js
│ │ │ │ ├── exdate_la_before_midnight.js
│ │ │ │ ├── exdate_syd_at_midnight_dst.js
│ │ │ │ ├── exdate_syd_at_midnight_std.js
│ │ │ │ ├── exdate_syd_before_midnight.js
│ │ │ │ ├── fail-basic-auth.js
│ │ │ │ ├── fullday_event_over_multiple_days_nonrepeating.js
│ │ │ │ ├── fullday_until.js
│ │ │ │ ├── germany_at_end_of_day_repeating.js
│ │ │ │ ├── long-fullday-event.js
│ │ │ │ ├── old-basic-auth.js
│ │ │ │ ├── recurring.js
│ │ │ │ ├── rrule_until.js
│ │ │ │ ├── show-duplicates-in-calendar.js
│ │ │ │ ├── single-fullday-event.js
│ │ │ │ ├── sliceMultiDayEvents.js
│ │ │ │ └── symboltest.js
│ │ │ ├── clock/
│ │ │ │ ├── clock_12hr.js
│ │ │ │ ├── clock_24hr.js
│ │ │ │ ├── clock_analog.js
│ │ │ │ ├── clock_displaySeconds_false.js
│ │ │ │ ├── clock_showDateAnalog.js
│ │ │ │ ├── clock_showPeriodUpper.js
│ │ │ │ ├── clock_showSunMoon.js
│ │ │ │ ├── clock_showSunNoEvent.js
│ │ │ │ ├── clock_showTime.js
│ │ │ │ ├── clock_showWeek.js
│ │ │ │ ├── clock_showWeek_short.js
│ │ │ │ ├── de/
│ │ │ │ │ ├── clock_showWeek.js
│ │ │ │ │ └── clock_showWeek_short.js
│ │ │ │ └── es/
│ │ │ │ ├── clock_12hr.js
│ │ │ │ ├── clock_24hr.js
│ │ │ │ ├── clock_showPeriodUpper.js
│ │ │ │ ├── clock_showWeek.js
│ │ │ │ └── clock_showWeek_short.js
│ │ │ ├── compliments/
│ │ │ │ ├── compliments_animateCSS.js
│ │ │ │ ├── compliments_animateCSS_fallbackToDefault.js
│ │ │ │ ├── compliments_animateCSS_invertedAnimationName.js
│ │ │ │ ├── compliments_anytime.js
│ │ │ │ ├── compliments_cron_entry.js
│ │ │ │ ├── compliments_date.js
│ │ │ │ ├── compliments_e2e_cron_entry.js
│ │ │ │ ├── compliments_evening.js
│ │ │ │ ├── compliments_file.js
│ │ │ │ ├── compliments_file_change.js
│ │ │ │ ├── compliments_only_anytime.js
│ │ │ │ ├── compliments_parts_day.js
│ │ │ │ ├── compliments_remote.js
│ │ │ │ ├── compliments_specialDayUnique_false.js
│ │ │ │ └── compliments_specialDayUnique_true.js
│ │ │ ├── display.js
│ │ │ ├── helloworld/
│ │ │ │ ├── helloworld.js
│ │ │ │ └── helloworld_default.js
│ │ │ ├── newsfeed/
│ │ │ │ ├── default.js
│ │ │ │ ├── ignore_items.js
│ │ │ │ ├── incorrect_url.js
│ │ │ │ └── prohibited_words.js
│ │ │ ├── positions.js
│ │ │ └── weather/
│ │ │ ├── currentweather_compliments.js
│ │ │ ├── currentweather_default.js
│ │ │ ├── currentweather_options.js
│ │ │ ├── currentweather_units.js
│ │ │ ├── forecastweather_absolute.js
│ │ │ ├── forecastweather_default.js
│ │ │ ├── forecastweather_options.js
│ │ │ ├── forecastweather_units.js
│ │ │ ├── hourlyweather_default.js
│ │ │ ├── hourlyweather_options.js
│ │ │ └── hourlyweather_showPrecipitation.js
│ │ ├── noIpWhiteList.js
│ │ ├── port_8090.js
│ │ ├── port_variable.env
│ │ ├── port_variable.js.template
│ │ └── without_modules.js
│ ├── e2e/
│ │ ├── animateCSS_spec.js
│ │ ├── custom_module_regions_spec.js
│ │ ├── env_spec.js
│ │ ├── fonts_spec.js
│ │ ├── helpers/
│ │ │ ├── basic-auth.js
│ │ │ ├── global-setup.js
│ │ │ └── weather-functions.js
│ │ ├── ipWhitelist_spec.js
│ │ ├── modules/
│ │ │ ├── alert_spec.js
│ │ │ ├── calendar_spec.js
│ │ │ ├── clock_de_spec.js
│ │ │ ├── clock_es_spec.js
│ │ │ ├── clock_spec.js
│ │ │ ├── compliments_spec.js
│ │ │ ├── helloworld_spec.js
│ │ │ ├── newsfeed_spec.js
│ │ │ ├── weather_current_spec.js
│ │ │ ├── weather_forecast_spec.js
│ │ │ └── weather_hourly_spec.js
│ │ ├── modules_display_spec.js
│ │ ├── modules_empty_spec.js
│ │ ├── modules_position_spec.js
│ │ ├── port_spec.js
│ │ ├── serveronly_spec.js
│ │ ├── template_spec.js
│ │ ├── translations_spec.js
│ │ └── vendor_spec.js
│ ├── electron/
│ │ ├── env_spec.js
│ │ ├── helpers/
│ │ │ ├── global-setup.js
│ │ │ └── weather-setup.js
│ │ └── modules/
│ │ ├── calendar_spec.js
│ │ ├── compliments_spec.js
│ │ └── weather_spec.js
│ ├── mocks/
│ │ ├── 12_events.ics
│ │ ├── 3_move_first_allday_repeating_event.ics
│ │ ├── RepeatingEvent.Oct21.ics
│ │ ├── bad_rrule.ics
│ │ ├── calendar_duplicates_1.ics
│ │ ├── calendar_duplicates_2.ics
│ │ ├── calendar_test.ics
│ │ ├── calendar_test_clone.ics
│ │ ├── calendar_test_full_day_events.ics
│ │ ├── calendar_test_icons.ics
│ │ ├── calendar_test_multi_day_starting_today.ics
│ │ ├── calendar_test_recurring.ics
│ │ ├── chicago-nyedge.ics
│ │ ├── chicago_late_in_timezone.ics
│ │ ├── compliments_file.json
│ │ ├── compliments_test.json
│ │ ├── diff_tz_start_end.ics
│ │ ├── end_of_day_berlin_moved.ics
│ │ ├── event_with_time_over_multiple_days_non_repeating.ics
│ │ ├── exdate_and_recurrence_together.ics
│ │ ├── exdate_la_at_midnight_dst.ics
│ │ ├── exdate_la_at_midnight_std.ics
│ │ ├── exdate_la_before_midnight.ics
│ │ ├── exdate_syd_at_midnight_dst.ics
│ │ ├── exdate_syd_at_midnight_std.ics
│ │ ├── exdate_syd_before_midnight.ics
│ │ ├── fullday_event_over_multiple_days_nonrepeating.ics
│ │ ├── fullday_until.ics
│ │ ├── germany_at_end_of_day_repeating.ics
│ │ ├── newsfeed_test.xml
│ │ ├── rrule_until.ics
│ │ ├── sliceMultiDayEvents.ics
│ │ ├── testNotification/
│ │ │ └── testNotification.js
│ │ ├── translation_test.json
│ │ ├── weather_current.json
│ │ ├── weather_forecast.json
│ │ ├── weather_hourly.json
│ │ └── whole_day_moved_over_dst_change_berlin.ics
│ ├── unit/
│ │ ├── classes/
│ │ │ ├── class_spec.js
│ │ │ ├── deprecated_spec.js
│ │ │ ├── translator_spec.js
│ │ │ └── utils_spec.js
│ │ ├── functions/
│ │ │ ├── __snapshots__/
│ │ │ │ └── updatenotification_spec.js.snap
│ │ │ ├── cmp_versions_spec.js
│ │ │ ├── server_functions_spec.js
│ │ │ └── updatenotification_spec.js
│ │ ├── global_vars/
│ │ │ ├── defaults_modules_spec.js
│ │ │ └── root_path_spec.js
│ │ ├── helpers/
│ │ │ └── global-setup.js
│ │ └── modules/
│ │ └── default/
│ │ ├── calendar/
│ │ │ ├── calendar_fetcher_utils_bad_rrule.js
│ │ │ ├── calendar_fetcher_utils_spec.js
│ │ │ └── calendar_utils_spec.js
│ │ ├── compliments/
│ │ │ └── compliments_spec.js
│ │ ├── utils_spec.js
│ │ └── weather/
│ │ ├── weather_object_spec.js
│ │ └── weather_utils_spec.js
│ └── utils/
│ ├── vitest-setup.js
│ └── weather_mocker.js
├── translations/
│ ├── af.json
│ ├── ar.json
│ ├── bg.json
│ ├── ca.json
│ ├── cs.json
│ ├── cv.json
│ ├── cy.json
│ ├── da.json
│ ├── de.json
│ ├── el.json
│ ├── en.json
│ ├── eo.json
│ ├── es.json
│ ├── et.json
│ ├── fi.json
│ ├── fr.json
│ ├── fy.json
│ ├── gl.json
│ ├── gu.json
│ ├── he.json
│ ├── hi.json
│ ├── hr.json
│ ├── hu.json
│ ├── id.json
│ ├── is.json
│ ├── it.json
│ ├── ja.json
│ ├── ko.json
│ ├── lt.json
│ ├── ms-my.json
│ ├── nb.json
│ ├── nl.json
│ ├── nn.json
│ ├── pl.json
│ ├── ps.json
│ ├── pt-br.json
│ ├── pt.json
│ ├── ro.json
│ ├── ru.json
│ ├── sk.json
│ ├── sv.json
│ ├── th.json
│ ├── tlh.json
│ ├── tr.json
│ ├── translations.js
│ ├── uk.json
│ ├── zh-cn.json
│ └── zh-tw.json
└── vitest.config.mjs
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
max_line_length = 250
trim_trailing_whitespace = true
[*.{js,json}]
indent_size = 4
indent_style = tab
================================================
FILE: .gitattributes
================================================
# .gitattributes snippet to force users to use same line endings for project.
#
# Handle line endings automatically for files detected as text
# and leave all files detected as binary untouched.
* text=auto
#
# The above will handle all files NOT found below
# https://help.github.com/articles/dealing-with-line-endings/
# https://github.com/Danimoth/gitattributes/blob/master/Web.gitattributes
# These files are text and should be normalized (Convert crlf => lf)
*.php text
*.css text
*.scss text
*.js text
*.json text
*.htm text
*.html text
*.xml text
*.txt text
*.ini text
*.inc text
*.pl text
*.rb text
*.py text
*.scm text
*.sql text
.htaccess text
*.sh text
Dockerfile* text
*.yml text
*.yaml text
*.md text
*.markdown text
# These files are binary and should be left untouched
# (binary is a macro for -text -diff)
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.mov binary
*.mp4 binary
*.mp3 binary
*.flv binary
*.fla binary
*.swf binary
*.gz binary
*.zip binary
*.7z binary
*.ttf binary
*.pyc binary
================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
- Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or advances of
any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email address,
without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official email address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement:
Contact [Rejas](https://forum.magicmirror.builders/user/rejas),
[Karsten](https://forum.magicmirror.builders/user/karsten13),
[Sam](https://forum.magicmirror.builders/user/sdetweil) or
[Kristjan](https://forum.magicmirror.builders/user/kristjanesperanto)
via private message in the forum.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contribution Policy for MagicMirror²
Thanks for contributing to MagicMirror²!
We hold our code to standard, and these standards are documented below.
## Linters
We use [prettier](https://prettier.io/) for automatic formatting a lot all our files. The configuration is in our `prettier.config.mjs` file.
To run prettier, use `node --run lint:prettier`.
### JavaScript: Run ESLint
We use [ESLint](https://eslint.org) to lint our JavaScript files. The configuration is in our `eslint.config.mjs` file.
To run ESLint, use `node --run lint:js`.
### CSS: Run StyleLint
We use [StyleLint](https://stylelint.io) to lint our CSS. The configuration is in our `stylelint.config.mjs` file.
To run StyleLint, use `node --run lint:css`.
### Markdown: Run markdownlint
We use [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) to lint our markdown files. The configuration is in our `.markdownlint.json` file.
To run markdownlint, use `node --run lint:markdown`.
## Testing
We use [Vitest](https://vitest.dev) for JavaScript testing.
To run all tests, use `node --run test`.
The `package.json` scripts expose finer-grained test commands:
- `test:unit` – run unit tests only
- `test:e2e` – execute browser-driven end-to-end tests
- `test:electron` – launch the Electron-based regression suite
- `test:coverage` – collect coverage while running every suite
- `test:watch` – keep Vitest in watch mode for fast local feedback
- `test:ui` – open the Vitest UI dashboard (needs OS file-watch support enabled)
- `test:calendar` – run the legacy calendar debug helper
- `test:css`, `test:markdown`, `test:prettier`, `test:spelling`, `test:js` – lint-only scripts that enforce formatting, spelling, markdown style, and ESLint.
You can invoke any script with `node --run <script>` (or `npm run <script>`). Individual files can still be targeted directly, e.g. `npx vitest run tests/e2e/env_spec.js`.
================================================
FILE: .github/FUNDING.yaml
================================================
github: MichMich
custom: ["https://magicmirror.builders/#donate"]
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: 🐛 Report a problem
description: Report an issue with MagicMirror² 🚨
title: "[Bug] {{ brief description }}"
labels:
- bug
body:
- type: markdown
attributes:
value: |
Thanks for reporting a bug! Please fill in the following template to help us reproduce the issue.
Please only submit reproducible issues. If you're not sure if it's a real bug or if it's just you, please open a topic on the forum.
- type: textarea
id: environment
attributes:
label: Environment
description: |
Please tell us about how your MagicMirror² is set up.
Optimal would be the systeminformation from the logs, which looks like this:
```bash
[2025-01-14 20:05:03.529] [INFO] System information:
### SYSTEM: manufacturer: Raspberry Pi Foundation; model: Raspberry Pi 4 Model B Rev 1.5; virtual: false
### OS: platform: linux; distro: Debian GNU/Linux; release: 12; arch: arm64; kernel: 6.1.21-v8+
### VERSIONS: electron: 31.2.1; used node: 20.15.0; installed node: 22.4.1; npm: 10.8.1; pm2:
### OTHER: timeZone: Europe/Berlin; ELECTRON_ENABLE_GPU: undefined
```
If you can't provide this information, please provide the following:
- MagicMirror² version: Can be found in the `package.json` file. Please use the latest version before reporting a bug.
- Node version: Run `node -v` to find out. Make sure it's version 20 or later (recommended is 22).
- npm version: Run `npm -v` to find out.
- Platform: Are you using a Raspberry Pi (2/3/4/5), Windows, Mac, Linux, Docker, or something else?
value: |
MagicMirror² version:
Node version:
npm version:
Platform:
validations:
required: true
- type: dropdown
id: start-option
attributes:
label: Which start option are you using?
description: |
Please keep in mind that some problems are specific to certain start options.
options:
- "node --run start"
- "node --run start:wayland"
- "node --run start:windows"
- "node --run start:x11"
- "node --run server"
- "node clientonly --address ... --port ..."
validations:
required: true
- type: dropdown
id: pm2
attributes:
label: Are you using PM2?
options:
- "No"
- "Yes"
- "I don't know"
validations:
required: true
- type: dropdown
id: module
attributes:
label: Module
description: |
If the issue is related to a specific module, please provide the name of the module.
Note: Please don't report issues with 3rd party modules here. Report them on the module's repository.
options:
- "alert"
- "calendar"
- "clock"
- "compliments"
- "helloworld"
- "newsfeed"
- "updatenotification"
- "weather"
- type: checkboxes
id: module-disabled
attributes:
label: Have you tried disabling other modules?
options:
- label: "Yes"
- label: "No"
- type: checkboxes
id: search
attributes:
label: Have you searched if someone else has already reported the issue on the forum or in the issues?
options:
- label: "Yes"
required: true
- type: textarea
id: description
attributes:
label: What did you do?
description: |
Please include a *minimal* reproduction case. List the step by step process to reproduce the issue.
You can use Markdown in this field.
value: |
<details>
<summary>Configuration</summary>
```
<!-- Paste your configuration here. Don't forget to remove any sensitive information! -->
```
</details>
```js
<!-- Paste relevant code here -->
```
Steps to reproduce the issue:
validations:
required: true
- type: textarea
id: expectation
attributes:
label: What did you expect to happen?
description: |
You can use Markdown in this field.
validations:
required: true
- type: textarea
id: lint-output
attributes:
label: What actually happened?
description: |
Please copy-paste relevant log output or error messages.
You can use Markdown in this field.
validations:
required: true
- type: textarea
id: comments
attributes:
label: Additional comments
description: |
Is there anything else that's important for the team to know?
Fill out all fields and provide as much information as possible.
Adding screenshots might help us understand your problem better.
- type: checkboxes
attributes:
label: Participation
options:
- label: "I am willing to submit a pull request for this change."
required: false
- type: markdown
attributes:
value: Please **do not** open a pull request until this issue has been accepted by the team.
================================================
FILE: .github/ISSUE_TEMPLATE/change_request.yml
================================================
name: 🔀 Request a change
description: Request a change that is not a bug fix, a feature request or a support request.
title: "[Change Request] {{ brief description }}"
labels:
- enhancement
- core
body:
- type: markdown
attributes:
value: Thanks for requesting a change! Please fill in the following template to help us understand your request.
- type: textarea
attributes:
label: What problem do you want to solve with this change?
description: |
Please explain your use case in as much detail as possible.
placeholder: |
Currently...
validations:
required: true
- type: textarea
attributes:
label: What do you think is the correct solution?
description: |
Please explain how you'd like to change MagicMirror² to address the problem.
placeholder: |
I'd like MagicMirror² to...
validations:
required: true
- type: checkboxes
attributes:
label: Participation
options:
- label: I am willing to submit a pull request for this change.
required: false
- type: markdown
attributes:
value: Please **do not** open a pull request until this issue has been accepted by the team.
- type: textarea
attributes:
label: Additional comments
description: Is there anything else that's important for the team to know?
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: 📚 Documentation
url: https://github.com/MagicMirrorOrg/MagicMirror-Documentation/issues
about: This issue tracker is not for documentation issues. Please file documentation issues on the docs repo.
- name: 🤔 Support Question
url: https://forum.magicmirror.builders/
about: Problems installing or configuring your MagicMirror? Please post your question on the MagicMirror² Forum.
- name: 💬 Exchange of ideas
url: https://discord.gg/AmGBBwPph5
about: This issue tracker is not for general discussion. Please use the Discord channel.
- name: 📦 Issues with a 3rd-party module
url: https://kristjanesperanto.github.io/MagicMirror-3rd-Party-Modules/
about: This issue tracker is not for 3rd-party module issues. Please file 3rd-party module issues on the module's repo.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: 🚀 Feature Request
description: Suggest a new feature for MagicMirror² 💡
title: "[Feature Request] {{ brief description }}"
body:
- type: checkboxes
id: prerequisites
attributes:
label: Prerequisites
description: Please ensure you have completed all of the following.
options:
- label: I am running the latest version of MagicMirror², and know that this feature is not available now.
required: true
- label: I know my issue is not related to a third-party module.
required: true
- label: I have searched for [existing issues](https://github.com/MagicMirrorOrg/MagicMirror/issues) that already include this feature request, without success.
required: true
- type: textarea
id: description
attributes:
label: Describe the Feature Request
description: A clear and concise description of what the feature does.
validations:
required: true
- type: textarea
id: use-case
attributes:
label: Describe the Use Case
description: A clear and concise use case for what problem this feature would solve.
validations:
required: true
- type: textarea
id: proposed-solution
attributes:
label: Describe Preferred Solution
description: A clear and concise description of how you want this feature to be added to MagicMirror².
- type: textarea
id: alternatives-considered
attributes:
label: Describe Alternatives
description: A clear and concise description of any alternative solutions or features you have considered.
- type: textarea
id: related-code
attributes:
label: Related Code
description: If you are able to illustrate the feature request with an example, please provide a sample here.
- type: textarea
id: additional-information
attributes:
label: Additional Information
description: List any other information that is relevant to your issue. Related issues, suggestions on how to implement, Stack Overflow links, forum links, etc.
- type: checkboxes
attributes:
label: Participation
options:
- label: I am willing to submit a pull request for this change.
required: false
- type: markdown
attributes:
value: Please **do not** open a pull request until this issue has been accepted by the team.
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
Hello and thank you for wanting to contribute to the MagicMirror² project!
**Please make sure that you have followed these 3 rules before submitting your Pull Request:**
> 1. Base your pull requests against the `develop` branch.
> 2. Include these infos in the description:
>
> - Does the pull request solve a **related** issue?
> - If so, can you reference the issue like this `Fixes #<issue_number>`?
> - What does the pull request accomplish? Use a list if needed.
> - If it includes major visual changes please add screenshots.
>
> 3. Please run `node --run lint:prettier` before submitting so that
> style issues are fixed.
**Note**: Sometimes the development moves very fast. It is highly
recommended that you update your branch of `develop` before creating a
pull request to send us your changes. This makes everyone's lives
easier (including yours) and helps us out on the development team.
Thanks again and have a nice day!
================================================
FILE: .github/dependabot.yaml
================================================
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
target-branch: "develop"
labels:
- "dependencies"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "monthly"
target-branch: "develop"
labels:
- "dependencies"
- "javascript"
================================================
FILE: .github/workflows/automated-tests.yaml
================================================
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: "Run Automated Tests"
on:
push:
branches: [master, develop]
pull_request:
branches: [master, develop]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
code-style-check:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: "Checkout code"
uses: actions/checkout@v6
- name: "Use Node.js"
uses: actions/setup-node@v6
with:
node-version: lts/*
cache: "npm"
- name: "Install dependencies"
run: |
node --run install-mm:dev
- name: "Run linter tests"
run: |
node --run test:prettier
node --run test:js
node --run test:css
node --run test:markdown
test:
runs-on: ubuntu-24.04
timeout-minutes: 30
strategy:
matrix:
node-version: [22.21.1, 22.x, 24.x]
steps:
- name: Install electron dependencies and labwc
run: |
sudo apt-get update
sudo apt-get install -y libnss3 libasound2t64 labwc
- name: "Checkout code"
uses: actions/checkout@v6
- name: "Use Node.js ${{ matrix.node-version }}"
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
check-latest: true
cache: "npm"
- name: "Install MagicMirror²"
run: |
node --run install-mm:dev
- name: "Install Playwright browsers"
run: |
npx playwright install --with-deps chromium
- name: "Prepare environment for tests"
run: |
# Fix chrome-sandbox permissions:
sudo chown root:root ./node_modules/electron/dist/chrome-sandbox
sudo chmod 4755 ./node_modules/electron/dist/chrome-sandbox
# Start labwc
WLR_BACKENDS=headless WLR_LIBINPUT_NO_DEVICES=1 WLR_RENDERER=pixman labwc &
touch css/custom.css
- name: "Run tests"
run: |
export WAYLAND_DISPLAY=wayland-0
node --run test
================================================
FILE: .github/workflows/dep-review.yaml
================================================
# This workflow scans your pull requests for dependency changes, and will raise an error if any vulnerabilities or invalid licenses are being introduced.
# For more information see: https://github.com/actions/dependency-review-action
name: "Review Dependencies"
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: "Checkout code"
uses: actions/checkout@v6
- name: "Dependency Review"
uses: actions/dependency-review-action@v4
================================================
FILE: .github/workflows/electron-rebuild.yaml
================================================
name: "Electron Rebuild Testing"
on: [pull_request]
jobs:
rebuild:
name: Run electron-rebuild
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [22.21.1, 22.x, 24.x]
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: "Use Node.js ${{ matrix.node-version }}"
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
check-latest: true
- name: Install MagicMirror
run: node --run install-mm
- name: Install @electron/rebuild
run: npm install @electron/rebuild
- name: Install test library (serialport) to be rebuilt
run: npm install serialport
- name: Run electron-rebuild
run: npx electron-rebuild
continue-on-error: false
================================================
FILE: .github/workflows/enforce-pullrequest-rules.yaml
================================================
# This workflow enforces on every pull request that the PR is not based against master,
# taken from https://github.com/oppia/oppia-android/blob/develop/.github/workflows/static_checks.yml
name: "Enforce Pull-Request Rules"
on:
pull_request:
push:
branches-ignore:
- develop
- master
jobs:
check:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
timeout-minutes: 10
steps:
- name: "Branch is not based on develop"
if: ${{ github.base_ref != 'develop' && !contains(github.event.pull_request.labels.*.name, 'mastermerge') }}
run: |
echo "Current base branch: $BASE_BRANCH"
echo "Note: PRs should only ever be merged into develop so please rebase your branch on develop and try again."
exit 1
env:
BASE_BRANCH: ${{ github.base_ref }}
================================================
FILE: .github/workflows/release-notes.yaml
================================================
# This workflow writes a draft release on GitHub named `unreleased` after every push on develop
name: "Create Release Notes"
on:
push:
branches: [develop]
permissions:
contents: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
release-notes:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: "Checkout code"
uses: actions/checkout@v6
with:
fetch-depth: "0"
- name: "Use Node.js"
uses: actions/setup-node@v6
with:
node-version: lts/*
cache: "npm"
- name: "Create Markdown content"
run: |
export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
node js/releasenotes.js
================================================
FILE: .github/workflows/spellcheck.yaml
================================================
# This workflow will run a spellcheck on the codebase.
# It runs a few days before each release. At 00:00 on day-of-month 27 in March, June, September, and December.
name: Run Spellcheck
on:
schedule:
- cron: "0 0 27 3,6,9,12 *"
permissions:
contents: read
jobs:
spellcheck:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: develop
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: lts/*
check-latest: true
cache: "npm"
- name: Install dependencies
run: |
node --run install-mm:dev
- name: Run Spellcheck
run: node --run test:spelling
================================================
FILE: .github/workflows/stale.yaml
================================================
name: "Close stale issues and PRs"
on:
workflow_dispatch: # needed for manually running this workflow
schedule:
- cron: "30 1 * * 6" # every Saturday at 1:30
permissions:
issues: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v10
with:
stale-issue-message: "This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions."
days-before-issue-stale: 60
days-before-issue-close: 7
operations-per-run: 100
stale-issue-label: "wontfix"
exempt-issue-labels: "pinned,security,under investigation,pr welcome,ready (coming with next release)"
================================================
FILE: .gitignore
================================================
# Various Node ignoramuses.
logs
*.log
npm-debug.log*
pids
*.pid
*.seed
lib-cov
coverage
.lock-wscript
build/Release
node_modules
jspm_modules
.npm
.node_repl_history
# Visual Studio Code ignoramuses.
.vscode/
# IDE Code ignoramuses.
.idea/
# Various Windows ignoramuses.
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
*.cab
*.msi
*.msm
*.msp
*.lnk
# Various OSX ignoramuses.
.DS_Store
.AppleDouble
.LSOverride
Icon
._*
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Various Linux ignoramuses.
.fuse_hidden*
.directory
.Trash-*
# Ignore all modules except the default modules.
/modules/*
!/modules/default
# Ignore changes to the custom css files but keep the sample and main.
/css/*
!/css/custom.css.sample
!/css/font-awesome.css
!/css/main.css
!/css/roboto.css
# Ignore users config file but keep the sample.
config
!config/config.js.sample
# Vim
## swap
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
## diff patch
*.orig
*.rej
*.bak
# Ignore positions file (#3518)
js/positions.js
# Ignore lock files other than package-lock.json
pnpm-lock.yaml
yarn.lock
# Vitest temporary test files
tests/**/.tmp/
================================================
FILE: .husky/pre-commit
================================================
#!/bin/sh
if command -v npx &> /dev/null; then
npx lint-staged
fi
================================================
FILE: .markdownlint.json
================================================
{
"line_length": false,
"no-duplicate-heading": false,
"no-inline-html": false,
"no-trailing-punctuation": false
}
================================================
FILE: .npmrc
================================================
engine-strict=true
audit=false
loglevel="error"
================================================
FILE: .prettierignore
================================================
*.js
*.mjs
.husky/pre-commit
.prettierignore
/config
/coverage
package-lock.json
**.ics
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/#donate) With your help we can continue to improve the MagicMirror².
## Obsolete
This file is no longer being updated. Release notes are now automatically generated via a GitHub action.
## [2.33.0] - 2025-10-01
Thanks to: @Crazylegstoo, @dathbe, @m-idler, @plebcity, @khassel, @KristjanESPERANTO, @rejas and @sdetweil!
> ⚠️ This release needs nodejs version `v22.18.0 or higher`
### Added
- Add configuration option for `User-Agent`, used by calendar & news module (#3255)
- [linter] Add prettier plugin for nunjuck templates (#3887)
- [core] Add clear log for occupied port at startup (#3890)
### Changed
- [clock] Add CSS to prevent line breaking of sunset/sunrise time display (#3816)
- [core] Enhance system information logging format and include additional env and RAM details (#3839, #3843)
- [refactor] Add new file `js/module_functions.js` to move code used in several modules to one place (#3837)
- [refactor] Use global.root_path where possible and add tests for config:check (#3883, #3885, #3886, #3889)
- [tests] refactor: simplify jest config file (#3844)
- [tests] refactor: extract constants for weather electron tests (#3845)
- [tests] refactor: add `setupDOMEnvironment` helper function to eliminate repetitive JSDOM setup code (#3860)
- [tests] replace `console` with `Log` in calendar `debug.js` to avoid exception in eslint config (#3846)
- [tests] speed up e2e tests, cleanup and stabilize weather e2e tests, update snapshot url (#3847, #3848, #3861)
- [tests] refactor translation tests (#3866)
- Remove `sinon` dependency in favor of Jest native mocking
- Unify test helper functions across translation test suites
- Rename `setupDOMEnvironment` to `createTranslationTestEnvironment` for consistency
- Simplify DOM setup by removing unnecessary Promise/async patterns
- Avoid potential port conflicts by using port 3001 for translator unit tests
- Improve test reliability and maintainability
- [tests] add alert module tests for different welcome_message configurations (#3867)
- [lint-staged] use `prettier --write --ignore-unknown` in `lint-staged` to avoid errors on unsupported files (#3888)
### Updated
- [calendar] Update defaultSymbol name and also the link to the icon search site (#3879)
- [core] Update dependencies including electron to v38 as well as github actions (#3831, #3849, #3857, #3858, #3872, #3876, #3882, #3891, #3896)
- [weather] Update feels_like temperature calculation formula (#3869)
- [weather] Update null value handling for weather type (#3892)
- [layout] Update styles for weather and calendar (#3894)
### Fixed
- [calendar] Fixed broken unittest that only broke on the 1st of July and 1st of january (#3830)
- [clock] Fixed missing icons when no other modules with icons is loaded (#3834)
- [weather] Fixed handling of empty values in weathergov providers handling of precipitationAmount (#3859)
- [calendar] Fix regression handling of limit days (#3840)
- [calendar] Fixed regression of calendarfetcherutils.shouldEventBeExcluded (#3841)
- [core] Fixed socket.io timeout when server is slow to send notification, notification lost at client (#3380)
- [tests] refactor AnimateCSS tests after jsdom 27 upgrade (#3891)
- [weather] Use `apparent_temperature` data from openmeteo's hourly weather for current feelsLikeTemp (#3868).
- [weather] Updated envcanada Provider to use new database/URL schema for accessing weather data (#3878).
## [2.32.0] - 2025-07-01
Thanks to: @bughaver, @bugsounet, @khassel, @KristjanESPERANTO, @plebcity, @rejas, @sdetweil.
> ⚠️ This release needs nodejs version `v22.14.0 or higher`
### Added
- [config] Allow to change module order for final renderer (or dynamically with CSS): Feature `order` in config (#3762)
- [clock] Added option 'disableNextEvent' to hide next sun event (#3769)
- [clock] Implement short syntax for clock week (#3775)
### Changed
- [refactor] Simplify module loading process (#3766)
- Use `node --run` instead of `npm run` (#3764) and adapt `start:dev` script (#3773)
- [workflow] Run linter and spellcheck with LTS node version (#3767)
- [workflow] Split "Run test" step into two steps for more clarity (#3767)
- [linter] Review linter setup (#3783)
- Fix command to lint markdown in `CONTRIBUTING.md`
- Re-activate JSDoc linting and fix linting issues
- Refactor ESLint config to use `defineConfig` and `globalIgnores`
- Replace `eslint-plugin-import` with `eslint-plugin-import-x`
- Switch Stylelint config to flat format and simplify Stylelint scripts
- [workflow] Replace Node.js version v23 with v24 (#3770)
- [refactor] Replace deprecated constants `fs.F_OK` and `fs.R_OK` (#3789)
- [refactor] Replace `ansis` with built-in function `util.styleText` (#3793)
- [core] Integrate stuff from `vendor` and `fonts` folders into main `package.json`, simplifies install and maintaining dependencies (#3795, #3805)
- [l10n] Complete translations (with the help of translation tools) (#3794)
- [refactor] Refactored `calendarfetcherutils` in Calendar module to handle timezones better (#3806)
- Removed as many of the date conversions as possible
- Use `moment-timezone` when calculating recurring events, this will fix problems from the past with offsets and DST not being handled properly
- Added some tests to test the behavior of the refactored methods to make sure the correct event dates are returned
- [linter] Enable ESLint rule `no-console` and replace `console` with `Log` in some files (#3810)
- [tests] Review and refactor translation tests (#3792)
### Fixed
- [fix] Handle spellcheck issues (#3783)
- [calendar] fix fullday event rrule until with timezone offset (#3781)
- [feat] Add rule `no-undef` in config file validation to fix #3785 (#3786)
- [fonts] Fix `roboto.css` to avoid error message `Unknown descriptor 'var(' in @font-face rule.` in firefox console (#3787)
- [tests] Fix and refactor e2e test `Same keys` in `translations_spec.js` (#3809)
- [tests] Fix e2e tests newsfeed and calendar to exit without open handles (#3817)
### Updated
- [core] Update dependencies including electron to v36 (#3774, #3788, #3811, #3804, #3815, #3823)
- [core] Update package type to `commonjs`
- [logger] Review factory code part: use `switch/case` instead of `if/else if` (#3812)
## [2.31.0] - 2025-04-01
Thanks to: @Developer-Incoming, @eltociear, @geraki, @khassel, @KristjanESPERANTO, @MagMar94, @mixasgr, @n8many, @OWL4C, @rejas, @savvadam, @sdetweil.
> ⚠️ This release needs nodejs version `v22.14.0 or higher`
### Added
- Add CSS support to the digital clock hour/minute/second through the use of the classes `clock-hour-digital`, `clock-minute-digital`, and `clock-second-digital`.
- Add Arabic (#3719) and Esperanto translation (#3740)
- Mark option `secondsColor` as deprecated in clock module.
- Add Greek translation to Alerts module.
- [newsfeed] Add specific ignoreOlderThan value (override) per feed (#3360)
- [weather] Added option Humidity to hourly View
- [weather] Added option to hide hourly entries that are Zero, hiding the entire column if empty.
- [updatenotification] Added option to iterate over modules directory instead using modules defined in `config.js` (#3739)
### Changed
- [core] Starting clientonly now checks for needed env var `WAYLAND_DISPLAY` or `DISPLAY` and starts electron with needed parameters (if both are set Wayland is used) (#3677)
- [core] Optimize systeminformation calls and output (#3689)
- [core] Add issue templates for feature requests and bug reports (#3695)
- [core] Adapt `start:x11:dev` script
- [weather/yr] The Yr weather provider now enforces a minimum `updateInterval` of 600 000 ms (10 minutes) to comply with the terms of service. If a lower value is set, it will be automatically increased to this minimum.
- [weather/weatherflow] Fixed icons and added hourly support as well as UV, precipitation, and location name support.
- [workflow] Run `sudo apt-get update` before installing packages to avoid install errors
- [workflow] Exclude issues with label `ready (coming with next release)` from stale job
### Removed
### Updated
- [core] Update requirements and dependencies including electron to v35 and formatting (#3593, #3693, #3717)
- [core] Update prettier, ESLint and simplify config
- Update Greek translation
### Fixed
- [calendar] Fix clipping events being broadcast (#3678)
- [tests] Fix Electron tests by running them under new github image ubuntu-24.04, replace xserver with labwc, running under xserver and labwc depending on env variable WAYLAND_DISPLAY is set (#3676)
- [calendar] Fix arrayed symbols, #3267, again, add testcase, add testcase for #3678
- [weather] Fix wrong weatherCondition name in openmeteo provider which lead to n/a icon (#3691)
- [core] Fix wrong port in log message when starting server only (#3696)
- [calendar] Fix NewYork event processed on system in Central timezone shows wrong time #3701
- [weather/yr] The Yr weather provider is now able to recover from bad API responses instead of freezing (#3296)
- [compliments] Fix evening events being shown during the day (#3727)
- [weather] Fixed minor spacing issues when using UV Index in Hourly
- [workflow] Fix command to run spellcheck
## [2.30.0] - 2025-01-01
Thanks to: @xsorifc28, @HeikoGr, @bugsounet, @khassel, @KristjanESPERANTO, @rejas, @sdetweil.
> ⚠️ This release needs nodejs version `v20` or `v22 or higher`, minimum version is `v20.18.1`
### Added
- [core] Add Wayland and Windows start options to `package.json` (#3594)
- [docs] Add step for npm publishing in release process (#3595)
- [core] Add GitHub workflow to run spellcheck a few days before each release (#3623)
- [core] Add test flag to `index.html` to pass to module js for test mode detection (needed by #3630)
- [core] Add export on animation names (#3644)
- [compliments] Add support for refreshing remote compliments file, and test cases (#3630)
- [linter] Re-add `eslint-plugin-import`now that it supports ESLint v9 (#3586)
- [linter] Re-activate `eslint-plugin-package-json` to lint `package.json` (#3643)
- [linter] Add linting for markdown files (#3646)
- [linter] Add some handy ESLint rules (#3665)
- [calendar] Add ability to display end date for full date events, where end is not same day (showEnd=true) (#3650)
- [core] Add text to the config.js.sample file about the locale variable (#3654, #3655)
- [core] Add fetch timeout for all node_helpers (thru undici, forces node 20.18.1 minimum) to help on slower systems. (#3660) (3661)
### Changed
- [core] Run code style checks in workflow only once (#3648)
- [core] Fix animations export #3644 only on server side (#3649)
- [core] Use project URL in fallback config (#3656)
- [core] Fix Access Denied crash writing js/positions.js (on synology nas) #3651. new message, MM starts, but no modules showing (#3652)
- [linter] Switch to 'npx' for lint-staged in pre-commit hook (#3658)
### Removed
- [tests] Remove `node-pty` and `drivelist` from rebuilded test (#3575)
- [deps] Remove `@eslint/js` dependency. Already installed with `eslint` in deep (#3636)
### Updated
- [repo] Reactivate `stale.yaml` as GitHub action to mark issues as stale after 60 days and close them 7 days later (if no activity) (#3577, #3580, #3581)
- [core] Update electron dependency to v32 (test electron rebuild) and all other dependencies too (#3657)
- [tests] All test configs have been updated to allow full external access, allowing for easier debugging (especially when running as a container)
- [core] Run and test with node 23 (#3588)
- [workflow] delete exception `allow-ghsas: GHSA-8hc4-vh64-cxmj` in `dep-review.yaml` (#3659)
### Fixed
- [updatenotification] Fix pm2 using detection when pm2 script is inside or outside MagicMirror root folder (#3576) (#3605) (#3626) (#3628)
- [core] Fix loading node_helper of modules: avoid black screen, display errors and continue loading with next module (#3578)
- [weather] Change default value for weatherEndpoint of provider openweathermap to "/onecall" (#3574)
- [tests] Fix electron tests with mock dates, the mock on server side was missing (#3597)
- [tests] Fix testcases with hard coded Date.now (#3597)
- [core] Fix missing `basePath` where `location.host` is used (#3613)
- [compliments] croner library changed filenames used in latest version (#3624)
- [linter] Fix ESLint ignore pattern which caused that default modules not to be linted (#3632)
- [core] Fix module path in case of sub/sub folder is used and use path.resolve for resolve `moduleFolder` and `defaultModuleFolder` in app.js (#3653)
- [calendar] Update to resolve issues #3098 #3144 #3351 #3422 #3443 #3467 #3537 related to timezone changes
- [calendar] Fix #3267 (styles array), also fixes event with both exdate AND recurrence(and testcase)
- [calendar] Fix showEndsOnlyWithDuration not working, #3598, applies ONLY to full day events
- [calendar] Fix showEnd for Full Day events (#3602)
- [tests] Suppress "module is not defined" in e2e tests (#3647)
- [calendar] Fix #3267 (styles array, really this time!)
- [core] Fix #3662 js/positions.js created incorrectly
## [2.29.0] - 2024-10-01
Thanks to: @bugsounet, @dkallen78, @jargordon, @khassel, @KristjanESPERANTO, @MarcLandis, @rejas, @ryan-d-williams, @sdetweil, @skpanagiotis.
> ⚠️ This release needs nodejs version `v20` or `v22`, minimum version is `v20.9.0`
### Added
- [compliments] Added support for cron type date/time format entries mm hh DD MM dow (minutes/hours/days/months and day of week) see <https://crontab.cronhub.io> for construction (#3481)
- [core] Check config at every start of MagicMirror² (#3450)
- [core] Add spelling check (cspell): `npm run test:spelling` and handle spelling issues (#3544)
- [core] removed `config.paths.vendor` (could not work because `vendor` is hardcoded in `index.html`), renamed `config.paths.modules` to `config.foreignModulesDir`, added variable `MM_CUSTOMCSS_FILE` which - if set - overrides `config.customCss`, added variable `MM_MODULES_DIR` which - if set - overrides `config.foreignModulesDir`, added test for `MM_MODULES_DIR` (#3530)
- [core] elements are now removed from `index.html` when loading script or stylesheet files fails
- [core] Added `MODULE_DOM_UPDATED` notification each time the DOM is re-rendered via `updateDom` (#3534)
- [tests] added minimal needed node version to tests (currently v20.9.0) to avoid releases with wrong node version info
- [tests] Added `node-libgpiod` library to electron-rebuild tests (#3563)
### Removed
- [core] removed installer only files (#3492)
- [core] removed raspberry object from systeminformation (#3505)
- [linter] removed `eslint-plugin-import`, because it doesn't support ESLint v9. We will reenter it later when it does.
- [tests] removed `onoff` library from electron-rebuild tests (#3563)
### Updated
- [weather] Updated `apiVersion` default from 2.5 to 3.0 (#3424)
- [core] Updated dependencies including stylistic-eslint
- [core] nail down `node-ical` version to `0.18.0` with exception `allow-ghsas: GHSA-8hc4-vh64-cxmj` in `dep-review.yaml` (which should removed after next `node-ical` update)
- [core] Updated SocketIO catch all to new API
- [core] Allow custom modules positions by scanning index.html for the defined regions, instead of hard coded (PR #3518 fixes issue #3504)
- [core] Detail optimizations in `config_check.js`
- [core] Updated minimal needed node version in `package.json` (currently v20.9.0) (#3559) and except for v21 (no security updates) (#3561)
- [linter] Switch to ESLint v9 and flat config and replace `eslint-plugin-unicorn` by `@eslint/js`
- [core] Fix discovering module positions twice after #3450
### Fixed
- [docs] Fixed `checks` badge in README.md
- [weather] Fixed issue with the UK Met Office provider following a change in their API paths and header info.
- [core] Add check for node_helper loading for multiple instances of same module (#3502)
- [weather] Fixed issue for respecting unit config on broadcasted notifications
- [tests] Fixes calendar test by moving it from e2e to electron with fixed date (#3532)
- [calendar] fixed sliceMultiDayEvents getting wrong count and displaying incorrect entries, Europe/Berlin (#3542)
- [tests] ignore `js/positions.js` when linting (this file is created at runtime)
- [calendar] fixed sliceMultiDayEvents showing previous day without config enabled
## [2.28.0] - 2024-07-01
Thanks to: @btoconnor, @bugsounet, @JasonStieber, @khassel, @kleinmantara and @WallysWellies.
> ⚠️ This release needs nodejs version >= v20.9.0
### Added
- [calendar] Added config option "showEndsOnlyWithDuration" for default calendar
- [compliments] Added `specialDayUnique` config option, defaults to `false` (#3465)
- [weather] Provider weathergov: Use `precipitationLast3Hours` if `precipitationLastHour` is `null` (#3124)
### Removed
- [tests] delete node v18 support (#3462)
### Updated
- [core] Update dependencies including electron to v31
- [core] use node >= v20 (#3462)
- [core] Update `config.js.sample` to use openmeteo as weather provider which needs no api key
- [tests] Use latest@version of node for `automated-tests.yaml` (#3483)
- [updatenotification] Avoid using pm2 when running in docker container
### Fixed
- [core] Fixed crash possibility if `module: <name>` is not defined and on `position: <position>` mistake (#3445)
- [weather] Fixed precipitationProbability in forecast for provider openmeteo (#3446)
- [weather] Fixed type=daily for provider openmeteo having no data when running after 23:00 (#3449)
- [weather] Fixed type=daily for provider openmeteo showing nightly icons in forecast when current time is "nightly" (#3458)
- [weather] Fixed forecast and hourly weather for provider openmeteo to use real temperatures, not apparent temperatures (#3466)
- [tests] Fixed e2e tests running in docker container which needs `address: "0.0.0.0"` (#3479)
## [2.27.0] - 2024-04-01
Thanks to: @bugsounet, @crazyscot, @illimarkangur, @jkriegshauser, @khassel, @KristjanESPERANTO, @Paranoid93, @rejas, @sdetweil and @vppencilsharpener.
This release marks the first release without Michael Teeuw (@michmich). A very special thanks to him for creating MagicMirror and leading the project for so many years.
For more info, please read the following post: [A New Chapter for MagicMirror: The Community Takes the Lead](https://forum.magicmirror.builders/topic/18329/a-new-chapter-for-magicmirror-the-community-takes-the-lead).
### Added
- Output of system information to the console for troubleshooting (#3328 and #3337), ignore errors under aarch64 (#3349)
- [linter] Add `eslint-plugin-package-json` to lint the `package.json` files (#3368)
- [weather] `showHumidity` config is now a string describing where to show this element. Supported values: "wind", "temp", "feelslike", "below", "none". (#3330)
- electron-rebuild test suite for electron and 3rd party modules compatibility (#3392)
- Create MM² icon and attach it to electron process (#3407)
### Updated
- [updatenotification] Recode update_helper.js with pm2 library (#3332)
- Removing lodash dependency by replacing merge by spread operator (#3339)
- Use node prefix for build-in modules (#3340)
- Rework logging colors (#3350)
- Update pm2 to v5.3.1 with no allow-ghsas (#3364)
- [core] Update husky and let lint-staged fix ESLint issues
- [core] Update dependencies including electron to v29 (#3357) and node-ical
- Update translations for estonian (#3371)
- Update electron to v29 and update other dependencies
- [calendar] fullDay events over several days now show the left days from the first day on and 'today' on the last day
- [weather] Update layout of current weather indoor values
### Fixed
- [weather] Correct apiBase of weathergov weatherProvider to match documentation (#2926)
- Worked around several issues in the RRULE library that were causing deleted calendar events to still show, some
initial and recurring events to not show, and some event times to be off an hour. (#3291)
- Skip changelog requirement when running tests for dependency updates (#3320)
- Display precipitation probability when it is 0% instead of blank/empty (#3345)
- [newsfeed] Suppress unsightly animation cases when there are 0 or 1 active news items (#3336)
- [newsfeed] Always compute the feed item URL using the same helper function (#3336)
- Ignore all custom css files (#3359)
- [newsfeed] Fix newsfeed stall issue introduced by #3336 (#3361)
- Changed `log.debug` to `log.log` in `app.js` where logLevel is not set because config is not loaded at this time (#3353)
- [calendar] deny fetch interval < 60000 and set 60000 in this case (prevent fetch loop failed) (#3382)
- added message in case where config.js is missing the module.export line PR #3383
- Fixed an issue where recurring events could extend past their recurrence end date (#3393)
- Don't display any `npm WARN <....>` on install (#3399)
- [core] Moved suncalc dependency to production from dev, as it is used by clock module
- [compliments] Fix mirror not responding anymore when no compliments are to be shown (#3385)
- [core] Fixed mastermerge workflow (#3415)
### Deleted
- Unneeded file headers (#3358)
- Removed codecov.yaml
## [2.26.0] - 2024-01-01
Thanks to: @bnitkin, @bugsounet, @dependabot, @jkriegshauser, @kaennchenstruggle, @KristjanESPERANTO and @Ybbet.
Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not all) of the work on this release as project collaborators. This version would not be there without their effort. Thank you guys! You are awesome!
This release also marks the latest release by Michael Teeuw. For more info, please read the following post: [A New Chapter for MagicMirror: The Community Takes the Lead](https://forum.magicmirror.builders/topic/18329/a-new-chapter-for-magicmirror-the-community-takes-the-lead).
### Added
- Added update notification updater (for 3rd party modules)
- Added node 21 to the test matrix
- Added transform object to calendar:customEvents
- Added ESLint rules for jest (including jest/expect-expect and jest/no-done-callback)
### Removed
- Removed Codecov workflow (not working anymore, other workflow required) (#3107)
- Removed titleReplace from calendar, replaced + extended by customEvents (backward compatibility included) (#3249)
- Removed failing unit test (#3254)
- Removed some unused variables
### Updated
- Update electron to v27 and update other dependencies as well as github actions
- Update newsfeed: Use `html-to-text` instead of regex for transform description
- Review ESLint config (#3269)
- Update dependencies
- Clock module: optionally display current moon phase in addition to rise/set times
- electron is now per default started without gpu, if needed it must be enabled with new env var `ELECTRON_ENABLE_GPU=1` on startup (#3226)
- Replace prettier by stylistic in ESLint config to lint JavaScript (and disable some rules for `config/config.js*` files)
- Update node-ical to v0.17.1 and fix tests
### Fixed
- Avoid fade out/in on updateDom when many calendars are used
- Fix the option eventClass on customEvents.
- Fix yr API version in locationforecast and sunrise call (#3227)
- Fix cloneObject() function to respect RegExp (#3237)
- Fix newsfeed module for feeds using "a10:updated" tag (#3238)
- Fix issue template (#3167)
- Fix #3256 filter out bad results from rrule.between
- Fix calendar events sometimes not respecting deleted events (#3250)
- Fix electron loadURL locally on Windows when address "0.0.0.0" (#2550)
- Fix updatenotification (update_helper.js): catch error if response is not an JSON format (check PM2)
- Fix missing typeof in calendar module
- Fix style issues after prettier update
- Fix calendar test (#3291) by moving "Exdate check" from e2e to electron to run on a Thursday
- Fix calendar config params `fetchInterval` and `excludedEvents` were never used from single calendar config (#3297)
- Fix MM_PORT variable not used in electron and allow full path for MM_CONFIG_FILE variable (#3302)
## [2.25.0] - 2023-10-01
Thanks to: @bugsounet, @dgoth, @dependabot, @kenzal, @Knapoc, @KristjanESPERANTO, @martingron, @NolanKingdon, @Paranoid93, @TeddyStarinvest and @Ybbet.
Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not all) of the work on this release as project collaborators. This version would not be there without their effort. Thank you guys! You are awesome!
> ⚠️ This release needs nodejs version >= `v18`, older releases have reached end of life and will not work!
### Added
- Added UV Index support to OpenWeatherMap
- Added 'hideDuplicates' flag to the calendar module
- Added `allowOverrideNotification` to weather module to enable sending current weather objects with the `CURRENT_WEATHER_OVERRIDE` notification to supplement/replace the current weather displayed
- Added optional AnimateCSS animate for `hide()`, `show()`, `updateDom()`
- Added AnimateIn and animateOut in module config definition
- Apply AnimateIn rules on the first start
- Added automatic client page reload when server was restarted by setting `reloadAfterServerRestart: true` in `config.js`, per default `false` (#3105)
- Added eventClass option for customEvents on the default calendar
- Added AnimateCSS integration in tests suite (#3206)
- Added npm dependabot [Reserved to developer] (#3210)
- Added improved logging for calendar (#3110)
### Removed
- **Breaking Change**: Removed `digest` authentication method from calendar module (which was already broken since release `2.15.0`)
### Updated
- Update roboto fonts to version v5
- Update issue template
- Update dev/dependencies incl. electron to v26
- Replace pretty-quick by lint-staged (<https://github.com/azz/pretty-quick/issues/164>)
- Update engine node >=18. v16 reached its end of life. (#3170)
- Update typescript definition for modules
- Cleaned up nunjuck templates
- Replace `node-fetch` with internal fetch (#2649) and remove `digest-fetch`
- Update the French translation according to the English file.
- Update dependabot incl. vendor/fonts (monthly check)
- Renew `package-lock.json` for release
### Fixed
- Fix engine check on npm install (#3135)
- Fix undefined formatTime method in clock module (#3143)
- Fix clientonly startup fails after async added (#3151)
- Fix electron width/height when using xrandr under bullseye
- Fix time issue with certain recurring events in calendar module
- Fix ipWhiteList test (#3179)
- Fix newsfeed: Convert HTML entities, codes and tag in description (#3191)
- Respect width/height (no fullscreen) if set in electronOptions (together with `fullscreen: false`) in `config.js` (#3174)
- Fix: AnimateCSS merge hide() and show() animated css class when we do multiple call
- Fix `Uncaught SyntaxError: Identifier 'getCorsUrl' has already been declared (at utils.js:1:1)` when using `clock` and `weather` module (#3204)
- Fix overriding `config.js` when running tests (#3201)
- Fix issue in weathergov provider with probability of precipitation not showing up on hourly or daily forecast
- Fix yr weather provider after changes in yr API (#3189)
## [2.24.0] - 2023-07-01
Thanks to: @angeldeejay, @bugsounet, @buxxi, @CarJem, @dariom, @DaveChild, @dWoolridge, @eddiehung, @grenagit, @Hirschberger, @ismarslomic, @JakeBinney, @KristjanESPERANTO, @MagMar94, @naveensrinivasan, @nfogal, @oscarb, @OWL4C, @psieg, @rajniszp, @retroflex, @SkySails and @tomzt
Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not all) of the work on this release as project collaborators. This version would not be there without their effort. Thank you guys! You are awesome!
### Added
- Added UV Index to hourly and current Weather, with support for Openmeteo
- Added tests for serveronly
- Set Timezone `Europe/Berlin` in unit tests (needed for new formatTime tests)
- [linter] Added no-param-reassign eslint rule and fix warnings
- [updatenotification] Added `sendUpdatesNotifications` feature. Broadcast update with `UPDATES` notification to other modules
- [updatenotification] Allow force scanning with `SCAN_UPDATES` notification from other modules
- Added per-calendar fetchInterval
### Removed
- Removed unneeded (and unwanted) '.' after the year in calendar repeatingCountTitle (#2896, second attempt ...)
### Updated
- [weather] Added support for precipitation probability with openmeteo weather-provider
- Update electron to v25.2 and other dependencies
- Use node v20 in github workflow (replacing v14)
- Refactor formatTime into common util function for default modules
- Refactor some calendar methods into own class and added tests for them
- Split install and run commands in github actions
- Changed `fetchInterval` of calendar in `config.js.sample` to 7 days so we not to request example calendar too frequently
- Changed default calendar fetchInterval to one hour
- Changed calendar url in sample config
### Fixed
- Fix envcanada hourly forecast time (#3080)
- Fix electron not running under windows after async changes (#3083)
- Fix style issues after eslint-plugin-jsdoc update
- Fix don't filter out ongoing full day events (#3095)
- Fix date not shown when clock in analog mode (#3100)
- Fix envcanada today percentage-of-precipitation (#3106)
- Fix updatenotification where no branch is checked out but e.g. a version tag (#3130)
## [2.23.0] - 2023-04-04
Thanks to: @angeldeejay, @buxxi, @CarJem, @dariom, @DaveChild, @dWoolridge, @grenagit, @Hirschberger, @KristjanESPERANTO, @MagMar94, @naveensrinivasan, @nfogal, @psieg, @rajniszp, @retroflex, @SkySails and @tomzt.
Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not all) of the work on this release as project collaborators. This version would not be there without their effort. Thank you guys! You are awesome!
### Added
- Added increments for hourly forecasts in weather module (#2996)
- Added tests for hourly weather forecast
- Added possibility to ignore MagicMirror repo in updatenotification module
- Added Pirate Weather as new weather-provider (#3005)
- Added possibility to use your own templates in Alert module
- Added error message if `<modulename>.js` file is missing in module folder to get a hint in the logs (#2403)
- Added possibility to use environment variables in `config.js` (#1756)
- Added option `pastDaysCount` to default calendar module to control of how many days past events should be displayed
- Added thai language to alert module
- Added option `sendNotifications` in clock module (#3056)
- Added tests for some weather utils
### Removed
- Removed darksky weather-provider
- Removed unneeded (and unwanted) '.' after the year in calendar repeatingCountTitle (#2896)
### Updated
- Use develop as target branch for dependabot
- Update issue template, contributing doc and sample config
- The weather modules clearly separates precipitation amount and probability (risk of rain/snow)
- This requires all providers that only supports probability to change the config from `showPrecipitationAmount` to `showPrecipitationProbability`.
- Update tests for weather and calendar module
- Changed updatenotification module for MagicMirror repo only: Send only notifications for `master` if there is a tag on a newer commit
- Update dates in Calendar widgets every minute
- Cleanup jest coverage for patches
- Update `stylelint` dependencies, switch to `stylelint-config-standard` and handle `stylelint` issues, update `main.css` matching new rules
- Update Eslint config, add new rule and handle issue
- Convert lots of callbacks to async/await
- Revise require imports (#3071 and #3072)
- Use `config.js-old` instead of file with timestamp suffix when backing up config with a `config.template` in use (#3104)
### Fixed
- Fix wrong day labels in envcanada forecast (#2987)
- Fix for missing default class name prefix for customEvents in calendar
- Fix electron flashing white screen on startup (#1919)
- Fix weathergov provider hourly forecast (#3008)
- Fix message display with HTML code into alert module (#2828)
- Fix typo in french translation
- Yr wind direction is no longer inverted
- Fix async node_helper stopping electron start (#2487)
- The wind direction arrow now points in the direction the wind is flowing, not into the wind (#3019)
- Fix precipitation css styles and rounding value
- Fix wrong vertical alignment of calendar title column when wrapEvents is true (#3053)
- Fix empty news feed stopping the reload forever
- Fix e2e tests (failed after async changes) by running calendar and newsfeed tests last
- Lint: Use template literals instead of string concatenation
- Fix default alert module to render HTML for title and message
- Fix Open-Meteo wind speed units
## [2.22.0] - 2023-01-01
Thanks to: @angeldeejay, @buxxi, @dariom, @dWoolridge, @KristjanESPERANTO, @MagMar94, @naveensrinivasan, @retroflex, @SkySails and @Tom.
Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not all) of the work on this release as project collaborators. This version would not be there without their effort. Thank you!
### Added
- Added new calendar options for colored entries and improved styling (#3033)
- Added test for remoteFile option in compliments module
- Added hourlyWeather functionality to Weather.gov weather-provider
- Added css class names "today" and "tomorrow" for default calendar
- Added Collaboration.md
- Added new github action for dependency review (#2862)
- Added a WeatherProvider for Open-Meteo
- Added Yr as a weather-provider
- Added config options "ignoreXOriginHeader" and "ignoreContentSecurityPolicy"
- Added thai language
- Added workflow rule to make sure PRs are based against develop
### Removed
- Removed usage of internal fetch function of node until it is more stable
- Removed weatherEndpoint definition from weathergov.js (not used)
### Updated
- Cleaned up test directory (#2937) and jest config (#2959)
- Wait for all modules to start before declaring the system ready (#2487)
- Updated e2e tests (moved `done()` in helper functions) and use es6 syntax in all tests
- Updated da translation
- Rework weather module
- Make sure smhi provider api only gets a maximum of 6 digits coordinates (#2955)
- Use fetch instead of XMLHttpRequest in weather-provider (#2935)
- Reworked how weather-providers handle units (#2849)
- Use unix() method for parsing times, fix suntimes on the way (#2950)
- Refactor conversion functions into utils class (#2958)
- The `cors`-method in `server.js` now supports sending and receiving HTTP headers
- Replace `…` by `…`
- Cleanup compliments module
- Updated dependencies including electron to v22 (#2903)
### Fixed
- Correctly show apparent temperature in SMHI weather-provider
- Ensure updatenotification module isn't shown when local is _ahead_ of remote
- Handle node_helper errors during startup (#2944)
- Possibility to change FontAwesome class in calendar, so icons like `fab fa-facebook-square` works.
- Fix cors problems with newsfeed articles (as far as possible), allow disabling cors per feed with option `useCorsProxy: false` (#2840)
- Tests not waiting for the application to start and stop before starting the next test
- Fix electron tests failing sometimes in github workflow
- Fixed gap in clock module when displayed on the left side with displayType=digital
- Fixed playwright issue by upgrading to v1.29.1 (#2969)
## [2.21.0] - 2022-10-01
Special thanks to: @BKeyport, @buxxi, @davide125, @khassel, @kolbyjack, @krukle, @MikeBishop, @rejas, @sdetweil, @SkySails and @veeck
### Added
- Added possibility to fetch calendars through socket notifications.
- New scripts `install-mm` (and `install-mm:dev`) for simplifying mm installation (now: `npm run install-mm`) and adding params `--no-audit --no-fund --no-update-notifier` for less noise.
- New `showTimeToday` option in calendar module shows time for current-day events even if `timeFormat` is `"relative"`.
- Added hourly forecasts, apparent temperature & custom location name to SMHI weather-provider.
- Added new electron tests for calendar and moved some compliments tests from `e2e` to `electron` because of date mocking, removed mock stuff from compliments module.
### Removed
- Removed old and deprecated weather modules `currentweather` and `weatherforecast`.
- Removed `DAYAFTERTOMORROW` from English.
### Updated
- Updated dependencies.
- Updated jsdoc.
- Updated font tree to use variables consistently.
- Removed deprecated Docker Repository from issue template.
### Fixed
- Broadcast all calendar events while still honoring global and per-calendar maximumEntries.
- Respect rss ttl provided by newsfeed (#2883).
- Fix multi day calendar events always presented as "(1/X)" instead of the amount of days the event has progressed.
- Fix weatherbit provider to use type config value instead of endpoint.
- Fix calendar events which DO NOT specify rrule byday adjusted incorrectly (#2885).
- Fix e2e tests not failing on errors (#2911).
## [2.20.0] - 2022-07-02
Special thanks to the following contributors: @eouia, @khassel, @kolbyjack, @KristjanESPERANTO, @nathannaveen, @naveensrinivasan, @rejas, @rohitdharavath and @sdetweil.
### Added
- Added a new config option `httpHeaders` used by helmet (see <https://helmetjs.github.io/>). You can now set own httpHeaders which will override the defaults in `js/defaults.js` which is useful e.g. if you want to embed MagicMirror into another website (solves #2847).
- Show endDate for calendar events when dateHeader is enabled and showEnd is set to true (#2192).
- Added the notification emitting from the weather module on information updated.
- Use recommended file extension for YAML files (#2864).
### Updated
- Use latest node 18 when running tests on github actions.
- Updated `electron` to v19 and other dependencies.
- Use internal fetch function of node instead external `node-fetch` library if used node version >= `v18`.
- Include duplicate events in broadcasts.
### Fixed
- Fix problems with non latin fonds caused by updating to fontsource (fixes #2835).
## [2.19.0] - 2022-04-01
Special thanks to the following contributors: @10bias, @CFenner, @JHWelch, @k1rd3rf, @khassel, @kolbyjack, @krekos, @KristjanESPERANTO, @Nerfzooka, @oraclesean, @oscarb, @philnagel, @rejas, @sdetweil, @shin10, @SiderealArt and @Tom-Hirschberger.
### Added
- Added a config option under the weather module, `absoluteDates`, providing an option to format weather forecast date output with either absolute or relative dates.
- Added test for new weather forecast `absoluteDates` property.
- The modules get a class hidden added/removed if they get hidden/shown which will also toggle pointer-events.
- Added new config option `showTitleAsUrl` to newsfeed module. If set, the displayed title is a link to the article which is useful when running in a browser and you want to read this article.
- Added internal cors proxy to get weather-providers working without public proxies (fixes #2714). The new url `http(s)://address:port/cors?url=https://whatever-to-proxy` can be used in other modules too.
- Added a WeatherProvider for Weatherflow.
- Added new env var `ELECTRON_DISABLE_GPU` which disable gpu under electron if set (fixes #2831).
- Added missing Czech translations.
### Updated
- Deprecated roboto fonts package `roboto-fontface-bower` replaced with `fontsource`.
- Updated `electron` to v17, `helmet` to v5 (use defaults of v4) and other dependencies
- Updated Font Awesome css class to new default style (fixes #2768)
- Replaced deprecated modules `currentweather` and `weatherforecast` with dummy modules only displaying that they have to be replaced.
- Include all calendar events from the configured date range when broadcasting.
- Updated Danish and German translation.
- Updated `node-ical` to v0.15 and added `luxon` as dependency for not breaking the "no-optional" install (see #2718 and #2824).
### Fixed
- Improved and speedup e2e tests, artificial wait after mm start removed.
- Improved husky setup not blocking `git commit` if `husky` or `npm` is not installed.
- Using a consistent spelling of MagicMirror².
- Fix minor console output issue for loading translations (#2814).
- Don't adjust startDate for full day events if endDate is in the past.
- Fix windspeed conversion error in openweathermap provider. (#2812)
- Fix conflicting parameter turning off showEnd for full day events. (#2629)
- Fix regression, calendar.maximumEntries not used to filter calendar level entries (#2868)
## [2.18.0] - 2022-01-01
Special thanks to the following contributors: @AmpioRosso, @eouia, @fewieden, @jupadin, @khassel, @kolbyjack, @KristjanESPERANTO, @MariusVaice, @rejas, @rico24 and @sdetweil.
### Added
- Added test for calendar recurring event with checks the correct date displayed (related to #2752).
### Updated
- ESLint version supports now ECMAScript 2018.
- Cleaned up `updatenotification` module and switched to nunjuck template.
- Moved calendar tests from category `electron` to `e2e`.
- Updated missed translations for Korean language (ko.json).
- Updated missed translations for Dutch language (nl.json).
- Cleaned up `alert` module and switched to nunjuck template.
- Moved weather tests from category `electron` to `e2e`.
- Updated github actions.
- Replace spectron with playwright, update dependencies including electron update to v16.
- Added lithuanian language to translations.js.
- Show info message if newsfeed is empty (fixes #2731).
- Added dangerouslyDisableAutoEscaping config option for newsfeed templates (fixes #2712).
- Added missing shebang to `installers/mm.sh`.
- Node versions in templates and github workflows.
- Updated translations for Traditional Chinese (Taiwan) (zh-tw.json).
### Fixed
- Fixed wrong file `kr.json` to `ko.json`. Use language code 'ko' instead of 'kr' for Korean language.
- [weather] Fixed `feels_like` data from openweathermap's current weather being ignored (#2678).
- Fixed chaotic newsfeed display after network connection loss thanks to @jalibu (#2638).
- Fixed incorrect time zone correction of recurring full day events (#2632 and #2634).
- Fixed e2e tests by increasing testTimeout.
- Revert node-ical update due to missing luxon package.
- Fixed User-Agent-Header for newsfeed and calendar module (#2729).
- Replace broken shields in Readme and use https for links.
- Fixed electron tests with retry.
- Fixed Calendar recurring cross timezone error (add/subtract a day, not just offset hours) (#2632).
- Fixed Calendar showEnd and Full Date overlay (#2629).
- Fixed regression on #2632, #2752.
- Broadcast custom symbols in CALENDAR_EVENTS.
## [2.17.1] - 2021-10-01
### Fixed
- Fixed error when accessing letsencrypt certificates
- Fixed Calendar module enhancement: displaying full events without time (#2424)
## [2.17.0] - 2021-10-01
Special thanks to the following contributors: @apiontek, @eouia, @jupadin, @khassel and @rejas.
### Added
- Added showTime parameter to clock module for enabling/disabling time display in analog clock.
- Added custom electron switches from user config (`config.electronSwitches`).
- Added unit tests for updatenotification module.
### Updated
- Bump electron to v13 (and spectron to v15) and update other dependencies in package.json.
- Refactor test configs, use default test config for all tests.
- Updated github templates.
- Actually test all js and css files when lint script is run.
- Updated jsdocs and print warnings during testing too.
- Updated weathergov provider to try fetching not just current, but also forecast, when API URLs available.
- [clock] Refactored clock layout.
- Refactored methods from weather-providers into weatherobject (isDaytime, updateSunTime).
- Use of `logger.js` in jest tests.
- Run prettier over all relevant files.
- Move tests needing electron in new category `electron`, use `server only` mode in `e2e` tests.
- Updated dependencies in package.json.
### Fixed
- Fix undefined error with ignoreToday option in weather module (#2620).
- Fix time zone correction in calendar module when the date hour is equal to the time zone correction value (#2632).
- Fix black cursor on startup when using electron.
- Fix update notification not working for own repository (#2644).
## [2.16.0] - 2021-07-01
Special thanks to the following contributors: @210954, @B1gG, @codac, @Crazylegstoo, @daniel, @earlman, @ezeholz, @FrancoisRmn, @jupadin, @khassel, @KristjanESPERANTO, @njwilliams, @oemel09, @r3wald, @rejas, @rico24, Faizan Ahmed.
### Added
- Added French translations for "MODULE_CONFIG_ERROR" and "PRECIP".
- Added German translation for "PRECIP".
- Added Dutch translation for "WEEK", "PRECIP", "MODULE_CONFIG_CHANGED" and "MODULE_CONFIG_ERROR".
- Added first test for Alert module.
- Added support for `dateFormat` when not using `timeFormat: "absolute"`.
- Added custom-properties for colors and fonts for improved styling experience, see `custom.css.sample` file.
- Added custom-properties for gaps around body and between modules.
- Added test case for recurring calendar events.
- Added new Environment Canada provider for default WEATHER module (weather data for Canadian locations only).
- Added list view for newsfeed module.
- Added dev dependency jest, switching from mocha to jest.
### Updated
- Bump node-ical to v0.13.0 (now last runtime dependency using deprecated `request` package is removed).
- Use codecov in informational mode.
- Refactor code into es6 where possible (e.g. var -> let/const).
- Use node v16 in github workflow (replacing node v10).
- Moved some files into better suited directories.
- Updated dependencies in package.json, require node >= v12, remove `rrule-alt` and `rrule`.
- Updated dependencies in package.json and migrate husky to v6, fix husky setup in prod environment.
- Cleaned up error handling in newsfeed and calendar modules for real.
- Updated default WEATHER module such that a provider can optionally set a custom unit-of-measure for precipitation (`weatherObject.precipitationUnits`).
- Updated documentation.
- Updated jest tests: Reset changes on js/logger.js, mock logger.js in global_vars tests.
- Updated dependencies in package.json.
### Removed
- Switching from mocha to jest so removed following dev dependencies: chai, chai-as-promised, mocha, mocha-each, mocha-logger.
### Fixed
- Fix calendar start function logging inconsistency.
- Fix updatenotification start function logging inconsistency.
- Checks and applies the showDescription setting for the newsfeed module again.
- Fix issue with openweathermap not showing current or forecast info when using onecall API.
- Fix tests in weather module and add one for decimalPoint in forecast.
- Fix decimalSymbol in the forecast part of the new weather module (#2530).
- Fix wrong treatment of `appendLocationNameToHeader` when using `ukmetofficedatahub`.
- Fix alert not recognizing multiple alerts (#2522).
- Fix fetch option httpsAgent to agent in calendar module (#466).
- Fix module updatenotification which did not work for repos with many refs (#1907).
- Fix config check failing when encountering let syntax ("Parsing error: Unexpected token config").
- Fix calendar debug check.
- Really run prettier over all files.
- Fix logger.js after jest changes, use --forceExit running jest.
- Workaround for dev_console test using getWindowCount.
## [2.15.0] - 2021-04-01
Special thanks to the following contributors: @EdgardosReis, @MystaraTheGreat, @TheDuffman85, @ashishtank, @buxxi, @codac, @fewieden, @khassel, @klaernie, @qu1que, @rejas, @sdetweil & @thomasrockhu.
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`.
### Added
- Added Galician language.
- Added GitHub workflows for automated testing and changelog enforcement.
- Added CodeCov badge to Readme.
- Added CURRENTWEATHER_TYPE notification to currentweather and weather module, use it in compliments module.
- Added `start:dev` command to the npm scripts for starting electron with devTools open.
- Added logging when using deprecated modules weatherforecast or currentweather.
- Added Portuguese translations for "MODULE_CONFIG_CHANGED" and "PRECIP".
- Respect parameter ColoredSymbolOnly also for custom events.
- Added a new parameter to hide time portion on relative times.
- `module.show` has now the option for a callback on error.
- Added locale to sample config file.
- Added support for self-signed certificates for the default calendar module (#466).
- Added hiddenOnStartup flag to module config (#2475).
### Updated
- Updated markdown files for github.
- Cleaned up old code on server side.
- Convert `-0` to `0` when displaying temperature.
- Code cleanup for FEELS like and added {DEGREE} placeholder for FEELSLIKE for each language.
- Converted newsfeed module to use templates.
- Updated documentation and help screen about invalid config files.
- Moving weather-provider specific code and configuration into each provider and making hourly part of the interface.
- Bump electron to v11 and enable contextIsolation.
- Don't update the DOM when a module is not displayed.
- Cleaned up jsdoc and tests.
- Exposed logger as node module for easier access for 3rd party modules.
- Replaced deprecated `request` package with `node-fetch` and `digest-fetch`.
- Refactored calendar fetcher.
- Cleaned up newsfeed module.
- Cleaned up translations and translator code.
### Removed
- Removed danger.js library.
- Removed `ical` which was substituted by `node-ical` in release `v2.13.0`. Module developers must install this dependency themselves in the module folder if needed.
- Removed valid-url library.
### Fixed
- Added default log levels to stop calendar log spamming.
- Fix socket.io cors errors, see [breaking change since socket.io v3](https://socket.io/docs/v3/handling-cors/).
- Fix Issue with weather forecast icons due to fixed day start and end time (#2221).
- Fix empty directory for each module's main javascript file in the inspector.
- Fix Issue with weather forecast icons unit tests with different timezones (#2221).
- Fix issue with unencoded characters in translated strings when using nunjuck template (`Loading …` as an example).
- Fix socket.io backward compatibility with socket v2 clients.
- Fix 3rd party module language loading if language is English.
- Fix e2e tests after spectron update.
- Fix updatenotification creating zombie processes by setting a timeout for the git process.
- Fix weather module openweathermap not loading if lat and lon set without onecall.
- Fix calendar daylight savings offset calculation if recurring start date before 2007.
- Fix calendar time/date adjustment when time with GMT offset is different day (#2488).
- Fix calendar daylight savings offset calculation if recurring FULL DAY start date before 2007 (#2483).
- Fix newsreaders template, for wrong test for nowrap in 2 places (should be if not).
## [2.14.0] - 2021-01-01
Special thanks to the following contributors: @Alvinger, @AndyPoms, @ashishtank, @bluemanos, @flopp999, @jakemulley, @jakobsarwary1, @marvai-vgtu, @mirontoli, @rejas, @sdetweil, @Snille & @Sub028.
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`.
### Added
- Added new log level "debug" to the logger.
- Added new parameter "useKmh" to weather module for displaying wind speed as kmh.
- Added Chuvash translation.
- Added Weatherbit as a provider to Weather module.
- Added SMHI as a provider to Weather module.
- Added Hindi & Gujarati translation.
- Added optional support for DEGREE position in Feels like translation.
- Added support for variables in nunjucks templates for translate filter.
- Added Chuvash translation.
- Added new option "limitDays" - limit the number of discreet days displayed.
- Added new option "customEvents" - use custom symbol/color based on keyword in event title.
### Updated
- Merging .gitignore in the config-folder with the .gitignore in the root-folder.
- Weather module - forecast now show TODAY and TOMORROW instead of weekday, to make it easier to understand.
- Updated dependencies to latest versions.
- Updated dependencies eslint, feedme, simple-git and socket.io to latest versions.
- Updated lithuanian translation.
- Updated config sample.
- Highlight required version mismatch.
- No select Text for TouchScreen use.
- Corrected logic for timeFormat "relative" and "absolute".
- Added missing function call in module.show()
- Translator variables can have falsy values (e.g. empty string)
- Fix issue with weather module with DEGREE label in FEELS like
### Deleted
- Removed Travis CI integration.
### Fixed
- JSON Parse translation files with comments crashing UI. (#2149)
- Calendar parsing where RRULE bug returns wrong date, add Windows timezone name support. (#2145, #2151)
- Wrong node-ical version installed (package.json) requested version. (#2153)
- Fix calendar fetcher subsequent timing. (#2160)
- Rename Greek translation to correct ISO 639-1 alpha-2 code (gr > el). (#2155)
- Add a space after icons of sunrise and sunset. (#2169)
- Fix calendar when no DTEND record found in event, startDate overlay when endDate set. (#2177)
- Fix windspeed conversion error in ukmetoffice weather-provider. (#2189)
- Fix console.debug not having timestamps. (#2199)
- Fix calendar full day event east of UTC start time. (#2200)
- Fix non-fullday recurring rule processing. (#2216)
- Catch errors when parsing calendar data with ical. (#2022)
- Fix Default Alert Module does not hide black overlay when alert is dismissed manually. (#2228)
- Weather module - Always displays night icons when local is other than English. (#2221)
- Updated node-ical 0.12.4, fix invalid RRULE format in cal entries
- Fix package.json for optional electron dependency (2378)
- Updated node-ical version again, 0.12.5, change RRULE fix (#2371, #2379)
- Remove undefined objects from modules array (#2382)
- Updated node-ical version again, 0.12.7, change RRULE fix (#2371, #2379), node-ical now throws error (which we catch)
- Updated simple-git version to 2.31 unhandled promise rejection (#2383)
## [2.13.0] - 2020-10-01
Special thanks to the following contributors: @bryanzzhu, @bugsounet, @chamakura, @cjbrunner, @easyas314, @larryare, @oemel09, @rejas, @sdetweil & @sthuber90.
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`.
### Added
- `--dry-run` Added option in fetch call within updatenotification node_helper. This is to prevent
MagicMirror² from consuming any fetch result. Causes conflict with MMPM when attempting to check
for updates to MagicMirror² and/or MagicMirror² modules.
- Test coverage with Istanbul, run it with `npm run test:coverage`.
- Added lithuanian language.
- Added support in weatherforecast for OpenWeather onecall API.
- Added config option to calendar-icons for recurring- and fullday-events.
- Added current, hourly (max 48), and daily (max 7) weather forecasts to weather module via OpenWeatherMap One Call API.
- Added eslint-plugin for jsdoc comments.
- Added new configDeepMerge option for module developers.
### Updated
- Change incorrect weather.js default properties.
- Cleaned up newsfeed module.
- Cleaned up jsdoc comments.
- Cleaned up clock tests.
- Move lodash into devDependencies, update other dependencies.
- Switch from ical to node-ical library.
### Fixed
- Fix backward compatibility issues for Safari < 11.
- Fix the use of "maxNumberOfDays" in the module "weatherforecast depending on the endpoint (forecast/daily or forecast)". [#2018](https://github.com/MagicMirrorOrg/MagicMirror/issues/2018)
- Fix calendar display. Account for current timezone. [#2068](https://github.com/MagicMirrorOrg/MagicMirror/issues/2068)
- Fix logLevel being set before loading config.
- Fix incorrect namespace links in svg clockfaces. [#2072](https://github.com/MagicMirrorOrg/MagicMirror/issues/2072)
- Fix weather/providers/weathergov for API guidelines. [#2045](https://github.com/MagicMirrorOrg/MagicMirror/issues/2045)
- Fix "undefined" in weather modules header. [#1985](https://github.com/MagicMirrorOrg/MagicMirror/issues/1985)
- Fix #2110, #2111, #2118: Recurring full day events should not use timezone adjustment. Just compare month/day.
## [2.12.0] - 2020-07-01
Special thanks to the following contributors: @AndreKoepke, @andrezibaia, @bryanzzhu, @chamakura, @DarthBrento, @Ekristoffe, @khassel, @Legion2, @ndom91, @radokristof, @rejas, @XBCreepinJesus & @ZoneMR.
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`.
### Added
- Added option to config the level of logging.
- Added prettier for an even cleaner codebase.
- Hide Sunrise/Sunset in Weather module.
- Hide Sunrise/Sunset in Current Weather module.
- Added Met Office DataHub (UK) provider.
### Updated
- Cleaned up alert module code.
- Cleaned up check_config code.
- Replaced grunt-based linters with their non-grunt equivalents.
- Switch to most of the eslint:recommended rules and fix warnings.
- Replaced insecure links with https ones.
- Cleaned up all "no-undef" warnings from eslint.
- Added location title wrapping for calendar module.
- Updated the BG translation.
### Deleted
- Removed truetype (ttf) fonts.
### Fixed
- The broken modules due to Socket.io change from last release. [#1973](https://github.com/MagicMirrorOrg/MagicMirror/issues/1973)
- Add backward compatibility for old module code in socketclient.js. [#1973](https://github.com/MagicMirrorOrg/MagicMirror/issues/1973)
- Support multiple instances of calendar module with different config. [#1109](https://github.com/MagicMirrorOrg/MagicMirror/issues/1109)
- Fix the use of "maxNumberOfDays" in the module "weatherforecast". [#2018](https://github.com/MagicMirrorOrg/MagicMirror/issues/2018)
- Throw error when check_config fails. [#1928](https://github.com/MagicMirrorOrg/MagicMirror/issues/1928)
- Bug fix related to 'maxEntries' not displaying Calendar events. [#2050](https://github.com/MagicMirrorOrg/MagicMirror/issues/2050)
- Updated ical library to the latest version. [#1926](https://github.com/MagicMirrorOrg/MagicMirror/issues/1926)
- Fix config check after merge of prettier [#2109](https://github.com/MagicMirrorOrg/MagicMirror/issues/2109)
## [2.11.0] - 2020-04-01
🚨 READ THIS BEFORE UPDATING 🚨
In the past years the project has grown a lot. This came with a huge downside: poor maintainability. If I let the project continue the way it was, it would eventually crash and burn. More important: I would completely lose the drive and interest to continue the project. Because of this the decision was made to simplify the core by removing all side features like automatic installers and support for exotic platforms. This release (2.11.0) is the first real release that will reflect (parts) of these changes. As a result of this, some things might break. So before you continue make sure to backup your installation. Your config, your modules or better yet: your full MagicMirror² folder. In other words: update at your own risk.
For more information regarding this major change, please check issue [#1860](https://github.com/MagicMirrorOrg/MagicMirror/issues/1860).
### Deleted
- Remove installers.
- Remove externalized scripts.
- Remove jshint dependency, instead eslint checks your config file now
### Added
- Brazilian translation for "FEELS".
- Ukrainian translation.
- Finnish translation for "PRECIP", "UPDATE_INFO_MULTIPLE" and "UPDATE_INFO_SINGLE".
- Added the ability to hide the temp label and weather icon in the `currentweather` module to allow showing only information such as wind and sunset/rise.
- The `clock` module now optionally displays sun and moon data, including rise/set times, remaining daylight, and percent of moon illumination.
- Added Hebrew translation.
- Add HTTPS support and update config.js.sample
- Run tests on long term support and latest stable version of nodejs
- Added the ability to configure a list of modules that shouldn't be update checked.
- Run linters on git commits
- Added date functionality to compliments: display birthday wishes or celebrate an anniversary
- Add HTTPS support for clientonly-mode.
### Fixed
- Force declaration of public ip address in config file (ISSUE #1852)
- Fixes `run-start.sh`: If running in docker-container, don't check the environment, just start electron (ISSUE #1859)
- Fix calendar time offset for recurring events crossing Daylight Savings Time (ISSUE #1798)
- Fix regression in currentweather module causing 'undefined' to show up when config.hideTemp is false
- Fix FEELS translation for Croatian
- Fixed weather tests [#1840](https://github.com/MagicMirrorOrg/MagicMirror/issues/1840)
- Fixed Socket.io can't be used with Reverse Proxy in serveronly mode [#1934](https://github.com/MagicMirrorOrg/MagicMirror/issues/1934)
- Fix update checking skipping 3rd party modules the first time
### Changed
- Remove documentation from core repository and link to new dedicated docs site: [docs.magicmirror.builders](https://docs.magicmirror.builders).
- Updated config.js.sample: Corrected some grammar on `config.js.sample` comment section.
- Removed `run-start.sh` script and update start commands:
- To start using electron, use `npm run start`.
- To start in server only mode, use `npm run server`.
- Remove redundant logging from modules.
- Timestamp in log output now also contains the date
- Turkish translation.
- Option to configure the size of the currentweather module.
- Changed "Gevoelstemperatuur" to "Voelt als" shorter text.
## [2.10.1] - 2020-01-10
### Changed
- Updated README.md: Added links to the official documentation website and remove links to broken installer.
## [2.10.0] - 2020-01-01
Special thanks to @sdetweil for all his great contributions!
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`.
### Added
- Timestamps in log output.
- Padding in dateheader mode of the calendar module.
- New upgrade script to help users consume regular updates installers/upgrade-script.sh.
- New script to help setup pm2, without install installers/fixuppm2.sh.
### Updated
- Updated lower bound of `lodash` and `helmet` dependencies for security patches.
- Updated compliments.js to handle newline in text, as text fields to not interpolate contents.
- Updated raspberry.sh installer script to handle new platform issues, split node/npm, pm2, and screen saver changes.
- Improve handling for armv6l devices, where electron support has gone away, add optional serveronly config option.
- Improved run-start.sh to handle for serveronly mode, by choice, or when electron not available.
- Only check for xwindows running if not on macOS.
### Fixed
- Fixed issue in weatherforecast module where predicted amount of rain was not using the decimal symbol specified in config.js.
- Module header now updates correctly, if a module need to dynamically show/hide its header based on a condition.
- Fix handling of config.js for serverOnly mode commented out.
- Fixed issue in calendar module where the debug script didn't work correctly with authentication.
- Fixed issue that some full day events were not correctly recognized as such.
- Display full day events lasting multiple days as happening today instead of some days ago if they are still ongoing.
## [2.9.0] - 2019-10-01
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`. If you are having issues running Electron, make sure your [Raspbian is up to date](https://www.raspberrypi.org/documentation/raspbian/updating.md).
### Added
- Spanish translation for "PRECIP".
- Adding a Malay (Malaysian) translation for MagicMirror².
- Add test check URLs of vendors 200 and 404 HTTP CODE.
- Add tests for new weather module and helper to stub ajax requests.
### Updated
- Updatenotification module: Display update notification for a limited (configurable) time.
- Enabled e2e/vendor_spec.js tests.
- The css/custom.css will be renamed after the next release. We've added into `run-start.sh` an instruction by GIT to ignore with `--skip-worktree` and `rm --cached`. [#1540](https://github.com/MagicMirrorOrg/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/MagicMirrorOrg/MagicMirror/issues/1722)
- Fixed weatherforecast module not displaying rain amount on fallback endpoint.
- Notifications CLOCK_SECOND & CLOCK_MINUTE being from startup instead of matched against the clock and avoid drifting.
## [2.8.0] - 2019-07-01
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`. If you are having issues running Electron, make sure your [Raspbian is up to date](https://www.raspberrypi.org/documentation/raspbian/updating.md).
### Added
- Option to show event location in calendar
- Finnish translation for "Feels" and "Weeks"
- Russian translation for “Feels”
- Calendar module: added `nextDaysRelative` config option
- Add `broadcastPastEvents` config option for calendars to include events from the past `maximumNumberOfDays` in event broadcasts
- Added feature to broadcast news feed items `NEWS_FEED` and updated news items `NEWS_FEED_UPDATED` in default [newsfeed](https://github.com/MagicMirrorOrg/MagicMirror/tree/develop/modules/default/newsfeed) module (when news is updated) with documented default and `config.js` options in [README.md](https://github.com/MagicMirrorOrg/MagicMirror/blob/develop/modules/default/newsfeed/README.md)
- Added notifications to default `clock` module broadcasting `CLOCK_SECOND` and `CLOCK_MINUTE` for the respective time elapsed.
- Added UK Met Office Datapoint feed as a provider in the default weather module.
- Added new provider class
- Added suncalc.js dependency to calculate sun times (not provided in UK Met Office feed)
- Added "tempUnits" and "windUnits" to allow, for example, temp in metric (i.e. celsius) and wind in imperial (i.e. mph). These will override "units" if specified, otherwise the "units" value will be used.
- Use Feels Like temp from feed if present
- Optionally display probability of precipitation (PoP) in current weather (UK Met Office data)
- Automatically try to fix eslint errors by passing `--fix` option to it
- Added sunrise and sunset times to weathergov weather-provider [#1705](https://github.com/MagicMirrorOrg/MagicMirror/issues/1705)
- Added "useLocationAsHeader" to display "location" in `config.js` as header when location name is not returned
- Added to `newsfeed.js`: in order to design the news article better with css, three more class-names were introduced: newsfeed-desc, newsfeed-desc, newsfeed-desc
### Updated
- English translation for "Feels" to "Feels like"
- Fixed the example calendar url in `config.js.sample`
- Updated `ical.js` to solve various calendar issues.
- Updated weather city list url [#1676](https://github.com/MagicMirrorOrg/MagicMirror/issues/1676)
- Only update clock once per minute when seconds aren't shown
- Updated weather-provider documentation.
### Fixed
- Fixed uncaught exception, race condition on module update
- Fixed issue [#1696](https://github.com/MagicMirrorOrg/MagicMirror/issues/1696), some ical files start date to not parse to date type
- Allowance HTML5 autoplay-policy (policy is changed from Chrome 66 updates)
- Handle SIGTERM messages
- Fixes sliceMultiDayEvents so it respects maximumNumberOfDays
- Minor types in default NewsFeed [README.md](https://github.com/MagicMirrorOrg/MagicMirror/blob/develop/modules/default/newsfeed/README.md)
- Fix typos and small syntax errors, cleanup dependencies, remove multiple-empty-lines, add semi-rule
- Fixed issues with calendar not displaying one-time changes to repeating events
- Updated the fetchedLocationName variable in currentweather.js so that city shows up in the header
### Updated installer
- give non-pi2+ users (pi0, odroid, jetson nano, mac, windows, ...) option to continue install
- use current username vs hardcoded 'pi' to support non-pi install
- check for npm installed. node install doesn't do npm anymore
- check for mac as part of PM2 install, add install option string
- Updated pm2 config with current username instead of hard coded 'pi'
- check for screen saver config, "/etc/xdg/lxsession", bypass if not setup
## [2.7.1] - 2019-04-02
Fixed `package.json` version number.
## [2.7.0] - 2019-04-01
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`. If you are having issues running Electron, make sure your [Raspbian is up to date](https://www.raspberrypi.org/documentation/raspbian/updating.md).
### Added
- Italian translation for "Feels"
- Basic Klingon (tlhIngan Hol) translations
- Disabled the screensaver on raspbian with installation script
- Added option to truncate the number of vertical lines a calendar item can span if `wrapEvents` is enabled.
- Danish translation for "Feels" and "Weeks"
- Added option to split multiple day events in calendar to separate numbered events
- Slovakian translation
- Alerts now can contain Font Awesome icons
- Notifications display time can be set in request
- Newsfeed: added support for `ARTICLE_INFO_REQUEST` notification
- Add `name` config option for calendars to be sent along with event broadcasts
### Updated
- Bumped the Electron dependency to v3.0.13 to support the most recent Raspbian. [#1500](https://github.com/MagicMirrorOrg/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/MagicMirrorOrg/MagicMirror/issues/1503), [#1511](https://github.com/MagicMirrorOrg/MagicMirror/issues/1511).
- Fixed unhandled error on bad git data in updatenotification module [#1285](https://github.com/MagicMirrorOrg/MagicMirror/issues/1285).
- Weather forecast now works with openweathermap in new weather module. Daily data are displayed, see issue [#1504](https://github.com/MagicMirrorOrg/MagicMirror/issues/1504).
- Fixed analogue clock border display issue where non-black backgrounds used (previous fix for issue 611)
- Fixed compatibility issues caused when modules request different versions of Font Awesome, see issue [#1522](https://github.com/MagicMirrorOrg/MagicMirror/issues/1522). MagicMirror² now uses [Font Awesome 5 with v4 shims included for backwards compatibility](https://fontawesome.com/how-to-use/on-the-web/setup/upgrading-from-version-4#shims).
- Installation script problems with raspbian
- Calendar: only show repeating count if the event is actually repeating [#1534](https://github.com/MagicMirrorOrg/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/MagicMirrorOrg/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/MagicMirrorOrg/MagicMirror/issues/1589)
- Notification: fixed background color (was white text on white background)
- Use getHeader instead of data.header when creating the DOM so overwriting the function also propagates into it
- Fix documentation of `useKMPHwind` option in currentweather
### New weather module
- Fixed weather forecast table display [#1499](https://github.com/MagicMirrorOrg/MagicMirror/issues/1499).
- Dimmed loading indicator for weather forecast.
- Implemented config option `decimalSymbol` [#1499](https://github.com/MagicMirrorOrg/MagicMirror/issues/1499).
- Aligned indoor values in current weather vertical [#1499](https://github.com/MagicMirrorOrg/MagicMirror/issues/1499).
- Added humidity support to nunjuck unit filter.
- Do not display degree symbol for temperature in Kelvin [#1503](https://github.com/MagicMirrorOrg/MagicMirror/issues/1503).
- Weather forecast now works with openweathermap for both, `/forecast` and `/forecast/daily`, in new weather module. If you use the `/forecast`-weatherEndpoint, the hourly data are converted to daily data, see issues [#1504](https://github.com/MagicMirrorOrg/MagicMirror/issues/1504), [#1513](https://github.com/MagicMirrorOrg/MagicMirror/issues/1513).
- Added fade, fadePoint and maxNumberOfDays properties to the forecast mode [#1516](https://github.com/MagicMirrorOrg/MagicMirror/issues/1516)
- Fixed Loading string and decimalSymbol string replace [#1538](https://github.com/MagicMirrorOrg/MagicMirror/issues/1538)
- Show Snow amounts in new weather module [#1545](https://github.com/MagicMirrorOrg/MagicMirror/issues/1545)
- Added weather.gov as a new weather-provider for US locations
## [2.6.0] - 2019-01-01
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`. If you are having issues updating, make sure you are running the latest version of Node.
### ✨ Experimental ✨
- New default [module weather](modules/default/weather). This module will eventually replace the current `currentweather` and `weatherforecast` modules. The new module is still pretty experimental, but it's included so you can give it a try and help us improve this module. Please give us you feedback using [this forum post](https://forum.magicmirror.builders/topic/9335/default-weather-module-refactoring).
A huge, huge, huge thanks to user @fewieden for all his hard work on the new `weather` module!
### Added
- Possibility to add classes to the cell of symbol, title and time of the events of calendar.
- Font-awesome 5, still has 4 for backwards compatibility.
- Missing `showEnd` in calendar documentation
- Screenshot for the new feed module
- Screenshot for the compliments module
- Screenshot for the clock module
- Screenshot for the current weather
- Screenshot for the weather forecast module
- Portuguese translation for "Feels"
- Croatian translation
- Fading for dateheaders timeFormat in Calendar [#1464](https://github.com/MagicMirrorOrg/MagicMirror/issues/1464)
- Documentation for the existing `scale` option in the Weather Forecast module.
### Fixed
- Allow parsing recurring calendar events where the start date is before 1900
- Fixed Polish translation for Single Update Info
- Ignore entries with unparseable details in the calendar module
- Bug showing FullDayEvents one day too long in calendar fixed
- Bug in newsfeed when `removeStartTags` is used on the description [#1478](https://github.com/MagicMirrorOrg/MagicMirror/issues/1478)
### Updated
- The default calendar setting `showEnd` is changed to `false`.
### Changed
- The Weather Forecast module by default displays the ° symbol after every numeric value to be consistent with the Current Weather module.
## [2.5.0] - 2018-10-01
### Added
- Romanian translation for "Feels"
- Support multi-line compliments
- Simplified Chinese translation for "Feels"
- Polish translate for "Feels"
- French translate for "Feels"
- Translations for newsfeed module
- Support for toggling news article in fullscreen
- Hungarian translation for "Feels" and "Week"
- Spanish translation for "Feels"
- Add classes instead of inline style to the message from the module Alert
- Support for events having a duration instead of an end
- Support for showing end of events through config parameters showEnd and dateEndFormat
### Fixed
- Fixed gzip encoded calendar loading issue #1400.
- Fixed mixup between german and spanish translation for newsfeed.
- Fixed close dates to be absolute, if no configured in the config.js - module Calendar
- Fixed the updatenotification module message about new commits in the repository, so they can be correctly localized in singular and plural form.
- Fix for weatherforecast rainfall rounding [#1374](https://github.com/MagicMirrorOrg/MagicMirror/issues/1374)
- Fix calendar parsing issue for Midori on Raspberry Pi Zero w, related to issue #694.
- Fix weather city ID link in sample config
- Fixed issue with clientonly not updating with IP address and port provided on command line.
### Updated
- Updated Simplified Chinese translation
- Swedish translations
- Hungarian translations for the updatenotification module
- Updated Norsk bokmål translation
- Updated Norsk nynorsk translation
- Consider multi days event as full day events
## [2.4.1] - 2018-07-04
### Fixed
- Fix weather parsing issue #1332.
## [2.4.0] - 2018-07-01
⚠️ **Warning:** This release includes an updated version of Electron. This requires a Raspberry Pi configuration change to allow the best performance and prevent the CPU from overheating. Please read the information on the [MagicMirror² Wiki](https://github.com/MagicMirrorOrg/MagicMirror/wiki/configuring-the-raspberry-pi#enable-the-open-gl-driver-to-decrease-electrons-cpu-usage).
ℹ️ **Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`
### Added
- Enabled translation of feelsLike for module currentweather
- Added support for on-going calendar events
- Added scroll up in fullscreen newsfeed article view
- Changed fullscreen newsfeed width from 100% to 100vw (better results)
- Added option to calendar module that colors only the symbol instead of the whole line
- Added option for new display format in the calendar module with date headers with times/events below.
- Ability to fetch compliments from a remote server
- Add regex filtering to calendar module
- Customize classes for table
- Added option to newsfeed module to only log error parsing a news article if enabled
- Add update translations for Português Brasileiro
### Changed
- Upgrade to Electron 2.0.0.
- Remove yarn-or-npm which breaks production builds.
- Invoke module suspend even if no dom content. [#1308](https://github.com/MagicMirrorOrg/MagicMirror/issues/1308)
### Fixed
- Fixed issue where wind chill could not be displayed in Fahrenheit. [#1247](https://github.com/MagicMirrorOrg/MagicMirror/issues/1247)
- Fixed issues where a module crashes when it tries to dismiss a non existing alert. [#1240](https://github.com/MagicMirrorOrg/MagicMirror/issues/1240)
- In default module currentWeather/currentWeather.js line 296, 300, self.config.animationSpeed can not be found because the notificationReceived function does not have "self" variable.
- Fixed browser-side code to work on the Midori browser.
- Fixed issue where heat index was reporting incorrect values in Celsius and Fahrenheit. [#1263](https://github.com/MagicMirrorOrg/MagicMirror/issues/1263)
- Fixed weatherforecast to use dt_txt field instead of dt to handle timezones better
- Newsfeed now remembers to show the description when `"ARTICLE_LESS_DETAILS"` is called if the user wants to always show the description. [#1282](https://github.com/MagicMirrorOrg/MagicMirror/issues/1282)
- `clientonly/*.js` is now linted, and one linting error is fixed
- Fix issue #1196 by changing underscore to hyphen in locale id, in align with moment.js.
- Fixed issue where heat index and wind chill were reporting incorrect values in Kelvin. [#1263](https://github.com/MagicMirrorOrg/MagicMirror/issues/1263)
### Updated
- Updated Italian translation
- Updated German translation
- Updated Dutch translation
## [2.3.1] - 2018-04-01
### Fixed
- Downgrade electron to 1.4.15 to solve the black screen issue.[#1243](https://github.com/MagicMirrorOrg/MagicMirror/issues/1243)
## [2.3.0] - 2018-04-01
### Added
- Add new settings in compliments module: setting time intervals for morning and afternoon
- Add system notification `MODULE_DOM_CREATED` for notifying each module when their Dom has been fully loaded.
- Add types for module.
- Implement Danger.js to notify contributors when CHANGELOG.md is missing in PR.
- Allow scrolling in full page article view of default newsfeed module with gesture events from [MMM-Gestures](https://github.com/thobach/MMM-Gestures)
- Changed 'compliments.js' - Updated DOM if remote compliments are loaded instead of waiting one updateInterval to show custom compliments
- Automated unit tests utils, deprecated, translator, cloneObject(lockStrings)
- Automated integration tests translations
- Add advanced filtering to the excludedEvents configuration of the default calendar module
- New currentweather module config option: `showFeelsLike`: Shows how it actually feels like. (wind chill or heat index)
- New currentweather module config option: `useKMPHwind`: adds an option to see wind speed in Kmph instead of just m/s or Beaufort.
- Add dc:date to parsing in newsfeed module, which allows parsing of more rss feeds.
### Changed
- Add link to GitHub repository which contains the respective Dockerfile.
- Optimized automated unit tests cloneObject, cmpVersions
- Updated notifications use now translation templates instead of normal strings.
- Yarn can be used now as an installation tool
- Changed Electron dependency to v1.7.13.
### Fixed
- News article in fullscreen (iframe) is now shown in front of modules.
- Forecast respects maxNumberOfDays regardless of endpoint.
- Fix exception on translation of objects.
## [2.2.2] - 2018-01-02
### Added
- Add missing `package-lock.json`.
### Changed
- Changed Electron dependency to v1.7.10.
## [2.2.1] - 2018-01-01
### Fixed
- Fixed linting errors.
## [2.2.0] - 2018-01-01
**Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`
### Changed
- Calendar week is now handled with a variable translation in order to move number language specific.
- Reverted the Electron dependency back to 1.4.15 since newer version don't seem to work on the Raspberry Pi very well.
### Added
- Add option to use [Nunjucks](https://mozilla.github.io/nunjucks/) templates in modules. (See `helloworld` module as an example.)
- Add Bulgarian translations for MagicMirror² and Alert module.
- Add graceful shutdown of modules by calling `stop` function of each `node_helper` on SIGINT before exiting.
- Link update subtext to Github diff of current version versus tracking branch.
- Add Catalan translation.
- Add ability to filter out newsfeed items based on prohibited words found in title (resolves #1071)
- Add options to truncate description support of a feed in newsfeed module
- Add reloadInterval option for particular feed in newsfeed module
- Add no-cache entries of HTTP headers in newsfeed module (fetcher)
- Add Czech translation.
- Add option for decimal symbols other than the decimal point for temperature values in both default weather modules: WeatherForecast and CurrentWeather.
### Fixed
- Fixed issue with calendar module showing more than `maximumEntries` allows
- WeatherForecast and CurrentWeather are now using HTTPS instead of HTTP
- Correcting translation for Indonesian language
- Fix issue where calendar icons wouldn't align correctly
## [2.1.3] - 2017-10-01
**Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`
### Changed
- Remove Roboto fonts files inside `fonts` and these are installed by npm install command.
### Added
- Add `clientonly` script to start only the electron client for a remote server.
- Add symbol and color properties of event when `CALENDAR_EVENTS` notification is broadcasted from `default/calendar` module.
- Add `.vscode/` folder to `.gitignore` to keep custom Visual Studio Code config out of git.
- Add unit test the capitalizeFirstLetter function of newsfeed module.
- Add new unit tests for function `shorten` in calendar module.
- Add new unit tests for function `getLocaleSpecification` in calendar module.
- Add unit test for js/class.js.
- Add unit tests for function `roundValue` in currentweather module.
- Add test e2e showWeek feature in spanish language.
- Add warning Log when is used old authentication method in the calendar module.
- Add test e2e for helloworld module with default config text.
- Add ability for `currentweather` module to display indoor humidity via INDOOR_HUMIDITY notification.
- Add Welsh (Cymraeg) translation.
- Add Slack badge to Readme.
### Updated
- Changed 'default.js' - listen on all attached interfaces by default.
- Add execution of `npm list` after the test are ran in Travis CI.
- Change hooks for the vendors e2e tests.
- Add log when clientonly failed on starting.
- Add warning color when are using full ip whitelist.
- Set version of the `express-ipfilter` on 0.3.1.
### Fixed
- Fixed issue with incorrect alignment of analog clock when displayed in the center column of the MM.
- Fixed ipWhitelist behavior to make empty whitelist ([]) allow any and all hosts access to the MM.
- Fixed issue with calendar module where 'excludedEvents' count towards 'maximumEntries'.
- Fixed issue with calendar module where global configuration of maximumEntries was not overridden by calendar specific config (see module doc).
- Fixed issue where `this.file(filename)` returns a path with two hashes.
- Workaround for the WeatherForecast API limitation.
## [2.1.2] - 2017-07-01
### Changed
- Revert Docker related changes in favor of [docker-MagicMirror](https://github.com/bastilimbach/docker-MagicMirror). All Docker images are outsourced. ([#856](https://github.com/MagicMirrorOrg/MagicMirror/pull/856))
- Change Docker base image (Debian + Node) to an arm based distro (AlpineARM + Node) ([#846](https://github.com/MagicMirrorOrg/MagicMirror/pull/846))
- Fix the dockerfile to have it running from the first time.
### Added
- Add in option to wrap long calendar events to multiple lines using `wrapEvents` configuration option.
- Add test e2e `show title newsfeed` for newsfeed module.
- Add task to check configuration file.
- Add test check URLs of vendors.
- Add test of match current week number on clock module with showWeek configuration.
- Add test default modules present modules/default/defaultmodules.js.
- Add unit test calendar_modules function capFirst.
- Add test for check if exists the directories present in defaults modules.
- Add support for showing wind direction as an arrow instead of abbreviation in currentWeather module.
- Add support for writing translation functions to support flexible word order
- Add test for check if exits the directories present in defaults modules.
- Add calendar option to set a separate date format for full day events.
- Add ability for `currentweather` module to display indoor temperature via INDOOR_TEMPERATURE notification
- Add ability to change the path of the `custom.css`.
- Add translation Dutch to Alert module.
- Added Romanian translation.
### Updated
- Added missing keys to Polish translation.
- Added missing key to German translation.
- Added better translation with flexible word order to Finnish translation.
### Fixed
- Fix instruction in README for using automatically installer script.
- Bug of [duplicated compliments](https://forum.magicmirror.builders/topic/2381/compliments-module-stops-cycling-compliments).
- Fix double message about port when server is starting
- Corrected Swedish translations for TODAY/TOMORROW/DAYAFTERTOMORROW.
- Removed unused import from js/electron.js
- Made calendar.js respect config.timeFormat irrespective of locale setting.
- Fixed alignment of analog clock when a large calendar is displayed in the same side bar.
## [2.1.1] - 2017-04-01
**Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`
### Changed
- Add `anytime` group for Compliments module.
- Compliments module can use remoteFile without default daytime arrays defined.
- Installer: Use init config.js from config.js.sample.
- Switched out `rrule` package for `rrule-alt` and fixes in `ical.js` in order to fix calendar issues. ([#565](https://github.com/MagicMirrorOrg/MagicMirror/issues/565))
- Make mouse events pass through the region fullscreen_above to modules below.
- Scaled the splash screen down to make it a bit more subtle.
- Replace HTML tables with markdown tables in README files.
- Added `DAYAFTERTOMORROW`, `UPDATE_NOTIFICATION` and `UPDATE_NOTIFICATION_MODULE` to Finnish translations.
- Run `npm test` on Travis automatically.
- Show the splash screen image even when is reboot or halted.
- Added some missing translation strings in the sv.json file.
- Run task jsonlint to check translation files.
- Restructured Test Suite.
### Added
- Added Docker support (Pull Request [#673](https://github.com/MagicMirrorOrg/MagicMirror/pull/673)).
- Calendar-specific support for `maximumEntries`, and `maximumNumberOfDays`.
- Add loaded function to modules, providing an async callback.
- Made default newsfeed module aware of gesture events from [MMM-Gestures](https://github.com/thobach/MMM-Gestures)
- Add use pm2 for manager process into Installer RaspberryPi script.
- Russian Translation.
- Afrikaans Translation.
- Add postinstall script to notify user that MagicMirror² installed successfully despite warnings from NPM.
- Init tests using mocha.
- Option to use RegExp in Calendar's titleReplace.
- Hungarian Translation.
- Icelandic Translation.
- Add use a script to prevent when is run by SSH session set DISPLAY environment.
- Enable ability to set configuration file by the environment variable called MM_CONFIG_FILE.
- Option to give each calendar a different color.
- Option for colored min-temp and max-temp.
- Add test e2e helloworld.
- Add test e2e environment.
- Add `chai-as-promised` npm module to devDependencies.
- Basic set of tests for clock module.
- Run e2e test in Travis.
- Estonian Translation.
- Add test for compliments module for parts of day.
- Korean Translation.
- Added console warning on startup when deprecated config options are used.
- Add option to display temperature unit label to the current weather module.
- Added ability to disable wrapping of news items.
- Added in the ability to hide events in the calendar module based on simple string filters.
- Updated Norwegian translation.
- Added hideLoading option for News Feed module.
- Added configurable dateFormat to clock module.
- Added multiple calendar icon support.
- Added tests for Translations, dev argument, version, dev console.
- Added test anytime feature compliments module.
- Added test ipWhitelist configuration directive.
- Added test for calendar module: default, basic-auth, backward compatibility, fail-basic-auth.
- Added meta tags to support fullscreen mode on iOS (for server mode)
- Added `ignoreOldItems` and `ignoreOlderThan` options to the News Feed module
- Added test for MM_PORT environment variable.
- Added a configurable Week section to the clock module.
### Fixed
- Updated .gitignore to not ignore default modules folder.
- Remove white flash on boot up.
- Added `update` in Raspberry Pi installation script.
- Fix an issue where the analog clock looked scrambled. ([#611](https://github.com/MagicMirrorOrg/MagicMirror/issues/611))
- If units are set to imperial, the showRainAmount option of weatherforecast will show the correct unit.
- Module currentWeather: check if temperature received from api is defined.
- Fix an issue with module hidden status changing to `true` although lock string prevented showing it.
- Fix newsfeed module bug (removeStartTags)
- Fix when is set MM_PORT environment variable.
- Fixed missing animation on `this.show(speed)` when module is alone in a region.
## [2.1.0] - 2016-12-31
**Note:** This update uses new dependencies. Please update using the following command: `git pull && npm install`
### Added
- Finnish translation.
- Danish translation.
- Turkish translation.
- Option to limit access to certain IP addresses based on the value of `ipWhitelist` in the `config.js`, default is access from localhost only (Issue [#456](https://github.com/MagicMirrorOrg/MagicMirror/issues/456)).
- Added ability to change the point of time when calendar events get relative.
- Add Splash screen on boot.
- Add option to show humidity in currentWeather module.
- Add VSCode IntelliSense support.
- Module API: Add Visibility locking to module system. [See documentation](https://github.com/MagicMirrorOrg/MagicMirror/tree/develop/modules#visibility-locking) for more information.
- Module API: Method to overwrite the module's header. [See documentation](https://github.com/MagicMirrorOrg/MagicMirror/tree/develop/modules#getheader) for more information.
- Module API: Option to define the minimum MagicMirror² version to run a module. [See documentation](https://github.com/MagicMirrorOrg/MagicMirror/tree/develop/modules#requiresversion) for more information.
- Calendar module now broadcasts the event list to all other modules using the notification system. [See documentation](https://github.com/MagicMirrorOrg/MagicMirror/tree/develop/modules/default/calendar) for more information.
- Possibility to use the calendar feed as the source for the weather (currentweather & weatherforecast) location data. [See documentation](https://github.com/MagicMirrorOrg/MagicMirror/tree/develop/modules/default/weatherforecast) for more information.
- Added option to show rain amount in the weatherforecast default module
- Add module `updatenotification` to get an update whenever a new version is available. [See documentation](https://github.com/MagicMirrorOrg/MagicMirror/tree/develop/modules/default/updatenotification) for more information.
- Add the ability to set timezone on the date display in the Clock Module
- Ability to set date format in calendar module
- Possibility to use currentweather for the compliments
- Added option `disabled` for modules.
- Added option `address` to set bind address.
- Added option `onlyTemp` for currentweather module to show only current temperature and weather icon.
- Added option `remoteFile` to compliments module to load compliment array from filesystem.
- Added option `zoom` to scale the whole mirror display with a given factor.
- Added option `roundTemp` for currentweather and weatherforecast modules to display temperatures rounded to nearest integer.
- Added ability set the classes option to compliments module for style and text size of compliments.
- Added ability to configure electronOptions
- Calendar module: option to hide private events
- Add root_path for global vars
### Updated
- Modified translations for Frysk.
- Modified core English translations.
- Updated package.json as a result of Snyk security update.
- Improve object instantiation to prevent reference errors.
- Improve logger. `Log.log()` now accepts multiple arguments.
- Remove extensive logging in newsfeed node helper.
- Calendar times are now uniformly capitalized.
- Modules are now secure, and Helmet is now used to prevent abuse of the Mirror's API.
### Fixed
- Solve an issue where module margins would appear when the first module of a section was hidden.
- Solved visual display errors on chrome, if all modules in one of the right sections are hidden.
- Global and Module default config values are no longer modified when setting config values.
- Hide a region if all modules in a region are hidden. Prevention unwanted margins.
- Replaced `electron-prebuilt` package with `electron` in order to fix issues that would happen after 2017.
- Documentation of alert module
## [2.0.5] - 2016-09-20
### Added
- Added ability to remove tags from the beginning or end of newsfeed items in 'newsfeed.js'.
- Added ability to define "the day after tomorrow" for calendar events (Definition for German and Dutch already included).
- Added CII Badge (we are compliant with the CII Best Practices)
- Add support for doing http basic auth when loading calendars
- Add the ability to turn off and on the date display in the Clock Module
### Fixed
- Fix typo in installer.
- Add message to unsupported Pi error to mention that Pi Zeros must use server only mode, as ARMv6 is unsupported. Closes #374.
- Fix API url for weather API.
### Updated
- Force fullscreen when kioskmode is active.
- Updated the .github templates and information with more modern information.
- Updated the Gruntfile with a more functional StyleLint implementation.
## [2.0.4] - 2016-08-07
### Added
- Brazilian Portuguese Translation.
- Option to enable Kiosk mode.
- Added ability to start the app with Dev Tools.
- Added ability to turn off the date display in `clock.js` when in analog mode.
- Greek Translation
### Fixed
- Prevent `getModules()` selectors from returning duplicate entries.
- Append endpoints of weather modules with `/` to retrieve the correct data. (Issue [#337](https://github.com/MagicMirrorOrg/MagicMirror/issues/337))
- Corrected grammar in `module.js` from 'suspend' to 'suspended'.
- Fixed openweathermap.org URL in config sample.
- Prevent currentweather module from crashing when received data object is incorrect.
- Fix issue where translation loading prevented the UI start-up when the language was set to 'en'. (Issue [#388](https://github.com/MagicMirrorOrg/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/MagicMirrorOrg/MagicMirror/issues/300))
- Removed 'null' reference from updateModuleContent(). This fixes recent Edge and Internet Explorer browser displays (Issue [#319](https://github.com/MagicMirrorOrg/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/MagicMirrorOrg/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)
[2.33.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.32.0...v2.33.0
[2.32.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.31.0...v2.32.0
[2.31.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.30.0...v2.31.0
[2.30.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.29.0...v2.30.0
[2.29.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.28.0...v2.29.0
[2.28.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.27.0...v2.28.0
[2.27.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.26.0...v2.27.0
[2.26.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.25.0...v2.26.0
[2.25.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.24.0...v2.25.0
[2.24.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.23.0...v2.24.0
[2.23.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.22.0...v2.23.0
[2.22.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.21.0...v2.22.0
[2.21.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.20.0...v2.21.0
[2.20.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.19.0...v2.20.0
[2.19.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.18.0...v2.19.0
[2.18.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.17.1...v2.18.0
[2.17.1]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.17.0...v2.17.1
[2.17.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.16.0...v2.17.0
[2.16.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.15.0...v2.16.0
[2.15.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.14.0...v2.15.0
[2.14.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.13.0...v2.14.0
[2.13.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.12.0...v2.13.0
[2.12.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.11.0...v2.12.0
[2.11.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.10.1...v2.11.0
[2.10.1]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.10.0...v2.10.1
[2.10.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.9.0...v2.10.0
[2.9.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.8.0...v2.9.0
[2.8.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.7.1...v2.8.0
[2.7.1]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.7.0...v2.7.1
[2.7.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.6.0...v2.7.0
[2.6.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.5.0...v2.6.0
[2.5.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.4.1...v2.5.0
[2.4.1]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.4.0...v2.4.1
[2.4.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.3.1...v2.4.0
[2.3.1]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.3.0...v2.3.1
[2.3.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.2.2...v2.3.0
[2.2.2]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.2.1...v2.2.2
[2.2.1]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.2.0...v2.2.1
[2.2.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.1.3...v2.2.0
[2.1.3]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.1.2...v2.1.3
[2.1.2]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.1.1...v2.1.2
[2.1.1]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.1.0...v2.1.1
[2.1.0]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.0.5...v2.1.0
[2.0.5]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.0.4...v2.0.5
[2.0.4]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.0.3...v2.0.4
[2.0.3]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.0.2...v2.0.3
[2.0.2]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.0.1...v2.0.2
[2.0.1]: https://github.com/MagicMirrorOrg/MagicMirror/compare/v2.0.0...v2.0.1
[2.0.0]: https://github.com/MagicMirrorOrg/MagicMirror/releases/tag/v2.0.0
================================================
FILE: Collaboration.md
================================================
# Collaboration
This document describes how collaborators of this repository should work together.
## Pull Requests
- never merge your own PR's
- never merge without someone having approved (approving and merging from same person is allowed)
- wait for all approvals requested (or the author decides something different in the comments)
- merge to `master` only for releases or other urgent issues (update notification is only triggered by tags)
- merges to master should be tagged with the "mastermerge" label so that the test runs through
## Issues
- "real" Issues are closed if the problem is solved and the fix is released
- unrelated Issues (e.g. related to a foreign module) are closed immediately with a comment to open an issue in the module repository or to discuss this further in the forum or discord
## Releases
Are done by
- [ ] @rejas
- [ ] @sdetweil
- [ ] @khassel
- [ ] @KristjanESPERANTO
### Pre-Deployment steps
- [ ] update dependencies (a few days before)
### Deployment steps
- [ ] pull latest `develop` branch
- [ ] create `prep-release` branch from `develop`
- [ ] update `package.json` and `package-lock.json` to reflect correct version number `2.xx.0`
- [ ] test `prep-release` branch
- [ ] commit and push all changes
- [ ] create pull request from `prep-release` to `develop` branch with title `Prepare Release 2.xx.0`
- [ ] after successful test run via github actions: merge pull request to `develop`
- [ ] review the content of the automatically generated draft release named `unreleased`
- [ ] check contributor names
- [ ] check auto generated min. node version and adjust it for better readability if necessary
- [ ] check if all elements are assigned to the correct category
- [ ] change release name to `v2.xx.0`
- [ ] after successful test run via github actions: create pull request from `develop` to `master` branch
- [ ] add label `mastermerge`
- [ ] title of the PR is `Release 2.xx.0`
- [ ] description of the PR is the body of the draft release with name `v2.xx.0`
- [ ] after PR tests run without issues, merge PR
- [ ] edit draft release with name `v2.xx.0`
- [ ] set corresponding version tag `v2.xx.0` (with `Select tag` and then `Create new tag`)
- [ ] update release link in `Compare to previous Release` by replacing `develop` with new tag `v2.xx.0`
- [ ] publish the release (button at the bottom)
### Draft new development release
- [ ] checkout `develop` branch
- [ ] update `package.json` and `package-lock.json` to reflect correct version number `2.xx.0-develop`
- [ ] commit and push `develop` branch
- [ ] if new release will be in January, update the year in LICENSE.md
### After release
- [ ] publish release notes with link to github release on forum in new locked topic
- [ ] close all issues with label `ready (coming with next release)`
- [ ] release new documentation by merging `develop` on `master` in documentation repository
- [ ] publish new version on [npm](https://www.npmjs.com/package/magicmirror)
- [ ] use a clean environment (e.g. container)
- [ ] clone this repository with the new `master` branch and `cd` into the local repository directory
- [ ] log in to npm with `npm login --auth-type legacy` which will ask for username and password and one-time-password which is sent via mail
- [ ] execute `npm publish`
================================================
FILE: LICENSE.md
================================================
# The MIT License (MIT)
Copyright © 2016-2026 Michael Teeuw
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the “Software”), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
**The software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.**
================================================
FILE: README.md
================================================
# 
<p style="text-align: center">
<a href="https://choosealicense.com/licenses/mit">
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License">
</a>
<img src="https://img.shields.io/github/actions/workflow/status/magicmirrororg/magicmirror/automated-tests.yaml" alt="GitHub Actions">
<img src="https://img.shields.io/github/check-runs/magicmirrororg/magicmirror/master" alt="Build Status">
<a href="https://github.com/MagicMirrorOrg/MagicMirror">
<img src="https://img.shields.io/github/stars/magicmirrororg/magicmirror?style=social" alt="GitHub Stars">
</a>
</p>
**MagicMirror²** is an open source modular smart mirror platform. With a growing list of installable modules, the **MagicMirror²** allows you to convert your hallway or bathroom mirror into your personal assistant. **MagicMirror²** is built by the creator of [the original MagicMirror](https://michaelteeuw.nl/tagged/magicmirror) with the incredible help of a [growing community of contributors](https://github.com/MagicMirrorOrg/MagicMirror/graphs/contributors).
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)
- Technical discussions: <https://forum.magicmirror.builders/category/11/core-system>
- Discord: [https://discord.gg/J5BAtvx](https://discord.gg/J5BAtvx)
- Blog: [https://michaelteeuw.nl/tagged/magicmirror](https://michaelteeuw.nl/tagged/magicmirror)
- Donations: [https://magicmirror.builders/#donate](https://magicmirror.builders/#donate)
## Contributing Guidelines
Contributions of all kinds are welcome, not only in the form of code but also with regards to
- bug reports
- documentation
- translations
For the full contribution guidelines, check out: [https://docs.magicmirror.builders/about/contributing.html](https://docs.magicmirror.builders/about/contributing.html)
## Enjoying MagicMirror? Consider a donation!
MagicMirror² is Open Source and free. That doesn't mean we don't need any money.
Please consider a donation to help us cover the ongoing costs like webservers and email services.
If we receive enough donations we might even be able to free up some working hours and spend some extra time improving the MagicMirror² core.
To donate, please follow [this](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=G5D8E9MR5DTD2&source=url) link.
<p style="text-align: center">
<a href="https://forum.magicmirror.builders/topic/728/magicmirror-is-voted-number-1-in-the-magpi-top-50"><img src="https://magicmirror.builders/img/magpi-best-watermark-custom.png" width="150" alt="MagPi Top 50"></a>
</p>
================================================
FILE: clientonly/index.js
================================================
"use strict";
// Use separate scope to prevent global scope pollution
(function () {
const config = {};
/**
* Helper function to get server address/hostname from either the commandline or env
*/
function getServerAddress () {
/**
* Get command line parameters
* Assumes that a cmdline parameter is defined with `--key [value]`
* @param {string} key key to look for at the command line
* @param {string} defaultValue value if no key is given at the command line
* @returns {string} the value of the parameter
*/
function getCommandLineParameter (key, defaultValue = undefined) {
const index = process.argv.indexOf(`--${key}`);
const value = index > -1 ? process.argv[index + 1] : undefined;
return value !== undefined ? String(value) : defaultValue;
}
// Prefer command line arguments over environment variables
["address", "port"].forEach((key) => {
config[key] = getCommandLineParameter(key, process.env[key.toUpperCase()]);
});
// determine if "--use-tls"-flag was provided
config.tls = process.argv.indexOf("--use-tls") > 0;
}
/**
* Gets the config from the specified server url
* @param {string} url location where the server is running.
* @returns {Promise} the config
*/
function getServerConfig (url) {
// Return new pending promise
return new Promise((resolve, reject) => {
// Select http or https module, depending on requested url
const lib = url.startsWith("https") ? require("node:https") : require("node:http");
const request = lib.get(url, (response) => {
let configData = "";
// Gather incoming data
response.on("data", function (chunk) {
configData += chunk;
});
// Resolve promise at the end of the HTTP/HTTPS stream
response.on("end", function () {
resolve(JSON.parse(configData));
});
});
request.on("error", function (error) {
reject(new Error(`Unable to read config from server (${url} (${error.message}`));
});
});
}
/**
* Print a message to the console in case of errors
* @param {string} message error message to print
* @param {number} code error code for the exit call
*/
function fail (message, code = 1) {
if (message !== undefined && typeof message === "string") {
console.log(message);
} else {
console.log("Usage: 'node clientonly --address 192.168.1.10 --port 8080 [--use-tls]'");
}
process.exit(code);
}
getServerAddress();
(config.address && config.port) || fail();
const prefix = config.tls ? "https://" : "http://";
// Only start the client if a non-local server was provided
if (["localhost", "127.0.0.1", "::1", "::ffff:127.0.0.1", undefined].indexOf(config.address) === -1) {
getServerConfig(`${prefix}${config.address}:${config.port}/config/`)
.then(function (configReturn) {
// check environment for DISPLAY or WAYLAND_DISPLAY
const elecParams = ["js/electron.js"];
if (process.env.WAYLAND_DISPLAY) {
console.log(`Client: Using WAYLAND_DISPLAY=${process.env.WAYLAND_DISPLAY}`);
elecParams.push("--enable-features=UseOzonePlatform");
elecParams.push("--ozone-platform=wayland");
} else if (process.env.DISPLAY) {
console.log(`Client: Using DISPLAY=${process.env.DISPLAY}`);
} else {
fail("Error: Requires environment variable WAYLAND_DISPLAY or DISPLAY, none is provided.");
}
// Pass along the server config via an environment variable
const env = Object.create(process.env);
env.clientonly = true; // set to pass to electron.js
const options = { env: env };
configReturn.address = config.address;
configReturn.port = config.port;
configReturn.tls = config.tls;
env.config = JSON.stringify(configReturn);
// Spawn electron application
const electron = require("electron");
const child = require("node:child_process").spawn(electron, elecParams, options);
// Pipe all child process output to current stdout
child.stdout.on("data", function (buf) {
process.stdout.write(`Client: ${buf}`);
});
// Pipe all child process errors to current stderr
child.stderr.on("data", function (buf) {
process.stderr.write(`Client: ${buf}`);
});
child.on("error", function (err) {
process.stdout.write(`Client: ${err}`);
});
child.on("close", (code) => {
if (code !== 0) {
console.log(`There something wrong. The clientonly is not running code ${code}`);
}
});
})
.catch(function (reason) {
fail(`Unable to connect to server: (${reason})`);
});
} else {
fail();
}
}());
================================================
FILE: cspell.config.json
================================================
{
"version": "0.2",
"language": "en",
"words": [
"aarch",
"Adak",
"Alvinger",
"Ampio",
"andrezibaia",
"angeldeejay",
"apikey",
"apiontek",
"armv",
"ashishtank",
"autoplay",
"Autorestart",
"beada",
"Behaviour",
"Binney",
"bluemanos",
"bnitkin",
"bokmål",
"bouncyflip",
"boxspinner",
"Brasileiro",
"Brento",
"browserwindow",
"bryanzzhu",
"btoconnor",
"bughaver",
"bugsounet",
"buxxi",
"byday",
"calcage",
"calendarfetcher",
"calendarfetcherutils",
"calendarutils",
"calevents",
"chamakura",
"Citypage",
"cjbrunner",
"clearsky",
"clientonly",
"clockfaces",
"cloudcover",
"cmdline",
"codac",
"Codrops",
"cornerexpand",
"Crazylegstoo",
"crazyscot",
"Creepin",
"currentweather",
"CUSTOMCSS",
"customregions",
"cxmj",
"Cymraeg",
"dariom",
"darksky",
"dataheaders",
"Datamart",
"dateheader",
"dateheaders",
"datekey",
"dathbe",
"davide",
"DAYAFTERTOMORROW",
"DAYBEFOREYESTERDAY",
"defaultmodules",
"Deificit",
"Descr",
"dewpoint",
"dgoth",
"difflink",
"dismissttl",
"Displayer",
"dkallen",
"drivelist",
"DTEND",
"DTSTAMP",
"DTSTART",
"Duffman",
"earlman",
"easyas",
"eddiehung",
"Edgardos",
"Ekristoffe",
"elec",
"elif",
"eltociear",
"endfor",
"endmacro",
"envcanada",
"envsub",
"envsubst",
"eouia",
"Evapotranspration",
"exdate",
"exdates",
"expectedheaders",
"exploader",
"ezeholz",
"Fadesteps",
"Faizan",
"feedme",
"feelslike",
"Fenner",
"Feuchte",
"fewieden",
"fixuppm",
"flopp",
"fontawesome",
"fontface",
"forecastweather",
"fortawesome",
"frameguard",
"freezinglevel",
"Frysk",
"fullarticle",
"fulldate",
"fullday",
"fullscreen",
"geraki",
"Gevoelstemperatuur",
"GHSA",
"ghsas",
"grenagit",
"Halfclear",
"heavyrain",
"heavyrainandthunder",
"heavyrainshowers",
"heavyrainshowersandthunder",
"heavysleet",
"heavysleetshowersandthunder",
"heavysnow",
"heavysnowandthunder",
"Heiko",
"Hirschberger",
"hourlyweather",
"humidex",
"Hwind",
"ical",
"illimarkangur",
"Ingan",
"ipfilter",
"ismarslomic",
"jakemulley",
"jakobsarwary",
"jalibu",
"jargordon",
"jetson",
"jkriegshauser",
"jsdocs",
"jsonlint",
"jupadin",
"kaennchenstruggle",
"Kalenderwoche",
"kenzal",
"Keyport",
"khassel",
"Kingdon",
"kioskmode",
"klaernie",
"kleinmantara",
"Kmph",
"Knapoc",
"Koepke",
"kolbyjack",
"Komplex",
"krekos",
"Kristjan",
"krukle",
"labwc",
"Landis",
"larryare",
"Lastberechnung",
"letsencrypt",
"libgpiod",
"Lightspeed",
"loadingcircle",
"locationforecast",
"lockstring",
"lstrip",
"Luciella",
"luxon",
"lxsession",
"magicmirror",
"martingron",
"marvai",
"mastermerge",
"matchtype",
"maxentries",
"Meteo",
"michaelteeuw",
"michmich",
"Midori",
"mirontoli",
"MISSINGLANG",
"mixasgr",
"MMPM",
"modernizr",
"modulename",
"multiday",
"Mystara",
"Ñandú",
"nathannaveen",
"naveensrinivasan",
"nbsp",
"ndom",
"Nerfzooka",
"NEWSFEED",
"newsfeedfetcher",
"newsfetcher",
"newsitems",
"nfogal",
"njwilliams",
"nonrepeating",
"Norsk",
"nunjuck",
"odroid",
"oemel",
"oldconfig",
"onecall",
"onevent",
"openmeteo",
"openmeto",
"openweathermap",
"oraclesean",
"oscarb",
"pcat",
"philnagel",
"pirateweather",
"plained",
"plebcity",
"pmax",
"pmean",
"pmedian",
"pmin",
"Português",
"PRECIP",
"Problema",
"psieg",
"pubdate",
"radokristof",
"rajniszp",
"rebuilded",
"Reis",
"rejas",
"relativehumidity",
"Resig",
"roboto",
"rohitdharavath",
"Rosso",
"Rothfusz",
"rrule",
"savvadam",
"sdetweil",
"searchstr",
"sendheaders",
"serveronly",
"sexualized",
"Sitecode",
"skpanagiotis",
"SMHI",
"Snille",
"snowandthunder",
"snowshowersandthunder",
"socketclient",
"socketio",
"spectron",
"Starinvest",
"stationid",
"STEADMAN",
"sthuber",
"Stieber",
"strinner",
"stylelintrc",
"sunaction",
"suncalc",
"suntimes",
"symboltest",
"systeminformation",
"tada",
"taglist",
"Teeuw",
"Teil",
"TESTMODE",
"thomasrockhu",
"thumbslider",
"timeformat",
"titlereplacestr",
"titlesearchstr",
"todaytemp",
"tomzt",
"trunc",
"ttlms",
"ukmetoffice",
"ukmetofficedatahub",
"unitless",
"unixtime",
"unparseable",
"updatenotification",
"uxdt",
"Vaice",
"veeck",
"verjaardag",
"VEVENT",
"vgtu",
"Vitest",
"Voelt",
"Vorberechnung",
"vppencilsharpener",
"Wallys",
"Weatherbit",
"weathercode",
"WEATHERDATA",
"Weatherflow",
"weatherforecast",
"weathergov",
"weathericon",
"weathericons",
"weatherobject",
"weatherprovider",
"weatherutils",
"webcal",
"winddirection",
"windgusts",
"windspeed",
"Woolridge",
"worktree",
"Wsymb",
"xlarge",
"xmark",
"xrandr",
"xsmall",
"xsorifc",
"xwindows",
"xxxe",
"Ybbet",
"yearmatch",
"yearmatchgroup"
],
"ignorePaths": [
"css/roboto.css",
"node_modules/**",
"modules/!(default)/**",
"modules/default/**/translations/!(en).json",
"modules/default/calendar/windowsZones.json",
"modules/default/clock/faces/*.svg",
"modules/default/weather/providers/yr.js",
"tests/mocks/**",
"tests/e2e/modules/clock_es_spec.js",
"translations/**"
],
"dictionaries": ["node"]
}
================================================
FILE: css/custom.css.sample
================================================
/* Custom CSS Sample
*
* Change color and fonts here.
*
* Beware that properties cannot be unitless, so for example write '--gap-body: 0px;' instead of just '--gap-body: 0;'
*/
/* Uncomment and adjust accordingly if you want to import another font from the google-fonts-api: */
/* @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@100;300;400;700&display=swap'); */
:root {
--color-text: #999;
--color-text-dimmed: #666;
--color-text-bright: #fff;
--color-background: black;
--font-primary: "Roboto Condensed";
--font-secondary: "Roboto";
--font-size: 20px;
--font-size-small: 0.75rem;
--gap-body-top: 60px;
--gap-body-right: 60px;
--gap-body-bottom: 60px;
--gap-body-left: 60px;
--gap-modules: 30px;
}
================================================
FILE: css/font-awesome.css
================================================
@import url("../node_modules/@fortawesome/fontawesome-free/css/all.min.css");
@import url("../node_modules/@fortawesome/fontawesome-free/css/v4-shims.min.css");
================================================
FILE: css/main.css
================================================
:root {
--color-text: #999;
--color-text-dimmed: #666;
--color-text-bright: #fff;
--color-background: #000;
--font-primary: "Roboto Condensed";
--font-secondary: "Roboto";
--font-size: 20px;
--font-size-xsmall: 0.75rem;
--font-size-small: 1rem;
--font-size-medium: 1.5rem;
--font-size-large: 3.25rem;
--font-size-xlarge: 3.75rem;
--gap-body-top: 60px;
--gap-body-right: 60px;
--gap-body-bottom: 60px;
--gap-body-left: 60px;
--gap-modules: 30px;
}
html {
cursor: none;
overflow: hidden;
background: var(--color-background);
user-select: none;
font-size: var(--font-size);
}
::-webkit-scrollbar {
display: none;
}
body {
margin: var(--gap-body-top) var(--gap-body-right) var(--gap-body-bottom) var(--gap-body-left);
position: absolute;
height: calc(100% - var(--gap-body-top) - var(--gap-body-bottom));
width: calc(100% - var(--gap-body-right) - var(--gap-body-left));
background: var(--color-background);
color: var(--color-text);
font-family: var(--font-primary), sans-serif;
font-weight: 400;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
/**
* Default styles.
*/
.dimmed {
color: var(--color-text-dimmed);
}
.normal {
color: var(--color-text);
}
.bright {
color: var(--color-text-bright);
}
.xsmall {
font-size: var(--font-size-xsmall);
line-height: 1.275;
}
.small {
font-size: var(--font-size-small);
line-height: 1.25;
}
.medium {
font-size: var(--font-size-medium);
line-height: 1.225;
}
.large {
font-size: var(--font-size-large);
line-height: 1;
}
.xlarge {
font-size: var(--font-size-xlarge);
line-height: 1;
letter-spacing: -3px;
}
.thin {
font-family: var(--font-secondary), sans-serif;
font-weight: 100;
}
.light {
font-family: var(--font-primary), sans-serif;
font-weight: 300;
}
.regular {
font-family: var(--font-primary), sans-serif;
font-weight: 400;
}
.bold {
font-family: var(--font-primary), sans-serif;
font-weight: 700;
}
.align-right {
text-align: right;
}
.align-left {
text-align: left;
}
header {
text-transform: uppercase;
font-size: var(--font-size-xsmall);
font-family: var(--font-primary), Arial, Helvetica, sans-serif;
font-weight: 400;
border-bottom: 1px solid var(--color-text-dimmed);
line-height: 15px;
padding-bottom: 5px;
margin-bottom: 10px;
color: var(--color-text);
}
sup {
font-size: 50%;
line-height: 50%;
}
/**
* Module styles.
*/
.module {
margin-bottom: var(--gap-modules);
}
.module.hidden {
pointer-events: none;
}
.module:not(.hidden) {
pointer-events: auto;
}
.region.bottom .module {
margin-top: var(--gap-modules);
margin-bottom: 0;
}
.no-wrap {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.pre-line {
white-space: pre-line;
}
/**
* Region Definitions.
*/
.region {
position: absolute;
}
.region.fullscreen {
position: absolute;
inset: calc(-1 * var(--gap-body-top)) calc(-1 * var(--gap-body-right)) calc(-1 * var(--gap-body-bottom)) calc(-1 * var(--gap-body-left));
pointer-events: none;
}
.region.right {
right: 0;
text-align: right;
}
.region.top {
top: 0;
}
.region.top.center,
.region.bottom.center {
left: 50%;
transform: translateX(-50%);
}
.region.top.right,
.region.top.left,
.region.top.center {
top: 100%;
}
.region.bottom {
bottom: 0;
}
.region.bottom.right,
.region.bottom.center,
.region.bottom.left {
bottom: 100%;
}
.region.bar {
width: 100%;
text-align: center;
}
.region.third,
.region.middle.center {
width: 100%;
text-align: center;
transform: translateY(-50%);
}
.region.upper.third {
top: 33%;
}
.region.middle.center {
top: 50%;
}
.region.lower.third {
top: 66%;
}
.region.left {
text-align: left;
}
.region table {
width: 100%;
border-spacing: 0;
border-collapse: separate;
}
/**
* Container Definitions.
*/
.region .container {
display: flex;
flex-direction: column;
}
.region .container.hidden {
display: none;
}
.region.left .flex {
justify-content: flex-start;
}
.region.center .flex {
justify-content: center;
}
.region.right .flex {
justify-content: flex-end;
}
================================================
FILE: css/roboto.css
================================================
/* roboto-cyrillic-ext-100-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 100;
src:
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-ext-100-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-ext-100-normal.woff") format("woff");
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* roboto-cyrillic-100-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 100;
src:
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-100-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-100-normal.woff") format("woff");
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* roboto-greek-ext-100-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 100;
src:
url("../node_modules/@fontsource/roboto/files/roboto-greek-ext-100-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-greek-ext-100-normal.woff") format("woff");
unicode-range: U+1F00-1FFF;
}
/* roboto-greek-100-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 100;
src:
url("../node_modules/@fontsource/roboto/files/roboto-greek-100-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-greek-100-normal.woff") format("woff");
unicode-range: U+0370-03FF;
}
/* roboto-vietnamese-100-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 100;
src:
url("../node_modules/@fontsource/roboto/files/roboto-vietnamese-100-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-vietnamese-100-normal.woff") format("woff");
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* roboto-latin-ext-100-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 100;
src:
url("../node_modules/@fontsource/roboto/files/roboto-latin-ext-100-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-latin-ext-100-normal.woff") format("woff");
unicode-range: U+0100-02AF, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* roboto-latin-100-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 100;
src:
url("../node_modules/@fontsource/roboto/files/roboto-latin-100-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-latin-100-normal.woff") format("woff");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* roboto-cyrillic-ext-300-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 300;
src:
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-ext-300-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-ext-300-normal.woff") format("woff");
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* roboto-cyrillic-300-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 300;
src:
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-300-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-300-normal.woff") format("woff");
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* roboto-greek-ext-300-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 300;
src:
url("../node_modules/@fontsource/roboto/files/roboto-greek-ext-300-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-greek-ext-300-normal.woff") format("woff");
unicode-range: U+1F00-1FFF;
}
/* roboto-greek-300-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 300;
src:
url("../node_modules/@fontsource/roboto/files/roboto-greek-300-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-greek-300-normal.woff") format("woff");
unicode-range: U+0370-03FF;
}
/* roboto-vietnamese-300-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 300;
src:
url("../node_modules/@fontsource/roboto/files/roboto-vietnamese-300-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-vietnamese-300-normal.woff") format("woff");
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* roboto-latin-ext-300-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 300;
src:
url("../node_modules/@fontsource/roboto/files/roboto-latin-ext-300-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-latin-ext-300-normal.woff") format("woff");
unicode-range: U+0100-02AF, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* roboto-latin-300-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 300;
src:
url("../node_modules/@fontsource/roboto/files/roboto-latin-300-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-latin-300-normal.woff") format("woff");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* roboto-cyrillic-ext-400-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 400;
src:
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-ext-400-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-ext-400-normal.woff") format("woff");
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* roboto-cyrillic-400-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 400;
src:
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-400-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-400-normal.woff") format("woff");
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* roboto-greek-ext-400-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 400;
src:
url("../node_modules/@fontsource/roboto/files/roboto-greek-ext-400-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-greek-ext-400-normal.woff") format("woff");
unicode-range: U+1F00-1FFF;
}
/* roboto-greek-400-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 400;
src:
url("../node_modules/@fontsource/roboto/files/roboto-greek-400-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-greek-400-normal.woff") format("woff");
unicode-range: U+0370-03FF;
}
/* roboto-vietnamese-400-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 400;
src:
url("../node_modules/@fontsource/roboto/files/roboto-vietnamese-400-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-vietnamese-400-normal.woff") format("woff");
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* roboto-latin-ext-400-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 400;
src:
url("../node_modules/@fontsource/roboto/files/roboto-latin-ext-400-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-latin-ext-400-normal.woff") format("woff");
unicode-range: U+0100-02AF, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* roboto-latin-400-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 400;
src:
url("../node_modules/@fontsource/roboto/files/roboto-latin-400-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-latin-400-normal.woff") format("woff");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* roboto-cyrillic-ext-500-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 500;
src:
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-ext-500-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-ext-500-normal.woff") format("woff");
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* roboto-cyrillic-500-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 500;
src:
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-500-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-500-normal.woff") format("woff");
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* roboto-greek-ext-500-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 500;
src:
url("../node_modules/@fontsource/roboto/files/roboto-greek-ext-500-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-greek-ext-500-normal.woff") format("woff");
unicode-range: U+1F00-1FFF;
}
/* roboto-greek-500-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 500;
src:
url("../node_modules/@fontsource/roboto/files/roboto-greek-500-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-greek-500-normal.woff") format("woff");
unicode-range: U+0370-03FF;
}
/* roboto-vietnamese-500-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 500;
src:
url("../node_modules/@fontsource/roboto/files/roboto-vietnamese-500-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-vietnamese-500-normal.woff") format("woff");
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* roboto-latin-ext-500-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 500;
src:
url("../node_modules/@fontsource/roboto/files/roboto-latin-ext-500-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-latin-ext-500-normal.woff") format("woff");
unicode-range: U+0100-02AF, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* roboto-latin-500-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 500;
src:
url("../node_modules/@fontsource/roboto/files/roboto-latin-500-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-latin-500-normal.woff") format("woff");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* roboto-cyrillic-ext-700-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 700;
src:
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-ext-700-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-ext-700-normal.woff") format("woff");
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* roboto-cyrillic-700-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 700;
src:
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-700-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-cyrillic-700-normal.woff") format("woff");
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* roboto-greek-ext-700-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 700;
src:
url("../node_modules/@fontsource/roboto/files/roboto-greek-ext-700-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-greek-ext-700-normal.woff") format("woff");
unicode-range: U+1F00-1FFF;
}
/* roboto-greek-700-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 700;
src:
url("../node_modules/@fontsource/roboto/files/roboto-greek-700-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-greek-700-normal.woff") format("woff");
unicode-range: U+0370-03FF;
}
/* roboto-vietnamese-700-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 700;
src:
url("../node_modules/@fontsource/roboto/files/roboto-vietnamese-700-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-vietnamese-700-normal.woff") format("woff");
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* roboto-latin-ext-700-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 700;
src:
url("../node_modules/@fontsource/roboto/files/roboto-latin-ext-700-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-latin-ext-700-normal.woff") format("woff");
unicode-range: U+0100-02AF, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* roboto-latin-700-normal */
@font-face {
font-family: Roboto;
font-style: normal;
font-display: swap;
font-weight: 700;
src:
url("../node_modules/@fontsource/roboto/files/roboto-latin-700-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto/files/roboto-latin-700-normal.woff") format("woff");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* roboto-condensed-cyrillic-ext-300-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 300;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-cyrillic-ext-300-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-cyrillic-ext-300-normal.woff") format("woff");
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* roboto-condensed-cyrillic-300-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 300;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-cyrillic-300-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-cyrillic-300-normal.woff") format("woff");
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* roboto-condensed-greek-ext-300-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 300;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-greek-ext-300-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-greek-ext-300-normal.woff") format("woff");
unicode-range: U+1F00-1FFF;
}
/* roboto-condensed-greek-300-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 300;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-greek-300-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-greek-300-normal.woff") format("woff");
unicode-range: U+0370-03FF;
}
/* roboto-condensed-vietnamese-300-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 300;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-vietnamese-300-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-vietnamese-300-normal.woff") format("woff");
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* roboto-condensed-latin-ext-300-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 300;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-ext-300-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-ext-300-normal.woff") format("woff");
unicode-range: U+0100-02AF, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* roboto-condensed-latin-300-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 300;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-300-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-300-normal.woff") format("woff");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* roboto-condensed-cyrillic-ext-400-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 400;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-cyrillic-ext-400-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-cyrillic-ext-400-normal.woff") format("woff");
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* roboto-condensed-cyrillic-400-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 400;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-cyrillic-400-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-cyrillic-400-normal.woff") format("woff");
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* roboto-condensed-greek-ext-400-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 400;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-greek-ext-400-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-greek-ext-400-normal.woff") format("woff");
unicode-range: U+1F00-1FFF;
}
/* roboto-condensed-greek-400-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 400;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-greek-400-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-greek-400-normal.woff") format("woff");
unicode-range: U+0370-03FF;
}
/* roboto-condensed-vietnamese-400-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 400;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-vietnamese-400-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-vietnamese-400-normal.woff") format("woff");
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* roboto-condensed-latin-ext-400-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 400;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-ext-400-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-ext-400-normal.woff") format("woff");
unicode-range: U+0100-02AF, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* roboto-condensed-latin-400-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 400;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-400-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-400-normal.woff") format("woff");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* roboto-condensed-cyrillic-ext-700-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 700;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-cyrillic-ext-700-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-cyrillic-ext-700-normal.woff") format("woff");
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* roboto-condensed-cyrillic-700-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 700;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-cyrillic-700-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-cyrillic-700-normal.woff") format("woff");
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* roboto-condensed-greek-ext-700-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 700;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-greek-ext-700-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-greek-ext-700-normal.woff") format("woff");
unicode-range: U+1F00-1FFF;
}
/* roboto-condensed-greek-700-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 700;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-greek-700-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-greek-700-normal.woff") format("woff");
unicode-range: U+0370-03FF;
}
/* roboto-condensed-vietnamese-700-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 700;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-vietnamese-700-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-vietnamese-700-normal.woff") format("woff");
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* roboto-condensed-latin-ext-700-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 700;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-ext-700-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-ext-700-normal.woff") format("woff");
unicode-range: U+0100-02AF, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* roboto-condensed-latin-700-normal */
@font-face {
font-family: "Roboto Condensed";
font-style: normal;
font-display: swap;
font-weight: 700;
src:
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-700-normal.woff2") format("woff2"),
url("../node_modules/@fontsource/roboto-condensed/files/roboto-condensed-latin-700-normal.woff") format("woff");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
================================================
FILE: eslint.config.mjs
================================================
import {defineConfig, globalIgnores} from "eslint/config";
import globals from "globals";
import {flatConfigs as importX} from "eslint-plugin-import-x";
import js from "@eslint/js";
import jsdocPlugin from "eslint-plugin-jsdoc";
import packageJson from "eslint-plugin-package-json";
import playwright from "eslint-plugin-playwright";
import stylistic from "@stylistic/eslint-plugin";
import vitest from "eslint-plugin-vitest";
export default defineConfig([
globalIgnores(["config/**", "modules/**/*", "!modules/default/**", "js/positions.js"]),
{
files: ["**/*.js"],
languageOptions: {
ecmaVersion: "latest",
globals: {
...globals.browser,
...globals.node,
...vitest.environments.env.globals,
Log: "readonly",
MM: "readonly",
Module: "readonly",
config: "readonly",
moment: "readonly"
}
},
plugins: {js, stylistic, vitest},
extends: [importX.recommended, vitest.configs.recommended, "js/recommended", jsdocPlugin.configs["flat/recommended"], "stylistic/all"],
rules: {
"@stylistic/array-element-newline": ["error", "consistent"],
"@stylistic/arrow-parens": ["error", "always"],
"@stylistic/brace-style": "off",
"@stylistic/comma-dangle": ["error", "never"],
"@stylistic/dot-location": ["error", "property"],
"@stylistic/function-call-argument-newline": ["error", "consistent"],
"@stylistic/function-paren-newline": ["error", "consistent"],
"@stylistic/implicit-arrow-linebreak": ["error", "beside"],
"@stylistic/indent": ["error", "tab"],
"@stylistic/max-statements-per-line": ["error", {max: 2}],
"@stylistic/multiline-comment-style": "off",
"@stylistic/multiline-ternary": ["error", "always-multiline"],
"@stylistic/newline-per-chained-call": ["error", {ignoreChainWithDepth: 4}],
"@stylistic/no-extra-parens": "off",
"@stylistic/no-tabs": "off",
"@stylistic/object-curly-spacing": ["error", "always"],
"@stylistic/object-property-newline": ["error", {allowAllPropertiesOnSameLine: true}],
"@stylistic/operator-linebreak": ["error", "before"],
"@stylistic/padded-blocks": "off",
"@stylistic/quote-props": ["error", "as-needed"],
"@stylistic/quotes": ["error", "double"],
"@stylistic/semi": ["error", "always"],
"@stylistic/space-before-function-paren": ["error", "always"],
"@stylistic/spaced-comment": "off",
"dot-notation": "error",
eqeqeq: "error",
"id-length": "off",
"import-x/extensions": "error",
"import-x/newline-after-import": "error",
"import-x/order": "error",
"init-declarations": "off",
"vitest/consistent-test-it": "warn",
"vitest/expect-expect": [
"warn",
{
assertFunctionNames: [
"expect",
"testElementLength",
"testTextContain",
"doTest",
"runAnimationTest",
"waitForAnimationClass",
"assertNoAnimationWithin"
]
}
],
"vitest/prefer-to-be": "warn",
"vitest/prefer-to-have-length": "warn",
"max-lines-per-function": ["warn", 400],
"max-statements": "off",
"no-global-assign": "off",
"no-inline-comments": "off",
"no-magic-numbers": "off",
"no-param-reassign": "error",
"no-plusplus": "off",
"no-prototype-builtins": "off",
"no-ternary": "off",
"no-throw-literal": "error",
"no-undefined": "off",
"no-unneeded-ternary": "error",
"no-unused-vars": "off",
"no-useless-return": "error",
"no-warning-comments": "off",
"object-shorthand": ["error", "methods"],
"one-var": "off",
"prefer-template": "error",
"sort-keys": "off"
}
},
{
files: ["**/*.js"],
ignores: [
"clientonly/index.js",
"js/logger.js",
"tests/**/*.js"
],
rules: {"no-console": "error"}
},
{
files: ["**/package.json"],
plugins: {packageJson},
extends: ["packageJson/recommended"]
},
{
files: ["**/*.mjs"],
languageOptions: {
ecmaVersion: "latest",
globals: {
...globals.node
},
sourceType: "module"
},
plugins: {js, stylistic},
extends: [importX.recommended, "js/all", "stylistic/all"],
rules: {
"@stylistic/array-element-newline": "off",
"@stylistic/indent": ["error", "tab"],
"@stylistic/object-property-newline": ["error", {allowAllPropertiesOnSameLine: true}],
"@stylistic/padded-blocks": ["error", "never"],
"@stylistic/quote-props": ["error", "as-needed"],
"import-x/no-unresolved": ["error", {ignore: ["eslint/config"]}],
"max-lines-per-function": ["error", 100],
"no-magic-numbers": "off",
"one-var": ["error", "never"],
"sort-keys": "off"
}
},
{
files: ["tests/configs/modules/weather/*.js"],
rules: {
"@stylistic/quotes": "off"
}
},
{
files: ["tests/e2e/**/*.js"],
extends: [playwright.configs["flat/recommended"]],
rules: {
"playwright/no-standalone-expect": "off"
}
}
]);
================================================
FILE: index.html
================================================
<!doctype html>
<html>
<head>
<title>MagicMirror²</title>
<meta name="google" content="notranslate" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="format-detection" content="telephone=no" />
<meta name="mobile-web-app-capable" content="yes" />
<link rel="icon" href="data:;base64,iVBORw0KGgo=" />
<link rel="stylesheet" type="text/css" href="css/main.css" />
<link rel="stylesheet" type="text/css" href="css/roboto.css" />
<link rel="stylesheet" type="text/css" href="node_modules/animate.css/animate.min.css" />
<!-- custom.css is loaded by the loader.js to make sure it's loaded after the module css files. -->
<script type="text/javascript">
window.mmVersion = "#VERSION#";
window.mmTestMode = "#TESTMODE#";
</script>
</head>
<body>
<div class="region fullscreen below"><div class="container"></div></div>
<div class="region top bar">
<div class="container"></div>
<div class="region top left"><div class="container"></div></div>
<div class="region top center"><div class="container"></div></div>
<div class="region top right"><div class="container"></div></div>
</div>
<div class="region upper third"><div class="container"></div></div>
<div class="region middle center"><div class="container"></div></div>
<div class="region lower third">
<div class="container"><br /></div>
</div>
<div class="region bottom bar">
<div class="container"></div>
<div class="region bottom left"><div class="container"></div></div>
<div class="region bottom center"><div class="container"></div></div>
<div class="region bottom right"><div class="container"></div></div>
</div>
<div class="region fullscreen above"><div class="container"></div></div>
<script type="text/javascript" src="socket.io/socket.io.js"></script>
<script type="text/javascript" src="node_modules/nunjucks/browser/nunjucks.min.js"></script>
<script type="text/javascript" src="js/defaults.js"></script>
<script type="text/javascript" src="#CONFIG_FILE#"></script>
<script type="text/javascript" src="js/vendor.js"></script>
<script type="text/javascript" src="modules/default/defaultmodules.js"></script>
<script type="text/javascript" src="modules/default/utils.js"></script>
<script type="text/javascript" src="js/logger.js"></script>
<script type="text/javascript" src="translations/translations.js"></script>
<script type="text/javascript" src="js/translator.js"></script>
<script type="text/javascript" src="js/class.js"></script>
<script type="text/javascript" src="js/module.js"></script>
<script type="text/javascript" src="js/loader.js"></script>
<script type="text/javascript" src="js/socketclient.js"></script>
<script type="text/javascript" src="js/animateCSS.js"></script>
<script type="text/javascript" src="js/positions.js"></script>
<script type="text/javascript" src="js/main.js"></script>
</body>
</html>
================================================
FILE: jest.config.js
================================================
const aliasMapper = {
logger: "<rootDir>/js/logger.js"
};
const config = {
verbose: true,
testTimeout: 20000,
testSequencer: "<rootDir>/tests/utils/test_sequencer.js",
projects: [
{
displayName: "unit",
globalSetup: "<rootDir>/tests/unit/helpers/global-setup.js",
moduleNameMapper: aliasMapper,
testMatch: ["**/tests/unit/**/*.[jt]s?(x)"],
testPathIgnorePatterns: ["<rootDir>/tests/unit/mocks", "<rootDir>/tests/unit/helpers"]
},
{
displayName: "electron",
testMatch: ["**/tests/electron/**/*.[jt]s?(x)"],
moduleNameMapper: aliasMapper,
testPathIgnorePatterns: ["<rootDir>/tests/electron/helpers"]
},
{
displayName: "e2e",
testMatch: ["**/tests/e2e/**/*.[jt]s?(x)"],
modulePaths: ["<rootDir>/js/"],
moduleNameMapper: aliasMapper,
testPathIgnorePatterns: ["<rootDir>/tests/e2e/helpers", "<rootDir>/tests/e2e/mocks"]
}
],
collectCoverageFrom: [
"<rootDir>/clientonly/**/*.js",
"<rootDir>/js/**/*.js",
"<rootDir>/modules/default/**/*.js",
"<rootDir>/serveronly/**/*.js"
],
coverageReporters: ["lcov", "text"],
coverageProvider: "v8"
};
module.exports = config;
================================================
FILE: js/alias-resolver.js
================================================
// Internal alias mapping for default and 3rd party modules.
// Provides short require identifiers: "logger" and "node_helper".
// For a future ESM migration, replace this with a public export/import surface.
const path = require("node:path");
const Module = require("module");
const root = path.join(__dirname, "..");
// Keep this list minimal; do not add new aliases without architectural review.
const ALIASES = {
logger: "js/logger.js",
node_helper: "js/node_helper.js"
};
// Resolve to absolute paths now.
const resolved = Object.fromEntries(
Object.entries(ALIASES).map(([k, rel]) => [k, path.join(root, rel)])
);
// Prevent multiple patching if this file is required more than once.
if (!Module._mmAliasPatched) {
const origResolveFilename = Module._resolveFilename;
Module._resolveFilename = function (request, parent, isMain, options) {
if (Object.prototype.hasOwnProperty.call(resolved, request)) {
return resolved[request];
}
return origResolveFilename.call(this, request, parent, isMain, options);
};
Module._mmAliasPatched = true; // non-enumerable marker would be overkill here
}
================================================
FILE: js/animateCSS.js
================================================
/* enumeration of animations in Array **/
const AnimateCSSIn = [
// Attention seekers
"bounce",
"flash",
"pulse",
"rubberBand",
"shakeX",
"shakeY",
"headShake",
"swing",
"tada",
"wobble",
"jello",
"heartBeat",
// Back entrances
"backInDown",
"backInLeft",
"backInRight",
"backInUp",
// Bouncing entrances
"bounceIn",
"bounceInDown",
"bounceInLeft",
"bounceInRight",
"bounceInUp",
// Fading entrances
"fadeIn",
"fadeInDown",
"fadeInDownBig",
"fadeInLeft",
"fadeInLeftBig",
"fadeInRight",
"fadeInRightBig",
"fadeInUp",
"fadeInUpBig",
"fadeInTopLeft",
"fadeInTopRight",
"fadeInBottomLeft",
"fadeInBottomRight",
// Flippers
"flip",
"flipInX",
"flipInY",
// Lightspeed
"lightSpeedInRight",
"lightSpeedInLeft",
// Rotating entrances
"rotateIn",
"rotateInDownLeft",
"rotateInDownRight",
"rotateInUpLeft",
"rotateInUpRight",
// Specials
"jackInTheBox",
"rollIn",
// Zooming entrances
"zoomIn",
"zoomInDown",
"zoomInLeft",
"zoomInRight",
"zoomInUp",
// Sliding entrances
"slideInDown",
"slideInLeft",
"slideInRight",
"slideInUp"
];
const AnimateCSSOut = [
// Back exits
"backOutDown",
"backOutLeft",
"backOutRight",
"backOutUp",
// Bouncing exits
"bounceOut",
"bounceOutDown",
"bounceOutLeft",
"bounceOutRight",
"bounceOutUp",
// Fading exits
"fadeOut",
"fadeOutDown",
"fadeOutDownBig",
"fadeOutLeft",
"fadeOutLeftBig",
"fadeOutRight",
"fadeOutRightBig",
"fadeOutUp",
"fadeOutUpBig",
"fadeOutTopLeft",
"fadeOutTopRight",
"fadeOutBottomRight",
"fadeOutBottomLeft",
// Flippers
"flipOutX",
"flipOutY",
// Lightspeed
"lightSpeedOutRight",
"lightSpeedOutLeft",
// Rotating exits
"rotateOut",
"rotateOutDownLeft",
"rotateOutDownRight",
"rotateOutUpLeft",
"rotateOutUpRight",
// Specials
"hinge",
"rollOut",
// Zooming exits
"zoomOut",
"zoomOutDown",
"zoomOutLeft",
"zoomOutRight",
"zoomOutUp",
// Sliding exits
"slideOutDown",
"slideOutLeft",
"slideOutRight",
"slideOutUp"
];
/**
* Create an animation with Animate CSS
* @param {string} [element] div element to animate.
* @param {string} [animation] animation name.
* @param {number} [animationTime] animation duration.
*/
function addAnimateCSS (element, animation, animationTime) {
const animationName = `animate__${animation}`;
const node = document.getElementById(element);
if (!node) {
// don't execute animate: we don't find div
Log.warn("node not found for adding", element);
return;
}
node.style.setProperty("--animate-duration", `${animationTime}s`);
node.classList.add("animate__animated", animationName);
}
/**
* Remove an animation with Animate CSS
* @param {string} [element] div element to animate.
* @param {string} [animation] animation name.
*/
function removeAnimateCSS (element, animation) {
c
gitextract__ioz32a6/ ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── CODE_OF_CONDUCT.md │ ├── CONTRIBUTING.md │ ├── FUNDING.yaml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── change_request.yml │ │ ├── config.yml │ │ └── feature_request.yml │ ├── PULL_REQUEST_TEMPLATE.md │ ├── dependabot.yaml │ └── workflows/ │ ├── automated-tests.yaml │ ├── dep-review.yaml │ ├── electron-rebuild.yaml │ ├── enforce-pullrequest-rules.yaml │ ├── release-notes.yaml │ ├── spellcheck.yaml │ └── stale.yaml ├── .gitignore ├── .husky/ │ └── pre-commit ├── .markdownlint.json ├── .npmrc ├── .prettierignore ├── CHANGELOG.md ├── Collaboration.md ├── LICENSE.md ├── README.md ├── clientonly/ │ └── index.js ├── cspell.config.json ├── css/ │ ├── custom.css.sample │ ├── font-awesome.css │ ├── main.css │ └── roboto.css ├── eslint.config.mjs ├── index.html ├── jest.config.js ├── js/ │ ├── alias-resolver.js │ ├── animateCSS.js │ ├── app.js │ ├── check_config.js │ ├── class.js │ ├── defaults.js │ ├── deprecated.js │ ├── electron.js │ ├── ip_access_control.js │ ├── loader.js │ ├── logger.js │ ├── main.js │ ├── module.js │ ├── module_functions.js │ ├── node_helper.js │ ├── releasenotes.js │ ├── server.js │ ├── server_functions.js │ ├── socketclient.js │ ├── translator.js │ ├── utils.js │ └── vendor.js ├── jsconfig.json ├── module-types.ts ├── modules/ │ └── default/ │ ├── alert/ │ │ ├── README.md │ │ ├── alert.js │ │ ├── notificationFx.js │ │ ├── styles/ │ │ │ ├── center.css │ │ │ ├── left.css │ │ │ ├── notificationFx.css │ │ │ └── right.css │ │ ├── templates/ │ │ │ ├── alert.njk │ │ │ └── notification.njk │ │ └── translations/ │ │ ├── bg.json │ │ ├── da.json │ │ ├── de.json │ │ ├── el.json │ │ ├── en.json │ │ ├── eo.json │ │ ├── es.json │ │ ├── fr.json │ │ ├── hu.json │ │ ├── nl.json │ │ ├── pt-br.json │ │ ├── pt.json │ │ ├── ru.json │ │ └── th.json │ ├── calendar/ │ │ ├── README.md │ │ ├── calendar.css │ │ ├── calendar.js │ │ ├── calendarfetcher.js │ │ ├── calendarfetcherutils.js │ │ ├── calendarutils.js │ │ ├── debug.js │ │ ├── node_helper.js │ │ └── windowsZones.json │ ├── clock/ │ │ ├── README.md │ │ ├── clock.js │ │ └── clock_styles.css │ ├── compliments/ │ │ ├── README.md │ │ └── compliments.js │ ├── defaultmodules.js │ ├── helloworld/ │ │ ├── README.md │ │ ├── helloworld.js │ │ └── helloworld.njk │ ├── newsfeed/ │ │ ├── README.md │ │ ├── fullarticle.njk │ │ ├── newsfeed.css │ │ ├── newsfeed.js │ │ ├── newsfeed.njk │ │ ├── newsfeedfetcher.js │ │ ├── node_helper.js │ │ └── oldconfig.njk │ ├── updatenotification/ │ │ ├── README.md │ │ ├── git_helper.js │ │ ├── node_helper.js │ │ ├── update_helper.js │ │ ├── updatenotification.css │ │ ├── updatenotification.js │ │ └── updatenotification.njk │ ├── utils.js │ └── weather/ │ ├── README.md │ ├── current.njk │ ├── forecast.njk │ ├── hourly.njk │ ├── providers/ │ │ ├── README.md │ │ ├── envcanada.js │ │ ├── openmeteo.js │ │ ├── openweathermap.js │ │ ├── overrideWrapper.js │ │ ├── pirateweather.js │ │ ├── smhi.js │ │ ├── ukmetofficedatahub.js │ │ ├── weatherbit.js │ │ ├── weatherflow.js │ │ ├── weathergov.js │ │ └── yr.js │ ├── weather.css │ ├── weather.js │ ├── weatherobject.js │ ├── weatherprovider.js │ └── weatherutils.js ├── package.json ├── prettier.config.mjs ├── serveronly/ │ ├── index.js │ └── watcher.js ├── stylelint.config.mjs ├── tests/ │ ├── configs/ │ │ ├── customregions.js │ │ ├── default.js │ │ ├── empty_ipWhiteList.js │ │ ├── modules/ │ │ │ ├── alert/ │ │ │ │ ├── welcome_false.js │ │ │ │ ├── welcome_string.js │ │ │ │ └── welcome_true.js │ │ │ ├── calendar/ │ │ │ │ ├── 3_move_first_allday_repeating_event.js │ │ │ │ ├── auth-default.js │ │ │ │ ├── bad_rrule.js │ │ │ │ ├── basic-auth.js │ │ │ │ ├── berlin_end_of_day_repeating.js │ │ │ │ ├── berlin_multi.js │ │ │ │ ├── berlin_whole_day_event_moved_over_dst_change.js │ │ │ │ ├── changed-port.js │ │ │ │ ├── chicago-looking-at-ny-recurring.js │ │ │ │ ├── chicago_late_in_timezone.js │ │ │ │ ├── countCalendarEvents.js │ │ │ │ ├── custom.js │ │ │ │ ├── default.js │ │ │ │ ├── diff_tz_start_end.js │ │ │ │ ├── end_of_day_berlin_moved.js │ │ │ │ ├── event_with_time_over_multiple_days_non_repeating_display_end.js │ │ │ │ ├── event_with_time_over_multiple_days_non_repeating_no_display_end.js │ │ │ │ ├── exdate_and_recurrence_together.js │ │ │ │ ├── exdate_la_at_midnight_dst.js │ │ │ │ ├── exdate_la_at_midnight_std.js │ │ │ │ ├── exdate_la_before_midnight.js │ │ │ │ ├── exdate_syd_at_midnight_dst.js │ │ │ │ ├── exdate_syd_at_midnight_std.js │ │ │ │ ├── exdate_syd_before_midnight.js │ │ │ │ ├── fail-basic-auth.js │ │ │ │ ├── fullday_event_over_multiple_days_nonrepeating.js │ │ │ │ ├── fullday_until.js │ │ │ │ ├── germany_at_end_of_day_repeating.js │ │ │ │ ├── long-fullday-event.js │ │ │ │ ├── old-basic-auth.js │ │ │ │ ├── recurring.js │ │ │ │ ├── rrule_until.js │ │ │ │ ├── show-duplicates-in-calendar.js │ │ │ │ ├── single-fullday-event.js │ │ │ │ ├── sliceMultiDayEvents.js │ │ │ │ └── symboltest.js │ │ │ ├── clock/ │ │ │ │ ├── clock_12hr.js │ │ │ │ ├── clock_24hr.js │ │ │ │ ├── clock_analog.js │ │ │ │ ├── clock_displaySeconds_false.js │ │ │ │ ├── clock_showDateAnalog.js │ │ │ │ ├── clock_showPeriodUpper.js │ │ │ │ ├── clock_showSunMoon.js │ │ │ │ ├── clock_showSunNoEvent.js │ │ │ │ ├── clock_showTime.js │ │ │ │ ├── clock_showWeek.js │ │ │ │ ├── clock_showWeek_short.js │ │ │ │ ├── de/ │ │ │ │ │ ├── clock_showWeek.js │ │ │ │ │ └── clock_showWeek_short.js │ │ │ │ └── es/ │ │ │ │ ├── clock_12hr.js │ │ │ │ ├── clock_24hr.js │ │ │ │ ├── clock_showPeriodUpper.js │ │ │ │ ├── clock_showWeek.js │ │ │ │ └── clock_showWeek_short.js │ │ │ ├── compliments/ │ │ │ │ ├── compliments_animateCSS.js │ │ │ │ ├── compliments_animateCSS_fallbackToDefault.js │ │ │ │ ├── compliments_animateCSS_invertedAnimationName.js │ │ │ │ ├── compliments_anytime.js │ │ │ │ ├── compliments_cron_entry.js │ │ │ │ ├── compliments_date.js │ │ │ │ ├── compliments_e2e_cron_entry.js │ │ │ │ ├── compliments_evening.js │ │ │ │ ├── compliments_file.js │ │ │ │ ├── compliments_file_change.js │ │ │ │ ├── compliments_only_anytime.js │ │ │ │ ├── compliments_parts_day.js │ │ │ │ ├── compliments_remote.js │ │ │ │ ├── compliments_specialDayUnique_false.js │ │ │ │ └── compliments_specialDayUnique_true.js │ │ │ ├── display.js │ │ │ ├── helloworld/ │ │ │ │ ├── helloworld.js │ │ │ │ └── helloworld_default.js │ │ │ ├── newsfeed/ │ │ │ │ ├── default.js │ │ │ │ ├── ignore_items.js │ │ │ │ ├── incorrect_url.js │ │ │ │ └── prohibited_words.js │ │ │ ├── positions.js │ │ │ └── weather/ │ │ │ ├── currentweather_compliments.js │ │ │ ├── currentweather_default.js │ │ │ ├── currentweather_options.js │ │ │ ├── currentweather_units.js │ │ │ ├── forecastweather_absolute.js │ │ │ ├── forecastweather_default.js │ │ │ ├── forecastweather_options.js │ │ │ ├── forecastweather_units.js │ │ │ ├── hourlyweather_default.js │ │ │ ├── hourlyweather_options.js │ │ │ └── hourlyweather_showPrecipitation.js │ │ ├── noIpWhiteList.js │ │ ├── port_8090.js │ │ ├── port_variable.env │ │ ├── port_variable.js.template │ │ └── without_modules.js │ ├── e2e/ │ │ ├── animateCSS_spec.js │ │ ├── custom_module_regions_spec.js │ │ ├── env_spec.js │ │ ├── fonts_spec.js │ │ ├── helpers/ │ │ │ ├── basic-auth.js │ │ │ ├── global-setup.js │ │ │ └── weather-functions.js │ │ ├── ipWhitelist_spec.js │ │ ├── modules/ │ │ │ ├── alert_spec.js │ │ │ ├── calendar_spec.js │ │ │ ├── clock_de_spec.js │ │ │ ├── clock_es_spec.js │ │ │ ├── clock_spec.js │ │ │ ├── compliments_spec.js │ │ │ ├── helloworld_spec.js │ │ │ ├── newsfeed_spec.js │ │ │ ├── weather_current_spec.js │ │ │ ├── weather_forecast_spec.js │ │ │ └── weather_hourly_spec.js │ │ ├── modules_display_spec.js │ │ ├── modules_empty_spec.js │ │ ├── modules_position_spec.js │ │ ├── port_spec.js │ │ ├── serveronly_spec.js │ │ ├── template_spec.js │ │ ├── translations_spec.js │ │ └── vendor_spec.js │ ├── electron/ │ │ ├── env_spec.js │ │ ├── helpers/ │ │ │ ├── global-setup.js │ │ │ └── weather-setup.js │ │ └── modules/ │ │ ├── calendar_spec.js │ │ ├── compliments_spec.js │ │ └── weather_spec.js │ ├── mocks/ │ │ ├── 12_events.ics │ │ ├── 3_move_first_allday_repeating_event.ics │ │ ├── RepeatingEvent.Oct21.ics │ │ ├── bad_rrule.ics │ │ ├── calendar_duplicates_1.ics │ │ ├── calendar_duplicates_2.ics │ │ ├── calendar_test.ics │ │ ├── calendar_test_clone.ics │ │ ├── calendar_test_full_day_events.ics │ │ ├── calendar_test_icons.ics │ │ ├── calendar_test_multi_day_starting_today.ics │ │ ├── calendar_test_recurring.ics │ │ ├── chicago-nyedge.ics │ │ ├── chicago_late_in_timezone.ics │ │ ├── compliments_file.json │ │ ├── compliments_test.json │ │ ├── diff_tz_start_end.ics │ │ ├── end_of_day_berlin_moved.ics │ │ ├── event_with_time_over_multiple_days_non_repeating.ics │ │ ├── exdate_and_recurrence_together.ics │ │ ├── exdate_la_at_midnight_dst.ics │ │ ├── exdate_la_at_midnight_std.ics │ │ ├── exdate_la_before_midnight.ics │ │ ├── exdate_syd_at_midnight_dst.ics │ │ ├── exdate_syd_at_midnight_std.ics │ │ ├── exdate_syd_before_midnight.ics │ │ ├── fullday_event_over_multiple_days_nonrepeating.ics │ │ ├── fullday_until.ics │ │ ├── germany_at_end_of_day_repeating.ics │ │ ├── newsfeed_test.xml │ │ ├── rrule_until.ics │ │ ├── sliceMultiDayEvents.ics │ │ ├── testNotification/ │ │ │ └── testNotification.js │ │ ├── translation_test.json │ │ ├── weather_current.json │ │ ├── weather_forecast.json │ │ ├── weather_hourly.json │ │ └── whole_day_moved_over_dst_change_berlin.ics │ ├── unit/ │ │ ├── classes/ │ │ │ ├── class_spec.js │ │ │ ├── deprecated_spec.js │ │ │ ├── translator_spec.js │ │ │ └── utils_spec.js │ │ ├── functions/ │ │ │ ├── __snapshots__/ │ │ │ │ └── updatenotification_spec.js.snap │ │ │ ├── cmp_versions_spec.js │ │ │ ├── server_functions_spec.js │ │ │ └── updatenotification_spec.js │ │ ├── global_vars/ │ │ │ ├── defaults_modules_spec.js │ │ │ └── root_path_spec.js │ │ ├── helpers/ │ │ │ └── global-setup.js │ │ └── modules/ │ │ └── default/ │ │ ├── calendar/ │ │ │ ├── calendar_fetcher_utils_bad_rrule.js │ │ │ ├── calendar_fetcher_utils_spec.js │ │ │ └── calendar_utils_spec.js │ │ ├── compliments/ │ │ │ └── compliments_spec.js │ │ ├── utils_spec.js │ │ └── weather/ │ │ ├── weather_object_spec.js │ │ └── weather_utils_spec.js │ └── utils/ │ ├── vitest-setup.js │ └── weather_mocker.js ├── translations/ │ ├── af.json │ ├── ar.json │ ├── bg.json │ ├── ca.json │ ├── cs.json │ ├── cv.json │ ├── cy.json │ ├── da.json │ ├── de.json │ ├── el.json │ ├── en.json │ ├── eo.json │ ├── es.json │ ├── et.json │ ├── fi.json │ ├── fr.json │ ├── fy.json │ ├── gl.json │ ├── gu.json │ ├── he.json │ ├── hi.json │ ├── hr.json │ ├── hu.json │ ├── id.json │ ├── is.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── lt.json │ ├── ms-my.json │ ├── nb.json │ ├── nl.json │ ├── nn.json │ ├── pl.json │ ├── ps.json │ ├── pt-br.json │ ├── pt.json │ ├── ro.json │ ├── ru.json │ ├── sk.json │ ├── sv.json │ ├── th.json │ ├── tlh.json │ ├── tr.json │ ├── translations.js │ ├── uk.json │ ├── zh-cn.json │ └── zh-tw.json └── vitest.config.mjs
SYMBOL INDEX (465 symbols across 58 files)
FILE: clientonly/index.js
function getServerAddress (line 10) | function getServerAddress () {
function getServerConfig (line 39) | function getServerConfig (url) {
function fail (line 68) | function fail (message, code = 1) {
FILE: js/alias-resolver.js
constant ALIASES (line 11) | const ALIASES = {
FILE: js/animateCSS.js
function addAnimateCSS (line 130) | function addAnimateCSS (element, animation, animationTime) {
function removeAnimateCSS (line 147) | function removeAnimateCSS (element, animation) {
FILE: js/app.js
function App (line 56) | function App () {
FILE: js/check_config.js
function getConfigFile (line 23) | function getConfigFile () {
function checkConfigFile (line 31) | function checkConfigFile () {
function validateModulePositions (line 90) | function validateModulePositions (configFileName) {
FILE: js/class.js
function Class (line 70) | function Class () {
function cloneObject (line 95) | function cloneObject (obj) {
FILE: js/electron.js
function createWindow (line 33) | function createWindow () {
FILE: js/ip_access_control.js
function isAllowed (line 10) | function isAllowed (clientIp, whitelist) {
function ipAccessControl (line 41) | function ipAccessControl (whitelist) {
FILE: js/loader.js
method loadModules (line 243) | async loadModules () {
method loadFileForModule (line 269) | async loadFileForModule (fileName, module) {
FILE: js/logger.js
method debug (line 114) | debug () {}
method log (line 115) | log () {}
method info (line 116) | info () {}
method warn (line 117) | warn () {}
method error (line 118) | error () {}
method group (line 119) | group () {}
method groupCollapsed (line 120) | groupCollapsed () {}
method groupEnd (line 121) | groupEnd () {}
method time (line 122) | time () {}
method timeEnd (line 123) | timeEnd () {}
method timeStamp (line 124) | timeStamp () {}
FILE: js/main.js
method init (line 583) | async init () {
method modulesStarted (line 597) | modulesStarted (moduleObjects) {
method sendNotification (line 646) | sendNotification (notification, payload, sender) {
method updateDom (line 671) | updateDom (module, updateOptions) {
method getModules (line 693) | getModules () {
method hideModule (line 705) | hideModule (module, speed, callback, options) {
method showModule (line 717) | showModule (module, speed, callback, options) {
FILE: js/module.js
method init (line 40) | init () {
method start (line 46) | async start () {
method getScripts (line 54) | getScripts () {
method getStyles (line 62) | getStyles () {
method getTranslations (line 72) | getTranslations () {
method getDom (line 82) | getDom () {
method getHeader (line 115) | getHeader () {
method getTemplate (line 126) | getTemplate () {
method getTemplateData (line 135) | getTemplateData () {
method notificationReceived (line 145) | notificationReceived (notification, payload, sender) {
method nunjucksEnvironment (line 158) | nunjucksEnvironment () {
method socketNotificationReceived (line 180) | socketNotificationReceived (notification, payload) {
method suspend (line 187) | suspend () {
method resume (line 194) | resume () {
method setData (line 208) | setData (data) {
method setConfig (line 224) | setConfig (config, deep) {
method socket (line 233) | socket () {
method file (line 250) | file (file) {
method loadStyles (line 258) | loadStyles () {
method loadScripts (line 266) | loadScripts () {
method loadDependencies (line 275) | async loadDependencies (funcName) {
method loadTranslations (line 296) | async loadTranslations () {
method translate (line 328) | translate (key, defaultValueOrVariables, defaultValue) {
method updateDom (line 339) | updateDom (updateOptions) {
method sendNotification (line 348) | sendNotification (notification, payload) {
method sendSocketNotification (line 357) | sendSocketNotification (notification, payload) {
method hide (line 367) | hide (speed, callback, options = {}) {
method show (line 394) | show (speed, callback, options) {
function configMerge (line 438) | function configMerge (result) {
function cmpVersions (line 501) | function cmpVersions (a, b) {
FILE: js/node_helper.js
method init (line 6) | init () {
method loaded (line 10) | loaded () {
method start (line 14) | start () {
method stop (line 23) | stop () {
method socketNotificationReceived (line 32) | socketNotificationReceived (notification, payload) {
method setName (line 40) | setName (name) {
method setPath (line 48) | setPath (path) {
method sendSocketNotification (line 59) | sendSocketNotification (notification, payload) {
method setExpressApp (line 70) | setExpressApp (app) {
method setSocketIO (line 83) | setSocketIO (io) {
FILE: js/server.js
function Server (line 20) | function Server (config) {
FILE: js/server_functions.js
function getConfig (line 12) | function getConfig (req, res) {
function getStartup (line 21) | function getStartup (req, res) {
function cors (line 35) | async function cors (req, res) {
function getHeadersToSend (line 78) | function getHeadersToSend (url) {
function geExpectedReceivedHeaders (line 99) | function geExpectedReceivedHeaders (url) {
function getHtml (line 116) | function getHtml (req, res) {
function getVersion (line 135) | function getVersion (req, res) {
function getUserAgent (line 143) | function getUserAgent () {
function getEnvVarsAsObj (line 164) | function getEnvVarsAsObj () {
function getEnvVars (line 181) | function getEnvVars (req, res) {
function getConfigFilePath (line 190) | function getConfigFilePath () {
FILE: js/translator.js
function loadJSON (line 10) | async function loadJSON (file) {
method translate (line 39) | translate (module, key, variables = {}) {
method load (line 88) | async load (module, file, isFallback) {
method loadCoreTranslations (line 104) | async loadCoreTranslations (lang) {
method loadCoreTranslationsFallback (line 119) | async loadCoreTranslationsFallback () {
FILE: js/utils.js
method logSystemInformation (line 13) | async logSystemInformation (mirrorVersion) {
method getAvailableModulePositions (line 45) | getAvailableModulePositions () {
method moduleHasValidPosition (line 50) | moduleHasValidPosition (position) {
method getModulePositions (line 55) | getModulePositions () {
FILE: module-types.ts
type ModuleProperties (line 1) | type ModuleProperties = {
FILE: modules/default/alert/alert.js
method getScripts (line 14) | getScripts () {
method getStyles (line 18) | getStyles () {
method getTranslations (line 22) | getTranslations () {
method getTemplate (line 40) | getTemplate (type) {
method start (line 44) | async start () {
method notificationReceived (line 57) | notificationReceived (notification, payload, sender) {
method showNotification (line 69) | async showNotification (notification) {
method showAlert (line 80) | async showAlert (alert, sender) {
method hideAlert (line 113) | hideAlert (sender, close = true) {
method renderMessage (line 125) | renderMessage (type, data) {
method toggleBlur (line 137) | toggleBlur (add = false) {
FILE: modules/default/alert/notificationFx.js
function extend (line 22) | function extend (a, b) {
function NotificationFx (line 36) | function NotificationFx (options) {
method onClose (line 67) | onClose () {
method onOpen (line 70) | onOpen () {
FILE: modules/default/calendar/calendar.js
method getStyles (line 72) | getStyles () {
method getScripts (line 77) | getScripts () {
method getTranslations (line 82) | getTranslations () {
method start (line 93) | start () {
method notificationReceived (line 170) | notificationReceived (notification, payload, sender) {
method socketNotificationReceived (line 179) | socketNotificationReceived (notification, payload) {
method getDom (line 231) | getDom () {
method hasCalendarURL (line 588) | hasCalendarURL (url) {
method timestampToMoment (line 603) | timestampToMoment (timestamp) {
method createEventList (line 612) | createEventList (limitNumberOfEntries) {
method listContainsEvent (line 742) | listContainsEvent (eventList, event) {
method addCalendar (line 757) | addCalendar (url, auth, calendarConfig) {
method symbolsForEvent (line 780) | symbolsForEvent (event) {
method mergeUnique (line 807) | mergeUnique (arr1, arr2) {
method symbolClassForUrl (line 820) | symbolClassForUrl (url) {
method titleClassForUrl (line 829) | titleClassForUrl (url) {
method timeClassForUrl (line 838) | timeClassForUrl (url) {
method calendarNameForUrl (line 847) | calendarNameForUrl (url) {
method colorForUrl (line 857) | colorForUrl (url, isBg) {
method countTitleForUrl (line 866) | countTitleForUrl (url) {
method maximumEntriesForUrl (line 875) | maximumEntriesForUrl (url) {
method maximumPastDaysForUrl (line 884) | maximumPastDaysForUrl (url) {
method getCalendarProperty (line 895) | getCalendarProperty (url, property, defaultValue) {
method getCalendarPropertyAsArray (line 905) | getCalendarPropertyAsArray (url, property, defaultValue) {
method hasCalendarProperty (line 920) | hasCalendarProperty (url, property) {
method broadcastEvents (line 928) | broadcastEvents () {
method selfUpdate (line 948) | selfUpdate () {
FILE: modules/default/calendar/calendarfetcher.js
constant FIFTEEN_MINUTES (line 7) | const FIFTEEN_MINUTES = 15 * 60 * 1000;
constant THIRTY_MINUTES (line 8) | const THIRTY_MINUTES = 30 * 60 * 1000;
constant MAX_SERVER_BACKOFF (line 9) | const MAX_SERVER_BACKOFF = 3;
class CalendarFetcher (line 15) | class CalendarFetcher {
method constructor (line 28) | constructor (url, reloadInterval, excludedEvents, maximumEntries, maxi...
method clearReloadTimer (line 49) | clearReloadTimer () {
method scheduleNextFetch (line 60) | scheduleNextFetch (delay) {
method getRequestOptions (line 72) | getRequestOptions () {
method parseRetryAfter (line 96) | parseRetryAfter (retryAfter) {
method getDelayForResponse (line 115) | getDelayForResponse (response) {
method fetchCalendar (line 147) | async fetchCalendar () {
method shouldRefetch (line 189) | shouldRefetch () {
method broadcastEvents (line 200) | broadcastEvents () {
method onReceive (line 209) | onReceive (callback) {
method onError (line 217) | onError (callback) {
FILE: modules/default/calendar/calendarfetcherutils.js
method shouldEventBeExcluded (line 17) | shouldEventBeExcluded (config, title) {
method getLocalTimezone (line 39) | getLocalTimezone () {
method getMomentsFromRecurringEvent (line 51) | getMomentsFromRecurringEvent (event, pastLocalMoment, futureLocalMoment,...
method filterEvents (line 99) | filterEvents (data, config) {
method getTitleFromEvent (line 231) | getTitleFromEvent (event) {
method isFullDayEvent (line 247) | isFullDayEvent (event) {
method timeFilterApplies (line 270) | timeFilterApplies (now, endDate, filter) {
method titleFilterApplies (line 291) | titleFilterApplies (title, filter, useRegex, regexFlags) {
method expandRecurringEvent (line 313) | expandRecurringEvent (event, pastLocalMoment, futureLocalMoment, duratio...
method checkEventAgainstFilter (line 368) | checkEventAgainstFilter (title, filterConfig) {
FILE: modules/default/calendar/calendarutils.js
method capFirst (line 8) | capFirst (string) {
method getLocaleSpecification (line 19) | getLocaleSpecification (timeFormat) {
method shorten (line 41) | shorten (string, maxLength, wrapEvents, maxTitleLines) {
method titleTransform (line 96) | titleTransform (title, titleReplace) {
FILE: modules/default/calendar/node_helper.js
method start (line 8) | start () {
method socketNotificationReceived (line 14) | socketNotificationReceived (notification, payload) {
method createFetcher (line 41) | createFetcher (url, fetchInterval, excludedEvents, maximumEntries, maxim...
method broadcastEvents (line 93) | broadcastEvents (fetcher, identifier) {
FILE: modules/default/clock/clock.js
method getScripts (line 34) | getScripts () {
method getStyles (line 38) | getStyles () {
method start (line 42) | start () {
method getDom (line 91) | getDom () {
FILE: modules/default/compliments/compliments.js
method getScripts (line 33) | getScripts () {
method start (line 38) | async start () {
method isCronEntry (line 84) | isCronEntry (entry) {
method getSecondsUntilNextCronRun (line 93) | getSecondsUntilNextCronRun (cronExpression, timestamp = new Date()) {
method randomIndex (line 110) | randomIndex (compliments) {
method complimentArray (line 134) | complimentArray () {
method loadComplimentFile (line 209) | async loadComplimentFile () {
method getRandomCompliment (line 243) | getRandomCompliment () {
method getDom (line 262) | getDom () {
method notificationReceived (line 311) | notificationReceived (notification, payload, sender) {
FILE: modules/default/helloworld/helloworld.js
method getTemplate (line 7) | getTemplate () {
method getTemplateData (line 11) | getTemplateData () {
FILE: modules/default/newsfeed/newsfeed.js
method getUrlPrefix (line 39) | getUrlPrefix (item) {
method getScripts (line 48) | getScripts () {
method getStyles (line 53) | getStyles () {
method getTranslations (line 58) | getTranslations () {
method start (line 66) | start () {
method socketNotificationReceived (line 84) | socketNotificationReceived (notification, payload) {
method getTemplate (line 104) | getTemplate () {
method getTemplateData (line 114) | getTemplateData () {
method getActiveItemURL (line 159) | getActiveItemURL () {
method registerFeeds (line 171) | registerFeeds () {
method getFeedProperty (line 186) | getFeedProperty (feed, property) {
method generateFeed (line 197) | generateFeed (feeds) {
method subscribedToFeed (line 290) | subscribedToFeed (feedUrl) {
method titleForFeed (line 304) | titleForFeed (feedUrl) {
method scheduleUpdateInterval (line 316) | scheduleUpdateInterval () {
method resetDescrOrFullArticleAndTimer (line 357) | resetDescrOrFullArticleAndTimer () {
method notificationReceived (line 368) | notificationReceived (notification, payload, sender) {
method showFullArticle (line 429) | showFullArticle () {
FILE: modules/default/newsfeed/node_helper.js
method start (line 7) | start () {
method socketNotificationReceived (line 13) | socketNotificationReceived (notification, payload) {
method createFetcher (line 25) | createFetcher (feed, config) {
method broadcastFeeds (line 72) | broadcastFeeds () {
FILE: modules/default/updatenotification/git_helper.js
class GitHelper (line 7) | class GitHelper {
method constructor (line 8) | constructor () {
method getRefRegex (line 13) | getRefRegex (branch) {
method execShell (line 17) | async execShell (command) {
method isGitRepo (line 23) | async isGitRepo (moduleFolder) {
method add (line 35) | async add (moduleName) {
method getStatusInfo (line 60) | async getStatusInfo (repo) {
method getRepoInfo (line 115) | async getRepoInfo (repo) {
method getRepos (line 175) | async getRepos () {
method checkUpdates (line 196) | async checkUpdates () {
FILE: modules/default/updatenotification/node_helper.js
constant ONE_MINUTE (line 9) | const ONE_MINUTE = 60 * 1000;
method getModules (line 20) | getModules (modules) {
method configureModules (line 35) | async configureModules (modules) {
method socketNotificationReceived (line 47) | async socketNotificationReceived (notification, payload) {
method performFetch (line 72) | async performFetch () {
method scheduleNextFetch (line 97) | scheduleNextFetch (delay) {
method ignoreUpdateChecking (line 108) | ignoreUpdateChecking (moduleName) {
FILE: modules/default/updatenotification/update_helper.js
class Updater (line 43) | class Updater {
method constructor (line 44) | constructor (config) {
method parse (line 58) | async parse (modules) {
method updateProcess (line 97) | updateProcess (module) {
method restart (line 137) | restart () {
method pm2Restart (line 143) | pm2Restart () {
method nodeRestart (line 154) | nodeRestart () {
method check_PM2_Process (line 164) | check_PM2_Process () {
method isMagicMirror (line 216) | isMagicMirror (module) {
method applyCommand (line 222) | applyCommand (module) {
FILE: modules/default/updatenotification/updatenotification.js
method start (line 18) | start () {
method suspend (line 27) | suspend () {
method resume (line 31) | resume () {
method notificationReceived (line 36) | notificationReceived (notification) {
method socketNotificationReceived (line 48) | socketNotificationReceived (notification, payload) {
method getStyles (line 62) | getStyles () {
method getTemplate (line 66) | getTemplate () {
method getTemplateData (line 70) | getTemplateData () {
method updateUI (line 74) | updateUI (payload) {
method addFilters (line 92) | addFilters () {
method updatesNotifier (line 104) | updatesNotifier (payload, done = true) {
FILE: modules/default/utils.js
function performWebRequest (line 11) | async function performWebRequest (url, type = "json", useCorsProxy = fal...
FILE: modules/default/weather/providers/envcanada.js
method setConfig (line 47) | setConfig (config) {
method start (line 62) | start () {
method fetchCurrentWeather (line 70) | fetchCurrentWeather () {
method fetchWeatherForecast (line 77) | fetchWeatherForecast () {
method fetchWeatherHourly (line 86) | fetchWeatherHourly () {
method fetchCommon (line 103) | fetchCommon (target) {
method getUrl (line 210) | getUrl () {
method getCurrentHourGMT (line 220) | getCurrentHourGMT () {
method generateWeatherObjectFromCurrentWeather (line 228) | generateWeatherObjectFromCurrentWeather (ECdoc) {
method generateWeatherObjectsFromForecast (line 297) | generateWeatherObjectsFromForecast (ECdoc) {
method generateWeatherObjectsFromHourly (line 424) | generateWeatherObjectsFromHourly (ECdoc) {
method setMinMaxTemps (line 470) | setMinMaxTemps (weather, foreGroup, today, fullDay, currentTemp) {
method setPrecipitation (line 549) | setPrecipitation (weather, foreGroup, today) {
method convertWeatherType (line 565) | convertWeatherType (weatherType) {
FILE: modules/default/weather/providers/openmeteo.js
constant GEOCODE_BASE (line 9) | const GEOCODE_BASE = "https://api.bigdatacloud.net/data/reverse-geocode-...
constant OPEN_METEO_BASE (line 10) | const OPEN_METEO_BASE = "https://api.open-meteo.com/v1";
method fetchedLocation (line 141) | fetchedLocation () {
method fetchCurrentWeather (line 145) | fetchCurrentWeather () {
method fetchWeatherForecast (line 163) | fetchWeatherForecast () {
method fetchWeatherHourly (line 181) | fetchWeatherHourly () {
method setConfig (line 203) | setConfig (config) {
method getQueryParameters (line 227) | getQueryParameters () {
method getUrl (line 279) | getUrl () {
method checkDST (line 284) | checkDST (dt) {
method transposeDataMatrix (line 295) | transposeDataMatrix (data) {
method parseWeatherApiResponse (line 306) | parseWeatherApiResponse (data) {
method fetchLocation (line 344) | fetchLocation () {
method generateWeatherDayFromCurrentWeather (line 359) | generateWeatherDayFromCurrentWeather (weather) {
method generateWeatherObjectsFromForecast (line 406) | generateWeatherObjectsFromForecast (weathers) {
method generateWeatherObjectsFromHourly (line 434) | generateWeatherObjectsFromHourly (weathers) {
method convertWeatherType (line 469) | convertWeatherType (weathercode, isDayTime) {
method getScripts (line 554) | getScripts () {
FILE: modules/default/weather/providers/openweathermap.js
method fetchCurrentWeather (line 32) | fetchCurrentWeather () {
method fetchWeatherForecast (line 51) | fetchWeatherForecast () {
method fetchWeatherHourly (line 73) | fetchWeatherHourly () {
method getUrl (line 100) | getUrl () {
method generateWeatherObjectFromCurrentWeather (line 107) | generateWeatherObjectFromCurrentWeather (currentWeatherData) {
method generateWeatherObjectsFromForecast (line 126) | generateWeatherObjectsFromForecast (forecasts) {
method generateWeatherObjectsFromOnecall (line 139) | generateWeatherObjectsFromOnecall (data) {
method generateForecastHourly (line 151) | generateForecastHourly (forecasts) {
method generateForecastDaily (line 229) | generateForecastDaily (forecasts) {
method fetchOnecall (line 273) | fetchOnecall (data) {
method convertWeatherType (line 374) | convertWeatherType (weatherType) {
method getParams (line 405) | getParams () {
FILE: modules/default/weather/providers/overrideWrapper.js
method init (line 24) | init (baseProvider) {
method setConfig (line 36) | setConfig (config) {
method start (line 39) | start () {
method fetchCurrentWeather (line 42) | fetchCurrentWeather () {
method fetchWeatherForecast (line 45) | fetchWeatherForecast () {
method fetchWeatherHourly (line 48) | fetchWeatherHourly () {
method weatherForecast (line 51) | weatherForecast () {
method weatherHourly (line 54) | weatherHourly () {
method fetchedLocation (line 57) | fetchedLocation () {
method setWeatherForecast (line 60) | setWeatherForecast (weatherForecastArray) {
method setWeatherHourly (line 63) | setWeatherHourly (weatherHourlyArray) {
method setFetchedLocation (line 66) | setFetchedLocation (name) {
method updateAvailable (line 69) | updateAvailable () {
method fetchData (line 72) | async fetchData (url, type = "json", requestHeaders = undefined, expecte...
method currentWeather (line 82) | currentWeather () {
method setCurrentWeather (line 92) | setCurrentWeather (currentWeatherObject) {
method notificationReceived (line 104) | notificationReceived (payload) {
FILE: modules/default/weather/providers/pirateweather.js
method fetchCurrentWeather (line 25) | async fetchCurrentWeather () {
method fetchWeatherForecast (line 41) | async fetchWeatherForecast () {
method getUrl (line 58) | getUrl () {
method generateWeatherDayFromCurrentWeather (line 63) | generateWeatherDayFromCurrentWeather (currentWeatherData) {
method generateWeatherObjectsFromForecast (line 78) | generateWeatherObjectsFromForecast (forecasts) {
method convertWeatherType (line 112) | convertWeatherType (weatherType) {
FILE: modules/default/weather/providers/smhi.js
method fetchCurrentWeather (line 22) | fetchCurrentWeather () {
method fetchWeatherForecast (line 38) | fetchWeatherForecast () {
method fetchWeatherHourly (line 53) | fetchWeatherHourly () {
method setConfig (line 69) | setConfig (config) {
method getClosestToCurrentTime (line 82) | getClosestToCurrentTime (times) {
method getURL (line 98) | getURL () {
method calculateApparentTemperature (line 113) | calculateApparentTemperature (weatherData) {
method convertWeatherDataToObject (line 130) | convertWeatherDataToObject (weatherData, coordinates) {
method convertWeatherDataGroupedBy (line 178) | convertWeatherDataGroupedBy (allWeatherData, coordinates, groupBy = "day...
method resolveCoordinates (line 227) | resolveCoordinates (data) {
method fillInGaps (line 237) | fillInGaps (data) {
method paramValue (line 259) | paramValue (currentWeatherData, name) {
method convertWeatherType (line 271) | convertWeatherType (input, isDayTime) {
FILE: modules/default/weather/providers/ukmetofficedatahub.js
method getUrl (line 48) | getUrl (forecastType) {
method getHeaders (line 63) | getHeaders () {
method fetchWeather (line 71) | async fetchWeather (url, headers) {
method fetchCurrentWeather (line 79) | fetchCurrentWeather () {
method generateWeatherObjectFromCurrentWeather (line 109) | generateWeatherObjectFromCurrentWeather (currentWeatherData) {
method fetchWeatherForecast (line 154) | fetchWeatherForecast () {
method generateWeatherObjectsFromForecast (line 184) | generateWeatherObjectsFromForecast (forecasts) {
method setFetchedLocation (line 231) | setFetchedLocation (name) {
method convertWeatherType (line 240) | convertWeatherType (weatherType) {
FILE: modules/default/weather/providers/weatherbit.js
method fetchedLocation (line 23) | fetchedLocation () {
method fetchCurrentWeather (line 27) | fetchCurrentWeather () {
method fetchWeatherForecast (line 44) | fetchWeatherForecast () {
method setConfig (line 67) | setConfig (config) {
method getUrl (line 88) | getUrl () {
method generateWeatherDayFromCurrentWeather (line 93) | generateWeatherDayFromCurrentWeather (currentWeatherData) {
method generateWeatherObjectsFromForecast (line 115) | generateWeatherObjectsFromForecast (forecasts) {
method convertWeatherType (line 135) | convertWeatherType (weatherType) {
FILE: modules/default/weather/providers/weatherflow.js
method fetchCurrentWeather (line 22) | fetchCurrentWeather () {
method fetchWeatherForecast (line 49) | fetchWeatherForecast () {
method fetchWeatherHourly (line 89) | fetchWeatherHourly () {
method convertWeatherType (line 120) | convertWeatherType (weatherType) {
method getUrl (line 147) | getUrl () {
FILE: modules/default/weather/providers/weathergov.js
method setConfig (line 39) | setConfig (config) {
method fetchedLocation (line 45) | fetchedLocation () {
method fetchCurrentWeather (line 50) | fetchCurrentWeather () {
method fetchWeatherForecast (line 71) | fetchWeatherForecast () {
method fetchWeatherHourly (line 92) | fetchWeatherHourly () {
method fetchWxGovURLs (line 121) | fetchWxGovURLs (config) {
method generateWeatherObjectsFromHourly (line 167) | generateWeatherObjectsFromHourly (forecasts) {
method generateWeatherObjectFromCurrentWeather (line 206) | generateWeatherObjectFromCurrentWeather (currentWeatherData) {
method generateWeatherObjectsFromForecast (line 236) | generateWeatherObjectsFromForecast (forecasts) {
method fetchForecastDaily (line 243) | fetchForecastDaily (forecasts) {
method convertWeatherType (line 310) | convertWeatherType (weatherType, isDaytime) {
FILE: modules/default/weather/providers/yr.js
method start (line 20) | start () {
method fetchCurrentWeather (line 33) | fetchCurrentWeather () {
method getCurrentWeather (line 45) | async getCurrentWeather () {
method getWeatherData (line 73) | getWeatherData () {
method getWeatherDataFromYrOrCache (line 102) | getWeatherDataFromYrOrCache (resolve, reject) {
method weatherDataIsValid (line 139) | weatherDataIsValid (weatherData) {
method getWeatherDataFromCache (line 148) | getWeatherDataFromCache () {
method getWeatherDataFromYr (line 157) | getWeatherDataFromYr (currentDataFetchedAt) {
method getConfigOptions (line 179) | getConfigOptions () {
method getForecastUrl (line 195) | getForecastUrl () {
method cacheWeatherData (line 212) | cacheWeatherData (weatherData) {
method getStellarData (line 216) | getStellarData () {
method getStellarDataFromYrOrCache (line 245) | getStellarDataFromYrOrCache (resolve, reject) {
method getStellarDataFromCache (line 303) | getStellarDataFromCache () {
method getStellarDataFromYr (line 312) | getStellarDataFromYr (date, days = 1) {
method getStellarDataUrl (line 325) | getStellarDataUrl (date, days) {
method cacheStellarData (line 356) | cacheStellarData (data) {
method getWeatherDataFrom (line 360) | getWeatherDataFrom (forecast, stellarData, units) {
method convertWeatherType (line 381) | convertWeatherType (weatherType, weatherTime) {
method getForecastForXHoursFrom (line 473) | getForecastForXHoursFrom (weather) {
method fetchWeatherHourly (line 501) | fetchWeatherHourly () {
method getWeatherForecast (line 513) | async getWeatherForecast (type) {
method getHourlyForecastFrom (line 539) | getHourlyForecastFrom (weatherData) {
method getDailyForecastFrom (line 562) | getDailyForecastFrom (weatherData) {
method fetchWeatherForecast (line 612) | fetchWeatherForecast () {
FILE: modules/default/weather/weather.js
method getStyles (line 55) | getStyles () {
method getScripts (line 60) | getScripts () {
method getHeader (line 65) | getHeader () {
method start (line 75) | start () {
method notificationReceived (line 104) | notificationReceived (notification, payload, sender) {
method getTemplate (line 129) | getTemplate () {
method getTemplateData (line 145) | getTemplateData () {
method updateAvailable (line 165) | updateAvailable () {
method scheduleUpdate (line 192) | scheduleUpdate (delay = null) {
method roundValue (line 216) | roundValue (temperature) {
method addFilters (line 222) | addFilters () {
FILE: modules/default/weather/weatherobject.js
class WeatherObject (line 6) | class WeatherObject {
method constructor (line 11) | constructor () {
method cardinalWindDirection (line 28) | cardinalWindDirection () {
method nextSunAction (line 71) | nextSunAction (date = moment()) {
method feelsLike (line 75) | feelsLike () {
method isDayTime (line 86) | isDayTime () {
method updateSunTime (line 98) | updateSunTime (lat, lon) {
method simpleClone (line 112) | simpleClone () {
FILE: modules/default/weather/weatherprovider.js
method init (line 26) | init (config) {
method setConfig (line 32) | setConfig (config) {
method start (line 38) | start () {
method fetchCurrentWeather (line 44) | fetchCurrentWeather () {
method fetchWeatherForecast (line 50) | fetchWeatherForecast () {
method fetchWeatherHourly (line 56) | fetchWeatherHourly () {
method currentWeather (line 61) | currentWeather () {
method weatherForecast (line 66) | weatherForecast () {
method weatherHourly (line 71) | weatherHourly () {
method fetchedLocation (line 76) | fetchedLocation () {
method setCurrentWeather (line 81) | setCurrentWeather (currentWeatherObject) {
method setWeatherForecast (line 87) | setWeatherForecast (weatherForecastArray) {
method setWeatherHourly (line 93) | setWeatherHourly (weatherHourlyArray) {
method setFetchedLocation (line 98) | setFetchedLocation (name) {
method updateAvailable (line 103) | updateAvailable () {
method fetchData (line 115) | async fetchData (url, type = "json", requestHeaders = undefined, expecte...
FILE: modules/default/weather/weatherutils.js
method beaufortWindSpeed (line 8) | beaufortWindSpeed (speedInMS) {
method convertPrecipitationUnit (line 27) | convertPrecipitationUnit (value, valueUnit, outputUnit) {
method convertPrecipitationToInch (line 48) | convertPrecipitationToInch (value, valueUnit) {
method convertTemp (line 60) | convertTemp (tempInC, unit) {
method convertTempToMetric (line 69) | convertTempToMetric (tempInF) {
method convertWind (line 80) | convertWind (windInMS, unit) {
method convertWindDirection (line 99) | convertWindDirection (windDirection) {
method convertWindToMetric (line 122) | convertWindToMetric (mph) {
method convertWindToMs (line 126) | convertWindToMs (kmh) {
method calculateFeelsLike (line 137) | calculateFeelsLike (temperature, windSpeed, humidity) {
method convertWeatherObjectToImperial (line 182) | convertWeatherObjectToImperial (weatherObject) {
FILE: serveronly/watcher.js
constant RESTART_DELAY_MS (line 12) | const RESTART_DELAY_MS = 500;
constant PORT_CHECK_MAX_ATTEMPTS (line 13) | const PORT_CHECK_MAX_ATTEMPTS = 20;
constant PORT_CHECK_INTERVAL_MS (line 14) | const PORT_CHECK_INTERVAL_MS = 500;
function getServerConfig (line 27) | function getServerConfig () {
function isPortAvailable (line 50) | function isPortAvailable (port) {
function waitForPort (line 75) | async function waitForPort (port, maxAttempts = PORT_CHECK_MAX_ATTEMPTS) {
function startServer (line 89) | function startServer () {
function notifyClientsToReload (line 121) | function notifyClientsToReload () {
function restartServer (line 148) | async function restartServer (reason) {
function watchFile (line 184) | function watchFile (file) {
FILE: tests/e2e/animateCSS_spec.js
function getComplimentsElement (line 19) | async function getComplimentsElement () {
function waitForAnimationClass (line 31) | async function waitForAnimationClass (cls, { timeout = 6000 } = {}) {
function assertNoAnimationWithin (line 44) | async function assertNoAnimationWithin (ms = 2000) {
function runAnimationTest (line 62) | async function runAnimationTest (animationIn, animationOut) {
FILE: tests/e2e/helpers/global-setup.js
function ensureContext (line 30) | async function ensureContext () {
function openPage (line 55) | async function openPage (url) {
function closeBrowser (line 69) | async function closeBrowser () {
FILE: tests/e2e/translations_spec.js
function createTranslationTestEnvironment (line 12) | function createTranslationTestEnvironment () {
method file (line 133) | file (file) {
FILE: tests/electron/modules/weather_spec.js
constant CURRENT_WEATHER_CONFIG (line 5) | const CURRENT_WEATHER_CONFIG = "tests/configs/modules/weather/currentwea...
constant SUNRISE_DATE (line 6) | const SUNRISE_DATE = "13 Jan 2019 00:30:00 GMT";
constant SUNSET_DATE (line 7) | const SUNSET_DATE = "13 Jan 2019 12:30:00 GMT";
constant SUN_EVENT_SELECTOR (line 8) | const SUN_EVENT_SELECTOR = ".weather .normal.medium span:nth-child(4)";
constant EXPECTED_SUNRISE_TEXT (line 9) | const EXPECTED_SUNRISE_TEXT = "7:00 am";
constant EXPECTED_SUNSET_TEXT (line 10) | const EXPECTED_SUNSET_TEXT = "3:45 pm";
FILE: tests/mocks/testNotification/testNotification.js
method notificationReceived (line 13) | notificationReceived (notification, payload) {
method maketd (line 31) | maketd (row, info) {
method addTableRow (line 41) | addTableRow (table, col1 = null, col2 = null, col3 = null) {
method getDom (line 51) | getDom () {
FILE: tests/unit/classes/translator_spec.js
function createTranslationTestEnvironment (line 11) | function createTranslationTestEnvironment () {
method file (line 148) | file (file) {
FILE: tests/unit/functions/updatenotification_spec.js
function createGitHelper (line 10) | async function createGitHelper (fsStatSyncMockRef, loggerMockRef, execSh...
Condensed preview — 382 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,201K chars).
[
{
"path": ".editorconfig",
"chars": 238,
"preview": "# editorconfig.org\nroot = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 2\nindent_style = space\ninsert_final_n"
},
{
"path": ".gitattributes",
"chars": 1034,
"preview": "# .gitattributes snippet to force users to use same line endings for project.\n# \n# Handle line endings automatically for"
},
{
"path": ".github/CODE_OF_CONDUCT.md",
"chars": 5750,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": ".github/CONTRIBUTING.md",
"chars": 1915,
"preview": "# Contribution Policy for MagicMirror²\n\nThanks for contributing to MagicMirror²!\n\nWe hold our code to standard, and thes"
},
{
"path": ".github/FUNDING.yaml",
"chars": 66,
"preview": "github: MichMich\ncustom: [\"https://magicmirror.builders/#donate\"]\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.yml",
"chars": 5069,
"preview": "name: 🐛 Report a problem\ndescription: Report an issue with MagicMirror² 🚨\ntitle: \"[Bug] {{ brief description }}\"\nlabels:"
},
{
"path": ".github/ISSUE_TEMPLATE/change_request.yml",
"chars": 1382,
"preview": "name: 🔀 Request a change\ndescription: Request a change that is not a bug fix, a feature request or a support request.\nti"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 855,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: 📚 Documentation\n url: https://github.com/MagicMirrorOrg/MagicMir"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.yml",
"chars": 2382,
"preview": "name: 🚀 Feature Request\ndescription: Suggest a new feature for MagicMirror² 💡\ntitle: \"[Feature Request] {{ brief descrip"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 940,
"preview": "Hello and thank you for wanting to contribute to the MagicMirror² project!\n\n**Please make sure that you have followed th"
},
{
"path": ".github/dependabot.yaml",
"chars": 356,
"preview": "version: 2\nupdates:\n - package-ecosystem: \"github-actions\"\n directory: \"/\"\n schedule:\n interval: \"weekly\"\n "
},
{
"path": ".github/workflows/automated-tests.yaml",
"chars": 2324,
"preview": "# This workflow will do a clean install of node dependencies, build the source code and run tests across different versi"
},
{
"path": ".github/workflows/dep-review.yaml",
"chars": 528,
"preview": "# This workflow scans your pull requests for dependency changes, and will raise an error if any vulnerabilities or inval"
},
{
"path": ".github/workflows/electron-rebuild.yaml",
"chars": 811,
"preview": "name: \"Electron Rebuild Testing\"\n\non: [pull_request]\n\njobs:\n rebuild:\n name: Run electron-rebuild\n runs-on: ubunt"
},
{
"path": ".github/workflows/enforce-pullrequest-rules.yaml",
"chars": 856,
"preview": "# This workflow enforces on every pull request that the PR is not based against master,\n# taken from https://github.com/"
},
{
"path": ".github/workflows/release-notes.yaml",
"chars": 755,
"preview": "# This workflow writes a draft release on GitHub named `unreleased` after every push on develop\n\nname: \"Create Release N"
},
{
"path": ".github/workflows/spellcheck.yaml",
"chars": 740,
"preview": "# This workflow will run a spellcheck on the codebase.\n# It runs a few days before each release. At 00:00 on day-of-mont"
},
{
"path": ".github/workflows/stale.yaml",
"chars": 764,
"preview": "name: \"Close stale issues and PRs\"\n\non:\n workflow_dispatch: # needed for manually running this workflow\n schedule:\n "
},
{
"path": ".gitignore",
"chars": 1221,
"preview": "# Various Node ignoramuses.\nlogs\n*.log\nnpm-debug.log*\npids\n*.pid\n*.seed\nlib-cov\ncoverage\n.lock-wscript\nbuild/Release\nnod"
},
{
"path": ".husky/pre-commit",
"chars": 69,
"preview": "#!/bin/sh\n\nif command -v npx &> /dev/null; then\n npx lint-staged\nfi\n"
},
{
"path": ".markdownlint.json",
"chars": 119,
"preview": "{\n\t\"line_length\": false,\n\t\"no-duplicate-heading\": false,\n\t\"no-inline-html\": false,\n\t\"no-trailing-punctuation\": false\n}\n"
},
{
"path": ".npmrc",
"chars": 48,
"preview": "engine-strict=true\naudit=false\nloglevel=\"error\"\n"
},
{
"path": ".prettierignore",
"chars": 88,
"preview": "*.js\n*.mjs\n.husky/pre-commit\n.prettierignore\n/config\n/coverage\npackage-lock.json\n**.ics\n"
},
{
"path": "CHANGELOG.md",
"chars": 103963,
"preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
},
{
"path": "Collaboration.md",
"chars": 3343,
"preview": "# Collaboration\n\nThis document describes how collaborators of this repository should work together.\n\n## Pull Requests\n\n-"
},
{
"path": "LICENSE.md",
"chars": 1089,
"preview": "# The MIT License (MIT)\n\nCopyright © 2016-2026 Michael Teeuw\n\nPermission is hereby granted, free of charge, to any perso"
},
{
"path": "README.md",
"chars": 3379,
"preview": "# \n\n<p style=\"text-align: center\">\n "
},
{
"path": "clientonly/index.js",
"chars": 4556,
"preview": "\"use strict\";\n\n// Use separate scope to prevent global scope pollution\n(function () {\n\tconst config = {};\n\n\t/**\n\t * Help"
},
{
"path": "cspell.config.json",
"chars": 5423,
"preview": "{\n\t\"version\": \"0.2\",\n\t\"language\": \"en\",\n\t\"words\": [\n\t\t\"aarch\",\n\t\t\"Adak\",\n\t\t\"Alvinger\",\n\t\t\"Ampio\",\n\t\t\"andrezibaia\",\n\t\t\"an"
},
{
"path": "css/custom.css.sample",
"chars": 757,
"preview": "/* Custom CSS Sample\n *\n * Change color and fonts here.\n *\n * Beware that properties cannot be unitless, so for example "
},
{
"path": "css/font-awesome.css",
"chars": 161,
"preview": "@import url(\"../node_modules/@fortawesome/fontawesome-free/css/all.min.css\");\n@import url(\"../node_modules/@fortawesome/"
},
{
"path": "css/main.css",
"chars": 4147,
"preview": ":root {\n --color-text: #999;\n --color-text-dimmed: #666;\n --color-text-bright: #fff;\n --color-background: #000;\n --"
},
{
"path": "css/roboto.css",
"chars": 26965,
"preview": "/* roboto-cyrillic-ext-100-normal */\n@font-face {\n font-family: Roboto;\n font-style: normal;\n font-display: swap;\n f"
},
{
"path": "eslint.config.mjs",
"chars": 4764,
"preview": "import {defineConfig, globalIgnores} from \"eslint/config\";\nimport globals from \"globals\";\nimport {flatConfigs as importX"
},
{
"path": "index.html",
"chars": 3189,
"preview": "<!doctype html>\n<html>\n <head>\n <title>MagicMirror²</title>\n <meta name=\"google\" content=\"notranslate\" />\n <me"
},
{
"path": "jest.config.js",
"chars": 1132,
"preview": "const aliasMapper = {\n\tlogger: \"<rootDir>/js/logger.js\"\n};\n\nconst config = {\n\tverbose: true,\n\ttestTimeout: 20000,\n\ttestS"
},
{
"path": "js/alias-resolver.js",
"chars": 1115,
"preview": "// Internal alias mapping for default and 3rd party modules.\n// Provides short require identifiers: \"logger\" and \"node_h"
},
{
"path": "js/animateCSS.js",
"chars": 3206,
"preview": "/* enumeration of animations in Array **/\nconst AnimateCSSIn = [\n\t// Attention seekers\n\t\"bounce\",\n\t\"flash\",\n\t\"pulse\",\n\t\""
},
{
"path": "js/app.js",
"chars": 13074,
"preview": "// Load lightweight internal alias resolver\nrequire(\"./alias-resolver\");\n\nconst fs = require(\"node:fs\");\nconst path = re"
},
{
"path": "js/check_config.js",
"chars": 4420,
"preview": "// Ensure internal require aliases (e.g., \"logger\") resolve when this file is run as a standalone script\nrequire(\"./alia"
},
{
"path": "js/class.js",
"chars": 2743,
"preview": "/* global Class, xyz */\n\n/*\n * Simple JavaScript Inheritance\n * By John Resig https://johnresig.com/\n *\n * Inspired by b"
},
{
"path": "js/defaults.js",
"chars": 2342,
"preview": "/* global mmPort */\n\nconst address = \"localhost\";\nlet port = 8080;\nif (typeof mmPort !== \"undefined\") {\n\tport = mmPort;\n"
},
{
"path": "js/deprecated.js",
"chars": 72,
"preview": "module.exports = {\n\tconfigs: [\"kioskmode\"],\n\tclock: [\"secondsColor\"]\n};\n"
},
{
"path": "js/electron.js",
"chars": 6548,
"preview": "\"use strict\";\n\nconst electron = require(\"electron\");\nconst core = require(\"./app\");\nconst Log = require(\"./logger\");\n\n//"
},
{
"path": "js/ip_access_control.js",
"chars": 1888,
"preview": "const ipaddr = require(\"ipaddr.js\");\nconst Log = require(\"logger\");\n\n/**\n * Checks if a client IP matches any entry in t"
},
{
"path": "js/loader.js",
"chars": 8669,
"preview": "/* global defaultModules, vendor */\n\nconst Loader = (function () {\n\n\t/* Create helper variables */\n\n\tconst loadedModuleF"
},
{
"path": "js/logger.js",
"chars": 3834,
"preview": "// This logger is very simple, but needs to be extended.\n(function (root, factory) {\n\tif (typeof exports === \"object\") {"
},
{
"path": "js/main.js",
"chars": 25472,
"preview": "/* global Loader, defaults, Translator, addAnimateCSS, removeAnimateCSS, AnimateCSSIn, AnimateCSSOut, modulePositions, i"
},
{
"path": "js/module.js",
"chars": 14423,
"preview": "/* global Class, cloneObject, Loader, MMSocket, nunjucks, Translator */\n\n/*\n * Module Blueprint.\n * @typedef {Object} Mo"
},
{
"path": "js/module_functions.js",
"chars": 527,
"preview": "/**\n * Schedule the timer for the next update\n * @param {object} timer The timer of the module\n * @param {bigint} interv"
},
{
"path": "js/node_helper.js",
"chars": 3330,
"preview": "const express = require(\"express\");\nconst Log = require(\"logger\");\nconst Class = require(\"./class\");\n\nconst NodeHelper ="
},
{
"path": "js/releasenotes.js",
"chars": 5799,
"preview": "/* eslint no-console: \"off\" */\nconst util = require(\"node:util\");\nconst exec = util.promisify(require(\"node:child_proces"
},
{
"path": "js/server.js",
"chars": 4379,
"preview": "const fs = require(\"node:fs\");\nconst http = require(\"node:http\");\nconst https = require(\"node:https\");\nconst path = requ"
},
{
"path": "js/server_functions.js",
"chars": 6046,
"preview": "const fs = require(\"node:fs\");\nconst path = require(\"node:path\");\nconst Log = require(\"logger\");\n\nconst startUp = new Da"
},
{
"path": "js/socketclient.js",
"chars": 1224,
"preview": "/* global io */\n\nconst MMSocket = function (moduleName) {\n\tif (typeof moduleName !== \"string\") {\n\t\tthrow new Error(\"Plea"
},
{
"path": "js/translator.js",
"chars": 4283,
"preview": "/* global translations */\n\nconst Translator = (function () {\n\n\t/**\n\t * Load a JSON file via fetch.\n\t * @param {string} f"
},
{
"path": "js/utils.js",
"chars": 3426,
"preview": "const os = require(\"node:os\");\nconst fs = require(\"node:fs\");\nconst si = require(\"systeminformation\");\nconst Log = requi"
},
{
"path": "js/vendor.js",
"chars": 612,
"preview": "const vendor = {\n\t\"moment.js\": \"node_modules/moment/min/moment-with-locales.js\",\n\t\"moment-timezone.js\": \"node_modules/mo"
},
{
"path": "jsconfig.json",
"chars": 264,
"preview": "{\n\t// See https://go.microsoft.com/fwlink/?LinkId=759670\n\t// for the documentation about the jsconfig.json format\n\t\"comp"
},
{
"path": "module-types.ts",
"chars": 1130,
"preview": "type ModuleProperties = {\n defaults?: object;\n [key: string]: any;\n start?(): void;\n getScripts?(): string[];\n getS"
},
{
"path": "modules/default/alert/README.md",
"chars": 269,
"preview": "# Module: Alert\n\nThe alert module is one of the default modules of the MagicMirror². This module displays notifications "
},
{
"path": "modules/default/alert/alert.js",
"chars": 3656,
"preview": "/* global NotificationFx */\n\nModule.register(\"alert\", {\n\talerts: {},\n\n\tdefaults: {\n\t\teffect: \"slide\", // scale|slide|gen"
},
{
"path": "modules/default/alert/notificationFx.js",
"chars": 3910,
"preview": "/**\n * Based on work by\n *\n * notificationFx.js v1.0.0\n * https://tympanus.net/codrops/\n *\n * Licensed under the MIT lic"
},
{
"path": "modules/default/alert/styles/center.css",
"chars": 77,
"preview": ".ns-box {\n margin-left: auto;\n margin-right: auto;\n text-align: center;\n}\n"
},
{
"path": "modules/default/alert/styles/left.css",
"chars": 54,
"preview": ".ns-box {\n margin-right: auto;\n text-align: left;\n}\n"
},
{
"path": "modules/default/alert/styles/notificationFx.css",
"chars": 20501,
"preview": "/* Based on work by https://tympanus.net/codrops/licensing/ */\n\n.ns-box {\n background-color: rgb(0 0 0 / 93%);\n paddin"
},
{
"path": "modules/default/alert/styles/right.css",
"chars": 54,
"preview": ".ns-box {\n margin-left: auto;\n text-align: right;\n}\n"
},
{
"path": "modules/default/alert/templates/alert.njk",
"chars": 686,
"preview": "{% if imageUrl or imageFA %}\n {% set imageHeight = imageHeight if imageHeight else \"80px\" %}\n {% if imageUrl %}\n <i"
},
{
"path": "modules/default/alert/templates/notification.njk",
"chars": 286,
"preview": "{% if title %}\n <span class=\"thin dimmed medium\">{{ title if titleType == 'text' else title | safe }}</span>\n{% endif %"
},
{
"path": "modules/default/alert/translations/bg.json",
"chars": 98,
"preview": "{\n\t\"sysTitle\": \"MagicMirror² нотификация\",\n\t\"welcome\": \"Добре дошли, стартирането беше успешно\"\n}\n"
},
{
"path": "modules/default/alert/translations/da.json",
"chars": 103,
"preview": "{\n\t\"sysTitle\": \"MagicMirror² Notifikation\",\n\t\"welcome\": \"Velkommen, modulet er succesfuldt startet!\"\n}\n"
},
{
"path": "modules/default/alert/translations/de.json",
"chars": 99,
"preview": "{\n\t\"sysTitle\": \"MagicMirror² Benachrichtigung\",\n\t\"welcome\": \"Willkommen, Start war erfolgreich!\"\n}\n"
},
{
"path": "modules/default/alert/translations/el.json",
"chars": 98,
"preview": "{\n\t\"sysTitle\": \"MagicMirror² Ειδοποίηση\",\n\t\"welcome\": \"Καλώς ήρθατε, η εκκίνηση ήταν επιτυχής!\"\n}\n"
},
{
"path": "modules/default/alert/translations/en.json",
"chars": 91,
"preview": "{\n\t\"sysTitle\": \"MagicMirror² Notification\",\n\t\"welcome\": \"Welcome, start was successful!\"\n}\n"
},
{
"path": "modules/default/alert/translations/eo.json",
"chars": 80,
"preview": "{\n\t\"sysTitle\": \"MagicMirror²-sciigo\",\n\t\"welcome\": \"Bonvenon, lanĉo sukcesis!\"\n}\n"
},
{
"path": "modules/default/alert/translations/es.json",
"chars": 102,
"preview": "{\n\t\"sysTitle\": \"MagicMirror² Notificaciones\",\n\t\"welcome\": \"Bienvenido, ¡se iniciado correctamente!\"\n}\n"
},
{
"path": "modules/default/alert/translations/fr.json",
"chars": 101,
"preview": "{\n\t\"sysTitle\": \"MagicMirror² Notification\",\n\t\"welcome\": \"Bienvenue, le démarrage a été un succès!\"\n}\n"
},
{
"path": "modules/default/alert/translations/hu.json",
"chars": 86,
"preview": "{\n\t\"sysTitle\": \"MagicMirror² értesítés\",\n\t\"welcome\": \"Üdvözöljük, indulás sikeres!\"\n}\n"
},
{
"path": "modules/default/alert/translations/nl.json",
"chars": 86,
"preview": "{\n\t\"sysTitle\": \"MagicMirror² Notificatie\",\n\t\"welcome\": \"Welkom, Succesvol gestart!\"\n}\n"
},
{
"path": "modules/default/alert/translations/pt-br.json",
"chars": 104,
"preview": "{\n\t\"sysTitle\": \"Notificação do MagicMirror²\",\n\t\"welcome\": \"Bem-vindo, o sistema iniciou com sucesso!\"\n}\n"
},
{
"path": "modules/default/alert/translations/pt.json",
"chars": 104,
"preview": "{\n\t\"sysTitle\": \"Notificação do MagicMirror²\",\n\t\"welcome\": \"Bem-vindo, o sistema iniciou com sucesso!\"\n}\n"
},
{
"path": "modules/default/alert/translations/ru.json",
"chars": 97,
"preview": "{\n\t\"sysTitle\": \"MagicMirror² Уведомление\",\n\t\"welcome\": \"Добро пожаловать, старт был успешным!\"\n}\n"
},
{
"path": "modules/default/alert/translations/th.json",
"chars": 96,
"preview": "{\n\t\"sysTitle\": \"การแจ้งเตือน MagicMirror²\",\n\t\"welcome\": \"ยินดีต้อนรับ การเริ่มต้นสำเร็จแล้ว!\"\n}\n"
},
{
"path": "modules/default/calendar/README.md",
"chars": 318,
"preview": "# Module: Calendar\n\nThe `calendar` module is one of the default modules of the MagicMirror².\nThis module displays events"
},
{
"path": "modules/default/calendar/calendar.css",
"chars": 208,
"preview": ".calendar .symbol {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n gap: 5px;\n}\n\n.calendar .title"
},
{
"path": "modules/default/calendar/calendar.js",
"chars": 35764,
"preview": "/* global CalendarUtils */\n\nModule.register(\"calendar\", {\n\t// Define module defaults\n\tdefaults: {\n\t\tmaximumEntries: 10, "
},
{
"path": "modules/default/calendar/calendarfetcher.js",
"chars": 7317,
"preview": "const https = require(\"node:https\");\nconst ical = require(\"node-ical\");\nconst Log = require(\"logger\");\nconst CalendarFet"
},
{
"path": "modules/default/calendar/calendarfetcherutils.js",
"chars": 13849,
"preview": "/**\n * @external Moment\n */\nconst moment = require(\"moment-timezone\");\n\nconst Log = require(\"logger\");\n\nconst CalendarFe"
},
{
"path": "modules/default/calendar/calendarutils.js",
"chars": 4511,
"preview": "const CalendarUtils = {\n\n\t/**\n\t * Capitalize the first letter of a string\n\t * @param {string} string The string to capit"
},
{
"path": "modules/default/calendar/debug.js",
"chars": 1263,
"preview": "/*\n * CalendarFetcher Tester\n * use this script with `node debug.js` to test the fetcher without the need\n * of starting"
},
{
"path": "modules/default/calendar/node_helper.js",
"chars": 4100,
"preview": "const zlib = require(\"node:zlib\");\nconst NodeHelper = require(\"node_helper\");\nconst Log = require(\"logger\");\nconst Calen"
},
{
"path": "modules/default/calendar/windowsZones.json",
"chars": 14649,
"preview": "{\n\t\"Dateline Standard Time\": { \"iana\": [\"Etc/GMT+12\"] },\n\t\"UTC-11\": { \"iana\": [\"Etc/GMT+11\"] },\n\t\"Aleutian Standard Time"
},
{
"path": "modules/default/clock/README.md",
"chars": 306,
"preview": "# Module: Clock\n\nThe `clock` module is one of the default modules of the MagicMirror².\nThis module displays the current "
},
{
"path": "modules/default/clock/clock.js",
"chars": 12297,
"preview": "/* global SunCalc, formatTime */\n\nModule.register(\"clock\", {\n\t// Module config defaults.\n\tdefaults: {\n\t\tdisplayType: \"di"
},
{
"path": "modules/default/clock/clock_styles.css",
"chars": 2188,
"preview": ".clock-grid {\n display: inline-flex;\n gap: 15px;\n}\n\n.clock-grid-left {\n flex-direction: row;\n}\n\n.clock-grid-right {\n "
},
{
"path": "modules/default/compliments/README.md",
"chars": 276,
"preview": "# Module: Compliments\n\nThe `compliments` module is one of the default modules of the MagicMirror².\nThis module displays "
},
{
"path": "modules/default/compliments/compliments.js",
"chars": 10447,
"preview": "/* global Cron */\n\nModule.register(\"compliments\", {\n\t// Module config defaults.\n\tdefaults: {\n\t\tcompliments: {\n\t\t\tanytime"
},
{
"path": "modules/default/defaultmodules.js",
"chars": 398,
"preview": "/*\n * Default Modules List\n * Modules listed below can be loaded without the 'default/' prefix. Omitting the default fol"
},
{
"path": "modules/default/helloworld/README.md",
"chars": 291,
"preview": "# Module: Hello World\n\nThe `helloworld` module is one of the default modules of the MagicMirror². It is a simple way to "
},
{
"path": "modules/default/helloworld/helloworld.js",
"chars": 201,
"preview": "Module.register(\"helloworld\", {\n\t// Default module config.\n\tdefaults: {\n\t\ttext: \"Hello World!\"\n\t},\n\n\tgetTemplate () {\n\t\t"
},
{
"path": "modules/default/helloworld/helloworld.njk",
"chars": 161,
"preview": "<!--\n\tUse ` | safe` to allow html tags within the text string.\n\thttps://mozilla.github.io/nunjucks/templating.html#autoe"
},
{
"path": "modules/default/newsfeed/README.md",
"chars": 442,
"preview": "# Module: News Feed\n\nThe `newsfeed` module is one of the default modules of the MagicMirror².\nThis module displays news "
},
{
"path": "modules/default/newsfeed/fullarticle.njk",
"chars": 78,
"preview": "<div>\n <iframe class=\"newsfeed-fullarticle\" src=\"{{ url }}\"></iframe>\n</div>\n"
},
{
"path": "modules/default/newsfeed/newsfeed.css",
"chars": 357,
"preview": "iframe.newsfeed-fullarticle {\n width: 100vw;\n\n /* very large height value to allow scrolling */\n height: 3000px;\n to"
},
{
"path": "modules/default/newsfeed/newsfeed.js",
"chars": 13284,
"preview": "Module.register(\"newsfeed\", {\n\t// Default module config.\n\tdefaults: {\n\t\tfeeds: [\n\t\t\t{\n\t\t\t\ttitle: \"New York Times\",\n\t\t\t\tu"
},
{
"path": "modules/default/newsfeed/newsfeed.njk",
"chars": 3688,
"preview": "{% macro escapeText(text, dangerouslyDisableAutoEscaping=false) %}\n {% if dangerouslyDisableAutoEscaping -%}\n {{ tex"
},
{
"path": "modules/default/newsfeed/newsfeedfetcher.js",
"chars": 4855,
"preview": "const crypto = require(\"node:crypto\");\nconst stream = require(\"node:stream\");\nconst FeedMe = require(\"feedme\");\nconst ic"
},
{
"path": "modules/default/newsfeed/node_helper.js",
"chars": 2328,
"preview": "const NodeHelper = require(\"node_helper\");\nconst Log = require(\"logger\");\nconst NewsfeedFetcher = require(\"./newsfeedfet"
},
{
"path": "modules/default/newsfeed/oldconfig.njk",
"chars": 108,
"preview": "<div class=\"small bright\">{{ \"MODULE_CONFIG_CHANGED\" | translate({MODULE_NAME: \"Newsfeed\"}) | safe }}</div>\n"
},
{
"path": "modules/default/updatenotification/README.md",
"chars": 353,
"preview": "# Module: Update Notification\n\nThe `updatenotification` module is one of the default modules of the MagicMirror².\nThis w"
},
{
"path": "modules/default/updatenotification/git_helper.js",
"chars": 6361,
"preview": "const util = require(\"node:util\");\nconst exec = util.promisify(require(\"node:child_process\").exec);\nconst fs = require(\""
},
{
"path": "modules/default/updatenotification/node_helper.js",
"chars": 3126,
"preview": "const fs = require(\"node:fs\");\nconst path = require(\"node:path\");\nconst NodeHelper = require(\"node_helper\");\n\nconst defa"
},
{
"path": "modules/default/updatenotification/update_helper.js",
"chars": 6836,
"preview": "const Exec = require(\"node:child_process\").exec;\nconst Spawn = require(\"node:child_process\").spawn;\nconst fs = require(\""
},
{
"path": "modules/default/updatenotification/updatenotification.css",
"chars": 67,
"preview": ".module.updatenotification a.difflink {\n text-decoration: none;\n}\n"
},
{
"path": "modules/default/updatenotification/updatenotification.js",
"chars": 3156,
"preview": "Module.register(\"updatenotification\", {\n\tdefaults: {\n\t\tupdateInterval: 10 * 60 * 1000, // every 10 minutes\n\t\trefreshInte"
},
{
"path": "modules/default/updatenotification/updatenotification.njk",
"chars": 1535,
"preview": "{% if not suspended %}\n {% if needRestart %}\n <div class=\"small bright\">\n <i class=\"fas fa-rotate\"></i>\n <"
},
{
"path": "modules/default/utils.js",
"chars": 6250,
"preview": "/**\n * A function to make HTTP requests via the server to avoid CORS-errors.\n * @param {string} url the url to fetch fro"
},
{
"path": "modules/default/weather/README.md",
"chars": 309,
"preview": "# Weather Module\n\nThis module will be configurable to be used as a current weather view, or to show the forecast. This w"
},
{
"path": "modules/default/weather/current.njk",
"chars": 4209,
"preview": "{% macro humidity() %}\n {% if current.humidity %}\n <span class=\"humidity\"\n ><span>{{ current.humidity | decimal"
},
{
"path": "modules/default/weather/forecast.njk",
"chars": 2253,
"preview": "{% if forecast %}\n {% set numSteps = forecast | calcNumSteps %}\n {% set currentStep = 0 %}\n <table class=\"{{ config.t"
},
{
"path": "modules/default/weather/hourly.njk",
"chars": 2124,
"preview": "{% if hourly %}\n {% set numSteps = hourly | calcNumEntries %}\n {% set currentStep = 0 %}\n <table class=\"{{ config.tab"
},
{
"path": "modules/default/weather/providers/README.md",
"chars": 223,
"preview": "# Weather Module Weather Provider Development Documentation\n\nFor how to develop your own weather provider, please check "
},
{
"path": "modules/default/weather/providers/envcanada.js",
"chars": 25643,
"preview": "/* global WeatherProvider, WeatherObject, WeatherUtils */\n\n/*\n * This class is a provider for Environment Canada MSC Dat"
},
{
"path": "modules/default/weather/providers/openmeteo.js",
"chars": 19750,
"preview": "/* global WeatherProvider, WeatherObject */\n\n/*\n * This class is a provider for Open-Meteo,\n * see https://open-meteo.co"
},
{
"path": "modules/default/weather/providers/openweathermap.js",
"chars": 14162,
"preview": "/* global WeatherProvider, WeatherObject */\n\n/*\n * This class is a provider for Openweathermap,\n * see https://openweath"
},
{
"path": "modules/default/weather/providers/overrideWrapper.js",
"chars": 3893,
"preview": "/* global Class, WeatherObject */\n\n/*\n * Wrapper class to enable overrides of currentOverrideWeatherObject.\n *\n * Sits b"
},
{
"path": "modules/default/weather/providers/pirateweather.js",
"chars": 3815,
"preview": "/* global WeatherProvider, WeatherObject */\n\n/*\n * This class is a provider for Pirate Weather, it is a replacement for "
},
{
"path": "modules/default/weather/providers/smhi.js",
"chars": 12341,
"preview": "/* global WeatherProvider, WeatherObject */\n\n/*\n * This class is a provider for SMHI (Sweden only).\n * Metric system is "
},
{
"path": "modules/default/weather/providers/ukmetofficedatahub.js",
"chars": 10715,
"preview": "/* global WeatherProvider, WeatherObject */\n\n/*\n * This class is a provider for UK Met Office Data Hub (the replacement "
},
{
"path": "modules/default/weather/providers/weatherbit.js",
"chars": 5874,
"preview": "/* global WeatherProvider, WeatherObject */\n\n/*\n * This class is a provider for Weatherbit,\n * see https://www.weatherbi"
},
{
"path": "modules/default/weather/providers/weatherflow.js",
"chars": 5493,
"preview": "/* global WeatherProvider, WeatherObject, WeatherUtils */\n\n/*\n * This class is a provider for Weatherflow.\n * Note that "
},
{
"path": "modules/default/weather/providers/weathergov.js",
"chars": 12213,
"preview": "/* global WeatherProvider, WeatherObject, WeatherUtils */\n\n/*\n * Provider: weather.gov\n * https://weather-gov.github.io/"
},
{
"path": "modules/default/weather/providers/yr.js",
"chars": 22976,
"preview": "/* global WeatherProvider, WeatherObject */\n\n/*\n * This class is a provider for Yr.no, a norwegian weather service.\n * T"
},
{
"path": "modules/default/weather/weather.css",
"chars": 710,
"preview": ".weather .weathericon,\n.weather .fa-home {\n font-size: 75%;\n}\n\n.weather .humidity-icon {\n padding-right: 4px;\n}\n\n.weat"
},
{
"path": "modules/default/weather/weather.js",
"chars": 10176,
"preview": "/* global WeatherProvider, WeatherUtils, formatTime */\n\nModule.register(\"weather\", {\n\t// Default module config.\n\tdefault"
},
{
"path": "modules/default/weather/weatherobject.js",
"chars": 4182,
"preview": "/* global SunCalc, WeatherUtils */\n\n/**\n * @external Moment\n */\nclass WeatherObject {\n\n\t/**\n\t * Constructor for a Weathe"
},
{
"path": "modules/default/weather/weatherprovider.js",
"chars": 5618,
"preview": "/* global Class, performWebRequest, OverrideWrapper */\n\n// This class is the blueprint for a weather provider.\nconst Wea"
},
{
"path": "modules/default/weather/weatherutils.js",
"chars": 6982,
"preview": "const WeatherUtils = {\n\n\t/**\n\t * Convert wind (from m/s) to beaufort scale\n\t * @param {number} speedInMS the windspeed y"
},
{
"path": "package.json",
"chars": 4293,
"preview": "{\n\t\"name\": \"magicmirror\",\n\t\"version\": \"2.34.0\",\n\t\"description\": \"The open source modular smart mirror platform.\",\n\t\"keyw"
},
{
"path": "prettier.config.mjs",
"chars": 277,
"preview": "const config = {\n\tplugins: [\"prettier-plugin-jinja-template\"],\n\toverrides: [\n\t\t{\n\t\t\tfiles: \"*.md\",\n\t\t\toptions: {\n\t\t\t\tpar"
},
{
"path": "serveronly/index.js",
"chars": 361,
"preview": "const app = require(\"../js/app\");\nconst Log = require(\"../js/logger\");\n\napp.start().then((config) => {\n\tconst bindAddres"
},
{
"path": "serveronly/watcher.js",
"chars": 6817,
"preview": "// Load lightweight internal alias resolver to enable require(\"logger\")\nrequire(\"../js/alias-resolver\");\n\nconst { spawn "
},
{
"path": "stylelint.config.mjs",
"chars": 143,
"preview": "const config = {\n\textends: [\"stylelint-config-standard\", \"stylelint-prettier/recommended\"],\n\troot: true,\n\trules: {}\n};\n\n"
},
{
"path": "tests/configs/customregions.js",
"chars": 571,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\tmodules:\n\t\t// Using exotic content. This is why don't accept go t"
},
{
"path": "tests/configs/default.js",
"chars": 339,
"preview": "if (typeof exports === \"object\") {\n\t// running in nodejs (not in browser)\n\texports.configFactory = (options) => {\n\t\tretu"
},
{
"path": "tests/configs/empty_ipWhiteList.js",
"chars": 243,
"preview": "let config = require(`${process.cwd()}/tests/configs/default.js`).configFactory({\n\tipWhitelist: [],\n\tport: 8282\n});\n\n/**"
},
{
"path": "tests/configs/modules/alert/welcome_false.js",
"chars": 299,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"alert\",\n\t\t\tconfig: {\n\t\t\t\tdisplay_time:"
},
{
"path": "tests/configs/modules/alert/welcome_string.js",
"chars": 319,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"alert\",\n\t\t\tconfig: {\n\t\t\t\tdisplay_time:"
},
{
"path": "tests/configs/modules/alert/welcome_true.js",
"chars": 298,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"alert\",\n\t\t\tconfig: {\n\t\t\t\tdisplay_time:"
},
{
"path": "tests/configs/modules/calendar/3_move_first_allday_repeating_event.js",
"chars": 695,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\n\ttimeFormat: 24,\n\tunits: \"metric\",\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"ca"
},
{
"path": "tests/configs/modules/calendar/auth-default.js",
"chars": 506,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/bad_rrule.js",
"chars": 444,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\tlogLevel: [\"INFO\", \"LOG\", \"WARN\", \"ERROR\", \"DEBU"
},
{
"path": "tests/configs/modules/calendar/basic-auth.js",
"chars": 530,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/berlin_end_of_day_repeating.js",
"chars": 632,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\n\ttimeFormat: 24,\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/berlin_multi.js",
"chars": 629,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\n\ttimeFormat: 24,\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/berlin_whole_day_event_moved_over_dst_change.js",
"chars": 647,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\n\ttimeFormat: 24,\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/changed-port.js",
"chars": 506,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/chicago-looking-at-ny-recurring.js",
"chars": 623,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\n\ttimeFormat: 24,\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/chicago_late_in_timezone.js",
"chars": 692,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\n\ttimeFormat: 24,\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/countCalendarEvents.js",
"chars": 733,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\tforeignModulesDir: \"tests/mocks\",\n\tmodules: [\n\t\t"
},
{
"path": "tests/configs/modules/calendar/custom.js",
"chars": 731,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/default.js",
"chars": 429,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/diff_tz_start_end.js",
"chars": 662,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\n\ttimeFormat: 24,\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/end_of_day_berlin_moved.js",
"chars": 632,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\n\ttimeFormat: 24,\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_display_end.js",
"chars": 664,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\n\ttimeFormat: 24,\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/event_with_time_over_multiple_days_non_repeating_no_display_end.js",
"chars": 700,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\n\ttimeFormat: 24,\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/exdate_and_recurrence_together.js",
"chars": 639,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\n\ttimeFormat: 24,\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/exdate_la_at_midnight_dst.js",
"chars": 678,
"preview": "/*\n * MagicMirror² Test calendar exdate\n *\n * By jkriegshauser\n * MIT Licensed.\n *\n * See issue #3250\n * See tests/elect"
},
{
"path": "tests/configs/modules/calendar/exdate_la_at_midnight_std.js",
"chars": 678,
"preview": "/*\n * MagicMirror² Test calendar exdate\n *\n * By jkriegshauser\n * MIT Licensed.\n *\n * See issue #3250\n * See tests/elect"
},
{
"path": "tests/configs/modules/calendar/exdate_la_before_midnight.js",
"chars": 678,
"preview": "/*\n * MagicMirror² Test calendar exdate\n *\n * By jkriegshauser\n * MIT Licensed.\n *\n * See issue #3250\n * See tests/elect"
},
{
"path": "tests/configs/modules/calendar/exdate_syd_at_midnight_dst.js",
"chars": 679,
"preview": "/*\n * MagicMirror² Test calendar exdate\n *\n * By jkriegshauser\n * MIT Licensed.\n *\n * See issue #3250\n * See tests/elect"
},
{
"path": "tests/configs/modules/calendar/exdate_syd_at_midnight_std.js",
"chars": 679,
"preview": "/*\n * MagicMirror² Test calendar exdate\n *\n * By jkriegshauser\n * MIT Licensed.\n *\n * See issue #3250\n * See tests/elect"
},
{
"path": "tests/configs/modules/calendar/exdate_syd_before_midnight.js",
"chars": 679,
"preview": "/*\n * MagicMirror² Test calendar exdate\n *\n * By jkriegshauser\n * MIT Licensed.\n *\n * See issue #3250\n * See tests/elect"
},
{
"path": "tests/configs/modules/calendar/fail-basic-auth.js",
"chars": 536,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/fullday_event_over_multiple_days_nonrepeating.js",
"chars": 625,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\n\ttimeFormat: 24,\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/fullday_until.js",
"chars": 474,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/germany_at_end_of_day_repeating.js",
"chars": 621,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tposit"
},
{
"path": "tests/configs/modules/calendar/long-fullday-event.js",
"chars": 601,
"preview": "/*\n * MagicMirror² Test config for fullday calendar entries over multiple days\n *\n * By Paranoid93 https://github.com/Pa"
},
{
"path": "tests/configs/modules/calendar/old-basic-auth.js",
"chars": 482,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/recurring.js",
"chars": 463,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/rrule_until.js",
"chars": 516,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/show-duplicates-in-calendar.js",
"chars": 701,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/single-fullday-event.js",
"chars": 592,
"preview": "/*\n * MagicMirror² Test config for fullday calendar entries over multiple days\n *\n * By Paranoid93 https://github.com/Pa"
},
{
"path": "tests/configs/modules/calendar/sliceMultiDayEvents.js",
"chars": 511,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tpositi"
},
{
"path": "tests/configs/modules/calendar/symboltest.js",
"chars": 459,
"preview": "\nlet config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"calendar\",\n\t\t\tposit"
},
{
"path": "tests/configs/modules/clock/clock_12hr.js",
"chars": 274,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"clock\",\n\t\t\tposition:"
},
{
"path": "tests/configs/modules/clock/clock_24hr.js",
"chars": 256,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"clock\",\n\t\t\tposition: \"middle_center\"\n\t"
},
{
"path": "tests/configs/modules/clock/clock_analog.js",
"chars": 350,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"clock\",\n\t\t\tposition: \"middle_center\",\n"
},
{
"path": "tests/configs/modules/clock/clock_displaySeconds_false.js",
"chars": 319,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"clock\",\n\t\t\tposition:"
},
{
"path": "tests/configs/modules/clock/clock_showDateAnalog.js",
"chars": 359,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"clock\",\n\t\t\tposition:"
},
{
"path": "tests/configs/modules/clock/clock_showPeriodUpper.js",
"chars": 319,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"clock\",\n\t\t\tposition:"
},
{
"path": "tests/configs/modules/clock/clock_showSunMoon.js",
"chars": 341,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"clock\",\n\t\t\tposition:"
},
{
"path": "tests/configs/modules/clock/clock_showSunNoEvent.js",
"chars": 330,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"clock\",\n\t\t\tposition:"
},
{
"path": "tests/configs/modules/clock/clock_showTime.js",
"chars": 313,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"clock\",\n\t\t\tposition:"
},
{
"path": "tests/configs/modules/clock/clock_showWeek.js",
"chars": 312,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"clock\",\n\t\t\tposition:"
},
{
"path": "tests/configs/modules/clock/clock_showWeek_short.js",
"chars": 315,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"clock\",\n\t\t\tposition:"
},
{
"path": "tests/configs/modules/clock/de/clock_showWeek.js",
"chars": 329,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\tlanguage: \"de\",\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"clo"
},
{
"path": "tests/configs/modules/clock/de/clock_showWeek_short.js",
"chars": 332,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\tlanguage: \"de\",\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"clo"
},
{
"path": "tests/configs/modules/clock/es/clock_12hr.js",
"chars": 291,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\tlanguage: \"es\",\n\ttimeFormat: 12,\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"clo"
},
{
"path": "tests/configs/modules/clock/es/clock_24hr.js",
"chars": 274,
"preview": "let config = {\n\taddress: \"0.0.0.0\",\n\tipWhitelist: [],\n\tlanguage: \"es\",\n\n\tmodules: [\n\t\t{\n\t\t\tmodule: \"clock\",\n\t\t\tposition:"
}
]
// ... and 182 more files (download for full content)
About this extraction
This page contains the full source code of the MagicMirrorOrg/MagicMirror GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 382 files (1.0 MB), approximately 316.4k tokens, and a symbol index with 465 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.