-[](https://david-dm.org/MichMich/MagicMirror/v2-beta#info=devDependencies)
+**MagicMirror²** is an open source modular smart mirror platform. With a growing list of installable modules, the **MagicMirror²** allows you to convert your hallway or bathroom mirror into your personal assistant. **MagicMirror²** is built by the creator of [the original MagicMirror](http://michaelteeuw.nl/tagged/magicmirror) with the incredible help of a [growing community of contributors](https://github.com/MichMich/MagicMirror/graphs/contributors).
-This version of the Magic Mirror software focusses on a modular plugin system. Besides that, the Magic Mirror software now also uses [Electron](http://electron.atom.io/), so no more webserver or browser installs necessary.
+MagicMirror² focuses on a modular plugin system and uses [Electron](http://electron.atom.io/) as an application wrapper. So no more web server or browser installs necessary!
-**WARNING!** This version is in a *very* early stage. It is **not** completed yet. **Please** use the master branch.
+**NOTE:** This version is in currently in beta stage. Please use the master branch if you need a thoroughly tested version.
+
+## Table Of Contents
+
+- [Usage](#usage)
+- [Configuration](#configuration)
+- [Modules](#modules)
+- [Known Issues](#known-issues)
+- [Contributing Guidelines](#contributing-guidelines)
## Usage
-#### Automatic Installer
-
-1. Clone the repository: `git clone -b v2-beta https://github.com/MichMich/MagicMirror`
-2. Enter the new directory: `cd MagicMirror`
-3. Give the installer permission to run: `sudo chmod +x install.sh`
-4. Start the installer: `sudo ./install.sh`
+#### Automatic Installer (Raspberry Pi Only!)
+Execute the following command on your Raspberry Pi to install MagicMirror²:
+````
+curl -sL https://raw.githubusercontent.com/MichMich/MagicMirror/v2-beta/installers/raspberry.sh | bash
+````
#### Manual Installation
-1. Download the latest Node.js version:
- - `wget https://nodejs.org/dist/latest/node-v5.10.0-linux-armv6l.tar.gz` for Raspberry Pi
- - `wget https://nodejs.org/dist/latest/node-v5.10.0-linux-armv7l.tar.gz` for Raspberry Pi 2
- - `wget https://nodejs.org/dist/latest/node-v5.10.0-linux-arm64.tar.gz` for Raspberry Pi 3
-2. Unpack the archive file: `tar -xvf filename.tar.gz`
-3. Install Node.js: `cd foldername && sudo cp -R * /usr/local/`
-4. Set loglevel `npm config set loglevel info`
-5. Clone the repository and check out the beta branch: `git clone -b v2-beta https://github.com/MichMich/MagicMirror`
-6. Enter the repository: `cd MagicMirror`
-7. `npm install && npm start` (You may have to restart your terminal before this works!)
+1. Download and install the latest Node.js version.
+2. Clone the repository and check out the beta branch: `git clone -b v2-beta https://github.com/MichMich/MagicMirror`
+3. Enter the repository: `cd ~/MagicMirror`
+4. Install and run the app: `npm install && npm start` (You may have to restart your terminal before this works!)
-**Important:** `npm start` does **not** work via SSH! You will have to execute it in a terminal session running in a window manager.
+**Important:** `npm start` does **not** work via SSH, use `DISPLAY=:0 nohup npm start &` instead. This starts the mirror on the remote display.
+
+#### Server Only
+
+In some cases, you want to start the application without an actual app window. In this case, execute the following command from the MagicMirror folder: `node serveronly`. This will start the server, after which you can open the application in your browser of choice.
+
+#### 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)
## Configuration
1. Duplicate `config/config.js.sample` to `config/config.js`.
2. Modify your required settings.
-## Todo List
+The following properties can be configured:
-Here is a list of various things that still have to be implemented or changed.
-- [ ] Allow show/hide animations to animate the height. This way, the other modules won't jump around.
-- [ ] Allow vertical centering of alerts.
-- [ ] Rewrite the [alert](modules/default/alert) module in vanilla JavaScript.
-- [ ] Write all the documentation.
+| **Option** | **Description** |
+| --- | --- |
+| `port` | The port on which the MagicMirror² server will run on. The default value is `8080`. |
+| `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`. |
+| `modules` | An array of active modules. **The array must contain objects. See the next table below for more information.** |
+
+Module configuration:
+
+| **Option** | **Description** |
+| --- | --- |
+| `module` | The name of the module. This can also contain the subfolder. Valid examples include `clock`, `default/calendar` and `custommodules/mymodule`. |
+| `position` | The location of the module in which the module will be loaded. Possible values are `top_ bar`, `top_left`, `top_center`, `top_right`, `upper_third`, `middle_center`, `lower_third`, `bottom_left`, `bottom_center`, `bottom_right`, `bottom_bar`, `fullscreen_above`, and `fullscreen_below`. This field is optional but most modules require this field to set. Check the documentation of the module for more information. Multiple modules with the same position will be ordered based on the order in the configuration file. |
+| `classes` | Additional classes which are passed to the module. The field is optional. |
+| `header` | To display a header text above the module, add the header property. This field is optional. |
+| `config` | An object with the module configuration properties. Check the documentation of the module for more information. This field is optional, unless the module requires extra configuration. |
## Modules
@@ -67,16 +94,27 @@ The following modules are created by their respective authors.
- **[MMM-Facial-Recognition by PaViRo](https://github.com/paviro/MMM-Facial-Recognition)** Facial recognition and module swapping based on the current user ...
- **[MMM-Wunderlist by PaViRo](https://github.com/paviro/MMM-Wunderlist)** Displays your Wunderlist todos on your mirror ...
+
+- **[MMM-wordnik by Vendittelli](https://github.com/SVendittelli/MMM-wordnik)** Get the word of the day, its definition, and origin ...
-## Contributing
+- **[MMM-SystemTemperature by MichMich](https://github.com/MichMich/mmm-systemtemperature)** Display your Raspberry Pi's processor temperature on your MagicMirror.
+
+**Note:** If you want to build your own modules, check out the [MagicMirror² Module Development Documentation](modules)
+
+## Known issues
+
+- Electron seems to have some issues on certain Raspberry Pi 2's. See [#145](https://github.com/MichMich/MagicMirror/issues/145).
+- MagicMirror² (Electron) sometimes quits without an error after an extended period of use. See [#150](https://github.com/MichMich/MagicMirror/issues/150).
+
+## Contributing Guidelines
Contributions of all kinds are welcome, not only in the form of code but also with regards bug reports and documentation.
Please keep the following in mind:
- **Bug Reports**: Make sure you're running the latest version. If the issue(s) still persist: please open a clearly documented issue with a clear title.
-- **Minor Bug Fixes**: Please send a pull request with a clear explanation of the issue or a link to the isssue it solves.
+- **Minor Bug Fixes**: Please send a pull request with a clear explanation of the issue or a link to the issue it solves.
- **Major Bug Fixes**: please discuss your approach in an GitHub issue before you start to alter a big part of the code.
- **New Features**: please please discuss in a GitHub issue before you start to alter a big part of the code. Without discussion upfront, the pull request will not be accepted / merged.
-Thanks for your help in making Magic Mirror better!
+Thanks for your help in making MagicMirror² better!
diff --git a/config/.gitignore b/config/.gitignore
index 1bf4259a..d85e3bd4 100644
--- a/config/.gitignore
+++ b/config/.gitignore
@@ -1 +1,2 @@
-config.js
+*
+!config.js.sample
diff --git a/config/config.js.sample b/config/config.js.sample
index f6388249..ef3e9aef 100644
--- a/config/config.js.sample
+++ b/config/config.js.sample
@@ -1,6 +1,4 @@
-/* exported config */
-
-/* Magic Mirror Config
+/* Magic Mirror Config Sample
*
* By Michael Teeuw http://michaelteeuw.nl
* MIT Licensed.
@@ -66,6 +64,5 @@ var config = {
};
-
/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== 'undefined') {module.exports = config;}
diff --git a/css/main.css b/css/main.css
index de3e78ed..1d9a0de7 100644
--- a/css/main.css
+++ b/css/main.css
@@ -1,59 +1,108 @@
html {
- cursor: none;
+ cursor: none;
+ overflow:hidden;
}
body {
- margin: 60px;
- position: absolute;
- height: calc(100% - 120px);
- width: calc(100% - 120px);
- background: #000;
- color: #aaa;
- font-family: 'Roboto Condensed', sans-serif;
- font-weight: 400;
- font-size: 2em;
- line-height: 1.5em;
- -webkit-font-smoothing: antialiased;
+ overflow:hidden;
+ margin: 60px;
+ position: absolute;
+ height: calc(100% - 120px);
+ width: calc(100% - 120px);
+ background: #000;
+ color: #aaa;
+ font-family: "Roboto Condensed", sans-serif;
+ font-weight: 400;
+ font-size: 2em;
+ line-height: 1.5em;
+ -webkit-font-smoothing: antialiased;
}
/**
* Default styles.
*/
-.dimmed {color: #555;}
-.normal {color: #999;}
-.bright {color: #fff;}
+.dimmed {
+ color: #666;
+}
-.xsmall {font-size: 15px; line-height: 20px;}
-.small {font-size: 20px; line-height: 25px;}
-.medium {font-size: 30px; line-height: 35px;}
-.large {font-size: 65px; line-height: 65px;}
-.xlarge {font-size: 75px; line-height: 75px; letter-spacing: -3px;}
+.normal {
+ color: #999;
+}
-.thin {font-family: 'Roboto', sans-serif; font-weight: 100;}
-.light {font-family: 'Roboto Condensed', sans-serif; font-weight: 300;}
-.regular {font-family: 'Roboto Condensed', sans-serif; font-weight: 400;}
-.bold {font-family: 'Roboto Condensed', sans-serif; font-weight: 700;}
+.bright {
+ color: #fff;
+}
-.align-right {text-align: right;}
-.align-left {text-align: left;}
+.xsmall {
+ font-size: 15px;
+ line-height: 20px;
+}
+.small {
+ font-size: 20px;
+ line-height: 25px;
+}
+
+.medium {
+ font-size: 30px;
+ line-height: 35px;
+}
+
+.large {
+ font-size: 65px;
+ line-height: 65px;
+}
+
+.xlarge {
+ font-size: 75px;
+ line-height: 75px;
+ letter-spacing: -3px;
+}
+
+.thin {
+ font-family: Roboto, sans-serif;
+ font-weight: 100;
+}
+
+.light {
+ font-family: "Roboto Condensed", sans-serif;
+ font-weight: 300;
+}
+
+.regular {
+ font-family: "Roboto Condensed", sans-serif;
+ font-weight: 400;
+}
+
+.bold {
+ font-family: "Roboto Condensed", sans-serif;
+ font-weight: 700;
+}
+
+.align-right {
+ text-align: right;
+}
+
+.align-left {
+ text-align: left;
+}
header {
- text-transform: uppercase;
- font-size: 15px;
- font-family: 'Roboto Condensed';
- font-weight: 400;
- border-bottom: 1px solid #333;
- line-height: 0.35em;
- padding-bottom: 10px;
- margin-bottom: 10px;
- color: #666;
+ text-transform: uppercase;
+ font-size: 15px;
+ font-family: "Roboto Condensed";
+ font-weight: 400;
+ border-bottom: 1px solid #666;
+ line-height: 0.35em;
+ padding-bottom: 10px;
+ margin-bottom: 10px;
+ color: #999;
}
sup {
- font-size: 50%;
- line-height: 50%;
+ font-size: 50%;
+ line-height: 50%;
}
/**
@@ -61,10 +110,11 @@ sup {
*/
.module {
- margin-top: 30px;
+ margin-top: 30px;
}
+
.module:first-child {
- margin-top: 0px;
+ margin-top: 0;
}
/**
@@ -72,73 +122,105 @@ sup {
*/
.region {
- position: absolute;
+ position: absolute;
}
+
+.region.fullscreen {
+ position: absolute;
+ top: -60px;
+ left: -60px;
+ right: -60px;
+ bottom: -60px;
+}
+
.region.right {
- right: 0;
+ right: 0;
}
+
.region.top {
- top: 0;
+ top: 0;
}
-.region.top .container{
- margin-bottom: 25px;
+
+.region.top .container {
+ margin-bottom: 25px;
}
+
.region.top .container:empty {
- margin-bottom: 0px;
+ margin-bottom: 0;
}
-.region.top.center, .region.bottom.center {
- left: 50%;
- -moz-transform: translateX(-50%);
- -o-transform: translateX(-50%);
- -webkit-transform: translateX(-50%);
- -ms-transform: translateX(-50%);
- transform: translateX(-50%);
+
+.region.top.center,
+.region.bottom.center {
+ left: 50%;
+ -moz-transform: translateX(-50%);
+ -o-transform: translateX(-50%);
+ -webkit-transform: translateX(-50%);
+ -ms-transform: translateX(-50%);
+ transform: translateX(-50%);
}
-.region.top.right, .region.top.left, .region.top.center {
- top: 100%;
+
+.region.top.right,
+.region.top.left,
+.region.top.center {
+ top: 100%;
}
+
.region.bottom {
- bottom: 0;
+ bottom: 0;
}
-.region.bottom .container{
- margin-top: 25px;
+
+.region.bottom .container {
+ margin-top: 25px;
}
+
.region.bottom .container:empty {
- margin-top: 0px;
+ margin-top: 0;
}
-.region.bottom.right, .region.bottom.center, .region.bottom.left {
- bottom: 100%;
+
+.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;
- -moz-transform: translateY(-50%);
- -o-transform: translateY(-50%);
- -webkit-transform: translateY(-50%);
- -ms-transform: translateY(-50%);
- transform: translateY(-50%);
-}
-.region.upper.third {
- top: 33%;
+ width: 100%;
+ text-align: center;
}
+
+.region.third,
.region.middle.center {
- top: 50%;
+ width: 100%;
+ text-align: center;
+ -moz-transform: translateY(-50%);
+ -o-transform: translateY(-50%);
+ -webkit-transform: translateY(-50%);
+ -ms-transform: translateY(-50%);
+ transform: translateY(-50%);
}
+
+.region.upper.third {
+ top: 33%;
+}
+
+.region.middle.center {
+ top: 50%;
+}
+
.region.lower.third {
- top: 66%;
+ top: 66%;
}
+
.region.left {
- text-align: left;
+ text-align: left;
}
+
.region.right {
- text-align: right;
+ text-align: right;
}
-.region table {
- width: 100%;
- border-spacing: 0px;
- border-collapse: separate;
+
+.region table {
+ width: 100%;
+ border-spacing: 0;
+ border-collapse: separate;
}
diff --git a/fonts/roboto.css b/fonts/roboto.css
index e33b5ef7..ce9099da 100644
--- a/fonts/roboto.css
+++ b/fonts/roboto.css
@@ -1,87 +1,95 @@
@font-face {
- font-family: 'Roboto';
- font-style: normal;
- font-weight: 100;
- src: local('Roboto Thin'),
- local('Roboto-Thin'),
- url('Roboto-Thin/Roboto-Thin.woff2') format('woff2'),
- url('Roboto-Thin/Roboto-Thin.woff') format('woff'),
- url('Roboto-Thin/Roboto-Thin.ttf') format('truetype');
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: 100;
+ src:
+ local("Roboto Thin"),
+ local("Roboto-Thin"),
+ url("Roboto-Thin/Roboto-Thin.woff2") format("woff2"),
+ url("Roboto-Thin/Roboto-Thin.woff") format("woff"),
+ url("Roboto-Thin/Roboto-Thin.ttf") format("truetype");
}
@font-face {
- font-family: 'Roboto Condensed';
- font-style: normal;
- font-weight: 300;
- src: local('Roboto Condensed Light'),
- local('RobotoCondensed-Light'),
- url('RobotoCondensed-Light/RobotoCondensed-Light.woff2') format('woff2'),
- url('RobotoCondensed-Light/RobotoCondensed-Light.woff') format('woff'),
- url('RobotoCondensed-Light/RobotoCondensed-Light.ttf') format('truetype');
+ font-family: "Roboto Condensed";
+ font-style: normal;
+ font-weight: 300;
+ src:
+ local("Roboto Condensed Light"),
+ local("RobotoCondensed-Light"),
+ url("RobotoCondensed-Light/RobotoCondensed-Light.woff2") format("woff2"),
+ url("RobotoCondensed-Light/RobotoCondensed-Light.woff") format("woff"),
+ url("RobotoCondensed-Light/RobotoCondensed-Light.ttf") format("truetype");
}
@font-face {
- font-family: 'Roboto Condensed';
- font-style: normal;
- font-weight: 400;
- src: local('Roboto Condensed'),
- local('RobotoCondensed-Regular'),
- url('RobotoCondensed-Regular/RobotoCondensed-Regular.woff2') format('woff2'),
- url('RobotoCondensed-Regular/RobotoCondensed-Regular.woff') format('woff'),
- url('RobotoCondensed-Regular/RobotoCondensed-Regular.ttf') format('truetype');
+ font-family: "Roboto Condensed";
+ font-style: normal;
+ font-weight: 400;
+ src:
+ local("Roboto Condensed"),
+ local("RobotoCondensed-Regular"),
+ url("RobotoCondensed-Regular/RobotoCondensed-Regular.woff2") format("woff2"),
+ url("RobotoCondensed-Regular/RobotoCondensed-Regular.woff") format("woff"),
+ url("RobotoCondensed-Regular/RobotoCondensed-Regular.ttf") format("truetype");
}
@font-face {
- font-family: 'Roboto Condensed';
- font-style: normal;
- font-weight: 700;
- src: local('Roboto Condensed Bold'),
- local('RobotoCondensed-Bold'),
- url('RobotoCondensed-Bold/RobotoCondensed-Bold.woff2') format('woff2'),
- url('RobotoCondensed-Bold/RobotoCondensed-Bold.woff') format('woff'),
- url('RobotoCondensed-Bold/RobotoCondensed-Bold.ttf') format('truetype');
+ font-family: "Roboto Condensed";
+ font-style: normal;
+ font-weight: 700;
+ src:
+ local("Roboto Condensed Bold"),
+ local("RobotoCondensed-Bold"),
+ url("RobotoCondensed-Bold/RobotoCondensed-Bold.woff2") format("woff2"),
+ url("RobotoCondensed-Bold/RobotoCondensed-Bold.woff") format("woff"),
+ url("RobotoCondensed-Bold/RobotoCondensed-Bold.ttf") format("truetype");
}
@font-face {
- font-family: 'Roboto';
- font-style: normal;
- font-weight: 400;
- src: local('Roboto'),
- local('Roboto-Regular'),
- url('Roboto-Regular/Roboto-Regular.woff2') format('woff2'),
- url('Roboto-Regular/Roboto-Regular.woff') format('woff'),
- url('Roboto-Regular/Roboto-Regular.ttf') format('truetype');
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: 400;
+ src:
+ local("Roboto"),
+ local("Roboto-Regular"),
+ url("Roboto-Regular/Roboto-Regular.woff2") format("woff2"),
+ url("Roboto-Regular/Roboto-Regular.woff") format("woff"),
+ url("Roboto-Regular/Roboto-Regular.ttf") format("truetype");
}
@font-face {
- font-family: 'Roboto';
- font-style: normal;
- font-weight: 500;
- src: local('Roboto Medium'),
- local('Roboto-Medium'),
- url('Roboto-Medium/Roboto-Medium.woff2') format('woff2'),
- url('Roboto-Medium/Roboto-Medium.woff') format('woff'),
- url('Roboto-Medium/Roboto-Medium.ttf') format('truetype');
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: 500;
+ src:
+ local("Roboto Medium"),
+ local("Roboto-Medium"),
+ url("Roboto-Medium/Roboto-Medium.woff2") format("woff2"),
+ url("Roboto-Medium/Roboto-Medium.woff") format("woff"),
+ url("Roboto-Medium/Roboto-Medium.ttf") format("truetype");
}
@font-face {
- font-family: 'Roboto';
- font-style: normal;
- font-weight: 700;
- src: local('Roboto Bold'),
- local('Roboto-Bold'),
- url('Roboto-Bold/Roboto-Bold.woff2') format('woff2'),
- url('Roboto-Bold/Roboto-Bold.woff') format('woff'),
- url('Roboto-Bold/Roboto-Bold.ttf') format('truetype');
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: 700;
+ src:
+ local("Roboto Bold"),
+ local("Roboto-Bold"),
+ url("Roboto-Bold/Roboto-Bold.woff2") format("woff2"),
+ url("Roboto-Bold/Roboto-Bold.woff") format("woff"),
+ url("Roboto-Bold/Roboto-Bold.ttf") format("truetype");
}
@font-face {
- font-family: 'Roboto';
- font-style: normal;
- font-weight: 300;
- src: local('Roboto Light'),
- local('Roboto-Light'),
- url('Roboto-Light/Roboto-Light.woff2') format('woff2'),
- url('Roboto-Light/Roboto-Light.woff') format('woff'),
- url('Roboto-Light/Roboto-Light.ttf') format('truetype');
+ font-family: Roboto;
+ font-style: normal;
+ font-weight: 300;
+ src:
+ local("Roboto Light"),
+ local("Roboto-Light"),
+ url("Roboto-Light/Roboto-Light.woff2") format("woff2"),
+ url("Roboto-Light/Roboto-Light.woff") format("woff"),
+ url("Roboto-Light/Roboto-Light.ttf") format("truetype");
}
diff --git a/index.html b/index.html
index cfcffff5..a6d7265c 100644
--- a/index.html
+++ b/index.html
@@ -1,33 +1,32 @@
+
Magic Mirror
-
+
+
-
-
+
-
-
+
-
-
+
diff --git a/install.sh b/install.sh
deleted file mode 100644
index 00473f92..00000000
--- a/install.sh
+++ /dev/null
@@ -1,69 +0,0 @@
-# $$\ $$\ $$\ $$\ $$\ $$\ $$$$$$\
-# $$$\ $$$ | \__| $$$\ $$$ |\__| $$ __$$\
-# $$$$\ $$$$ | $$$$$$\ $$$$$$\ $$\ $$$$$$$\ $$$$\ $$$$ |$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ \__/ $$ |
-# $$\$$\$$ $$ | \____$$\ $$ __$$\ $$ |$$ _____|$$\$$\$$ $$ |$$ |$$ __$$\ $$ __$$\ $$ __$$\ $$ __$$\ $$$$$$ |
-# $$ \$$$ $$ | $$$$$$$ |$$ / $$ |$$ |$$ / $$ \$$$ $$ |$$ |$$ | \__|$$ | \__|$$ / $$ |$$ | \__|$$ ____/
-# $$ |\$ /$$ |$$ __$$ |$$ | $$ |$$ |$$ | $$ |\$ /$$ |$$ |$$ | $$ | $$ | $$ |$$ | $$ |
-# $$ | \_/ $$ |\$$$$$$$ |\$$$$$$$ |$$ |\$$$$$$$\ $$ | \_/ $$ |$$ |$$ | $$ | \$$$$$$ |$$ | $$$$$$$$\
-# \__| \__| \_______| \____$$ |\__| \_______|\__| \__|\__|\__| \__| \______/ \__| \________|
-# $$\ $$ |
-# \$$$$$$ |
-# \______/
-#
-# This is an installer script for MagicMirror2. It works well enough
-# that it can detect if you have Node installed, run a binary script
-# and then download and run MagicMirror2.
-sudo apt-get install curl wget build-essential unzip
-ARM=$(uname -m) # Determine which Pi is running.
-NODE_LATEST="v5.10.0" # Set the latest version here.
-6L_HASH="019a257faa5eebf6304686dfeffdbcb4c22f0547aa366f6e563aad39ab1b1ab1" # Set the armv6l hash here.
-7L_HASH="3f7524d3db60175c2323bb2a0a13ad1ca7d47d4ede6f42834b6b8425be70e0a2" # Set the armv7l hash here.
-8_HASH="df88803bda234b32240906b620315c8f6d6200332047a88cb0ec83009cf25dd5" # Set the arm64 hash here.
-DOWNLOAD_URL="https://nodejs.org/dist/latest/node-$NODE_LATEST-linux-$ARM.tar.gz" # Construct the download URL.
-wget $DOWNLOAD_URL # Download the file given.
-if [ $ARM = "armv6l" ]; then
- if [ -f "node-$NODE_LATEST-linux-armv6l.tar.gz"]; then
- COMMAND256="sha256sum node-$NODE_LATEST-linux-armv6l.tar.gz"
- if [ $($COMMAND256) = "019a257faa5eebf6304686dfeffdbcb4c22f0547aa366f6e563aad39ab1b1ab1" ]; then
- echo "Node.js was downloaded and verified successfully."
- else
- echo "Node.js was downloaded, but verification failed. Make sure sha256sum works."
- exit 1
- fi
- fi
-elif [ $ARM = "armv7l" ]; then
- if [ -f "node-$NODE_LATEST-linux-armv7l.tar.gz" ]; then
- COMMAND256="sha256sum node-$NODE_LATEST-linux-armv7l.tar.gz"
- if [ $($COMMAND256) = "3f7524d3db60175c2323bb2a0a13ad1ca7d47d4ede6f42834b6b8425be70e0a2" ]; then
- echo "Node.js was downloaded and verified successfully."
- else
- echo "Node.js was downloaded, but verification failed. Make sure sha256sum works."
- exit 1
- fi
- fi
-elif [ $ARM = "arm64" ]; then
- if [ -f "node-$NODE_LATEST-linux-arm64.tar.gz" ]; then
- COMMAND256="sha256sum node-$NODE_LATEST-linux-arm64.tar.gz"
- if [ $($COMMAND256) = "df88803bda234b32240906b620315c8f6d6200332047a88cb0ec83009cf25dd5" ]; then
- echo "Node.js was downloaded and verified successfully."
- else
- echo "Node.js was downloaded, but verification failed. Make sure sha256sum works."
- exit 1
- fi
- fi
-fi
-tar xvf node-$NODE_LATEST-linux-$ARM.tar.gz
-cd node*
-sudo cp -R * /usr/local
-cd ..
-rm -rf node*
-# Run Node checks to make sure Node works properly.
-curl -sL https://deb.nodesource.com/test | bash -
-npm config set loglevel info
-if [ ! -f package.json ]; then
- wget https://github.com/nhubbard/MagicMirror/archive/v2-beta.zip
- unzip v2-beta.zip
- cd MagicMirror-2-beta
-fi
-npm install
-echo "We're ready! Run `npm start` from the MagicMirror-2-beta directory (not over SSH) and enjoy MagicMirror2!"
diff --git a/installers/raspberry.sh b/installers/raspberry.sh
new file mode 100644
index 00000000..26591329
--- /dev/null
+++ b/installers/raspberry.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+# $$\ $$\ $$\ $$\ $$\ $$\ $$$$$$\
+# $$$\ $$$ | \__| $$$\ $$$ |\__| $$ __$$\
+# $$$$\ $$$$ | $$$$$$\ $$$$$$\ $$\ $$$$$$$\ $$$$\ $$$$ |$$\ $$$$$$\ $$$$$$\ $$$$$$\ $$$$$$\ \__/ $$ |
+# $$\$$\$$ $$ | \____$$\ $$ __$$\ $$ |$$ _____|$$\$$\$$ $$ |$$ |$$ __$$\ $$ __$$\ $$ __$$\ $$ __$$\ $$$$$$ |
+# $$ \$$$ $$ | $$$$$$$ |$$ / $$ |$$ |$$ / $$ \$$$ $$ |$$ |$$ | \__|$$ | \__|$$ / $$ |$$ | \__|$$ ____/
+# $$ |\$ /$$ |$$ __$$ |$$ | $$ |$$ |$$ | $$ |\$ /$$ |$$ |$$ | $$ | $$ | $$ |$$ | $$ |
+# $$ | \_/ $$ |\$$$$$$$ |\$$$$$$$ |$$ |\$$$$$$$\ $$ | \_/ $$ |$$ |$$ | $$ | \$$$$$$ |$$ | $$$$$$$$\
+# \__| \__| \_______| \____$$ |\__| \_______|\__| \__|\__|\__| \__| \______/ \__| \________|
+# $$\ $$ |
+# \$$$$$$ |
+# \______/
+#
+# This is an installer script for MagicMirror2. It works well enough
+# that it can detect if you have Node installed, run a binary script
+# and then download and run MagicMirror2.
+
+echo "Installing helper tools ..."
+sudo apt-get install curl wget build-essential unzip || exit
+ARM=$(uname -m) # Determine which Pi is running.
+NODE_LATEST="v5.10.1" # Set the latest version here.
+DOWNLOAD_URL="https://nodejs.org/dist/latest/node-$NODE_LATEST-linux-$ARM.tar.gz" # Construct the download URL.
+
+echo "Installing Latest Node.js ..."
+mkdir ~/.MagicMirrorInstaller || exit
+cd ~/.MagicMirrorInstaller || exit
+wget $DOWNLOAD_URL || exit # Download the file given.
+tar xvf node-$NODE_LATEST-linux-$ARM.tar.gz || exit
+cd node* || exit
+sudo cp -R * /usr/local || exit
+cd ~ || exit
+rm -Rf ~/.MagicMirrorInstaller || exit
+
+echo "Cloning MagicMirror ..."
+git clone -b v2-beta https://github.com/MichMich/MagicMirror.git || exit
+cd ~/MagicMirror || exit
+npm install || exit
+echo "We're ready! Run `DISPLAY=:0 npm start` from the MagicMirror directory."
diff --git a/js/app.js b/js/app.js
new file mode 100644
index 00000000..8257bcd0
--- /dev/null
+++ b/js/app.js
@@ -0,0 +1,133 @@
+/* Magic Mirror
+ * The Core App (Server)
+ *
+ * By Michael Teeuw http://michaelteeuw.nl
+ * MIT Licensed.
+ */
+
+var fs = require("fs");
+var Server = require(__dirname + "/server.js");
+var defaultModules = require(__dirname + "/../modules/default/defaultmodules.js");
+var path = require("path");
+
+/* App - The core app.
+ */
+var App = function() {
+ var nodeHelpers = [];
+
+ /* loadConfig(callback)
+ * Loads the config file. combines it with the defaults,
+ * and runs the callback with the found config as argument.
+ *
+ * argument callback function - The callback function.
+ */
+
+ var loadConfig = function(callback) {
+ console.log("Loading config ...");
+ var defaults = require(__dirname + "/defaults.js");
+ var configFilename = path.resolve(__dirname + "/../config/config.js");
+ try {
+ fs.accessSync(configFilename, fs.F_OK);
+ var c = require(configFilename);
+ var config = Object.assign(defaults, c);
+ callback(config);
+ } catch (e) {
+ console.error('WARNING! Could not find config. Please create one.');
+ callback(defaults);
+ }
+ };
+
+ /* loadModule(module)
+ * Loads a specific module.
+ *
+ * argument module string - The name of the module (including subpath).
+ */
+ var loadModule = function(module) {
+
+ var elements = module.split("/");
+ var moduleName = elements[elements.length - 1];
+ var moduleFolder = __dirname + "/../modules/" + module;
+
+ if (defaultModules.indexOf(moduleName) !== -1) {
+ moduleFolder = __dirname + "/../modules/default/" + module;
+ }
+
+ var helperPath = moduleFolder + "/node_helper.js";
+
+ var loadModule = true;
+ try {
+ fs.accessSync(helperPath, fs.R_OK);
+ } catch (e) {
+ loadModule = false;
+ console.log("No helper found for module: " + moduleName + ".");
+ }
+
+ if (loadModule) {
+ var Module = require(helperPath);
+ var m = new Module();
+ m.setName(moduleName);
+ m.setPath(path.resolve(moduleFolder));
+ nodeHelpers.push(m);
+ }
+ };
+
+ /* loadModules(modules)
+ * Loads all modules.
+ *
+ * argument module string - The name of the module (including subpath).
+ */
+ var loadModules = function(modules) {
+ console.log("Loading module helpers ...");
+
+ for (var m in modules) {
+ loadModule(modules[m]);
+ }
+
+ console.log("All module helpers loaded.");
+ };
+
+ /* start(callback)
+ * This methods starts the core app.
+ * It loads the config, then it loads all modules.
+ * When it's done it executs the callback with the config as argument.
+ *
+ * argument callback function - The callback function.
+ */
+ this.start = function(callback) {
+
+ loadConfig(function(c) {
+ config = c;
+
+ var modules = [];
+
+ for (var m in config.modules) {
+ var module = config.modules[m];
+ if (modules.indexOf(module.module) === -1) {
+ modules.push(module.module);
+ }
+ }
+
+ loadModules(modules);
+
+ var server = new Server(config, function(app, io) {
+ console.log("Server started ...");
+
+ for (var h in nodeHelpers) {
+ var nodeHelper = nodeHelpers[h];
+ nodeHelper.setExpressApp(app);
+ nodeHelper.setSocketIO(io);
+ nodeHelper.start();
+ }
+
+ console.log("Sockets connected & modules started ...");
+
+ if (typeof callback === 'function') {
+ callback(config);
+ }
+
+ });
+ });
+ };
+};
+
+module.exports = new App();
\ No newline at end of file
diff --git a/js/class.js b/js/class.js
index 726e1abc..69b0cb09 100644
--- a/js/class.js
+++ b/js/class.js
@@ -4,66 +4,66 @@
*/
// Inspired by base2 and Prototype
-(function(){
- var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
+(function() {
+ var initializing = false;
+ var fnTest = /xyz/.test(function() {xyz;}) ? /\b_super\b/ : /.*/;
- // The base Class implementation (does nothing)
- this.Class = function(){};
+ // The base Class implementation (does nothing)
+ this.Class = function() {};
- // Create a new Class that inherits from this class
- Class.extend = function(prop) {
- var _super = this.prototype;
+ // Create a new Class that inherits from this class
+ Class.extend = function(prop) {
+ var _super = this.prototype;
- // Instantiate a base class (but only create the instance,
- // don't run the init constructor)
- initializing = true;
- var prototype = new this();
- initializing = false;
+ // Instantiate a base class (but only create the instance,
+ // don't run the init constructor)
+ initializing = true;
+ var prototype = new this();
+ initializing = false;
- // Copy the properties over onto the new prototype
- for (var name in prop) {
- // Check if we're overwriting an existing function
- prototype[name] = typeof prop[name] == "function" &&
- typeof _super[name] == "function" && fnTest.test(prop[name]) ?
- (function(name, fn){
- return function() {
- var tmp = this._super;
+ // Copy the properties over onto the new prototype
+ for (var name in prop) {
+ // Check if we're overwriting an existing function
+ prototype[name] = typeof prop[name] == "function" &&
+ typeof _super[name] == "function" && fnTest.test(prop[name]) ?
+ (function(name, fn) {
+ return function() {
+ var tmp = this._super;
- // Add a new ._super() method that is the same method
- // but on the super-class
- this._super = _super[name];
+ // Add a new ._super() method that is the same method
+ // but on the super-class
+ this._super = _super[name];
- // The method only need to be bound temporarily, so we
- // remove it when we're done executing
- var ret = fn.apply(this, arguments);
- this._super = tmp;
+ // The method only need to be bound temporarily, so we
+ // remove it when we're done executing
+ var ret = fn.apply(this, arguments);
+ this._super = tmp;
- return ret;
- };
+ return ret;
+ };
})(name, prop[name]) :
prop[name];
- }
+ }
- // The dummy class constructor
- function Class() {
- // All construction is actually done in the init method
- if ( !initializing && this.init )
- this.init.apply(this, arguments);
- }
+ // The dummy class constructor
+ function Class() {
+ // All construction is actually done in the init method
+ if (!initializing && this.init)
+ this.init.apply(this, arguments);
+ }
- // Populate our constructed prototype object
- Class.prototype = prototype;
+ // Populate our constructed prototype object
+ Class.prototype = prototype;
- // Enforce the constructor to be what we expect
- Class.prototype.constructor = Class;
+ // Enforce the constructor to be what we expect
+ Class.prototype.constructor = Class;
- // And make this class extendable
- Class.extend = arguments.callee;
+ // And make this class extendable
+ Class.extend = arguments.callee;
- return Class;
- };
+ return Class;
+ };
})();
-
/*************** DO NOT EDIT THE LINE BELOW ***************/
-if (typeof module !== 'undefined') {module.exports = Class;}
+if (typeof module !== "undefined") {module.exports = Class;}
diff --git a/js/defaults.js b/js/defaults.js
index 52df69fe..e3dc3d2b 100644
--- a/js/defaults.js
+++ b/js/defaults.js
@@ -10,49 +10,48 @@
var defaults = {
port: 8080,
- language: 'en',
+ language: "en",
timeFormat: 24,
modules: [
{
- module: 'helloworld',
- position: 'upper_third',
+ module: "helloworld",
+ position: "upper_third",
+ classes: "large thin",
config: {
- text: 'Magic Mirror V2',
- classes: 'large thin'
+ text: "Magic Mirror2"
}
},
{
- module: 'helloworld',
- position: 'middle_center',
+ module: "helloworld",
+ position: "middle_center",
config: {
- text: 'Please create a config file.'
+ text: "Please create a config file."
}
},
{
- module: 'helloworld',
- position: 'middle_center',
+ module: "helloworld",
+ position: "middle_center",
+ classes: "small dimmed",
config: {
- text: 'See README for more information.',
- classes: 'small dimmed'
+ text: "See README for more information."
}
},
{
- module: 'helloworld',
- position: 'bottom_bar',
+ module: "helloworld",
+ position: "bottom_bar",
+ classes: "xsmall dimmed",
config: {
- text: 'www.michaelteeuw.nl',
- classes: 'xsmall dimmed'
+ text: "www.michaelteeuw.nl"
}
},
],
paths: {
- modules: 'modules',
- vendor: 'vendor'
+ modules: "modules",
+ vendor: "vendor"
},
};
-
/*************** DO NOT EDIT THE LINE BELOW ***************/
-if (typeof module !== 'undefined') {module.exports = defaults;}
+if (typeof module !== "undefined") {module.exports = defaults;}
diff --git a/js/electron.js b/js/electron.js
index b3a857a4..5de41003 100755
--- a/js/electron.js
+++ b/js/electron.js
@@ -1,12 +1,8 @@
-'use strict';
+"use strict";
-//load modules
-const walk = require('walk');
-const fs = require('fs');
-const Server = require(__dirname + '/server.js');
-const spawn = require('child_process').spawn;
-const electron = require('electron');
-const defaultModules = require(__dirname + '/../modules/default/defaultmodules.js');
+const Server = require(__dirname + "/server.js");
+const electron = require("electron");
+const core = require(__dirname + "/app.js");
// Config
var config = {};
@@ -15,25 +11,23 @@ const app = electron.app;
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow;
-var nodeHelpers = [];
-
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;
-function createWindow () {
+function createWindow() {
// Create the browser window.
mainWindow = new BrowserWindow({width: 800, height: 600, fullscreen: true, autoHideMenuBar: true, webPreferences: {nodeIntegration: false}});
// and load the index.html of the app.
//mainWindow.loadURL('file://' + __dirname + '../../index.html');
- mainWindow.loadURL('http://localhost:' + config.port);
+ mainWindow.loadURL("http://localhost:" + config.port);
// Open the DevTools.
//mainWindow.webContents.openDevTools();
// Emitted when the window is closed.
- mainWindow.on('closed', function() {
+ mainWindow.on("closed", function() {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
@@ -41,107 +35,32 @@ function createWindow () {
});
}
-function loadConfig (callback) {
- console.log("Loading config ...");
- var defaults = require(__dirname + '/defaults.js');
- var configFilename = __dirname + '/../config/config.js';
-
- try {
- fs.accessSync(configFilename, fs.R_OK);
- var c = require(configFilename);
- var config = Object.assign(defaults, c);
- callback(config);
- } catch (e) {
- callback(defaults);
- }
-}
-
-function loadModule(module) {
-
- var elements = module.split('/');
- var moduleName = elements[elements.length - 1];
- var moduleFolder = __dirname + '/../modules/' + module;
-
- if (defaultModules.indexOf(moduleName) !== -1) {
- moduleFolder = __dirname + '/../modules/default/' + module;
- }
-
- var helperPath = moduleFolder + '/node_helper.js';
-
- var loadModule = true;
- try {
- fs.accessSync(helperPath, fs.R_OK);
- } catch (e) {
- loadModule = false;
- console.log("No helper found for module: " + moduleName + ".");
- }
-
- if (loadModule) {
- var Module = require(helperPath);
- var m = new Module();
- m.setName(moduleName);
- nodeHelpers.push(m);
- }
-}
-
-function loadModules(modules) {
- console.log("Loading module helpers ...");
-
- for (var m in modules) {
- loadModule(modules[m]);
- }
-
- console.log("All module helpers loaded.");
-}
-
-loadConfig(function(c) {
- config = c;
-
- var modules = [];
-
- for (var m in config.modules) {
- var module = config.modules[m];
- if (modules.indexOf(module.module) === -1) {
- modules.push(module.module);
- }
- }
-
- loadModules(modules);
-
- var server = new Server(config, function(io) {
- console.log('Server started ...');
-
- for (var h in nodeHelpers) {
- var nodeHelper = nodeHelpers[h];
- nodeHelper.setSocketIO(io);
- nodeHelper.start();
- }
-
- console.log('Sockets connected & modules started ...');
-
- });
-});
-
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
-app.on('ready', function() {
- console.log('Launching application.');
+app.on("ready", function() {
+ console.log("Launching application.");
createWindow();
});
// Quit when all windows are closed.
-app.on('window-all-closed', function () {
+app.on("window-all-closed", function() {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
- if (process.platform !== 'darwin') {
+ if (process.platform !== "darwin") {
app.quit();
}
});
-app.on('activate', function () {
+app.on("activate", function() {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
createWindow();
}
});
+
+// Start the core application.
+// This starts all node helpers and starts the webserver.
+core.start(function(c) {
+ config = c;
+});
diff --git a/js/loader.js b/js/loader.js
index 4f650ac6..2553a187 100644
--- a/js/loader.js
+++ b/js/loader.js
@@ -2,7 +2,6 @@
/* jshint unused:false */
/* jshint -W061 */
-
/* Magic Mirror
* Module and File loaders.
*
@@ -18,10 +17,8 @@ var Loader = (function() {
var loadedFiles = [];
var moduleObjects = [];
-
/* Private Methods */
-
/* loadModules()
* Loops thru all modules and requests load for every module.
*/
@@ -79,33 +76,31 @@ var Loader = (function() {
var moduleData = modules[m];
var module = moduleData.module;
- var elements = module.split('/');
+ var elements = module.split("/");
var moduleName = elements[elements.length - 1];
- var moduleFolder = config.paths.modules + '/' + module;
+ var moduleFolder = config.paths.modules + "/" + module;
if (defaultModules.indexOf(moduleName) !== -1) {
- moduleFolder = config.paths.modules + '/default/' + module;
+ moduleFolder = config.paths.modules + "/default/" + module;
}
moduleFiles.push({
index: m,
- identifier: 'module_' + m + '_' + module,
+ identifier: "module_" + m + "_" + module,
name: moduleName,
- path: moduleFolder + '/' ,
- file: moduleName + '.js',
+ path: moduleFolder + "/" ,
+ file: moduleName + ".js",
position: moduleData.position,
header: moduleData.header,
config: moduleData.config,
- classes: (typeof moduleData.classes !== 'undefined') ? moduleData.classes + ' ' + module : module
+ classes: (typeof moduleData.classes !== "undefined") ? moduleData.classes + " " + module : module
});
-
}
return moduleFiles;
};
-
/* loadModule(module)
* Load modules via ajax request and create module objects.
*
@@ -113,7 +108,7 @@ var Loader = (function() {
* argument module object - Information about the module we want to load.
*/
var loadModule = function(module, callback) {
- var url = module.path + '/' + module.file;
+ var url = module.path + "/" + module.file;
var afterLoad = function() {
var moduleObject = Module.create(module.name);
@@ -141,21 +136,20 @@ var Loader = (function() {
* argument callback function - Function called when done.
*/
var bootstrapModule = function(module, mObj, callback) {
- Log.info('Bootstrapping module: ' + module.name);
+ Log.info("Bootstrapping module: " + module.name);
mObj.setData(module);
mObj.loadScripts(function() {
- Log.log('Scripts loaded for: ' + module.name);
- mObj.loadStyles(function(){
- Log.log('Styles loaded for: ' + module.name);
+ Log.log("Scripts loaded for: " + module.name);
+ mObj.loadStyles(function() {
+ Log.log("Styles loaded for: " + module.name);
moduleObjects.push(mObj);
callback();
});
});
-
};
/* loadFile(fileName)
@@ -170,27 +164,27 @@ var Loader = (function() {
switch (extension.toLowerCase()) {
case "js":
- Log.log('Load script: ' + fileName);
+ Log.log("Load script: " + fileName);
var script = document.createElement("script");
script.type = "text/javascript";
script.src = fileName;
script.onload = function() {
- if (typeof callback === 'function') {callback();}
+ if (typeof callback === "function") {callback();}
};
document.getElementsByTagName("body")[0].appendChild(script);
break;
case "css":
- Log.log('Load stylesheet: ' + fileName);
+ Log.log("Load stylesheet: " + fileName);
var stylesheet = document.createElement("link");
stylesheet.rel = "stylesheet";
stylesheet.type = "text/css";
stylesheet.href = fileName;
stylesheet.onload = function() {
- if (typeof callback === 'function') {callback();}
+ if (typeof callback === "function") {callback();}
};
document.getElementsByTagName("head")[0].appendChild(stylesheet);
@@ -220,12 +214,12 @@ var Loader = (function() {
loadFile: function(fileName, module, callback) {
if (loadedFiles.indexOf(fileName.toLowerCase()) !== -1) {
- Log.log('File already loaded: ' + fileName);
+ Log.log("File already loaded: " + fileName);
callback();
return;
}
- if (fileName.indexOf('http://') === 0 || fileName.indexOf('https://') === 0 || fileName.indexOf('/') !== -1) {
+ if (fileName.indexOf("http://") === 0 || fileName.indexOf("https://") === 0 || fileName.indexOf("/") !== -1) {
// This is an absolute or relative path.
// Load it and then return.
loadedFiles.push(fileName.toLowerCase());
@@ -237,7 +231,7 @@ var Loader = (function() {
// This file is available in the vendor folder.
// Load it from this vendor folder.
loadedFiles.push(fileName.toLowerCase());
- loadFile(config.paths.vendor+'/'+vendor[fileName], callback);
+ loadFile(config.paths.vendor + "/" + vendor[fileName], callback);
return;
}
diff --git a/js/logger.js b/js/logger.js
index 1389c5f9..994fe534 100644
--- a/js/logger.js
+++ b/js/logger.js
@@ -8,11 +8,9 @@
* MIT Licensed.
*/
-
// This logger is very simple, but needs to be extended.
// This system can eventually be used to push the log messages to an external target.
-
var Log = (function() {
return {
info: function(message) {
diff --git a/js/main.js b/js/main.js
index a45c56b1..ed89826e 100644
--- a/js/main.js
+++ b/js/main.js
@@ -22,7 +22,7 @@ var MM = (function() {
for (var m in modules) {
var module = modules[m];
- if (typeof module.data.position === 'string') {
+ if (typeof module.data.position === "string") {
var wrapper = selectWrapper(module.data.position);
@@ -30,14 +30,14 @@ var MM = (function() {
dom.id = module.identifier;
dom.className = module.name;
- if (typeof module.data.classes === 'string') {
- dom.className = 'module '+ dom.className + ' ' + module.data.classes;
+ 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 !== '') {
+ if (typeof module.data.header !== "undefined" && module.data.header !== "") {
var moduleHeader = document.createElement("header");
moduleHeader.innerHTML = module.data.header;
dom.appendChild(moduleHeader);
@@ -51,7 +51,7 @@ var MM = (function() {
}
}
- sendNotification('DOM_OBJECTS_CREATED');
+ sendNotification("DOM_OBJECTS_CREATED");
};
/* selectWrapper(position)
@@ -60,10 +60,10 @@ var MM = (function() {
* argument position string - The name of the position.
*/
var selectWrapper = function(position) {
- var classes = position.replace('_',' ');
+ var classes = position.replace("_"," ");
var parentWrapper = document.getElementsByClassName(classes);
if (parentWrapper.length > 0) {
- var wrapper = parentWrapper[0].getElementsByClassName('container');
+ var wrapper = parentWrapper[0].getElementsByClassName("container");
if (wrapper.length > 0) {
return wrapper[0];
}
@@ -117,7 +117,6 @@ var MM = (function() {
}
};
-
/* moduleNeedsUpdate(module, newContent)
* Check if the content has changed.
*
@@ -128,9 +127,9 @@ var MM = (function() {
*/
var moduleNeedsUpdate = function(module, newContent) {
var moduleWrapper = document.getElementById(module.identifier);
- var contentWrapper = moduleWrapper.getElementsByClassName('module-content')[0];
+ var contentWrapper = moduleWrapper.getElementsByClassName("module-content")[0];
- var tempWrapper = document.createElement('div');
+ var tempWrapper = document.createElement("div");
tempWrapper.appendChild(newContent);
return tempWrapper.innerHTML !== contentWrapper.innerHTML;
@@ -144,7 +143,7 @@ var MM = (function() {
*/
var updateModuleContent = function(module, content) {
var moduleWrapper = document.getElementById(module.identifier);
- var contentWrapper = moduleWrapper.getElementsByClassName('module-content')[0];
+ var contentWrapper = moduleWrapper.getElementsByClassName("module-content")[0];
contentWrapper.innerHTML = null;
contentWrapper.appendChild(content);
@@ -163,14 +162,15 @@ var MM = (function() {
moduleWrapper.style.transition = "opacity " + speed / 1000 + "s";
moduleWrapper.style.opacity = 0;
- setTimeout(function() {
+ clearTimeout(module.showHideTimer);
+ module.showHideTimer = setTimeout(function() {
// To not take up any space, we just make the position absolute.
// since it's fade out anyway, we can see it lay above or
// below other modules. This works way better than adjusting
// the .display property.
- moduleWrapper.style.position = 'absolute';
+ moduleWrapper.style.position = "absolute";
- if (typeof callback === 'function') { callback(); }
+ if (typeof callback === "function") { callback(); }
}, speed);
}
};
@@ -187,11 +187,12 @@ var MM = (function() {
if (moduleWrapper !== null) {
moduleWrapper.style.transition = "opacity " + speed / 1000 + "s";
// Restore the postition. See hideModule() for more info.
- moduleWrapper.style.position = 'static';
+ moduleWrapper.style.position = "static";
moduleWrapper.style.opacity = 1;
- setTimeout(function() {
- if (typeof callback === 'function') { callback(); }
+ clearTimeout(module.showHideTimer);
+ module.showHideTimer = setTimeout(function() {
+ if (typeof callback === "function") { callback(); }
}, speed);
}
@@ -201,9 +202,9 @@ var MM = (function() {
* Loads the core config and combines it with de system defaults.
*/
var loadConfig = function() {
- if (typeof config === 'undefined') {
+ if (typeof config === "undefined") {
config = defaults;
- Log.error('Config file is missing! Please create a config file.');
+ Log.error("Config file is missing! Please create a config file.");
return;
}
@@ -228,13 +229,13 @@ var MM = (function() {
var newModules = [];
var searchClasses = className;
- if (typeof className === 'string') {
- searchClasses = className.split(' ');
+ if (typeof className === "string") {
+ searchClasses = className.split(" ");
}
for (var m in modules) {
var module = modules[m];
- var classes = module.data.classes.toLowerCase().split(' ');
+ var classes = module.data.classes.toLowerCase().split(" ");
for (var c in searchClasses) {
var searchClass = searchClasses[c];
@@ -259,13 +260,13 @@ var MM = (function() {
var newModules = [];
var searchClasses = className;
- if (typeof className === 'string') {
- searchClasses = className.split(' ');
+ if (typeof className === "string") {
+ searchClasses = className.split(" ");
}
for (var m in modules) {
var module = modules[m];
- var classes = module.data.classes.toLowerCase().split(' ');
+ var classes = module.data.classes.toLowerCase().split(" ");
var foundClass = false;
for (var c in searchClasses) {
var searchClass = searchClasses[c];
@@ -316,13 +317,12 @@ var MM = (function() {
}
};
- if (typeof modules.withClass === 'undefined') { Object.defineProperty(modules, 'withClass', {value: withClass, enumerable: false}); }
- if (typeof modules.exceptWithClass === 'undefined') { Object.defineProperty(modules, 'exceptWithClass', {value: exceptWithClass, enumerable: false}); }
- if (typeof modules.exceptModule === 'undefined') { Object.defineProperty(modules, 'exceptModule', {value: exceptModule, enumerable: false}); }
- if (typeof modules.enumerate === 'undefined') { Object.defineProperty(modules, 'enumerate', {value: enumerate, enumerable: false}); }
+ if (typeof modules.withClass === "undefined") { Object.defineProperty(modules, "withClass", {value: withClass, enumerable: false}); }
+ if (typeof modules.exceptWithClass === "undefined") { Object.defineProperty(modules, "exceptWithClass", {value: exceptWithClass, enumerable: false}); }
+ if (typeof modules.exceptModule === "undefined") { Object.defineProperty(modules, "exceptModule", {value: exceptModule, enumerable: false}); }
+ if (typeof modules.enumerate === "undefined") { Object.defineProperty(modules, "enumerate", {value: enumerate, enumerable: false}); }
};
-
return {
/* Public Methods */
@@ -330,7 +330,7 @@ var MM = (function() {
* Main init method.
*/
init: function() {
- Log.info('Initializing MagicMirror.');
+ Log.info("Initializing MagicMirror.");
loadConfig();
Loader.loadModules();
},
@@ -347,8 +347,8 @@ var MM = (function() {
modules[module.data.index] = module;
}
- Log.info('All modules started!');
- sendNotification('ALL_MODULES_STARTED');
+ Log.info("All modules started!");
+ sendNotification("ALL_MODULES_STARTED");
createDomObjects();
},
@@ -362,17 +362,17 @@ var MM = (function() {
*/
sendNotification: function(notification, payload, sender) {
if (arguments.length < 3) {
- Log.error('sendNotification: Missing arguments.');
+ Log.error("sendNotification: Missing arguments.");
return;
}
- if (typeof notification !== 'string') {
- Log.error('sendNotification: Notification should be a string.');
+ if (typeof notification !== "string") {
+ Log.error("sendNotification: Notification should be a string.");
return;
}
if (!(sender instanceof Module)) {
- Log.error('sendNotification: Sender should be a module.');
+ Log.error("sendNotification: Sender should be a module.");
return;
}
@@ -388,7 +388,7 @@ var MM = (function() {
*/
updateDom: function(module, speed) {
if (!(module instanceof Module)) {
- Log.error('updateDom: Sender should be a module.');
+ Log.error("updateDom: Sender should be a module.");
return;
}
diff --git a/js/module.js b/js/module.js
index 2c2c18d2..9be652bf 100644
--- a/js/module.js
+++ b/js/module.js
@@ -17,6 +17,9 @@ var Module = Class.extend({
// Module config defaults.
defaults: {},
+ // Timer reference used for showHide animation callbacks.
+ showHideTimer: null,
+
/* init()
* Is called when the module is instantiated.
*/
@@ -28,7 +31,7 @@ var Module = Class.extend({
* Is called when the module is started.
*/
start: function() {
- Log.info('Starting module: ' + this.name);
+ Log.info("Starting module: " + this.name);
},
/* getScripts()
@@ -82,9 +85,9 @@ var Module = Class.extend({
*/
notificationReceived: function(notification, payload, sender) {
if (sender) {
- Log.log(this.name + ' received a module notification: ' + notification + ' from sender: ' + sender.name);
+ Log.log(this.name + " received a module notification: " + notification + " from sender: " + sender.name);
} else {
- Log.log(this.name + ' received a system notification: ' + notification);
+ Log.log(this.name + " received a system notification: " + notification);
}
},
@@ -95,10 +98,9 @@ var Module = Class.extend({
* argument payload mixed - The payload of the notification.
*/
socketNotificationReceived: function(notification, payload) {
- Log.log(this.name + ' received a socket notification: ' + notification + ' - Payload: ' + payload);
+ Log.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload);
},
-
/*********************************************
* The methods below don't need subclassing. *
*********************************************/
@@ -131,7 +133,7 @@ var Module = Class.extend({
* It also registers the notification callback.
*/
socket: function() {
- if (typeof this._socket === 'undefined') {
+ if (typeof this._socket === "undefined") {
this._socket = this._socket = new MMSocket(this.name);
}
@@ -151,7 +153,7 @@ var Module = Class.extend({
* return string - File path.
*/
file: function(file) {
- return this.data.path + '/' + file;
+ return this.data.path + "/" + file;
},
/* loadStyles()
@@ -258,16 +260,16 @@ Module.create = function(name) {
//Define the clone method for later use.
function cloneObject(obj) {
- if (obj === null || typeof obj !== 'object') {
- return obj;
- }
+ if (obj === null || typeof obj !== "object") {
+ return obj;
+ }
- var temp = obj.constructor(); // give temp the original obj's constructor
- for (var key in obj) {
- temp[key] = cloneObject(obj[key]);
- }
+ var temp = obj.constructor(); // give temp the original obj's constructor
+ for (var key in obj) {
+ temp[key] = cloneObject(obj[key]);
+ }
- return temp;
+ return temp;
}
var moduleDefinition = Module.definitions[name];
@@ -281,6 +283,6 @@ Module.create = function(name) {
};
Module.register = function(name, moduleDefinition) {
- Log.log('Module registered: ' + name);
+ Log.log("Module registered: " + name);
Module.definitions[name] = moduleDefinition;
};
diff --git a/js/server.js b/js/server.js
index aac4d839..b5eab036 100644
--- a/js/server.js
+++ b/js/server.js
@@ -5,29 +5,29 @@
* MIT Licensed.
*/
-var express = require('express');
-var app = require('express')();
-var server = require('http').Server(app);
-var io = require('socket.io')(server);
-var path = require('path');
+var express = require("express");
+var app = require("express")();
+var server = require("http").Server(app);
+var io = require("socket.io")(server);
+var path = require("path");
var Server = function(config, callback) {
console.log("Starting server op port " + config.port + " ... ");
server.listen(config.port);
- app.use('/js', express.static(__dirname));
- app.use('/config', express.static(path.resolve(__dirname + '/../config')));
- app.use('/css', express.static(path.resolve(__dirname + '/../css')));
- app.use('/fonts', express.static(path.resolve(__dirname + '/../fonts')));
- app.use('/modules', express.static(path.resolve(__dirname + '/../modules')));
- app.use('/vendor', express.static(path.resolve(__dirname + '/../vendor')));
+ app.use("/js", express.static(__dirname));
+ app.use("/config", express.static(path.resolve(__dirname + "/../config")));
+ app.use("/css", express.static(path.resolve(__dirname + "/../css")));
+ app.use("/fonts", express.static(path.resolve(__dirname + "/../fonts")));
+ app.use("/modules", express.static(path.resolve(__dirname + "/../modules")));
+ app.use("/vendor", express.static(path.resolve(__dirname + "/../vendor")));
- app.get('/', function (req, res) {
- res.sendFile(path.resolve(__dirname + '/../index.html'));
+ app.get("/", function(req, res) {
+ res.sendFile(path.resolve(__dirname + "/../index.html"));
});
- if (typeof callback === 'function') {
- callback(io);
+ if (typeof callback === "function") {
+ callback(app, io);
}
};
diff --git a/js/socket.js b/js/socket.js
index 8f3abaf1..c62c24da 100644
--- a/js/socket.js
+++ b/js/socket.js
@@ -11,22 +11,21 @@ var MMSocket = function(moduleName) {
var self = this;
- if (typeof moduleName !== 'string') {
- throw new Error('Please set the module name for the MMSocket.');
+ if (typeof moduleName !== "string") {
+ throw new Error("Please set the module name for the MMSocket.");
}
self.moduleName = moduleName;
-
- self.socket = io('http://localhost:8080');
- self.socket.on('notification', function (data) {
+ self.socket = io("http://localhost:8080");
+ self.socket.on("notification", function(data) {
MM.sendNotification(data.notification, data.payload, Socket);
});
return {
sendMessage: function(notification, payload, sender) {
- Log.log('Send socket message: ' + notification);
- self.socket.emit('notification', {
+ Log.log("Send socket message: " + notification);
+ self.socket.emit("notification", {
notification: notification,
sender: sender,
payload: payload
diff --git a/js/socketclient.js b/js/socketclient.js
index c834679a..f7d1843a 100644
--- a/js/socketclient.js
+++ b/js/socketclient.js
@@ -1,40 +1,39 @@
var MMSocket = function(moduleName) {
var self = this;
- if (typeof moduleName !== 'string') {
- throw new Error('Please set the module name for the MMSocket.');
+ if (typeof moduleName !== "string") {
+ throw new Error("Please set the module name for the MMSocket.");
}
self.moduleName = moduleName;
// Private Methods
- socket = io.connect('/' + self.moduleName);
+ socket = io.connect("/" + self.moduleName);
var notificationCallback = function() {};
var onevent = socket.onevent;
- socket.onevent = function (packet) {
+ socket.onevent = function(packet) {
var args = packet.data || [];
- onevent.call (this, packet); // original call
+ onevent.call(this, packet); // original call
packet.data = ["*"].concat(args);
onevent.call(this, packet); // additional call to catch-all
};
// register catch all.
- socket.on('*', function (notification, payload) {
- if (notification !== '*') {
+ socket.on("*", function(notification, payload) {
+ if (notification !== "*") {
//console.log('Received notification: ' + notification +', payload: ' + payload);
notificationCallback(notification, payload);
}
});
-
// Public Methods
this.setNotificationCallback = function(callback) {
notificationCallback = callback;
};
this.sendNotification = function(notification, payload) {
- if (typeof payload === 'undefined') {
+ if (typeof payload === "undefined") {
payload = {};
}
socket.emit(notification, payload);
diff --git a/modules/README.md b/modules/README.md
new file mode 100644
index 00000000..965051b0
--- /dev/null
+++ b/modules/README.md
@@ -0,0 +1,461 @@
+# MagicMirror² Module Development Documentation
+
+This document describes the way to develop your own MagicMirror² modules.
+
+## Module structure
+
+All modules are loaded in de `modules` folder. The default modules are grouped together in the `modules/default` folder. Your module should be placed in a subfolder of `modules`. Note that any file or folder your create in the `modules` folder will be ignored by git, allowing you to upgrade the MagicMirror² without the loss of your files.
+
+A module can be placed in one single folder. Or multiple modules can be grouped in a subfoler. Note that name of the module must be unique. Even when a module with a similar name is placed in a different folder, they can't be loaded at the same time.
+
+### Files
+- **modulename/modulename.js** - This is your core module script.
+- **modulename/node_helper.js** - This is an optional helper that whill be loaded by the node script. The node helper and module script can communicate with each other using an intergrated socket system.
+- **modulename/public** - Any files in this folder can be accesed via the browser on `/modulename/filename.ext`.
+- **modulename/anyfileorfolder** Any other file or folder in the module folder can be used by the core module script. For example: *modulename/css/modulename.css* would be a good path for your additional module styles.
+
+## Core module file: modulename.js
+This is the script in which the module will be defined. This script is required in order for the module to be used. In it's most simple form, the core module file must contain:
+````javascript
+Module.register("modulename",{});
+````
+Of course, the above module would not do anything fancy, so it's good to look at one of the simplest modules: **helloworld**:
+
+````javascript
+//helloworld.js:
+
+Module.register("helloworld",{
+ // Default module config.
+ defaults: {
+ text: "Hello World!"
+ },
+
+ // Override dom generator.
+ getDom: function() {
+ var wrapper = document.createElement("div");
+ wrapper.innerHTML = this.config.text;
+ return wrapper;
+ }
+});
+````
+
+As you can see, the `Module.register()` method takes two arguments: the name of the module and an object with the module properties.
+
+### Available module instance properties
+After the module is initialized, the module instance has a few available module properties:
+
+####`this.name`
+**String**
+
+The name of the module.
+
+####`this.identifier`
+**String**
+
+This is a unique identifier for the module instance.
+
+####`this.hidden`
+**Boolean**
+
+This represents if the module is currently hidden (faded away).
+
+####`this.config`
+**Boolean**
+
+The configuration of the module instance as set in the user's config.js file. This config will also contain the module's defaults if these properties are not over written by the user config.
+
+####`this.data`
+**Object**
+
+The data object contains additional metadata about the module instance:
+- `data.classes` - The classes which are added to the module dom wrapper.
+- `data.file` - The filename of the core module file.
+- `data.path` - The path of the module folder.
+- `data.header` - The header added to the module.
+- `data.position` - The position in which the instance will be shown.
+
+
+####`defaults: {}`
+Any properties defined in the defaults object, will be merged with the module config as defined in the user's config.js file. This is the best place to set your modules's configuration defaults. Any of the module configuration properties can be accessed using `this.config.propertyName`, but more about that later.
+
+### Subclassable module methods
+
+####`init()`
+This method is called when a module gets instantiated. In most cases you do not need to subclass this method.
+
+####`start()`
+This method is called when all modules are loaded an the system is ready to boot up. Keep in mind that the dom object for the module is not yet created. The start method is a perfect place to define any additional module properties:
+
+**Example:**
+````javascript
+start: function() {
+ this.mySpecialProperty = "So much wow!";
+ Log.log(this.name + ' is started!');
+}
+````
+
+####`getScripts()`
+**Should return: Array**
+
+The getScripts method is called to request any additional scripts that need to be loaded. This method should therefor return an array with strings. If you want to return a full path to a file in the module folder, use the `this.file('filename.js')` method. In all cases the loader will only load a file once. It even checks if the file is available in the default vendor folder.
+
+**Example:**
+````javascript
+getScripts: function() {
+ return [
+ 'script.js', // will try to load it from the vendor folder, otherwise it will load is from the module folder.
+ 'moment.js', // this file is available in the vendor folder, so it doesn't need to be avialable in the module folder.
+ this.file('anotherfile.js'), // this file will be loaded straight from the module folder.
+ 'https://code.jquery.com/jquery-2.2.3.min.js', // this file will be loaded from the jquery servers.
+ ]
+}
+
+````
+**Note:** If a file can not be loaded, the boot up of the mirror will stall. Therefore it's advised not to use any external urls.
+
+
+####`getStyles()`
+**Should return: Array**
+
+The getStyles method is called to request any additional scripts that need to be loaded. This method should therefor return an array with strings. If you want to return a full path to a file in the module folder, use the `this.file('filename.css')` method. In all cases the loader will only load a file once. It even checks if the file is available in the default vendor folder.
+
+**Example:**
+````javascript
+getStyles: function() {
+ return [
+ 'script.css', // will try to load it from the vendor folder, otherwise it will load is from the module folder.
+ 'font-awesome.css', // this file is available in the vendor folder, so it doesn't need to be avialable in the module folder.
+ this.file('anotherfile.css'), // this file will be loaded straight from the module folder.
+ 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css', // this file will be loaded from the bootstrapcdn servers.
+ ]
+}
+
+````
+**Note:** If a file can not be loaded, the boot up of the mirror will stall. Therefore it's advised not to use any external urls.
+
+####`getDom()`
+**Should return:** Dom Object
+
+Whenever the MagicMirror needs to update the information on screen (because it starts, or because your module asked a refresh using `this.updateDom()`), the system calls the getDom method. This method should therefor return a dom object.
+
+**Example:**
+````javascript
+getDom: function() {
+ var wrapper = document.createElement("div");
+ wrapper.innerHTML = 'Hello world!';
+ return wrapper;
+}
+
+````
+
+####`notificationReceived(notification, payload, sender)`
+
+That MagicMirror core has the ability to send notifications to modules. Or even better: the modules have the possibility to send notifications to other modules. When this module is called, it has 3 arguments:
+
+- `notification` - String - The notification identifier.
+- `payload` - AnyType - The payload of a notification.
+- `sender` - Module - The sender of the notification. If this argument is `undefined`, the sender of the notififiction is the core system.
+
+**Example:**
+````javascript
+notificationReceived: function(notification, payload, sender) {
+ if (sender) {
+ Log.log(this.name + " received a module notification: " + notification + " from sender: " + sender.name);
+ } else {
+ Log.log(this.name + " received a system notification: " + notification);
+ }
+}
+````
+
+**Note:** the system sends two notifiations 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.
+
+
+####`socketNotificationReceived: function(notification, payload)`
+When using a node_helper, the node helper can send your module notifications. When this module is called, it has 2 arguments:
+
+- `notification` - String - The notification identifier.
+- `payload` - AnyType - The payload of a notification.
+
+**Note 1:** When a node helper send a notification, all modules of that module type receive the same notifications.
+**Note 2:** The socket connection is established as soon as the module sends it's first message using [sendSocketNotification](thissendsocketnotificationnotification-payload).
+
+**Example:**
+````javascript
+socketNotificationReceived: function(notification, payload) {
+ Log.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload);
+},
+````
+
+### Module instance methods
+
+Each module instance has some handy methods which can be helpfull building your module.
+
+
+####`this.file(filename)`
+***filename* String** - The name of the file you want to create the path for.
+**Returns String**
+
+If you want to create a path to a file in your module folder, use the `file()` method. It returns the path to the filename given as the attribute. Is method comes in handy when configuring the [getScripts](#getscripts) and [getStyles](#getstyles) methods.
+
+####`this.updateDom(speed)`
+***speed* Number** - Optional. Animation speed in milliseconds.
+
+Whenever your module need to be updated, call the `updateDom(speed)` method. It requests the MagicMirror core to update it's dom object. If you define the speed, the content update will be animated, but only if the content will realy change.
+
+As an example: the clock modules calls this method every second:
+
+````javascript
+...
+start: function() {
+ var self = this;
+ setInterval(function() {
+ self.updateDom(); // no speed defined, so it updates instantly.
+ }, 1000); //perform every 1000 milliseconds.
+},
+...
+````
+
+####`this.sendNotification(notification, payload)`
+***notification* String** - The notification identifier.
+***payload* AnyType** - Optional. A notification payload.
+
+If you want to send a notification to all other modules, use the `sendNotification(notification, payload)`. All other modules will receive the message via the [notificationReceived](#notificationreceivednotification-payload-sender) method. In that case, the sender is automaticly set to the instance calling the sendNotification method.
+
+**Example:**
+````javascript
+this.sendNotification('MYMODULE_READY_FOR_ACTION', {foo:bar});
+````
+
+####`this.sendSocketNotification(notification, payload)`
+***notification* String** - The notification identifier.
+***payload* AnyType** - Optional. A notification payload.
+
+If you want to send a notification to the node_helper, use the `sendSocketNotification(notification, payload)`. Only the node_helper of this module will recieve the socket notification.
+
+**Example:**
+````javascript
+this.sendSocketNotification('SET_CONFIG', this.config);
+````
+
+####`this.hide(speed, callback)`
+***speed* Number** - Optional, The speed of the hide animation in milliseconds.
+***callback* Function** - Optional, The callback after the hide animation is finished.
+
+To hide a module, you can call the `hide(speed, callback)` method. You can call the hide method on the module instance itselve using `this.hide()`, but of course you can also hide an other module using `anOtherModule.hide()`.
+
+**Note 1:** If the hide animation is canceled, for instance because the show method is called before the hide animation was finished, the callback will not be called.
+**Note 2:** If the hide animation is hijacked (an other method calls hide on the same module), the callback will not be called.
+**Note 3:** If the dom is not yet created, the hide method won't work. Wait for the `DOM_OBJECTS_CREATED` [notification](#notificationreceivednotification-payload-sender).
+
+####`this.show(speed, callback)`
+***speed* Number** - Optional, The speed of the show animation in milliseconds.
+***callback* Function** - Optional, The callback after the show animation is finished.
+
+To show a module, you can call the `show(speed, callback)` method. You can call the show method on the module instance itselve using `this.show()`, but of course you can also show an other module using `anOtherModule.show()`.
+
+**Note 1:** If the show animation is canceled, for instance because the hide method is called before the show animation was finished, the callback will not be called.
+**Note 2:** If the show animation is hijacked (an other method calls show on the same module), the callback will not be called.
+**Note 3:** If the dom is not yet created, the show method won't work. Wait for the `DOM_OBJECTS_CREATED` [notification](#notificationreceivednotification-payload-sender).
+
+
+## The Node Helper: node_helper.js
+
+The node helper is a Node.js script that is able to do some backend task to support your module. For every module type, only one node helper instance will be created. For example: if your MagicMirror uses two calendar modules, there will be only one calendar node helper instantiated.
+
+**Note:** Because there is only one node helper per module type, there is no default config available within your module. It's your task to send the desired config from your module to your node helper.
+
+In it's most simple form, the node_helper.js file must contain:
+
+````javascript
+var NodeHelper = require("node_helper");
+module.exports = NodeHelper.create({});
+````
+
+Of course, the above helper would not do anything usefull. So with the information above, you should be able to make it a bit more sophisticated.
+
+### Available module instance properties
+
+####`this.name`
+**String**
+
+The name of the module
+
+####`this.path`
+**String**
+
+The path of the module
+
+####`this.expressApp`
+**Express App Instance**
+
+This is a link to the express instance. It will allow you to define extra routes.
+
+**Example:**
+````javascript
+start: function() {
+ this.expressApp.get('/foobar', function (req, res) {
+ res.send('GET request to /foobar');
+ });
+}
+````
+
+**Note: ** By default, a public path to your module's public folder will be created:
+````javascript
+this.expressApp.use("/" + this.name, express.static(this.path + "/public"));
+````
+
+####`this.io`
+**Socket IO Instance**
+
+This is a link to the IO instance. It will allow you to do some Socket.IO magic. In most cases you won't need this, since the Node Helper has a few convenience methods to make this simple.
+
+### Subclassable module methods
+
+####`init()`
+This method is called when a node helper gets instantiated. In most cases you do not need to subclass this method.
+
+####`start()`
+This method is called when all node helper are loaded an the system is ready to boot up. The start method is a perfect place to define any additional module properties:
+
+**Example:**
+````javascript
+start: function() {
+ this.mySpecialProperty = "So much wow!";
+ Log.log(this.name + ' is started!');
+}
+````
+
+####`socketNotificationReceived: function(notification, payload)`
+With this method, your node helper can receive notifications form your modules. When this method is called, it has 2 arguments:
+
+- `notification` - String - The notification identifier.
+- `payload` - AnyType - The payload of a notification.
+
+**Note:** The socket connection is established as soon as the module sends it's first message using [sendSocketNotification](thissendsocketnotificationnotification-payload).
+
+**Example:**
+````javascript
+socketNotificationReceived: function(notification, payload) {
+ Log.log(this.name + " received a socket notification: " + notification + " - Payload: " + payload);
+},
+````
+
+### Module instance methods
+
+Each node helper has some handy methods which can be helpfull building your module.
+
+####`this.sendSocketNotification(notification, payload)`
+***notification* String** - The notification identifier.
+***payload* AnyType** - Optional. A notification payload.
+
+If you want to send a notification to all your modules, use the `sendSocketNotification(notification, payload)`. Only the module of your module type will recieve the socket notification.
+
+**Note:** Since all instances of you module will receive the notifications, it's your task to make sure the right module responds to your messages.
+
+**Example:**
+````javascript
+this.sendSocketNotification('SET_CONFIG', this.config);
+````
+
+## MagicMirror Helper Methods
+
+The core Magic Mirror object: `MM` has some handy method that will help you in controlling your and other modules. Most of the `MM` methods are available via convenience methods on the Module instance.
+
+### Module selection
+The only additional method available for your module, is the feature to retrieve references to other modules. This can be used to hide and show other modules.
+
+####`MM.getModules()`
+**Returns Array** - An array with module instances.
+
+To make a selection of all currently loaded module instances, run the `MM.getModules()` method. It will return an array with all currently loaded module instances. The returned array has a lot of filtering methods. See below for more info.
+
+**Note:** This method returns an empty array if not all modules are started yet. Wait for the `ALL_MODULES_STARTED` [notification](#notificationreceivednotification-payload-sender).
+
+
+#####`.withClass(classnames)`
+***classnames* String or Array** - The class names on which you want to filer.
+**Returns Array** - An array with module instances.
+
+If you want to make a selection based on one ore more class names, use the withClass method on a result of the `MM.getModules()` method. The argument of the `withClass(classname)` method can be an array, or space separated string.
+
+**Examples:**
+````javascript
+var modules = MM.getModules().withClass('classname');
+var modules = MM.getModules().withClass('classname1 classname2');
+var modules = MM.getModules().withClass(['classname1','classname2']);
+````
+
+#####`.exceptWithClass(classnames)`
+***classnames* String or Array** - The class names of the modules you want to remove from the results.
+**Returns Array** - An array with module instances.
+
+If you to remove some modules from a selection based on a classname, use the exceptWithClass method on a result of the `MM.getModules()` method. The argument of the `exceptWithClass(classname)` method can be an array, or space separated string.
+
+**Examples:**
+````javascript
+var modules = MM.getModules().exceptWithClass('classname');
+var modules = MM.getModules().exceptWithClass('classname1 classname2');
+var modules = MM.getModules().exceptWithClass(['classname1','classname2']);
+````
+
+#####`.exceptModule(module)`
+***module* Module Object** - The reference to a module you want to remove from the results.
+**Returns Array** - An array with module instances.
+
+If you to remove a specific module instance from a selection based on a classname, use the exceptWithClass method on a result of the `MM.getModules()` method. This can be helpfull if you want to select all module instances except the instance of your module.
+
+**Examples:**
+````javascript
+var modules = MM.getModules().exceptModule(this);
+````
+
+Of course, you can combine all of the above filters:
+
+**Example:**
+````javascript
+var modules = MM.getModules().withClass('classname1').exceptwithClass('classname2').exceptModule(aModule);
+````
+
+#####`.enumerate(callback)`
+***callback* Function(module)** - The callback run on every instance.
+
+If you want to perform an action on all selected modules, you can use the `enumerate` function:
+
+````javascript
+MM.getModules().enumerate(function(module) {
+ Log.log(module.name);
+});
+````
+
+**Example:**
+To hide all modules except the your module instance, you could write something like:
+````javascript
+Module.register("modulename",{
+ //...
+ notificationReceived: function(notification, payload, sender) {
+ if (notification === 'DOM_OBJECTS_CREATED') {
+ MM.getModules().exceptModule(this).enumerate(function(module) {
+ module.hide(1000, function() {
+ //Module hidden.
+ });
+ });
+ }
+ },
+ //...
+});
+````
+
+## MagicMirror Logger
+
+The Magic Mirror contains a convenience wrapper for logging. Currently, this logger is a simple proxy to the original `console.log` methods. But it might get additional features in the future. The Loggers is currently only available in the core module file (not in the node_helper).
+
+**Examples:**
+````javascript
+Log.info('error');
+Log.log('log');
+Log.error('info');
+```
\ No newline at end of file
diff --git a/modules/default/alert/alert.js b/modules/default/alert/alert.js
index 7ba47a86..1a8c55dd 100644
--- a/modules/default/alert/alert.js
+++ b/modules/default/alert/alert.js
@@ -7,114 +7,111 @@
* MIT Licensed.
*/
-Module.register('alert',{
+Module.register("alert",{
defaults: {
// scale|slide|genie|jelly|flip|bouncyflip|exploader
effect: "slide",
// scale|slide|genie|jelly|flip|bouncyflip|exploader
- alert_effect:"jelly",
+ alert_effect: "jelly",
//time a notification is displayed in seconds
display_time: 3500,
//Position
position: "center",
//shown at startup
- welcome_message: "Welcome, start was successfull!"
+ welcome_message: "Welcome, start was successful!"
},
getScripts: function() {
- return ["classie.js", "modernizr.custom.js", 'notificationFx.js'];
+ return ["classie.js", "modernizr.custom.js", "notificationFx.js"];
},
getStyles: function() {
- return ['ns-default.css'];
+ return ["ns-default.css"];
},
- show_notification: function (message) {
- if (this.config.effect == "slide"){this.config.effect=this.config.effect + "-" + this.config.position}
- message = "" + message.title + " " + message.message + ""
+ show_notification: function(message) {
+ if (this.config.effect == "slide") {this.config.effect = this.config.effect + "-" + this.config.position;}
+ message = "" + message.title + " " + message.message + "";
new NotificationFx({
- message : message,
- layout : "growl",
- effect : this.config.effect,
+ message: message,
+ layout: "growl",
+ effect: this.config.effect,
ttl: this.config.display_time
}).show();
},
- show_alert: function (params, sender) {
- var self = this
+ show_alert: function(params, sender) {
+ var self = this;
//Set standard params if not provided by module
- if (typeof params.timer === 'undefined') { params.timer = null; }
- if (typeof params.imageHeight === 'undefined') { params.imageHeight = "80px"; }
- if (typeof params.imageUrl === 'undefined') {
+ if (typeof params.timer === "undefined") { params.timer = null; }
+ if (typeof params.imageHeight === "undefined") { params.imageHeight = "80px"; }
+ if (typeof params.imageUrl === "undefined") {
params.imageUrl = null;
- image = ""
- }
- else {
- image = " "
+ image = "";
+ } else {
+ image = " ";
}
//Create overlay
var overlay = document.createElement("div");
- overlay.id = "overlay"
- overlay.innerHTML += '';
+ overlay.id = "overlay";
+ overlay.innerHTML += "";
document.body.insertBefore(overlay, document.body.firstChild);
-
+
//If module already has an open alert close it
- if (this.alerts[sender.name]){
- this.hide_alert(sender)
+ if (this.alerts[sender.name]) {
+ this.hide_alert(sender);
}
-
- message = "" + params.title + " " + params.message + ""
+
+ message = "" + params.title + " " + params.message + "";
//Store alert in this.alerts
this.alerts[sender.name] = new NotificationFx({
- message : image + message,
- effect : this.config.alert_effect,
+ message: image + message,
+ effect: this.config.alert_effect,
ttl: params.timer,
al_no: "ns-alert"
});
//Show alert
- this.alerts[sender.name].show()
+ this.alerts[sender.name].show();
//Add timer to dismiss alert and overlay
if (params.timer) {
- setTimeout( function() {
- self.hide_alert(sender)
- }, params.timer );
+ setTimeout(function() {
+ self.hide_alert(sender);
+ }, params.timer);
}
-
+
},
- hide_alert: function (sender) {
+ hide_alert: function(sender) {
//Dismiss alert and remove from this.alerts
- this.alerts[sender.name].dismiss()
- this.alerts[sender.name] = null
+ this.alerts[sender.name].dismiss();
+ this.alerts[sender.name] = null;
//Remove overlay
var overlay = document.getElementById("overlay");
overlay.parentNode.removeChild(overlay);
},
- setPosition: function (pos) {
+ setPosition: function(pos) {
//Add css to body depending on the set position for notifications
- var sheet = document.createElement('style')
- if (pos == "center"){sheet.innerHTML = ".ns-box {margin-left: auto; margin-right: auto;text-align: center;}";}
- if (pos == "right"){sheet.innerHTML = ".ns-box {margin-left: auto;text-align: right;}";}
- if (pos == "left"){sheet.innerHTML = ".ns-box {margin-right: auto;text-align: left;}";}
+ var sheet = document.createElement("style");
+ if (pos == "center") {sheet.innerHTML = ".ns-box {margin-left: auto; margin-right: auto;text-align: center;}";}
+ if (pos == "right") {sheet.innerHTML = ".ns-box {margin-left: auto;text-align: right;}";}
+ if (pos == "left") {sheet.innerHTML = ".ns-box {margin-right: auto;text-align: left;}";}
document.body.appendChild(sheet);
-
+
},
notificationReceived: function(notification, payload, sender) {
- if (notification === 'SHOW_ALERT') {
- if (typeof payload.type === 'undefined') { payload.type = "alert"; }
- if (payload.type == "alert"){
- this.show_alert(payload, sender)
+ if (notification === "SHOW_ALERT") {
+ if (typeof payload.type === "undefined") { payload.type = "alert"; }
+ if (payload.type == "alert") {
+ this.show_alert(payload, sender);
+ } else if (payload.type = "notification") {
+ this.show_notification(payload);
}
- else if (payload.type = "notification"){
- this.show_notification(payload)
- }
- }
- else if (notification === 'HIDE_ALERT') {
- this.hide_alert(sender)
+ } else if (notification === "HIDE_ALERT") {
+ this.hide_alert(sender);
}
},
start: function() {
- this.alerts = {}
- this.setPosition(this.config.position)
- if (this.config.welcome_message){
- this.show_notification({title: "MagicMirror Notification", message: this.config.welcome_message})
+ this.alerts = {};
+ this.setPosition(this.config.position);
+ if (this.config.welcome_message) {
+ this.show_notification({title: "MagicMirror Notification", message: this.config.welcome_message});
}
- Log.info('Starting module: ' + this.name);
+ Log.info("Starting module: " + this.name);
}
-});
\ No newline at end of file
+});
diff --git a/modules/default/alert/classie.js b/modules/default/alert/classie.js
index a9675548..4a096f1e 100755
--- a/modules/default/alert/classie.js
+++ b/modules/default/alert/classie.js
@@ -1,80 +1,79 @@
/*!
* classie - class helper functions
* from bonzo https://github.com/ded/bonzo
- *
+ *
* classie.has( elem, 'my-class' ) -> true/false
* classie.add( elem, 'my-new-class' )
* classie.remove( elem, 'my-unwanted-class' )
* classie.toggle( elem, 'my-class' )
*/
-
+// jscs:disable
/*jshint browser: true, strict: true, undef: true */
/*global define: false */
-( function( window ) {
+(function(window) {
-'use strict';
+"use strict";
// class helper functions from bonzo https://github.com/ded/bonzo
-function classReg( className ) {
- return new RegExp("(^|\\s+)" + className + "(\\s+|$)");
+function classReg(className) {
+ return new RegExp("(^|\\s+)" + className + "(\\s+|$)");
}
// classList support for class management
// altho to be fair, the api sucks because it won't accept multiple classes at once
var hasClass, addClass, removeClass;
-if ( 'classList' in document.documentElement ) {
- hasClass = function( elem, c ) {
- return elem.classList.contains( c );
- };
- addClass = function( elem, c ) {
- elem.classList.add( c );
- };
- removeClass = function( elem, c ) {
- elem.classList.remove( c );
- };
-}
-else {
- hasClass = function( elem, c ) {
- return classReg( c ).test( elem.className );
- };
- addClass = function( elem, c ) {
- if ( !hasClass( elem, c ) ) {
- elem.className = elem.className + ' ' + c;
- }
- };
- removeClass = function( elem, c ) {
- elem.className = elem.className.replace( classReg( c ), ' ' );
- };
+if ("classList" in document.documentElement) {
+ hasClass = function(elem, c) {
+ return elem.classList.contains(c);
+ };
+ addClass = function(elem, c) {
+ elem.classList.add(c);
+ };
+ removeClass = function(elem, c) {
+ elem.classList.remove(c);
+ };
+} else {
+ hasClass = function(elem, c) {
+ return classReg(c).test(elem.className);
+ };
+ addClass = function(elem, c) {
+ if (!hasClass(elem, c)) {
+ elem.className = elem.className + " " + c;
+ }
+ };
+ removeClass = function(elem, c) {
+ elem.className = elem.className.replace(classReg(c), " ");
+ };
}
-function toggleClass( elem, c ) {
- var fn = hasClass( elem, c ) ? removeClass : addClass;
- fn( elem, c );
+function toggleClass(elem, c) {
+ var fn = hasClass(elem, c) ? removeClass : addClass;
+ fn(elem, c);
}
var classie = {
- // full names
- hasClass: hasClass,
- addClass: addClass,
- removeClass: removeClass,
- toggleClass: toggleClass,
- // short names
- has: hasClass,
- add: addClass,
- remove: removeClass,
- toggle: toggleClass
+ // full names
+ hasClass: hasClass,
+ addClass: addClass,
+ removeClass: removeClass,
+ toggleClass: toggleClass,
+ // short names
+ has: hasClass,
+ add: addClass,
+ remove: removeClass,
+ toggle: toggleClass
};
// transport
-if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( classie );
+if (typeof define === "function" && define.amd) {
+ // AMD
+ define(classie);
} else {
- // browser global
- window.classie = classie;
+ // browser global
+ window.classie = classie;
}
-})( window );
+})(window);
diff --git a/modules/default/alert/modernizr.custom.js b/modules/default/alert/modernizr.custom.js
index adf2df7e..3d33a8be 100755
--- a/modules/default/alert/modernizr.custom.js
+++ b/modules/default/alert/modernizr.custom.js
@@ -1,4 +1,5 @@
/* Modernizr 2.8.3 (Custom Build) | MIT & BSD
* Build: http://modernizr.com/download/#-cssanimations-shiv-cssclasses-prefixed-testprop-testallprops-domprefixes-load
*/
-;window.Modernizr=function(a,b,c){function x(a){j.cssText=a}function y(a,b){return x(prefixes.join(a+";")+(b||""))}function z(a,b){return typeof a===b}function A(a,b){return!!~(""+a).indexOf(b)}function B(a,b){for(var d in a){var e=a[d];if(!A(e,"-")&&j[e]!==c)return b=="pfx"?e:!0}return!1}function C(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:z(f,"function")?f.bind(d||b):f}return!1}function D(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+n.join(d+" ")+d).split(" ");return z(b,"string")||z(b,"undefined")?B(e,b):(e=(a+" "+o.join(d+" ")+d).split(" "),C(e,b,c))}var d="2.8.3",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m="Webkit Moz O ms",n=m.split(" "),o=m.toLowerCase().split(" "),p={},q={},r={},s=[],t=s.slice,u,v={}.hasOwnProperty,w;!z(v,"undefined")&&!z(v.call,"undefined")?w=function(a,b){return v.call(a,b)}:w=function(a,b){return b in a&&z(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=t.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(t.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(t.call(arguments)))};return e}),p.cssanimations=function(){return D("animationName")};for(var E in p)w(p,E)&&(u=E.toLowerCase(),e[u]=p[E](),s.push((e[u]?"":"no-")+u));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)w(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},x(""),i=k=null,function(a,b){function l(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function m(){var a=s.elements;return typeof a=="string"?a.split(" "):a}function n(a){var b=j[a[h]];return b||(b={},i++,a[h]=i,j[i]=b),b}function o(a,c,d){c||(c=b);if(k)return c.createElement(a);d||(d=n(c));var g;return d.cache[a]?g=d.cache[a].cloneNode():f.test(a)?g=(d.cache[a]=d.createElem(a)).cloneNode():g=d.createElem(a),g.canHaveChildren&&!e.test(a)&&!g.tagUrn?d.frag.appendChild(g):g}function p(a,c){a||(a=b);if(k)return a.createDocumentFragment();c=c||n(a);var d=c.frag.cloneNode(),e=0,f=m(),g=f.length;for(;e",g="hidden"in a,k=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){g=!0,k=!0}})();var s={elements:d.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:c,shivCSS:d.shivCSS!==!1,supportsUnknownElements:k,shivMethods:d.shivMethods!==!1,type:"default",shivDocument:r,createElement:o,createDocumentFragment:p};a.html5=s,r(b)}(this,b),e._version=d,e._domPrefixes=o,e._cssomPrefixes=n,e.testProp=function(a){return B([a])},e.testAllProps=D,e.prefixed=function(a,b,c){return b?D(a,b,c):D(a,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+s.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f",d.insertBefore(c.lastChild,d.firstChild);}function m() {var a = s.elements;return typeof a == "string" ? a.split(" ") : a;}function n(a) {var b = j[a[h]];return b || (b = {},i++,a[h] = i,j[i] = b),b;}function o(a, c, d) {c || (c = b);if (k)return c.createElement(a);d || (d = n(c));var g;return d.cache[a] ? g = d.cache[a].cloneNode() : f.test(a) ? g = (d.cache[a] = d.createElem(a)).cloneNode() : g = d.createElem(a),g.canHaveChildren && !e.test(a) && !g.tagUrn ? d.frag.appendChild(g) : g;}function p(a, c) {a || (a = b);if (k)return a.createDocumentFragment();c = c || n(a);var d = c.frag.cloneNode(),e = 0,f = m(),g = f.length;for (; e < g; e++)d.createElement(f[e]);return d;}function q(a, b) {b.cache || (b.cache = {},b.createElem = a.createElement,b.createFrag = a.createDocumentFragment,b.frag = b.createFrag()),a.createElement = function(c) {return s.shivMethods ? o(c,a,b) : b.createElem(c);},a.createDocumentFragment = Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&(" + m().join().replace(/[\w\-]+/g,function(a) {return b.createElem(a),b.frag.createElement(a),"c(\"" + a + "\")";}) + ");return n}")(s,b.frag);}function r(a) {a || (a = b);var c = n(a);return s.shivCSS && !g && !c.hasCSS && (c.hasCSS = !!l(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),k || q(a,c),a;}var c = "3.7.0",d = a.html5 || {},e = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,f = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,g,h = "_html5shiv",i = 0,j = {},k;(function() {try {var a = b.createElement("a");a.innerHTML = "",g = "hidden"in a,k = a.childNodes.length == 1 || function() {b.createElement("a");var a = b.createDocumentFragment();return typeof a.cloneNode == "undefined" || typeof a.createDocumentFragment == "undefined" || typeof a.createElement == "undefined";}();}catch (c) {g = !0,k = !0;}})();var s = {elements: d.elements || "abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version: c,shivCSS: d.shivCSS !== !1,supportsUnknownElements: k,shivMethods: d.shivMethods !== !1,type: "default",shivDocument: r,createElement: o,createDocumentFragment: p};a.html5 = s,r(b);}(this,b),e._version = d,e._domPrefixes = o,e._cssomPrefixes = n,e.testProp = function(a) {return B([a]);},e.testAllProps = D,e.prefixed = function(a, b, c) {return b ? D(a,b,c) : D(a,"pfx");},g.className = g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2") + (f ? " js " + s.join(" ") : ""),e;}(this,this.document),function(a, b, c) {function d(a) {return "[object Function]" == o.call(a);}function e(a) {return "string" == typeof a;}function f() {}function g(a) {return !a || "loaded" == a || "complete" == a || "uninitialized" == a;}function h() {var a = p.shift();q = 1,a ? a.t ? m(function() {("c" == a.t ? B.injectCss : B.injectJs)(a.s,0,a.a,a.x,a.e,1);},0) : (a(),h()) : q = 0;}function i(a, c, d, e, f, i, j) {function k(b) {if (!o && g(l.readyState) && (u.r = o = 1,!q && h(),l.onload = l.onreadystatechange = null,b)) {"img" != a && m(function() {t.removeChild(l);},50);for (var d in y[c])y[c].hasOwnProperty(d) && y[c][d].onload();}}var j = j || B.errorTimeout,l = b.createElement(a),o = 0,r = 0,u = {t: d,s: c,e: f,a: i,x: j};1 === y[c] && (r = 1,y[c] = []),"object" == a ? l.data = c : (l.src = c,l.type = a),l.width = l.height = "0",l.onerror = l.onload = l.onreadystatechange = function() {k.call(this,r);},p.splice(e,0,u),"img" != a && (r || 2 === y[c] ? (t.insertBefore(l,s ? null : n),m(k,j)) : y[c].push(l));}function j(a, b, c, d, f) {return q = 0,b = b || "j",e(a) ? i("c" == b ? v : u,a,b,this.i++,c,d,f) : (p.splice(this.i++,0,a),1 == p.length && h()),this;}function k() {var a = B;return a.loader = {load: j,i: 0},a;}var l = b.documentElement,m = a.setTimeout,n = b.getElementsByTagName("script")[0],o = {}.toString,p = [],q = 0,r = "MozAppearance"in l.style,s = r && !!b.createRange().compareNode,t = s ? l : n.parentNode,l = a.opera && "[object Opera]" == o.call(a.opera),l = !!b.attachEvent && !l,u = r ? "object" : l ? "script" : "img",v = l ? "script" : u,w = Array.isArray || function(a) {return "[object Array]" == o.call(a);},x = [],y = {},z = {timeout: function(a, b) {return b.length && (a.timeout = b[0]),a;}},A,B;B = function(a) {function b(a) {var a = a.split("!"),b = x.length,c = a.pop(),d = a.length,c = {url: c,origUrl: c,prefixes: a},e,f,g;for (f = 0; f < d; f++)g = a[f].split("="),(e = z[g.shift()]) && (c = e(c,g));for (f = 0; f < b; f++)c = x[f](c);return c;}function g(a, e, f, g, h) {var i = b(a),j = i.autoCallback;i.url.split(".").pop().split("?").shift(),i.bypass || (e && (e = d(e) ? e : e[a] || e[g] || e[a.split("/").pop().split("?")[0]]),i.instead ? i.instead(a,e,f,g,h) : (y[i.url] ? i.noexec = !0 : y[i.url] = 1,f.load(i.url,i.forceCSS || !i.forceJS && "css" == i.url.split(".").pop().split("?").shift() ? "c" : c,i.noexec,i.attrs,i.timeout),(d(e) || d(j)) && f.load(function() {k(),e && e(i.origUrl,h,g),j && j(i.origUrl,h,g),y[i.url] = 2;})));}function h(a, b) {function c(a, c) {if (a) {if (e(a))c || (j = function() {var a = [].slice.call(arguments);k.apply(this,a),l();}),g(a,j,b,0,h);else if (Object(a) === a)for (n in m = function() {var b = 0,c;for (c in a)a.hasOwnProperty(c) && b++;return b;}(),a)a.hasOwnProperty(n) && (!c && !--m && (d(j) ? j = function() {var a = [].slice.call(arguments);k.apply(this,a),l();} : j[n] = function(a) {return function() {var b = [].slice.call(arguments);a && a.apply(this,b),l();};}(k[n])),g(a[n],j,b,n,h));}else !c && l();}var h = !!a.test,i = a.load || a.both,j = a.callback || f,k = j,l = a.complete || f,m,n;c(h ? a.yep : a.nope,!!i),i && c(i);}var i,j,l = this.yepnope.loader;if (e(a))g(a,0,l,0);else if (w(a))for (i = 0; i < a.length; i++)j = a[i],e(j) ? g(j,0,l,0) : w(j) ? B(j) : Object(j) === j && h(j,l);else Object(a) === a && h(a,l);},B.addPrefix = function(a, b) {z[a] = b;},B.addFilter = function(a) {x.push(a);},B.errorTimeout = 1e4,null == b.readyState && b.addEventListener && (b.readyState = "loading",b.addEventListener("DOMContentLoaded",A = function() {b.removeEventListener("DOMContentLoaded",A,0),b.readyState = "complete";},0)),a.yepnope = k(),a.yepnope.executeStack = h,a.yepnope.injectJs = function(a, c, d, e, i, j) {var k = b.createElement("script"),l,o,e = e || B.errorTimeout;k.src = a;for (o in d)k.setAttribute(o,d[o]);c = j ? h : c || f,k.onreadystatechange = k.onload = function() {!l && g(k.readyState) && (l = 1,c(),k.onload = k.onreadystatechange = null);},m(function() {l || (l = 1,c(1));},e),i ? k.onload() : n.parentNode.insertBefore(k,n);},a.yepnope.injectCss = function(a, c, d, e, g, i) {var e = b.createElement("link"),j,c = i ? h : c || f;e.href = a,e.rel = "stylesheet",e.type = "text/css";for (j in d)e.setAttribute(j,d[j]);g || (n.parentNode.insertBefore(e,n),m(c,0));};}(this,document),Modernizr.load = function() {yepnope.apply(window,[].slice.call(arguments,0));};
diff --git a/modules/default/alert/notificationFx.js b/modules/default/alert/notificationFx.js
index 9f7c7207..b74c9d14 100644
--- a/modules/default/alert/notificationFx.js
+++ b/modules/default/alert/notificationFx.js
@@ -4,31 +4,32 @@
*
* Licensed under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
- *
+ *
* Copyright 2014, Codrops
* http://www.codrops.com
*/
-;( function( window ) {
-
- 'use strict';
+// jscs:disable
+;(function(window) {
+
+ "use strict";
var docElem = window.document.documentElement,
- support = { animations : Modernizr.cssanimations },
+ support = {animations: Modernizr.cssanimations},
animEndEventNames = {
- 'WebkitAnimation' : 'webkitAnimationEnd',
- 'OAnimation' : 'oAnimationEnd',
- 'msAnimation' : 'MSAnimationEnd',
- 'animation' : 'animationend'
+ "WebkitAnimation": "webkitAnimationEnd",
+ "OAnimation": "oAnimationEnd",
+ "msAnimation": "MSAnimationEnd",
+ "animation": "animationend"
},
// animation end event name
- animEndEventName = animEndEventNames[ Modernizr.prefixed( 'animation' ) ];
-
+ animEndEventName = animEndEventNames[ Modernizr.prefixed("animation") ];
+
/**
* extend obj function
*/
- function extend( a, b ) {
- for( var key in b ) {
- if( b.hasOwnProperty( key ) ) {
+ function extend(a, b) {
+ for (var key in b) {
+ if (b.hasOwnProperty(key)) {
a[key] = b[key];
}
}
@@ -38,9 +39,9 @@
/**
* NotificationFx function
*/
- function NotificationFx( options ) {
- this.options = extend( {}, this.options );
- extend( this.options, options );
+ function NotificationFx(options) {
+ this.options = extend({}, this.options);
+ extend(this.options, options);
this._init();
}
@@ -50,28 +51,28 @@
NotificationFx.prototype.options = {
// element to which the notification will be appended
// defaults to the document.body
- wrapper : document.body,
+ wrapper: document.body,
// the message
- message : 'yo!',
+ message: "yo!",
// layout type: growl|attached|bar|other
- layout : 'growl',
+ layout: "growl",
// effects for the specified layout:
// for growl layout: scale|slide|genie|jelly
// for attached layout: flip|bouncyflip
// for other layout: boxspinner|cornerexpand|loadingcircle|thumbslider
// ...
- effect : 'slide',
+ effect: "slide",
// notice, warning, error, success
// will add class ns-type-warning, ns-type-error or ns-type-success
- type : 'notice',
- // if the user doesn´t close the notification then we remove it
+ type: "notice",
+ // if the user doesn´t close the notification then we remove it
// after the following time
- ttl : 6000,
+ ttl: 6000,
al_no: "ns-box",
// callbacks
- onClose : function() { return false; },
- onOpen : function() { return false; }
- }
+ onClose: function() { return false; },
+ onOpen: function() { return false; }
+ };
/**
* init function
@@ -79,29 +80,29 @@
*/
NotificationFx.prototype._init = function() {
// create HTML structure
- this.ntf = document.createElement( 'div' );
- this.ntf.className = this.options.al_no + ' ns-' + this.options.layout + ' ns-effect-' + this.options.effect + ' ns-type-' + this.options.type;
- var strinner = '