Merge branch 'MichMich/develop'

This commit is contained in:
Thomas Bachmann 2018-01-29 21:37:25 +01:00
commit 790249dd1a
19 changed files with 5413 additions and 303 deletions

View File

@ -12,5 +12,11 @@
"browser": true,
"node": true,
"es6": true
}
},
"parserOptions": {
"sourceType": "module",
"ecmaFeatures": {
"globalReturn": true
}
}
}

View File

@ -1,10 +1,8 @@
language: node_js
node_js:
- "8"
- "7"
- "6"
- "5.1"
before_script:
- yarn danger ci
- npm install grunt-cli -g
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"

View File

@ -2,16 +2,32 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [2.3.0] - Unreleased
### Added
- 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.
### Fixed
- News article in fullscreen (iframe) is now shown in front of modules.
*This release is scheduled to be released on 2018-04-01.*
## [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

130
README.md
View File

@ -16,45 +16,80 @@ MagicMirror² focuses on a modular plugin system and uses [Electron](http://elec
## Table Of Contents
- [Usage](#usage)
- [Installation](#installation)
- [Raspberry Pi](#raspberrypi)
- [General](#general)
- [Server Only](#server-only)
- [Client Only](#client-only)
- [Docker](#docker)
- [Configuration](#configuration)
- [Modules](#modules)
- [Updating](#updating)
- [Known Issues](#known-issues)
- [Community](#community)
- [Contributing Guidelines](#contributing-guidelines)
## Usage
## Installation
### Raspberry Pi Support
Electron, the app wrapper around MagicMirror², only supports the Raspberry Pi 2 & 3. The Raspberry Pi 1 is currently **not** supported. If you want to run this on a Raspberry Pi 1, use the [server only](#server-only) feature and setup a fullscreen browser yourself.
### Raspberry Pi
### Automatic Installer (Raspberry Pi Only!)
*Electron*, the app wrapper around MagicMirror², only supports the Raspberry Pi 2/3. The Raspberry Pi 0/1 is currently **not** supported. If you want to run this on a Raspberry Pi 1, use the [server only](#server-only) feature and setup a fullscreen browser yourself. (Yes, people have managed to run MM² also on a Pi0, so if you insist, search in the forums.)
#### Automatic Installation (Raspberry Pi only!)
Execute the following command on your Raspberry Pi to install MagicMirror²:
````
```bash
bash -c "$(curl -sL https://raw.githubusercontent.com/MichMich/MagicMirror/master/installers/raspberry.sh)"
````
```
### Manual Installation
#### Manual Installation
1. Download and install the latest Node.js version.
1. Download and install the latest *Node.js* version.
2. Clone the repository and check out the master branch: `git clone https://github.com/MichMich/MagicMirror`
3. Enter the repository: `cd ~/MagicMirror`
4. Install and run the app: `npm install && npm start`
4. Install and run the app with: `npm install && npm start` \
For **Server Only** use: `npm install && node serveronly` .
**Important:** `npm start` does **not** work via SSH, use `DISPLAY=:0 nohup npm start &` instead. This starts the mirror on the remote display.
**Note:** if you want to debug on Raspberry Pi you can use `npm start dev` which will start the MagicMirror app with Dev Tools enabled.
**:warning: Important!**
- **The installation step for `npm install` will take a very long time**, often with little or no terminal response! \
For the RPi3 this is **~10** minutes and for the Rpi2 **~25** minutes. \
Do not interrupt or you risk getting a :broken_heart: by Raspberry Jam.
Also note that:
- `npm start` does **not** work via SSH. But you can use `DISPLAY=:0 nohup npm start &` instead. \
This starts the mirror on the remote display.
- If you want to debug on Raspberry Pi you can use `npm start dev` which will start MM with *Dev Tools* enabled.
- To access toolbar menu when in mirror mode, hit `ALT` key.
- To toggle the (web) `Developer Tools` from mirror mode, use `CTRL-SHIFT-I` or `ALT` and select `View`.
### Server Only
In some cases, you want to start the application without an actual app window. In this case, you can start MagicMirror² in server only mode by manually running `node serveronly` or using Docker. This will start the server, after which you can open the application in your browser of choice. Detailed description below.
**Important:** Make sure that you whitelist the interface/ip (`ipWhitelist`) in the server config where you want the client to connect to, otherwise it will not be allowed to connect to the server. You also need to set the local host `address` field to `0.0.0.0` in order for the RPi to listen on all interfaces and not only `localhost` (default).
```javascript
var config = {
address: "0.0.0.0", // default is "localhost"
port: 8080, // default
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:172.17.0.1"], // default -- need to add your IP here
...
};
```
### Client Only
When you have a server running remotely and want to connect a standalone client to this instance, you can manually run `node clientonly --address 192.168.1.5 --port 8080`. (Specify the ip address and port number of the server)
**Important:** Make sure that you whitelist the interface/ip in the server config where you want the client to connect to, otherwise it will not be allowed to connect to the server
This is when you already have a server running remotely and want your RPi to connect as a standalone client to this instance, to show the MM from the server. Then from your RPi, you run it with: `node clientonly --address 192.168.1.5 --port 8080`. (Specify the ip address and port number of the server)
#### Docker
### Docker
MagicMirror² in server only mode can be deployed using [Docker](https://docker.com). After a successful [Docker installation](https://docs.docker.com/engine/installation/) you just need to execute the following command in the shell:
@ -75,55 +110,34 @@ docker run -d \
You may need to add your Docker Host IP to your `ipWhitelist` option. If you have some issues setting up this configuration, check [this forum post](https://forum.magicmirror.builders/topic/1326/ipwhitelist-howto).
```javascript
var config = {
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:172.17.0.1"]
};
```
If you want to run the server on a raspberry pi, use the `raspberry` tag. (bastilimbach/docker-magicmirror:raspberry)
#### Manual
1. Download and install the latest Node.js version.
2. Clone the repository and check out the master branch: `git clone https://github.com/MichMich/MagicMirror`
3. Enter the repository: `cd ~/MagicMirror`
4. Install and run the app: `npm install && node serveronly`
### Raspberry Configuration & Auto Start.
The following wiki links are helpful in the configuration of your MagicMirror² operating system:
- [Configuring the Raspberry Pi](https://github.com/MichMich/MagicMirror/wiki/Configuring-the-Raspberry-Pi)
- [Auto Starting MagicMirror](https://github.com/MichMich/MagicMirror/wiki/Auto-Starting-MagicMirror)
### Updating your MagicMirror²
If you want to update your MagicMirror² to the latest version, use your terminal to go to your Magic Mirror folder and type the following command:
```bash
git pull && npm install
```
If you changed nothing more than the config or the modules, this should work without any problems.
Type `git status` to see your changes, if there are any, you can reset them with `git reset --hard`. After that, git pull should be possible.
## Configuration
1. Duplicate `config/config.js.sample` to `config/config.js`. **Note:** If you used the installer script. This step is already done for you.
2. Modify your required settings.
### Raspberry Specific
The following wiki links are helpful for the initial configuration of your MagicMirror² operating system:
- [Configuring the Raspberry Pi](https://github.com/MichMich/MagicMirror/wiki/Configuring-the-Raspberry-Pi)
- [Auto Starting MagicMirror](https://github.com/MichMich/MagicMirror/wiki/Auto-Starting-MagicMirror)
### General
1. Copy `config/config.js.sample` to `config/config.js`. \
**Note:** If you used the installer script. This step is already done for you.
2. Modify your required settings. \
Note: You'll can check your configuration running `npm run config:check`.
Note: You'll can check your configuration running the follow command:
```bash
npm run config:check
```
The following properties can be configured:
| **Option** | **Description** |
| --- | --- |
| `port` | The port on which the MagicMirror² server will run on. The default value is `8080`. |
| `address` | The ip address the accept connections. The default open bind `localhost`. Example config: `192.168.10.100`. |
| `ipWhitelist` | The list of IPs from which you are allowed to access the MagicMirror². The default value is `["127.0.0.1", "::ffff:127.0.0.1", "::1"]`. It is possible to specify IPs with subnet masks (`["127.0.0.1", "127.0.0.1/24"]`) or define ip ranges (`["127.0.0.1", ["192.168.0.1", "192.168.0.100"]]`). Set `[]` to allow all IP addresses. For more information about how configure this directive see the [follow post ipWhitelist HowTo](https://forum.magicmirror.builders/topic/1326/ipwhitelist-howto) |
| `address` | The *interface* ip address on which to accept connections. The default is `localhost`, which would prevent exposing the built-in webserver to machines on the local network. To expose it to other machines, use: `0.0.0.0`. |
| `ipWhitelist` | The list of IPs from which you are allowed to access the MagicMirror². The default value is `["127.0.0.1", "::ffff:127.0.0.1", "::1"]`, which is from `localhost` only. Add your IP when needed. You can also specify IP ranges with subnet masks (`["127.0.0.1", "127.0.0.1/24"]`) or directly with (`["127.0.0.1", ["192.168.0.1", "192.168.0.100"]]`). Set `[]` to allow all IP addresses. For more information see: [follow post ipWhitelist HowTo](https://forum.magicmirror.builders/topic/1326/ipwhitelist-howto) |
| `zoom` | This allows to scale the mirror contents with a given zoom factor. The default value is `1.0`|
| `language` | The language of the interface. (Note: Not all elements will be localized.) Possible values are `en`, `nl`, `ru`, `fr`, etc., but the default value is `en`. |
| `timeFormat` | The form of time notation that will be used. Possible values are `12` or `24`. The default is `24`. |
@ -158,6 +172,18 @@ The following modules are installed by default.
For more available modules, check out out the wiki page: [MagicMirror² Modules](https://github.com/MichMich/MagicMirror/wiki/MagicMirror²-Modules). If you want to build your own modules, check out the [MagicMirror² Module Development Documentation](modules) and don't forget to add it to the wiki and the [forum](https://forum.magicmirror.builders/category/7/showcase)!
## Updating
If you want to update your MagicMirror² to the latest version, use your terminal to go to your Magic Mirror folder and type the following command:
```bash
git pull && npm install
```
If you changed nothing more than the config or the modules, this should work without any problems.
Type `git status` to see your changes, if there are any, you can reset them with `git reset --hard`. After that, git pull should be possible.
## Known issues
- Electron seems to have some issues on certain Raspberry Pi 2's. See [#145](https://github.com/MichMich/MagicMirror/issues/145).

17
dangerfile.js Normal file
View File

@ -0,0 +1,17 @@
import { danger, fail, warn } from "danger"
// Check if the CHANGELOG.md file has been edited
// Fail the build and post a comment reminding submitters to do so if it wasn't changed
if (!danger.git.modified_files.includes("CHANGELOG.md")) {
warn("Please include an updated `CHANGELOG.md` file.<br>This way we can keep track of all the contributions.")
}
// Check if the PR request is send to the master branch.
// This should only be done by MichMich.
if (danger.github.pr.base.ref === "master" && danger.github.pr.user.login !== "MichMich") {
// Check if the PR body or title includes the text: #accepted.
// If not, the PR will fail.
if ((danger.github.pr.body + danger.github.pr.title).includes("#accepted")) {
fail("Please send all your pull requests to the `develop` branch.<br>Pull requests on the `master` branch will not be accepted.")
}
}

View File

@ -1,5 +1,5 @@
/* global Log, Loader, Module, config, defaults */
/* jshint -W020 */
/* jshint -W020, esversion: 6 */
/* Magic Mirror
* Main System
@ -19,42 +19,49 @@ var MM = (function() {
* are configured for a specific position.
*/
var createDomObjects = function() {
for (var m in modules) {
var module = modules[m];
var domCreationPromises = [];
if (typeof module.data.position === "string") {
var wrapper = selectWrapper(module.data.position);
var dom = document.createElement("div");
dom.id = module.identifier;
dom.className = module.name;
if (typeof module.data.classes === "string") {
dom.className = "module " + dom.className + " " + module.data.classes;
}
dom.opacity = 0;
wrapper.appendChild(dom);
if (typeof module.data.header !== "undefined" && module.data.header !== "") {
var moduleHeader = document.createElement("header");
moduleHeader.innerHTML = module.data.header;
moduleHeader.className = "module-header";
dom.appendChild(moduleHeader);
}
var moduleContent = document.createElement("div");
moduleContent.className = "module-content";
dom.appendChild(moduleContent);
updateDom(module, 0);
modules.forEach(module => {
if (typeof module.data.position !== "string") {
return;
}
}
var wrapper = selectWrapper(module.data.position);
var dom = document.createElement("div");
dom.id = module.identifier;
dom.className = module.name;
if (typeof module.data.classes === "string") {
dom.className = "module " + dom.className + " " + module.data.classes;
}
dom.opacity = 0;
wrapper.appendChild(dom);
if (typeof module.data.header !== "undefined" && module.data.header !== "") {
var moduleHeader = document.createElement("header");
moduleHeader.innerHTML = module.data.header;
moduleHeader.className = "module-header";
dom.appendChild(moduleHeader);
}
var moduleContent = document.createElement("div");
moduleContent.className = "module-content";
dom.appendChild(moduleContent);
var domCreationPromise = updateDom(module, 0);
domCreationPromises.push(domCreationPromise);
domCreationPromise.then(() => {
sendNotification("MODULE_DOM_CREATED", null, null, module);
}).catch(Log.error);
});
updateWrapperStates();
sendNotification("DOM_OBJECTS_CREATED");
Promise.all(domCreationPromises).then(() => {
sendNotification("DOM_OBJECTS_CREATED");
});
};
/* selectWrapper(position)
@ -79,11 +86,12 @@ var MM = (function() {
* argument notification string - The identifier of the notification.
* argument payload mixed - The payload of the notification.
* argument sender Module - The module that sent the notification.
* argument sendTo Module - The module to send the notification to. (optional)
*/
var sendNotification = function(notification, payload, sender) {
var sendNotification = function(notification, payload, sender, sendTo) {
for (var m in modules) {
var module = modules[m];
if (module !== sender) {
if (module !== sender && (!sendTo || module === sendTo)) {
module.notificationReceived(notification, payload, sender);
}
}
@ -94,19 +102,53 @@ var MM = (function() {
*
* argument module Module - The module that needs an update.
* argument speed Number - The number of microseconds for the animation. (optional)
*
* return Promise - Resolved when the dom is fully updated.
*/
var updateDom = function(module, speed) {
var newContent = module.getDom();
var newHeader = module.getHeader();
return new Promise((resolve) => {
var newContentPromise = module.getDom();
var newHeader = module.getHeader();
if (!module.hidden) {
if (!(newContentPromise instanceof Promise)) {
// convert to a promise if not already one to avoid if/else's everywhere
newContentPromise = Promise.resolve(newContentPromise);
}
newContentPromise.then((newContent) => {
var updatePromise = updateDomWithContent(module, speed, newHeader, newContent);
updatePromise.then(resolve).catch(Log.error);
}).catch(Log.error);
});
};
/* updateDomWithContent(module, speed, newHeader, newContent)
* Update the dom with the specified content
*
* argument module Module - The module that needs an update.
* argument speed Number - The number of microseconds for the animation. (optional)
* argument newHeader String - The new header that is generated.
* argument newContent Domobject - The new content that is generated.
*
* return Promise - Resolved when the module dom has been updated.
*/
var updateDomWithContent = function(module, speed, newHeader, newContent) {
return new Promise((resolve) => {
if (module.hidden || !speed) {
updateModuleContent(module, newHeader, newContent);
resolve();
return;
}
if (!moduleNeedsUpdate(module, newHeader, newContent)) {
resolve();
return;
}
if (!speed) {
updateModuleContent(module, newHeader, newContent);
resolve();
return;
}
@ -115,16 +157,16 @@ var MM = (function() {
if (!module.hidden) {
showModule(module, speed / 2);
}
resolve();
});
} else {
updateModuleContent(module, newHeader, newContent);
}
});
};
/* moduleNeedsUpdate(module, newContent)
* Check if the content has changed.
*
* argument module Module - The module to check.
* argument newHeader String - The new header that is generated.
* argument newContent Domobject - The new content that is generated.
*
* return bool - Does the module need an update?
@ -152,6 +194,7 @@ var MM = (function() {
* Update the content of a module on screen.
*
* argument module Module - The module to check.
* argument newHeader String - The new header that is generated.
* argument newContent Domobject - The new content that is generated.
*/
var updateModuleContent = function(module, newHeader, newContent) {

View File

@ -78,34 +78,33 @@ var Module = Class.extend({
* This method can to be subclassed if the module wants to display info on the mirror.
* Alternatively, the getTemplete method could be subclassed.
*
* return domobject - The dom to display.
* return DomObject | Promise - The dom or a promise with the dom to display.
*/
getDom: function () {
var div = document.createElement("div");
var template = this.getTemplate();
var templateData = this.getTemplateData();
return new Promise((resolve) => {
var div = document.createElement("div");
var template = this.getTemplate();
var templateData = this.getTemplateData();
// Check to see if we need to render a template string or a file.
if (/^.*((\.html)|(\.njk))$/.test(template)) {
// the template is a filename
this.nunjucksEnvironment().render(template, templateData, function (err, res) {
if (err) {
Log.error(err)
}
// Check to see if we need to render a template string or a file.
if (/^.*((\.html)|(\.njk))$/.test(template)) {
// the template is a filename
this.nunjucksEnvironment().render(template, templateData, function (err, res) {
if (err) {
Log.error(err)
}
// The inner content of the div will be set after the template is received.
// This isn't the most optimal way, but since it's near instant
// it probably won't be an issue.
// If it gives problems, we can always add a way to pre fetch the templates.
// Let's not over optimise this ... KISS! :)
div.innerHTML = res;
});
} else {
// the template is a template string.
div.innerHTML = this.nunjucksEnvironment().renderString(template, templateData);
}
div.innerHTML = res;
return div;
resolve(div);
});
} else {
// the template is a template string.
div.innerHTML = this.nunjucksEnvironment().renderString(template, templateData);
resolve(div);
}
});
},
/* getHeader()

31
module-types.ts Normal file
View File

@ -0,0 +1,31 @@
type ModuleProperties = {
defaults?: object,
start?(): void,
getHeader?(): string,
getTemplate?(): string,
getTemplateData?(): object,
notificationReceived?(notification: string, payload: any, sender: object): void,
socketNotificationReceived?(notification: string, payload: any): void,
suspend?(): void,
resume?(): void,
getDom?(): HTMLElement,
getStyles?(): string[],
[key: string]: any,
};
export declare const Module: {
register(moduleName: string, moduleProperties: ModuleProperties): void;
};
export declare const Log: {
info(message?: any, ...optionalParams: any[]): void,
log(message?: any, ...optionalParams: any[]): void,
error(message?: any, ...optionalParams: any[]): void,
warn(message?: any, ...optionalParams: any[]): void,
group(groupTitle?: string, ...optionalParams: any[]): void,
groupCollapsed(groupTitle?: string, ...optionalParams: any[]): void,
groupEnd(): void,
time(timerName?: string): void,
timeEnd(timerName?: string): void,
timeStamp(timerName?: string): void,
};

View File

@ -2,6 +2,18 @@
This document describes the way to develop your own MagicMirror² modules.
## General Advice
As MagicMirror has gained huge popularity, so has the number of available modules. For new users and developers alike, it is very time consuming to navigate around the various repositories in order to find out what exactly a certain modules does, how it looks and what it depends on. Unfortunately, this information is rarely available, nor easily obtained without having to install it first.
Therefore **we highly recommend you to include the following information in your README file.**
- A high quality screenshot of your working module
- A short, one sentence, clear description what it does (duh!)
- What external API's it depend on, including web links to those
- Wheteher the API/request require a key and the user limitations of those. (Is it free?)
Surely this also help you get better recognition and feedback for your work.
## Module structure
All modules are loaded in the `modules` folder. The default modules are grouped together in the `modules/default` folder. Your module should be placed in a subfolder of `modules`. Note that any file or folder your create in the `modules` folder will be ignored by git, allowing you to upgrade the MagicMirror² without the loss of your files.
@ -230,11 +242,12 @@ notificationReceived: function(notification, payload, sender) {
}
````
**Note:** the system sends two notifications when starting up. These notifications could come in handy!
**Note:** the system sends three notifications when starting up. These notifications could come in handy!
- `ALL_MODULES_STARTED` - All modules are started. You can now send notifications to other modules.
- `DOM_OBJECTS_CREATED` - All dom objects are created. The system is now ready to perform visual changes.
- `MODULE_DOM_CREATED` - This module's dom has been fully loaded. You can now access your module's dom objects.
#### `socketNotificationReceived: function(notification, payload)`

BIN
modules/default/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -108,7 +108,7 @@ vows.describe('node-ical').addBatch({
assert.equal(topic.end.getFullYear(), 1998);
assert.equal(topic.end.getUTCMonth(), 2);
assert.equal(topic.end.getUTCDate(), 15);
assert.equal(topic.end.getUTCHours(), 00);
assert.equal(topic.end.getUTCHours(), 0);
assert.equal(topic.end.getUTCMinutes(), 30);
}
}
@ -146,7 +146,7 @@ vows.describe('node-ical').addBatch({
}
, 'has a start datetime' : function(topic) {
assert.equal(topic.start.getFullYear(), 2011);
assert.equal(topic.start.getMonth(), 09);
assert.equal(topic.start.getMonth(), 9);
assert.equal(topic.start.getDate(), 11);
}
@ -192,7 +192,7 @@ vows.describe('node-ical').addBatch({
}
, 'has a start' : function(topic){
assert.equal(topic.start.tz, 'America/Phoenix')
assert.equal(topic.start.toISOString(), new Date(2011, 10, 09, 19, 0,0).toISOString())
assert.equal(topic.start.toISOString(), new Date(2011, 10, 9, 19, 0,0).toISOString())
}
}
}
@ -208,7 +208,7 @@ vows.describe('node-ical').addBatch({
})[0];
}
, 'has a start' : function(topic){
assert.equal(topic.start.toISOString(), new Date(2011, 07, 04, 12, 0,0).toISOString())
assert.equal(topic.start.toISOString(), new Date(2011, 7, 4, 12, 0,0).toISOString())
}
}
, 'event with rrule' :{
@ -249,7 +249,7 @@ vows.describe('node-ical').addBatch({
},
'task completed': function(task){
assert.equal(task.completion, 100);
assert.equal(task.completed.toISOString(), new Date(2013, 06, 16, 10, 57, 45).toISOString());
assert.equal(task.completed.toISOString(), new Date(2013, 6, 16, 10, 57, 45).toISOString());
}
}
}
@ -367,7 +367,7 @@ vows.describe('node-ical').addBatch({
assert.equal(topic.end.getFullYear(), 2014);
assert.equal(topic.end.getMonth(), 3);
assert.equal(topic.end.getUTCHours(), 19);
assert.equal(topic.end.getUTCMinutes(), 00);
assert.equal(topic.end.getUTCMinutes(), 0);
}
}
},

View File

@ -43,7 +43,7 @@ The following properties can be configured:
| `showWindDirectionAsArrow` | Show the wind direction as an arrow instead of abbreviation <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
| `showHumidity` | Show the current humidity <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
| `showIndoorTemperature` | If you have another module that emits the INDOOR_TEMPERATURE notification, the indoor temperature will be displayed <br> **Default value:** `false`
| `onlyTemp` | Show only current Temperature and weather icon. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
| `onlyTemp` | Show only current Temperature and weather icon without windspeed, sunset and sunrise time. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `false`
| `useBeaufort` | Pick between using the Beaufort scale for wind speed or using the default units. <br><br> **Possible values:** `true` or `false` <br> **Default value:** `true`
| `lang` | The language of the days. <br><br> **Possible values:** `en`, `nl`, `ru`, etc ... <br> **Default value:** uses value of _config.language_
| `decimalSymbol` | The decimal symbol to use.<br><br> **Possible values:** `.`, `,` or any other symbol.<br> **Default value:** `.`

View File

@ -72,7 +72,7 @@ The following properties can be configured:
| `updateInterval` | How often do you want to display a new headline? (Milliseconds) <br><br> **Possible values:**`1000` - `60000` <br> **Default value:** `10000` (10 seconds)
| `animationSpeed` | Speed of the update animation. (Milliseconds) <br><br> **Possible values:**`0` - `5000` <br> **Default value:** `2500` (2.5 seconds)
| `maxNewsItems` | Total amount of news items to cycle through. (0 for unlimited) <br><br> **Possible values:**`0` - `...` <br> **Default value:** `0`
| `ignoreOldItems` | Ignore news items that are outdated. <br><br> **Possible values:**`true` or `false <br> **Default value:** `false`
| `ignoreOldItems` | Ignore news items that are outdated. <br><br> **Possible values:**`true` or `false` <br> **Default value:** `false`
| `ignoreOlderThan` | How old should news items be before they are considered outdated? (Milliseconds) <br><br> **Possible values:**`1` - `...` <br> **Default value:** `86400000` (1 day)
| `removeStartTags` | Some newsfeeds feature tags at the **beginning** of their titles or descriptions, such as _[VIDEO]_. This setting allows for the removal of specified tags from the beginning of an item's description and/or title. <br><br> **Possible values:**`'title'`, `'description'`, `'both'`
| `startTags` | List the tags you would like to have removed at the beginning of the feed item <br><br> **Possible values:** `['TAG']` or `['TAG1','TAG2',...]`

View File

@ -188,6 +188,7 @@ Module.register("newsfeed",{
fullArticle.style.left = "0";
fullArticle.style.border = "none";
fullArticle.src = typeof this.newsItems[this.activeItem].url === "string" ? this.newsItems[this.activeItem].url : this.newsItems[this.activeItem].url.href;
fullArticle.style.zIndex = 1;
wrapper.appendChild(fullArticle);
}

836
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "magicmirror",
"version": "2.2.2",
"version": "2.3.0-dev",
"description": "The open source modular smart mirror platform.",
"main": "js/electron.js",
"scripts": {
@ -36,6 +36,7 @@
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"current-week-number": "^1.0.7",
"danger": "^3.1.3",
"grunt": "latest",
"grunt-eslint": "latest",
"grunt-jsonlint": "latest",
@ -54,7 +55,7 @@
"dependencies": {
"body-parser": "^1.18.2",
"colors": "^1.1.2",
"electron": "1.4.15",
"electron": "^1.7.10",
"express": "^4.16.2",
"express-ipfilter": "0.3.1",
"feedme": "latest",

View File

@ -14,8 +14,6 @@ var path = require("path");
var fs = require("fs");
var Utils = require(__dirname + "/../../js/utils.js");
if (process.env.NODE_ENV == "test") { return 0 };
/* getConfigFile()
* Return string with path of configuration file
* Check if set by enviroment variable MM_CONFIG_FILE
@ -30,37 +28,43 @@ function getConfigFile() {
return configFileName;
}
var configFileName = getConfigFile();
// Check if file is present
if (fs.existsSync(configFileName) === false) {
console.error(Utils.colors.error("File not found: "), configFileName);
return;
}
// check permision
try {
fs.accessSync(configFileName, fs.F_OK);
} catch (e) {
console.log(Utils.colors.error(e));
return;
}
// Validate syntax of the configuration file.
// In case the there errors show messages and
// return
console.info(Utils.colors.info("Checking file... ", configFileName));
// I'm not sure if all ever is utf-8
fs.readFile(configFileName, "utf-8", function (err, data) {
if (err) { throw err; }
v.JSHINT(data); // Parser by jshint
if (v.JSHINT.errors.length == 0) {
console.log("Your configuration file doesn't contain syntax errors :)");
return true;
} else {
errors = v.JSHINT.data().errors;
for (idx in errors) {
error = errors[idx];
console.log("Line", error.line, "col", error.character, error.reason);
}
function checkConfigFile() {
var configFileName = getConfigFile();
// Check if file is present
if (fs.existsSync(configFileName) === false) {
console.error(Utils.colors.error("File not found: "), configFileName);
return;
}
});
// check permision
try {
fs.accessSync(configFileName, fs.F_OK);
} catch (e) {
console.log(Utils.colors.error(e));
return;
}
// Validate syntax of the configuration file.
// In case the there errors show messages and
// return
console.info(Utils.colors.info("Checking file... ", configFileName));
// I'm not sure if all ever is utf-8
fs.readFile(configFileName, "utf-8", function (err, data) {
if (err) { throw err; }
v.JSHINT(data); // Parser by jshint
if (v.JSHINT.errors.length == 0) {
console.log("Your configuration file doesn't contain syntax errors :)");
return true;
} else {
errors = v.JSHINT.data().errors;
for (idx in errors) {
error = errors[idx];
console.log("Line", error.line, "col", error.character, error.reason);
}
}
});
}
if (process.env.NODE_ENV !== "test") {
checkConfigFile();
};

View File

@ -3,7 +3,7 @@
"TODAY": "Today",
"TOMORROW": "Tomorrow",
"DAYAFTERTOMORROW": "The day after tomorrow",
"DAYAFTERTOMORROW": "In 2 days",
"RUNNING": "Ends in",
"EMPTY": "No upcoming events.",

4391
yarn.lock Normal file

File diff suppressed because it is too large Load Diff