Merge branch 'develop' into calendar-module
@@ -1,4 +1,5 @@
|
||||
# Module: Alert
|
||||
|
||||
The alert module is one of the default modules of the MagicMirror. This module displays notifications from other modules.
|
||||
|
||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/alert.html).
|
||||
|
@@ -1,13 +1,12 @@
|
||||
/* global Module */
|
||||
/* global NotificationFx */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: alert
|
||||
*
|
||||
* By Paul-Vincent Roll http://paulvincentroll.com
|
||||
* By Paul-Vincent Roll https://paulvincentroll.com/
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
Module.register("alert",{
|
||||
Module.register("alert", {
|
||||
defaults: {
|
||||
// scale|slide|genie|jelly|flip|bouncyflip|exploader
|
||||
effect: "slide",
|
||||
@@ -18,31 +17,33 @@ Module.register("alert",{
|
||||
//Position
|
||||
position: "center",
|
||||
//shown at startup
|
||||
welcome_message: false,
|
||||
welcome_message: false
|
||||
},
|
||||
getScripts: function() {
|
||||
return ["classie.js", "modernizr.custom.js", "notificationFx.js"];
|
||||
getScripts: function () {
|
||||
return ["notificationFx.js"];
|
||||
},
|
||||
getStyles: function() {
|
||||
return ["ns-default.css", "font-awesome.css"];
|
||||
getStyles: function () {
|
||||
return ["notificationFx.css", "font-awesome.css"];
|
||||
},
|
||||
// Define required translations.
|
||||
getTranslations: function() {
|
||||
getTranslations: function () {
|
||||
return {
|
||||
en: "translations/en.json",
|
||||
de: "translations/de.json",
|
||||
nl: "translations/nl.json",
|
||||
nl: "translations/nl.json"
|
||||
};
|
||||
},
|
||||
show_notification: function(message) {
|
||||
if (this.config.effect === "slide") {this.config.effect = this.config.effect + "-" + this.config.position;}
|
||||
msg = "";
|
||||
show_notification: function (message) {
|
||||
if (this.config.effect === "slide") {
|
||||
this.config.effect = this.config.effect + "-" + this.config.position;
|
||||
}
|
||||
let msg = "";
|
||||
if (message.title) {
|
||||
msg += "<span class='thin dimmed medium'>" + message.title + "</span>";
|
||||
}
|
||||
if (message.message){
|
||||
if (msg !== ""){
|
||||
msg+= "<br />";
|
||||
if (message.message) {
|
||||
if (msg !== "") {
|
||||
msg += "<br />";
|
||||
}
|
||||
msg += "<span class='light bright small'>" + message.message + "</span>";
|
||||
}
|
||||
@@ -54,23 +55,26 @@ Module.register("alert",{
|
||||
ttl: message.timer !== undefined ? message.timer : this.config.display_time
|
||||
}).show();
|
||||
},
|
||||
show_alert: function(params, sender) {
|
||||
var self = this;
|
||||
show_alert: function (params, sender) {
|
||||
let image = "";
|
||||
//Set standard params if not provided by module
|
||||
if (typeof params.timer === "undefined") { params.timer = null; }
|
||||
if (typeof params.imageHeight === "undefined") { params.imageHeight = "80px"; }
|
||||
if (typeof params.timer === "undefined") {
|
||||
params.timer = null;
|
||||
}
|
||||
if (typeof params.imageHeight === "undefined") {
|
||||
params.imageHeight = "80px";
|
||||
}
|
||||
if (typeof params.imageUrl === "undefined" && typeof params.imageFA === "undefined") {
|
||||
params.imageUrl = null;
|
||||
image = "";
|
||||
} else if (typeof params.imageFA === "undefined"){
|
||||
image = "<img src='" + (params.imageUrl).toString() + "' height='" + (params.imageHeight).toString() + "' style='margin-bottom: 10px;'/><br />";
|
||||
} else if (typeof params.imageUrl === "undefined"){
|
||||
image = "<span class='bright " + "fa fa-" + params.imageFA + "' style='margin-bottom: 10px;font-size:" + (params.imageHeight).toString() + ";'/></span><br />";
|
||||
} else if (typeof params.imageFA === "undefined") {
|
||||
image = "<img src='" + params.imageUrl.toString() + "' height='" + params.imageHeight.toString() + "' style='margin-bottom: 10px;'/><br />";
|
||||
} else if (typeof params.imageUrl === "undefined") {
|
||||
image = "<span class='bright " + "fa fa-" + params.imageFA + "' style='margin-bottom: 10px;font-size:" + params.imageHeight.toString() + ";'/></span><br />";
|
||||
}
|
||||
//Create overlay
|
||||
var overlay = document.createElement("div");
|
||||
const overlay = document.createElement("div");
|
||||
overlay.id = "overlay";
|
||||
overlay.innerHTML += "<div class=\"black_overlay\"></div>";
|
||||
overlay.innerHTML += '<div class="black_overlay"></div>';
|
||||
document.body.insertBefore(overlay, document.body.firstChild);
|
||||
|
||||
//If module already has an open alert close it
|
||||
@@ -79,12 +83,12 @@ Module.register("alert",{
|
||||
}
|
||||
|
||||
//Display title and message only if they are provided in notification parameters
|
||||
var message = "";
|
||||
let message = "";
|
||||
if (params.title) {
|
||||
message += "<span class='light dimmed medium'>" + params.title + "</span>";
|
||||
}
|
||||
if (params.message) {
|
||||
if (message !== ""){
|
||||
if (message !== "") {
|
||||
message += "<br />";
|
||||
}
|
||||
|
||||
@@ -102,34 +106,40 @@ Module.register("alert",{
|
||||
this.alerts[sender.name].show();
|
||||
//Add timer to dismiss alert and overlay
|
||||
if (params.timer) {
|
||||
setTimeout(function() {
|
||||
self.hide_alert(sender);
|
||||
setTimeout(() => {
|
||||
this.hide_alert(sender);
|
||||
}, params.timer);
|
||||
}
|
||||
|
||||
},
|
||||
hide_alert: function(sender) {
|
||||
hide_alert: function (sender) {
|
||||
//Dismiss alert and remove from this.alerts
|
||||
if (this.alerts[sender.name]) {
|
||||
this.alerts[sender.name].dismiss();
|
||||
this.alerts[sender.name] = null;
|
||||
//Remove overlay
|
||||
var overlay = document.getElementById("overlay");
|
||||
const 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;}";}
|
||||
const sheet = document.createElement("style");
|
||||
if (pos === "center") {
|
||||
sheet.innerHTML = ".ns-box {margin-left: auto; margin-right: auto;text-align: center;}";
|
||||
}
|
||||
if (pos === "right") {
|
||||
sheet.innerHTML = ".ns-box {margin-left: auto;text-align: right;}";
|
||||
}
|
||||
if (pos === "left") {
|
||||
sheet.innerHTML = ".ns-box {margin-right: auto;text-align: left;}";
|
||||
}
|
||||
document.body.appendChild(sheet);
|
||||
|
||||
},
|
||||
notificationReceived: function(notification, payload, sender) {
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
if (notification === "SHOW_ALERT") {
|
||||
if (typeof payload.type === "undefined") { payload.type = "alert"; }
|
||||
if (typeof payload.type === "undefined") {
|
||||
payload.type = "alert";
|
||||
}
|
||||
if (payload.type === "alert") {
|
||||
this.show_alert(payload, sender);
|
||||
} else if (payload.type === "notification") {
|
||||
@@ -139,15 +149,14 @@ Module.register("alert",{
|
||||
this.hide_alert(sender);
|
||||
}
|
||||
},
|
||||
start: function() {
|
||||
start: function () {
|
||||
this.alerts = {};
|
||||
this.setPosition(this.config.position);
|
||||
if (this.config.welcome_message) {
|
||||
if (this.config.welcome_message === true){
|
||||
this.show_notification({title: this.translate("sysTitle"), message: this.translate("welcome")});
|
||||
}
|
||||
else{
|
||||
this.show_notification({title: this.translate("sysTitle"), message: this.config.welcome_message});
|
||||
if (this.config.welcome_message === true) {
|
||||
this.show_notification({ title: this.translate("sysTitle"), message: this.translate("welcome") });
|
||||
} else {
|
||||
this.show_notification({ title: this.translate("sysTitle"), message: this.config.welcome_message });
|
||||
}
|
||||
}
|
||||
Log.info("Starting module: " + this.name);
|
||||
|
@@ -1,79 +0,0 @@
|
||||
/*!
|
||||
* 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) {
|
||||
|
||||
"use strict";
|
||||
|
||||
// class helper functions from bonzo https://github.com/ded/bonzo
|
||||
|
||||
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), " ");
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
// transport
|
||||
if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(classie);
|
||||
} else {
|
||||
// browser global
|
||||
window.classie = classie;
|
||||
}
|
||||
|
||||
})(window);
|
933
modules/default/alert/notificationFx.css
Normal file
@@ -0,0 +1,933 @@
|
||||
/* Based on work by https://tympanus.net/codrops/licensing/ */
|
||||
|
||||
.ns-box {
|
||||
background-color: rgba(0, 0, 0, 0.93);
|
||||
padding: 17px;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 10px;
|
||||
z-index: 1;
|
||||
color: black;
|
||||
font-size: 70%;
|
||||
position: relative;
|
||||
display: table;
|
||||
word-wrap: break-word;
|
||||
max-width: 100%;
|
||||
border-width: 1px;
|
||||
border-radius: 5px;
|
||||
border-style: solid;
|
||||
border-color: #666;
|
||||
}
|
||||
|
||||
.ns-alert {
|
||||
border-style: solid;
|
||||
border-color: #fff;
|
||||
padding: 17px;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 10px;
|
||||
z-index: 3;
|
||||
color: white;
|
||||
font-size: 70%;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
right: 0;
|
||||
left: 0;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
top: 40%;
|
||||
width: 40%;
|
||||
height: auto;
|
||||
word-wrap: break-word;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.black_overlay {
|
||||
position: fixed;
|
||||
z-index: 2;
|
||||
background-color: rgba(0, 0, 0, 0.93);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
[class^="ns-effect-"].ns-growl.ns-hide,
|
||||
[class*=" ns-effect-"].ns-growl.ns-hide {
|
||||
animation-direction: reverse;
|
||||
}
|
||||
|
||||
.ns-effect-flip {
|
||||
transform-origin: 50% 100%;
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
|
||||
.ns-effect-flip.ns-show,
|
||||
.ns-effect-flip.ns-hide {
|
||||
animation-name: animFlipFront;
|
||||
animation-duration: 0.3s;
|
||||
}
|
||||
|
||||
.ns-effect-flip.ns-hide {
|
||||
animation-name: animFlipBack;
|
||||
}
|
||||
|
||||
@keyframes animFlipFront {
|
||||
0% {
|
||||
transform: perspective(1000px) rotate3d(1, 0, 0, -90deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: perspective(1000px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animFlipBack {
|
||||
0% {
|
||||
transform: perspective(1000px) rotate3d(1, 0, 0, 90deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: perspective(1000px);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-bouncyflip.ns-show,
|
||||
.ns-effect-bouncyflip.ns-hide {
|
||||
animation-name: flipInX;
|
||||
animation-duration: 0.8s;
|
||||
}
|
||||
|
||||
@keyframes flipInX {
|
||||
0% {
|
||||
transform: perspective(400px) rotate3d(1, 0, 0, -90deg);
|
||||
transition-timing-function: ease-in;
|
||||
}
|
||||
|
||||
40% {
|
||||
transform: perspective(400px) rotate3d(1, 0, 0, 20deg);
|
||||
transition-timing-function: ease-out;
|
||||
}
|
||||
|
||||
60% {
|
||||
transform: perspective(400px) rotate3d(1, 0, 0, -10deg);
|
||||
transition-timing-function: ease-in;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
80% {
|
||||
transform: perspective(400px) rotate3d(1, 0, 0, 5deg);
|
||||
transition-timing-function: ease-out;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: perspective(400px);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-bouncyflip.ns-hide {
|
||||
animation-name: flipInXSimple;
|
||||
animation-duration: 0.3s;
|
||||
}
|
||||
|
||||
@keyframes flipInXSimple {
|
||||
0% {
|
||||
transform: perspective(400px) rotate3d(1, 0, 0, -90deg);
|
||||
transition-timing-function: ease-in;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: perspective(400px);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-exploader {
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
.ns-effect-exploader p {
|
||||
padding: 0.25em 2em 0.25em 3em;
|
||||
}
|
||||
|
||||
.ns-effect-exploader.ns-show {
|
||||
animation-name: animLoad;
|
||||
animation-duration: 1s;
|
||||
}
|
||||
|
||||
@keyframes animLoad {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scale3d(0, 0.3, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-exploader.ns-hide {
|
||||
animation-name: animFade;
|
||||
animation-duration: 0.3s;
|
||||
}
|
||||
|
||||
.ns-effect-exploader.ns-show .ns-box-inner,
|
||||
.ns-effect-exploader.ns-show .ns-close {
|
||||
animation-fill-mode: both;
|
||||
animation-duration: 0.3s;
|
||||
animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
.ns-effect-exploader.ns-show .ns-close {
|
||||
animation-name: animFade;
|
||||
}
|
||||
|
||||
.ns-effect-exploader.ns-show .ns-box-inner {
|
||||
animation-name: animFadeMove;
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
|
||||
@keyframes animFadeMove {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate3d(0, 10px, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animFade {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-scale.ns-show,
|
||||
.ns-effect-scale.ns-hide {
|
||||
animation-name: animScale;
|
||||
animation-duration: 0.25s;
|
||||
}
|
||||
|
||||
@keyframes animScale {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate3d(0, 40px, 0) scale3d(0.1, 0.6, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0) scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-jelly.ns-show {
|
||||
animation-name: animJelly;
|
||||
animation-duration: 1s;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
.ns-effect-jelly.ns-hide {
|
||||
animation-name: animFade;
|
||||
animation-duration: 0.3s;
|
||||
}
|
||||
|
||||
@keyframes animFade {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animJelly {
|
||||
0% {
|
||||
transform: matrix3d(0.7, 0, 0, 0, 0, 0.7, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
2.083333% {
|
||||
transform: matrix3d(0.75266, 0, 0, 0, 0, 0.76342, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
4.166667% {
|
||||
transform: matrix3d(0.81071, 0, 0, 0, 0, 0.84545, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
6.25% {
|
||||
transform: matrix3d(0.86808, 0, 0, 0, 0, 0.9286, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
8.333333% {
|
||||
transform: matrix3d(0.92038, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
10.416667% {
|
||||
transform: matrix3d(0.96482, 0, 0, 0, 0, 1.05202, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
12.5% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1.08204, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
14.583333% {
|
||||
transform: matrix3d(1.02563, 0, 0, 0, 0, 1.09149, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
16.666667% {
|
||||
transform: matrix3d(1.04227, 0, 0, 0, 0, 1.08453, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
18.75% {
|
||||
transform: matrix3d(1.05102, 0, 0, 0, 0, 1.06666, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
20.833333% {
|
||||
transform: matrix3d(1.05334, 0, 0, 0, 0, 1.04355, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
22.916667% {
|
||||
transform: matrix3d(1.05078, 0, 0, 0, 0, 1.02012, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: matrix3d(1.04487, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
27.083333% {
|
||||
transform: matrix3d(1.03699, 0, 0, 0, 0, 0.98534, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
29.166667% {
|
||||
transform: matrix3d(1.02831, 0, 0, 0, 0, 0.97688, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
31.25% {
|
||||
transform: matrix3d(1.01973, 0, 0, 0, 0, 0.97422, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
33.333333% {
|
||||
transform: matrix3d(1.01191, 0, 0, 0, 0, 0.97618, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
35.416667% {
|
||||
transform: matrix3d(1.00526, 0, 0, 0, 0, 0.98122, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
37.5% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.98773, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
39.583333% {
|
||||
transform: matrix3d(0.99617, 0, 0, 0, 0, 0.99433, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
41.666667% {
|
||||
transform: matrix3d(0.99368, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
43.75% {
|
||||
transform: matrix3d(0.99237, 0, 0, 0, 0, 1.00413, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
45.833333% {
|
||||
transform: matrix3d(0.99202, 0, 0, 0, 0, 1.00651, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
47.916667% {
|
||||
transform: matrix3d(0.99241, 0, 0, 0, 0, 1.00726, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: matrix3d(0.99329, 0, 0, 0, 0, 1.00671, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
52.083333% {
|
||||
transform: matrix3d(0.99447, 0, 0, 0, 0, 1.00529, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
54.166667% {
|
||||
transform: matrix3d(0.99577, 0, 0, 0, 0, 1.00346, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
56.25% {
|
||||
transform: matrix3d(0.99705, 0, 0, 0, 0, 1.0016, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
58.333333% {
|
||||
transform: matrix3d(0.99822, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
60.416667% {
|
||||
transform: matrix3d(0.99921, 0, 0, 0, 0, 0.99884, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
62.5% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.99816, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
64.583333% {
|
||||
transform: matrix3d(1.00057, 0, 0, 0, 0, 0.99795, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
66.666667% {
|
||||
transform: matrix3d(1.00095, 0, 0, 0, 0, 0.99811, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
68.75% {
|
||||
transform: matrix3d(1.00114, 0, 0, 0, 0, 0.99851, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
70.833333% {
|
||||
transform: matrix3d(1.00119, 0, 0, 0, 0, 0.99903, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
72.916667% {
|
||||
transform: matrix3d(1.00114, 0, 0, 0, 0, 0.99955, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: matrix3d(1.001, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
77.083333% {
|
||||
transform: matrix3d(1.00083, 0, 0, 0, 0, 1.00033, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
79.166667% {
|
||||
transform: matrix3d(1.00063, 0, 0, 0, 0, 1.00052, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
81.25% {
|
||||
transform: matrix3d(1.00044, 0, 0, 0, 0, 1.00058, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
83.333333% {
|
||||
transform: matrix3d(1.00027, 0, 0, 0, 0, 1.00053, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
85.416667% {
|
||||
transform: matrix3d(1.00012, 0, 0, 0, 0, 1.00042, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
87.5% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1.00027, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
89.583333% {
|
||||
transform: matrix3d(0.99991, 0, 0, 0, 0, 1.00013, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
91.666667% {
|
||||
transform: matrix3d(0.99986, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
93.75% {
|
||||
transform: matrix3d(0.99983, 0, 0, 0, 0, 0.99991, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
95.833333% {
|
||||
transform: matrix3d(0.99982, 0, 0, 0, 0, 0.99985, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
97.916667% {
|
||||
transform: matrix3d(0.99983, 0, 0, 0, 0, 0.99984, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-slide-left.ns-show {
|
||||
animation-name: animSlideElasticLeft;
|
||||
animation-duration: 1s;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
@keyframes animSlideElasticLeft {
|
||||
0% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -1000, 0, 0, 1);
|
||||
}
|
||||
|
||||
1.666667% {
|
||||
transform: matrix3d(1.92933, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -739.26805, 0, 0, 1);
|
||||
}
|
||||
|
||||
3.333333% {
|
||||
transform: matrix3d(1.96989, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -521.82545, 0, 0, 1);
|
||||
}
|
||||
|
||||
5% {
|
||||
transform: matrix3d(1.70901, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -349.26115, 0, 0, 1);
|
||||
}
|
||||
|
||||
6.666667% {
|
||||
transform: matrix3d(1.4235, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -218.3238, 0, 0, 1);
|
||||
}
|
||||
|
||||
8.333333% {
|
||||
transform: matrix3d(1.21065, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -123.29848, 0, 0, 1);
|
||||
}
|
||||
|
||||
10% {
|
||||
transform: matrix3d(1.08167, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -57.59273, 0, 0, 1);
|
||||
}
|
||||
|
||||
11.666667% {
|
||||
transform: matrix3d(1.0165, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -14.72371, 0, 0, 1);
|
||||
}
|
||||
|
||||
13.333333% {
|
||||
transform: matrix3d(0.99057, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 11.12794, 0, 0, 1);
|
||||
}
|
||||
|
||||
15% {
|
||||
transform: matrix3d(0.98478, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 24.86339, 0, 0, 1);
|
||||
}
|
||||
|
||||
16.666667% {
|
||||
transform: matrix3d(0.98719, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 30.40503, 0, 0, 1);
|
||||
}
|
||||
|
||||
18.333333% {
|
||||
transform: matrix3d(0.9916, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 30.75275, 0, 0, 1);
|
||||
}
|
||||
|
||||
20% {
|
||||
transform: matrix3d(0.99541, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 28.10141, 0, 0, 1);
|
||||
}
|
||||
|
||||
21.666667% {
|
||||
transform: matrix3d(0.99795, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 23.98271, 0, 0, 1);
|
||||
}
|
||||
|
||||
23.333333% {
|
||||
transform: matrix3d(0.99936, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 19.40752, 0, 0, 1);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 14.99558, 0, 0, 1);
|
||||
}
|
||||
|
||||
26.666667% {
|
||||
transform: matrix3d(1.00021, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 11.08575, 0, 0, 1);
|
||||
}
|
||||
|
||||
28.333333% {
|
||||
transform: matrix3d(1.00022, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 7.82507, 0, 0, 1);
|
||||
}
|
||||
|
||||
30% {
|
||||
transform: matrix3d(1.00016, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 5.23737, 0, 0, 1);
|
||||
}
|
||||
|
||||
31.666667% {
|
||||
transform: matrix3d(1.0001, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 3.27389, 0, 0, 1);
|
||||
}
|
||||
|
||||
33.333333% {
|
||||
transform: matrix3d(1.00005, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1.84893, 0, 0, 1);
|
||||
}
|
||||
|
||||
35% {
|
||||
transform: matrix3d(1.00002, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.86364, 0, 0, 1);
|
||||
}
|
||||
|
||||
36.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.22079, 0, 0, 1);
|
||||
}
|
||||
|
||||
38.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.16687, 0, 0, 1);
|
||||
}
|
||||
|
||||
40% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.37284, 0, 0, 1);
|
||||
}
|
||||
|
||||
41.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.45594, 0, 0, 1);
|
||||
}
|
||||
|
||||
43.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.46116, 0, 0, 1);
|
||||
}
|
||||
|
||||
45% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.4214, 0, 0, 1);
|
||||
}
|
||||
|
||||
46.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.35963, 0, 0, 1);
|
||||
}
|
||||
|
||||
48.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.29103, 0, 0, 1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.22487, 0, 0, 1);
|
||||
}
|
||||
|
||||
51.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.16624, 0, 0, 1);
|
||||
}
|
||||
|
||||
53.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.11734, 0, 0, 1);
|
||||
}
|
||||
|
||||
55% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.07854, 0, 0, 1);
|
||||
}
|
||||
|
||||
56.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.04909, 0, 0, 1);
|
||||
}
|
||||
|
||||
58.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.02773, 0, 0, 1);
|
||||
}
|
||||
|
||||
60% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.01295, 0, 0, 1);
|
||||
}
|
||||
|
||||
61.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.00331, 0, 0, 1);
|
||||
}
|
||||
|
||||
63.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.0025, 0, 0, 1);
|
||||
}
|
||||
|
||||
65% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00559, 0, 0, 1);
|
||||
}
|
||||
|
||||
66.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00684, 0, 0, 1);
|
||||
}
|
||||
|
||||
68.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00692, 0, 0, 1);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00632, 0, 0, 1);
|
||||
}
|
||||
|
||||
71.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00539, 0, 0, 1);
|
||||
}
|
||||
|
||||
73.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00436, 0, 0, 1);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00337, 0, 0, 1);
|
||||
}
|
||||
|
||||
76.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00249, 0, 0, 1);
|
||||
}
|
||||
|
||||
78.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00176, 0, 0, 1);
|
||||
}
|
||||
|
||||
80% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00118, 0, 0, 1);
|
||||
}
|
||||
|
||||
81.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00074, 0, 0, 1);
|
||||
}
|
||||
|
||||
83.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00042, 0, 0, 1);
|
||||
}
|
||||
|
||||
85% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00019, 0, 0, 1);
|
||||
}
|
||||
|
||||
86.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.00005, 0, 0, 1);
|
||||
}
|
||||
|
||||
88.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.00004, 0, 0, 1);
|
||||
}
|
||||
|
||||
90% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.00008, 0, 0, 1);
|
||||
}
|
||||
|
||||
91.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.0001, 0, 0, 1);
|
||||
}
|
||||
|
||||
93.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.0001, 0, 0, 1);
|
||||
}
|
||||
|
||||
95% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.00009, 0, 0, 1);
|
||||
}
|
||||
|
||||
96.666667% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.00008, 0, 0, 1);
|
||||
}
|
||||
|
||||
98.333333% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.00007, 0, 0, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-slide-left.ns-hide {
|
||||
animation-name: animSlideLeft;
|
||||
animation-duration: 0.25s;
|
||||
}
|
||||
|
||||
@keyframes animSlideLeft {
|
||||
0% {
|
||||
transform: translate3d(-30px, 0, 0) translate3d(-100%, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-slide-right.ns-show {
|
||||
animation: animSlideElasticRight 2000ms linear both;
|
||||
}
|
||||
|
||||
@keyframes animSlideElasticRight {
|
||||
0% {
|
||||
transform: matrix3d(2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1000, 0, 0, 1);
|
||||
}
|
||||
|
||||
2.15% {
|
||||
transform: matrix3d(1.486, 0, 0, 0, 0, 0.514, 0, 0, 0, 0, 1, 0, 664.594, 0, 0, 1);
|
||||
}
|
||||
|
||||
4.1% {
|
||||
transform: matrix3d(1.147, 0, 0, 0, 0, 0.853, 0, 0, 0, 0, 1, 0, 419.708, 0, 0, 1);
|
||||
}
|
||||
|
||||
4.3% {
|
||||
transform: matrix3d(1.121, 0, 0, 0, 0, 0.879, 0, 0, 0, 0, 1, 0, 398.136, 0, 0, 1);
|
||||
}
|
||||
|
||||
6.46% {
|
||||
transform: matrix3d(0.948, 0, 0, 0, 0, 1.052, 0, 0, 0, 0, 1, 0, 206.714, 0, 0, 1);
|
||||
}
|
||||
|
||||
8.11% {
|
||||
transform: matrix3d(0.908, 0, 0, 0, 0, 1.092, 0, 0, 0, 0, 1, 0, 105.491, 0, 0, 1);
|
||||
}
|
||||
|
||||
8.61% {
|
||||
transform: matrix3d(0.907, 0, 0, 0, 0, 1.093, 0, 0, 0, 0, 1, 0, 81.572, 0, 0, 1);
|
||||
}
|
||||
|
||||
12.11% {
|
||||
transform: matrix3d(0.95, 0, 0, 0, 0, 1.05, 0, 0, 0, 0, 1, 0, -18.434, 0, 0, 1);
|
||||
}
|
||||
|
||||
14.16% {
|
||||
transform: matrix3d(0.979, 0, 0, 0, 0, 1.021, 0, 0, 0, 0, 1, 0, -38.734, 0, 0, 1);
|
||||
}
|
||||
|
||||
16.12% {
|
||||
transform: matrix3d(0.997, 0, 0, 0, 0, 1.003, 0, 0, 0, 0, 1, 0, -43.356, 0, 0, 1);
|
||||
}
|
||||
|
||||
19.72% {
|
||||
transform: matrix3d(1.006, 0, 0, 0, 0, 0.994, 0, 0, 0, 0, 1, 0, -34.155, 0, 0, 1);
|
||||
}
|
||||
|
||||
27.23% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -7.839, 0, 0, 1);
|
||||
}
|
||||
|
||||
30.83% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -1.951, 0, 0, 1);
|
||||
}
|
||||
|
||||
38.34% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1.037, 0, 0, 1);
|
||||
}
|
||||
|
||||
41.99% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.812, 0, 0, 1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.159, 0, 0, 1);
|
||||
}
|
||||
|
||||
60.56% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -0.025, 0, 0, 1);
|
||||
}
|
||||
|
||||
82.78% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0.001, 0, 0, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-slide-right.ns-hide {
|
||||
animation-name: animSlideRight;
|
||||
animation-duration: 0.25s;
|
||||
}
|
||||
|
||||
@keyframes animSlideRight {
|
||||
0% {
|
||||
transform: translate3d(30px, 0, 0) translate3d(100%, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-slide-center.ns-show {
|
||||
animation: animSlideElasticCenter 2000ms linear both;
|
||||
}
|
||||
|
||||
@keyframes animSlideElasticCenter {
|
||||
0% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, -300, 0, 1);
|
||||
}
|
||||
|
||||
2.15% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1.971, 0, 0, 0, 0, 1, 0, 0, -199.378, 0, 1);
|
||||
}
|
||||
|
||||
4.1% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1.294, 0, 0, 0, 0, 1, 0, 0, -125.912, 0, 1);
|
||||
}
|
||||
|
||||
4.3% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1.243, 0, 0, 0, 0, 1, 0, 0, -119.441, 0, 1);
|
||||
}
|
||||
|
||||
6.46% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.895, 0, 0, 0, 0, 1, 0, 0, -62.014, 0, 1);
|
||||
}
|
||||
|
||||
8.11% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.817, 0, 0, 0, 0, 1, 0, 0, -31.647, 0, 1);
|
||||
}
|
||||
|
||||
8.61% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.813, 0, 0, 0, 0, 1, 0, 0, -24.472, 0, 1);
|
||||
}
|
||||
|
||||
12.11% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.9, 0, 0, 0, 0, 1, 0, 0, 5.53, 0, 1);
|
||||
}
|
||||
|
||||
14.16% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.959, 0, 0, 0, 0, 1, 0, 0, 11.62, 0, 1);
|
||||
}
|
||||
|
||||
16.12% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.994, 0, 0, 0, 0, 1, 0, 0, 13.007, 0, 1);
|
||||
}
|
||||
|
||||
19.72% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1.012, 0, 0, 0, 0, 1, 0, 0, 10.247, 0, 1);
|
||||
}
|
||||
|
||||
27.23% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 2.352, 0, 1);
|
||||
}
|
||||
|
||||
30.83% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 0.999, 0, 0, 0, 0, 1, 0, 0, 0.585, 0, 1);
|
||||
}
|
||||
|
||||
38.34% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, -0.311, 0, 1);
|
||||
}
|
||||
|
||||
41.99% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, -0.244, 0, 1);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, -0.048, 0, 1);
|
||||
}
|
||||
|
||||
60.56% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0.007, 0, 1);
|
||||
}
|
||||
|
||||
82.78% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-slide-center.ns-hide {
|
||||
animation-name: animSlideCenter;
|
||||
animation-duration: 0.25s;
|
||||
}
|
||||
|
||||
@keyframes animSlideCenter {
|
||||
0% {
|
||||
transform: translate3d(0, -30px, 0) translate3d(0, -100%, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.ns-effect-genie.ns-show,
|
||||
.ns-effect-genie.ns-hide {
|
||||
animation-name: animGenie;
|
||||
animation-duration: 0.4s;
|
||||
}
|
||||
|
||||
@keyframes animGenie {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate3d(0, calc(200% + 30px), 0) scale3d(0, 1, 1);
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
|
||||
40% {
|
||||
opacity: 0.5;
|
||||
transform: translate3d(0, 0, 0) scale3d(0.02, 1.1, 1);
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
|
||||
70% {
|
||||
opacity: 0.6;
|
||||
transform: translate3d(0, -40px, 0) scale3d(0.8, 1.1, 1);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0) scale3d(1, 1, 1);
|
||||
}
|
||||
}
|
@@ -1,34 +1,21 @@
|
||||
/**
|
||||
* Based on work by
|
||||
*
|
||||
* notificationFx.js v1.0.0
|
||||
* http://www.codrops.com
|
||||
* https://tympanus.net/codrops/
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* https://opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Copyright 2014, Codrops
|
||||
* http://www.codrops.com
|
||||
* https://tympanus.net/codrops/
|
||||
*/
|
||||
// jscs:disable
|
||||
;(function(window) {
|
||||
|
||||
"use strict";
|
||||
|
||||
var docElem = window.document.documentElement,
|
||||
support = {animations: Modernizr.cssanimations},
|
||||
animEndEventNames = {
|
||||
"WebkitAnimation": "webkitAnimationEnd",
|
||||
"OAnimation": "oAnimationEnd",
|
||||
"msAnimation": "MSAnimationEnd",
|
||||
"animation": "animationend"
|
||||
},
|
||||
// animation end event name
|
||||
animEndEventName = animEndEventNames[ Modernizr.prefixed("animation") ];
|
||||
|
||||
(function (window) {
|
||||
/**
|
||||
* extend obj function
|
||||
*/
|
||||
function extend(a, b) {
|
||||
for (var key in b) {
|
||||
for (let key in b) {
|
||||
if (b.hasOwnProperty(key)) {
|
||||
a[key] = b[key];
|
||||
}
|
||||
@@ -70,19 +57,23 @@
|
||||
ttl: 6000,
|
||||
al_no: "ns-box",
|
||||
// callbacks
|
||||
onClose: function() { return false; },
|
||||
onOpen: function() { return false; }
|
||||
onClose: function () {
|
||||
return false;
|
||||
},
|
||||
onOpen: function () {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* init function
|
||||
* initialize and cache some vars
|
||||
*/
|
||||
NotificationFx.prototype._init = function() {
|
||||
NotificationFx.prototype._init = function () {
|
||||
// create HTML structure
|
||||
this.ntf = document.createElement("div");
|
||||
this.ntf.className = this.options.al_no + " ns-" + this.options.layout + " ns-effect-" + this.options.effect + " ns-type-" + this.options.type;
|
||||
var strinner = "<div class=\"ns-box-inner\">";
|
||||
this.ntf.className = this.options.al_no + " ns-" + this.options.layout + " ns-effect-" + this.options.effect + " ns-type-" + this.options.type;
|
||||
let strinner = '<div class="ns-box-inner">';
|
||||
strinner += this.options.message;
|
||||
strinner += "</div>";
|
||||
this.ntf.innerHTML = strinner;
|
||||
@@ -91,13 +82,12 @@
|
||||
this.options.wrapper.insertBefore(this.ntf, this.options.wrapper.nextSibling);
|
||||
|
||||
// dismiss after [options.ttl]ms
|
||||
var self = this;
|
||||
if (this.options.ttl) {
|
||||
this.dismissttl = setTimeout(function() {
|
||||
if (self.active) {
|
||||
self.dismiss();
|
||||
}
|
||||
}, this.options.ttl);
|
||||
this.dismissttl = setTimeout(() => {
|
||||
if (this.active) {
|
||||
this.dismiss();
|
||||
}
|
||||
}, this.options.ttl);
|
||||
}
|
||||
|
||||
// init events
|
||||
@@ -107,59 +97,54 @@
|
||||
/**
|
||||
* init events
|
||||
*/
|
||||
NotificationFx.prototype._initEvents = function() {
|
||||
var self = this;
|
||||
NotificationFx.prototype._initEvents = function () {
|
||||
// dismiss notification by tapping on it if someone has a touchscreen
|
||||
this.ntf.querySelector(".ns-box-inner").addEventListener("click", function() { self.dismiss(); });
|
||||
this.ntf.querySelector(".ns-box-inner").addEventListener("click", () => {
|
||||
this.dismiss();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* show the notification
|
||||
*/
|
||||
NotificationFx.prototype.show = function() {
|
||||
NotificationFx.prototype.show = function () {
|
||||
this.active = true;
|
||||
classie.remove(this.ntf, "ns-hide");
|
||||
classie.add(this.ntf, "ns-show");
|
||||
this.ntf.classList.remove("ns-hide");
|
||||
this.ntf.classList.add("ns-show");
|
||||
this.options.onOpen();
|
||||
};
|
||||
|
||||
/**
|
||||
* dismiss the notification
|
||||
*/
|
||||
NotificationFx.prototype.dismiss = function() {
|
||||
var self = this;
|
||||
NotificationFx.prototype.dismiss = function () {
|
||||
this.active = false;
|
||||
clearTimeout(this.dismissttl);
|
||||
classie.remove(this.ntf, "ns-show");
|
||||
setTimeout(function() {
|
||||
classie.add(self.ntf, "ns-hide");
|
||||
this.ntf.classList.remove("ns-show");
|
||||
setTimeout(() => {
|
||||
this.ntf.classList.add("ns-hide");
|
||||
|
||||
// callback
|
||||
self.options.onClose();
|
||||
this.options.onClose();
|
||||
}, 25);
|
||||
|
||||
// after animation ends remove ntf from the DOM
|
||||
var onEndAnimationFn = function(ev) {
|
||||
if (support.animations) {
|
||||
if (ev.target !== self.ntf) return false;
|
||||
this.removeEventListener(animEndEventName, onEndAnimationFn);
|
||||
const onEndAnimationFn = (ev) => {
|
||||
if (ev.target !== this.ntf) {
|
||||
return false;
|
||||
}
|
||||
this.ntf.removeEventListener("animationend", onEndAnimationFn);
|
||||
|
||||
if (this.parentNode === self.options.wrapper) {
|
||||
self.options.wrapper.removeChild(this);
|
||||
if (ev.target.parentNode === this.options.wrapper) {
|
||||
this.options.wrapper.removeChild(this.ntf);
|
||||
}
|
||||
};
|
||||
|
||||
if (support.animations) {
|
||||
this.ntf.addEventListener(animEndEventName, onEndAnimationFn);
|
||||
} else {
|
||||
onEndAnimationFn();
|
||||
}
|
||||
this.ntf.addEventListener("animationend", onEndAnimationFn);
|
||||
};
|
||||
|
||||
/**
|
||||
* add to global namespace
|
||||
*/
|
||||
window.NotificationFx = NotificationFx;
|
||||
|
||||
})(window);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror нотификация",
|
||||
"welcome": "Добре дошли, стартирането беше успешно"
|
||||
"sysTitle": "MagicMirror нотификация",
|
||||
"welcome": "Добре дошли, стартирането беше успешно"
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror Notifikation",
|
||||
"welcome": "Velkommen, modulet er succesfuldt startet!"
|
||||
"sysTitle": "MagicMirror Notifikation",
|
||||
"welcome": "Velkommen, modulet er succesfuldt startet!"
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror Benachrichtigung",
|
||||
"welcome": "Willkommen, Start war erfolgreich!"
|
||||
"sysTitle": "MagicMirror Benachrichtigung",
|
||||
"welcome": "Willkommen, Start war erfolgreich!"
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror Notification",
|
||||
"welcome": "Welcome, start was successful!"
|
||||
"sysTitle": "MagicMirror Notification",
|
||||
"welcome": "Welcome, start was successful!"
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror Notificaciones",
|
||||
"welcome": "Bienvenido, ¡se iniciado correctamente!"
|
||||
"sysTitle": "MagicMirror Notificaciones",
|
||||
"welcome": "Bienvenido, ¡se iniciado correctamente!"
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror Notification",
|
||||
"welcome": "Bienvenue, le démarrage a été un succès!"
|
||||
"sysTitle": "MagicMirror Notification",
|
||||
"welcome": "Bienvenue, le démarrage a été un succès!"
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror értesítés",
|
||||
"welcome": "Üdvözöljük, indulás sikeres!"
|
||||
"sysTitle": "MagicMirror értesítés",
|
||||
"welcome": "Üdvözöljük, indulás sikeres!"
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror Notificatie",
|
||||
"welcome": "Welkom, Succesvol gestart!"
|
||||
"sysTitle": "MagicMirror Notificatie",
|
||||
"welcome": "Welkom, Succesvol gestart!"
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror Уведомление",
|
||||
"welcome": "Добро пожаловать, старт был успешным!"
|
||||
"sysTitle": "MagicMirror Уведомление",
|
||||
"welcome": "Добро пожаловать, старт был успешным!"
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
# Module: Calendar
|
||||
|
||||
The `calendar` module is one of the default modules of the MagicMirror.
|
||||
This module displays events from a public .ical calendar. It can combine multiple calendars.
|
||||
|
||||
|
@@ -7,8 +7,6 @@
|
||||
|
||||
.calendar .symbol span {
|
||||
display: inline-block;
|
||||
-ms-transform: translate(0, 2px); /* IE 9 */
|
||||
-webkit-transform: translate(0, 2px); /* Safari */
|
||||
transform: translate(0, 2px);
|
||||
}
|
||||
|
||||
|
@@ -1,20 +1,18 @@
|
||||
/* global Module */
|
||||
/* global cloneObject */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: Calendar
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
Module.register("calendar", {
|
||||
|
||||
// Define module defaults
|
||||
defaults: {
|
||||
maximumEntries: 10, // Total Maximum Entries
|
||||
maximumNumberOfDays: 365,
|
||||
displaySymbol: true,
|
||||
defaultSymbol: "calendar", // Fontawesome Symbol see http://fontawesome.io/cheatsheet/
|
||||
defaultSymbol: "calendar", // Fontawesome Symbol see https://fontawesome.com/cheatsheet?from=io
|
||||
showLocation: false,
|
||||
displayRepeatingCountTitle: false,
|
||||
defaultRepeatingCountTitle: "",
|
||||
@@ -40,8 +38,8 @@ Module.register("calendar", {
|
||||
calendars: [
|
||||
{
|
||||
symbol: "calendar",
|
||||
url: "http://www.calendarlabs.com/templates/ical/US-Holidays.ics",
|
||||
},
|
||||
url: "https://www.calendarlabs.com/templates/ical/US-Holidays.ics"
|
||||
}
|
||||
],
|
||||
titleReplace: {
|
||||
"De verjaardag van ": "",
|
||||
@@ -86,7 +84,7 @@ Module.register("calendar", {
|
||||
var calendarConfig = {
|
||||
maximumEntries: calendar.maximumEntries,
|
||||
maximumNumberOfDays: calendar.maximumNumberOfDays,
|
||||
broadcastPastEvents: calendar.broadcastPastEvents,
|
||||
broadcastPastEvents: calendar.broadcastPastEvents
|
||||
};
|
||||
if (calendar.symbolClass === "undefined" || calendar.symbolClass === null) {
|
||||
calendarConfig.symbolClass = "";
|
||||
@@ -99,7 +97,7 @@ Module.register("calendar", {
|
||||
}
|
||||
|
||||
// we check user and password here for backwards compatibility with old configs
|
||||
if(calendar.user && calendar.pass) {
|
||||
if (calendar.user && calendar.pass) {
|
||||
Log.warn("Deprecation warning: Please update your calendar authentication configuration.");
|
||||
Log.warn("https://github.com/MichMich/MagicMirror/tree/v2.1.2/modules/default/calendar#calendar-authentication-options");
|
||||
calendar.auth = {
|
||||
@@ -113,7 +111,7 @@ Module.register("calendar", {
|
||||
// Trigger ADD_CALENDAR every fetchInterval to make sure there is always a calendar
|
||||
// fetcher running on the server side.
|
||||
var self = this;
|
||||
setInterval(function() {
|
||||
setInterval(function () {
|
||||
self.addCalendar(calendar.url, calendar.auth, calendarConfig);
|
||||
}, self.config.fetchInterval);
|
||||
}
|
||||
@@ -145,13 +143,12 @@ Module.register("calendar", {
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function () {
|
||||
|
||||
var events = this.createEventList();
|
||||
var wrapper = document.createElement("table");
|
||||
wrapper.className = this.config.tableClass;
|
||||
|
||||
if (events.length === 0) {
|
||||
wrapper.innerHTML = (this.loaded) ? this.translate("EMPTY") : this.translate("LOADING");
|
||||
wrapper.innerHTML = this.loaded ? this.translate("EMPTY") : this.translate("LOADING");
|
||||
wrapper.className = this.config.tableClass + " dimmed";
|
||||
return wrapper;
|
||||
}
|
||||
@@ -170,8 +167,8 @@ Module.register("calendar", {
|
||||
for (var e in events) {
|
||||
var event = events[e];
|
||||
var dateAsString = moment(event.startDate, "x").format(this.config.dateFormat);
|
||||
if(this.config.timeFormat === "dateheaders"){
|
||||
if(lastSeenDate !== dateAsString){
|
||||
if (this.config.timeFormat === "dateheaders") {
|
||||
if (lastSeenDate !== dateAsString) {
|
||||
var dateRow = document.createElement("tr");
|
||||
dateRow.className = "normal";
|
||||
var dateCell = document.createElement("td");
|
||||
@@ -182,9 +179,10 @@ Module.register("calendar", {
|
||||
dateRow.appendChild(dateCell);
|
||||
wrapper.appendChild(dateRow);
|
||||
|
||||
if (e >= startFade) { //fading
|
||||
if (e >= startFade) {
|
||||
//fading
|
||||
currentFadeStep = e - startFade;
|
||||
dateRow.style.opacity = 1 - (1 / fadeSteps * currentFadeStep);
|
||||
dateRow.style.opacity = 1 - (1 / fadeSteps) * currentFadeStep;
|
||||
}
|
||||
|
||||
lastSeenDate = dateAsString;
|
||||
@@ -210,20 +208,20 @@ Module.register("calendar", {
|
||||
symbolWrapper.className = "symbol align-right " + symbolClass;
|
||||
|
||||
var symbols = this.symbolsForUrl(event.url);
|
||||
if(typeof symbols === "string") {
|
||||
if (typeof symbols === "string") {
|
||||
symbols = [symbols];
|
||||
}
|
||||
|
||||
for(var i = 0; i < symbols.length; i++) {
|
||||
for (var i = 0; i < symbols.length; i++) {
|
||||
var symbol = document.createElement("span");
|
||||
symbol.className = "fa fa-fw fa-" + symbols[i];
|
||||
if(i > 0){
|
||||
if (i > 0) {
|
||||
symbol.style.paddingLeft = "5px";
|
||||
}
|
||||
symbolWrapper.appendChild(symbol);
|
||||
}
|
||||
eventWrapper.appendChild(symbolWrapper);
|
||||
} else if(this.config.timeFormat === "dateheaders"){
|
||||
} else if (this.config.timeFormat === "dateheaders") {
|
||||
var blankCell = document.createElement("td");
|
||||
blankCell.innerHTML = " ";
|
||||
eventWrapper.appendChild(blankCell);
|
||||
@@ -233,7 +231,6 @@ Module.register("calendar", {
|
||||
repeatingCountTitle = "";
|
||||
|
||||
if (this.config.displayRepeatingCountTitle && event.firstYear !== undefined) {
|
||||
|
||||
repeatingCountTitle = this.countTitleForUrl(event.url);
|
||||
|
||||
if (repeatingCountTitle !== "") {
|
||||
@@ -254,17 +251,15 @@ Module.register("calendar", {
|
||||
titleWrapper.className = "title " + titleClass;
|
||||
}
|
||||
|
||||
if(this.config.timeFormat === "dateheaders"){
|
||||
var timeWrapper;
|
||||
|
||||
if (this.config.timeFormat === "dateheaders") {
|
||||
if (event.fullDayEvent) {
|
||||
titleWrapper.colSpan = "2";
|
||||
titleWrapper.align = "left";
|
||||
|
||||
} else {
|
||||
|
||||
var timeClass = this.timeClassForUrl(event.url);
|
||||
var timeWrapper = document.createElement("td");
|
||||
timeWrapper.className = "time light " + timeClass;
|
||||
timeWrapper = document.createElement("td");
|
||||
timeWrapper.className = "time light " + this.timeClassForUrl(event.url);
|
||||
timeWrapper.align = "left";
|
||||
timeWrapper.style.paddingLeft = "2px";
|
||||
timeWrapper.innerHTML = moment(event.startDate, "x").format("LT");
|
||||
@@ -274,7 +269,7 @@ Module.register("calendar", {
|
||||
|
||||
eventWrapper.appendChild(titleWrapper);
|
||||
} else {
|
||||
var timeWrapper = document.createElement("td");
|
||||
timeWrapper = document.createElement("td");
|
||||
|
||||
eventWrapper.appendChild(titleWrapper);
|
||||
//console.log(event.today);
|
||||
@@ -299,14 +294,14 @@ Module.register("calendar", {
|
||||
}
|
||||
} else {
|
||||
/* Check to see if the user displays absolute or relative dates with their events
|
||||
* Also check to see if an event is happening within an 'urgency' time frameElement
|
||||
* For example, if the user set an .urgency of 7 days, those events that fall within that
|
||||
* time frame will be displayed with 'in xxx' time format or moment.fromNow()
|
||||
*
|
||||
* Note: this needs to be put in its own function, as the whole thing repeats again verbatim
|
||||
*/
|
||||
* Also check to see if an event is happening within an 'urgency' time frameElement
|
||||
* For example, if the user set an .urgency of 7 days, those events that fall within that
|
||||
* time frame will be displayed with 'in xxx' time format or moment.fromNow()
|
||||
*
|
||||
* Note: this needs to be put in its own function, as the whole thing repeats again verbatim
|
||||
*/
|
||||
if (this.config.timeFormat === "absolute") {
|
||||
if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * oneDay))) {
|
||||
if (this.config.urgency > 1 && event.startDate - now < this.config.urgency * oneDay) {
|
||||
// This event falls within the config.urgency period that the user has set
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").from(moment().format("YYYYMMDD")));
|
||||
} else {
|
||||
@@ -316,9 +311,9 @@ Module.register("calendar", {
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").from(moment().format("YYYYMMDD")));
|
||||
}
|
||||
}
|
||||
if(this.config.showEnd){
|
||||
timeWrapper.innerHTML += "-" ;
|
||||
timeWrapper.innerHTML += this.capFirst(moment(event.endDate , "x").format(this.config.fullDayEventDateFormat));
|
||||
if (this.config.showEnd) {
|
||||
timeWrapper.innerHTML += "-";
|
||||
timeWrapper.innerHTML += this.capFirst(moment(event.endDate, "x").format(this.config.fullDayEventDateFormat));
|
||||
}
|
||||
} else {
|
||||
if (event.startDate >= new Date()) {
|
||||
@@ -328,7 +323,7 @@ Module.register("calendar", {
|
||||
// If event is within 6 hour, display 'in xxx' time format or moment.fromNow()
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
||||
} else {
|
||||
if(this.config.timeFormat === "absolute" && !this.config.nextDaysRelative) {
|
||||
if (this.config.timeFormat === "absolute" && !this.config.nextDaysRelative) {
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.dateFormat));
|
||||
} else {
|
||||
// Otherwise just say 'Today/Tomorrow at such-n-such time'
|
||||
@@ -337,14 +332,14 @@ Module.register("calendar", {
|
||||
}
|
||||
} else {
|
||||
/* Check to see if the user displays absolute or relative dates with their events
|
||||
* Also check to see if an event is happening within an 'urgency' time frameElement
|
||||
* For example, if the user set an .urgency of 7 days, those events that fall within that
|
||||
* time frame will be displayed with 'in xxx' time format or moment.fromNow()
|
||||
*
|
||||
* Note: this needs to be put in its own function, as the whole thing repeats again verbatim
|
||||
*/
|
||||
* Also check to see if an event is happening within an 'urgency' time frameElement
|
||||
* For example, if the user set an .urgency of 7 days, those events that fall within that
|
||||
* time frame will be displayed with 'in xxx' time format or moment.fromNow()
|
||||
*
|
||||
* Note: this needs to be put in its own function, as the whole thing repeats again verbatim
|
||||
*/
|
||||
if (this.config.timeFormat === "absolute") {
|
||||
if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * oneDay))) {
|
||||
if (this.config.urgency > 1 && event.startDate - now < this.config.urgency * oneDay) {
|
||||
// This event falls within the config.urgency period that the user has set
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
||||
} else {
|
||||
@@ -365,13 +360,11 @@ Module.register("calendar", {
|
||||
if (this.config.showEnd) {
|
||||
timeWrapper.innerHTML += "-";
|
||||
timeWrapper.innerHTML += this.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat));
|
||||
|
||||
}
|
||||
}
|
||||
//timeWrapper.innerHTML += ' - '+ moment(event.startDate,'x').format('lll');
|
||||
//console.log(event);
|
||||
var timeClass = this.timeClassForUrl(event.url);
|
||||
timeWrapper.className = "time light " + timeClass;
|
||||
timeWrapper.className = "time light " + this.timeClassForUrl(event.url);
|
||||
eventWrapper.appendChild(timeWrapper);
|
||||
}
|
||||
|
||||
@@ -380,7 +373,7 @@ Module.register("calendar", {
|
||||
// Create fade effect.
|
||||
if (e >= startFade) {
|
||||
currentFadeStep = e - startFade;
|
||||
eventWrapper.style.opacity = 1 - (1 / fadeSteps * currentFadeStep);
|
||||
eventWrapper.style.opacity = 1 - (1 / fadeSteps) * currentFadeStep;
|
||||
}
|
||||
|
||||
if (this.config.showLocation) {
|
||||
@@ -403,7 +396,7 @@ Module.register("calendar", {
|
||||
|
||||
if (e >= startFade) {
|
||||
currentFadeStep = e - startFade;
|
||||
locationRow.style.opacity = 1 - (1 / fadeSteps * currentFadeStep);
|
||||
locationRow.style.opacity = 1 - (1 / fadeSteps) * currentFadeStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -420,20 +413,17 @@ Module.register("calendar", {
|
||||
* @param {number} timeFormat Specifies either 12 or 24 hour time format
|
||||
* @returns {moment.LocaleSpecification}
|
||||
*/
|
||||
getLocaleSpecification: function(timeFormat) {
|
||||
getLocaleSpecification: function (timeFormat) {
|
||||
switch (timeFormat) {
|
||||
case 12: {
|
||||
return { longDateFormat: {LT: "h:mm A"} };
|
||||
break;
|
||||
}
|
||||
case 24: {
|
||||
return { longDateFormat: {LT: "HH:mm"} };
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return { longDateFormat: {LT: moment.localeData().longDateFormat("LT")} };
|
||||
break;
|
||||
}
|
||||
case 12: {
|
||||
return { longDateFormat: { LT: "h:mm A" } };
|
||||
}
|
||||
case 24: {
|
||||
return { longDateFormat: { LT: "HH:mm" } };
|
||||
}
|
||||
default: {
|
||||
return { longDateFormat: { LT: moment.localeData().longDateFormat("LT") } };
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -469,37 +459,37 @@ Module.register("calendar", {
|
||||
var calendar = this.calendarData[c];
|
||||
for (var e in calendar) {
|
||||
var event = JSON.parse(JSON.stringify(calendar[e])); // clone object
|
||||
if(event.endDate < now) {
|
||||
if (event.endDate < now) {
|
||||
continue;
|
||||
}
|
||||
if(this.config.hidePrivate) {
|
||||
if(event.class === "PRIVATE") {
|
||||
// do not add the current event, skip it
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(this.config.hideOngoing) {
|
||||
if(event.startDate < now) {
|
||||
if (this.config.hidePrivate) {
|
||||
if (event.class === "PRIVATE") {
|
||||
// do not add the current event, skip it
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(this.listContainsEvent(events,event)){
|
||||
if (this.config.hideOngoing) {
|
||||
if (event.startDate < now) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (this.listContainsEvent(events, event)) {
|
||||
continue;
|
||||
}
|
||||
event.url = c;
|
||||
event.today = event.startDate >= today && event.startDate < (today + 24 * 60 * 60 * 1000);
|
||||
event.today = event.startDate >= today && event.startDate < today + 24 * 60 * 60 * 1000;
|
||||
|
||||
/* if sliceMultiDayEvents is set to true, multiday events (events exceeding at least one midnight) are sliced into days,
|
||||
* otherwise, esp. in dateheaders mode it is not clear how long these events are.
|
||||
*/
|
||||
var maxCount = Math.ceil(((event.endDate - 1) - moment(event.startDate, "x").endOf("day").format("x"))/(1000*60*60*24)) + 1;
|
||||
* otherwise, esp. in dateheaders mode it is not clear how long these events are.
|
||||
*/
|
||||
var maxCount = Math.ceil((event.endDate - 1 - moment(event.startDate, "x").endOf("day").format("x")) / (1000 * 60 * 60 * 24)) + 1;
|
||||
if (this.config.sliceMultiDayEvents && maxCount > 1) {
|
||||
var splitEvents = [];
|
||||
var midnight = moment(event.startDate, "x").clone().startOf("day").add(1, "day").format("x");
|
||||
var count = 1;
|
||||
while (event.endDate > midnight) {
|
||||
var thisEvent = JSON.parse(JSON.stringify(event)); // clone object
|
||||
thisEvent.today = thisEvent.startDate >= today && thisEvent.startDate < (today + 24 * 60 * 60 * 1000);
|
||||
thisEvent.today = thisEvent.startDate >= today && thisEvent.startDate < today + 24 * 60 * 60 * 1000;
|
||||
thisEvent.endDate = midnight;
|
||||
thisEvent.title += " (" + count + "/" + maxCount + ")";
|
||||
splitEvents.push(thisEvent);
|
||||
@@ -509,11 +499,11 @@ Module.register("calendar", {
|
||||
midnight = moment(midnight, "x").add(1, "day").format("x"); // next day
|
||||
}
|
||||
// Last day
|
||||
event.title += " ("+count+"/"+maxCount+")";
|
||||
event.title += " (" + count + "/" + maxCount + ")";
|
||||
splitEvents.push(event);
|
||||
|
||||
for (event of splitEvents) {
|
||||
if ((event.endDate > now) && (event.endDate <= future)) {
|
||||
if (event.endDate > now && event.endDate <= future) {
|
||||
events.push(event);
|
||||
}
|
||||
}
|
||||
@@ -529,9 +519,9 @@ Module.register("calendar", {
|
||||
return events.slice(0, this.config.maximumEntries);
|
||||
},
|
||||
|
||||
listContainsEvent: function(eventList, event){
|
||||
for(var evt of eventList){
|
||||
if(evt.title === event.title && parseInt(evt.startDate) === parseInt(event.startDate)){
|
||||
listContainsEvent: function (eventList, event) {
|
||||
for (var evt of eventList) {
|
||||
if (evt.title === event.title && parseInt(evt.startDate) === parseInt(event.startDate)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -554,7 +544,7 @@ Module.register("calendar", {
|
||||
titleClass: calendarConfig.titleClass,
|
||||
timeClass: calendarConfig.timeClass,
|
||||
auth: auth,
|
||||
broadcastPastEvents: calendarConfig.broadcastPastEvents || this.config.broadcastPastEvents,
|
||||
broadcastPastEvents: calendarConfig.broadcastPastEvents || this.config.broadcastPastEvents
|
||||
});
|
||||
},
|
||||
|
||||
@@ -681,8 +671,9 @@ Module.register("calendar", {
|
||||
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
var word = words[i];
|
||||
if (currentLine.length + word.length < (typeof maxLength === "number" ? maxLength : 25) - 1) { // max - 1 to account for a space
|
||||
currentLine += (word + " ");
|
||||
if (currentLine.length + word.length < (typeof maxLength === "number" ? maxLength : 25) - 1) {
|
||||
// max - 1 to account for a space
|
||||
currentLine += word + " ";
|
||||
} else {
|
||||
line++;
|
||||
if (line > maxTitleLines - 1) {
|
||||
@@ -693,9 +684,9 @@ Module.register("calendar", {
|
||||
}
|
||||
|
||||
if (currentLine.length > 0) {
|
||||
temp += (currentLine + "<br>" + word + " ");
|
||||
temp += currentLine + "<br>" + word + " ";
|
||||
} else {
|
||||
temp += (word + "<br>");
|
||||
temp += word + "<br>";
|
||||
}
|
||||
currentLine = "";
|
||||
}
|
||||
@@ -734,8 +725,8 @@ Module.register("calendar", {
|
||||
|
||||
var regParts = needle.match(/^\/(.+)\/([gim]*)$/);
|
||||
if (regParts) {
|
||||
// the parsed pattern is a regexp.
|
||||
needle = new RegExp(regParts[1], regParts[2]);
|
||||
// the parsed pattern is a regexp.
|
||||
needle = new RegExp(regParts[1], regParts[2]);
|
||||
}
|
||||
|
||||
title = title.replace(needle, replacement);
|
||||
@@ -763,11 +754,10 @@ Module.register("calendar", {
|
||||
}
|
||||
}
|
||||
|
||||
eventList.sort(function(a,b) {
|
||||
eventList.sort(function (a, b) {
|
||||
return a.startDate - b.startDate;
|
||||
});
|
||||
|
||||
this.sendNotification("CALENDAR_EVENTS", eventList);
|
||||
|
||||
}
|
||||
});
|
||||
|
@@ -1,51 +1,49 @@
|
||||
/* Magic Mirror
|
||||
* Node Helper: Calendar - CalendarFetcher
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
var ical = require("./vendor/ical.js");
|
||||
var moment = require("moment");
|
||||
const ical = require("./vendor/ical.js");
|
||||
const moment = require("moment");
|
||||
|
||||
var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, includePastEvents) {
|
||||
var CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, includePastEvents) {
|
||||
var self = this;
|
||||
|
||||
var reloadTimer = null;
|
||||
var events = [];
|
||||
|
||||
var fetchFailedCallback = function() {};
|
||||
var eventsReceivedCallback = function() {};
|
||||
var fetchFailedCallback = function () {};
|
||||
var eventsReceivedCallback = function () {};
|
||||
|
||||
/* fetchCalendar()
|
||||
* Initiates calendar fetch.
|
||||
*/
|
||||
var fetchCalendar = function() {
|
||||
|
||||
var fetchCalendar = function () {
|
||||
clearTimeout(reloadTimer);
|
||||
reloadTimer = null;
|
||||
|
||||
nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
|
||||
var nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
|
||||
var opts = {
|
||||
headers: {
|
||||
"User-Agent": "Mozilla/5.0 (Node.js "+ nodeVersion + ") MagicMirror/" + global.version + " (https://github.com/MichMich/MagicMirror/)"
|
||||
"User-Agent": "Mozilla/5.0 (Node.js " + nodeVersion + ") MagicMirror/" + global.version + " (https://github.com/MichMich/MagicMirror/)"
|
||||
},
|
||||
gzip: true
|
||||
};
|
||||
|
||||
if (auth) {
|
||||
if(auth.method === "bearer"){
|
||||
if (auth.method === "bearer") {
|
||||
opts.auth = {
|
||||
bearer: auth.pass
|
||||
};
|
||||
|
||||
} else {
|
||||
opts.auth = {
|
||||
user: auth.user,
|
||||
pass: auth.pass
|
||||
};
|
||||
|
||||
if(auth.method === "digest"){
|
||||
if (auth.method === "digest") {
|
||||
opts.auth.sendImmediately = false;
|
||||
} else {
|
||||
opts.auth.sendImmediately = true;
|
||||
@@ -53,7 +51,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
}
|
||||
}
|
||||
|
||||
ical.fromURL(url, opts, function(err, data) {
|
||||
ical.fromURL(url, opts, function (err, data) {
|
||||
if (err) {
|
||||
fetchFailedCallback(self, err);
|
||||
scheduleTimer();
|
||||
@@ -61,28 +59,29 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
}
|
||||
|
||||
// console.log(data);
|
||||
newEvents = [];
|
||||
var newEvents = [];
|
||||
|
||||
// limitFunction doesn't do much limiting, see comment re: the dates array in rrule section below as to why we need to do the filtering ourselves
|
||||
var limitFunction = function(date, i) {return true;};
|
||||
var limitFunction = function (date, i) {
|
||||
return true;
|
||||
};
|
||||
|
||||
var eventDate = function(event, time) {
|
||||
return (event[time].length === 8) ? moment(event[time], "YYYYMMDD") : moment(new Date(event[time]));
|
||||
var eventDate = function (event, time) {
|
||||
return event[time].length === 8 ? moment(event[time], "YYYYMMDD") : moment(new Date(event[time]));
|
||||
};
|
||||
|
||||
for (var e in data) {
|
||||
var event = data[e];
|
||||
var now = new Date();
|
||||
var today = moment().startOf("day").toDate();
|
||||
var future = moment().startOf("day").add(maximumNumberOfDays, "days").subtract(1,"seconds").toDate(); // Subtract 1 second so that events that start on the middle of the night will not repeat.
|
||||
var future = moment().startOf("day").add(maximumNumberOfDays, "days").subtract(1, "seconds").toDate(); // Subtract 1 second so that events that start on the middle of the night will not repeat.
|
||||
var past = today;
|
||||
|
||||
if (includePastEvents) {
|
||||
past = moment().startOf("day").subtract(maximumNumberOfDays, "days").toDate();
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
// Ugly fix to solve the facebook birthday issue.
|
||||
// FIXME: Ugly fix to solve the facebook birthday issue.
|
||||
// Otherwise, the recurring events only show the birthday for next year.
|
||||
var isFacebookBirthday = false;
|
||||
if (typeof event.uid !== "undefined") {
|
||||
@@ -92,13 +91,12 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
}
|
||||
|
||||
if (event.type === "VEVENT") {
|
||||
|
||||
var startDate = eventDate(event, "start");
|
||||
var endDate;
|
||||
if (typeof event.end !== "undefined") {
|
||||
endDate = eventDate(event, "end");
|
||||
} else if(typeof event.duration !== "undefined") {
|
||||
dur=moment.duration(event.duration);
|
||||
} else if (typeof event.duration !== "undefined") {
|
||||
var dur = moment.duration(event.duration);
|
||||
endDate = startDate.clone().add(dur);
|
||||
} else {
|
||||
if (!isFacebookBirthday) {
|
||||
@@ -176,8 +174,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
var addedEvents = 0;
|
||||
|
||||
// can cause problems with e.g. birthdays before 1900
|
||||
if(rule.options && rule.origOptions && rule.origOptions.dtstart && rule.origOptions.dtstart.getFullYear() < 1900 ||
|
||||
rule.options && rule.options.dtstart && rule.options.dtstart.getFullYear() < 1900){
|
||||
if ((rule.options && rule.origOptions && rule.origOptions.dtstart && rule.origOptions.dtstart.getFullYear() < 1900) || (rule.options && rule.options.dtstart && rule.options.dtstart.getFullYear() < 1900)) {
|
||||
rule.origOptions.dtstart.setYear(1900);
|
||||
rule.options.dtstart.setYear(1900);
|
||||
}
|
||||
@@ -188,7 +185,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
var pastLocal = moment(past).subtract(past.getTimezoneOffset(), "minutes").toDate();
|
||||
var futureLocal = moment(future).subtract(future.getTimezoneOffset(), "minutes").toDate();
|
||||
var datesLocal = rule.between(pastLocal, futureLocal, true, limitFunction);
|
||||
var dates = datesLocal.map(function(dateLocal) {
|
||||
var dates = datesLocal.map(function (dateLocal) {
|
||||
var date = moment(dateLocal).add(dateLocal.getTimezoneOffset(), "minutes").toDate();
|
||||
return date;
|
||||
});
|
||||
@@ -200,17 +197,14 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
// because the logic below will filter out any recurrences that don"t actually belong within
|
||||
// our display range.
|
||||
// Would be great if there was a better way to handle this.
|
||||
if (event.recurrences != undefined)
|
||||
{
|
||||
if (event.recurrences !== undefined) {
|
||||
var pastMoment = moment(past);
|
||||
var futureMoment = moment(future);
|
||||
|
||||
for (var r in event.recurrences)
|
||||
{
|
||||
for (var r in event.recurrences) {
|
||||
// Only add dates that weren't already in the range we added from the rrule so that
|
||||
// we don"t double-add those events.
|
||||
if (moment(new Date(r)).isBetween(pastMoment, futureMoment) != true)
|
||||
{
|
||||
if (moment(new Date(r)).isBetween(pastMoment, futureMoment) !== true) {
|
||||
dates.push(new Date(r));
|
||||
}
|
||||
}
|
||||
@@ -222,7 +216,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
// ical.js started returning recurrences and exdates as ISOStrings without time information.
|
||||
// .toISOString().substring(0,10) is the method they use to calculate keys, so we'll do the same
|
||||
// (see https://github.com/peterbraden/ical.js/pull/84 )
|
||||
var dateKey = date.toISOString().substring(0,10);
|
||||
var dateKey = date.toISOString().substring(0, 10);
|
||||
var curEvent = event;
|
||||
var showRecurrence = true;
|
||||
|
||||
@@ -235,22 +229,20 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
startDate = moment(date);
|
||||
|
||||
// For each date that we"re checking, it"s possible that there is a recurrence override for that one day.
|
||||
if ((curEvent.recurrences != undefined) && (curEvent.recurrences[dateKey] != undefined))
|
||||
{
|
||||
if (curEvent.recurrences !== undefined && curEvent.recurrences[dateKey] !== undefined) {
|
||||
// We found an override, so for this recurrence, use a potentially different title, start date, and duration.
|
||||
curEvent = curEvent.recurrences[dateKey];
|
||||
startDate = moment(curEvent.start);
|
||||
duration = parseInt(moment(curEvent.end).format("x")) - parseInt(startDate.format("x"));
|
||||
}
|
||||
// If there"s no recurrence override, check for an exception date. Exception dates represent exceptions to the rule.
|
||||
else if ((curEvent.exdate != undefined) && (curEvent.exdate[dateKey] != undefined))
|
||||
{
|
||||
else if (curEvent.exdate !== undefined && curEvent.exdate[dateKey] !== undefined) {
|
||||
// This date is an exception date, which means we should skip it in the recurrence pattern.
|
||||
showRecurrence = false;
|
||||
}
|
||||
|
||||
endDate = moment(parseInt(startDate.format("x")) + duration, "x");
|
||||
if (startDate.format("x") == endDate.format("x")) {
|
||||
if (startDate.format("x") === endDate.format("x")) {
|
||||
endDate = endDate.endOf("day");
|
||||
}
|
||||
|
||||
@@ -266,7 +258,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
showRecurrence = false;
|
||||
}
|
||||
|
||||
if ((showRecurrence === true) && (addedEvents < maximumEntries)) {
|
||||
if (showRecurrence === true && addedEvents < maximumEntries) {
|
||||
addedEvents++;
|
||||
newEvents.push({
|
||||
title: recurrenceTitle,
|
||||
@@ -285,7 +277,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
} else {
|
||||
// console.log("Single event ...");
|
||||
// Single event.
|
||||
var fullDayEvent = (isFacebookBirthday) ? true : isFullDayEvent(event);
|
||||
var fullDayEvent = isFacebookBirthday ? true : isFullDayEvent(event);
|
||||
|
||||
if (includePastEvents) {
|
||||
if (endDate < past) {
|
||||
@@ -330,12 +322,11 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
geo: geo,
|
||||
description: description
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newEvents.sort(function(a, b) {
|
||||
newEvents.sort(function (a, b) {
|
||||
return a.startDate - b.startDate;
|
||||
});
|
||||
|
||||
@@ -351,10 +342,10 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
/* scheduleTimer()
|
||||
* Schedule the timer for the next update.
|
||||
*/
|
||||
var scheduleTimer = function() {
|
||||
var scheduleTimer = function () {
|
||||
//console.log('Schedule update timer.');
|
||||
clearTimeout(reloadTimer);
|
||||
reloadTimer = setTimeout(function() {
|
||||
reloadTimer = setTimeout(function () {
|
||||
fetchCalendar();
|
||||
}, reloadInterval);
|
||||
};
|
||||
@@ -366,7 +357,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
*
|
||||
* return bool - The event is a fullday event.
|
||||
*/
|
||||
var isFullDayEvent = function(event) {
|
||||
var isFullDayEvent = function (event) {
|
||||
if (event.start.length === 8 || event.start.dateOnly) {
|
||||
return true;
|
||||
}
|
||||
@@ -374,7 +365,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
var start = event.start || 0;
|
||||
var startDate = new Date(start);
|
||||
var end = event.end || 0;
|
||||
if (((end - start) % (24 * 60 * 60 * 1000)) === 0 && startDate.getHours() === 0 && startDate.getMinutes() === 0) {
|
||||
if ((end - start) % (24 * 60 * 60 * 1000) === 0 && startDate.getHours() === 0 && startDate.getMinutes() === 0) {
|
||||
// Is 24 hours, and starts on the middle of the night.
|
||||
return true;
|
||||
}
|
||||
@@ -391,7 +382,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
*
|
||||
* return bool - The event should be filtered out
|
||||
*/
|
||||
var timeFilterApplies = function(now, endDate, filter) {
|
||||
var timeFilterApplies = function (now, endDate, filter) {
|
||||
if (filter) {
|
||||
var until = filter.split(" "),
|
||||
value = parseInt(until[0]),
|
||||
@@ -405,16 +396,16 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
};
|
||||
|
||||
/* getTitleFromEvent(event)
|
||||
* Gets the title from the event.
|
||||
*
|
||||
* argument event object - The event object to check.
|
||||
*
|
||||
* return string - The title of the event, or "Event" if no title is found.
|
||||
*/
|
||||
* Gets the title from the event.
|
||||
*
|
||||
* argument event object - The event object to check.
|
||||
*
|
||||
* return string - The title of the event, or "Event" if no title is found.
|
||||
*/
|
||||
var getTitleFromEvent = function (event) {
|
||||
var title = "Event";
|
||||
if (event.summary) {
|
||||
title = (typeof event.summary.val !== "undefined") ? event.summary.val : event.summary;
|
||||
title = typeof event.summary.val !== "undefined" ? event.summary.val : event.summary;
|
||||
} else if (event.description) {
|
||||
title = event.description;
|
||||
}
|
||||
@@ -443,14 +434,14 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
/* startFetch()
|
||||
* Initiate fetchCalendar();
|
||||
*/
|
||||
this.startFetch = function() {
|
||||
this.startFetch = function () {
|
||||
fetchCalendar();
|
||||
};
|
||||
|
||||
/* broadcastItems()
|
||||
* Broadcast the existing events.
|
||||
*/
|
||||
this.broadcastEvents = function() {
|
||||
this.broadcastEvents = function () {
|
||||
//console.log('Broadcasting ' + events.length + ' events.');
|
||||
eventsReceivedCallback(self);
|
||||
};
|
||||
@@ -460,7 +451,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
*
|
||||
* argument callback function - The on success callback.
|
||||
*/
|
||||
this.onReceive = function(callback) {
|
||||
this.onReceive = function (callback) {
|
||||
eventsReceivedCallback = callback;
|
||||
};
|
||||
|
||||
@@ -469,7 +460,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
*
|
||||
* argument callback function - The on error callback.
|
||||
*/
|
||||
this.onError = function(callback) {
|
||||
this.onError = function (callback) {
|
||||
fetchFailedCallback = callback;
|
||||
};
|
||||
|
||||
@@ -478,7 +469,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
*
|
||||
* return string - The url of this fetcher.
|
||||
*/
|
||||
this.url = function() {
|
||||
this.url = function () {
|
||||
return url;
|
||||
};
|
||||
|
||||
@@ -487,7 +478,7 @@ var CalendarFetcher = function(url, reloadInterval, excludedEvents, maximumEntri
|
||||
*
|
||||
* return array - The current available events for this fetcher.
|
||||
*/
|
||||
this.events = function() {
|
||||
this.events = function () {
|
||||
return events;
|
||||
};
|
||||
};
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* use this script with `node debug.js` to test the fetcher without the need
|
||||
* of starting the MagicMirror core. Adjust the values below to your desire.
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
@@ -15,8 +15,6 @@ var maximumEntries = 10;
|
||||
var maximumNumberOfDays = 365;
|
||||
var user = "magicmirror";
|
||||
var pass = "MyStrongPass";
|
||||
var broadcastPastEvents = false;
|
||||
|
||||
var auth = {
|
||||
user: user,
|
||||
pass: pass
|
||||
@@ -24,14 +22,14 @@ var auth = {
|
||||
|
||||
console.log("Create fetcher ...");
|
||||
|
||||
fetcher = new CalendarFetcher(url, fetchInterval, [], maximumEntries, maximumNumberOfDays, auth);
|
||||
var fetcher = new CalendarFetcher(url, fetchInterval, [], maximumEntries, maximumNumberOfDays, auth);
|
||||
|
||||
fetcher.onReceive(function(fetcher) {
|
||||
fetcher.onReceive(function (fetcher) {
|
||||
console.log(fetcher.events());
|
||||
console.log("------------------------------------------------------------");
|
||||
});
|
||||
|
||||
fetcher.onError(function(fetcher, error) {
|
||||
fetcher.onError(function (fetcher, error) {
|
||||
console.log("Fetcher error:");
|
||||
console.log(error);
|
||||
});
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/* Magic Mirror
|
||||
* Node Helper: Calendar
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
@@ -11,17 +11,16 @@ var CalendarFetcher = require("./calendarfetcher.js");
|
||||
|
||||
module.exports = NodeHelper.create({
|
||||
// Override start method.
|
||||
start: function() {
|
||||
start: function () {
|
||||
var events = [];
|
||||
|
||||
this.fetchers = [];
|
||||
|
||||
console.log("Starting node helper for: " + this.name);
|
||||
|
||||
},
|
||||
|
||||
// Override socketNotificationReceived method.
|
||||
socketNotificationReceived: function(notification, payload) {
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
if (notification === "ADD_CALENDAR") {
|
||||
//console.log('ADD_CALENDAR: ');
|
||||
this.createFetcher(payload.url, payload.fetchInterval, payload.excludedEvents, payload.maximumEntries, payload.maximumNumberOfDays, payload.auth, payload.broadcastPastEvents);
|
||||
@@ -36,11 +35,11 @@ module.exports = NodeHelper.create({
|
||||
* attribute reloadInterval number - Reload interval in milliseconds.
|
||||
*/
|
||||
|
||||
createFetcher: function(url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents) {
|
||||
createFetcher: function (url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents) {
|
||||
var self = this;
|
||||
|
||||
if (!validUrl.isUri(url)) {
|
||||
self.sendSocketNotification("INCORRECT_URL", {url: url});
|
||||
self.sendSocketNotification("INCORRECT_URL", { url: url });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -49,7 +48,7 @@ module.exports = NodeHelper.create({
|
||||
console.log("Create new calendar fetcher for url: " + url + " - Interval: " + fetchInterval);
|
||||
fetcher = new CalendarFetcher(url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents);
|
||||
|
||||
fetcher.onReceive(function(fetcher) {
|
||||
fetcher.onReceive(function (fetcher) {
|
||||
//console.log('Broadcast events.');
|
||||
//console.log(fetcher.events());
|
||||
|
||||
@@ -59,7 +58,7 @@ module.exports = NodeHelper.create({
|
||||
});
|
||||
});
|
||||
|
||||
fetcher.onError(function(fetcher, error) {
|
||||
fetcher.onError(function (fetcher, error) {
|
||||
console.error("Calendar Error. Could not fetch calendar: ", fetcher.url(), error);
|
||||
self.sendSocketNotification("FETCH_ERROR", {
|
||||
url: fetcher.url(),
|
||||
|
@@ -1,4 +1,5 @@
|
||||
# Module: Clock
|
||||
|
||||
The `clock` module is one of the default modules of the MagicMirror.
|
||||
This module displays the current date and time. The information will be updated realtime.
|
||||
|
||||
|
@@ -1,11 +1,12 @@
|
||||
/* global Log, Module, moment, config */
|
||||
/* global SunCalc */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: Clock
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
Module.register("clock",{
|
||||
Module.register("clock", {
|
||||
// Module config defaults.
|
||||
defaults: {
|
||||
displayType: "digital", // options: digital, analog, both
|
||||
@@ -30,18 +31,18 @@ Module.register("clock",{
|
||||
showSunTimes: false,
|
||||
showMoonTimes: false,
|
||||
lat: 47.630539,
|
||||
lon: -122.344147,
|
||||
lon: -122.344147
|
||||
},
|
||||
// Define required scripts.
|
||||
getScripts: function() {
|
||||
getScripts: function () {
|
||||
return ["moment.js", "moment-timezone.js", "suncalc.js"];
|
||||
},
|
||||
// Define styles.
|
||||
getStyles: function() {
|
||||
getStyles: function () {
|
||||
return ["clock_styles.css"];
|
||||
},
|
||||
// Define start sequence.
|
||||
start: function() {
|
||||
start: function () {
|
||||
Log.info("Starting module: " + this.name);
|
||||
|
||||
// Schedule update interval.
|
||||
@@ -50,16 +51,16 @@ Module.register("clock",{
|
||||
self.minute = moment().minute();
|
||||
|
||||
//Calculate how many ms should pass until next update depending on if seconds is displayed or not
|
||||
var delayCalculator = function(reducedSeconds) {
|
||||
var delayCalculator = function (reducedSeconds) {
|
||||
if (self.config.displaySeconds) {
|
||||
return 1000 - moment().milliseconds();
|
||||
} else {
|
||||
return ((60 - reducedSeconds) * 1000) - moment().milliseconds();
|
||||
return (60 - reducedSeconds) * 1000 - moment().milliseconds();
|
||||
}
|
||||
};
|
||||
|
||||
//A recursive timeout function instead of interval to avoid drifting
|
||||
var notificationTimer = function() {
|
||||
var notificationTimer = function () {
|
||||
self.updateDom();
|
||||
|
||||
//If seconds is displayed CLOCK_SECOND-notification should be sent (but not when CLOCK_MINUTE-notification is sent)
|
||||
@@ -83,11 +84,9 @@ Module.register("clock",{
|
||||
|
||||
// Set locale.
|
||||
moment.locale(config.language);
|
||||
|
||||
},
|
||||
// Override dom generator.
|
||||
getDom: function() {
|
||||
|
||||
getDom: function () {
|
||||
var wrapper = document.createElement("div");
|
||||
|
||||
/************************************
|
||||
@@ -126,12 +125,12 @@ Module.register("clock",{
|
||||
}
|
||||
|
||||
if (this.config.clockBold === true) {
|
||||
timeString = now.format(hourSymbol + "[<span class=\"bold\">]mm[</span>]");
|
||||
timeString = now.format(hourSymbol + '[<span class="bold">]mm[</span>]');
|
||||
} else {
|
||||
timeString = now.format(hourSymbol + ":mm");
|
||||
}
|
||||
|
||||
if(this.config.showDate){
|
||||
if (this.config.showDate) {
|
||||
dateWrapper.innerHTML = now.format(this.config.dateFormat);
|
||||
}
|
||||
if (this.config.showWeek) {
|
||||
@@ -172,9 +171,18 @@ Module.register("clock",{
|
||||
}
|
||||
const untilNextEvent = moment.duration(moment(nextEvent).diff(now));
|
||||
const untilNextEventString = untilNextEvent.hours() + "h " + untilNextEvent.minutes() + "m";
|
||||
sunWrapper.innerHTML = "<span class=\"" + (isVisible ? "bright" : "") + "\"><i class=\"fa fa-sun-o\" aria-hidden=\"true\"></i> " + untilNextEventString + "</span>" +
|
||||
"<span><i class=\"fa fa-arrow-up\" aria-hidden=\"true\"></i>" + formatTime(this.config, sunTimes.sunrise) + "</span>" +
|
||||
"<span><i class=\"fa fa-arrow-down\" aria-hidden=\"true\"></i>" + formatTime(this.config, sunTimes.sunset) + "</span>";
|
||||
sunWrapper.innerHTML =
|
||||
'<span class="' +
|
||||
(isVisible ? "bright" : "") +
|
||||
'"><i class="fa fa-sun-o" aria-hidden="true"></i> ' +
|
||||
untilNextEventString +
|
||||
"</span>" +
|
||||
'<span><i class="fa fa-arrow-up" aria-hidden="true"></i>' +
|
||||
formatTime(this.config, sunTimes.sunrise) +
|
||||
"</span>" +
|
||||
'<span><i class="fa fa-arrow-down" aria-hidden="true"></i>' +
|
||||
formatTime(this.config, sunTimes.sunset) +
|
||||
"</span>";
|
||||
}
|
||||
if (this.config.showMoonTimes) {
|
||||
const moonIllumination = SunCalc.getMoonIllumination(now.toDate());
|
||||
@@ -189,24 +197,32 @@ Module.register("clock",{
|
||||
}
|
||||
const isVisible = now.isBetween(moonRise, moonSet) || moonTimes.alwaysUp === true;
|
||||
const illuminatedFractionString = Math.round(moonIllumination.fraction * 100) + "%";
|
||||
moonWrapper.innerHTML = "<span class=\"" + (isVisible ? "bright" : "") + "\"><i class=\"fa fa-moon-o\" aria-hidden=\"true\"></i> " + illuminatedFractionString + "</span>" +
|
||||
"<span><i class=\"fa fa-arrow-up\" aria-hidden=\"true\"></i> " + (moonRise ? formatTime(this.config, moonRise) : "...") + "</span>"+
|
||||
"<span><i class=\"fa fa-arrow-down\" aria-hidden=\"true\"></i> " + (moonSet ? formatTime(this.config, moonSet) : "...") + "</span>";
|
||||
moonWrapper.innerHTML =
|
||||
'<span class="' +
|
||||
(isVisible ? "bright" : "") +
|
||||
'"><i class="fa fa-moon-o" aria-hidden="true"></i> ' +
|
||||
illuminatedFractionString +
|
||||
"</span>" +
|
||||
'<span><i class="fa fa-arrow-up" aria-hidden="true"></i> ' +
|
||||
(moonRise ? formatTime(this.config, moonRise) : "...") +
|
||||
"</span>" +
|
||||
'<span><i class="fa fa-arrow-down" aria-hidden="true"></i> ' +
|
||||
(moonSet ? formatTime(this.config, moonSet) : "...") +
|
||||
"</span>";
|
||||
}
|
||||
|
||||
/****************************************************************
|
||||
* Create wrappers for ANALOG clock, only if specified in config
|
||||
*/
|
||||
|
||||
if (this.config.displayType !== "digital") {
|
||||
if (this.config.displayType !== "digital") {
|
||||
// If it isn't 'digital', then an 'analog' clock was also requested
|
||||
|
||||
// Calculate the degree offset for each hand of the clock
|
||||
var now = moment();
|
||||
if (this.config.timezone) {
|
||||
now.tz(this.config.timezone);
|
||||
}
|
||||
var second = now.seconds() * 6,
|
||||
var second = now.seconds() * 6,
|
||||
minute = now.minute() * 6 + second / 60,
|
||||
hour = ((now.hours() % 12) / 12) * 360 + 90 + minute / 12;
|
||||
|
||||
@@ -217,13 +233,12 @@ Module.register("clock",{
|
||||
clockCircle.style.height = this.config.analogSize;
|
||||
|
||||
if (this.config.analogFace !== "" && this.config.analogFace !== "simple" && this.config.analogFace !== "none") {
|
||||
clockCircle.style.background = "url("+ this.data.path + "faces/" + this.config.analogFace + ".svg)";
|
||||
clockCircle.style.background = "url(" + this.data.path + "faces/" + this.config.analogFace + ".svg)";
|
||||
clockCircle.style.backgroundSize = "100%";
|
||||
|
||||
// The following line solves issue: https://github.com/MichMich/MagicMirror/issues/611
|
||||
// clockCircle.style.border = "1px solid black";
|
||||
clockCircle.style.border = "rgba(0, 0, 0, 0.1)"; //Updated fix for Issue 611 where non-black backgrounds are used
|
||||
|
||||
} else if (this.config.analogFace !== "none") {
|
||||
clockCircle.style.border = "2px solid white";
|
||||
}
|
||||
@@ -289,11 +304,12 @@ Module.register("clock",{
|
||||
// Both clocks have been configured, check position
|
||||
var placement = this.config.analogPlacement;
|
||||
|
||||
analogWrapper = document.createElement("div");
|
||||
var analogWrapper = document.createElement("div");
|
||||
analogWrapper.id = "analog";
|
||||
analogWrapper.style.cssFloat = "none";
|
||||
analogWrapper.appendChild(clockCircle);
|
||||
digitalWrapper = document.createElement("div");
|
||||
|
||||
var digitalWrapper = document.createElement("div");
|
||||
digitalWrapper.id = "digital";
|
||||
digitalWrapper.style.cssFloat = "none";
|
||||
digitalWrapper.appendChild(dateWrapper);
|
||||
@@ -302,9 +318,9 @@ Module.register("clock",{
|
||||
digitalWrapper.appendChild(moonWrapper);
|
||||
digitalWrapper.appendChild(weekWrapper);
|
||||
|
||||
var appendClocks = function(condition, pos1, pos2) {
|
||||
var padding = [0,0,0,0];
|
||||
padding[(placement === condition) ? pos1 : pos2] = "20px";
|
||||
var appendClocks = function (condition, pos1, pos2) {
|
||||
var padding = [0, 0, 0, 0];
|
||||
padding[placement === condition ? pos1 : pos2] = "20px";
|
||||
analogWrapper.style.padding = padding.join(" ");
|
||||
if (placement === condition) {
|
||||
wrapper.appendChild(analogWrapper);
|
||||
|
@@ -1,78 +1,72 @@
|
||||
.clockCircle {
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
.clockFace {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.clockFace::after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
margin: -3px 0 0 -3px;
|
||||
background: white;
|
||||
border-radius: 3px;
|
||||
content: "";
|
||||
display: block;
|
||||
}
|
||||
|
||||
.clockHour {
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -2px 0 -2px -25%; /* numbers much match negative length & thickness */
|
||||
padding: 2px 0 2px 25%; /* indicator length & thickness */
|
||||
background: white;
|
||||
-webkit-transform-origin: 100% 50%;
|
||||
-ms-transform-origin: 100% 50%;
|
||||
transform-origin: 100% 50%;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
|
||||
.clockMinute {
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -35% -2px 0; /* numbers must match negative length & thickness */
|
||||
padding: 35% 2px 0; /* indicator length & thickness */
|
||||
background: white;
|
||||
-webkit-transform-origin: 50% 100%;
|
||||
-ms-transform-origin: 50% 100%;
|
||||
transform-origin: 50% 100%;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
|
||||
.clockSecond {
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -38% -1px 0 0; /* numbers must match negative length & thickness */
|
||||
padding: 38% 1px 0 0; /* indicator length & thickness */
|
||||
background: #888;
|
||||
-webkit-transform-origin: 50% 100%;
|
||||
-ms-transform-origin: 50% 100%;
|
||||
transform-origin: 50% 100%;
|
||||
}
|
||||
|
||||
.module.clock .sun,
|
||||
.module.clock .moon {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.module.clock .sun > *,
|
||||
.module.clock .moon > * {
|
||||
flex: 1;
|
||||
}
|
||||
.clockCircle {
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
.clockFace {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.clockFace::after {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
margin: -3px 0 0 -3px;
|
||||
background: white;
|
||||
border-radius: 3px;
|
||||
content: "";
|
||||
display: block;
|
||||
}
|
||||
|
||||
.clockHour {
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -2px 0 -2px -25%; /* numbers much match negative length & thickness */
|
||||
padding: 2px 0 2px 25%; /* indicator length & thickness */
|
||||
background: white;
|
||||
transform-origin: 100% 50%;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
|
||||
.clockMinute {
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -35% -2px 0; /* numbers must match negative length & thickness */
|
||||
padding: 35% 2px 0; /* indicator length & thickness */
|
||||
background: white;
|
||||
transform-origin: 50% 100%;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
|
||||
.clockSecond {
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -38% -1px 0 0; /* numbers must match negative length & thickness */
|
||||
padding: 38% 1px 0 0; /* indicator length & thickness */
|
||||
background: #888;
|
||||
transform-origin: 50% 100%;
|
||||
}
|
||||
|
||||
.module.clock .sun,
|
||||
.module.clock .moon {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.module.clock .sun > *,
|
||||
.module.clock .moon > * {
|
||||
flex: 1;
|
||||
}
|
||||
|
@@ -1 +1 @@
|
||||
<svg id="Hour_Markers_-_Singlets" data-name="Hour Markers - Singlets" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 250 250"><defs><style>.cls-1,.cls-2{fill:none;stroke:#fff;stroke-linecap:round;stroke-miterlimit:10;}.cls-2{stroke-width:0.5px;}</style></defs><title>face-001</title><line class="cls-1" x1="125" y1="1.25" x2="125" y2="16.23"/><line class="cls-1" x1="186.87" y1="17.83" x2="179.39" y2="30.8"/><line class="cls-1" x1="232.17" y1="63.12" x2="219.2" y2="70.61"/><line class="cls-1" x1="248.75" y1="125" x2="233.77" y2="125"/><line class="cls-1" x1="232.17" y1="186.87" x2="219.2" y2="179.39"/><line class="cls-1" x1="186.88" y1="232.17" x2="179.39" y2="219.2"/><line class="cls-1" x1="125" y1="248.75" x2="125" y2="233.77"/><line class="cls-1" x1="63.13" y1="232.17" x2="70.61" y2="219.2"/><line class="cls-1" x1="17.83" y1="186.88" x2="30.8" y2="179.39"/><line class="cls-1" x1="1.25" y1="125" x2="16.23" y2="125"/><line class="cls-1" x1="17.83" y1="63.13" x2="30.8" y2="70.61"/><line class="cls-1" x1="63.12" y1="17.83" x2="70.61" y2="30.8"/><line class="cls-2" x1="138.01" y1="1.25" x2="136.96" y2="11.23"/><line class="cls-2" x1="150.87" y1="3.29" x2="148.78" y2="13.11"/><line class="cls-2" x1="163.45" y1="6.66" x2="160.35" y2="16.21"/><line class="cls-2" x1="175.61" y1="11.33" x2="171.53" y2="20.5"/><line class="cls-2" x1="198.14" y1="24.33" x2="192.24" y2="32.45"/><line class="cls-2" x1="208.26" y1="32.53" x2="201.54" y2="39.99"/><line class="cls-2" x1="217.47" y1="41.74" x2="210.01" y2="48.46"/><line class="cls-2" x1="225.67" y1="51.86" x2="217.55" y2="57.76"/><line class="cls-2" x1="238.67" y1="74.39" x2="229.5" y2="78.47"/><line class="cls-2" x1="243.34" y1="86.55" x2="233.79" y2="89.65"/><line class="cls-2" x1="246.71" y1="99.13" x2="236.89" y2="101.22"/><line class="cls-2" x1="248.75" y1="111.99" x2="238.77" y2="113.04"/><line class="cls-2" x1="248.75" y1="138.01" x2="238.77" y2="136.96"/><line class="cls-2" x1="246.71" y1="150.87" x2="236.89" y2="148.78"/><line class="cls-2" x1="243.34" y1="163.45" x2="233.79" y2="160.35"/><line class="cls-2" x1="238.67" y1="175.61" x2="229.5" y2="171.53"/><line class="cls-2" x1="225.67" y1="198.14" x2="217.55" y2="192.24"/><line class="cls-2" x1="217.47" y1="208.26" x2="210.01" y2="201.54"/><line class="cls-2" x1="208.26" y1="217.47" x2="201.54" y2="210.01"/><line class="cls-2" x1="198.14" y1="225.67" x2="192.24" y2="217.55"/><line class="cls-2" x1="175.61" y1="238.67" x2="171.53" y2="229.5"/><line class="cls-2" x1="163.45" y1="243.34" x2="160.35" y2="233.79"/><line class="cls-2" x1="150.87" y1="246.71" x2="148.78" y2="236.89"/><line class="cls-2" x1="138.01" y1="248.75" x2="136.96" y2="238.77"/><line class="cls-2" x1="111.99" y1="248.75" x2="113.04" y2="238.77"/><line class="cls-2" x1="99.13" y1="246.71" x2="101.22" y2="236.89"/><line class="cls-2" x1="86.55" y1="243.34" x2="89.65" y2="233.79"/><line class="cls-2" x1="74.39" y1="238.67" x2="78.47" y2="229.5"/><line class="cls-2" x1="51.86" y1="225.67" x2="57.76" y2="217.55"/><line class="cls-2" x1="41.74" y1="217.47" x2="48.46" y2="210.01"/><line class="cls-2" x1="32.53" y1="208.26" x2="39.99" y2="201.54"/><line class="cls-2" x1="24.33" y1="198.14" x2="32.45" y2="192.24"/><line class="cls-2" x1="11.33" y1="175.61" x2="20.5" y2="171.53"/><line class="cls-2" x1="6.66" y1="163.45" x2="16.21" y2="160.35"/><line class="cls-2" x1="3.29" y1="150.87" x2="13.11" y2="148.78"/><line class="cls-2" x1="1.25" y1="138.01" x2="11.23" y2="136.96"/><line class="cls-2" x1="1.25" y1="111.99" x2="11.23" y2="113.04"/><line class="cls-2" x1="3.29" y1="99.13" x2="13.11" y2="101.22"/><line class="cls-2" x1="6.66" y1="86.55" x2="16.21" y2="89.65"/><line class="cls-2" x1="11.33" y1="74.39" x2="20.5" y2="78.47"/><line class="cls-2" x1="24.33" y1="51.86" x2="32.45" y2="57.76"/><line class="cls-2" x1="32.53" y1="41.74" x2="39.99" y2="48.46"/><line class="cls-2" x1="41.74" y1="32.53" x2="48.46" y2="39.99"/><line class="cls-2" x1="51.86" y1="24.33" x2="57.76" y2="32.45"/><line class="cls-2" x1="74.39" y1="11.33" x2="78.47" y2="20.5"/><line class="cls-2" x1="86.55" y1="6.66" x2="89.65" y2="16.21"/><line class="cls-2" x1="99.13" y1="3.29" x2="101.22" y2="13.11"/><line class="cls-2" x1="111.99" y1="1.25" x2="113.04" y2="11.23"/></svg>
|
||||
<svg id="Hour_Markers_-_Singlets" data-name="Hour Markers - Singlets" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 250 250"><defs><style>.cls-1,.cls-2{fill:none;stroke:#fff;stroke-linecap:round;stroke-miterlimit:10;}.cls-2{stroke-width:0.5px;}</style></defs><title>face-001</title><line class="cls-1" x1="125" y1="1.25" x2="125" y2="16.23"/><line class="cls-1" x1="186.87" y1="17.83" x2="179.39" y2="30.8"/><line class="cls-1" x1="232.17" y1="63.12" x2="219.2" y2="70.61"/><line class="cls-1" x1="248.75" y1="125" x2="233.77" y2="125"/><line class="cls-1" x1="232.17" y1="186.87" x2="219.2" y2="179.39"/><line class="cls-1" x1="186.88" y1="232.17" x2="179.39" y2="219.2"/><line class="cls-1" x1="125" y1="248.75" x2="125" y2="233.77"/><line class="cls-1" x1="63.13" y1="232.17" x2="70.61" y2="219.2"/><line class="cls-1" x1="17.83" y1="186.88" x2="30.8" y2="179.39"/><line class="cls-1" x1="1.25" y1="125" x2="16.23" y2="125"/><line class="cls-1" x1="17.83" y1="63.13" x2="30.8" y2="70.61"/><line class="cls-1" x1="63.12" y1="17.83" x2="70.61" y2="30.8"/><line class="cls-2" x1="138.01" y1="1.25" x2="136.96" y2="11.23"/><line class="cls-2" x1="150.87" y1="3.29" x2="148.78" y2="13.11"/><line class="cls-2" x1="163.45" y1="6.66" x2="160.35" y2="16.21"/><line class="cls-2" x1="175.61" y1="11.33" x2="171.53" y2="20.5"/><line class="cls-2" x1="198.14" y1="24.33" x2="192.24" y2="32.45"/><line class="cls-2" x1="208.26" y1="32.53" x2="201.54" y2="39.99"/><line class="cls-2" x1="217.47" y1="41.74" x2="210.01" y2="48.46"/><line class="cls-2" x1="225.67" y1="51.86" x2="217.55" y2="57.76"/><line class="cls-2" x1="238.67" y1="74.39" x2="229.5" y2="78.47"/><line class="cls-2" x1="243.34" y1="86.55" x2="233.79" y2="89.65"/><line class="cls-2" x1="246.71" y1="99.13" x2="236.89" y2="101.22"/><line class="cls-2" x1="248.75" y1="111.99" x2="238.77" y2="113.04"/><line class="cls-2" x1="248.75" y1="138.01" x2="238.77" y2="136.96"/><line class="cls-2" x1="246.71" y1="150.87" x2="236.89" y2="148.78"/><line class="cls-2" x1="243.34" y1="163.45" x2="233.79" y2="160.35"/><line class="cls-2" x1="238.67" y1="175.61" x2="229.5" y2="171.53"/><line class="cls-2" x1="225.67" y1="198.14" x2="217.55" y2="192.24"/><line class="cls-2" x1="217.47" y1="208.26" x2="210.01" y2="201.54"/><line class="cls-2" x1="208.26" y1="217.47" x2="201.54" y2="210.01"/><line class="cls-2" x1="198.14" y1="225.67" x2="192.24" y2="217.55"/><line class="cls-2" x1="175.61" y1="238.67" x2="171.53" y2="229.5"/><line class="cls-2" x1="163.45" y1="243.34" x2="160.35" y2="233.79"/><line class="cls-2" x1="150.87" y1="246.71" x2="148.78" y2="236.89"/><line class="cls-2" x1="138.01" y1="248.75" x2="136.96" y2="238.77"/><line class="cls-2" x1="111.99" y1="248.75" x2="113.04" y2="238.77"/><line class="cls-2" x1="99.13" y1="246.71" x2="101.22" y2="236.89"/><line class="cls-2" x1="86.55" y1="243.34" x2="89.65" y2="233.79"/><line class="cls-2" x1="74.39" y1="238.67" x2="78.47" y2="229.5"/><line class="cls-2" x1="51.86" y1="225.67" x2="57.76" y2="217.55"/><line class="cls-2" x1="41.74" y1="217.47" x2="48.46" y2="210.01"/><line class="cls-2" x1="32.53" y1="208.26" x2="39.99" y2="201.54"/><line class="cls-2" x1="24.33" y1="198.14" x2="32.45" y2="192.24"/><line class="cls-2" x1="11.33" y1="175.61" x2="20.5" y2="171.53"/><line class="cls-2" x1="6.66" y1="163.45" x2="16.21" y2="160.35"/><line class="cls-2" x1="3.29" y1="150.87" x2="13.11" y2="148.78"/><line class="cls-2" x1="1.25" y1="138.01" x2="11.23" y2="136.96"/><line class="cls-2" x1="1.25" y1="111.99" x2="11.23" y2="113.04"/><line class="cls-2" x1="3.29" y1="99.13" x2="13.11" y2="101.22"/><line class="cls-2" x1="6.66" y1="86.55" x2="16.21" y2="89.65"/><line class="cls-2" x1="11.33" y1="74.39" x2="20.5" y2="78.47"/><line class="cls-2" x1="24.33" y1="51.86" x2="32.45" y2="57.76"/><line class="cls-2" x1="32.53" y1="41.74" x2="39.99" y2="48.46"/><line class="cls-2" x1="41.74" y1="32.53" x2="48.46" y2="39.99"/><line class="cls-2" x1="51.86" y1="24.33" x2="57.76" y2="32.45"/><line class="cls-2" x1="74.39" y1="11.33" x2="78.47" y2="20.5"/><line class="cls-2" x1="86.55" y1="6.66" x2="89.65" y2="16.21"/><line class="cls-2" x1="99.13" y1="3.29" x2="101.22" y2="13.11"/><line class="cls-2" x1="111.99" y1="1.25" x2="113.04" y2="11.23"/></svg>
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
@@ -1 +1 @@
|
||||
<svg id="Hour_Markers_-_Doubles" data-name="Hour Markers - Doubles" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 250 250"><defs><style>.cls-1{fill:none;stroke:#fff;stroke-linecap:round;stroke-miterlimit:10;stroke-width:2.98px;}</style></defs><title>face-002</title><line class="cls-1" x1="122.01" y1="1.75" x2="122.01" y2="16.67"/><line class="cls-1" x1="186.62" y1="18.26" x2="179.17" y2="31.18"/><line class="cls-1" x1="231.74" y1="63.37" x2="218.82" y2="70.83"/><line class="cls-1" x1="248.25" y1="127.99" x2="233.33" y2="127.99"/><line class="cls-1" x1="231.74" y1="186.62" x2="218.82" y2="179.17"/><line class="cls-1" x1="186.63" y1="231.74" x2="179.17" y2="218.82"/><line class="cls-1" x1="127.99" y1="248.25" x2="127.99" y2="233.33"/><line class="cls-1" x1="63.38" y1="231.74" x2="70.83" y2="218.82"/><line class="cls-1" x1="18.26" y1="186.63" x2="31.18" y2="179.17"/><line class="cls-1" x1="1.75" y1="122.01" x2="16.67" y2="122.01"/><line class="cls-1" x1="18.26" y1="63.38" x2="31.18" y2="70.83"/><line class="cls-1" x1="63.37" y1="18.26" x2="70.83" y2="31.18"/><line class="cls-1" x1="127.99" y1="1.75" x2="127.99" y2="16.67"/><line class="cls-1" x1="248.25" y1="122.01" x2="233.33" y2="122.01"/><line class="cls-1" x1="122.01" y1="248.25" x2="122.01" y2="233.33"/><line class="cls-1" x1="1.75" y1="127.99" x2="16.67" y2="127.99"/></svg>
|
||||
<svg id="Hour_Markers_-_Doubles" data-name="Hour Markers - Doubles" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 250 250"><defs><style>.cls-1{fill:none;stroke:#fff;stroke-linecap:round;stroke-miterlimit:10;stroke-width:2.98px;}</style></defs><title>face-002</title><line class="cls-1" x1="122.01" y1="1.75" x2="122.01" y2="16.67"/><line class="cls-1" x1="186.62" y1="18.26" x2="179.17" y2="31.18"/><line class="cls-1" x1="231.74" y1="63.37" x2="218.82" y2="70.83"/><line class="cls-1" x1="248.25" y1="127.99" x2="233.33" y2="127.99"/><line class="cls-1" x1="231.74" y1="186.62" x2="218.82" y2="179.17"/><line class="cls-1" x1="186.63" y1="231.74" x2="179.17" y2="218.82"/><line class="cls-1" x1="127.99" y1="248.25" x2="127.99" y2="233.33"/><line class="cls-1" x1="63.38" y1="231.74" x2="70.83" y2="218.82"/><line class="cls-1" x1="18.26" y1="186.63" x2="31.18" y2="179.17"/><line class="cls-1" x1="1.75" y1="122.01" x2="16.67" y2="122.01"/><line class="cls-1" x1="18.26" y1="63.38" x2="31.18" y2="70.83"/><line class="cls-1" x1="63.37" y1="18.26" x2="70.83" y2="31.18"/><line class="cls-1" x1="127.99" y1="1.75" x2="127.99" y2="16.67"/><line class="cls-1" x1="248.25" y1="122.01" x2="233.33" y2="122.01"/><line class="cls-1" x1="122.01" y1="248.25" x2="122.01" y2="233.33"/><line class="cls-1" x1="1.75" y1="127.99" x2="16.67" y2="127.99"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
@@ -1,4 +1,5 @@
|
||||
# Module: Compliments
|
||||
|
||||
The `compliments` module is one of the default modules of the MagicMirror.
|
||||
This module displays a random compliment.
|
||||
|
||||
|
@@ -1,37 +1,18 @@
|
||||
/* global Log, Module, moment */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: Compliments
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
Module.register("compliments", {
|
||||
|
||||
// Module config defaults.
|
||||
defaults: {
|
||||
compliments: {
|
||||
anytime: [
|
||||
"Hey there sexy!"
|
||||
],
|
||||
morning: [
|
||||
"Good morning, handsome!",
|
||||
"Enjoy your day!",
|
||||
"How was your sleep?"
|
||||
],
|
||||
afternoon: [
|
||||
"Hello, beauty!",
|
||||
"You look sexy!",
|
||||
"Looking good today!"
|
||||
],
|
||||
evening: [
|
||||
"Wow, you look hot!",
|
||||
"You look nice!",
|
||||
"Hi, sexy!"
|
||||
],
|
||||
"....-01-01": [
|
||||
"Happy new year!"
|
||||
]
|
||||
anytime: ["Hey there sexy!"],
|
||||
morning: ["Good morning, handsome!", "Enjoy your day!", "How was your sleep?"],
|
||||
afternoon: ["Hello, beauty!", "You look sexy!", "Looking good today!"],
|
||||
evening: ["Wow, you look hot!", "You look nice!", "Hi, sexy!"],
|
||||
"....-01-01": ["Happy new year!"]
|
||||
},
|
||||
updateInterval: 30000,
|
||||
remoteFile: null,
|
||||
@@ -41,33 +22,46 @@ Module.register("compliments", {
|
||||
afternoonStartTime: 12,
|
||||
afternoonEndTime: 17,
|
||||
random: true,
|
||||
mockDate: null
|
||||
mockDate: null,
|
||||
advice: false
|
||||
},
|
||||
lastIndexUsed:-1,
|
||||
lastIndexUsed: -1,
|
||||
// Set currentweather from module
|
||||
currentWeatherType: "",
|
||||
|
||||
// Define required scripts.
|
||||
getScripts: function() {
|
||||
getScripts: function () {
|
||||
return ["moment.js"];
|
||||
},
|
||||
|
||||
// Define start sequence.
|
||||
start: function() {
|
||||
start: function () {
|
||||
Log.info("Starting module: " + this.name);
|
||||
|
||||
this.lastComplimentIndex = -1;
|
||||
|
||||
var self = this;
|
||||
if (this.config.remoteFile !== null) {
|
||||
this.complimentFile(function(response) {
|
||||
this.complimentFile(function (response) {
|
||||
self.config.compliments = JSON.parse(response);
|
||||
self.updateDom();
|
||||
});
|
||||
} else if (this.config.advice) {
|
||||
var xobj = new XMLHttpRequest();
|
||||
xobj.overrideMimeType("application/json");
|
||||
xobj.open("GET", "https://api.adviceslip.com/advice", true);
|
||||
xobj.onreadystatechange = function () {
|
||||
if (xobj.readyState === 4 && xobj.status === 200) {
|
||||
const adviceResp = JSON.parse(xobj.responseText);
|
||||
self.config.compliments = adviceResp.slip.advice;
|
||||
self.updateDom();
|
||||
}
|
||||
};
|
||||
xobj.send(null);
|
||||
}
|
||||
|
||||
// Schedule update timer.
|
||||
setInterval(function() {
|
||||
setInterval(function () {
|
||||
self.updateDom(self.config.fadeSpeed);
|
||||
}, this.config.updateInterval);
|
||||
},
|
||||
@@ -79,12 +73,12 @@ Module.register("compliments", {
|
||||
*
|
||||
* return Number - Random index.
|
||||
*/
|
||||
randomIndex: function(compliments) {
|
||||
randomIndex: function (compliments) {
|
||||
if (compliments.length === 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var generate = function() {
|
||||
var generate = function () {
|
||||
return Math.floor(Math.random() * compliments.length);
|
||||
};
|
||||
|
||||
@@ -104,7 +98,7 @@ Module.register("compliments", {
|
||||
*
|
||||
* return compliments Array<String> - Array with compliments for the time of the day.
|
||||
*/
|
||||
complimentArray: function() {
|
||||
complimentArray: function () {
|
||||
var hour = moment().hour();
|
||||
var date = this.config.mockDate ? this.config.mockDate : moment().format("YYYY-MM-DD");
|
||||
var compliments;
|
||||
@@ -113,7 +107,7 @@ Module.register("compliments", {
|
||||
compliments = this.config.compliments.morning.slice(0);
|
||||
} else if (hour >= this.config.afternoonStartTime && hour < this.config.afternoonEndTime && this.config.compliments.hasOwnProperty("afternoon")) {
|
||||
compliments = this.config.compliments.afternoon.slice(0);
|
||||
} else if(this.config.compliments.hasOwnProperty("evening")) {
|
||||
} else if (this.config.compliments.hasOwnProperty("evening")) {
|
||||
compliments = this.config.compliments.evening.slice(0);
|
||||
}
|
||||
|
||||
@@ -127,7 +121,7 @@ Module.register("compliments", {
|
||||
|
||||
compliments.push.apply(compliments, this.config.compliments.anytime);
|
||||
|
||||
for (entry in this.config.compliments) {
|
||||
for (var entry in this.config.compliments) {
|
||||
if (new RegExp(entry).test(date)) {
|
||||
compliments.push.apply(compliments, this.config.compliments[entry]);
|
||||
}
|
||||
@@ -139,13 +133,13 @@ Module.register("compliments", {
|
||||
/* complimentFile(callback)
|
||||
* Retrieve a file from the local filesystem
|
||||
*/
|
||||
complimentFile: function(callback) {
|
||||
complimentFile: function (callback) {
|
||||
var xobj = new XMLHttpRequest(),
|
||||
isRemote = this.config.remoteFile.indexOf("http://") === 0 || this.config.remoteFile.indexOf("https://") === 0,
|
||||
path = isRemote ? this.config.remoteFile : this.file(this.config.remoteFile);
|
||||
xobj.overrideMimeType("application/json");
|
||||
xobj.open("GET", path, true);
|
||||
xobj.onreadystatechange = function() {
|
||||
xobj.onreadystatechange = function () {
|
||||
if (xobj.readyState === 4 && xobj.status === 200) {
|
||||
callback(xobj.responseText);
|
||||
}
|
||||
@@ -158,27 +152,26 @@ Module.register("compliments", {
|
||||
*
|
||||
* return compliment string - A compliment.
|
||||
*/
|
||||
randomCompliment: function() {
|
||||
randomCompliment: function () {
|
||||
// get the current time of day compliments list
|
||||
var compliments = this.complimentArray();
|
||||
// variable for index to next message to display
|
||||
let index = 0;
|
||||
// are we randomizing
|
||||
if(this.config.random){
|
||||
if (this.config.random) {
|
||||
// yes
|
||||
index = this.randomIndex(compliments);
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
// no, sequential
|
||||
// if doing sequential, don't fall off the end
|
||||
index = (this.lastIndexUsed >= (compliments.length-1))?0: ++this.lastIndexUsed;
|
||||
index = this.lastIndexUsed >= compliments.length - 1 ? 0 : ++this.lastIndexUsed;
|
||||
}
|
||||
|
||||
return compliments[index] || "";
|
||||
},
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function() {
|
||||
getDom: function () {
|
||||
var wrapper = document.createElement("div");
|
||||
wrapper.className = this.config.classes ? this.config.classes : "thin xlarge bright pre-line";
|
||||
// get the compliment text
|
||||
@@ -188,7 +181,7 @@ Module.register("compliments", {
|
||||
// create a span to hold it all
|
||||
var compliment = document.createElement("span");
|
||||
// process all the parts of the compliment text
|
||||
for (part of parts){
|
||||
for (var part of parts) {
|
||||
// create a text element for each part
|
||||
compliment.appendChild(document.createTextNode(part));
|
||||
// add a break `
|
||||
@@ -202,7 +195,7 @@ Module.register("compliments", {
|
||||
},
|
||||
|
||||
// From data currentweather set weather type
|
||||
setCurrentWeatherType: function(data) {
|
||||
setCurrentWeatherType: function (data) {
|
||||
var weatherIconTable = {
|
||||
"01d": "day_sunny",
|
||||
"02d": "day_cloudy",
|
||||
@@ -227,10 +220,9 @@ Module.register("compliments", {
|
||||
},
|
||||
|
||||
// Override notification handler.
|
||||
notificationReceived: function(notification, payload, sender) {
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
if (notification === "CURRENTWEATHER_DATA") {
|
||||
this.setCurrentWeatherType(payload.data);
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
});
|
||||
|
@@ -1,4 +1,5 @@
|
||||
# Module: Current Weather
|
||||
|
||||
The `currentweather` module is one of the default modules of the MagicMirror.
|
||||
This module displays the current weather, including the windspeed, the sunset or sunrise time, the temperature and an icon to display the current conditions.
|
||||
|
||||
|
@@ -3,8 +3,6 @@
|
||||
font-size: 75%;
|
||||
line-height: 65px;
|
||||
display: inline-block;
|
||||
-ms-transform: translate(0, -3px); /* IE 9 */
|
||||
-webkit-transform: translate(0, -3px); /* Safari */
|
||||
transform: translate(0, -3px);
|
||||
}
|
||||
|
||||
|
@@ -1,14 +1,10 @@
|
||||
/* global Module */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: CurrentWeather
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
Module.register("currentweather",{
|
||||
|
||||
Module.register("currentweather", {
|
||||
// Default module config.
|
||||
defaults: {
|
||||
location: false,
|
||||
@@ -23,7 +19,6 @@ Module.register("currentweather",{
|
||||
showWindDirection: true,
|
||||
showWindDirectionAsArrow: false,
|
||||
useBeaufort: true,
|
||||
appendLocationNameToHeader: false,
|
||||
useKMPHwind: false,
|
||||
lang: config.language,
|
||||
decimalSymbol: ".",
|
||||
@@ -67,7 +62,7 @@ Module.register("currentweather",{
|
||||
"11n": "wi-night-thunderstorm",
|
||||
"13n": "wi-night-snow",
|
||||
"50n": "wi-night-alt-cloudy-windy"
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
// create a variable for the first upcoming calendar event. Used if no location is specified.
|
||||
@@ -77,17 +72,17 @@ Module.register("currentweather",{
|
||||
fetchedLocationName: "",
|
||||
|
||||
// Define required scripts.
|
||||
getScripts: function() {
|
||||
getScripts: function () {
|
||||
return ["moment.js"];
|
||||
},
|
||||
|
||||
// Define required scripts.
|
||||
getStyles: function() {
|
||||
getStyles: function () {
|
||||
return ["weather-icons.css", "currentweather.css"];
|
||||
},
|
||||
|
||||
// Define required translations.
|
||||
getTranslations: function() {
|
||||
getTranslations: function () {
|
||||
// The translations for the default modules are defined in the core translation files.
|
||||
// Therefor we can just return false. Otherwise we should have returned a dictionary.
|
||||
// If you're trying to build your own module including translations, check out the documentation.
|
||||
@@ -95,7 +90,7 @@ Module.register("currentweather",{
|
||||
},
|
||||
|
||||
// Define start sequence.
|
||||
start: function() {
|
||||
start: function () {
|
||||
Log.info("Starting module: " + this.name);
|
||||
|
||||
// Set locale.
|
||||
@@ -113,13 +108,11 @@ Module.register("currentweather",{
|
||||
this.feelsLike = null;
|
||||
this.loaded = false;
|
||||
this.scheduleUpdate(this.config.initialLoadDelay);
|
||||
|
||||
},
|
||||
|
||||
// add extra information of current weather
|
||||
// windDirection, humidity, sunrise and sunset
|
||||
addExtraInfoWeather: function(wrapper) {
|
||||
|
||||
addExtraInfoWeather: function (wrapper) {
|
||||
var small = document.createElement("div");
|
||||
small.className = "normal medium";
|
||||
|
||||
@@ -134,8 +127,8 @@ Module.register("currentweather",{
|
||||
if (this.config.showWindDirection) {
|
||||
var windDirection = document.createElement("sup");
|
||||
if (this.config.showWindDirectionAsArrow) {
|
||||
if(this.windDeg !== null) {
|
||||
windDirection.innerHTML = " <i class=\"fa fa-long-arrow-down\" style=\"transform:rotate("+this.windDeg+"deg);\"></i> ";
|
||||
if (this.windDeg !== null) {
|
||||
windDirection.innerHTML = ' <i class="fa fa-long-arrow-down" style="transform:rotate(' + this.windDeg + 'deg);"></i> ';
|
||||
}
|
||||
} else {
|
||||
windDirection.innerHTML = " " + this.translate(this.windDirection);
|
||||
@@ -150,15 +143,15 @@ Module.register("currentweather",{
|
||||
var humidity = document.createElement("span");
|
||||
humidity.innerHTML = this.humidity;
|
||||
|
||||
var spacer = document.createElement("sup");
|
||||
spacer.innerHTML = " ";
|
||||
var supspacer = document.createElement("sup");
|
||||
supspacer.innerHTML = " ";
|
||||
|
||||
var humidityIcon = document.createElement("sup");
|
||||
humidityIcon.className = "wi wi-humidity humidityIcon";
|
||||
humidityIcon.innerHTML = " ";
|
||||
|
||||
small.appendChild(humidity);
|
||||
small.appendChild(spacer);
|
||||
small.appendChild(supspacer);
|
||||
small.appendChild(humidityIcon);
|
||||
}
|
||||
|
||||
@@ -174,7 +167,7 @@ Module.register("currentweather",{
|
||||
},
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function() {
|
||||
getDom: function () {
|
||||
var wrapper = document.createElement("div");
|
||||
wrapper.className = this.config.tableClass;
|
||||
|
||||
@@ -201,17 +194,17 @@ Module.register("currentweather",{
|
||||
if (this.config.units === "metric" || this.config.units === "imperial") {
|
||||
degreeLabel += "°";
|
||||
}
|
||||
if(this.config.degreeLabel) {
|
||||
switch(this.config.units) {
|
||||
case "metric":
|
||||
degreeLabel += "C";
|
||||
break;
|
||||
case "imperial":
|
||||
degreeLabel += "F";
|
||||
break;
|
||||
case "default":
|
||||
degreeLabel += "K";
|
||||
break;
|
||||
if (this.config.degreeLabel) {
|
||||
switch (this.config.units) {
|
||||
case "metric":
|
||||
degreeLabel += "C";
|
||||
break;
|
||||
case "imperial":
|
||||
degreeLabel += "F";
|
||||
break;
|
||||
case "default":
|
||||
degreeLabel += "K";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +247,7 @@ Module.register("currentweather",{
|
||||
|
||||
wrapper.appendChild(large);
|
||||
|
||||
if (this.config.showFeelsLike && this.config.onlyTemp === false){
|
||||
if (this.config.showFeelsLike && this.config.onlyTemp === false) {
|
||||
var small = document.createElement("div");
|
||||
small.className = "normal medium";
|
||||
|
||||
@@ -270,7 +263,7 @@ Module.register("currentweather",{
|
||||
},
|
||||
|
||||
// Override getHeader method.
|
||||
getHeader: function() {
|
||||
getHeader: function () {
|
||||
if (this.config.appendLocationNameToHeader && this.data.header !== undefined) {
|
||||
return this.data.header + " " + this.fetchedLocationName;
|
||||
}
|
||||
@@ -283,10 +276,10 @@ Module.register("currentweather",{
|
||||
},
|
||||
|
||||
// Override notification handler.
|
||||
notificationReceived: function(notification, payload, sender) {
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
if (notification === "DOM_OBJECTS_CREATED") {
|
||||
if (this.config.appendLocationNameToHeader) {
|
||||
this.hide(0, {lockString: this.identifier});
|
||||
this.hide(0, { lockString: this.identifier });
|
||||
}
|
||||
}
|
||||
if (notification === "CALENDAR_EVENTS") {
|
||||
@@ -318,7 +311,7 @@ Module.register("currentweather",{
|
||||
* Requests new data from openweather.org.
|
||||
* Calls processWeather on succesfull response.
|
||||
*/
|
||||
updateWeather: function() {
|
||||
updateWeather: function () {
|
||||
if (this.config.appid === "") {
|
||||
Log.error("CurrentWeather: APPID not set!");
|
||||
return;
|
||||
@@ -330,7 +323,7 @@ Module.register("currentweather",{
|
||||
|
||||
var weatherRequest = new XMLHttpRequest();
|
||||
weatherRequest.open("GET", url, true);
|
||||
weatherRequest.onreadystatechange = function() {
|
||||
weatherRequest.onreadystatechange = function () {
|
||||
if (this.readyState === 4) {
|
||||
if (this.status === 200) {
|
||||
self.processWeather(JSON.parse(this.response));
|
||||
@@ -344,7 +337,7 @@ Module.register("currentweather",{
|
||||
}
|
||||
|
||||
if (retry) {
|
||||
self.scheduleUpdate((self.loaded) ? -1 : self.config.retryDelay);
|
||||
self.scheduleUpdate(self.loaded ? -1 : self.config.retryDelay);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -356,18 +349,18 @@ Module.register("currentweather",{
|
||||
*
|
||||
* return String - URL params.
|
||||
*/
|
||||
getParams: function() {
|
||||
getParams: function () {
|
||||
var params = "?";
|
||||
if(this.config.locationID) {
|
||||
if (this.config.locationID) {
|
||||
params += "id=" + this.config.locationID;
|
||||
} else if(this.config.location) {
|
||||
} else if (this.config.location) {
|
||||
params += "q=" + this.config.location;
|
||||
} else if (this.firstEvent && this.firstEvent.geo) {
|
||||
params += "lat=" + this.firstEvent.geo.lat + "&lon=" + this.firstEvent.geo.lon;
|
||||
} else if (this.firstEvent && this.firstEvent.location) {
|
||||
params += "q=" + this.firstEvent.location;
|
||||
} else {
|
||||
this.hide(this.config.animationSpeed, {lockString:this.identifier});
|
||||
this.hide(this.config.animationSpeed, { lockString: this.identifier });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -383,8 +376,7 @@ Module.register("currentweather",{
|
||||
*
|
||||
* argument data object - Weather information received form openweather.org.
|
||||
*/
|
||||
processWeather: function(data) {
|
||||
|
||||
processWeather: function (data) {
|
||||
if (!data || !data.main || typeof data.main.temp === "undefined") {
|
||||
// Did not receive usable new data.
|
||||
// Maybe this needs a better check?
|
||||
@@ -396,7 +388,7 @@ Module.register("currentweather",{
|
||||
this.fetchedLocationName = data.name;
|
||||
this.feelsLike = 0;
|
||||
|
||||
if (this.config.useBeaufort){
|
||||
if (this.config.useBeaufort) {
|
||||
this.windSpeed = this.ms2Beaufort(this.roundValue(data.wind.speed));
|
||||
} else if (this.config.useKMPHwind) {
|
||||
this.windSpeed = parseFloat((data.wind.speed * 60 * 60) / 1000).toFixed(0);
|
||||
@@ -408,52 +400,59 @@ Module.register("currentweather",{
|
||||
var windInMph = parseFloat(data.wind.speed * 2.23694);
|
||||
|
||||
var tempInF = 0;
|
||||
switch (this.config.units){
|
||||
case "metric": tempInF = 1.8 * this.temperature + 32;
|
||||
break;
|
||||
case "imperial": tempInF = this.temperature;
|
||||
break;
|
||||
case "default":
|
||||
var tc = this.temperature - 273.15;
|
||||
tempInF = 1.8 * tc + 32;
|
||||
break;
|
||||
switch (this.config.units) {
|
||||
case "metric":
|
||||
tempInF = 1.8 * this.temperature + 32;
|
||||
break;
|
||||
case "imperial":
|
||||
tempInF = this.temperature;
|
||||
break;
|
||||
case "default":
|
||||
tempInF = 1.8 * (this.temperature - 273.15) + 32;
|
||||
break;
|
||||
}
|
||||
|
||||
if (windInMph > 3 && tempInF < 50){
|
||||
if (windInMph > 3 && tempInF < 50) {
|
||||
// windchill
|
||||
var windChillInF = Math.round(35.74+0.6215*tempInF-35.75*Math.pow(windInMph,0.16)+0.4275*tempInF*Math.pow(windInMph,0.16));
|
||||
var windChillInC = (windChillInF - 32) * (5/9);
|
||||
var windChillInF = Math.round(35.74 + 0.6215 * tempInF - 35.75 * Math.pow(windInMph, 0.16) + 0.4275 * tempInF * Math.pow(windInMph, 0.16));
|
||||
var windChillInC = (windChillInF - 32) * (5 / 9);
|
||||
// this.feelsLike = windChillInC.toFixed(0);
|
||||
|
||||
switch (this.config.units){
|
||||
case "metric": this.feelsLike = windChillInC.toFixed(0);
|
||||
break;
|
||||
case "imperial": this.feelsLike = windChillInF.toFixed(0);
|
||||
break;
|
||||
case "default":
|
||||
var tc = windChillInC + 273.15;
|
||||
this.feelsLike = tc.toFixed(0);
|
||||
break;
|
||||
switch (this.config.units) {
|
||||
case "metric":
|
||||
this.feelsLike = windChillInC.toFixed(0);
|
||||
break;
|
||||
case "imperial":
|
||||
this.feelsLike = windChillInF.toFixed(0);
|
||||
break;
|
||||
case "default":
|
||||
this.feelsLike = (windChillInC + 273.15).toFixed(0);
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (tempInF > 80 && this.humidity > 40){
|
||||
} else if (tempInF > 80 && this.humidity > 40) {
|
||||
// heat index
|
||||
var Hindex = -42.379 + 2.04901523*tempInF + 10.14333127*this.humidity
|
||||
- 0.22475541*tempInF*this.humidity - 6.83783*Math.pow(10,-3)*tempInF*tempInF
|
||||
- 5.481717*Math.pow(10,-2)*this.humidity*this.humidity
|
||||
+ 1.22874*Math.pow(10,-3)*tempInF*tempInF*this.humidity
|
||||
+ 8.5282*Math.pow(10,-4)*tempInF*this.humidity*this.humidity
|
||||
- 1.99*Math.pow(10,-6)*tempInF*tempInF*this.humidity*this.humidity;
|
||||
var Hindex =
|
||||
-42.379 +
|
||||
2.04901523 * tempInF +
|
||||
10.14333127 * this.humidity -
|
||||
0.22475541 * tempInF * this.humidity -
|
||||
6.83783 * Math.pow(10, -3) * tempInF * tempInF -
|
||||
5.481717 * Math.pow(10, -2) * this.humidity * this.humidity +
|
||||
1.22874 * Math.pow(10, -3) * tempInF * tempInF * this.humidity +
|
||||
8.5282 * Math.pow(10, -4) * tempInF * this.humidity * this.humidity -
|
||||
1.99 * Math.pow(10, -6) * tempInF * tempInF * this.humidity * this.humidity;
|
||||
|
||||
switch (this.config.units){
|
||||
case "metric": this.feelsLike = parseFloat((Hindex - 32) / 1.8).toFixed(0);
|
||||
break;
|
||||
case "imperial": this.feelsLike = Hindex.toFixed(0);
|
||||
break;
|
||||
case "default":
|
||||
var tc = parseFloat((Hindex - 32) / 1.8) + 273.15;
|
||||
this.feelsLike = tc.toFixed(0);
|
||||
break;
|
||||
switch (this.config.units) {
|
||||
case "metric":
|
||||
this.feelsLike = parseFloat((Hindex - 32) / 1.8).toFixed(0);
|
||||
break;
|
||||
case "imperial":
|
||||
this.feelsLike = Hindex.toFixed(0);
|
||||
break;
|
||||
case "default":
|
||||
var tc = parseFloat((Hindex - 32) / 1.8) + 273.15;
|
||||
this.feelsLike = tc.toFixed(0);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this.feelsLike = parseFloat(this.temperature).toFixed(0);
|
||||
@@ -470,7 +469,7 @@ Module.register("currentweather",{
|
||||
// The moment().format('h') method has a bug on the Raspberry Pi.
|
||||
// So we need to generate the timestring manually.
|
||||
// See issue: https://github.com/MichMich/MagicMirror/issues/181
|
||||
var sunriseSunsetDateObject = (sunrise < now && sunset > now) ? sunset : sunrise;
|
||||
var sunriseSunsetDateObject = sunrise < now && sunset > now ? sunset : sunrise;
|
||||
var timeString = moment(sunriseSunsetDateObject).format("HH:mm");
|
||||
if (this.config.timeFormat !== 24) {
|
||||
//var hours = sunriseSunsetDateObject.getHours() % 12 || 12;
|
||||
@@ -489,12 +488,12 @@ Module.register("currentweather",{
|
||||
}
|
||||
|
||||
this.sunriseSunsetTime = timeString;
|
||||
this.sunriseSunsetIcon = (sunrise < now && sunset > now) ? "wi-sunset" : "wi-sunrise";
|
||||
this.sunriseSunsetIcon = sunrise < now && sunset > now ? "wi-sunset" : "wi-sunrise";
|
||||
|
||||
this.show(this.config.animationSpeed, {lockString:this.identifier});
|
||||
this.show(this.config.animationSpeed, { lockString: this.identifier });
|
||||
this.loaded = true;
|
||||
this.updateDom(this.config.animationSpeed);
|
||||
this.sendNotification("CURRENTWEATHER_DATA", {data: data});
|
||||
this.sendNotification("CURRENTWEATHER_DATA", { data: data });
|
||||
},
|
||||
|
||||
/* scheduleUpdate()
|
||||
@@ -502,14 +501,14 @@ Module.register("currentweather",{
|
||||
*
|
||||
* argument delay number - Milliseconds before next update. If empty, this.config.updateInterval is used.
|
||||
*/
|
||||
scheduleUpdate: function(delay) {
|
||||
scheduleUpdate: function (delay) {
|
||||
var nextLoad = this.config.updateInterval;
|
||||
if (typeof delay !== "undefined" && delay >= 0) {
|
||||
nextLoad = delay;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
self.updateWeather();
|
||||
}, nextLoad);
|
||||
},
|
||||
@@ -518,15 +517,15 @@ Module.register("currentweather",{
|
||||
* Converts m2 to beaufort (windspeed).
|
||||
*
|
||||
* see:
|
||||
* http://www.spc.noaa.gov/faq/tornado/beaufort.html
|
||||
* https://www.spc.noaa.gov/faq/tornado/beaufort.html
|
||||
* https://en.wikipedia.org/wiki/Beaufort_scale#Modern_scale
|
||||
*
|
||||
* argument ms number - Windspeed in m/s.
|
||||
*
|
||||
* return number - Windspeed in beaufort.
|
||||
*/
|
||||
ms2Beaufort: function(ms) {
|
||||
var kmh = ms * 60 * 60 / 1000;
|
||||
ms2Beaufort: function (ms) {
|
||||
var kmh = (ms * 60 * 60) / 1000;
|
||||
var speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000];
|
||||
for (var beaufort in speeds) {
|
||||
var speed = speeds[beaufort];
|
||||
@@ -537,8 +536,8 @@ Module.register("currentweather",{
|
||||
return 12;
|
||||
},
|
||||
|
||||
deg2Cardinal: function(deg) {
|
||||
if (deg>11.25 && deg<=33.75){
|
||||
deg2Cardinal: function (deg) {
|
||||
if (deg > 11.25 && deg <= 33.75) {
|
||||
return "NNE";
|
||||
} else if (deg > 33.75 && deg <= 56.25) {
|
||||
return "NE";
|
||||
@@ -580,9 +579,8 @@ Module.register("currentweather",{
|
||||
*
|
||||
* return string - Rounded Temperature.
|
||||
*/
|
||||
roundValue: function(temperature) {
|
||||
roundValue: function (temperature) {
|
||||
var decimals = this.config.roundTemp ? 0 : 1;
|
||||
return parseFloat(temperature).toFixed(decimals);
|
||||
}
|
||||
|
||||
});
|
||||
|
@@ -1,24 +1,15 @@
|
||||
/* Magic Mirror
|
||||
* Default Modules List
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
// Modules listed below can be loaded without the 'default/' prefix. Omitting the default folder name.
|
||||
|
||||
var defaultModules = [
|
||||
"alert",
|
||||
"calendar",
|
||||
"clock",
|
||||
"compliments",
|
||||
"currentweather",
|
||||
"helloworld",
|
||||
"newsfeed",
|
||||
"weatherforecast",
|
||||
"updatenotification",
|
||||
"weather"
|
||||
];
|
||||
var defaultModules = ["alert", "calendar", "clock", "compliments", "currentweather", "helloworld", "newsfeed", "weatherforecast", "updatenotification", "weather"];
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
if (typeof module !== "undefined") {module.exports = defaultModules;}
|
||||
if (typeof module !== "undefined") {
|
||||
module.exports = defaultModules;
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
# Module: Hello World
|
||||
|
||||
The `helloworld` module is one of the default modules of the MagicMirror. It is a simple way to display a static text on the mirror.
|
||||
|
||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/helloworld.html).
|
||||
|
@@ -1,14 +1,10 @@
|
||||
/* global Module */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: HelloWorld
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
Module.register("helloworld",{
|
||||
|
||||
Module.register("helloworld", {
|
||||
// Default module config.
|
||||
defaults: {
|
||||
text: "Hello World!"
|
||||
|
@@ -1,5 +1,6 @@
|
||||
# Module: News Feed
|
||||
The `newsfeed ` module is one of the default modules of the MagicMirror.
|
||||
This module displays news headlines based on an RSS feed. Scrolling through news headlines happens time-based (````updateInterval````), but can also be controlled by sending news feed specific notifications to the module.
|
||||
|
||||
The `newsfeed` module is one of the default modules of the MagicMirror.
|
||||
This module displays news headlines based on an RSS feed. Scrolling through news headlines happens time-based (`updateInterval`), but can also be controlled by sending news feed specific notifications to the module.
|
||||
|
||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/newsfeed.html).
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/* Magic Mirror
|
||||
* Fetcher
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
@@ -17,7 +17,7 @@ var iconv = require("iconv-lite");
|
||||
* attribute logFeedWarnings boolean - Log warnings when there is an error parsing a news article.
|
||||
*/
|
||||
|
||||
var Fetcher = function(url, reloadInterval, encoding, logFeedWarnings) {
|
||||
var Fetcher = function (url, reloadInterval, encoding, logFeedWarnings) {
|
||||
var self = this;
|
||||
if (reloadInterval < 1000) {
|
||||
reloadInterval = 1000;
|
||||
@@ -26,8 +26,8 @@ var Fetcher = function(url, reloadInterval, encoding, logFeedWarnings) {
|
||||
var reloadTimer = null;
|
||||
var items = [];
|
||||
|
||||
var fetchFailedCallback = function() {};
|
||||
var itemsReceivedCallback = function() {};
|
||||
var fetchFailedCallback = function () {};
|
||||
var itemsReceivedCallback = function () {};
|
||||
|
||||
/* private methods */
|
||||
|
||||
@@ -35,32 +35,29 @@ var Fetcher = function(url, reloadInterval, encoding, logFeedWarnings) {
|
||||
* Request the new items.
|
||||
*/
|
||||
|
||||
var fetchNews = function() {
|
||||
var fetchNews = function () {
|
||||
clearTimeout(reloadTimer);
|
||||
reloadTimer = null;
|
||||
items = [];
|
||||
|
||||
var parser = new FeedMe();
|
||||
|
||||
parser.on("item", function(item) {
|
||||
|
||||
parser.on("item", function (item) {
|
||||
var title = item.title;
|
||||
var description = item.description || item.summary || item.content || "";
|
||||
var pubdate = item.pubdate || item.published || item.updated || item["dc:date"];
|
||||
var url = item.url || item.link || "";
|
||||
|
||||
if (title && pubdate) {
|
||||
|
||||
var regex = /(<([^>]+)>)/ig;
|
||||
var regex = /(<([^>]+)>)/gi;
|
||||
description = description.toString().replace(regex, "");
|
||||
|
||||
items.push({
|
||||
title: title,
|
||||
description: description,
|
||||
pubdate: pubdate,
|
||||
url: url,
|
||||
url: url
|
||||
});
|
||||
|
||||
} else if (logFeedWarnings) {
|
||||
console.log("Can't parse feed item:");
|
||||
console.log(item);
|
||||
@@ -70,39 +67,37 @@ var Fetcher = function(url, reloadInterval, encoding, logFeedWarnings) {
|
||||
}
|
||||
});
|
||||
|
||||
parser.on("end", function() {
|
||||
parser.on("end", function () {
|
||||
//console.log("end parsing - " + url);
|
||||
self.broadcastItems();
|
||||
scheduleTimer();
|
||||
});
|
||||
|
||||
parser.on("error", function(error) {
|
||||
parser.on("error", function (error) {
|
||||
fetchFailedCallback(self, error);
|
||||
scheduleTimer();
|
||||
});
|
||||
|
||||
nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
|
||||
headers = {"User-Agent": "Mozilla/5.0 (Node.js "+ nodeVersion + ") MagicMirror/" + global.version + " (https://github.com/MichMich/MagicMirror/)",
|
||||
"Cache-Control": "max-age=0, no-cache, no-store, must-revalidate",
|
||||
"Pragma": "no-cache"};
|
||||
var nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
|
||||
var headers = { "User-Agent": "Mozilla/5.0 (Node.js " + nodeVersion + ") MagicMirror/" + global.version + " (https://github.com/MichMich/MagicMirror/)", "Cache-Control": "max-age=0, no-cache, no-store, must-revalidate", Pragma: "no-cache" };
|
||||
|
||||
request({uri: url, encoding: null, headers: headers})
|
||||
.on("error", function(error) {
|
||||
request({ uri: url, encoding: null, headers: headers })
|
||||
.on("error", function (error) {
|
||||
fetchFailedCallback(self, error);
|
||||
scheduleTimer();
|
||||
})
|
||||
.pipe(iconv.decodeStream(encoding)).pipe(parser);
|
||||
|
||||
.pipe(iconv.decodeStream(encoding))
|
||||
.pipe(parser);
|
||||
};
|
||||
|
||||
/* scheduleTimer()
|
||||
* Schedule the timer for the next update.
|
||||
*/
|
||||
|
||||
var scheduleTimer = function() {
|
||||
var scheduleTimer = function () {
|
||||
//console.log('Schedule update timer.');
|
||||
clearTimeout(reloadTimer);
|
||||
reloadTimer = setTimeout(function() {
|
||||
reloadTimer = setTimeout(function () {
|
||||
fetchNews();
|
||||
}, reloadInterval);
|
||||
};
|
||||
@@ -114,7 +109,7 @@ var Fetcher = function(url, reloadInterval, encoding, logFeedWarnings) {
|
||||
*
|
||||
* attribute interval number - Interval for the update in milliseconds.
|
||||
*/
|
||||
this.setReloadInterval = function(interval) {
|
||||
this.setReloadInterval = function (interval) {
|
||||
if (interval > 1000 && interval < reloadInterval) {
|
||||
reloadInterval = interval;
|
||||
}
|
||||
@@ -123,14 +118,14 @@ var Fetcher = function(url, reloadInterval, encoding, logFeedWarnings) {
|
||||
/* startFetch()
|
||||
* Initiate fetchNews();
|
||||
*/
|
||||
this.startFetch = function() {
|
||||
this.startFetch = function () {
|
||||
fetchNews();
|
||||
};
|
||||
|
||||
/* broadcastItems()
|
||||
* Broadcast the existing items.
|
||||
*/
|
||||
this.broadcastItems = function() {
|
||||
this.broadcastItems = function () {
|
||||
if (items.length <= 0) {
|
||||
//console.log('No items to broadcast yet.');
|
||||
return;
|
||||
@@ -139,19 +134,19 @@ var Fetcher = function(url, reloadInterval, encoding, logFeedWarnings) {
|
||||
itemsReceivedCallback(self);
|
||||
};
|
||||
|
||||
this.onReceive = function(callback) {
|
||||
this.onReceive = function (callback) {
|
||||
itemsReceivedCallback = callback;
|
||||
};
|
||||
|
||||
this.onError = function(callback) {
|
||||
this.onError = function (callback) {
|
||||
fetchFailedCallback = callback;
|
||||
};
|
||||
|
||||
this.url = function() {
|
||||
this.url = function () {
|
||||
return url;
|
||||
};
|
||||
|
||||
this.items = function() {
|
||||
this.items = function () {
|
||||
return items;
|
||||
};
|
||||
};
|
||||
|
@@ -1,20 +1,16 @@
|
||||
/* global Module */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: NewsFeed
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
Module.register("newsfeed",{
|
||||
|
||||
Module.register("newsfeed", {
|
||||
// Default module config.
|
||||
defaults: {
|
||||
feeds: [
|
||||
{
|
||||
title: "New York Times",
|
||||
url: "http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml",
|
||||
url: "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml",
|
||||
encoding: "UTF-8" //ISO-8859-1
|
||||
}
|
||||
],
|
||||
@@ -44,12 +40,12 @@ Module.register("newsfeed",{
|
||||
},
|
||||
|
||||
// Define required scripts.
|
||||
getScripts: function() {
|
||||
getScripts: function () {
|
||||
return ["moment.js"];
|
||||
},
|
||||
|
||||
// Define required translations.
|
||||
getTranslations: function() {
|
||||
getTranslations: function () {
|
||||
// The translations for the default modules are defined in the core translation files.
|
||||
// Therefor we can just return false. Otherwise we should have returned a dictionary.
|
||||
// If you're trying to build your own module including translations, check out the documentation.
|
||||
@@ -57,7 +53,7 @@ Module.register("newsfeed",{
|
||||
},
|
||||
|
||||
// Define start sequence.
|
||||
start: function() {
|
||||
start: function () {
|
||||
Log.info("Starting module: " + this.name);
|
||||
|
||||
// Set locale.
|
||||
@@ -74,7 +70,7 @@ Module.register("newsfeed",{
|
||||
},
|
||||
|
||||
// Override socket notification handler.
|
||||
socketNotificationReceived: function(notification, payload) {
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
if (notification === "NEWS_ITEMS") {
|
||||
this.generateFeed(payload);
|
||||
|
||||
@@ -87,7 +83,7 @@ Module.register("newsfeed",{
|
||||
},
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function() {
|
||||
getDom: function () {
|
||||
var wrapper = document.createElement("div");
|
||||
|
||||
if (this.config.feedUrl) {
|
||||
@@ -101,7 +97,6 @@ Module.register("newsfeed",{
|
||||
}
|
||||
|
||||
if (this.newsItems.length > 0) {
|
||||
|
||||
// this.config.showFullArticle is a run-time configuration, triggered by optional notifications
|
||||
if (!this.config.showFullArticle && (this.config.showSourceTitle || this.config.showPublishDate)) {
|
||||
var sourceAndTimestamp = document.createElement("div");
|
||||
@@ -116,7 +111,7 @@ Module.register("newsfeed",{
|
||||
if (this.config.showPublishDate) {
|
||||
sourceAndTimestamp.innerHTML += moment(new Date(this.newsItems[this.activeItem].pubdate)).fromNow();
|
||||
}
|
||||
if (this.config.showSourceTitle && this.newsItems[this.activeItem].sourceTitle !== "" || this.config.showPublishDate) {
|
||||
if ((this.config.showSourceTitle && this.newsItems[this.activeItem].sourceTitle !== "") || this.config.showPublishDate) {
|
||||
sourceAndTimestamp.innerHTML += ":";
|
||||
}
|
||||
|
||||
@@ -126,47 +121,42 @@ Module.register("newsfeed",{
|
||||
//Remove selected tags from the beginning of rss feed items (title or description)
|
||||
|
||||
if (this.config.removeStartTags === "title" || this.config.removeStartTags === "both") {
|
||||
|
||||
for (f=0; f<this.config.startTags.length;f++) {
|
||||
if (this.newsItems[this.activeItem].title.slice(0,this.config.startTags[f].length) === this.config.startTags[f]) {
|
||||
this.newsItems[this.activeItem].title = this.newsItems[this.activeItem].title.slice(this.config.startTags[f].length,this.newsItems[this.activeItem].title.length);
|
||||
for (let f = 0; f < this.config.startTags.length; f++) {
|
||||
if (this.newsItems[this.activeItem].title.slice(0, this.config.startTags[f].length) === this.config.startTags[f]) {
|
||||
this.newsItems[this.activeItem].title = this.newsItems[this.activeItem].title.slice(this.config.startTags[f].length, this.newsItems[this.activeItem].title.length);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (this.config.removeStartTags === "description" || this.config.removeStartTags === "both") {
|
||||
|
||||
if (this.isShowingDescription) {
|
||||
for (f=0; f<this.config.startTags.length;f++) {
|
||||
if (this.newsItems[this.activeItem].description.slice(0,this.config.startTags[f].length) === this.config.startTags[f]) {
|
||||
this.newsItems[this.activeItem].description = this.newsItems[this.activeItem].description.slice(this.config.startTags[f].length,this.newsItems[this.activeItem].description.length);
|
||||
for (let f = 0; f < this.config.startTags.length; f++) {
|
||||
if (this.newsItems[this.activeItem].description.slice(0, this.config.startTags[f].length) === this.config.startTags[f]) {
|
||||
this.newsItems[this.activeItem].description = this.newsItems[this.activeItem].description.slice(this.config.startTags[f].length, this.newsItems[this.activeItem].description.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Remove selected tags from the end of rss feed items (title or description)
|
||||
|
||||
if (this.config.removeEndTags) {
|
||||
for (f=0; f<this.config.endTags.length;f++) {
|
||||
if (this.newsItems[this.activeItem].title.slice(-this.config.endTags[f].length)===this.config.endTags[f]) {
|
||||
this.newsItems[this.activeItem].title = this.newsItems[this.activeItem].title.slice(0,-this.config.endTags[f].length);
|
||||
for (let f = 0; f < this.config.endTags.length; f++) {
|
||||
if (this.newsItems[this.activeItem].title.slice(-this.config.endTags[f].length) === this.config.endTags[f]) {
|
||||
this.newsItems[this.activeItem].title = this.newsItems[this.activeItem].title.slice(0, -this.config.endTags[f].length);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isShowingDescription) {
|
||||
for (f=0; f<this.config.endTags.length;f++) {
|
||||
if (this.newsItems[this.activeItem].description.slice(-this.config.endTags[f].length)===this.config.endTags[f]) {
|
||||
this.newsItems[this.activeItem].description = this.newsItems[this.activeItem].description.slice(0,-this.config.endTags[f].length);
|
||||
for (let f = 0; f < this.config.endTags.length; f++) {
|
||||
if (this.newsItems[this.activeItem].description.slice(-this.config.endTags[f].length) === this.config.endTags[f]) {
|
||||
this.newsItems[this.activeItem].description = this.newsItems[this.activeItem].description.slice(0, -this.config.endTags[f].length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!this.config.showFullArticle){
|
||||
if (!this.config.showFullArticle) {
|
||||
var title = document.createElement("div");
|
||||
title.className = "newsfeed-title bright medium light" + (!this.config.wrapTitle ? " no-wrap" : "");
|
||||
title.innerHTML = this.newsItems[this.activeItem].title;
|
||||
@@ -177,7 +167,7 @@ Module.register("newsfeed",{
|
||||
var description = document.createElement("div");
|
||||
description.className = "newsfeed-desc small light" + (!this.config.wrapDescription ? " no-wrap" : "");
|
||||
var txtDesc = this.newsItems[this.activeItem].description;
|
||||
description.innerHTML = (this.config.truncDescription ? (txtDesc.length > this.config.lengthDescription ? txtDesc.substring(0, this.config.lengthDescription) + "..." : txtDesc) : txtDesc);
|
||||
description.innerHTML = this.config.truncDescription ? (txtDesc.length > this.config.lengthDescription ? txtDesc.substring(0, this.config.lengthDescription) + "..." : txtDesc) : txtDesc;
|
||||
wrapper.appendChild(description);
|
||||
}
|
||||
|
||||
@@ -199,7 +189,6 @@ Module.register("newsfeed",{
|
||||
if (this.config.hideLoading) {
|
||||
this.show();
|
||||
}
|
||||
|
||||
} else {
|
||||
if (this.config.hideLoading) {
|
||||
this.hide();
|
||||
@@ -212,14 +201,14 @@ Module.register("newsfeed",{
|
||||
return wrapper;
|
||||
},
|
||||
|
||||
getActiveItemURL: function() {
|
||||
getActiveItemURL: function () {
|
||||
return typeof this.newsItems[this.activeItem].url === "string" ? this.newsItems[this.activeItem].url : this.newsItems[this.activeItem].url.href;
|
||||
},
|
||||
|
||||
/* registerFeeds()
|
||||
* registers the feeds to be used by the backend.
|
||||
*/
|
||||
registerFeeds: function() {
|
||||
registerFeeds: function () {
|
||||
for (var f in this.config.feeds) {
|
||||
var feed = this.config.feeds[f];
|
||||
this.sendSocketNotification("ADD_FEED", {
|
||||
@@ -234,7 +223,7 @@ Module.register("newsfeed",{
|
||||
*
|
||||
* attribute feeds object - An object with feeds returned by the node helper.
|
||||
*/
|
||||
generateFeed: function(feeds) {
|
||||
generateFeed: function (feeds) {
|
||||
var newsItems = [];
|
||||
for (var feed in feeds) {
|
||||
var feedItems = feeds[feed];
|
||||
@@ -242,24 +231,24 @@ Module.register("newsfeed",{
|
||||
for (var i in feedItems) {
|
||||
var item = feedItems[i];
|
||||
item.sourceTitle = this.titleForFeed(feed);
|
||||
if (!(this.config.ignoreOldItems && ((Date.now() - new Date(item.pubdate)) > this.config.ignoreOlderThan))) {
|
||||
if (!(this.config.ignoreOldItems && Date.now() - new Date(item.pubdate) > this.config.ignoreOlderThan)) {
|
||||
newsItems.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
newsItems.sort(function(a,b) {
|
||||
newsItems.sort(function (a, b) {
|
||||
var dateA = new Date(a.pubdate);
|
||||
var dateB = new Date(b.pubdate);
|
||||
return dateB - dateA;
|
||||
});
|
||||
if(this.config.maxNewsItems > 0) {
|
||||
if (this.config.maxNewsItems > 0) {
|
||||
newsItems = newsItems.slice(0, this.config.maxNewsItems);
|
||||
}
|
||||
|
||||
if(this.config.prohibitedWords.length > 0) {
|
||||
newsItems = newsItems.filter(function(value){
|
||||
for (var i=0; i < this.config.prohibitedWords.length; i++) {
|
||||
if (this.config.prohibitedWords.length > 0) {
|
||||
newsItems = newsItems.filter(function (value) {
|
||||
for (var i = 0; i < this.config.prohibitedWords.length; i++) {
|
||||
if (value["title"].toLowerCase().indexOf(this.config.prohibitedWords[i].toLowerCase()) > -1) {
|
||||
return false;
|
||||
}
|
||||
@@ -270,8 +259,8 @@ Module.register("newsfeed",{
|
||||
|
||||
// get updated news items and broadcast them
|
||||
var updatedItems = [];
|
||||
newsItems.forEach(value => {
|
||||
if (this.newsItems.findIndex(value1 => value1 === value) === -1) {
|
||||
newsItems.forEach((value) => {
|
||||
if (this.newsItems.findIndex((value1) => value1 === value) === -1) {
|
||||
// Add item to updated items list
|
||||
updatedItems.push(value);
|
||||
}
|
||||
@@ -279,7 +268,7 @@ Module.register("newsfeed",{
|
||||
|
||||
// check if updated items exist, if so and if we should broadcast these updates, then lets do so
|
||||
if (this.config.broadcastNewsUpdates && updatedItems.length > 0) {
|
||||
this.sendNotification("NEWS_FEED_UPDATE", {items: updatedItems});
|
||||
this.sendNotification("NEWS_FEED_UPDATE", { items: updatedItems });
|
||||
}
|
||||
|
||||
this.newsItems = newsItems;
|
||||
@@ -292,7 +281,7 @@ Module.register("newsfeed",{
|
||||
*
|
||||
* returns bool
|
||||
*/
|
||||
subscribedToFeed: function(feedUrl) {
|
||||
subscribedToFeed: function (feedUrl) {
|
||||
for (var f in this.config.feeds) {
|
||||
var feed = this.config.feeds[f];
|
||||
if (feed.url === feedUrl) {
|
||||
@@ -309,7 +298,7 @@ Module.register("newsfeed",{
|
||||
*
|
||||
* returns string
|
||||
*/
|
||||
titleForFeed: function(feedUrl) {
|
||||
titleForFeed: function (feedUrl) {
|
||||
for (var f in this.config.feeds) {
|
||||
var feed = this.config.feeds[f];
|
||||
if (feed.url === feedUrl) {
|
||||
@@ -322,23 +311,23 @@ Module.register("newsfeed",{
|
||||
/* scheduleUpdateInterval()
|
||||
* Schedule visual update.
|
||||
*/
|
||||
scheduleUpdateInterval: function() {
|
||||
scheduleUpdateInterval: function () {
|
||||
var self = this;
|
||||
|
||||
self.updateDom(self.config.animationSpeed);
|
||||
|
||||
// Broadcast NewsFeed if needed
|
||||
if (self.config.broadcastNewsFeeds) {
|
||||
self.sendNotification("NEWS_FEED", {items: self.newsItems});
|
||||
self.sendNotification("NEWS_FEED", { items: self.newsItems });
|
||||
}
|
||||
|
||||
timer = setInterval(function() {
|
||||
this.timer = setInterval(function () {
|
||||
self.activeItem++;
|
||||
self.updateDom(self.config.animationSpeed);
|
||||
|
||||
// Broadcast NewsFeed if needed
|
||||
if (self.config.broadcastNewsFeeds) {
|
||||
self.sendNotification("NEWS_FEED", {items: self.newsItems});
|
||||
self.sendNotification("NEWS_FEED", { items: self.newsItems });
|
||||
}
|
||||
}, this.config.updateInterval);
|
||||
},
|
||||
@@ -350,25 +339,25 @@ Module.register("newsfeed",{
|
||||
*
|
||||
* return string - Capitalized output string.
|
||||
*/
|
||||
capitalizeFirstLetter: function(string) {
|
||||
capitalizeFirstLetter: function (string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
},
|
||||
|
||||
resetDescrOrFullArticleAndTimer: function() {
|
||||
resetDescrOrFullArticleAndTimer: function () {
|
||||
this.isShowingDescription = this.config.showDescription;
|
||||
this.config.showFullArticle = false;
|
||||
this.scrollPosition = 0;
|
||||
// reset bottom bar alignment
|
||||
document.getElementsByClassName("region bottom bar")[0].style.bottom = "0";
|
||||
document.getElementsByClassName("region bottom bar")[0].style.top = "inherit";
|
||||
if(!timer){
|
||||
if (!this.timer) {
|
||||
this.scheduleUpdateInterval();
|
||||
}
|
||||
},
|
||||
|
||||
notificationReceived: function(notification, payload, sender) {
|
||||
if(notification === "ARTICLE_NEXT"){
|
||||
var before = this.activeItem;
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
var before = this.activeItem;
|
||||
if (notification === "ARTICLE_NEXT") {
|
||||
this.activeItem++;
|
||||
if (this.activeItem >= this.newsItems.length) {
|
||||
this.activeItem = 0;
|
||||
@@ -376,8 +365,7 @@ Module.register("newsfeed",{
|
||||
this.resetDescrOrFullArticleAndTimer();
|
||||
Log.info(this.name + " - going from article #" + before + " to #" + this.activeItem + " (of " + this.newsItems.length + ")");
|
||||
this.updateDom(100);
|
||||
} else if(notification === "ARTICLE_PREVIOUS"){
|
||||
var before = this.activeItem;
|
||||
} else if (notification === "ARTICLE_PREVIOUS") {
|
||||
this.activeItem--;
|
||||
if (this.activeItem < 0) {
|
||||
this.activeItem = this.newsItems.length - 1;
|
||||
@@ -387,58 +375,56 @@ Module.register("newsfeed",{
|
||||
this.updateDom(100);
|
||||
}
|
||||
// if "more details" is received the first time: show article summary, on second time show full article
|
||||
else if(notification === "ARTICLE_MORE_DETAILS"){
|
||||
else if (notification === "ARTICLE_MORE_DETAILS") {
|
||||
// full article is already showing, so scrolling down
|
||||
if(this.config.showFullArticle === true){
|
||||
if (this.config.showFullArticle === true) {
|
||||
this.scrollPosition += this.config.scrollLength;
|
||||
window.scrollTo(0, this.scrollPosition);
|
||||
Log.info(this.name + " - scrolling down");
|
||||
Log.info(this.name + " - ARTICLE_MORE_DETAILS, scroll position: " + this.config.scrollLength);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.showFullArticle();
|
||||
}
|
||||
} else if(notification === "ARTICLE_SCROLL_UP"){
|
||||
if(this.config.showFullArticle === true){
|
||||
} else if (notification === "ARTICLE_SCROLL_UP") {
|
||||
if (this.config.showFullArticle === true) {
|
||||
this.scrollPosition -= this.config.scrollLength;
|
||||
window.scrollTo(0, this.scrollPosition);
|
||||
Log.info(this.name + " - scrolling up");
|
||||
Log.info(this.name + " - ARTICLE_SCROLL_UP, scroll position: " + this.config.scrollLength);
|
||||
}
|
||||
} else if(notification === "ARTICLE_LESS_DETAILS"){
|
||||
} else if (notification === "ARTICLE_LESS_DETAILS") {
|
||||
this.resetDescrOrFullArticleAndTimer();
|
||||
Log.info(this.name + " - showing only article titles again");
|
||||
this.updateDom(100);
|
||||
} else if (notification === "ARTICLE_TOGGLE_FULL"){
|
||||
if (this.config.showFullArticle){
|
||||
} else if (notification === "ARTICLE_TOGGLE_FULL") {
|
||||
if (this.config.showFullArticle) {
|
||||
this.activeItem++;
|
||||
this.resetDescrOrFullArticleAndTimer();
|
||||
} else {
|
||||
this.showFullArticle();
|
||||
}
|
||||
} else if (notification === "ARTICLE_INFO_REQUEST"){
|
||||
} else if (notification === "ARTICLE_INFO_REQUEST") {
|
||||
this.sendNotification("ARTICLE_INFO_RESPONSE", {
|
||||
title: this.newsItems[this.activeItem].title,
|
||||
title: this.newsItems[this.activeItem].title,
|
||||
source: this.newsItems[this.activeItem].sourceTitle,
|
||||
date: this.newsItems[this.activeItem].pubdate,
|
||||
desc: this.newsItems[this.activeItem].description,
|
||||
url: this.getActiveItemURL()
|
||||
date: this.newsItems[this.activeItem].pubdate,
|
||||
desc: this.newsItems[this.activeItem].description,
|
||||
url: this.getActiveItemURL()
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
showFullArticle: function() {
|
||||
showFullArticle: function () {
|
||||
this.isShowingDescription = !this.isShowingDescription;
|
||||
this.config.showFullArticle = !this.isShowingDescription;
|
||||
// make bottom bar align to top to allow scrolling
|
||||
if(this.config.showFullArticle === true){
|
||||
if (this.config.showFullArticle === true) {
|
||||
document.getElementsByClassName("region bottom bar")[0].style.bottom = "inherit";
|
||||
document.getElementsByClassName("region bottom bar")[0].style.top = "-90px";
|
||||
}
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
Log.info(this.name + " - showing " + this.isShowingDescription ? "article description" : "full article");
|
||||
this.updateDom(100);
|
||||
}
|
||||
|
||||
});
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/* Magic Mirror
|
||||
* Node Helper: Newsfeed
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
@@ -11,13 +11,13 @@ var Fetcher = require("./fetcher.js");
|
||||
|
||||
module.exports = NodeHelper.create({
|
||||
// Subclass start method.
|
||||
start: function() {
|
||||
start: function () {
|
||||
console.log("Starting module: " + this.name);
|
||||
this.fetchers = [];
|
||||
},
|
||||
|
||||
// Subclass socketNotificationReceived received.
|
||||
socketNotificationReceived: function(notification, payload) {
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
if (notification === "ADD_FEED") {
|
||||
this.createFetcher(payload.feed, payload.config);
|
||||
return;
|
||||
@@ -31,7 +31,7 @@ module.exports = NodeHelper.create({
|
||||
* attribute feed object - A feed object.
|
||||
* attribute config object - A configuration object containing reload interval in milliseconds.
|
||||
*/
|
||||
createFetcher: function(feed, config) {
|
||||
createFetcher: function (feed, config) {
|
||||
var self = this;
|
||||
|
||||
var url = feed.url || "";
|
||||
@@ -48,11 +48,11 @@ module.exports = NodeHelper.create({
|
||||
console.log("Create new news fetcher for url: " + url + " - Interval: " + reloadInterval);
|
||||
fetcher = new Fetcher(url, reloadInterval, encoding, config.logFeedWarnings);
|
||||
|
||||
fetcher.onReceive(function(fetcher) {
|
||||
fetcher.onReceive(function (fetcher) {
|
||||
self.broadcastFeeds();
|
||||
});
|
||||
|
||||
fetcher.onError(function(fetcher, error) {
|
||||
fetcher.onError(function (fetcher, error) {
|
||||
self.sendSocketNotification("FETCH_ERROR", {
|
||||
url: fetcher.url(),
|
||||
error: error
|
||||
@@ -74,7 +74,7 @@ module.exports = NodeHelper.create({
|
||||
* Creates an object with all feed items of the different registered feeds,
|
||||
* and broadcasts these using sendSocketNotification.
|
||||
*/
|
||||
broadcastFeeds: function() {
|
||||
broadcastFeeds: function () {
|
||||
var feeds = {};
|
||||
for (var f in this.fetchers) {
|
||||
feeds[f] = this.fetchers[f].items();
|
||||
|
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"configuration_changed": "Die Konfigurationsoptionen für das Newsfeed-Modul haben sich geändert. \nBitte überprüfen Sie die Dokumentation."
|
||||
}
|
||||
"configuration_changed": "Die Konfigurationsoptionen für das Newsfeed-Modul haben sich geändert. \nBitte überprüfen Sie die Dokumentation."
|
||||
}
|
||||
|
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"configuration_changed": "The configuration options for the newsfeed module have changed.\nPlease check the documentation."
|
||||
}
|
||||
"configuration_changed": "The configuration options for the newsfeed module have changed.\nPlease check the documentation."
|
||||
}
|
||||
|
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"configuration_changed": "Las opciones de configuración para el módulo de suministro de noticias han cambiado. \nVerifique la documentación."
|
||||
}
|
||||
"configuration_changed": "Las opciones de configuración para el módulo de suministro de noticias han cambiado. \nVerifique la documentación."
|
||||
}
|
||||
|
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"configuration_changed": "Les options de configuration du module newsfeed ont changé. \nVeuillez consulter la documentation."
|
||||
}
|
||||
"configuration_changed": "Les options de configuration du module newsfeed ont changé. \nVeuillez consulter la documentation."
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
# Module: Update Notification
|
||||
|
||||
The `updatenotification` module is one of the default modules of the MagicMirror.
|
||||
This will display a message whenever a new version of the MagicMirror application is available.
|
||||
|
||||
|
@@ -6,25 +6,22 @@ var defaultModules = require(__dirname + "/../defaultmodules.js");
|
||||
var NodeHelper = require("node_helper");
|
||||
|
||||
module.exports = NodeHelper.create({
|
||||
|
||||
config: {},
|
||||
|
||||
updateTimer: null,
|
||||
updateProcessStarted: false,
|
||||
|
||||
start: function () {
|
||||
},
|
||||
|
||||
configureModules: function(modules) {
|
||||
start: function () {},
|
||||
|
||||
configureModules: function (modules) {
|
||||
// Push MagicMirror itself , biggest chance it'll show up last in UI and isn't overwritten
|
||||
// others will be added in front
|
||||
// this method returns promises so we can't wait for every one to resolve before continuing
|
||||
simpleGits.push({"module": "default", "git": SimpleGit(path.normalize(__dirname + "/../../../"))});
|
||||
simpleGits.push({ module: "default", git: SimpleGit(path.normalize(__dirname + "/../../../")) });
|
||||
|
||||
var promises = [];
|
||||
|
||||
for (moduleName in modules) {
|
||||
for (var moduleName in modules) {
|
||||
if (!this.ignoreUpdateChecking(moduleName)) {
|
||||
// Default modules are included in the main MagicMirror repo
|
||||
var moduleFolder = path.normalize(__dirname + "/../../" + moduleName);
|
||||
@@ -33,7 +30,7 @@ module.exports = NodeHelper.create({
|
||||
//console.log("checking git for module="+moduleName)
|
||||
let stat = fs.statSync(path.join(moduleFolder, ".git"));
|
||||
promises.push(this.resolveRemote(moduleName, moduleFolder));
|
||||
} catch(err) {
|
||||
} catch (err) {
|
||||
// Error when directory .git doesn't exist
|
||||
// This module is not managed with git, skip
|
||||
continue;
|
||||
@@ -47,7 +44,7 @@ module.exports = NodeHelper.create({
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
if (notification === "CONFIG") {
|
||||
this.config = payload;
|
||||
} else if(notification === "MODULES") {
|
||||
} else if (notification === "MODULES") {
|
||||
// if this is the 1st time thru the update check process
|
||||
if (!this.updateProcessStarted) {
|
||||
this.updateProcessStarted = true;
|
||||
@@ -56,7 +53,7 @@ module.exports = NodeHelper.create({
|
||||
}
|
||||
},
|
||||
|
||||
resolveRemote: function(moduleName, moduleFolder) {
|
||||
resolveRemote: function (moduleName, moduleFolder) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var git = SimpleGit(moduleFolder);
|
||||
git.getRemotes(true, (err, remotes) => {
|
||||
@@ -65,19 +62,19 @@ module.exports = NodeHelper.create({
|
||||
return resolve();
|
||||
}
|
||||
// Folder has .git and has at least one git remote, watch this folder
|
||||
simpleGits.unshift({"module": moduleName, "git": git});
|
||||
simpleGits.unshift({ module: moduleName, git: git });
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
performFetch: function() {
|
||||
performFetch: function () {
|
||||
var self = this;
|
||||
simpleGits.forEach((sg) => {
|
||||
sg.git.fetch().status((err, data) => {
|
||||
data.module = sg.module;
|
||||
if (!err) {
|
||||
sg.git.log({"-1": null}, (err, data2) => {
|
||||
sg.git.log({ "-1": null }, (err, data2) => {
|
||||
if (!err && data2.latest && "hash" in data2.latest) {
|
||||
data.hash = data2.latest.hash;
|
||||
self.sendSocketNotification("STATUS", data);
|
||||
@@ -90,19 +87,19 @@ module.exports = NodeHelper.create({
|
||||
this.scheduleNextFetch(this.config.updateInterval);
|
||||
},
|
||||
|
||||
scheduleNextFetch: function(delay) {
|
||||
scheduleNextFetch: function (delay) {
|
||||
if (delay < 60 * 1000) {
|
||||
delay = 60 * 1000;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
clearTimeout(this.updateTimer);
|
||||
this.updateTimer = setTimeout(function() {
|
||||
this.updateTimer = setTimeout(function () {
|
||||
self.performFetch();
|
||||
}, delay);
|
||||
},
|
||||
|
||||
ignoreUpdateChecking: function(moduleName) {
|
||||
ignoreUpdateChecking: function (moduleName) {
|
||||
// Should not check for updates for default modules
|
||||
if (defaultModules.indexOf(moduleName) >= 0) {
|
||||
return true;
|
||||
@@ -116,5 +113,4 @@ module.exports = NodeHelper.create({
|
||||
// The rest of the modules that passes should check for updates
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
|
@@ -1,5 +1,10 @@
|
||||
/* Magic Mirror
|
||||
* Module: UpdateNotification
|
||||
*
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
Module.register("updatenotification", {
|
||||
|
||||
defaults: {
|
||||
updateInterval: 10 * 60 * 1000, // every 10 minutes
|
||||
refreshInterval: 24 * 60 * 60 * 1000, // one day
|
||||
@@ -12,7 +17,10 @@ Module.register("updatenotification", {
|
||||
start: function () {
|
||||
var self = this;
|
||||
Log.log("Start updatenotification");
|
||||
setInterval( () => { self.moduleList = {};self.updateDom(2); } , self.config.refreshInterval);
|
||||
setInterval(() => {
|
||||
self.moduleList = {};
|
||||
self.updateDom(2);
|
||||
}, self.config.refreshInterval);
|
||||
},
|
||||
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
@@ -33,16 +41,15 @@ Module.register("updatenotification", {
|
||||
var self = this;
|
||||
if (payload && payload.behind > 0) {
|
||||
// if we haven't seen info for this module
|
||||
if(this.moduleList[payload.module] == undefined){
|
||||
if (this.moduleList[payload.module] === undefined) {
|
||||
// save it
|
||||
this.moduleList[payload.module]=payload;
|
||||
this.moduleList[payload.module] = payload;
|
||||
self.updateDom(2);
|
||||
}
|
||||
//self.show(1000, { lockString: self.identifier });
|
||||
|
||||
} else if (payload && payload.behind == 0){
|
||||
} else if (payload && payload.behind === 0) {
|
||||
// if the module WAS in the list, but shouldn't be
|
||||
if(this.moduleList[payload.module] != undefined){
|
||||
if (this.moduleList[payload.module] !== undefined) {
|
||||
// remove it
|
||||
delete this.moduleList[payload.module];
|
||||
self.updateDom(2);
|
||||
@@ -50,24 +57,19 @@ Module.register("updatenotification", {
|
||||
}
|
||||
},
|
||||
|
||||
diffLink: function(module, text) {
|
||||
diffLink: function (module, text) {
|
||||
var localRef = module.hash;
|
||||
var remoteRef = module.tracking.replace(/.*\//, "");
|
||||
return "<a href=\"https://github.com/MichMich/MagicMirror/compare/"+localRef+"..."+remoteRef+"\" "+
|
||||
"class=\"xsmall dimmed\" "+
|
||||
"style=\"text-decoration: none;\" "+
|
||||
"target=\"_blank\" >" +
|
||||
text +
|
||||
"</a>";
|
||||
return '<a href="https://github.com/MichMich/MagicMirror/compare/' + localRef + "..." + remoteRef + '" ' + 'class="xsmall dimmed" ' + 'style="text-decoration: none;" ' + 'target="_blank" >' + text + "</a>";
|
||||
},
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function () {
|
||||
var wrapper = document.createElement("div");
|
||||
if(this.suspended==false){
|
||||
if (this.suspended === false) {
|
||||
// process the hash of module info found
|
||||
for(key of Object.keys(this.moduleList)){
|
||||
let m= this.moduleList[key];
|
||||
for (var key of Object.keys(this.moduleList)) {
|
||||
let m = this.moduleList[key];
|
||||
|
||||
var message = document.createElement("div");
|
||||
message.className = "small bright";
|
||||
@@ -77,7 +79,7 @@ Module.register("updatenotification", {
|
||||
icon.innerHTML = " ";
|
||||
message.appendChild(icon);
|
||||
|
||||
var updateInfoKeyName = m.behind == 1 ? "UPDATE_INFO_SINGLE" : "UPDATE_INFO_MULTIPLE";
|
||||
var updateInfoKeyName = m.behind === 1 ? "UPDATE_INFO_SINGLE" : "UPDATE_INFO_MULTIPLE";
|
||||
|
||||
var subtextHtml = this.translate(updateInfoKeyName, {
|
||||
COMMIT_COUNT: m.behind,
|
||||
@@ -85,9 +87,9 @@ Module.register("updatenotification", {
|
||||
});
|
||||
|
||||
var text = document.createElement("span");
|
||||
if (m.module == "default") {
|
||||
if (m.module === "default") {
|
||||
text.innerHTML = this.translate("UPDATE_NOTIFICATION");
|
||||
subtextHtml = this.diffLink(m,subtextHtml);
|
||||
subtextHtml = this.diffLink(m, subtextHtml);
|
||||
} else {
|
||||
text.innerHTML = this.translate("UPDATE_NOTIFICATION_MODULE", {
|
||||
MODULE_NAME: m.module
|
||||
@@ -106,11 +108,11 @@ Module.register("updatenotification", {
|
||||
return wrapper;
|
||||
},
|
||||
|
||||
suspend: function() {
|
||||
this.suspended=true;
|
||||
suspend: function () {
|
||||
this.suspended = true;
|
||||
},
|
||||
resume: function() {
|
||||
this.suspended=false;
|
||||
resume: function () {
|
||||
this.suspended = false;
|
||||
this.updateDom(2);
|
||||
}
|
||||
});
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# Weather Module
|
||||
|
||||
This module is aimed to be the replacement for the current `currentweather` and `weatherforcast` modules. The module will be configurable to be used as a current weather view, or to show the forecast. This way the module can be used twice to fullfil both purposes.
|
||||
This module aims to be the replacement for the current `currentweather` and `weatherforcast` modules. The module will be configurable to be used as a current weather view, or to show the forecast. This way the module can be used twice to fullfil both purposes.
|
||||
|
||||
For configuration options, please check the [MagicMirror² documentation](https://docs.magicmirror.builders/modules/weather.html).
|
||||
|
@@ -13,17 +13,17 @@ Table of Contents:
|
||||
|
||||
## The weather provider file: yourprovider.js
|
||||
|
||||
This is the script in which the weather provider will be defined. In it's most simple form, the weather provider must implement the following:
|
||||
This is the script in which the weather provider will be defined. In its most simple form, the weather provider must implement the following:
|
||||
|
||||
````javascript
|
||||
```javascript
|
||||
WeatherProvider.register("yourprovider", {
|
||||
providerName: "YourProvider",
|
||||
providerName: "YourProvider",
|
||||
|
||||
fetchCurrentWeather() {},
|
||||
fetchCurrentWeather() {},
|
||||
|
||||
fetchWeatherForecast() {}
|
||||
fetchWeatherForecast() {}
|
||||
});
|
||||
````
|
||||
```
|
||||
|
||||
### Weather provider methods to implement
|
||||
|
||||
@@ -36,7 +36,7 @@ It will then automatically refresh the module DOM with the new data.
|
||||
|
||||
#### `fetchWeatherForecast()`
|
||||
|
||||
This method is called when the weather module tries to fetch the weather weather of your provider. The implementation of this method is required.
|
||||
This method is called when the weather module tries to fetch the weather of your provider. The implementation of this method is required.
|
||||
The implementation can make use of the already implemented function `this.fetchData(url, method, data);`, which is returning a promise.
|
||||
After the response is processed, the weather forecast information (as an array of [WeatherObject](#weatherobject)s) needs to be set with `this.setCurrentWeather(forecast);`.
|
||||
It will then automatically refresh the module DOM with the new data.
|
||||
@@ -89,24 +89,24 @@ A convenience function to make requests. It returns a promise.
|
||||
|
||||
### WeatherObject
|
||||
|
||||
| Property | Type | Value/Unit |
|
||||
| --- | --- | --- |
|
||||
| units | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
|
||||
| tempUnits | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
|
||||
| windUnits | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
|
||||
| date | `object` | [Moment.js](https://momentjs.com/) object of the time/date. |
|
||||
| windSpeed |`number` | Metric: `meter/second` <br> Imperial: `miles/hour` |
|
||||
| windDirection |`number` | Direction of the wind in degrees. |
|
||||
| sunrise |`object` | [Moment.js](https://momentjs.com/) object of sunrise. |
|
||||
| sunset |`object` | [Moment.js](https://momentjs.com/) object of sunset. |
|
||||
| temperature | `number` | Current temperature |
|
||||
| minTemperature | `number` | Lowest temperature of the day. |
|
||||
| maxTemperature | `number` | Highest temperature of the day. |
|
||||
| weatherType | `string` | Icon name of the weather type. <br> Possible values: [WeatherIcons](https://www.npmjs.com/package/weathericons) |
|
||||
| humidity | `number` | Percentage of humidity |
|
||||
| rain | `number` | Metric: `millimeters` <br> Imperial: `inches` |
|
||||
| snow | `number` | Metric: `millimeters` <br> Imperial: `inches` |
|
||||
| precipitation | `number` | Metric: `millimeters` <br> Imperial: `inches` <br> UK Met Office provider: `percent` |
|
||||
| Property | Type | Value/Unit |
|
||||
| -------------- | -------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| units | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
|
||||
| tempUnits | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
|
||||
| windUnits | `string` | Gets initialized with the constructor. <br> Possible values: `metric`, `imperial` |
|
||||
| date | `object` | [Moment.js](https://momentjs.com/) object of the time/date. |
|
||||
| windSpeed | `number` | Metric: `meter/second` <br> Imperial: `miles/hour` |
|
||||
| windDirection | `number` | Direction of the wind in degrees. |
|
||||
| sunrise | `object` | [Moment.js](https://momentjs.com/) object of sunrise. |
|
||||
| sunset | `object` | [Moment.js](https://momentjs.com/) object of sunset. |
|
||||
| temperature | `number` | Current temperature |
|
||||
| minTemperature | `number` | Lowest temperature of the day. |
|
||||
| maxTemperature | `number` | Highest temperature of the day. |
|
||||
| weatherType | `string` | Icon name of the weather type. <br> Possible values: [WeatherIcons](https://www.npmjs.com/package/weathericons) |
|
||||
| humidity | `number` | Percentage of humidity |
|
||||
| rain | `number` | Metric: `millimeters` <br> Imperial: `inches` |
|
||||
| snow | `number` | Metric: `millimeters` <br> Imperial: `inches` |
|
||||
| precipitation | `number` | Metric: `millimeters` <br> Imperial: `inches` <br> UK Met Office provider: `percent` |
|
||||
|
||||
#### Current weather
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
/* global WeatherProvider, WeatherDay */
|
||||
/* global WeatherProvider, WeatherObject */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: Weather
|
||||
@@ -16,40 +16,42 @@ WeatherProvider.register("darksky", {
|
||||
providerName: "Dark Sky",
|
||||
|
||||
units: {
|
||||
imperial: 'us',
|
||||
metric: 'si'
|
||||
imperial: "us",
|
||||
metric: "si"
|
||||
},
|
||||
|
||||
fetchCurrentWeather() {
|
||||
this.fetchData(this.getUrl())
|
||||
.then(data => {
|
||||
if(!data || !data.currently || typeof data.currently.temperature === "undefined") {
|
||||
.then((data) => {
|
||||
if (!data || !data.currently || typeof data.currently.temperature === "undefined") {
|
||||
// No usable data?
|
||||
return;
|
||||
}
|
||||
|
||||
const currentWeather = this.generateWeatherDayFromCurrentWeather(data);
|
||||
this.setCurrentWeather(currentWeather);
|
||||
}).catch(function(request) {
|
||||
})
|
||||
.catch(function (request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable())
|
||||
.finally(() => this.updateAvailable());
|
||||
},
|
||||
|
||||
fetchWeatherForecast() {
|
||||
this.fetchData(this.getUrl())
|
||||
.then(data => {
|
||||
if(!data || !data.daily || !data.daily.data.length) {
|
||||
.then((data) => {
|
||||
if (!data || !data.daily || !data.daily.data.length) {
|
||||
// No usable data?
|
||||
return;
|
||||
}
|
||||
|
||||
const forecast = this.generateWeatherObjectsFromForecast(data.daily.data);
|
||||
this.setWeatherForecast(forecast);
|
||||
}).catch(function(request) {
|
||||
})
|
||||
.catch(function (request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable())
|
||||
.finally(() => this.updateAvailable());
|
||||
},
|
||||
|
||||
// Create a URL from the config and base URL.
|
||||
@@ -109,12 +111,12 @@ WeatherProvider.register("darksky", {
|
||||
const weatherTypes = {
|
||||
"clear-day": "day-sunny",
|
||||
"clear-night": "night-clear",
|
||||
"rain": "rain",
|
||||
"snow": "snow",
|
||||
"sleet": "snow",
|
||||
"wind": "wind",
|
||||
"fog": "fog",
|
||||
"cloudy": "cloudy",
|
||||
rain: "rain",
|
||||
snow: "snow",
|
||||
sleet: "snow",
|
||||
wind: "wind",
|
||||
fog: "fog",
|
||||
cloudy: "cloudy",
|
||||
"partly-cloudy-day": "day-cloudy",
|
||||
"partly-cloudy-night": "night-cloudy"
|
||||
};
|
||||
|
@@ -3,14 +3,12 @@
|
||||
/* Magic Mirror
|
||||
* Module: Weather
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*
|
||||
* This class is the blueprint for a weather provider.
|
||||
*/
|
||||
|
||||
WeatherProvider.register("openweathermap", {
|
||||
|
||||
// Set the name of the provider.
|
||||
// This isn't strictly necessary, since it will fallback to the provider identifier
|
||||
// But for debugging (and future alerts) it would be nice to have the real name.
|
||||
@@ -19,7 +17,7 @@ WeatherProvider.register("openweathermap", {
|
||||
// Overwrite the fetchCurrentWeather method.
|
||||
fetchCurrentWeather() {
|
||||
this.fetchData(this.getUrl())
|
||||
.then(data => {
|
||||
.then((data) => {
|
||||
if (!data || !data.main || typeof data.main.temp === "undefined") {
|
||||
// Did not receive usable new data.
|
||||
// Maybe this needs a better check?
|
||||
@@ -31,16 +29,16 @@ WeatherProvider.register("openweathermap", {
|
||||
const currentWeather = this.generateWeatherObjectFromCurrentWeather(data);
|
||||
this.setCurrentWeather(currentWeather);
|
||||
})
|
||||
.catch(function(request) {
|
||||
.catch(function (request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable())
|
||||
.finally(() => this.updateAvailable());
|
||||
},
|
||||
|
||||
// Overwrite the fetchCurrentWeather method.
|
||||
fetchWeatherForecast() {
|
||||
this.fetchData(this.getUrl())
|
||||
.then(data => {
|
||||
.then((data) => {
|
||||
if (!data || !data.list || !data.list.length) {
|
||||
// Did not receive usable new data.
|
||||
// Maybe this needs a better check?
|
||||
@@ -52,10 +50,10 @@ WeatherProvider.register("openweathermap", {
|
||||
const forecast = this.generateWeatherObjectsFromForecast(data.list);
|
||||
this.setWeatherForecast(forecast);
|
||||
})
|
||||
.catch(function(request) {
|
||||
.catch(function (request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable())
|
||||
.finally(() => this.updateAvailable());
|
||||
},
|
||||
|
||||
/** OpenWeatherMap Specific Methods - These are not part of the default provider methods */
|
||||
@@ -87,7 +85,6 @@ WeatherProvider.register("openweathermap", {
|
||||
* Generate WeatherObjects based on forecast information
|
||||
*/
|
||||
generateWeatherObjectsFromForecast(forecasts) {
|
||||
|
||||
if (this.config.weatherEndpoint === "/forecast") {
|
||||
return this.fetchForecastHourly(forecasts);
|
||||
} else if (this.config.weatherEndpoint === "/forecast/daily") {
|
||||
@@ -114,7 +111,6 @@ WeatherProvider.register("openweathermap", {
|
||||
let weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
|
||||
|
||||
for (const forecast of forecasts) {
|
||||
|
||||
if (date !== moment(forecast.dt, "X").format("YYYY-MM-DD")) {
|
||||
// calculate minimum/maximum temperature, specify rain amount
|
||||
weather.minTemperature = Math.min.apply(null, minTemp);
|
||||
@@ -140,7 +136,6 @@ WeatherProvider.register("openweathermap", {
|
||||
|
||||
// If the first value of today is later than 17:00, we have an icon at least!
|
||||
weather.weatherType = this.convertWeatherType(forecast.weather[0].icon);
|
||||
|
||||
}
|
||||
|
||||
if (moment(forecast.dt, "X").format("H") >= 8 && moment(forecast.dt, "X").format("H") <= 17) {
|
||||
@@ -223,7 +218,7 @@ WeatherProvider.register("openweathermap", {
|
||||
days.push(weather);
|
||||
}
|
||||
|
||||
return days;
|
||||
return days;
|
||||
},
|
||||
|
||||
/*
|
||||
@@ -261,16 +256,16 @@ WeatherProvider.register("openweathermap", {
|
||||
*/
|
||||
getParams() {
|
||||
let params = "?";
|
||||
if(this.config.locationID) {
|
||||
if (this.config.locationID) {
|
||||
params += "id=" + this.config.locationID;
|
||||
} else if(this.config.location) {
|
||||
} else if (this.config.location) {
|
||||
params += "q=" + this.config.location;
|
||||
} else if (this.firstEvent && this.firstEvent.geo) {
|
||||
params += "lat=" + this.firstEvent.geo.lat + "&lon=" + this.firstEvent.geo.lon;
|
||||
} else if (this.firstEvent && this.firstEvent.location) {
|
||||
params += "q=" + this.firstEvent.location;
|
||||
} else {
|
||||
this.hide(this.config.animationSpeed, {lockString:this.identifier});
|
||||
this.hide(this.config.animationSpeed, { lockString: this.identifier });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
/* global WeatherProvider, WeatherObject */
|
||||
/* global WeatherProvider, WeatherObject, SunCalc */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: Weather
|
||||
@@ -8,10 +8,7 @@
|
||||
*
|
||||
* This class is a provider for UK Met Office Datapoint.
|
||||
*/
|
||||
|
||||
|
||||
WeatherProvider.register("ukmetoffice", {
|
||||
|
||||
// Set the name of the provider.
|
||||
// This isn't strictly necessary, since it will fallback to the provider identifier
|
||||
// But for debugging (and future alerts) it would be nice to have the real name.
|
||||
@@ -25,9 +22,8 @@ WeatherProvider.register("ukmetoffice", {
|
||||
// Overwrite the fetchCurrentWeather method.
|
||||
fetchCurrentWeather() {
|
||||
this.fetchData(this.getUrl("3hourly"))
|
||||
.then(data => {
|
||||
if (!data || !data.SiteRep || !data.SiteRep.DV || !data.SiteRep.DV.Location ||
|
||||
!data.SiteRep.DV.Location.Period || data.SiteRep.DV.Location.Period.length == 0) {
|
||||
.then((data) => {
|
||||
if (!data || !data.SiteRep || !data.SiteRep.DV || !data.SiteRep.DV.Location || !data.SiteRep.DV.Location.Period || data.SiteRep.DV.Location.Period.length === 0) {
|
||||
// Did not receive usable new data.
|
||||
// Maybe this needs a better check?
|
||||
return;
|
||||
@@ -38,18 +34,17 @@ WeatherProvider.register("ukmetoffice", {
|
||||
const currentWeather = this.generateWeatherObjectFromCurrentWeather(data);
|
||||
this.setCurrentWeather(currentWeather);
|
||||
})
|
||||
.catch(function(request) {
|
||||
.catch(function (request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable())
|
||||
.finally(() => this.updateAvailable());
|
||||
},
|
||||
|
||||
// Overwrite the fetchCurrentWeather method.
|
||||
fetchWeatherForecast() {
|
||||
this.fetchData(this.getUrl("daily"))
|
||||
.then(data => {
|
||||
if (!data || !data.SiteRep || !data.SiteRep.DV || !data.SiteRep.DV.Location ||
|
||||
!data.SiteRep.DV.Location.Period || data.SiteRep.DV.Location.Period.length == 0) {
|
||||
.then((data) => {
|
||||
if (!data || !data.SiteRep || !data.SiteRep.DV || !data.SiteRep.DV.Location || !data.SiteRep.DV.Location.Period || data.SiteRep.DV.Location.Period.length === 0) {
|
||||
// Did not receive usable new data.
|
||||
// Maybe this needs a better check?
|
||||
return;
|
||||
@@ -60,14 +55,12 @@ WeatherProvider.register("ukmetoffice", {
|
||||
const forecast = this.generateWeatherObjectsFromForecast(data);
|
||||
this.setWeatherForecast(forecast);
|
||||
})
|
||||
.catch(function(request) {
|
||||
.catch(function (request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable())
|
||||
.finally(() => this.updateAvailable());
|
||||
},
|
||||
|
||||
|
||||
|
||||
/** UK Met Office Specific Methods - These are not part of the default provider methods */
|
||||
/*
|
||||
* Gets the complete url for the request
|
||||
@@ -83,24 +76,23 @@ WeatherProvider.register("ukmetoffice", {
|
||||
const currentWeather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
|
||||
|
||||
// data times are always UTC
|
||||
let nowUtc = moment.utc()
|
||||
let midnightUtc = nowUtc.clone().startOf("day")
|
||||
let nowUtc = moment.utc();
|
||||
let midnightUtc = nowUtc.clone().startOf("day");
|
||||
let timeInMins = nowUtc.diff(midnightUtc, "minutes");
|
||||
|
||||
// loop round each of the (5) periods, look for today (the first period may be yesterday)
|
||||
for (i in currentWeatherData.SiteRep.DV.Location.Period) {
|
||||
let periodDate = moment.utc(currentWeatherData.SiteRep.DV.Location.Period[i].value.substr(0,10), "YYYY-MM-DD")
|
||||
for (var i in currentWeatherData.SiteRep.DV.Location.Period) {
|
||||
let periodDate = moment.utc(currentWeatherData.SiteRep.DV.Location.Period[i].value.substr(0, 10), "YYYY-MM-DD");
|
||||
|
||||
// ignore if period is before today
|
||||
if (periodDate.isSameOrAfter(moment.utc().startOf("day"))) {
|
||||
|
||||
// check this is the period we want, after today the diff will be -ve
|
||||
if (moment().diff(periodDate, "minutes") > 0) {
|
||||
// loop round the reports looking for the one we are in
|
||||
// $ value specifies the time in minutes-of-the-day: 0, 180, 360,...1260
|
||||
for (j in currentWeatherData.SiteRep.DV.Location.Period[i].Rep){
|
||||
for (var j in currentWeatherData.SiteRep.DV.Location.Period[i].Rep) {
|
||||
let p = currentWeatherData.SiteRep.DV.Location.Period[i].Rep[j].$;
|
||||
if (timeInMins >= p && timeInMins-180 < p) {
|
||||
if (timeInMins >= p && timeInMins - 180 < p) {
|
||||
// finally got the one we want, so populate weather object
|
||||
currentWeather.humidity = currentWeatherData.SiteRep.DV.Location.Period[i].Rep[j].H;
|
||||
currentWeather.temperature = this.convertTemp(currentWeatherData.SiteRep.DV.Location.Period[i].Rep[j].T);
|
||||
@@ -116,7 +108,7 @@ WeatherProvider.register("ukmetoffice", {
|
||||
}
|
||||
|
||||
// determine the sunrise/sunset times - not supplied in UK Met Office data
|
||||
let times = this.calcAstroData(currentWeatherData.SiteRep.DV.Location)
|
||||
let times = this.calcAstroData(currentWeatherData.SiteRep.DV.Location);
|
||||
currentWeather.sunrise = times[0];
|
||||
currentWeather.sunset = times[1];
|
||||
|
||||
@@ -127,22 +119,21 @@ WeatherProvider.register("ukmetoffice", {
|
||||
* Generate WeatherObjects based on forecast information
|
||||
*/
|
||||
generateWeatherObjectsFromForecast(forecasts) {
|
||||
|
||||
const days = [];
|
||||
|
||||
// loop round the (5) periods getting the data
|
||||
// for each period array, Day is [0], Night is [1]
|
||||
for (j in forecasts.SiteRep.DV.Location.Period) {
|
||||
for (var j in forecasts.SiteRep.DV.Location.Period) {
|
||||
const weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits);
|
||||
|
||||
// data times are always UTC
|
||||
dateStr = forecasts.SiteRep.DV.Location.Period[j].value
|
||||
let periodDate = moment.utc(dateStr.substr(0,10), "YYYY-MM-DD")
|
||||
const dateStr = forecasts.SiteRep.DV.Location.Period[j].value;
|
||||
let periodDate = moment.utc(dateStr.substr(0, 10), "YYYY-MM-DD");
|
||||
|
||||
// ignore if period is before today
|
||||
if (periodDate.isSameOrAfter(moment.utc().startOf("day"))) {
|
||||
// populate the weather object
|
||||
weather.date = moment.utc(dateStr.substr(0,10), "YYYY-MM-DD");
|
||||
weather.date = moment.utc(dateStr.substr(0, 10), "YYYY-MM-DD");
|
||||
weather.minTemperature = this.convertTemp(forecasts.SiteRep.DV.Location.Period[j].Rep[1].Nm);
|
||||
weather.maxTemperature = this.convertTemp(forecasts.SiteRep.DV.Location.Period[j].Rep[0].Dm);
|
||||
weather.weatherType = this.convertWeatherType(forecasts.SiteRep.DV.Location.Period[j].Rep[0].W);
|
||||
@@ -213,7 +204,7 @@ WeatherProvider.register("ukmetoffice", {
|
||||
* Convert temp (from degrees C) if required
|
||||
*/
|
||||
convertTemp(tempInC) {
|
||||
return this.tempUnits === "imperial" ? tempInC * 9 / 5 + 32 : tempInC;
|
||||
return this.tempUnits === "imperial" ? (tempInC * 9) / 5 + 32 : tempInC;
|
||||
},
|
||||
|
||||
/*
|
||||
@@ -228,22 +219,22 @@ WeatherProvider.register("ukmetoffice", {
|
||||
*/
|
||||
convertWindDirection(windDirection) {
|
||||
const windCardinals = {
|
||||
"N": 0,
|
||||
"NNE": 22,
|
||||
"NE": 45,
|
||||
"ENE": 67,
|
||||
"E": 90,
|
||||
"ESE": 112,
|
||||
"SE": 135,
|
||||
"SSE": 157,
|
||||
"S": 180,
|
||||
"SSW": 202,
|
||||
"SW": 225,
|
||||
"WSW": 247,
|
||||
"W": 270,
|
||||
"WNW": 292,
|
||||
"NW": 315,
|
||||
"NNW": 337
|
||||
N: 0,
|
||||
NNE: 22,
|
||||
NE: 45,
|
||||
ENE: 67,
|
||||
E: 90,
|
||||
ESE: 112,
|
||||
SE: 135,
|
||||
SSE: 157,
|
||||
S: 180,
|
||||
SSW: 202,
|
||||
SW: 225,
|
||||
WSW: 247,
|
||||
W: 270,
|
||||
WNW: 292,
|
||||
NW: 315,
|
||||
NNW: 337
|
||||
};
|
||||
|
||||
return windCardinals.hasOwnProperty(windDirection) ? windCardinals[windDirection] : null;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
/* global WeatherProvider, WeatherObject */
|
||||
/* global WeatherProvider, WeatherObject, SunCalc */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: Weather
|
||||
@@ -11,9 +11,7 @@
|
||||
* Note that this is only for US locations (lat and lon) and does not require an API key
|
||||
* Since it is free, there are some items missing - like sunrise, sunset, humidity, etc.
|
||||
*/
|
||||
|
||||
WeatherProvider.register("weathergov", {
|
||||
|
||||
// Set the name of the provider.
|
||||
// This isn't strictly necessary, since it will fallback to the provider identifier
|
||||
// But for debugging (and future alerts) it would be nice to have the real name.
|
||||
@@ -22,7 +20,7 @@ WeatherProvider.register("weathergov", {
|
||||
// Overwrite the fetchCurrentWeather method.
|
||||
fetchCurrentWeather() {
|
||||
this.fetchData(this.getUrl())
|
||||
.then(data => {
|
||||
.then((data) => {
|
||||
if (!data || !data.properties || !data.properties.periods || !data.properties.periods.length) {
|
||||
// Did not receive usable new data.
|
||||
// Maybe this needs a better check?
|
||||
@@ -32,16 +30,16 @@ WeatherProvider.register("weathergov", {
|
||||
const currentWeather = this.generateWeatherObjectFromCurrentWeather(data.properties.periods[0]);
|
||||
this.setCurrentWeather(currentWeather);
|
||||
})
|
||||
.catch(function(request) {
|
||||
.catch(function (request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable())
|
||||
.finally(() => this.updateAvailable());
|
||||
},
|
||||
|
||||
// Overwrite the fetchCurrentWeather method.
|
||||
fetchWeatherForecast() {
|
||||
this.fetchData(this.getUrl())
|
||||
.then(data => {
|
||||
.then((data) => {
|
||||
if (!data || !data.properties || !data.properties.periods || !data.properties.periods.length) {
|
||||
// Did not receive usable new data.
|
||||
// Maybe this needs a better check?
|
||||
@@ -51,10 +49,10 @@ WeatherProvider.register("weathergov", {
|
||||
const forecast = this.generateWeatherObjectsFromForecast(data.properties.periods);
|
||||
this.setWeatherForecast(forecast);
|
||||
})
|
||||
.catch(function(request) {
|
||||
.catch(function (request) {
|
||||
Log.error("Could not load data ... ", request);
|
||||
})
|
||||
.finally(() => this.updateAvailable())
|
||||
.finally(() => this.updateAvailable());
|
||||
},
|
||||
|
||||
/** Weather.gov Specific Methods - These are not part of the default provider methods */
|
||||
@@ -77,7 +75,7 @@ WeatherProvider.register("weathergov", {
|
||||
currentWeather.weatherType = this.convertWeatherType(currentWeatherData.shortForecast, currentWeatherData.isDaytime);
|
||||
|
||||
// determine the sunrise/sunset times - not supplied in weather.gov data
|
||||
let times = this.calcAstroData(this.config.lat, this.config.lon)
|
||||
let times = this.calcAstroData(this.config.lat, this.config.lon);
|
||||
currentWeather.sunrise = times[0];
|
||||
currentWeather.sunset = times[1];
|
||||
|
||||
@@ -106,9 +104,7 @@ WeatherProvider.register("weathergov", {
|
||||
weather.precipitation = 0;
|
||||
|
||||
for (const forecast of forecasts) {
|
||||
|
||||
if (date !== moment(forecast.startTime).format("YYYY-MM-DD")) {
|
||||
|
||||
// calculate minimum/maximum temperature, specify rain amount
|
||||
weather.minTemperature = Math.min.apply(null, minTemp);
|
||||
weather.maxTemperature = Math.max.apply(null, maxTemp);
|
||||
@@ -241,22 +237,22 @@ WeatherProvider.register("weathergov", {
|
||||
*/
|
||||
convertWindDirection(windDirection) {
|
||||
const windCardinals = {
|
||||
"N": 0,
|
||||
"NNE": 22,
|
||||
"NE": 45,
|
||||
"ENE": 67,
|
||||
"E": 90,
|
||||
"ESE": 112,
|
||||
"SE": 135,
|
||||
"SSE": 157,
|
||||
"S": 180,
|
||||
"SSW": 202,
|
||||
"SW": 225,
|
||||
"WSW": 247,
|
||||
"W": 270,
|
||||
"WNW": 292,
|
||||
"NW": 315,
|
||||
"NNW": 337
|
||||
N: 0,
|
||||
NNE: 22,
|
||||
NE: 45,
|
||||
ENE: 67,
|
||||
E: 90,
|
||||
ESE: 112,
|
||||
SE: 135,
|
||||
SSE: 157,
|
||||
S: 180,
|
||||
SSW: 202,
|
||||
SW: 225,
|
||||
WSW: 247,
|
||||
W: 270,
|
||||
WNW: 292,
|
||||
NW: 315,
|
||||
NNW: 337
|
||||
};
|
||||
|
||||
return windCardinals.hasOwnProperty(windDirection) ? windCardinals[windDirection] : null;
|
||||
|
@@ -3,8 +3,6 @@
|
||||
font-size: 75%;
|
||||
line-height: 65px;
|
||||
display: inline-block;
|
||||
-ms-transform: translate(0, -3px); /* IE 9 */
|
||||
-webkit-transform: translate(0, -3px); /* Safari */
|
||||
transform: translate(0, -3px);
|
||||
}
|
||||
|
||||
|
@@ -1,16 +1,14 @@
|
||||
/* global Module, WeatherProvider */
|
||||
/* global WeatherProvider */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: Weather
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
Module.register("weather",{
|
||||
Module.register("weather", {
|
||||
// Default module config.
|
||||
defaults: {
|
||||
updateInterval: 10 * 60 * 1000,
|
||||
weatherProvider: "openweathermap",
|
||||
roundTemp: false,
|
||||
type: "current", //current, forecast
|
||||
@@ -45,7 +43,7 @@ Module.register("weather",{
|
||||
retryDelay: 2500,
|
||||
|
||||
apiVersion: "2.5",
|
||||
apiBase: "http://api.openweathermap.org/data/",
|
||||
apiBase: "https://api.openweathermap.org/data/",
|
||||
weatherEndpoint: "/weather",
|
||||
|
||||
appendLocationNameToHeader: true,
|
||||
@@ -62,23 +60,17 @@ Module.register("weather",{
|
||||
weatherProvider: null,
|
||||
|
||||
// Define required scripts.
|
||||
getStyles: function() {
|
||||
getStyles: function () {
|
||||
return ["font-awesome.css", "weather-icons.css", "weather.css"];
|
||||
},
|
||||
|
||||
// Return the scripts that are necessary for the weather module.
|
||||
getScripts: function () {
|
||||
return [
|
||||
"moment.js",
|
||||
"weatherprovider.js",
|
||||
"weatherobject.js",
|
||||
"suncalc.js",
|
||||
this.file("providers/" + this.config.weatherProvider.toLowerCase() + ".js")
|
||||
];
|
||||
return ["moment.js", "weatherprovider.js", "weatherobject.js", "suncalc.js", this.file("providers/" + this.config.weatherProvider.toLowerCase() + ".js")];
|
||||
},
|
||||
|
||||
// Override getHeader method.
|
||||
getHeader: function() {
|
||||
getHeader: function () {
|
||||
if (this.config.appendLocationNameToHeader && this.data.header !== undefined && this.weatherProvider) {
|
||||
return this.data.header + " " + this.weatherProvider.fetchedLocation();
|
||||
}
|
||||
@@ -104,7 +96,7 @@ Module.register("weather",{
|
||||
},
|
||||
|
||||
// Override notification handler.
|
||||
notificationReceived: function(notification, payload, sender) {
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
if (notification === "CALENDAR_EVENTS") {
|
||||
var senderClasses = sender.data.classes.toLowerCase().split(" ");
|
||||
if (senderClasses.indexOf(this.config.calendarClass.toLowerCase()) !== -1) {
|
||||
@@ -147,13 +139,13 @@ Module.register("weather",{
|
||||
},
|
||||
|
||||
// What to do when the weather provider has new information available?
|
||||
updateAvailable: function() {
|
||||
updateAvailable: function () {
|
||||
Log.log("New weather information available.");
|
||||
this.updateDom(0);
|
||||
this.scheduleUpdate();
|
||||
},
|
||||
|
||||
scheduleUpdate: function(delay = null) {
|
||||
scheduleUpdate: function (delay = null) {
|
||||
var nextLoad = this.config.updateInterval;
|
||||
if (delay !== null && delay >= 0) {
|
||||
nextLoad = delay;
|
||||
@@ -168,88 +160,106 @@ Module.register("weather",{
|
||||
}, nextLoad);
|
||||
},
|
||||
|
||||
roundValue: function(temperature) {
|
||||
roundValue: function (temperature) {
|
||||
var decimals = this.config.roundTemp ? 0 : 1;
|
||||
return parseFloat(temperature).toFixed(decimals);
|
||||
},
|
||||
|
||||
addFilters() {
|
||||
this.nunjucksEnvironment().addFilter("formatTime", function(date) {
|
||||
date = moment(date);
|
||||
this.nunjucksEnvironment().addFilter(
|
||||
"formatTime",
|
||||
function (date) {
|
||||
date = moment(date);
|
||||
|
||||
if (this.config.timeFormat !== 24) {
|
||||
if (this.config.showPeriod) {
|
||||
if (this.config.showPeriodUpper) {
|
||||
return date.format("h:mm A");
|
||||
if (this.config.timeFormat !== 24) {
|
||||
if (this.config.showPeriod) {
|
||||
if (this.config.showPeriodUpper) {
|
||||
return date.format("h:mm A");
|
||||
} else {
|
||||
return date.format("h:mm a");
|
||||
}
|
||||
} else {
|
||||
return date.format("h:mm a");
|
||||
}
|
||||
} else {
|
||||
return date.format("h:mm");
|
||||
}
|
||||
}
|
||||
|
||||
return date.format("HH:mm");
|
||||
}.bind(this));
|
||||
|
||||
this.nunjucksEnvironment().addFilter("unit", function (value, type) {
|
||||
if (type === "temperature") {
|
||||
if (this.config.tempUnits === "metric" || this.config.tempUnits === "imperial") {
|
||||
value += "°";
|
||||
}
|
||||
if (this.config.degreeLabel) {
|
||||
if (this.config.tempUnits === "metric") {
|
||||
value += "C";
|
||||
} else if (this.config.tempUnits === "imperial") {
|
||||
value += "F";
|
||||
} else {
|
||||
value += "K";
|
||||
return date.format("h:mm");
|
||||
}
|
||||
}
|
||||
} else if (type === "precip") {
|
||||
if (isNaN(value) || value === 0 || value.toFixed(2) === "0.00") {
|
||||
value = "";
|
||||
} else {
|
||||
if (this.config.weatherProvider === "ukmetoffice") {
|
||||
value += "%";
|
||||
} else {
|
||||
value = `${value.toFixed(2)} ${this.config.units === "imperial" ? "in" : "mm"}`;
|
||||
}
|
||||
|
||||
return date.format("HH:mm");
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.nunjucksEnvironment().addFilter(
|
||||
"unit",
|
||||
function (value, type) {
|
||||
if (type === "temperature") {
|
||||
if (this.config.tempUnits === "metric" || this.config.tempUnits === "imperial") {
|
||||
value += "°";
|
||||
}
|
||||
if (this.config.degreeLabel) {
|
||||
if (this.config.tempUnits === "metric") {
|
||||
value += "C";
|
||||
} else if (this.config.tempUnits === "imperial") {
|
||||
value += "F";
|
||||
} else {
|
||||
value += "K";
|
||||
}
|
||||
}
|
||||
} else if (type === "precip") {
|
||||
if (isNaN(value) || value === 0 || value.toFixed(2) === "0.00") {
|
||||
value = "";
|
||||
} else {
|
||||
if (this.config.weatherProvider === "ukmetoffice") {
|
||||
value += "%";
|
||||
} else {
|
||||
value = `${value.toFixed(2)} ${this.config.units === "imperial" ? "in" : "mm"}`;
|
||||
}
|
||||
}
|
||||
} else if (type === "humidity") {
|
||||
value += "%";
|
||||
}
|
||||
} else if (type === "humidity") {
|
||||
value += "%";
|
||||
}
|
||||
|
||||
return value;
|
||||
}.bind(this));
|
||||
return value;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.nunjucksEnvironment().addFilter("roundValue", function(value) {
|
||||
return this.roundValue(value);
|
||||
}.bind(this));
|
||||
this.nunjucksEnvironment().addFilter(
|
||||
"roundValue",
|
||||
function (value) {
|
||||
return this.roundValue(value);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.nunjucksEnvironment().addFilter("decimalSymbol", function(value) {
|
||||
return value.toString().replace(/\./g, this.config.decimalSymbol);
|
||||
}.bind(this));
|
||||
this.nunjucksEnvironment().addFilter(
|
||||
"decimalSymbol",
|
||||
function (value) {
|
||||
return value.toString().replace(/\./g, this.config.decimalSymbol);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.nunjucksEnvironment().addFilter("calcNumSteps", function(forecast) {
|
||||
return Math.min(forecast.length, this.config.maxNumberOfDays);
|
||||
}.bind(this));
|
||||
this.nunjucksEnvironment().addFilter(
|
||||
"calcNumSteps",
|
||||
function (forecast) {
|
||||
return Math.min(forecast.length, this.config.maxNumberOfDays);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.nunjucksEnvironment().addFilter("opacity", function(currentStep, numSteps) {
|
||||
if (this.config.fade && this.config.fadePoint < 1) {
|
||||
if (this.config.fadePoint < 0) {
|
||||
this.config.fadePoint = 0;
|
||||
}
|
||||
var startingPoint = numSteps * this.config.fadePoint;
|
||||
var numFadesteps = numSteps - startingPoint;
|
||||
if (currentStep >= startingPoint) {
|
||||
return 1 - (currentStep - startingPoint) / numFadesteps;
|
||||
this.nunjucksEnvironment().addFilter(
|
||||
"opacity",
|
||||
function (currentStep, numSteps) {
|
||||
if (this.config.fade && this.config.fadePoint < 1) {
|
||||
if (this.config.fadePoint < 0) {
|
||||
this.config.fadePoint = 0;
|
||||
}
|
||||
var startingPoint = numSteps * this.config.fadePoint;
|
||||
var numFadesteps = numSteps - startingPoint;
|
||||
if (currentStep >= startingPoint) {
|
||||
return 1 - (currentStep - startingPoint) / numFadesteps;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@@ -1,20 +1,16 @@
|
||||
/* global Class */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: Weather
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*
|
||||
* This class is the blueprint for a day which includes weather information.
|
||||
*
|
||||
* Currently this is focused on the information which is necessary for the current weather.
|
||||
* As soon as we start implementing the forecast, mode properties will be added.
|
||||
*/
|
||||
|
||||
// Currently this is focused on the information which is necessary for the current weather.
|
||||
// As soon as we start implementing the forecast, mode properties will be added.
|
||||
|
||||
class WeatherObject {
|
||||
constructor(units, tempUnits, windUnits) {
|
||||
|
||||
this.units = units;
|
||||
this.tempUnits = tempUnits;
|
||||
this.windUnits = windUnits;
|
||||
@@ -32,11 +28,10 @@ class WeatherObject {
|
||||
this.snow = null;
|
||||
this.precipitation = null;
|
||||
this.feelsLikeTemp = null;
|
||||
|
||||
}
|
||||
|
||||
cardinalWindDirection() {
|
||||
if (this.windDirection > 11.25 && this.windDirection <= 33.75){
|
||||
if (this.windDirection > 11.25 && this.windDirection <= 33.75) {
|
||||
return "NNE";
|
||||
} else if (this.windDirection > 33.75 && this.windDirection <= 56.25) {
|
||||
return "NE";
|
||||
@@ -72,7 +67,7 @@ class WeatherObject {
|
||||
}
|
||||
|
||||
beaufortWindSpeed() {
|
||||
const windInKmh = (this.windUnits === "imperial") ? this.windSpeed * 1.609344 : this.windSpeed * 60 * 60 / 1000;
|
||||
const windInKmh = this.windUnits === "imperial" ? this.windSpeed * 1.609344 : (this.windSpeed * 60 * 60) / 1000;
|
||||
const speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000];
|
||||
for (const [index, speed] of speeds.entries()) {
|
||||
if (speed > windInKmh) {
|
||||
@@ -87,24 +82,28 @@ class WeatherObject {
|
||||
}
|
||||
|
||||
feelsLike() {
|
||||
if (this.feelsLikeTemp) {
|
||||
return this.feelsLikeTemp;
|
||||
if (this.feelsLikeTemp) {
|
||||
return this.feelsLikeTemp;
|
||||
}
|
||||
const windInMph = (this.windUnits === "imperial") ? this.windSpeed : this.windSpeed * 2.23694;
|
||||
const tempInF = this.tempUnits === "imperial" ? this.temperature : this.temperature * 9 / 5 + 32;
|
||||
const windInMph = this.windUnits === "imperial" ? this.windSpeed : this.windSpeed * 2.23694;
|
||||
const tempInF = this.tempUnits === "imperial" ? this.temperature : (this.temperature * 9) / 5 + 32;
|
||||
let feelsLike = tempInF;
|
||||
|
||||
if (windInMph > 3 && tempInF < 50) {
|
||||
feelsLike = Math.round(35.74 + 0.6215 * tempInF - 35.75 * Math.pow(windInMph, 0.16) + 0.4275 * tempInF * Math.pow(windInMph, 0.16));
|
||||
} else if (tempInF > 80 && this.humidity > 40) {
|
||||
feelsLike = -42.379 + 2.04901523 * tempInF + 10.14333127 * this.humidity
|
||||
- 0.22475541 * tempInF * this.humidity - 6.83783 * Math.pow(10, -3) * tempInF * tempInF
|
||||
- 5.481717 * Math.pow(10, -2) * this.humidity * this.humidity
|
||||
+ 1.22874 * Math.pow(10, -3) * tempInF * tempInF * this.humidity
|
||||
+ 8.5282 * Math.pow(10, -4) * tempInF * this.humidity * this.humidity
|
||||
- 1.99 * Math.pow(10, -6) * tempInF * tempInF * this.humidity * this.humidity;
|
||||
feelsLike =
|
||||
-42.379 +
|
||||
2.04901523 * tempInF +
|
||||
10.14333127 * this.humidity -
|
||||
0.22475541 * tempInF * this.humidity -
|
||||
6.83783 * Math.pow(10, -3) * tempInF * tempInF -
|
||||
5.481717 * Math.pow(10, -2) * this.humidity * this.humidity +
|
||||
1.22874 * Math.pow(10, -3) * tempInF * tempInF * this.humidity +
|
||||
8.5282 * Math.pow(10, -4) * tempInF * this.humidity * this.humidity -
|
||||
1.99 * Math.pow(10, -6) * tempInF * tempInF * this.humidity * this.humidity;
|
||||
}
|
||||
|
||||
return this.tempUnits === "imperial" ? feelsLike : (feelsLike - 32) * 5 / 9;
|
||||
return this.tempUnits === "imperial" ? feelsLike : ((feelsLike - 32) * 5) / 9;
|
||||
}
|
||||
}
|
||||
|
@@ -3,15 +3,11 @@
|
||||
/* Magic Mirror
|
||||
* Module: Weather
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*
|
||||
* This class is the blueprint for a weather provider.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base BluePrint for the WeatherProvider
|
||||
*/
|
||||
var WeatherProvider = Class.extend({
|
||||
// Weather Provider Properties
|
||||
providerName: null,
|
||||
@@ -32,77 +28,77 @@ var WeatherProvider = Class.extend({
|
||||
// All the following methods can be overwritten, although most are good as they are.
|
||||
|
||||
// Called when a weather provider is initialized.
|
||||
init: function(config) {
|
||||
init: function (config) {
|
||||
this.config = config;
|
||||
Log.info(`Weather provider: ${this.providerName} initialized.`);
|
||||
},
|
||||
|
||||
// Called to set the config, this config is the same as the weather module's config.
|
||||
setConfig: function(config) {
|
||||
setConfig: function (config) {
|
||||
this.config = config;
|
||||
Log.info(`Weather provider: ${this.providerName} config set.`, this.config);
|
||||
},
|
||||
|
||||
// Called when the weather provider is about to start.
|
||||
start: function() {
|
||||
start: function () {
|
||||
Log.info(`Weather provider: ${this.providerName} started.`);
|
||||
},
|
||||
|
||||
// This method should start the API request to fetch the current weather.
|
||||
// This method should definitely be overwritten in the provider.
|
||||
fetchCurrentWeather: function() {
|
||||
fetchCurrentWeather: function () {
|
||||
Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchCurrentWeather method.`);
|
||||
},
|
||||
|
||||
// This method should start the API request to fetch the weather forecast.
|
||||
// This method should definitely be overwritten in the provider.
|
||||
fetchWeatherForecast: function() {
|
||||
fetchWeatherForecast: function () {
|
||||
Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchWeatherForecast method.`);
|
||||
},
|
||||
|
||||
// This returns a WeatherDay object for the current weather.
|
||||
currentWeather: function() {
|
||||
currentWeather: function () {
|
||||
return this.currentWeatherObject;
|
||||
},
|
||||
|
||||
// This returns an array of WeatherDay objects for the weather forecast.
|
||||
weatherForecast: function() {
|
||||
weatherForecast: function () {
|
||||
return this.weatherForecastArray;
|
||||
},
|
||||
|
||||
// This returns the name of the fetched location or an empty string.
|
||||
fetchedLocation: function() {
|
||||
fetchedLocation: function () {
|
||||
return this.fetchedLocationName || "";
|
||||
},
|
||||
|
||||
// Set the currentWeather and notify the delegate that new information is available.
|
||||
setCurrentWeather: function(currentWeatherObject) {
|
||||
setCurrentWeather: function (currentWeatherObject) {
|
||||
// We should check here if we are passing a WeatherDay
|
||||
this.currentWeatherObject = currentWeatherObject;
|
||||
},
|
||||
|
||||
// Set the weatherForecastArray and notify the delegate that new information is available.
|
||||
setWeatherForecast: function(weatherForecastArray) {
|
||||
setWeatherForecast: function (weatherForecastArray) {
|
||||
// We should check here if we are passing a WeatherDay
|
||||
this.weatherForecastArray = weatherForecastArray;
|
||||
},
|
||||
|
||||
// Set the fetched location name.
|
||||
setFetchedLocation: function(name) {
|
||||
setFetchedLocation: function (name) {
|
||||
this.fetchedLocationName = name;
|
||||
},
|
||||
|
||||
// Notify the delegate that new weather is available.
|
||||
updateAvailable: function() {
|
||||
updateAvailable: function () {
|
||||
this.delegate.updateAvailable(this);
|
||||
},
|
||||
|
||||
// A convenience function to make requests. It returns a promise.
|
||||
fetchData: function(url, method = "GET", data = null) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
fetchData: function (url, method = "GET", data = null) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.open(method, url, true);
|
||||
request.onreadystatechange = function() {
|
||||
request.onreadystatechange = function () {
|
||||
if (this.readyState === 4) {
|
||||
if (this.status === 200) {
|
||||
resolve(JSON.parse(this.response));
|
||||
@@ -124,14 +120,14 @@ WeatherProvider.providers = [];
|
||||
/**
|
||||
* Static method to register a new weather provider.
|
||||
*/
|
||||
WeatherProvider.register = function(providerIdentifier, providerDetails) {
|
||||
WeatherProvider.register = function (providerIdentifier, providerDetails) {
|
||||
WeatherProvider.providers[providerIdentifier.toLowerCase()] = WeatherProvider.extend(providerDetails);
|
||||
};
|
||||
|
||||
/**
|
||||
* Static method to initialize a new weather provider.
|
||||
*/
|
||||
WeatherProvider.initialize = function(providerIdentifier, delegate) {
|
||||
WeatherProvider.initialize = function (providerIdentifier, delegate) {
|
||||
providerIdentifier = providerIdentifier.toLowerCase();
|
||||
|
||||
var provider = new WeatherProvider.providers[providerIdentifier]();
|
||||
|
@@ -1,4 +1,5 @@
|
||||
# Module: Weather Forecast
|
||||
|
||||
The `weatherforecast` module is one of the default modules of the MagicMirror.
|
||||
This module displays the weather forecast for the coming week, including an an icon to display the current conditions, the minimum temperature and the maximum temperature.
|
||||
|
||||
|
@@ -19,9 +19,9 @@
|
||||
}
|
||||
|
||||
.weatherforecast tr.colored .min-temp {
|
||||
color: #BCDDFF;
|
||||
color: #bcddff;
|
||||
}
|
||||
|
||||
.weatherforecast tr.colored .max-temp {
|
||||
color: #FF8E99;
|
||||
color: #ff8e99;
|
||||
}
|
||||
|
@@ -1,14 +1,10 @@
|
||||
/* global Module */
|
||||
|
||||
/* Magic Mirror
|
||||
* Module: WeatherForecast
|
||||
*
|
||||
* By Michael Teeuw http://michaelteeuw.nl
|
||||
* By Michael Teeuw https://michaelteeuw.nl
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
Module.register("weatherforecast",{
|
||||
|
||||
Module.register("weatherforecast", {
|
||||
// Default module config.
|
||||
defaults: {
|
||||
location: false,
|
||||
@@ -59,27 +55,27 @@ Module.register("weatherforecast",{
|
||||
"11n": "wi-night-thunderstorm",
|
||||
"13n": "wi-night-snow",
|
||||
"50n": "wi-night-alt-cloudy-windy"
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
// create a variable for the first upcoming calendaar event. Used if no location is specified.
|
||||
// create a variable for the first upcoming calendar event. Used if no location is specified.
|
||||
firstEvent: false,
|
||||
|
||||
// create a variable to hold the location name based on the API result.
|
||||
fetchedLocationName: "",
|
||||
|
||||
// Define required scripts.
|
||||
getScripts: function() {
|
||||
getScripts: function () {
|
||||
return ["moment.js"];
|
||||
},
|
||||
|
||||
// Define required scripts.
|
||||
getStyles: function() {
|
||||
getStyles: function () {
|
||||
return ["weather-icons.css", "weatherforecast.css"];
|
||||
},
|
||||
|
||||
// Define required translations.
|
||||
getTranslations: function() {
|
||||
getTranslations: function () {
|
||||
// The translations for the default modules are defined in the core translation files.
|
||||
// Therefor we can just return false. Otherwise we should have returned a dictionary.
|
||||
// If you're trying to build your own module including translations, check out the documentation.
|
||||
@@ -87,7 +83,7 @@ Module.register("weatherforecast",{
|
||||
},
|
||||
|
||||
// Define start sequence.
|
||||
start: function() {
|
||||
start: function () {
|
||||
Log.info("Starting module: " + this.name);
|
||||
|
||||
// Set locale.
|
||||
@@ -98,11 +94,10 @@ Module.register("weatherforecast",{
|
||||
this.scheduleUpdate(this.config.initialLoadDelay);
|
||||
|
||||
this.updateTimer = null;
|
||||
|
||||
},
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function() {
|
||||
getDom: function () {
|
||||
var wrapper = document.createElement("div");
|
||||
|
||||
if (this.config.appid === "") {
|
||||
@@ -146,17 +141,17 @@ Module.register("weatherforecast",{
|
||||
if (this.config.units === "metric" || this.config.units === "imperial") {
|
||||
degreeLabel += "°";
|
||||
}
|
||||
if(this.config.scale) {
|
||||
switch(this.config.units) {
|
||||
case "metric":
|
||||
degreeLabel += "C";
|
||||
break;
|
||||
case "imperial":
|
||||
degreeLabel += "F";
|
||||
break;
|
||||
case "default":
|
||||
degreeLabel = "K";
|
||||
break;
|
||||
if (this.config.scale) {
|
||||
switch (this.config.units) {
|
||||
case "metric":
|
||||
degreeLabel += "C";
|
||||
break;
|
||||
case "imperial":
|
||||
degreeLabel += "F";
|
||||
break;
|
||||
case "default":
|
||||
degreeLabel = "K";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +174,7 @@ Module.register("weatherforecast",{
|
||||
if (isNaN(forecast.rain)) {
|
||||
rainCell.innerHTML = "";
|
||||
} else {
|
||||
if(config.units !== "imperial") {
|
||||
if (config.units !== "imperial") {
|
||||
rainCell.innerHTML = parseFloat(forecast.rain).toFixed(1).replace(".", this.config.decimalSymbol) + " mm";
|
||||
} else {
|
||||
rainCell.innerHTML = (parseFloat(forecast.rain) / 25.4).toFixed(2).replace(".", this.config.decimalSymbol) + " in";
|
||||
@@ -197,7 +192,7 @@ Module.register("weatherforecast",{
|
||||
var steps = this.forecast.length - startingPoint;
|
||||
if (f >= startingPoint) {
|
||||
var currentStep = f - startingPoint;
|
||||
row.style.opacity = 1 - (1 / steps * currentStep);
|
||||
row.style.opacity = 1 - (1 / steps) * currentStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,7 +201,7 @@ Module.register("weatherforecast",{
|
||||
},
|
||||
|
||||
// Override getHeader method.
|
||||
getHeader: function() {
|
||||
getHeader: function () {
|
||||
if (this.config.appendLocationNameToHeader) {
|
||||
return this.data.header + " " + this.fetchedLocationName;
|
||||
}
|
||||
@@ -215,10 +210,10 @@ Module.register("weatherforecast",{
|
||||
},
|
||||
|
||||
// Override notification handler.
|
||||
notificationReceived: function(notification, payload, sender) {
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
if (notification === "DOM_OBJECTS_CREATED") {
|
||||
if (this.config.appendLocationNameToHeader) {
|
||||
this.hide(0, {lockString: this.identifier});
|
||||
this.hide(0, { lockString: this.identifier });
|
||||
}
|
||||
}
|
||||
if (notification === "CALENDAR_EVENTS") {
|
||||
@@ -242,7 +237,7 @@ Module.register("weatherforecast",{
|
||||
* Requests new data from openweather.org.
|
||||
* Calls processWeather on successful response.
|
||||
*/
|
||||
updateWeather: function() {
|
||||
updateWeather: function () {
|
||||
if (this.config.appid === "") {
|
||||
Log.error("WeatherForecast: APPID not set!");
|
||||
return;
|
||||
@@ -254,7 +249,7 @@ Module.register("weatherforecast",{
|
||||
|
||||
var weatherRequest = new XMLHttpRequest();
|
||||
weatherRequest.open("GET", url, true);
|
||||
weatherRequest.onreadystatechange = function() {
|
||||
weatherRequest.onreadystatechange = function () {
|
||||
if (this.readyState === 4) {
|
||||
if (this.status === 200) {
|
||||
self.processWeather(JSON.parse(this.response));
|
||||
@@ -272,7 +267,7 @@ Module.register("weatherforecast",{
|
||||
}
|
||||
|
||||
if (retry) {
|
||||
self.scheduleUpdate((self.loaded) ? -1 : self.config.retryDelay);
|
||||
self.scheduleUpdate(self.loaded ? -1 : self.config.retryDelay);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -284,18 +279,18 @@ Module.register("weatherforecast",{
|
||||
*
|
||||
* return String - URL params.
|
||||
*/
|
||||
getParams: function() {
|
||||
getParams: function () {
|
||||
var params = "?";
|
||||
if(this.config.locationID) {
|
||||
if (this.config.locationID) {
|
||||
params += "id=" + this.config.locationID;
|
||||
} else if(this.config.location) {
|
||||
} else if (this.config.location) {
|
||||
params += "q=" + this.config.location;
|
||||
} else if (this.firstEvent && this.firstEvent.geo) {
|
||||
params += "lat=" + this.firstEvent.geo.lat + "&lon=" + this.firstEvent.geo.lon;
|
||||
} else if (this.firstEvent && this.firstEvent.location) {
|
||||
params += "q=" + this.firstEvent.location;
|
||||
} else {
|
||||
this.hide(this.config.animationSpeed, {lockString:this.identifier});
|
||||
this.hide(this.config.animationSpeed, { lockString: this.identifier });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -310,12 +305,12 @@ Module.register("weatherforecast",{
|
||||
* parserDataWeather(data)
|
||||
*
|
||||
* Use the parse to keep the same struct between daily and forecast Endpoint
|
||||
* from Openweather
|
||||
* from openweather.org
|
||||
*
|
||||
*/
|
||||
parserDataWeather: function(data) {
|
||||
parserDataWeather: function (data) {
|
||||
if (data.hasOwnProperty("main")) {
|
||||
data["temp"] = {"min": data.main.temp_min, "max": data.main.temp_max};
|
||||
data["temp"] = { min: data.main.temp_min, max: data.main.temp_max };
|
||||
}
|
||||
return data;
|
||||
},
|
||||
@@ -325,7 +320,7 @@ Module.register("weatherforecast",{
|
||||
*
|
||||
* argument data object - Weather information received form openweather.org.
|
||||
*/
|
||||
processWeather: function(data) {
|
||||
processWeather: function (data) {
|
||||
this.fetchedLocationName = data.city.name + ", " + data.city.country;
|
||||
|
||||
this.forecast = [];
|
||||
@@ -333,13 +328,12 @@ Module.register("weatherforecast",{
|
||||
var forecastData = {};
|
||||
|
||||
for (var i = 0, count = data.list.length; i < count; i++) {
|
||||
|
||||
var forecast = data.list[i];
|
||||
this.parserDataWeather(forecast); // hack issue #1017
|
||||
|
||||
var day;
|
||||
var hour;
|
||||
if(!!forecast.dt_txt) {
|
||||
if (forecast.dt_txt) {
|
||||
day = moment(forecast.dt_txt, "YYYY-MM-DD hh:mm:ss").format("ddd");
|
||||
hour = moment(forecast.dt_txt, "YYYY-MM-DD hh:mm:ss").format("H");
|
||||
} else {
|
||||
@@ -348,7 +342,7 @@ Module.register("weatherforecast",{
|
||||
}
|
||||
|
||||
if (day !== lastDay) {
|
||||
var forecastData = {
|
||||
forecastData = {
|
||||
day: day,
|
||||
icon: this.config.iconTable[forecast.weather[0].icon],
|
||||
maxTemp: this.roundValue(forecast.temp.max),
|
||||
@@ -378,7 +372,7 @@ Module.register("weatherforecast",{
|
||||
}
|
||||
|
||||
//Log.log(this.forecast);
|
||||
this.show(this.config.animationSpeed, {lockString:this.identifier});
|
||||
this.show(this.config.animationSpeed, { lockString: this.identifier });
|
||||
this.loaded = true;
|
||||
this.updateDom(this.config.animationSpeed);
|
||||
},
|
||||
@@ -388,7 +382,7 @@ Module.register("weatherforecast",{
|
||||
*
|
||||
* argument delay number - Milliseconds before next update. If empty, this.config.updateInterval is used.
|
||||
*/
|
||||
scheduleUpdate: function(delay) {
|
||||
scheduleUpdate: function (delay) {
|
||||
var nextLoad = this.config.updateInterval;
|
||||
if (typeof delay !== "undefined" && delay >= 0) {
|
||||
nextLoad = delay;
|
||||
@@ -396,7 +390,7 @@ Module.register("weatherforecast",{
|
||||
|
||||
var self = this;
|
||||
clearTimeout(this.updateTimer);
|
||||
this.updateTimer = setTimeout(function() {
|
||||
this.updateTimer = setTimeout(function () {
|
||||
self.updateWeather();
|
||||
}, nextLoad);
|
||||
},
|
||||
@@ -405,15 +399,15 @@ Module.register("weatherforecast",{
|
||||
* Converts m2 to beaufort (windspeed).
|
||||
*
|
||||
* see:
|
||||
* http://www.spc.noaa.gov/faq/tornado/beaufort.html
|
||||
* https://www.spc.noaa.gov/faq/tornado/beaufort.html
|
||||
* https://en.wikipedia.org/wiki/Beaufort_scale#Modern_scale
|
||||
*
|
||||
* argument ms number - Windspeed in m/s.
|
||||
*
|
||||
* return number - Windspeed in beaufort.
|
||||
*/
|
||||
ms2Beaufort: function(ms) {
|
||||
var kmh = ms * 60 * 60 / 1000;
|
||||
ms2Beaufort: function (ms) {
|
||||
var kmh = (ms * 60 * 60) / 1000;
|
||||
var speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000];
|
||||
for (var beaufort in speeds) {
|
||||
var speed = speeds[beaufort];
|
||||
@@ -431,7 +425,7 @@ Module.register("weatherforecast",{
|
||||
*
|
||||
* return string - Rounded Temperature.
|
||||
*/
|
||||
roundValue: function(temperature) {
|
||||
roundValue: function (temperature) {
|
||||
var decimals = this.config.roundTemp ? 0 : 1;
|
||||
return parseFloat(temperature).toFixed(decimals);
|
||||
},
|
||||
@@ -443,29 +437,31 @@ Module.register("weatherforecast",{
|
||||
* That object has a property "3h" which contains the amount of rain since the previous forecast in the list.
|
||||
* This code finds all forecasts that is for the same day and sums the amount of rain and returns that.
|
||||
*/
|
||||
processRain: function(forecast, allForecasts) {
|
||||
processRain: function (forecast, allForecasts) {
|
||||
//If the amount of rain actually is a number, return it
|
||||
if (!isNaN(forecast.rain)) {
|
||||
return forecast.rain;
|
||||
}
|
||||
|
||||
//Find all forecasts that is for the same day
|
||||
var checkDateTime = (!!forecast.dt_txt) ? moment(forecast.dt_txt, "YYYY-MM-DD hh:mm:ss") : moment(forecast.dt, "X");
|
||||
var daysForecasts = allForecasts.filter(function(item) {
|
||||
var itemDateTime = (!!item.dt_txt) ? moment(item.dt_txt, "YYYY-MM-DD hh:mm:ss") : moment(item.dt, "X");
|
||||
var checkDateTime = forecast.dt_txt ? moment(forecast.dt_txt, "YYYY-MM-DD hh:mm:ss") : moment(forecast.dt, "X");
|
||||
var daysForecasts = allForecasts.filter(function (item) {
|
||||
var itemDateTime = item.dt_txt ? moment(item.dt_txt, "YYYY-MM-DD hh:mm:ss") : moment(item.dt, "X");
|
||||
return itemDateTime.isSame(checkDateTime, "day") && item.rain instanceof Object;
|
||||
});
|
||||
|
||||
//If no rain this day return undefined so it wont be displayed for this day
|
||||
if (daysForecasts.length == 0) {
|
||||
if (daysForecasts.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
//Summarize all the rain from the matching days
|
||||
return daysForecasts.map(function(item) {
|
||||
return Object.values(item.rain)[0];
|
||||
}).reduce(function(a, b) {
|
||||
return a + b;
|
||||
}, 0);
|
||||
return daysForecasts
|
||||
.map(function (item) {
|
||||
return Object.values(item.rain)[0];
|
||||
})
|
||||
.reduce(function (a, b) {
|
||||
return a + b;
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
|