mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-08-21 04:45:17 +00:00
Release 2.26.0 (#3319)
## [2.26.0] - 01-01-2024 Thanks to: @bnitkin, @bugsounet, @dependabot, @jkriegshauser, @kaennchenstruggle, @KristjanESPERANTO and @Ybbet. Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not all) of the work on this release as project collaborators. This version would not be there without their effort. Thank you guys! You are awesome! This release also marks the latest release by Michael Teeuw. For more info, please read the following post: [A New Chapter for MagicMirror: The Community Takes the Lead](https://forum.magicmirror.builders/topic/18329/a-new-chapter-for-magicmirror-the-community-takes-the-lead). ### Added - Added update notification updater (for 3rd party modules) - Added node 21 to the test matrix - Added transform object to calendar:customEvents - Added ESLint rules for jest (including jest/expect-expect and jest/no-done-callback) ### Removed - Removed Codecov workflow (not working anymore, other workflow required) (#3107) - Removed titleReplace from calendar, replaced + extended by customEvents (backward compatibility included) (#3249) - Removed failing unit test (#3254) - Removed some unused variables ### Updated - Update electron to v27 and update other dependencies as well as github actions - Update newsfeed: Use `html-to-text` instead of regex for transform description - Review ESLint config (#3269) - Updated dependencies - Clock module: optionally display current moon phase in addition to rise/set times - electron is now per default started without gpu, if needed it must be enabled with new env var `ELECTRON_ENABLE_GPU=1` on startup (#3226) - Replace prettier by stylistic in ESLint config to lint JavaScript (and disable some rules for `config/config.js*` files) - Update node-ical to v0.17.1 and fix tests ### Fixed - Avoid fade out/in on updateDom when many calendars are used - Fix the option eventClass on customEvents. - Fix yr API version in locationforecast and sunrise call (#3227) - Fix cloneObject() function to respect RegExp (#3237) - Fix newsfeed module for feeds using "a10:updated" tag (#3238) - Fix issue template (#3167) - Fix #3256 filter out bad results from rrule.between - Fix calendar events sometimes not respecting deleted events (#3250) - Fix electron loadurl locally on Windows when address "0.0.0.0" (#2550) - Fix updatanotification (update_helper.js): catch error if reponse is not an JSON format (check PM2) - Fix missing typeof in calendar module - Fix style issues after prettier update - Fix calendar test (#3291) by moving "Exdate check" from e2e to electron to run on a Thursday - Fix calendar config params `fetchInterval` and `excludedEvents` were never used from single calendar config (#3297) - Fix MM_PORT variable not used in electron and allow full path for MM_CONFIG_FILE variable (#3302) --------- Signed-off-by: naveen <172697+naveensrinivasan@users.noreply.github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Karsten Hassel <hassel@gmx.de> Co-authored-by: Malte Hallström <46646495+SkySails@users.noreply.github.com> Co-authored-by: Veeck <github@veeck.de> Co-authored-by: veeck <michael@veeck.de> Co-authored-by: dWoolridge <dwoolridge@charter.net> Co-authored-by: Johan <jojjepersson@yahoo.se> Co-authored-by: Dario Mratovich <dario_mratovich@hotmail.com> Co-authored-by: Dario Mratovich <dario.mratovich@outlook.com> Co-authored-by: Magnus <34011212+MagMar94@users.noreply.github.com> Co-authored-by: Naveen <172697+naveensrinivasan@users.noreply.github.com> Co-authored-by: buxxi <buxxi@omfilm.net> Co-authored-by: Thomas Hirschberger <47733292+Tom-Hirschberger@users.noreply.github.com> Co-authored-by: Kristjan ESPERANTO <35647502+KristjanESPERANTO@users.noreply.github.com> Co-authored-by: Andrés Vanegas Jiménez <142350+angeldeejay@users.noreply.github.com> Co-authored-by: Dave Child <dave@addedbytes.com> Co-authored-by: grenagit <46225780+grenagit@users.noreply.github.com> Co-authored-by: Grena <grena@grenabox.fr> Co-authored-by: Magnus Marthinsen <magmar@online.no> Co-authored-by: Patrick <psieg@users.noreply.github.com> Co-authored-by: Piotr Rajnisz <56397164+rajniszp@users.noreply.github.com> Co-authored-by: Suthep Yonphimai <tomzt@users.noreply.github.com> Co-authored-by: CarJem Generations (Carter Wallace) <cwallacecs@gmail.com> Co-authored-by: Nicholas Fogal <nfogal.misc@gmail.com> Co-authored-by: JakeBinney <126349119+JakeBinney@users.noreply.github.com> Co-authored-by: OWL4C <124401812+OWL4C@users.noreply.github.com> Co-authored-by: Oscar Björkman <17575446+oscarb@users.noreply.github.com> Co-authored-by: Ismar Slomic <ismar@slomic.no> Co-authored-by: Jørgen Veum-Wahlberg <jorgen.wahlberg@amedia.no> Co-authored-by: Eddie Hung <6740044+eddiehung@users.noreply.github.com> Co-authored-by: Bugsounet - Cédric <github@bugsounet.fr> Co-authored-by: bugsounet <bugsounet@bugsounet.fr> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Knapoc <Knapoc@users.noreply.github.com> Co-authored-by: sam detweiler <sdetweil@gmail.com> Co-authored-by: veeck <michael.veeck@nebenan.de> Co-authored-by: Paranoid93 <6515818+Paranoid93@users.noreply.github.com> Co-authored-by: NolanKingdon <27908974+NolanKingdon@users.noreply.github.com> Co-authored-by: J. Kenzal Hunter <kenzal.hunter@gmail.com> Co-authored-by: Teddy <teddy.payet@gmail.com> Co-authored-by: TeddyStarinvest <teddy.payet@starinvest.com> Co-authored-by: martingron <61826403+martingron@users.noreply.github.com> Co-authored-by: dgoth <132394363+dgoth@users.noreply.github.com> Co-authored-by: kaennchenstruggle <54073894+kaennchenstruggle@users.noreply.github.com> Co-authored-by: jkriegshauser <jkriegshauser@gmail.com> Co-authored-by: Ben Nitkin <ben@nitkin.net>
This commit is contained in:
@@ -134,12 +134,12 @@ const AnimateCSSOut = [
|
||||
* @param {string} [animation] animation name.
|
||||
* @param {number} [animationTime] animation duration.
|
||||
*/
|
||||
function addAnimateCSS(element, animation, animationTime) {
|
||||
function addAnimateCSS (element, animation, animationTime) {
|
||||
const animationName = `animate__${animation}`;
|
||||
const node = document.getElementById(element);
|
||||
if (!node) {
|
||||
// don't execute animate: we don't find div
|
||||
Log.warn(`addAnimateCSS: node not found for`, element);
|
||||
Log.warn("addAnimateCSS: node not found for", element);
|
||||
return;
|
||||
}
|
||||
node.style.setProperty("--animate-duration", `${animationTime}s`);
|
||||
@@ -151,12 +151,12 @@ function addAnimateCSS(element, animation, animationTime) {
|
||||
* @param {string} [element] div element to animate.
|
||||
* @param {string} [animation] animation name.
|
||||
*/
|
||||
function removeAnimateCSS(element, animation) {
|
||||
function removeAnimateCSS (element, animation) {
|
||||
const animationName = `animate__${animation}`;
|
||||
const node = document.getElementById(element);
|
||||
if (!node) {
|
||||
// don't execute animate: we don't find div
|
||||
Log.warn(`removeAnimateCSS: node not found for`, element);
|
||||
Log.warn("removeAnimateCSS: node not found for", element);
|
||||
return;
|
||||
}
|
||||
node.classList.remove("animate__animated", animationName);
|
||||
|
15
js/app.js
15
js/app.js
@@ -12,6 +12,7 @@ const fs = require("fs");
|
||||
const path = require("path");
|
||||
const envsub = require("envsub");
|
||||
const Log = require("logger");
|
||||
|
||||
const Server = require(`${__dirname}/server`);
|
||||
const Utils = require(`${__dirname}/utils`);
|
||||
const defaultModules = require(`${__dirname}/../modules/default/defaultmodules`);
|
||||
@@ -24,7 +25,7 @@ Log.log(`Starting MagicMirror: v${global.version}`);
|
||||
global.root_path = path.resolve(`${__dirname}/../`);
|
||||
|
||||
if (process.env.MM_CONFIG_FILE) {
|
||||
global.configuration_file = process.env.MM_CONFIG_FILE;
|
||||
global.configuration_file = process.env.MM_CONFIG_FILE.replace(`${global.root_path}/`, "");
|
||||
}
|
||||
|
||||
// FIXME: Hotfix Pull Request
|
||||
@@ -46,7 +47,7 @@ process.on("uncaughtException", function (err) {
|
||||
* The core app.
|
||||
* @class
|
||||
*/
|
||||
function App() {
|
||||
function App () {
|
||||
let nodeHelpers = [];
|
||||
let httpServer;
|
||||
|
||||
@@ -55,7 +56,7 @@ function App() {
|
||||
* @async
|
||||
* @returns {Promise<object>} the loaded config or the defaults if something goes wrong
|
||||
*/
|
||||
async function loadConfig() {
|
||||
async function loadConfig () {
|
||||
Log.log("Loading config ...");
|
||||
const defaults = require(`${__dirname}/defaults`);
|
||||
|
||||
@@ -135,7 +136,7 @@ function App() {
|
||||
* if it encounters one option from the deprecated.js list
|
||||
* @param {object} userConfig The user config
|
||||
*/
|
||||
function checkDeprecatedOptions(userConfig) {
|
||||
function checkDeprecatedOptions (userConfig) {
|
||||
const deprecated = require(`${global.root_path}/js/deprecated`);
|
||||
const deprecatedOptions = deprecated.configs;
|
||||
|
||||
@@ -149,7 +150,7 @@ function App() {
|
||||
* Loads a specific module.
|
||||
* @param {string} module The name of the module (including subpath).
|
||||
*/
|
||||
function loadModule(module) {
|
||||
function loadModule (module) {
|
||||
const elements = module.split("/");
|
||||
const moduleName = elements[elements.length - 1];
|
||||
let moduleFolder = `${__dirname}/../modules/${module}`;
|
||||
@@ -203,7 +204,7 @@ function App() {
|
||||
* @param {Module[]} modules All modules to be loaded
|
||||
* @returns {Promise} A promise that is resolved when all modules been loaded
|
||||
*/
|
||||
async function loadModules(modules) {
|
||||
async function loadModules (modules) {
|
||||
Log.log("Loading module helpers ...");
|
||||
|
||||
for (let module of modules) {
|
||||
@@ -220,7 +221,7 @@ function App() {
|
||||
* @returns {number} A positive number if a is larger than b, a negative
|
||||
* number if a is smaller and 0 if they are the same
|
||||
*/
|
||||
function cmpVersions(a, b) {
|
||||
function cmpVersions (a, b) {
|
||||
let i, diff;
|
||||
const regExStrip0 = /(\.0+)+$/;
|
||||
const segmentsA = a.replace(regExStrip0, "").split(".");
|
||||
|
@@ -20,7 +20,7 @@ const Utils = require(`${rootPath}/js/utils.js`);
|
||||
* Check if set by environment variable MM_CONFIG_FILE
|
||||
* @returns {string} path and filename of the config file
|
||||
*/
|
||||
function getConfigFile() {
|
||||
function getConfigFile () {
|
||||
// FIXME: This function should be in core. Do you want refactor me ;) ?, be good!
|
||||
return path.resolve(process.env.MM_CONFIG_FILE || `${rootPath}/config/config.js`);
|
||||
}
|
||||
@@ -28,7 +28,7 @@ function getConfigFile() {
|
||||
/**
|
||||
* Checks the config file using eslint.
|
||||
*/
|
||||
function checkConfigFile() {
|
||||
function checkConfigFile () {
|
||||
const configFileName = getConfigFile();
|
||||
|
||||
// Check if file is present
|
||||
|
40
js/class.js
40
js/class.js
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
(function () {
|
||||
let initializing = false;
|
||||
const fnTest = /xyz/.test(function () {
|
||||
const fnTest = (/xyz/).test(function () {
|
||||
xyz;
|
||||
})
|
||||
? /\b_super\b/
|
||||
@@ -36,31 +36,31 @@
|
||||
// Copy the properties over onto the new prototype
|
||||
for (const name in prop) {
|
||||
// Check if we're overwriting an existing function
|
||||
prototype[name] =
|
||||
typeof prop[name] === "function" && typeof _super[name] === "function" && fnTest.test(prop[name])
|
||||
prototype[name]
|
||||
= typeof prop[name] === "function" && typeof _super[name] === "function" && fnTest.test(prop[name])
|
||||
? (function (name, fn) {
|
||||
return function () {
|
||||
const tmp = this._super;
|
||||
return function () {
|
||||
const tmp = this._super;
|
||||
|
||||
// Add a new ._super() method that is the same method
|
||||
// but on the super-class
|
||||
this._super = _super[name];
|
||||
// Add a new ._super() method that is the same method
|
||||
// but on the super-class
|
||||
this._super = _super[name];
|
||||
|
||||
// The method only need to be bound temporarily, so we
|
||||
// remove it when we're done executing
|
||||
const ret = fn.apply(this, arguments);
|
||||
this._super = tmp;
|
||||
// The method only need to be bound temporarily, so we
|
||||
// remove it when we're done executing
|
||||
const ret = fn.apply(this, arguments);
|
||||
this._super = tmp;
|
||||
|
||||
return ret;
|
||||
};
|
||||
})(name, prop[name])
|
||||
return ret;
|
||||
};
|
||||
}(name, prop[name]))
|
||||
: prop[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* The dummy class constructor
|
||||
*/
|
||||
function Class() {
|
||||
function Class () {
|
||||
// All construction is actually done in the init method
|
||||
if (!initializing && this.init) {
|
||||
this.init.apply(this, arguments);
|
||||
@@ -78,18 +78,22 @@
|
||||
|
||||
return Class;
|
||||
};
|
||||
})();
|
||||
}());
|
||||
|
||||
/**
|
||||
* Define the clone method for later use. Helper Method.
|
||||
* @param {object} obj Object to be cloned
|
||||
* @returns {object} the cloned object
|
||||
*/
|
||||
function cloneObject(obj) {
|
||||
function cloneObject (obj) {
|
||||
if (obj === null || typeof obj !== "object") {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (obj.constructor.name === "RegExp") {
|
||||
return new RegExp(obj);
|
||||
}
|
||||
|
||||
const temp = obj.constructor(); // give temp the original obj's constructor
|
||||
for (const key in obj) {
|
||||
temp[key] = cloneObject(obj[key]);
|
||||
|
@@ -8,9 +8,10 @@ const Log = require("./logger");
|
||||
let config = process.env.config ? JSON.parse(process.env.config) : {};
|
||||
// Module to control application life.
|
||||
const app = electron.app;
|
||||
// If ELECTRON_DISABLE_GPU is set electron is started with --disable-gpu flag.
|
||||
// Per default electron is started with --disable-gpu flag, if you want the gpu enabled,
|
||||
// you must set the env var ELECTRON_ENABLE_GPU=1 on startup.
|
||||
// See https://www.electronjs.org/docs/latest/tutorial/offscreen-rendering for more info.
|
||||
if (process.env.ELECTRON_DISABLE_GPU !== undefined) {
|
||||
if (process.env.ELECTRON_ENABLE_GPU !== "1") {
|
||||
app.disableHardwareAcceleration();
|
||||
}
|
||||
|
||||
@@ -24,7 +25,7 @@ let mainWindow;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function createWindow() {
|
||||
function createWindow () {
|
||||
// see https://www.electronjs.org/docs/latest/api/screen
|
||||
// Create a window that fills the screen's available work area.
|
||||
let electronSize = (800, 600);
|
||||
@@ -77,8 +78,9 @@ function createWindow() {
|
||||
prefix = "http://";
|
||||
}
|
||||
|
||||
let address = (config.address === void 0) | (config.address === "") ? (config.address = "localhost") : config.address;
|
||||
mainWindow.loadURL(`${prefix}${address}:${config.port}`);
|
||||
let address = (config.address === void 0) | (config.address === "") | (config.address === "0.0.0.0") ? (config.address = "localhost") : config.address;
|
||||
const port = process.env.MM_PORT || config.port;
|
||||
mainWindow.loadURL(`${prefix}${address}:${port}`);
|
||||
|
||||
// Open the DevTools if run with "npm start dev"
|
||||
if (process.argv.includes("dev")) {
|
||||
@@ -120,11 +122,11 @@ function createWindow() {
|
||||
mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => {
|
||||
let curHeaders = details.responseHeaders;
|
||||
if (config["ignoreXOriginHeader"] || false) {
|
||||
curHeaders = Object.fromEntries(Object.entries(curHeaders).filter((header) => !/x-frame-options/i.test(header[0])));
|
||||
curHeaders = Object.fromEntries(Object.entries(curHeaders).filter((header) => !(/x-frame-options/i).test(header[0])));
|
||||
}
|
||||
|
||||
if (config["ignoreContentSecurityPolicy"] || false) {
|
||||
curHeaders = Object.fromEntries(Object.entries(curHeaders).filter((header) => !/content-security-policy/i.test(header[0])));
|
||||
curHeaders = Object.fromEntries(Object.entries(curHeaders).filter((header) => !(/content-security-policy/i).test(header[0])));
|
||||
}
|
||||
|
||||
callback({ responseHeaders: curHeaders });
|
||||
|
@@ -7,6 +7,7 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const Loader = (function () {
|
||||
|
||||
/* Create helper variables */
|
||||
|
||||
const loadedModuleFiles = [];
|
||||
@@ -196,10 +197,11 @@ const Loader = (function () {
|
||||
|
||||
/* Public Methods */
|
||||
return {
|
||||
|
||||
/**
|
||||
* Load all modules as defined in the config.
|
||||
*/
|
||||
loadModules: async function () {
|
||||
async loadModules () {
|
||||
let moduleData = getModuleData();
|
||||
|
||||
/**
|
||||
@@ -230,7 +232,7 @@ const Loader = (function () {
|
||||
* @param {Module} module The module that calls the loadFile function.
|
||||
* @returns {Promise} resolved when the file is loaded
|
||||
*/
|
||||
loadFileForModule: async function (fileName, module) {
|
||||
async loadFileForModule (fileName, module) {
|
||||
if (loadedFiles.indexOf(fileName.toLowerCase()) !== -1) {
|
||||
Log.log(`File already loaded: ${fileName}`);
|
||||
return;
|
||||
@@ -256,4 +258,4 @@ const Loader = (function () {
|
||||
return loadFile(module.file(fileName));
|
||||
}
|
||||
};
|
||||
})();
|
||||
}());
|
||||
|
28
js/logger.js
28
js/logger.js
@@ -22,7 +22,7 @@
|
||||
// Browser globals (root is window)
|
||||
root.Log = factory(root.config);
|
||||
}
|
||||
})(this, function (config) {
|
||||
}(this, function (config) {
|
||||
let logLevel;
|
||||
let enableLog;
|
||||
if (typeof exports === "object") {
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
logLevel.setLogLevel = function (newLevel) {
|
||||
if (newLevel) {
|
||||
Object.keys(logLevel).forEach(function (key, index) {
|
||||
Object.keys(logLevel).forEach(function (key) {
|
||||
if (!newLevel.includes(key.toLocaleUpperCase())) {
|
||||
logLevel[key] = function () {};
|
||||
}
|
||||
@@ -59,21 +59,21 @@
|
||||
};
|
||||
} else {
|
||||
logLevel = {
|
||||
debug: function () {},
|
||||
log: function () {},
|
||||
info: function () {},
|
||||
warn: function () {},
|
||||
error: function () {},
|
||||
group: function () {},
|
||||
groupCollapsed: function () {},
|
||||
groupEnd: function () {},
|
||||
time: function () {},
|
||||
timeEnd: function () {},
|
||||
timeStamp: function () {}
|
||||
debug () {},
|
||||
log () {},
|
||||
info () {},
|
||||
warn () {},
|
||||
error () {},
|
||||
group () {},
|
||||
groupCollapsed () {},
|
||||
groupEnd () {},
|
||||
time () {},
|
||||
timeEnd () {},
|
||||
timeStamp () {}
|
||||
};
|
||||
|
||||
logLevel.setLogLevel = function () {};
|
||||
}
|
||||
|
||||
return logLevel;
|
||||
});
|
||||
}));
|
||||
|
22
js/main.js
22
js/main.js
@@ -479,7 +479,6 @@ const MM = (function () {
|
||||
*/
|
||||
const loadConfig = function () {
|
||||
// FIXME: Think about how to pass config around without breaking tests
|
||||
/* eslint-disable */
|
||||
if (typeof config === "undefined") {
|
||||
config = defaults;
|
||||
Log.error("Config file is missing! Please create a config file.");
|
||||
@@ -487,7 +486,6 @@ const MM = (function () {
|
||||
}
|
||||
|
||||
config = Object.assign({}, defaults, config);
|
||||
/* eslint-enable */
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -495,6 +493,7 @@ const MM = (function () {
|
||||
* @param {Module[]} modules Array of modules.
|
||||
*/
|
||||
const setSelectionMethodsForModules = function (modules) {
|
||||
|
||||
/**
|
||||
* Filter modules with the specified classes.
|
||||
* @param {string|string[]} className one or multiple classnames (array or space divided).
|
||||
@@ -580,12 +579,13 @@ const MM = (function () {
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
/* Public Methods */
|
||||
|
||||
/**
|
||||
* Main init method.
|
||||
*/
|
||||
init: async function () {
|
||||
async init () {
|
||||
Log.info("Initializing MagicMirror².");
|
||||
loadConfig();
|
||||
|
||||
@@ -599,7 +599,7 @@ const MM = (function () {
|
||||
* Gets called when all modules are started.
|
||||
* @param {Module[]} moduleObjects All module instances.
|
||||
*/
|
||||
modulesStarted: function (moduleObjects) {
|
||||
modulesStarted (moduleObjects) {
|
||||
modules = [];
|
||||
let startUp = "";
|
||||
|
||||
@@ -636,7 +636,7 @@ const MM = (function () {
|
||||
* @param {*} payload The payload of the notification.
|
||||
* @param {Module} sender The module that sent the notification.
|
||||
*/
|
||||
sendNotification: function (notification, payload, sender) {
|
||||
sendNotification (notification, payload, sender) {
|
||||
if (arguments.length < 3) {
|
||||
Log.error("sendNotification: Missing arguments.");
|
||||
return;
|
||||
@@ -661,7 +661,7 @@ const MM = (function () {
|
||||
* @param {Module} module The module that needs an update.
|
||||
* @param {object|number} [updateOptions] The (optional) number of microseconds for the animation or object with updateOptions (speed/animates)
|
||||
*/
|
||||
updateDom: function (module, updateOptions) {
|
||||
updateDom (module, updateOptions) {
|
||||
if (!(module instanceof Module)) {
|
||||
Log.error("updateDom: Sender should be a module.");
|
||||
return;
|
||||
@@ -680,7 +680,7 @@ const MM = (function () {
|
||||
* Returns a collection of all modules currently active.
|
||||
* @returns {Module[]} A collection of all modules currently active.
|
||||
*/
|
||||
getModules: function () {
|
||||
getModules () {
|
||||
setSelectionMethodsForModules(modules);
|
||||
return modules;
|
||||
},
|
||||
@@ -692,7 +692,7 @@ const MM = (function () {
|
||||
* @param {Function} callback Called when the animation is done.
|
||||
* @param {object} [options] Optional settings for the hide method.
|
||||
*/
|
||||
hideModule: function (module, speed, callback, options) {
|
||||
hideModule (module, speed, callback, options) {
|
||||
module.hidden = true;
|
||||
hideModule(module, speed, callback, options);
|
||||
},
|
||||
@@ -704,12 +704,12 @@ const MM = (function () {
|
||||
* @param {Function} callback Called when the animation is done.
|
||||
* @param {object} [options] Optional settings for the show method.
|
||||
*/
|
||||
showModule: function (module, speed, callback, options) {
|
||||
showModule (module, speed, callback, options) {
|
||||
// do not change module.hidden yet, only if we really show it later
|
||||
showModule(module, speed, callback, options);
|
||||
}
|
||||
};
|
||||
})();
|
||||
}());
|
||||
|
||||
// Add polyfill for Object.assign.
|
||||
if (typeof Object.assign !== "function") {
|
||||
@@ -732,7 +732,7 @@ if (typeof Object.assign !== "function") {
|
||||
}
|
||||
return output;
|
||||
};
|
||||
})();
|
||||
}());
|
||||
}
|
||||
|
||||
MM.init();
|
||||
|
63
js/module.js
63
js/module.js
@@ -8,6 +8,7 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const Module = Class.extend({
|
||||
|
||||
/*********************************************************
|
||||
* All methods (and properties) below can be subclassed. *
|
||||
*********************************************************/
|
||||
@@ -33,14 +34,14 @@ const Module = Class.extend({
|
||||
/**
|
||||
* Called when the module is instantiated.
|
||||
*/
|
||||
init: function () {
|
||||
init () {
|
||||
//Log.log(this.defaults);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the module is started.
|
||||
*/
|
||||
start: async function () {
|
||||
async start () {
|
||||
Log.info(`Starting module: ${this.name}`);
|
||||
},
|
||||
|
||||
@@ -48,7 +49,7 @@ const Module = Class.extend({
|
||||
* Returns a list of scripts the module requires to be loaded.
|
||||
* @returns {string[]} An array with filenames.
|
||||
*/
|
||||
getScripts: function () {
|
||||
getScripts () {
|
||||
return [];
|
||||
},
|
||||
|
||||
@@ -56,7 +57,7 @@ const Module = Class.extend({
|
||||
* Returns a list of stylesheets the module requires to be loaded.
|
||||
* @returns {string[]} An array with filenames.
|
||||
*/
|
||||
getStyles: function () {
|
||||
getStyles () {
|
||||
return [];
|
||||
},
|
||||
|
||||
@@ -66,7 +67,7 @@ const Module = Class.extend({
|
||||
* return Map<String, String> -
|
||||
* @returns {*} A map with langKeys and filenames.
|
||||
*/
|
||||
getTranslations: function () {
|
||||
getTranslations () {
|
||||
return false;
|
||||
},
|
||||
|
||||
@@ -76,14 +77,14 @@ const Module = Class.extend({
|
||||
* Alternatively, the getTemplate method could be subclassed.
|
||||
* @returns {HTMLElement|Promise} The dom or a promise with the dom to display.
|
||||
*/
|
||||
getDom: function () {
|
||||
getDom () {
|
||||
return new Promise((resolve) => {
|
||||
const div = document.createElement("div");
|
||||
const template = this.getTemplate();
|
||||
const templateData = this.getTemplateData();
|
||||
|
||||
// Check to see if we need to render a template string or a file.
|
||||
if (/^.*((\.html)|(\.njk))$/.test(template)) {
|
||||
if ((/^.*((\.html)|(\.njk))$/).test(template)) {
|
||||
// the template is a filename
|
||||
this.nunjucksEnvironment().render(template, templateData, function (err, res) {
|
||||
if (err) {
|
||||
@@ -109,7 +110,7 @@ const Module = Class.extend({
|
||||
* This method needs to be subclassed if the module wants to display modified headers on the mirror.
|
||||
* @returns {string} The header to display above the header.
|
||||
*/
|
||||
getHeader: function () {
|
||||
getHeader () {
|
||||
return this.data.header;
|
||||
},
|
||||
|
||||
@@ -120,7 +121,7 @@ const Module = Class.extend({
|
||||
* If the string ends with '.html' it's considered a file from within the module's folder.
|
||||
* @returns {string} The template string of filename.
|
||||
*/
|
||||
getTemplate: function () {
|
||||
getTemplate () {
|
||||
return `<div class="normal">${this.name}</div><div class="small dimmed">${this.identifier}</div>`;
|
||||
},
|
||||
|
||||
@@ -129,7 +130,7 @@ const Module = Class.extend({
|
||||
* This method needs to be subclassed if the module wants to use a custom data.
|
||||
* @returns {object} The data for the template
|
||||
*/
|
||||
getTemplateData: function () {
|
||||
getTemplateData () {
|
||||
return {};
|
||||
},
|
||||
|
||||
@@ -139,7 +140,7 @@ const Module = Class.extend({
|
||||
* @param {*} payload The payload of the notification.
|
||||
* @param {Module} sender The module that sent the notification.
|
||||
*/
|
||||
notificationReceived: function (notification, payload, sender) {
|
||||
notificationReceived (notification, payload, sender) {
|
||||
if (sender) {
|
||||
// Log.log(this.name + " received a module notification: " + notification + " from sender: " + sender.name);
|
||||
} else {
|
||||
@@ -152,7 +153,7 @@ const Module = Class.extend({
|
||||
* The environment is checked in the _nunjucksEnvironment instance variable.
|
||||
* @returns {object} The Nunjucks Environment
|
||||
*/
|
||||
nunjucksEnvironment: function () {
|
||||
nunjucksEnvironment () {
|
||||
if (this._nunjucksEnvironment !== null) {
|
||||
return this._nunjucksEnvironment;
|
||||
}
|
||||
@@ -174,21 +175,21 @@ const Module = Class.extend({
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
*/
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
socketNotificationReceived (notification, payload) {
|
||||
Log.log(`${this.name} received a socket notification: ${notification} - Payload: ${payload}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the module is hidden.
|
||||
*/
|
||||
suspend: function () {
|
||||
suspend () {
|
||||
Log.log(`${this.name} is suspended.`);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the module is shown.
|
||||
*/
|
||||
resume: function () {
|
||||
resume () {
|
||||
Log.log(`${this.name} is resumed.`);
|
||||
},
|
||||
|
||||
@@ -200,7 +201,7 @@ const Module = Class.extend({
|
||||
* Set the module data.
|
||||
* @param {object} data The module data
|
||||
*/
|
||||
setData: function (data) {
|
||||
setData (data) {
|
||||
this.data = data;
|
||||
this.name = data.name;
|
||||
this.identifier = data.identifier;
|
||||
@@ -216,7 +217,7 @@ const Module = Class.extend({
|
||||
* @param {object} config The combined module config.
|
||||
* @param {boolean} deep Merge module config in deep.
|
||||
*/
|
||||
setConfig: function (config, deep) {
|
||||
setConfig (config, deep) {
|
||||
this.config = deep ? configMerge({}, this.defaults, config) : Object.assign({}, this.defaults, config);
|
||||
},
|
||||
|
||||
@@ -225,7 +226,7 @@ const Module = Class.extend({
|
||||
* It also registers the notification callback.
|
||||
* @returns {MMSocket} a socket object
|
||||
*/
|
||||
socket: function () {
|
||||
socket () {
|
||||
if (typeof this._socket === "undefined") {
|
||||
this._socket = new MMSocket(this.name);
|
||||
}
|
||||
@@ -242,7 +243,7 @@ const Module = Class.extend({
|
||||
* @param {string} file Filename
|
||||
* @returns {string} the file path
|
||||
*/
|
||||
file: function (file) {
|
||||
file (file) {
|
||||
return `${this.data.path}/${file}`.replace("//", "/");
|
||||
},
|
||||
|
||||
@@ -250,7 +251,7 @@ const Module = Class.extend({
|
||||
* Load all required stylesheets by requesting the MM object to load the files.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
loadStyles: function () {
|
||||
loadStyles () {
|
||||
return this.loadDependencies("getStyles");
|
||||
},
|
||||
|
||||
@@ -258,7 +259,7 @@ const Module = Class.extend({
|
||||
* Load all required scripts by requesting the MM object to load the files.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
loadScripts: function () {
|
||||
loadScripts () {
|
||||
return this.loadDependencies("getScripts");
|
||||
},
|
||||
|
||||
@@ -267,7 +268,7 @@ const Module = Class.extend({
|
||||
* @param {string} funcName Function name to call to get scripts or styles.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
loadDependencies: async function (funcName) {
|
||||
async loadDependencies (funcName) {
|
||||
let dependencies = this[funcName]();
|
||||
|
||||
const loadNextDependency = async () => {
|
||||
@@ -288,7 +289,7 @@ const Module = Class.extend({
|
||||
* Load all translations.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
loadTranslations: async function () {
|
||||
async loadTranslations () {
|
||||
const translations = this.getTranslations() || {};
|
||||
const language = config.language.toLowerCase();
|
||||
|
||||
@@ -320,7 +321,7 @@ const Module = Class.extend({
|
||||
* @param {string} [defaultValue] The default value with variables.
|
||||
* @returns {string} the translated key
|
||||
*/
|
||||
translate: function (key, defaultValueOrVariables, defaultValue) {
|
||||
translate (key, defaultValueOrVariables, defaultValue) {
|
||||
if (typeof defaultValueOrVariables === "object") {
|
||||
return Translator.translate(this, key, defaultValueOrVariables) || defaultValue || "";
|
||||
}
|
||||
@@ -331,7 +332,7 @@ const Module = Class.extend({
|
||||
* Request an (animated) update of the module.
|
||||
* @param {number|object} [updateOptions] The speed of the animation or object with for updateOptions (speed/animates)
|
||||
*/
|
||||
updateDom: function (updateOptions) {
|
||||
updateDom (updateOptions) {
|
||||
MM.updateDom(this, updateOptions);
|
||||
},
|
||||
|
||||
@@ -340,7 +341,7 @@ const Module = Class.extend({
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
*/
|
||||
sendNotification: function (notification, payload) {
|
||||
sendNotification (notification, payload) {
|
||||
MM.sendNotification(notification, payload, this);
|
||||
},
|
||||
|
||||
@@ -349,7 +350,7 @@ const Module = Class.extend({
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
*/
|
||||
sendSocketNotification: function (notification, payload) {
|
||||
sendSocketNotification (notification, payload) {
|
||||
this.socket().sendNotification(notification, payload);
|
||||
},
|
||||
|
||||
@@ -359,7 +360,7 @@ const Module = Class.extend({
|
||||
* @param {Function} callback Called when the animation is done.
|
||||
* @param {object} [options] Optional settings for the hide method.
|
||||
*/
|
||||
hide: function (speed, callback, options = {}) {
|
||||
hide (speed, callback, options = {}) {
|
||||
let usedCallback = callback || function () {};
|
||||
let usedOptions = options;
|
||||
|
||||
@@ -386,7 +387,7 @@ const Module = Class.extend({
|
||||
* @param {Function} callback Called when the animation is done.
|
||||
* @param {object} [options] Optional settings for the show method.
|
||||
*/
|
||||
show: function (speed, callback, options) {
|
||||
show (speed, callback, options) {
|
||||
let usedCallback = callback || function () {};
|
||||
let usedOptions = options;
|
||||
|
||||
@@ -430,7 +431,7 @@ const Module = Class.extend({
|
||||
* @param {object} result the initial object
|
||||
* @returns {object} the merged config
|
||||
*/
|
||||
function configMerge(result) {
|
||||
function configMerge (result) {
|
||||
const stack = Array.prototype.slice.call(arguments, 1);
|
||||
let item, key;
|
||||
|
||||
@@ -493,7 +494,7 @@ window.Module = Module;
|
||||
* @returns {number} A positive number if a is larger than b, a negative
|
||||
* number if a is smaller and 0 if they are the same
|
||||
*/
|
||||
function cmpVersions(a, b) {
|
||||
function cmpVersions (a, b) {
|
||||
const regExStrip0 = /(\.0+)+$/;
|
||||
const segmentsA = a.replace(regExStrip0, "").split(".");
|
||||
const segmentsB = b.replace(regExStrip0, "").split(".");
|
||||
|
@@ -9,15 +9,15 @@ const Log = require("logger");
|
||||
const Class = require("./class");
|
||||
|
||||
const NodeHelper = Class.extend({
|
||||
init() {
|
||||
init () {
|
||||
Log.log("Initializing new module helper ...");
|
||||
},
|
||||
|
||||
loaded() {
|
||||
loaded () {
|
||||
Log.log(`Module helper loaded: ${this.name}`);
|
||||
},
|
||||
|
||||
start() {
|
||||
start () {
|
||||
Log.log(`Starting module helper: ${this.name}`);
|
||||
},
|
||||
|
||||
@@ -26,7 +26,7 @@ const NodeHelper = Class.extend({
|
||||
* Close any open connections, stop any sub-processes and
|
||||
* gracefully exit the module.
|
||||
*/
|
||||
stop() {
|
||||
stop () {
|
||||
Log.log(`Stopping module helper: ${this.name}`);
|
||||
},
|
||||
|
||||
@@ -35,7 +35,7 @@ const NodeHelper = Class.extend({
|
||||
* @param {string} notification The identifier of the notification.
|
||||
* @param {*} payload The payload of the notification.
|
||||
*/
|
||||
socketNotificationReceived(notification, payload) {
|
||||
socketNotificationReceived (notification, payload) {
|
||||
Log.log(`${this.name} received a socket notification: ${notification} - Payload: ${payload}`);
|
||||
},
|
||||
|
||||
@@ -43,7 +43,7 @@ const NodeHelper = Class.extend({
|
||||
* Set the module name.
|
||||
* @param {string} name Module name.
|
||||
*/
|
||||
setName(name) {
|
||||
setName (name) {
|
||||
this.name = name;
|
||||
},
|
||||
|
||||
@@ -51,7 +51,7 @@ const NodeHelper = Class.extend({
|
||||
* Set the module path.
|
||||
* @param {string} path Module path.
|
||||
*/
|
||||
setPath(path) {
|
||||
setPath (path) {
|
||||
this.path = path;
|
||||
},
|
||||
|
||||
@@ -61,7 +61,7 @@ const NodeHelper = Class.extend({
|
||||
* argument notification string - The identifier of the notification.
|
||||
* argument payload mixed - The payload of the notification.
|
||||
*/
|
||||
sendSocketNotification(notification, payload) {
|
||||
sendSocketNotification (notification, payload) {
|
||||
this.io.of(this.name).emit(notification, payload);
|
||||
},
|
||||
|
||||
@@ -71,7 +71,7 @@ const NodeHelper = Class.extend({
|
||||
*
|
||||
* argument app Express app - The Express app object.
|
||||
*/
|
||||
setExpressApp(app) {
|
||||
setExpressApp (app) {
|
||||
this.expressApp = app;
|
||||
|
||||
app.use(`/${this.name}`, express.static(`${this.path}/public`));
|
||||
@@ -83,7 +83,7 @@ const NodeHelper = Class.extend({
|
||||
*
|
||||
* argument io Socket.io - The Socket io object.
|
||||
*/
|
||||
setSocketIO(io) {
|
||||
setSocketIO (io) {
|
||||
this.io = io;
|
||||
|
||||
Log.log(`Connecting socket for: ${this.name}`);
|
||||
|
@@ -22,7 +22,7 @@ const { cors, getConfig, getHtml, getVersion, getStartup } = require("./server_f
|
||||
* @param {object} config The MM config
|
||||
* @class
|
||||
*/
|
||||
function Server(config) {
|
||||
function Server (config) {
|
||||
const app = express();
|
||||
const port = process.env.MM_PORT || config.port;
|
||||
const serverSockets = new Set();
|
||||
|
@@ -1,6 +1,7 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const Log = require("logger");
|
||||
|
||||
const startUp = new Date();
|
||||
|
||||
/**
|
||||
@@ -8,7 +9,7 @@ const startUp = new Date();
|
||||
* @param {Request} req - the request
|
||||
* @param {Response} res - the result
|
||||
*/
|
||||
function getConfig(req, res) {
|
||||
function getConfig (req, res) {
|
||||
res.send(config);
|
||||
}
|
||||
|
||||
@@ -17,7 +18,7 @@ function getConfig(req, res) {
|
||||
* @param {Request} req - the request
|
||||
* @param {Response} res - the result
|
||||
*/
|
||||
function getStartup(req, res) {
|
||||
function getStartup (req, res) {
|
||||
res.send(startUp);
|
||||
}
|
||||
|
||||
@@ -30,7 +31,7 @@ function getStartup(req, res) {
|
||||
* @param {Request} req - the request
|
||||
* @param {Response} res - the result
|
||||
*/
|
||||
async function cors(req, res) {
|
||||
async function cors (req, res) {
|
||||
try {
|
||||
const urlRegEx = "url=(.+?)$";
|
||||
let url;
|
||||
@@ -67,7 +68,7 @@ async function cors(req, res) {
|
||||
* @param {string} url - The url containing the headers and values to send.
|
||||
* @returns {object} An object specifying name and value of the headers.
|
||||
*/
|
||||
function getHeadersToSend(url) {
|
||||
function getHeadersToSend (url) {
|
||||
const headersToSend = { "User-Agent": `Mozilla/5.0 MagicMirror/${global.version}` };
|
||||
const headersToSendMatch = new RegExp("sendheaders=(.+?)(&|$)", "g").exec(url);
|
||||
if (headersToSendMatch) {
|
||||
@@ -88,7 +89,7 @@ function getHeadersToSend(url) {
|
||||
* @param {string} url - The url containing the expected headers from the response.
|
||||
* @returns {string[]} headers - The name of the expected headers.
|
||||
*/
|
||||
function geExpectedRecievedHeaders(url) {
|
||||
function geExpectedRecievedHeaders (url) {
|
||||
const expectedRecievedHeaders = ["Content-Type"];
|
||||
const expectedRecievedHeadersMatch = new RegExp("expectedheaders=(.+?)(&|$)", "g").exec(url);
|
||||
if (expectedRecievedHeadersMatch) {
|
||||
@@ -105,7 +106,7 @@ function geExpectedRecievedHeaders(url) {
|
||||
* @param {Request} req - the request
|
||||
* @param {Response} res - the result
|
||||
*/
|
||||
function getHtml(req, res) {
|
||||
function getHtml (req, res) {
|
||||
let html = fs.readFileSync(path.resolve(`${global.root_path}/index.html`), { encoding: "utf8" });
|
||||
html = html.replace("#VERSION#", global.version);
|
||||
|
||||
@@ -123,7 +124,7 @@ function getHtml(req, res) {
|
||||
* @param {Request} req - the request
|
||||
* @param {Response} res - the result
|
||||
*/
|
||||
function getVersion(req, res) {
|
||||
function getVersion (req, res) {
|
||||
res.send(global.version);
|
||||
}
|
||||
|
||||
|
@@ -7,12 +7,13 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
const Translator = (function () {
|
||||
|
||||
/**
|
||||
* Load a JSON file via XHR.
|
||||
* @param {string} file Path of the file we want to load.
|
||||
* @returns {Promise<object>} the translations in the specified file
|
||||
*/
|
||||
async function loadJSON(file) {
|
||||
async function loadJSON (file) {
|
||||
const xhr = new XMLHttpRequest();
|
||||
return new Promise(function (resolve) {
|
||||
xhr.overrideMimeType("application/json");
|
||||
@@ -47,7 +48,8 @@ const Translator = (function () {
|
||||
* @param {object} variables The variables to use within the translation template (optional)
|
||||
* @returns {string} the translated key
|
||||
*/
|
||||
translate: function (module, key, variables = {}) {
|
||||
translate (module, key, variables = {}) {
|
||||
|
||||
/**
|
||||
* Combines template and variables like:
|
||||
* template: "Please wait for {timeToWait} before continuing with {work}."
|
||||
@@ -57,7 +59,7 @@ const Translator = (function () {
|
||||
* @param {object} variables Variables for the placeholder
|
||||
* @returns {string} the template filled with the variables
|
||||
*/
|
||||
function createStringFromTemplate(template, variables) {
|
||||
function createStringFromTemplate (template, variables) {
|
||||
if (Object.prototype.toString.call(template) !== "[object String]") {
|
||||
return template;
|
||||
}
|
||||
@@ -99,7 +101,7 @@ const Translator = (function () {
|
||||
* @param {string} file Path of the file we want to load.
|
||||
* @param {boolean} isFallback Flag to indicate fallback translations.
|
||||
*/
|
||||
async load(module, file, isFallback) {
|
||||
async load (module, file, isFallback) {
|
||||
Log.log(`${module.name} - Load translation${isFallback ? " fallback" : ""}: ${file}`);
|
||||
|
||||
if (this.translationsFallback[module.name]) {
|
||||
@@ -115,7 +117,7 @@ const Translator = (function () {
|
||||
* Load the core translations.
|
||||
* @param {string} lang The language identifier of the core language.
|
||||
*/
|
||||
loadCoreTranslations: async function (lang) {
|
||||
async loadCoreTranslations (lang) {
|
||||
if (lang in translations) {
|
||||
Log.log(`Loading core translation file: ${translations[lang]}`);
|
||||
this.coreTranslations = await loadJSON(translations[lang]);
|
||||
@@ -130,7 +132,7 @@ const Translator = (function () {
|
||||
* Load the core translations' fallback.
|
||||
* The first language defined in translations.js will be used.
|
||||
*/
|
||||
loadCoreTranslationsFallback: async function () {
|
||||
async loadCoreTranslationsFallback () {
|
||||
let first = Object.keys(translations)[0];
|
||||
if (first) {
|
||||
Log.log(`Loading core translation fallback file: ${translations[first]}`);
|
||||
@@ -138,6 +140,6 @@ const Translator = (function () {
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
}());
|
||||
|
||||
window.Translator = Translator;
|
||||
|
Reference in New Issue
Block a user