Merge pull request #1 from MichMich/v2-beta

V2 beta
This commit is contained in:
roxasvalor 2016-05-03 18:38:38 -05:00
commit 5e68a8200d
20 changed files with 198 additions and 42 deletions

View File

@ -4,5 +4,6 @@
"validateQuoteMarks": "\"",
"maximumLineLength": 250,
"requireCurlyBraces": [],
"requireCamelCaseOrUpperCaseIdentifiers": false
"requireCamelCaseOrUpperCaseIdentifiers": false,
"excludeFiles": [".jscsrc"],
}

View File

@ -20,6 +20,7 @@ MagicMirror² focuses on a modular plugin system and uses [Electron](http://elec
- [Configuration](#configuration)
- [Modules](#modules)
- [Known Issues](#known-issues)
- [community](#community)
- [Contributing Guidelines](#contributing-guidelines)
## Usage
@ -110,6 +111,10 @@ For more available modules, check out out the wiki page: [MagicMirror² Modules]
- 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).
## Community
The community around the MagicMirror² is constantly growing. We even have a [forum](https://forum.magicmirror.builders) now where you can share your ideas, ask questions, help others and get inspired by other builders. We would love to see you there!
## Contributing Guidelines
Contributions of all kinds are welcome, not only in the form of code but also with regards bug reports and documentation.

View File

@ -6,8 +6,8 @@
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<link rel="stylesheet" type="text/css" href="css/main.css">
<link rel="stylesheet" type="text/css" href="css/custom.css">
<link rel="stylesheet" type="text/css" href="fonts/roboto.css">
<!-- custom.css is loaded by the loader.js to make sure it's loaded after the module css files. -->
</head>
<body>
<div class="region fullscreen below"><div class="container"></div></div>

View File

@ -18,7 +18,7 @@
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.11.0" # Set the latest version here.
NODE_LATEST="v6.0.0" # 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 ..."

View File

@ -38,6 +38,14 @@ var defaults = {
text: "See README for more information."
}
},
{
module: "helloworld",
position: "middle_center",
classes: "xsmall",
config: {
text: "If you get this message while your config file is already<br>created, your config file probably contains an error.<br>Use a JavaScript linter to validate your file."
}
},
{
module: "helloworld",
position: "bottom_bar",

View File

@ -34,7 +34,15 @@ var Loader = (function() {
loadNextModule();
});
} else {
startModules();
// All modules loaded. Load custom.css
// This is done after all the moduels so we can
// overwrite all the defined styls.
loadFile('css/custom.css', function() {
// custom.css loaded. Start all modules.
startModules();
});
}
};

View File

@ -21,6 +21,27 @@ var Log = (function() {
},
error: function(message) {
console.error(message);
},
warn: function(message) {
console.warn(message);
},
group: function(message) {
console.group(message);
},
groupCollapsed: function(message) {
console.groupCollapsed(message);
},
groupEnd: function() {
console.groupEnd();
},
time: function(message) {
console.time(message);
},
timeEnd: function(message) {
console.timeEnd(message);
},
timeStamp: function(message) {
console.timeStamp(message);
}
};
})();

View File

@ -97,7 +97,7 @@ start: function() {
####`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.
The getScripts method is called to request any additional scripts that need to be loaded. This method should therefore return an array with strings. If you want to return a full path to a file in the module folder, use the `this.file('filename.js')` method. In all cases the loader will only load a file once. It even checks if the file is available in the default vendor folder.
**Example:**
````javascript
@ -117,7 +117,7 @@ getScripts: function() {
####`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.
The getStyles method is called to request any additional stylesheets that need to be loaded. This method should therefore return an array with strings. If you want to return a full path to a file in the module folder, use the `this.file('filename.css')` method. In all cases the loader will only load a file once. It even checks if the file is available in the default vendor folder.
**Example:**
````javascript
@ -133,6 +133,22 @@ getStyles: function() {
````
**Note:** If a file can not be loaded, the boot up of the mirror will stall. Therefore it's advised not to use any external urls.
####`getTranslations()`
**Should return: Dictionary**
The getTranslations method is called to request translation files that need to be loaded. This method should therefore return a dictionary with the files to load, identified by the country's short name.
**Example:**
````javascript
getTranslations: function() {
return {
en: "translations/en.json",
de: "translations/de.json"
}
}
````
####`getDom()`
**Should return:** Dom Object
@ -261,6 +277,25 @@ To show a module, you can call the `show(speed, callback)` method. You can call
**Note 2:** If the show animation is hijacked (an other method calls show on the same module), the callback will not be called.<br>
**Note 3:** If the dom is not yet created, the show method won't work. Wait for the `DOM_OBJECTS_CREATED` [notification](#notificationreceivednotification-payload-sender).
####`this.translate(identifier)`
***identifier* String** - Identifier of the string that should be translated.
The Magic Mirror contains a convenience wrapper for `l18n`. You can use this to automatically serve different translations for your modules based on the user's `language` configuration.
**Example:**
````javascript
this.translate("INFO") //Will return a translated string for the identifier INFO
````
**Example json file:**
````javascript
{
"INFO": "Really important information!"
}
````
**Note:** Currently there is no fallback if a translation identifier does not exist in one language. Right now you always have to add all identifier to all your translations even if they are not translated yet (see [#191](https://github.com/MichMich/MagicMirror/issues/191)).
## The Node Helper: node_helper.js
@ -458,4 +493,4 @@ The Magic Mirror contains a convenience wrapper for logging. Currently, this log
Log.info('error');
Log.log('log');
Log.error('info');
```
````

View File

@ -18,7 +18,7 @@ Module.register("alert",{
//Position
position: "center",
//shown at startup
welcome_message: true,
welcome_message: false,
},
getScripts: function() {
return ["classie.js", "modernizr.custom.js", "notificationFx.js"];

View File

@ -144,7 +144,10 @@
if (ev.target !== self.ntf) return false;
this.removeEventListener(animEndEventName, onEndAnimationFn);
}
self.options.wrapper.removeChild(this);
if (this.parentNode === self.options.wrapper) {
self.options.wrapper.removeChild(this);
}
};
if (support.animations) {

View File

@ -112,6 +112,14 @@ The following properties can be configured:
</code>
</td>
</tr>
<tr>
<td><code>displayRepeatingCountTitle</code></td>
<td>Show count title for yearly repeating events (e.g. "X. Birthday", "X. Anniversary")<br>
<br><b>Possible values:</b> <code>true</code> or <code>false</code>
<br><b>Default value:</b> <code>false</code>
</td>
</tr>
</tbody>
</table>
@ -154,5 +162,12 @@ config: {
<br><b>Possible values:</b> See <a href="http://fontawesome.io/icons/" target="_blank">Font Awsome</a> website.
</td>
</tr>
<tr>
<td><code> repeatingCountTitle </code></td>
<td>The count title for yearly repating events in this calendar. <br>
<br><b>Example:</b> <br>
<code>'Birthday'</code>
</td>
</tr>
</tbody>
</table>

View File

@ -15,6 +15,8 @@ Module.register("calendar",{
maximumNumberOfDays: 365,
displaySymbol: true,
defaultSymbol: "calendar", // Fontawsome Symbol see http://fontawesome.io/cheatsheet/
displayRepeatingCountTitle: false,
defaultRepeatingCountTitle: '',
maxTitleLength: 25,
fetchInterval: 5 * 60 * 1000, // Update every 5 minutes.
animationSpeed: 2000,
@ -46,7 +48,8 @@ Module.register("calendar",{
return {
en: "translations/en.json",
de: "translations/de.json",
nl: "translations/nl.json"
nl: "translations/nl.json",
fr: "translations/fr.json"
};
},
@ -113,8 +116,23 @@ Module.register("calendar",{
eventWrapper.appendChild(symbolWrapper);
}
var titleWrapper = document.createElement("td");
titleWrapper.innerHTML = this.titleTransform(event.title);
var titleWrapper = document.createElement("td"),
repeatingCountTitle = '';
if (this.config.displayRepeatingCountTitle) {
repeatingCountTitle = this.countTitleForUrl(event.url);
if(repeatingCountTitle !== '') {
var thisYear = new Date().getFullYear(),
yearDiff = thisYear - event.firstYear;
repeatingCountTitle = ', '+ yearDiff + '. ' + repeatingCountTitle;
}
}
titleWrapper.innerHTML = this.titleTransform(event.title) + repeatingCountTitle;
titleWrapper.className = "title bright";
eventWrapper.appendChild(titleWrapper);
@ -239,6 +257,23 @@ Module.register("calendar",{
return this.config.defaultSymbol;
},
/* countTitleForUrl(url)
* Retrieves the name for a specific url.
*
* argument url sting - Url to look for.
*
* return string - The Symbol
*/
countTitleForUrl: function(url) {
for (var c in this.config.calendars) {
var calendar = this.config.calendars[c];
if (calendar.url === url && typeof calendar.repeatingCountTitle === "string") {
return calendar.repeatingCountTitle;
}
}
return this.config.defaultRepeatingCountTitle;
},
/* shorten(string, maxLength)
* Shortens a sting if it's longer than maxLenthg.

View File

@ -55,14 +55,16 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
if (event.type === "VEVENT") {
//console.log(event);
var startDate = (event.start.length === 8) ? moment(event.start, "YYYYMMDD") : moment(new Date(event.start));
var endDate;
if (typeof event.end !== "undefined") {
endDate = (event.end.length === 8) ? moment(event.end, "YYYYMMDD") : moment(new Date(event.end));
} else {
endDate = startDate;
if (!isFacebookBirthday) {
endDate = startDate;
} else {
endDate = moment(startDate).add(1, 'days');
}
}
// calculate the duration f the event for use with recurring events.
@ -84,14 +86,15 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
title: (typeof event.summary.val !== "undefined") ? event.summary.val : event.summary,
startDate: startDate.format("x"),
endDate: endDate.format("x"),
fullDayEvent: isFullDayEvent(event)
fullDayEvent: isFullDayEvent(event),
firstYear: event.start.getFullYear()
});
}
}
} else {
// console.log("Single event ...");
// Single event.
var fullDayEvent = isFullDayEvent(event);
var fullDayEvent = (isFacebookBirthday) ? true : isFullDayEvent(event);
var title = (typeof event.summary.val !== "undefined") ? event.summary.val : event.summary;
if (!fullDayEvent && endDate < new Date()) {

View File

@ -1,7 +1,7 @@
{
"TODAY": "Heute"
, "TOMORROW": "Morgen"
, "RUNNING": "Noch"
, "RUNNING": "noch"
, "LOADING": "Lade Termine &hellip;"
, "EMPTY": "Keine Termine."
}

View File

@ -0,0 +1,7 @@
{
"TODAY": "Aujourd'hui"
, "TOMORROW": "Demain"
, "RUNNING": "Se termine dans"
, "LOADING": "Chargement des RDV &hellip;"
, "EMPTY": "Aucun RDV."
}

View File

@ -32,7 +32,7 @@ ical.objectHandlers['END'] = function(val, params, curr, stack){
if (curr.start.length === 8) {
var comps = /^(\d{4})(\d{2})(\d{2})$/.exec(curr.start);
if (comps) {
curr.start = new Date (comps[1], comps[2], comps[3]);
curr.start = new Date (comps[1], comps[2] - 1, comps[3]);
}
}

View File

@ -47,14 +47,21 @@ The following properties can be configured:
</tr>
<tr>
<td><code>showPeriod</code></td>
<td>Show the period (am/pm) with 12 hour format<br>
<td>Show the period (am/pm) with 12 hour format.<br>
<br><b>Possible values:</b> <code>true</code> or <code>false</code>
<br><b>Default value:</b> <code>true</code>
</td>
</tr>
<tr>
<td><code>showPeriodUpper</code></td>
<td>Show the period (AM/PM) with 12 hour format as uppercase<br>
<td>Show the period (AM/PM) with 12 hour format as uppercase.<br>
<br><b>Possible values:</b> <code>true</code> or <code>false</code>
<br><b>Default value:</b> <code>false</code>
</td>
</tr>
<tr>
<td><code>clockBold</code></td>
<td>Remove the colon and bold the minutes to make a more modern look.<br>
<br><b>Possible values:</b> <code>true</code> or <code>false</code>
<br><b>Default value:</b> <code>false</code>
</td>

View File

@ -1,41 +1,34 @@
/* global Log, Module, moment, config */
/* Magic Mirror
* Module: Clock
*
* By Michael Teeuw http://michaelteeuw.nl
* MIT Licensed.
*/
Module.register("clock",{
// Module config defaults.
defaults: {
timeFormat: config.timeFormat,
displaySeconds: true,
showPeriod: true,
showPeriodUpper: false,
clockBold: false
},
// Define required scripts.
getScripts: function() {
return ["moment.js"];
},
// Define start sequence.
start: function() {
Log.info("Starting module: " + this.name);
// Schedule update interval.
var self = this;
setInterval(function() {
self.updateDom();
}, 1000);
// Set locale.
moment.locale(config.language);
},
// Override dom generator.
getDom: function() {
// Create wrappers.
@ -44,35 +37,36 @@ Module.register("clock",{
var timeWrapper = document.createElement("div");
var secondsWrapper = document.createElement("sup");
var periodWrapper = document.createElement("span");
// Style Wrappers
dateWrapper.className = "date normal medium";
timeWrapper.className = "time bright large light";
secondsWrapper.className = "dimmed";
// Set content of wrappers.
// The moment().format('h') method has a bug on the Raspberry Pi.
// The moment().format("h") method has a bug on the Raspberry Pi.
// So we need to generate the timestring manually.
// See issue: https://github.com/MichMich/MagicMirror/issues/181
var timeString = moment().format('HH:mm');
if (this.config.clockBold === true) {
var timeString = moment().format("HH[<span class=\"bold\">]mm[</span>]");
} else {
var timeString = moment().format("HH:mm");
}
if (this.config.timeFormat !== 24) {
var now = new Date();
var hours = now.getHours() % 12 || 12;
timeString = hours + moment().format(':mm');
if (this.config.clockBold === true) {
timeString = hours + moment().format("[<span class=\"bold\">]mm[</span>]");
} else {
timeString = hours + moment().format(":mm");
}
}
dateWrapper.innerHTML = moment().format("dddd, LL");
timeWrapper.innerHTML = timeString;
secondsWrapper.innerHTML = moment().format("ss");
if (this.config.showPeriodUpper) {
periodWrapper.innerHTML = moment().format('A');
periodWrapper.innerHTML = moment().format("A");
} else {
periodWrapper.innerHTML = moment().format('a');
periodWrapper.innerHTML = moment().format("a");
}
// Combine wrappers.
wrapper.appendChild(dateWrapper);
wrapper.appendChild(timeWrapper);
@ -82,7 +76,6 @@ Module.register("clock",{
if (this.config.showPeriod && this.config.timeFormat !== 24) {
timeWrapper.appendChild(periodWrapper);
}
// Return the wrapper to the dom.
return wrapper;
}

View File

@ -55,6 +55,14 @@ The following properties can be configured:
<br><b>Default value:</b> <code>config.units</code>
</td>
</tr>
<tr>
<td><code>maxNumberOfDays</code></td>
<td>How many days of forecast to return. Specified by config.js<br>
<br><b>Possible values:</b> <code>1</code> - <code>16</code>
<br><b>Default value:</b> <code>7</code> (7 days)
<br>This value is optional. By default the weatherforecast module will return 7 days.
</td>
</tr>
<tr>
<td><code>updateInterval</code></td>
<td>How often does the content needs to be fetched? (Milliseconds)<br>

View File

@ -14,6 +14,7 @@ Module.register("weatherforecast",{
location: "",
appid: "",
units: config.units,
maxNumberOfDays: 7,
updateInterval: 10 * 60 * 1000, // every 10 minutes
animationSpeed: 1000,
timeFormat: config.timeFormat,
@ -189,6 +190,12 @@ Module.register("weatherforecast",{
params += "q=" + this.config.location;
params += "&units=" + this.config.units;
params += "&lang=" + this.config.lang;
/*
* Submit a specific number of days to forecast, between 1 to 16 days.
* The OpenWeatherMap API properly handles values outside of the 1 - 16 range and returns 7 days by default.
* This is simply being pedantic and doing it ourselves.
*/
params += "&cnt=" + (((this.config.maxNumberOfDays < 1) || (this.config.maxNumberOfDays > 16)) ? 7 : this.config.maxNumberOfDays);
params += "&APPID=" + this.config.appid;
return params;