Replace prettier by stylistic to lint JavaScript (#3303)

In the latest versions of ESLint, more and more formatting rules were
removed or declared deprecated. These rules have been integrated into
the new Stylistic package (https://eslint.style/guide/why) and expanded.

Stylistic acts as a better formatter  for JavaScript as Prettier.

With this PR there are many changes that make the code more uniform, but
it may be difficult to review due to the large amount. Even if I have no
worries about the changes, perhaps this would be something for the
release after next.

Let me know what you think.
This commit is contained in:
Kristjan ESPERANTO 2023-12-25 08:17:11 +01:00 committed by GitHub
parent 4e7b68a69d
commit 0b70274a1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 954 additions and 942 deletions

View File

@ -1,9 +1,9 @@
{ {
"extends": ["eslint:recommended", "plugin:import/recommended", "plugin:jest/recommended", "plugin:jsdoc/recommended", "plugin:prettier/recommended"], "extends": ["eslint:recommended", "plugin:@stylistic/all-extends", "plugin:import/recommended", "plugin:jest/recommended", "plugin:jsdoc/recommended"],
"plugins": [], "plugins": [],
"env": { "env": {
"browser": true, "browser": true,
"es2022": true, "es2023": true,
"jest/globals": true, "jest/globals": true,
"node": true "node": true
}, },
@ -16,7 +16,7 @@
}, },
"parserOptions": { "parserOptions": {
"sourceType": "module", "sourceType": "module",
"ecmaVersion": 2022, "ecmaVersion": 2023,
"ecmaFeatures": { "ecmaFeatures": {
"globalReturn": true "globalReturn": true
} }
@ -38,6 +38,46 @@
"no-throw-literal": "error", "no-throw-literal": "error",
"no-unused-vars": "off", "no-unused-vars": "off",
"no-useless-return": "error", "no-useless-return": "error",
"prefer-template": "error" "object-shorthand": ["error", "methods"],
} "prefer-template": "error",
"@stylistic/array-element-newline": ["error", "consistent"],
"@stylistic/arrow-parens": ["error", "always"],
"@stylistic/brace-style": "off",
"@stylistic/comma-dangle": ["error", "never"],
"@stylistic/dot-location": ["error", "property"],
"@stylistic/function-call-argument-newline": ["error", "consistent"],
"@stylistic/function-paren-newline": ["error", "consistent"],
"@stylistic/implicit-arrow-linebreak": ["error", "beside"],
"@stylistic/max-statements-per-line": ["error", { "max": 2 }],
"@stylistic/multiline-ternary": ["error", "always-multiline"],
"@stylistic/newline-per-chained-call": ["error", { "ignoreChainWithDepth": 4 }],
"@stylistic/no-extra-parens": "off",
"@stylistic/no-tabs": "off",
"@stylistic/object-curly-spacing": ["error", "always"],
"@stylistic/object-property-newline": ["error", { "allowAllPropertiesOnSameLine": true }],
"@stylistic/operator-linebreak": ["error", "before"],
"@stylistic/padded-blocks": "off",
"@stylistic/quote-props": ["error", "as-needed"],
"@stylistic/quotes": ["error", "double"],
"@stylistic/indent": ["error", "tab"],
"@stylistic/semi": ["error", "always"],
"@stylistic/space-before-function-paren": ["error", "always"],
"@stylistic/spaced-comment": "off"
},
"overrides": [
{
"files": ["config/config.js.sample"],
"rules": {
"@stylistic/comma-dangle": "off",
"@stylistic/indent": "off",
"@stylistic/no-multi-spaces": "off"
}
},
{
"files": ["tests/configs/modules/weather/*.js"],
"rules": {
"@stylistic/quotes": "off"
}
}
]
} }

View File

@ -1,4 +1,4 @@
.eslintignore *.js
.prettierignore .prettierignore
/config /config
/coverage /coverage

View File

@ -31,6 +31,7 @@ _This release is scheduled to be released on 2024-01-01._
- Updated dependencies - Updated dependencies
- Clock module: optionally display current moon phase in addition to rise/set times - 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) - 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
### Fixed ### Fixed

View File

@ -7,7 +7,8 @@
/** /**
* Helper function to get server address/hostname from either the commandline or env * Helper function to get server address/hostname from either the commandline or env
*/ */
function getServerAddress() { function getServerAddress () {
/** /**
* Get command line parameters * Get command line parameters
* Assumes that a cmdline parameter is defined with `--key [value]` * Assumes that a cmdline parameter is defined with `--key [value]`
@ -15,7 +16,7 @@
* @param {string} defaultValue value if no key is given at the command line * @param {string} defaultValue value if no key is given at the command line
* @returns {string} the value of the parameter * @returns {string} the value of the parameter
*/ */
function getCommandLineParameter(key, defaultValue = undefined) { function getCommandLineParameter (key, defaultValue = undefined) {
const index = process.argv.indexOf(`--${key}`); const index = process.argv.indexOf(`--${key}`);
const value = index > -1 ? process.argv[index + 1] : undefined; const value = index > -1 ? process.argv[index + 1] : undefined;
return value !== undefined ? String(value) : defaultValue; return value !== undefined ? String(value) : defaultValue;
@ -35,7 +36,7 @@
* @param {string} url location where the server is running. * @param {string} url location where the server is running.
* @returns {Promise} the config * @returns {Promise} the config
*/ */
function getServerConfig(url) { function getServerConfig (url) {
// Return new pending promise // Return new pending promise
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// Select http or https module, depending on requested url // Select http or https module, depending on requested url
@ -64,7 +65,7 @@
* @param {string} message error message to print * @param {string} message error message to print
* @param {number} code error code for the exit call * @param {number} code error code for the exit call
*/ */
function fail(message, code = 1) { function fail (message, code = 1) {
if (message !== undefined && typeof message === "string") { if (message !== undefined && typeof message === "string") {
console.log(message); console.log(message);
} else { } else {
@ -121,4 +122,4 @@
} else { } else {
fail(); fail();
} }
})(); }());

View File

@ -18,17 +18,17 @@ let config = {
// - "0.0.0.0", "::" to listen on any interface // - "0.0.0.0", "::" to listen on any interface
// Default, when address config is left out or empty, is "localhost" // Default, when address config is left out or empty, is "localhost"
port: 8080, port: 8080,
basePath: "/", // The URL path where MagicMirror² is hosted. If you are using a Reverse proxy basePath: "/", // The URL path where MagicMirror² is hosted. If you are using a Reverse proxy
// you must set the sub path here. basePath must end with a / // you must set the sub path here. basePath must end with a /
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], // Set [] to allow all IP addresses ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], // Set [] to allow all IP addresses
// or add a specific IPv4 of 192.168.1.5 : // or add a specific IPv4 of 192.168.1.5 :
// ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.1.5"], // ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.1.5"],
// or IPv4 range of 192.168.3.0 --> 192.168.3.15 use CIDR format : // or IPv4 range of 192.168.3.0 --> 192.168.3.15 use CIDR format :
// ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.3.0/28"], // ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.3.0/28"],
useHttps: false, // Support HTTPS or not, default "false" will use HTTP useHttps: false, // Support HTTPS or not, default "false" will use HTTP
httpsPrivateKey: "", // HTTPS private key path, only require when useHttps is true httpsPrivateKey: "", // HTTPS private key path, only require when useHttps is true
httpsCertificate: "", // HTTPS Certificate path, only require when useHttps is true httpsCertificate: "", // HTTPS Certificate path, only require when useHttps is true
language: "en", language: "en",
locale: "en-US", locale: "en-US",
@ -109,4 +109,4 @@ let config = {
}; };
/*************** DO NOT EDIT THE LINE BELOW ***************/ /*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {module.exports = config;} if (typeof module !== "undefined") { module.exports = config; }

View File

@ -134,12 +134,12 @@ const AnimateCSSOut = [
* @param {string} [animation] animation name. * @param {string} [animation] animation name.
* @param {number} [animationTime] animation duration. * @param {number} [animationTime] animation duration.
*/ */
function addAnimateCSS(element, animation, animationTime) { function addAnimateCSS (element, animation, animationTime) {
const animationName = `animate__${animation}`; const animationName = `animate__${animation}`;
const node = document.getElementById(element); const node = document.getElementById(element);
if (!node) { if (!node) {
// don't execute animate: we don't find div // 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; return;
} }
node.style.setProperty("--animate-duration", `${animationTime}s`); 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} [element] div element to animate.
* @param {string} [animation] animation name. * @param {string} [animation] animation name.
*/ */
function removeAnimateCSS(element, animation) { function removeAnimateCSS (element, animation) {
const animationName = `animate__${animation}`; const animationName = `animate__${animation}`;
const node = document.getElementById(element); const node = document.getElementById(element);
if (!node) { if (!node) {
// don't execute animate: we don't find div // 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; return;
} }
node.classList.remove("animate__animated", animationName); node.classList.remove("animate__animated", animationName);

View File

@ -47,7 +47,7 @@ process.on("uncaughtException", function (err) {
* The core app. * The core app.
* @class * @class
*/ */
function App() { function App () {
let nodeHelpers = []; let nodeHelpers = [];
let httpServer; let httpServer;
@ -56,7 +56,7 @@ function App() {
* @async * @async
* @returns {Promise<object>} the loaded config or the defaults if something goes wrong * @returns {Promise<object>} the loaded config or the defaults if something goes wrong
*/ */
async function loadConfig() { async function loadConfig () {
Log.log("Loading config ..."); Log.log("Loading config ...");
const defaults = require(`${__dirname}/defaults`); const defaults = require(`${__dirname}/defaults`);
@ -136,7 +136,7 @@ function App() {
* if it encounters one option from the deprecated.js list * if it encounters one option from the deprecated.js list
* @param {object} userConfig The user config * @param {object} userConfig The user config
*/ */
function checkDeprecatedOptions(userConfig) { function checkDeprecatedOptions (userConfig) {
const deprecated = require(`${global.root_path}/js/deprecated`); const deprecated = require(`${global.root_path}/js/deprecated`);
const deprecatedOptions = deprecated.configs; const deprecatedOptions = deprecated.configs;
@ -150,7 +150,7 @@ function App() {
* Loads a specific module. * Loads a specific module.
* @param {string} module The name of the module (including subpath). * @param {string} module The name of the module (including subpath).
*/ */
function loadModule(module) { function loadModule (module) {
const elements = module.split("/"); const elements = module.split("/");
const moduleName = elements[elements.length - 1]; const moduleName = elements[elements.length - 1];
let moduleFolder = `${__dirname}/../modules/${module}`; let moduleFolder = `${__dirname}/../modules/${module}`;
@ -204,7 +204,7 @@ function App() {
* @param {Module[]} modules All modules to be loaded * @param {Module[]} modules All modules to be loaded
* @returns {Promise} A promise that is resolved when all modules been 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 ..."); Log.log("Loading module helpers ...");
for (let module of modules) { for (let module of modules) {
@ -221,7 +221,7 @@ function App() {
* @returns {number} A positive number if a is larger than b, a negative * @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 * number if a is smaller and 0 if they are the same
*/ */
function cmpVersions(a, b) { function cmpVersions (a, b) {
let i, diff; let i, diff;
const regExStrip0 = /(\.0+)+$/; const regExStrip0 = /(\.0+)+$/;
const segmentsA = a.replace(regExStrip0, "").split("."); const segmentsA = a.replace(regExStrip0, "").split(".");

View File

@ -20,7 +20,7 @@ const Utils = require(`${rootPath}/js/utils.js`);
* Check if set by environment variable MM_CONFIG_FILE * Check if set by environment variable MM_CONFIG_FILE
* @returns {string} path and filename of the 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! // 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`); return path.resolve(process.env.MM_CONFIG_FILE || `${rootPath}/config/config.js`);
} }
@ -28,7 +28,7 @@ function getConfigFile() {
/** /**
* Checks the config file using eslint. * Checks the config file using eslint.
*/ */
function checkConfigFile() { function checkConfigFile () {
const configFileName = getConfigFile(); const configFileName = getConfigFile();
// Check if file is present // Check if file is present

View File

@ -9,7 +9,7 @@
*/ */
(function () { (function () {
let initializing = false; let initializing = false;
const fnTest = /xyz/.test(function () { const fnTest = (/xyz/).test(function () {
xyz; xyz;
}) })
? /\b_super\b/ ? /\b_super\b/
@ -36,31 +36,31 @@
// Copy the properties over onto the new prototype // Copy the properties over onto the new prototype
for (const name in prop) { for (const name in prop) {
// Check if we're overwriting an existing function // Check if we're overwriting an existing function
prototype[name] = prototype[name]
typeof prop[name] === "function" && typeof _super[name] === "function" && fnTest.test(prop[name]) = typeof prop[name] === "function" && typeof _super[name] === "function" && fnTest.test(prop[name])
? (function (name, fn) { ? (function (name, fn) {
return function () { return function () {
const tmp = this._super; const tmp = this._super;
// Add a new ._super() method that is the same method // Add a new ._super() method that is the same method
// but on the super-class // but on the super-class
this._super = _super[name]; this._super = _super[name];
// The method only need to be bound temporarily, so we // The method only need to be bound temporarily, so we
// remove it when we're done executing // remove it when we're done executing
const ret = fn.apply(this, arguments); const ret = fn.apply(this, arguments);
this._super = tmp; this._super = tmp;
return ret; return ret;
}; };
})(name, prop[name]) }(name, prop[name]))
: prop[name]; : prop[name];
} }
/** /**
* The dummy class constructor * The dummy class constructor
*/ */
function Class() { function Class () {
// All construction is actually done in the init method // All construction is actually done in the init method
if (!initializing && this.init) { if (!initializing && this.init) {
this.init.apply(this, arguments); this.init.apply(this, arguments);
@ -78,14 +78,14 @@
return Class; return Class;
}; };
})(); }());
/** /**
* Define the clone method for later use. Helper Method. * Define the clone method for later use. Helper Method.
* @param {object} obj Object to be cloned * @param {object} obj Object to be cloned
* @returns {object} the cloned object * @returns {object} the cloned object
*/ */
function cloneObject(obj) { function cloneObject (obj) {
if (obj === null || typeof obj !== "object") { if (obj === null || typeof obj !== "object") {
return obj; return obj;
} }

View File

@ -25,7 +25,7 @@ let mainWindow;
/** /**
* *
*/ */
function createWindow() { function createWindow () {
// see https://www.electronjs.org/docs/latest/api/screen // see https://www.electronjs.org/docs/latest/api/screen
// Create a window that fills the screen's available work area. // Create a window that fills the screen's available work area.
let electronSize = (800, 600); let electronSize = (800, 600);
@ -121,11 +121,11 @@ function createWindow() {
mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => { mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => {
let curHeaders = details.responseHeaders; let curHeaders = details.responseHeaders;
if (config["ignoreXOriginHeader"] || false) { 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) { 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 }); callback({ responseHeaders: curHeaders });

View File

@ -7,6 +7,7 @@
* MIT Licensed. * MIT Licensed.
*/ */
const Loader = (function () { const Loader = (function () {
/* Create helper variables */ /* Create helper variables */
const loadedModuleFiles = []; const loadedModuleFiles = [];
@ -196,10 +197,11 @@ const Loader = (function () {
/* Public Methods */ /* Public Methods */
return { return {
/** /**
* Load all modules as defined in the config. * Load all modules as defined in the config.
*/ */
loadModules: async function () { async loadModules () {
let moduleData = getModuleData(); let moduleData = getModuleData();
/** /**
@ -230,7 +232,7 @@ const Loader = (function () {
* @param {Module} module The module that calls the loadFile function. * @param {Module} module The module that calls the loadFile function.
* @returns {Promise} resolved when the file is loaded * @returns {Promise} resolved when the file is loaded
*/ */
loadFileForModule: async function (fileName, module) { async loadFileForModule (fileName, module) {
if (loadedFiles.indexOf(fileName.toLowerCase()) !== -1) { if (loadedFiles.indexOf(fileName.toLowerCase()) !== -1) {
Log.log(`File already loaded: ${fileName}`); Log.log(`File already loaded: ${fileName}`);
return; return;
@ -256,4 +258,4 @@ const Loader = (function () {
return loadFile(module.file(fileName)); return loadFile(module.file(fileName));
} }
}; };
})(); }());

View File

@ -22,7 +22,7 @@
// Browser globals (root is window) // Browser globals (root is window)
root.Log = factory(root.config); root.Log = factory(root.config);
} }
})(this, function (config) { }(this, function (config) {
let logLevel; let logLevel;
let enableLog; let enableLog;
if (typeof exports === "object") { if (typeof exports === "object") {
@ -59,21 +59,21 @@
}; };
} else { } else {
logLevel = { logLevel = {
debug: function () {}, debug () {},
log: function () {}, log () {},
info: function () {}, info () {},
warn: function () {}, warn () {},
error: function () {}, error () {},
group: function () {}, group () {},
groupCollapsed: function () {}, groupCollapsed () {},
groupEnd: function () {}, groupEnd () {},
time: function () {}, time () {},
timeEnd: function () {}, timeEnd () {},
timeStamp: function () {} timeStamp () {}
}; };
logLevel.setLogLevel = function () {}; logLevel.setLogLevel = function () {};
} }
return logLevel; return logLevel;
}); }));

View File

@ -479,7 +479,6 @@ const MM = (function () {
*/ */
const loadConfig = function () { const loadConfig = function () {
// FIXME: Think about how to pass config around without breaking tests // FIXME: Think about how to pass config around without breaking tests
/* eslint-disable */
if (typeof config === "undefined") { if (typeof config === "undefined") {
config = defaults; config = defaults;
Log.error("Config file is missing! Please create a config file."); Log.error("Config file is missing! Please create a config file.");
@ -487,7 +486,6 @@ const MM = (function () {
} }
config = Object.assign({}, defaults, config); config = Object.assign({}, defaults, config);
/* eslint-enable */
}; };
/** /**
@ -495,6 +493,7 @@ const MM = (function () {
* @param {Module[]} modules Array of modules. * @param {Module[]} modules Array of modules.
*/ */
const setSelectionMethodsForModules = function (modules) { const setSelectionMethodsForModules = function (modules) {
/** /**
* Filter modules with the specified classes. * Filter modules with the specified classes.
* @param {string|string[]} className one or multiple classnames (array or space divided). * @param {string|string[]} className one or multiple classnames (array or space divided).
@ -580,12 +579,13 @@ const MM = (function () {
}; };
return { return {
/* Public Methods */ /* Public Methods */
/** /**
* Main init method. * Main init method.
*/ */
init: async function () { async init () {
Log.info("Initializing MagicMirror²."); Log.info("Initializing MagicMirror².");
loadConfig(); loadConfig();
@ -599,7 +599,7 @@ const MM = (function () {
* Gets called when all modules are started. * Gets called when all modules are started.
* @param {Module[]} moduleObjects All module instances. * @param {Module[]} moduleObjects All module instances.
*/ */
modulesStarted: function (moduleObjects) { modulesStarted (moduleObjects) {
modules = []; modules = [];
let startUp = ""; let startUp = "";
@ -636,7 +636,7 @@ const MM = (function () {
* @param {*} payload The payload of the notification. * @param {*} payload The payload of the notification.
* @param {Module} sender The module that sent the notification. * @param {Module} sender The module that sent the notification.
*/ */
sendNotification: function (notification, payload, sender) { sendNotification (notification, payload, sender) {
if (arguments.length < 3) { if (arguments.length < 3) {
Log.error("sendNotification: Missing arguments."); Log.error("sendNotification: Missing arguments.");
return; return;
@ -661,7 +661,7 @@ const MM = (function () {
* @param {Module} module The module that needs an update. * @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) * @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)) { if (!(module instanceof Module)) {
Log.error("updateDom: Sender should be a module."); Log.error("updateDom: Sender should be a module.");
return; return;
@ -680,7 +680,7 @@ const MM = (function () {
* Returns a collection of all modules currently active. * Returns a collection of all modules currently active.
* @returns {Module[]} A collection of all modules currently active. * @returns {Module[]} A collection of all modules currently active.
*/ */
getModules: function () { getModules () {
setSelectionMethodsForModules(modules); setSelectionMethodsForModules(modules);
return modules; return modules;
}, },
@ -692,7 +692,7 @@ const MM = (function () {
* @param {Function} callback Called when the animation is done. * @param {Function} callback Called when the animation is done.
* @param {object} [options] Optional settings for the hide method. * @param {object} [options] Optional settings for the hide method.
*/ */
hideModule: function (module, speed, callback, options) { hideModule (module, speed, callback, options) {
module.hidden = true; module.hidden = true;
hideModule(module, speed, callback, options); hideModule(module, speed, callback, options);
}, },
@ -704,12 +704,12 @@ const MM = (function () {
* @param {Function} callback Called when the animation is done. * @param {Function} callback Called when the animation is done.
* @param {object} [options] Optional settings for the show method. * @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 // do not change module.hidden yet, only if we really show it later
showModule(module, speed, callback, options); showModule(module, speed, callback, options);
} }
}; };
})(); }());
// Add polyfill for Object.assign. // Add polyfill for Object.assign.
if (typeof Object.assign !== "function") { if (typeof Object.assign !== "function") {
@ -732,7 +732,7 @@ if (typeof Object.assign !== "function") {
} }
return output; return output;
}; };
})(); }());
} }
MM.init(); MM.init();

View File

@ -8,6 +8,7 @@
* MIT Licensed. * MIT Licensed.
*/ */
const Module = Class.extend({ const Module = Class.extend({
/********************************************************* /*********************************************************
* All methods (and properties) below can be subclassed. * * All methods (and properties) below can be subclassed. *
*********************************************************/ *********************************************************/
@ -33,14 +34,14 @@ const Module = Class.extend({
/** /**
* Called when the module is instantiated. * Called when the module is instantiated.
*/ */
init: function () { init () {
//Log.log(this.defaults); //Log.log(this.defaults);
}, },
/** /**
* Called when the module is started. * Called when the module is started.
*/ */
start: async function () { async start () {
Log.info(`Starting module: ${this.name}`); 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 a list of scripts the module requires to be loaded.
* @returns {string[]} An array with filenames. * @returns {string[]} An array with filenames.
*/ */
getScripts: function () { getScripts () {
return []; return [];
}, },
@ -56,7 +57,7 @@ const Module = Class.extend({
* Returns a list of stylesheets the module requires to be loaded. * Returns a list of stylesheets the module requires to be loaded.
* @returns {string[]} An array with filenames. * @returns {string[]} An array with filenames.
*/ */
getStyles: function () { getStyles () {
return []; return [];
}, },
@ -66,7 +67,7 @@ const Module = Class.extend({
* return Map<String, String> - * return Map<String, String> -
* @returns {*} A map with langKeys and filenames. * @returns {*} A map with langKeys and filenames.
*/ */
getTranslations: function () { getTranslations () {
return false; return false;
}, },
@ -76,14 +77,14 @@ const Module = Class.extend({
* Alternatively, the getTemplate method could be subclassed. * Alternatively, the getTemplate method could be subclassed.
* @returns {HTMLElement|Promise} The dom or a promise with the dom to display. * @returns {HTMLElement|Promise} The dom or a promise with the dom to display.
*/ */
getDom: function () { getDom () {
return new Promise((resolve) => { return new Promise((resolve) => {
const div = document.createElement("div"); const div = document.createElement("div");
const template = this.getTemplate(); const template = this.getTemplate();
const templateData = this.getTemplateData(); const templateData = this.getTemplateData();
// Check to see if we need to render a template string or a file. // 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 // the template is a filename
this.nunjucksEnvironment().render(template, templateData, function (err, res) { this.nunjucksEnvironment().render(template, templateData, function (err, res) {
if (err) { 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. * 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. * @returns {string} The header to display above the header.
*/ */
getHeader: function () { getHeader () {
return this.data.header; 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. * If the string ends with '.html' it's considered a file from within the module's folder.
* @returns {string} The template string of filename. * @returns {string} The template string of filename.
*/ */
getTemplate: function () { getTemplate () {
return `<div class="normal">${this.name}</div><div class="small dimmed">${this.identifier}</div>`; 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. * This method needs to be subclassed if the module wants to use a custom data.
* @returns {object} The data for the template * @returns {object} The data for the template
*/ */
getTemplateData: function () { getTemplateData () {
return {}; return {};
}, },
@ -139,7 +140,7 @@ const Module = Class.extend({
* @param {*} payload The payload of the notification. * @param {*} payload The payload of the notification.
* @param {Module} sender The module that sent the notification. * @param {Module} sender The module that sent the notification.
*/ */
notificationReceived: function (notification, payload, sender) { notificationReceived (notification, payload, sender) {
if (sender) { if (sender) {
// Log.log(this.name + " received a module notification: " + notification + " from sender: " + sender.name); // Log.log(this.name + " received a module notification: " + notification + " from sender: " + sender.name);
} else { } else {
@ -152,7 +153,7 @@ const Module = Class.extend({
* The environment is checked in the _nunjucksEnvironment instance variable. * The environment is checked in the _nunjucksEnvironment instance variable.
* @returns {object} The Nunjucks Environment * @returns {object} The Nunjucks Environment
*/ */
nunjucksEnvironment: function () { nunjucksEnvironment () {
if (this._nunjucksEnvironment !== null) { if (this._nunjucksEnvironment !== null) {
return this._nunjucksEnvironment; return this._nunjucksEnvironment;
} }
@ -174,21 +175,21 @@ const Module = Class.extend({
* @param {string} notification The identifier of the notification. * @param {string} notification The identifier of the notification.
* @param {*} payload The payload 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}`); Log.log(`${this.name} received a socket notification: ${notification} - Payload: ${payload}`);
}, },
/** /**
* Called when the module is hidden. * Called when the module is hidden.
*/ */
suspend: function () { suspend () {
Log.log(`${this.name} is suspended.`); Log.log(`${this.name} is suspended.`);
}, },
/** /**
* Called when the module is shown. * Called when the module is shown.
*/ */
resume: function () { resume () {
Log.log(`${this.name} is resumed.`); Log.log(`${this.name} is resumed.`);
}, },
@ -200,7 +201,7 @@ const Module = Class.extend({
* Set the module data. * Set the module data.
* @param {object} data The module data * @param {object} data The module data
*/ */
setData: function (data) { setData (data) {
this.data = data; this.data = data;
this.name = data.name; this.name = data.name;
this.identifier = data.identifier; this.identifier = data.identifier;
@ -216,7 +217,7 @@ const Module = Class.extend({
* @param {object} config The combined module config. * @param {object} config The combined module config.
* @param {boolean} deep Merge module config in deep. * @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); 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. * It also registers the notification callback.
* @returns {MMSocket} a socket object * @returns {MMSocket} a socket object
*/ */
socket: function () { socket () {
if (typeof this._socket === "undefined") { if (typeof this._socket === "undefined") {
this._socket = new MMSocket(this.name); this._socket = new MMSocket(this.name);
} }
@ -242,7 +243,7 @@ const Module = Class.extend({
* @param {string} file Filename * @param {string} file Filename
* @returns {string} the file path * @returns {string} the file path
*/ */
file: function (file) { file (file) {
return `${this.data.path}/${file}`.replace("//", "/"); 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. * Load all required stylesheets by requesting the MM object to load the files.
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
loadStyles: function () { loadStyles () {
return this.loadDependencies("getStyles"); 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. * Load all required scripts by requesting the MM object to load the files.
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
loadScripts: function () { loadScripts () {
return this.loadDependencies("getScripts"); return this.loadDependencies("getScripts");
}, },
@ -267,7 +268,7 @@ const Module = Class.extend({
* @param {string} funcName Function name to call to get scripts or styles. * @param {string} funcName Function name to call to get scripts or styles.
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
loadDependencies: async function (funcName) { async loadDependencies (funcName) {
let dependencies = this[funcName](); let dependencies = this[funcName]();
const loadNextDependency = async () => { const loadNextDependency = async () => {
@ -288,7 +289,7 @@ const Module = Class.extend({
* Load all translations. * Load all translations.
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
loadTranslations: async function () { async loadTranslations () {
const translations = this.getTranslations() || {}; const translations = this.getTranslations() || {};
const language = config.language.toLowerCase(); const language = config.language.toLowerCase();
@ -320,7 +321,7 @@ const Module = Class.extend({
* @param {string} [defaultValue] The default value with variables. * @param {string} [defaultValue] The default value with variables.
* @returns {string} the translated key * @returns {string} the translated key
*/ */
translate: function (key, defaultValueOrVariables, defaultValue) { translate (key, defaultValueOrVariables, defaultValue) {
if (typeof defaultValueOrVariables === "object") { if (typeof defaultValueOrVariables === "object") {
return Translator.translate(this, key, defaultValueOrVariables) || defaultValue || ""; return Translator.translate(this, key, defaultValueOrVariables) || defaultValue || "";
} }
@ -331,7 +332,7 @@ const Module = Class.extend({
* Request an (animated) update of the module. * Request an (animated) update of the module.
* @param {number|object} [updateOptions] The speed of the animation or object with for updateOptions (speed/animates) * @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); MM.updateDom(this, updateOptions);
}, },
@ -340,7 +341,7 @@ const Module = Class.extend({
* @param {string} notification The identifier of the notification. * @param {string} notification The identifier of the notification.
* @param {*} payload The payload of the notification. * @param {*} payload The payload of the notification.
*/ */
sendNotification: function (notification, payload) { sendNotification (notification, payload) {
MM.sendNotification(notification, payload, this); MM.sendNotification(notification, payload, this);
}, },
@ -349,7 +350,7 @@ const Module = Class.extend({
* @param {string} notification The identifier of the notification. * @param {string} notification The identifier of the notification.
* @param {*} payload The payload of the notification. * @param {*} payload The payload of the notification.
*/ */
sendSocketNotification: function (notification, payload) { sendSocketNotification (notification, payload) {
this.socket().sendNotification(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 {Function} callback Called when the animation is done.
* @param {object} [options] Optional settings for the hide method. * @param {object} [options] Optional settings for the hide method.
*/ */
hide: function (speed, callback, options = {}) { hide (speed, callback, options = {}) {
let usedCallback = callback || function () {}; let usedCallback = callback || function () {};
let usedOptions = options; let usedOptions = options;
@ -386,7 +387,7 @@ const Module = Class.extend({
* @param {Function} callback Called when the animation is done. * @param {Function} callback Called when the animation is done.
* @param {object} [options] Optional settings for the show method. * @param {object} [options] Optional settings for the show method.
*/ */
show: function (speed, callback, options) { show (speed, callback, options) {
let usedCallback = callback || function () {}; let usedCallback = callback || function () {};
let usedOptions = options; let usedOptions = options;
@ -430,7 +431,7 @@ const Module = Class.extend({
* @param {object} result the initial object * @param {object} result the initial object
* @returns {object} the merged config * @returns {object} the merged config
*/ */
function configMerge(result) { function configMerge (result) {
const stack = Array.prototype.slice.call(arguments, 1); const stack = Array.prototype.slice.call(arguments, 1);
let item, key; let item, key;
@ -493,7 +494,7 @@ window.Module = Module;
* @returns {number} A positive number if a is larger than b, a negative * @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 * number if a is smaller and 0 if they are the same
*/ */
function cmpVersions(a, b) { function cmpVersions (a, b) {
const regExStrip0 = /(\.0+)+$/; const regExStrip0 = /(\.0+)+$/;
const segmentsA = a.replace(regExStrip0, "").split("."); const segmentsA = a.replace(regExStrip0, "").split(".");
const segmentsB = b.replace(regExStrip0, "").split("."); const segmentsB = b.replace(regExStrip0, "").split(".");

View File

@ -9,15 +9,15 @@ const Log = require("logger");
const Class = require("./class"); const Class = require("./class");
const NodeHelper = Class.extend({ const NodeHelper = Class.extend({
init() { init () {
Log.log("Initializing new module helper ..."); Log.log("Initializing new module helper ...");
}, },
loaded() { loaded () {
Log.log(`Module helper loaded: ${this.name}`); Log.log(`Module helper loaded: ${this.name}`);
}, },
start() { start () {
Log.log(`Starting module helper: ${this.name}`); Log.log(`Starting module helper: ${this.name}`);
}, },
@ -26,7 +26,7 @@ const NodeHelper = Class.extend({
* Close any open connections, stop any sub-processes and * Close any open connections, stop any sub-processes and
* gracefully exit the module. * gracefully exit the module.
*/ */
stop() { stop () {
Log.log(`Stopping module helper: ${this.name}`); Log.log(`Stopping module helper: ${this.name}`);
}, },
@ -35,7 +35,7 @@ const NodeHelper = Class.extend({
* @param {string} notification The identifier of the notification. * @param {string} notification The identifier of the notification.
* @param {*} payload The payload 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}`); Log.log(`${this.name} received a socket notification: ${notification} - Payload: ${payload}`);
}, },
@ -43,7 +43,7 @@ const NodeHelper = Class.extend({
* Set the module name. * Set the module name.
* @param {string} name Module name. * @param {string} name Module name.
*/ */
setName(name) { setName (name) {
this.name = name; this.name = name;
}, },
@ -51,7 +51,7 @@ const NodeHelper = Class.extend({
* Set the module path. * Set the module path.
* @param {string} path Module path. * @param {string} path Module path.
*/ */
setPath(path) { setPath (path) {
this.path = path; this.path = path;
}, },
@ -61,7 +61,7 @@ const NodeHelper = Class.extend({
* argument notification string - The identifier of the notification. * argument notification string - The identifier of the notification.
* argument payload mixed - The payload 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); this.io.of(this.name).emit(notification, payload);
}, },
@ -71,7 +71,7 @@ const NodeHelper = Class.extend({
* *
* argument app Express app - The Express app object. * argument app Express app - The Express app object.
*/ */
setExpressApp(app) { setExpressApp (app) {
this.expressApp = app; this.expressApp = app;
app.use(`/${this.name}`, express.static(`${this.path}/public`)); 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. * argument io Socket.io - The Socket io object.
*/ */
setSocketIO(io) { setSocketIO (io) {
this.io = io; this.io = io;
Log.log(`Connecting socket for: ${this.name}`); Log.log(`Connecting socket for: ${this.name}`);

View File

@ -22,7 +22,7 @@ const { cors, getConfig, getHtml, getVersion, getStartup } = require("./server_f
* @param {object} config The MM config * @param {object} config The MM config
* @class * @class
*/ */
function Server(config) { function Server (config) {
const app = express(); const app = express();
const port = process.env.MM_PORT || config.port; const port = process.env.MM_PORT || config.port;
const serverSockets = new Set(); const serverSockets = new Set();

View File

@ -9,7 +9,7 @@ const startUp = new Date();
* @param {Request} req - the request * @param {Request} req - the request
* @param {Response} res - the result * @param {Response} res - the result
*/ */
function getConfig(req, res) { function getConfig (req, res) {
res.send(config); res.send(config);
} }
@ -18,7 +18,7 @@ function getConfig(req, res) {
* @param {Request} req - the request * @param {Request} req - the request
* @param {Response} res - the result * @param {Response} res - the result
*/ */
function getStartup(req, res) { function getStartup (req, res) {
res.send(startUp); res.send(startUp);
} }
@ -31,7 +31,7 @@ function getStartup(req, res) {
* @param {Request} req - the request * @param {Request} req - the request
* @param {Response} res - the result * @param {Response} res - the result
*/ */
async function cors(req, res) { async function cors (req, res) {
try { try {
const urlRegEx = "url=(.+?)$"; const urlRegEx = "url=(.+?)$";
let url; let url;
@ -68,7 +68,7 @@ async function cors(req, res) {
* @param {string} url - The url containing the headers and values to send. * @param {string} url - The url containing the headers and values to send.
* @returns {object} An object specifying name and value of the headers. * @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 headersToSend = { "User-Agent": `Mozilla/5.0 MagicMirror/${global.version}` };
const headersToSendMatch = new RegExp("sendheaders=(.+?)(&|$)", "g").exec(url); const headersToSendMatch = new RegExp("sendheaders=(.+?)(&|$)", "g").exec(url);
if (headersToSendMatch) { if (headersToSendMatch) {
@ -89,7 +89,7 @@ function getHeadersToSend(url) {
* @param {string} url - The url containing the expected headers from the response. * @param {string} url - The url containing the expected headers from the response.
* @returns {string[]} headers - The name of the expected headers. * @returns {string[]} headers - The name of the expected headers.
*/ */
function geExpectedRecievedHeaders(url) { function geExpectedRecievedHeaders (url) {
const expectedRecievedHeaders = ["Content-Type"]; const expectedRecievedHeaders = ["Content-Type"];
const expectedRecievedHeadersMatch = new RegExp("expectedheaders=(.+?)(&|$)", "g").exec(url); const expectedRecievedHeadersMatch = new RegExp("expectedheaders=(.+?)(&|$)", "g").exec(url);
if (expectedRecievedHeadersMatch) { if (expectedRecievedHeadersMatch) {
@ -106,7 +106,7 @@ function geExpectedRecievedHeaders(url) {
* @param {Request} req - the request * @param {Request} req - the request
* @param {Response} res - the result * @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" }); let html = fs.readFileSync(path.resolve(`${global.root_path}/index.html`), { encoding: "utf8" });
html = html.replace("#VERSION#", global.version); html = html.replace("#VERSION#", global.version);
@ -124,7 +124,7 @@ function getHtml(req, res) {
* @param {Request} req - the request * @param {Request} req - the request
* @param {Response} res - the result * @param {Response} res - the result
*/ */
function getVersion(req, res) { function getVersion (req, res) {
res.send(global.version); res.send(global.version);
} }

View File

@ -7,12 +7,13 @@
* MIT Licensed. * MIT Licensed.
*/ */
const Translator = (function () { const Translator = (function () {
/** /**
* Load a JSON file via XHR. * Load a JSON file via XHR.
* @param {string} file Path of the file we want to load. * @param {string} file Path of the file we want to load.
* @returns {Promise<object>} the translations in the specified file * @returns {Promise<object>} the translations in the specified file
*/ */
async function loadJSON(file) { async function loadJSON (file) {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
return new Promise(function (resolve) { return new Promise(function (resolve) {
xhr.overrideMimeType("application/json"); xhr.overrideMimeType("application/json");
@ -47,7 +48,8 @@ const Translator = (function () {
* @param {object} variables The variables to use within the translation template (optional) * @param {object} variables The variables to use within the translation template (optional)
* @returns {string} the translated key * @returns {string} the translated key
*/ */
translate: function (module, key, variables = {}) { translate (module, key, variables = {}) {
/** /**
* Combines template and variables like: * Combines template and variables like:
* template: "Please wait for {timeToWait} before continuing with {work}." * template: "Please wait for {timeToWait} before continuing with {work}."
@ -57,7 +59,7 @@ const Translator = (function () {
* @param {object} variables Variables for the placeholder * @param {object} variables Variables for the placeholder
* @returns {string} the template filled with the variables * @returns {string} the template filled with the variables
*/ */
function createStringFromTemplate(template, variables) { function createStringFromTemplate (template, variables) {
if (Object.prototype.toString.call(template) !== "[object String]") { if (Object.prototype.toString.call(template) !== "[object String]") {
return template; return template;
} }
@ -99,7 +101,7 @@ const Translator = (function () {
* @param {string} file Path of the file we want to load. * @param {string} file Path of the file we want to load.
* @param {boolean} isFallback Flag to indicate fallback translations. * @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}`); Log.log(`${module.name} - Load translation${isFallback ? " fallback" : ""}: ${file}`);
if (this.translationsFallback[module.name]) { if (this.translationsFallback[module.name]) {
@ -115,7 +117,7 @@ const Translator = (function () {
* Load the core translations. * Load the core translations.
* @param {string} lang The language identifier of the core language. * @param {string} lang The language identifier of the core language.
*/ */
loadCoreTranslations: async function (lang) { async loadCoreTranslations (lang) {
if (lang in translations) { if (lang in translations) {
Log.log(`Loading core translation file: ${translations[lang]}`); Log.log(`Loading core translation file: ${translations[lang]}`);
this.coreTranslations = await loadJSON(translations[lang]); this.coreTranslations = await loadJSON(translations[lang]);
@ -130,7 +132,7 @@ const Translator = (function () {
* Load the core translations' fallback. * Load the core translations' fallback.
* The first language defined in translations.js will be used. * The first language defined in translations.js will be used.
*/ */
loadCoreTranslationsFallback: async function () { async loadCoreTranslationsFallback () {
let first = Object.keys(translations)[0]; let first = Object.keys(translations)[0];
if (first) { if (first) {
Log.log(`Loading core translation fallback file: ${translations[first]}`); Log.log(`Loading core translation fallback file: ${translations[first]}`);
@ -138,6 +140,6 @@ const Translator = (function () {
} }
} }
}; };
})(); }());
window.Translator = Translator; window.Translator = Translator;

View File

@ -17,15 +17,15 @@ Module.register("alert", {
welcome_message: false // shown at startup welcome_message: false // shown at startup
}, },
getScripts() { getScripts () {
return ["notificationFx.js"]; return ["notificationFx.js"];
}, },
getStyles() { getStyles () {
return ["font-awesome.css", this.file(`./styles/notificationFx.css`), this.file(`./styles/${this.config.position}.css`)]; return ["font-awesome.css", this.file("./styles/notificationFx.css"), this.file(`./styles/${this.config.position}.css`)];
}, },
getTranslations() { getTranslations () {
return { return {
bg: "translations/bg.json", bg: "translations/bg.json",
da: "translations/da.json", da: "translations/da.json",
@ -40,11 +40,11 @@ Module.register("alert", {
}; };
}, },
getTemplate(type) { getTemplate (type) {
return `templates/${type}.njk`; return `templates/${type}.njk`;
}, },
async start() { async start () {
Log.info(`Starting module: ${this.name}`); Log.info(`Starting module: ${this.name}`);
if (this.config.effect === "slide") { if (this.config.effect === "slide") {
@ -57,7 +57,7 @@ Module.register("alert", {
} }
}, },
notificationReceived(notification, payload, sender) { notificationReceived (notification, payload, sender) {
if (notification === "SHOW_ALERT") { if (notification === "SHOW_ALERT") {
if (payload.type === "notification") { if (payload.type === "notification") {
this.showNotification(payload); this.showNotification(payload);
@ -69,7 +69,7 @@ Module.register("alert", {
} }
}, },
async showNotification(notification) { async showNotification (notification) {
const message = await this.renderMessage(notification.templateName || "notification", notification); const message = await this.renderMessage(notification.templateName || "notification", notification);
new NotificationFx({ new NotificationFx({
@ -80,7 +80,7 @@ Module.register("alert", {
}).show(); }).show();
}, },
async showAlert(alert, sender) { async showAlert (alert, sender) {
// If module already has an open alert close it // If module already has an open alert close it
if (this.alerts[sender.name]) { if (this.alerts[sender.name]) {
this.hideAlert(sender, false); this.hideAlert(sender, false);
@ -113,7 +113,7 @@ Module.register("alert", {
} }
}, },
hideAlert(sender, close = true) { hideAlert (sender, close = true) {
// Dismiss alert and remove from this.alerts // Dismiss alert and remove from this.alerts
if (this.alerts[sender.name]) { if (this.alerts[sender.name]) {
this.alerts[sender.name].dismiss(close); this.alerts[sender.name].dismiss(close);
@ -125,7 +125,7 @@ Module.register("alert", {
} }
}, },
renderMessage(type, data) { renderMessage (type, data) {
return new Promise((resolve) => { return new Promise((resolve) => {
this.nunjucksEnvironment().render(this.getTemplate(type), data, function (err, res) { this.nunjucksEnvironment().render(this.getTemplate(type), data, function (err, res) {
if (err) { if (err) {
@ -137,7 +137,7 @@ Module.register("alert", {
}); });
}, },
toggleBlur(add = false) { toggleBlur (add = false) {
const method = add ? "add" : "remove"; const method = add ? "add" : "remove";
const modules = document.querySelectorAll(".module"); const modules = document.querySelectorAll(".module");
for (const module of modules) { for (const module of modules) {

View File

@ -12,13 +12,14 @@
* @param {object} window The window object * @param {object} window The window object
*/ */
(function (window) { (function (window) {
/** /**
* Extend one object with another one * Extend one object with another one
* @param {object} a The object to extend * @param {object} a The object to extend
* @param {object} b The object which extends the other, overwrites existing keys * @param {object} b The object which extends the other, overwrites existing keys
* @returns {object} The merged object * @returns {object} The merged object
*/ */
function extend(a, b) { function extend (a, b) {
for (let key in b) { for (let key in b) {
if (b.hasOwnProperty(key)) { if (b.hasOwnProperty(key)) {
a[key] = b[key]; a[key] = b[key];
@ -32,7 +33,7 @@
* @param {object} options The configuration options * @param {object} options The configuration options
* @class * @class
*/ */
function NotificationFx(options) { function NotificationFx (options) {
this.options = extend({}, this.options); this.options = extend({}, this.options);
extend(this.options, options); extend(this.options, options);
this._init(); this._init();
@ -63,10 +64,10 @@
ttl: 6000, ttl: 6000,
al_no: "ns-box", al_no: "ns-box",
// callbacks // callbacks
onClose: function () { onClose () {
return false; return false;
}, },
onOpen: function () { onOpen () {
return false; return false;
} }
}; };
@ -78,7 +79,7 @@
// create HTML structure // create HTML structure
this.ntf = document.createElement("div"); 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}`; 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">'; let strinner = "<div class=\"ns-box-inner\">";
strinner += this.options.message; strinner += this.options.message;
strinner += "</div>"; strinner += "</div>";
this.ntf.innerHTML = strinner; this.ntf.innerHTML = strinner;
@ -153,4 +154,4 @@
* Add to global namespace * Add to global namespace
*/ */
window.NotificationFx = NotificationFx; window.NotificationFx = NotificationFx;
})(window); }(window));

View File

@ -75,17 +75,17 @@ Module.register("calendar", {
requiresVersion: "2.1.0", requiresVersion: "2.1.0",
// Define required scripts. // Define required scripts.
getStyles: function () { getStyles () {
return ["calendar.css", "font-awesome.css"]; return ["calendar.css", "font-awesome.css"];
}, },
// Define required scripts. // Define required scripts.
getScripts: function () { getScripts () {
return ["calendarutils.js", "moment.js"]; return ["calendarutils.js", "moment.js"];
}, },
// Define required translations. // Define required translations.
getTranslations: function () { getTranslations () {
// The translations for the default modules are defined in the core translation files. // The translations for the default modules are defined in the core translation files.
// Therefore we can just return false. Otherwise we should have returned a dictionary. // Therefore 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. // If you're trying to build your own module including translations, check out the documentation.
@ -93,7 +93,7 @@ Module.register("calendar", {
}, },
// Override start method. // Override start method.
start: function () { start () {
Log.info(`Starting module: ${this.name}`); Log.info(`Starting module: ${this.name}`);
if (this.config.colored) { if (this.config.colored) {
@ -169,7 +169,7 @@ Module.register("calendar", {
}, },
// Override socket notification handler. // Override socket notification handler.
socketNotificationReceived: function (notification, payload) { socketNotificationReceived (notification, payload) {
if (notification === "FETCH_CALENDAR") { if (notification === "FETCH_CALENDAR") {
this.sendSocketNotification(notification, { url: payload.url, id: this.identifier }); this.sendSocketNotification(notification, { url: payload.url, id: this.identifier });
} }
@ -210,7 +210,7 @@ Module.register("calendar", {
}, },
// Override dom generator. // Override dom generator.
getDom: function () { getDom () {
const ONE_SECOND = 1000; // 1,000 milliseconds const ONE_SECOND = 1000; // 1,000 milliseconds
const ONE_MINUTE = ONE_SECOND * 60; const ONE_MINUTE = ONE_SECOND * 60;
const ONE_HOUR = ONE_MINUTE * 60; const ONE_HOUR = ONE_MINUTE * 60;
@ -552,7 +552,7 @@ Module.register("calendar", {
* @param {string} url The calendar url * @param {string} url The calendar url
* @returns {boolean} True if the calendar config contains the url, False otherwise * @returns {boolean} True if the calendar config contains the url, False otherwise
*/ */
hasCalendarURL: function (url) { hasCalendarURL (url) {
for (const calendar of this.config.calendars) { for (const calendar of this.config.calendars) {
if (calendar.url === url) { if (calendar.url === url) {
return true; return true;
@ -567,7 +567,7 @@ Module.register("calendar", {
* @param {boolean} limitNumberOfEntries Whether to filter returned events for display. * @param {boolean} limitNumberOfEntries Whether to filter returned events for display.
* @returns {object[]} Array with events. * @returns {object[]} Array with events.
*/ */
createEventList: function (limitNumberOfEntries) { createEventList (limitNumberOfEntries) {
const ONE_SECOND = 1000; // 1,000 milliseconds const ONE_SECOND = 1000; // 1,000 milliseconds
const ONE_MINUTE = ONE_SECOND * 60; const ONE_MINUTE = ONE_SECOND * 60;
const ONE_HOUR = ONE_MINUTE * 60; const ONE_HOUR = ONE_MINUTE * 60;
@ -617,7 +617,12 @@ Module.register("calendar", {
const maxCount = Math.ceil((event.endDate - 1 - moment(event.startDate, "x").endOf("day").format("x")) / ONE_DAY) + 1; const maxCount = Math.ceil((event.endDate - 1 - moment(event.startDate, "x").endOf("day").format("x")) / ONE_DAY) + 1;
if (this.config.sliceMultiDayEvents && maxCount > 1) { if (this.config.sliceMultiDayEvents && maxCount > 1) {
const splitEvents = []; const splitEvents = [];
let midnight = moment(event.startDate, "x").clone().startOf("day").add(1, "day").format("x"); let midnight
= moment(event.startDate, "x")
.clone()
.startOf("day")
.add(1, "day")
.format("x");
let count = 1; let count = 1;
while (event.endDate > midnight) { while (event.endDate > midnight) {
const thisEvent = JSON.parse(JSON.stringify(event)); // clone object const thisEvent = JSON.parse(JSON.stringify(event)); // clone object
@ -686,7 +691,7 @@ Module.register("calendar", {
return events.slice(0, this.config.maximumEntries); return events.slice(0, this.config.maximumEntries);
}, },
listContainsEvent: function (eventList, event) { listContainsEvent (eventList, event) {
for (const evt of eventList) { for (const evt of eventList) {
if (evt.title === event.title && parseInt(evt.startDate) === parseInt(event.startDate) && parseInt(evt.endDate) === parseInt(event.endDate)) { if (evt.title === event.title && parseInt(evt.startDate) === parseInt(event.startDate) && parseInt(evt.endDate) === parseInt(event.endDate)) {
return true; return true;
@ -701,7 +706,7 @@ Module.register("calendar", {
* @param {object} auth The authentication method and credentials * @param {object} auth The authentication method and credentials
* @param {object} calendarConfig The config of the specific calendar * @param {object} calendarConfig The config of the specific calendar
*/ */
addCalendar: function (url, auth, calendarConfig) { addCalendar (url, auth, calendarConfig) {
this.sendSocketNotification("ADD_CALENDAR", { this.sendSocketNotification("ADD_CALENDAR", {
id: this.identifier, id: this.identifier,
url: url, url: url,
@ -724,7 +729,7 @@ Module.register("calendar", {
* @param {object} event Event to look for. * @param {object} event Event to look for.
* @returns {string[]} The symbols * @returns {string[]} The symbols
*/ */
symbolsForEvent: function (event) { symbolsForEvent (event) {
let symbols = this.getCalendarPropertyAsArray(event.url, "symbol", this.config.defaultSymbol); let symbols = this.getCalendarPropertyAsArray(event.url, "symbol", this.config.defaultSymbol);
if (event.recurringEvent === true && this.hasCalendarProperty(event.url, "recurringSymbol")) { if (event.recurringEvent === true && this.hasCalendarProperty(event.url, "recurringSymbol")) {
@ -751,7 +756,7 @@ Module.register("calendar", {
return symbols; return symbols;
}, },
mergeUnique: function (arr1, arr2) { mergeUnique (arr1, arr2) {
return arr1.concat( return arr1.concat(
arr2.filter(function (item) { arr2.filter(function (item) {
return arr1.indexOf(item) === -1; return arr1.indexOf(item) === -1;
@ -764,7 +769,7 @@ Module.register("calendar", {
* @param {string} url The calendar url * @param {string} url The calendar url
* @returns {string} The class to be used for the symbols of the calendar * @returns {string} The class to be used for the symbols of the calendar
*/ */
symbolClassForUrl: function (url) { symbolClassForUrl (url) {
return this.getCalendarProperty(url, "symbolClass", ""); return this.getCalendarProperty(url, "symbolClass", "");
}, },
@ -773,7 +778,7 @@ Module.register("calendar", {
* @param {string} url The calendar url * @param {string} url The calendar url
* @returns {string} The class to be used for the title of the calendar * @returns {string} The class to be used for the title of the calendar
*/ */
titleClassForUrl: function (url) { titleClassForUrl (url) {
return this.getCalendarProperty(url, "titleClass", ""); return this.getCalendarProperty(url, "titleClass", "");
}, },
@ -782,7 +787,7 @@ Module.register("calendar", {
* @param {string} url The calendar url * @param {string} url The calendar url
* @returns {string} The class to be used for the time of the calendar * @returns {string} The class to be used for the time of the calendar
*/ */
timeClassForUrl: function (url) { timeClassForUrl (url) {
return this.getCalendarProperty(url, "timeClass", ""); return this.getCalendarProperty(url, "timeClass", "");
}, },
@ -791,7 +796,7 @@ Module.register("calendar", {
* @param {string} url The calendar url * @param {string} url The calendar url
* @returns {string} The name of the calendar * @returns {string} The name of the calendar
*/ */
calendarNameForUrl: function (url) { calendarNameForUrl (url) {
return this.getCalendarProperty(url, "name", ""); return this.getCalendarProperty(url, "name", "");
}, },
@ -801,7 +806,7 @@ Module.register("calendar", {
* @param {boolean} isBg Determines if we fetch the bgColor or not * @param {boolean} isBg Determines if we fetch the bgColor or not
* @returns {string} The color * @returns {string} The color
*/ */
colorForUrl: function (url, isBg) { colorForUrl (url, isBg) {
return this.getCalendarProperty(url, isBg ? "bgColor" : "color", "#fff"); return this.getCalendarProperty(url, isBg ? "bgColor" : "color", "#fff");
}, },
@ -810,7 +815,7 @@ Module.register("calendar", {
* @param {string} url The calendar url * @param {string} url The calendar url
* @returns {string} The title * @returns {string} The title
*/ */
countTitleForUrl: function (url) { countTitleForUrl (url) {
return this.getCalendarProperty(url, "repeatingCountTitle", this.config.defaultRepeatingCountTitle); return this.getCalendarProperty(url, "repeatingCountTitle", this.config.defaultRepeatingCountTitle);
}, },
@ -819,7 +824,7 @@ Module.register("calendar", {
* @param {string} url The calendar url * @param {string} url The calendar url
* @returns {number} The maximum entry count * @returns {number} The maximum entry count
*/ */
maximumEntriesForUrl: function (url) { maximumEntriesForUrl (url) {
return this.getCalendarProperty(url, "maximumEntries", this.config.maximumEntries); return this.getCalendarProperty(url, "maximumEntries", this.config.maximumEntries);
}, },
@ -828,7 +833,7 @@ Module.register("calendar", {
* @param {string} url The calendar url * @param {string} url The calendar url
* @returns {number} The maximum past days count * @returns {number} The maximum past days count
*/ */
maximumPastDaysForUrl: function (url) { maximumPastDaysForUrl (url) {
return this.getCalendarProperty(url, "pastDaysCount", this.config.pastDaysCount); return this.getCalendarProperty(url, "pastDaysCount", this.config.pastDaysCount);
}, },
@ -839,7 +844,7 @@ Module.register("calendar", {
* @param {string} defaultValue The value if the property is not found * @param {string} defaultValue The value if the property is not found
* @returns {*} The property * @returns {*} The property
*/ */
getCalendarProperty: function (url, property, defaultValue) { getCalendarProperty (url, property, defaultValue) {
for (const calendar of this.config.calendars) { for (const calendar of this.config.calendars) {
if (calendar.url === url && calendar.hasOwnProperty(property)) { if (calendar.url === url && calendar.hasOwnProperty(property)) {
return calendar[property]; return calendar[property];
@ -849,7 +854,7 @@ Module.register("calendar", {
return defaultValue; return defaultValue;
}, },
getCalendarPropertyAsArray: function (url, property, defaultValue) { getCalendarPropertyAsArray (url, property, defaultValue) {
let p = this.getCalendarProperty(url, property, defaultValue); let p = this.getCalendarProperty(url, property, defaultValue);
if (property === "symbol" || property === "recurringSymbol" || property === "fullDaySymbol") { if (property === "symbol" || property === "recurringSymbol" || property === "fullDaySymbol") {
const className = this.getCalendarProperty(url, "symbolClassName", this.config.defaultSymbolClassName); const className = this.getCalendarProperty(url, "symbolClassName", this.config.defaultSymbolClassName);
@ -860,7 +865,7 @@ Module.register("calendar", {
return p; return p;
}, },
hasCalendarProperty: function (url, property) { hasCalendarProperty (url, property) {
return !!this.getCalendarProperty(url, property, undefined); return !!this.getCalendarProperty(url, property, undefined);
}, },
@ -868,7 +873,7 @@ Module.register("calendar", {
* Broadcasts the events to all other modules for reuse. * Broadcasts the events to all other modules for reuse.
* The all events available in one array, sorted on startdate. * The all events available in one array, sorted on startdate.
*/ */
broadcastEvents: function () { broadcastEvents () {
const eventList = this.createEventList(false); const eventList = this.createEventList(false);
for (const event of eventList) { for (const event of eventList) {
event.symbol = this.symbolsForEvent(event); event.symbol = this.symbolsForEvent(event);
@ -888,7 +893,7 @@ Module.register("calendar", {
* and it's allow to refresh The DOM every minute with animation speed too * and it's allow to refresh The DOM every minute with animation speed too
* (because updateDom is not set in CALENDAR_EVENTS for this case) * (because updateDom is not set in CALENDAR_EVENTS for this case)
*/ */
selfUpdate: function () { selfUpdate () {
const ONE_MINUTE = 60 * 1000; const ONE_MINUTE = 60 * 1000;
setTimeout( setTimeout(
() => { () => {

View File

@ -15,6 +15,7 @@ const zoneTable = require(path.join(__dirname, "windowsZones.json"));
const Log = require("../../../js/logger"); const Log = require("../../../js/logger");
const CalendarFetcherUtils = { const CalendarFetcherUtils = {
/** /**
* Calculate the time correction, either dst/std or full day in cases where * Calculate the time correction, either dst/std or full day in cases where
* utc time is day before plus offset * utc time is day before plus offset
@ -22,7 +23,7 @@ const CalendarFetcherUtils = {
* @param {Date} date the date on which this event happens * @param {Date} date the date on which this event happens
* @returns {number} the necessary adjustment in hours * @returns {number} the necessary adjustment in hours
*/ */
calculateTimezoneAdjustment: function (event, date) { calculateTimezoneAdjustment (event, date) {
let adjustHours = 0; let adjustHours = 0;
// if a timezone was specified // if a timezone was specified
if (!event.start.tz) { if (!event.start.tz) {
@ -123,7 +124,7 @@ const CalendarFetcherUtils = {
* @param {object} config The configuration object * @param {object} config The configuration object
* @returns {string[]} the filtered events * @returns {string[]} the filtered events
*/ */
filterEvents: function (data, config) { filterEvents (data, config) {
const newEvents = []; const newEvents = [];
// limitFunction doesn't do much limiting, see comment re: the dates // limitFunction doesn't do much limiting, see comment re: the dates
@ -142,7 +143,12 @@ const CalendarFetcherUtils = {
Log.debug("Processing entry..."); Log.debug("Processing entry...");
const now = new Date(); const now = new Date();
const today = moment().startOf("day").toDate(); const today = moment().startOf("day").toDate();
const future = moment().startOf("day").add(config.maximumNumberOfDays, "days").subtract(1, "seconds").toDate(); // Subtract 1 second so that events that start on the middle of the night will not repeat. const future
= moment()
.startOf("day")
.add(config.maximumNumberOfDays, "days")
.subtract(1, "seconds") // Subtract 1 second so that events that start on the middle of the night will not repeat.
.toDate();
let past = today; let past = today;
if (config.includePastEvents) { if (config.includePastEvents) {
@ -521,7 +527,7 @@ const CalendarFetcherUtils = {
* @param {string} msTZName the timezone name to lookup * @param {string} msTZName the timezone name to lookup
* @returns {string|null} the iana name or null of none is found * @returns {string|null} the iana name or null of none is found
*/ */
getIanaTZFromMS: function (msTZName) { getIanaTZFromMS (msTZName) {
// Get hash entry // Get hash entry
const he = zoneTable[msTZName]; const he = zoneTable[msTZName];
// If found return iana name, else null // If found return iana name, else null
@ -533,7 +539,7 @@ const CalendarFetcherUtils = {
* @param {object} event The event object to check. * @param {object} event The event object to check.
* @returns {string} The title of the event, or "Event" if no title is found. * @returns {string} The title of the event, or "Event" if no title is found.
*/ */
getTitleFromEvent: function (event) { getTitleFromEvent (event) {
let title = "Event"; let title = "Event";
if (event.summary) { 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;
@ -549,7 +555,7 @@ const CalendarFetcherUtils = {
* @param {object} event The event object to check. * @param {object} event The event object to check.
* @returns {boolean} True if the event is a fullday event, false otherwise * @returns {boolean} True if the event is a fullday event, false otherwise
*/ */
isFullDayEvent: function (event) { isFullDayEvent (event) {
if (event.start.length === 8 || event.start.dateOnly || event.datetype === "date") { if (event.start.length === 8 || event.start.dateOnly || event.datetype === "date") {
return true; return true;
} }
@ -572,7 +578,7 @@ const CalendarFetcherUtils = {
* @param {string} filter The time to subtract from the end date to determine if an event should be shown * @param {string} filter The time to subtract from the end date to determine if an event should be shown
* @returns {boolean} True if the event should be filtered out, false otherwise * @returns {boolean} True if the event should be filtered out, false otherwise
*/ */
timeFilterApplies: function (now, endDate, filter) { timeFilterApplies (now, endDate, filter) {
if (filter) { if (filter) {
const until = filter.split(" "), const until = filter.split(" "),
value = parseInt(until[0]), value = parseInt(until[0]),
@ -593,7 +599,7 @@ const CalendarFetcherUtils = {
* @param {string} regexFlags flags that should be applied to the regex * @param {string} regexFlags flags that should be applied to the regex
* @returns {boolean} True if the title should be filtered out, false otherwise * @returns {boolean} True if the title should be filtered out, false otherwise
*/ */
titleFilterApplies: function (title, filter, useRegex, regexFlags) { titleFilterApplies (title, filter, useRegex, regexFlags) {
if (useRegex) { if (useRegex) {
let regexFilter = filter; let regexFilter = filter;
// Assume if leading slash, there is also trailing slash // Assume if leading slash, there is also trailing slash

View File

@ -5,12 +5,13 @@
* MIT Licensed. * MIT Licensed.
*/ */
const CalendarUtils = { const CalendarUtils = {
/** /**
* Capitalize the first letter of a string * Capitalize the first letter of a string
* @param {string} string The string to capitalize * @param {string} string The string to capitalize
* @returns {string} The capitalized string * @returns {string} The capitalized string
*/ */
capFirst: function (string) { capFirst (string) {
return string.charAt(0).toUpperCase() + string.slice(1); return string.charAt(0).toUpperCase() + string.slice(1);
}, },
@ -21,7 +22,7 @@ const CalendarUtils = {
* @param {number} timeFormat Specifies either 12 or 24-hour time format * @param {number} timeFormat Specifies either 12 or 24-hour time format
* @returns {moment.LocaleSpecification} formatted time * @returns {moment.LocaleSpecification} formatted time
*/ */
getLocaleSpecification: function (timeFormat) { getLocaleSpecification (timeFormat) {
switch (timeFormat) { switch (timeFormat) {
case 12: { case 12: {
return { longDateFormat: { LT: "h:mm A" } }; return { longDateFormat: { LT: "h:mm A" } };
@ -43,7 +44,7 @@ const CalendarUtils = {
* @param {number} maxTitleLines The max number of vertical lines before cutting event title * @param {number} maxTitleLines The max number of vertical lines before cutting event title
* @returns {string} The shortened string * @returns {string} The shortened string
*/ */
shorten: function (string, maxLength, wrapEvents, maxTitleLines) { shorten (string, maxLength, wrapEvents, maxTitleLines) {
if (typeof string !== "string") { if (typeof string !== "string") {
return ""; return "";
} }
@ -98,7 +99,7 @@ const CalendarUtils = {
* yearmatchgroup: {number,optional} match group for year element * yearmatchgroup: {number,optional} match group for year element
* @returns {string} The transformed title. * @returns {string} The transformed title.
*/ */
titleTransform: function (title, titleReplace) { titleTransform (title, titleReplace) {
let transformedTitle = title; let transformedTitle = title;
for (let tr in titleReplace) { for (let tr in titleReplace) {
let transform = titleReplace[tr]; let transform = titleReplace[tr];

View File

@ -10,13 +10,13 @@ const CalendarFetcher = require("./calendarfetcher");
module.exports = NodeHelper.create({ module.exports = NodeHelper.create({
// Override start method. // Override start method.
start: function () { start () {
Log.log(`Starting node helper for: ${this.name}`); Log.log(`Starting node helper for: ${this.name}`);
this.fetchers = []; this.fetchers = [];
}, },
// Override socketNotificationReceived method. // Override socketNotificationReceived method.
socketNotificationReceived: function (notification, payload) { socketNotificationReceived (notification, payload) {
if (notification === "ADD_CALENDAR") { if (notification === "ADD_CALENDAR") {
this.createFetcher(payload.url, payload.fetchInterval, payload.excludedEvents, payload.maximumEntries, payload.maximumNumberOfDays, payload.auth, payload.broadcastPastEvents, payload.selfSignedCert, payload.id); this.createFetcher(payload.url, payload.fetchInterval, payload.excludedEvents, payload.maximumEntries, payload.maximumNumberOfDays, payload.auth, payload.broadcastPastEvents, payload.selfSignedCert, payload.id);
} else if (notification === "FETCH_CALENDAR") { } else if (notification === "FETCH_CALENDAR") {
@ -43,7 +43,7 @@ module.exports = NodeHelper.create({
* @param {boolean} selfSignedCert If true, the server certificate is not verified against the list of supplied CAs. * @param {boolean} selfSignedCert If true, the server certificate is not verified against the list of supplied CAs.
* @param {string} identifier ID of the module * @param {string} identifier ID of the module
*/ */
createFetcher: function (url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents, selfSignedCert, identifier) { createFetcher (url, fetchInterval, excludedEvents, maximumEntries, maximumNumberOfDays, auth, broadcastPastEvents, selfSignedCert, identifier) {
try { try {
new URL(url); new URL(url);
} catch (error) { } catch (error) {
@ -85,7 +85,7 @@ module.exports = NodeHelper.create({
* @param {object} fetcher the fetcher associated with the calendar * @param {object} fetcher the fetcher associated with the calendar
* @param {string} identifier the identifier of the calendar * @param {string} identifier the identifier of the calendar
*/ */
broadcastEvents: function (fetcher, identifier) { broadcastEvents (fetcher, identifier) {
this.sendSocketNotification("CALENDAR_EVENTS", { this.sendSocketNotification("CALENDAR_EVENTS", {
id: identifier, id: identifier,
url: fetcher.url(), url: fetcher.url(),

View File

@ -37,15 +37,15 @@ Module.register("clock", {
lon: -122.344147 lon: -122.344147
}, },
// Define required scripts. // Define required scripts.
getScripts: function () { getScripts () {
return ["moment.js", "moment-timezone.js", "suncalc.js"]; return ["moment.js", "moment-timezone.js", "suncalc.js"];
}, },
// Define styles. // Define styles.
getStyles: function () { getStyles () {
return ["clock_styles.css"]; return ["clock_styles.css"];
}, },
// Define start sequence. // Define start sequence.
start: function () { start () {
Log.info(`Starting module: ${this.name}`); Log.info(`Starting module: ${this.name}`);
// Schedule update interval. // Schedule update interval.
@ -94,7 +94,7 @@ Module.register("clock", {
moment.locale(config.language); moment.locale(config.language);
}, },
// Override dom generator. // Override dom generator.
getDom: function () { getDom () {
const wrapper = document.createElement("div"); const wrapper = document.createElement("div");
wrapper.classList.add("clock-grid"); wrapper.classList.add("clock-grid");
@ -186,10 +186,10 @@ Module.register("clock", {
} }
const untilNextEvent = moment.duration(moment(nextEvent).diff(now)); const untilNextEvent = moment.duration(moment(nextEvent).diff(now));
const untilNextEventString = `${untilNextEvent.hours()}h ${untilNextEvent.minutes()}m`; const untilNextEventString = `${untilNextEvent.hours()}h ${untilNextEvent.minutes()}m`;
sunWrapper.innerHTML = sunWrapper.innerHTML
`<span class="${isVisible ? "bright" : ""}"><i class="fas fa-sun" aria-hidden="true"></i> ${untilNextEventString}</span>` + = `<span class="${isVisible ? "bright" : ""}"><i class="fas fa-sun" aria-hidden="true"></i> ${untilNextEventString}</span>`
`<span><i class="fas fa-arrow-up" aria-hidden="true"></i> ${formatTime(this.config, sunTimes.sunrise)}</span>` + + `<span><i class="fas fa-arrow-up" aria-hidden="true"></i> ${formatTime(this.config, sunTimes.sunrise)}</span>`
`<span><i class="fas fa-arrow-down" aria-hidden="true"></i> ${formatTime(this.config, sunTimes.sunset)}</span>`; + `<span><i class="fas fa-arrow-down" aria-hidden="true"></i> ${formatTime(this.config, sunTimes.sunset)}</span>`;
digitalWrapper.appendChild(sunWrapper); digitalWrapper.appendChild(sunWrapper);
} }
@ -211,12 +211,12 @@ Module.register("clock", {
const showFraction = ["both", "percent"].includes(this.config.showMoonTimes); const showFraction = ["both", "percent"].includes(this.config.showMoonTimes);
const showUnicode = ["both", "phase"].includes(this.config.showMoonTimes); const showUnicode = ["both", "phase"].includes(this.config.showMoonTimes);
const illuminatedFractionString = `${Math.round(moonIllumination.fraction * 100)}%`; const illuminatedFractionString = `${Math.round(moonIllumination.fraction * 100)}%`;
const image = showUnicode ? [..."🌑🌒🌓🌔🌕🌖🌗🌘"][Math.floor(moonIllumination.phase * 8)] : '<i class="fas fa-moon" aria-hidden="true"></i>'; const image = showUnicode ? [..."🌑🌒🌓🌔🌕🌖🌗🌘"][Math.floor(moonIllumination.phase * 8)] : "<i class=\"fas fa-moon\" aria-hidden=\"true\"></i>";
moonWrapper.innerHTML = moonWrapper.innerHTML
`<span class="${isVisible ? "bright" : ""}">${image} ${showFraction ? illuminatedFractionString : ""}</span>` + = `<span class="${isVisible ? "bright" : ""}">${image} ${showFraction ? illuminatedFractionString : ""}</span>`
`<span><i class="fas fa-arrow-up" aria-hidden="true"></i> ${moonRise ? formatTime(this.config, moonRise) : "..."}</span>` + + `<span><i class="fas fa-arrow-up" aria-hidden="true"></i> ${moonRise ? formatTime(this.config, moonRise) : "..."}</span>`
`<span><i class="fas fa-arrow-down" aria-hidden="true"></i> ${moonSet ? formatTime(this.config, moonSet) : "..."}</span>`; + `<span><i class="fas fa-arrow-down" aria-hidden="true"></i> ${moonSet ? formatTime(this.config, moonSet) : "..."}</span>`;
digitalWrapper.appendChild(moonWrapper); digitalWrapper.appendChild(moonWrapper);
} }

View File

@ -28,12 +28,12 @@ Module.register("compliments", {
currentWeatherType: "", currentWeatherType: "",
// Define required scripts. // Define required scripts.
getScripts: function () { getScripts () {
return ["moment.js"]; return ["moment.js"];
}, },
// Define start sequence. // Define start sequence.
start: async function () { async start () {
Log.info(`Starting module: ${this.name}`); Log.info(`Starting module: ${this.name}`);
this.lastComplimentIndex = -1; this.lastComplimentIndex = -1;
@ -55,7 +55,7 @@ Module.register("compliments", {
* @param {string[]} compliments Array with compliments. * @param {string[]} compliments Array with compliments.
* @returns {number} a random index of given array * @returns {number} a random index of given array
*/ */
randomIndex: function (compliments) { randomIndex (compliments) {
if (compliments.length === 1) { if (compliments.length === 1) {
return 0; return 0;
} }
@ -79,7 +79,7 @@ Module.register("compliments", {
* Retrieve an array of compliments for the time of the day. * Retrieve an array of compliments for the time of the day.
* @returns {string[]} array with compliments for the time of the day. * @returns {string[]} array with compliments for the time of the day.
*/ */
complimentArray: function () { complimentArray () {
const hour = moment().hour(); const hour = moment().hour();
const date = moment().format("YYYY-MM-DD"); const date = moment().format("YYYY-MM-DD");
let compliments = []; let compliments = [];
@ -115,7 +115,7 @@ Module.register("compliments", {
* Retrieve a file from the local filesystem * Retrieve a file from the local filesystem
* @returns {Promise} Resolved when the file is loaded * @returns {Promise} Resolved when the file is loaded
*/ */
loadComplimentFile: async function () { async loadComplimentFile () {
const isRemote = this.config.remoteFile.indexOf("http://") === 0 || this.config.remoteFile.indexOf("https://") === 0, const isRemote = this.config.remoteFile.indexOf("http://") === 0 || this.config.remoteFile.indexOf("https://") === 0,
url = isRemote ? this.config.remoteFile : this.file(this.config.remoteFile); url = isRemote ? this.config.remoteFile : this.file(this.config.remoteFile);
const response = await fetch(url); const response = await fetch(url);
@ -126,7 +126,7 @@ Module.register("compliments", {
* Retrieve a random compliment. * Retrieve a random compliment.
* @returns {string} a compliment * @returns {string} a compliment
*/ */
getRandomCompliment: function () { getRandomCompliment () {
// get the current time of day compliments list // get the current time of day compliments list
const compliments = this.complimentArray(); const compliments = this.complimentArray();
// variable for index to next message to display // variable for index to next message to display
@ -145,7 +145,7 @@ Module.register("compliments", {
}, },
// Override dom generator. // Override dom generator.
getDom: function () { getDom () {
const wrapper = document.createElement("div"); const wrapper = document.createElement("div");
wrapper.className = this.config.classes ? this.config.classes : "thin xlarge bright pre-line"; wrapper.className = this.config.classes ? this.config.classes : "thin xlarge bright pre-line";
// get the compliment text // get the compliment text
@ -173,7 +173,7 @@ Module.register("compliments", {
}, },
// Override notification handler. // Override notification handler.
notificationReceived: function (notification, payload, sender) { notificationReceived (notification, payload, sender) {
if (notification === "CURRENTWEATHER_TYPE") { if (notification === "CURRENTWEATHER_TYPE") {
this.currentWeatherType = payload.type; this.currentWeatherType = payload.type;
} }

View File

@ -10,11 +10,11 @@ Module.register("helloworld", {
text: "Hello World!" text: "Hello World!"
}, },
getTemplate: function () { getTemplate () {
return "helloworld.njk"; return "helloworld.njk";
}, },
getTemplateData: function () { getTemplateData () {
return this.config; return this.config;
} }
}); });

View File

@ -42,7 +42,7 @@ Module.register("newsfeed", {
dangerouslyDisableAutoEscaping: false dangerouslyDisableAutoEscaping: false
}, },
getUrlPrefix: function (item) { getUrlPrefix (item) {
if (item.useCorsProxy) { if (item.useCorsProxy) {
return `${location.protocol}//${location.host}/cors?url=`; return `${location.protocol}//${location.host}/cors?url=`;
} else { } else {
@ -51,17 +51,17 @@ Module.register("newsfeed", {
}, },
// Define required scripts. // Define required scripts.
getScripts: function () { getScripts () {
return ["moment.js"]; return ["moment.js"];
}, },
//Define required styles. //Define required styles.
getStyles: function () { getStyles () {
return ["newsfeed.css"]; return ["newsfeed.css"];
}, },
// Define required translations. // Define required translations.
getTranslations: function () { getTranslations () {
// The translations for the default modules are defined in the core translation files. // 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. // 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. // If you're trying to build your own module including translations, check out the documentation.
@ -69,7 +69,7 @@ Module.register("newsfeed", {
}, },
// Define start sequence. // Define start sequence.
start: function () { start () {
Log.info(`Starting module: ${this.name}`); Log.info(`Starting module: ${this.name}`);
// Set locale. // Set locale.
@ -87,7 +87,7 @@ Module.register("newsfeed", {
}, },
// Override socket notification handler. // Override socket notification handler.
socketNotificationReceived: function (notification, payload) { socketNotificationReceived (notification, payload) {
if (notification === "NEWS_ITEMS") { if (notification === "NEWS_ITEMS") {
this.generateFeed(payload); this.generateFeed(payload);
@ -107,7 +107,7 @@ Module.register("newsfeed", {
}, },
//Override fetching of template name //Override fetching of template name
getTemplate: function () { getTemplate () {
if (this.config.feedUrl) { if (this.config.feedUrl) {
return "oldconfig.njk"; return "oldconfig.njk";
} else if (this.config.showFullArticle) { } else if (this.config.showFullArticle) {
@ -117,7 +117,7 @@ Module.register("newsfeed", {
}, },
//Override template data and return whats used for the current template //Override template data and return whats used for the current template
getTemplateData: function () { getTemplateData () {
// this.config.showFullArticle is a run-time configuration, triggered by optional notifications // this.config.showFullArticle is a run-time configuration, triggered by optional notifications
if (this.config.showFullArticle) { if (this.config.showFullArticle) {
return { return {
@ -156,7 +156,7 @@ Module.register("newsfeed", {
}; };
}, },
getActiveItemURL: function () { getActiveItemURL () {
const item = this.newsItems[this.activeItem]; const item = this.newsItems[this.activeItem];
if (item) { if (item) {
return typeof item.url === "string" ? this.getUrlPrefix(item) + item.url : this.getUrlPrefix(item) + item.url.href; return typeof item.url === "string" ? this.getUrlPrefix(item) + item.url : this.getUrlPrefix(item) + item.url.href;
@ -168,7 +168,7 @@ Module.register("newsfeed", {
/** /**
* Registers the feeds to be used by the backend. * Registers the feeds to be used by the backend.
*/ */
registerFeeds: function () { registerFeeds () {
for (let feed of this.config.feeds) { for (let feed of this.config.feeds) {
this.sendSocketNotification("ADD_FEED", { this.sendSocketNotification("ADD_FEED", {
feed: feed, feed: feed,
@ -181,7 +181,7 @@ Module.register("newsfeed", {
* Generate an ordered list of items for this configured module. * Generate an ordered list of items for this configured module.
* @param {object} feeds An object with feeds returned by the node helper. * @param {object} feeds An object with feeds returned by the node helper.
*/ */
generateFeed: function (feeds) { generateFeed (feeds) {
let newsItems = []; let newsItems = [];
for (let feed in feeds) { for (let feed in feeds) {
const feedItems = feeds[feed]; const feedItems = feeds[feed];
@ -274,7 +274,7 @@ Module.register("newsfeed", {
* @param {string} feedUrl Url of the feed to check. * @param {string} feedUrl Url of the feed to check.
* @returns {boolean} True if it is subscribed, false otherwise * @returns {boolean} True if it is subscribed, false otherwise
*/ */
subscribedToFeed: function (feedUrl) { subscribedToFeed (feedUrl) {
for (let feed of this.config.feeds) { for (let feed of this.config.feeds) {
if (feed.url === feedUrl) { if (feed.url === feedUrl) {
return true; return true;
@ -288,7 +288,7 @@ Module.register("newsfeed", {
* @param {string} feedUrl Url of the feed * @param {string} feedUrl Url of the feed
* @returns {string} The title of the feed * @returns {string} The title of the feed
*/ */
titleForFeed: function (feedUrl) { titleForFeed (feedUrl) {
for (let feed of this.config.feeds) { for (let feed of this.config.feeds) {
if (feed.url === feedUrl) { if (feed.url === feedUrl) {
return feed.title || ""; return feed.title || "";
@ -300,7 +300,7 @@ Module.register("newsfeed", {
/** /**
* Schedule visual update. * Schedule visual update.
*/ */
scheduleUpdateInterval: function () { scheduleUpdateInterval () {
this.updateDom(this.config.animationSpeed); this.updateDom(this.config.animationSpeed);
// Broadcast NewsFeed if needed // Broadcast NewsFeed if needed
@ -322,7 +322,7 @@ Module.register("newsfeed", {
}, this.config.updateInterval); }, this.config.updateInterval);
}, },
resetDescrOrFullArticleAndTimer: function () { resetDescrOrFullArticleAndTimer () {
this.isShowingDescription = this.config.showDescription; this.isShowingDescription = this.config.showDescription;
this.config.showFullArticle = false; this.config.showFullArticle = false;
this.scrollPosition = 0; this.scrollPosition = 0;
@ -333,7 +333,7 @@ Module.register("newsfeed", {
} }
}, },
notificationReceived: function (notification, payload, sender) { notificationReceived (notification, payload, sender) {
const before = this.activeItem; const before = this.activeItem;
if (notification === "MODULE_DOM_CREATED" && this.config.hideLoading) { if (notification === "MODULE_DOM_CREATED" && this.config.hideLoading) {
this.hide(); this.hide();
@ -394,7 +394,7 @@ Module.register("newsfeed", {
} }
}, },
showFullArticle: function () { showFullArticle () {
this.isShowingDescription = !this.isShowingDescription; this.isShowingDescription = !this.isShowingDescription;
this.config.showFullArticle = !this.isShowingDescription; this.config.showFullArticle = !this.isShowingDescription;
// make bottom bar align to top to allow scrolling // make bottom bar align to top to allow scrolling

View File

@ -11,13 +11,13 @@ const NewsfeedFetcher = require("./newsfeedfetcher");
module.exports = NodeHelper.create({ module.exports = NodeHelper.create({
// Override start method. // Override start method.
start: function () { start () {
Log.log(`Starting node helper for: ${this.name}`); Log.log(`Starting node helper for: ${this.name}`);
this.fetchers = []; this.fetchers = [];
}, },
// Override socketNotificationReceived received. // Override socketNotificationReceived received.
socketNotificationReceived: function (notification, payload) { socketNotificationReceived (notification, payload) {
if (notification === "ADD_FEED") { if (notification === "ADD_FEED") {
this.createFetcher(payload.feed, payload.config); this.createFetcher(payload.feed, payload.config);
} }
@ -29,7 +29,7 @@ module.exports = NodeHelper.create({
* @param {object} feed The feed object * @param {object} feed The feed object
* @param {object} config The configuration object * @param {object} config The configuration object
*/ */
createFetcher: function (feed, config) { createFetcher (feed, config) {
const url = feed.url || ""; const url = feed.url || "";
const encoding = feed.encoding || "UTF-8"; const encoding = feed.encoding || "UTF-8";
const reloadInterval = feed.reloadInterval || config.reloadInterval || 5 * 60 * 1000; const reloadInterval = feed.reloadInterval || config.reloadInterval || 5 * 60 * 1000;
@ -76,7 +76,7 @@ module.exports = NodeHelper.create({
* Creates an object with all feed items of the different registered feeds, * Creates an object with all feed items of the different registered feeds,
* and broadcasts these using sendSocketNotification. * and broadcasts these using sendSocketNotification.
*/ */
broadcastFeeds: function () { broadcastFeeds () {
const feeds = {}; const feeds = {};
for (let f in this.fetchers) { for (let f in this.fetchers) {
feeds[f] = this.fetchers[f].items(); feeds[f] = this.fetchers[f].items();

View File

@ -7,22 +7,22 @@ const Log = require("logger");
const BASE_DIR = path.normalize(`${__dirname}/../../../`); const BASE_DIR = path.normalize(`${__dirname}/../../../`);
class GitHelper { class GitHelper {
constructor() { constructor () {
this.gitRepos = []; this.gitRepos = [];
this.gitResultList = []; this.gitResultList = [];
} }
getRefRegex(branch) { getRefRegex (branch) {
return new RegExp(`s*([a-z,0-9]+[.][.][a-z,0-9]+) ${branch}`, "g"); return new RegExp(`s*([a-z,0-9]+[.][.][a-z,0-9]+) ${branch}`, "g");
} }
async execShell(command) { async execShell (command) {
const { stdout = "", stderr = "" } = await exec(command); const { stdout = "", stderr = "" } = await exec(command);
return { stdout, stderr }; return { stdout, stderr };
} }
async isGitRepo(moduleFolder) { async isGitRepo (moduleFolder) {
const { stderr } = await this.execShell(`cd ${moduleFolder} && git remote -v`); const { stderr } = await this.execShell(`cd ${moduleFolder} && git remote -v`);
if (stderr) { if (stderr) {
@ -34,7 +34,7 @@ class GitHelper {
return true; return true;
} }
async add(moduleName) { async add (moduleName) {
let moduleFolder = BASE_DIR; let moduleFolder = BASE_DIR;
if (moduleName !== "MagicMirror") { if (moduleName !== "MagicMirror") {
@ -59,7 +59,7 @@ class GitHelper {
} }
} }
async getStatusInfo(repo) { async getStatusInfo (repo) {
let gitInfo = { let gitInfo = {
module: repo.module, module: repo.module,
behind: 0, // commits behind behind: 0, // commits behind
@ -114,7 +114,7 @@ class GitHelper {
return gitInfo; return gitInfo;
} }
async getRepoInfo(repo) { async getRepoInfo (repo) {
const gitInfo = await this.getStatusInfo(repo); const gitInfo = await this.getStatusInfo(repo);
if (!gitInfo || !gitInfo.current) { if (!gitInfo || !gitInfo.current) {
@ -174,7 +174,7 @@ class GitHelper {
} }
} }
async getRepos() { async getRepos () {
this.gitResultList = []; this.gitResultList = [];
for (const repo of this.gitRepos) { for (const repo of this.gitRepos) {
@ -192,7 +192,7 @@ class GitHelper {
return this.gitResultList; return this.gitResultList;
} }
async checkUpdates() { async checkUpdates () {
var updates = []; var updates = [];
const allRepos = await this.gitResultList.map((module) => { const allRepos = await this.gitResultList.map((module) => {

View File

@ -14,7 +14,7 @@ module.exports = NodeHelper.create({
gitHelper: new GitHelper(), gitHelper: new GitHelper(),
updateHelper: null, updateHelper: null,
async configureModules(modules) { async configureModules (modules) {
for (const moduleName of modules) { for (const moduleName of modules) {
if (!this.ignoreUpdateChecking(moduleName)) { if (!this.ignoreUpdateChecking(moduleName)) {
await this.gitHelper.add(moduleName); await this.gitHelper.add(moduleName);
@ -26,7 +26,7 @@ module.exports = NodeHelper.create({
} }
}, },
async socketNotificationReceived(notification, payload) { async socketNotificationReceived (notification, payload) {
switch (notification) { switch (notification) {
case "CONFIG": case "CONFIG":
this.config = payload; this.config = payload;
@ -51,7 +51,7 @@ module.exports = NodeHelper.create({
} }
}, },
async performFetch() { async performFetch () {
const repos = await this.gitHelper.getRepos(); const repos = await this.gitHelper.getRepos();
for (const repo of repos) { for (const repo of repos) {
@ -76,7 +76,7 @@ module.exports = NodeHelper.create({
this.scheduleNextFetch(this.config.updateInterval); this.scheduleNextFetch(this.config.updateInterval);
}, },
scheduleNextFetch(delay) { scheduleNextFetch (delay) {
clearTimeout(this.updateTimer); clearTimeout(this.updateTimer);
this.updateTimer = setTimeout( this.updateTimer = setTimeout(
@ -87,7 +87,7 @@ module.exports = NodeHelper.create({
); );
}, },
ignoreUpdateChecking(moduleName) { ignoreUpdateChecking (moduleName) {
// Should not check for updates for default modules // Should not check for updates for default modules
if (defaultModules.includes(moduleName)) { if (defaultModules.includes(moduleName)) {
return true; return true;

View File

@ -39,7 +39,7 @@ const Log = require("logger");
*/ */
class Updater { class Updater {
constructor(config) { constructor (config) {
this.updates = config.updates; this.updates = config.updates;
this.timeout = config.updateTimeout; this.timeout = config.updateTimeout;
this.autoRestart = config.updateAutorestart; this.autoRestart = config.updateAutorestart;
@ -53,7 +53,7 @@ class Updater {
} }
// [main command] parse if module update is needed // [main command] parse if module update is needed
async parse(modules) { async parse (modules) {
var parser = modules.map(async (module) => { var parser = modules.map(async (module) => {
if (this.moduleList[module.module] === undefined) { if (this.moduleList[module.module] === undefined) {
this.moduleList[module.module] = {}; this.moduleList[module.module] = {};
@ -90,7 +90,7 @@ class Updater {
// updated: <boolean>, // if updated successfully // updated: <boolean>, // if updated successfully
// needRestart: <boolean> // if magicmirror restart required // needRestart: <boolean> // if magicmirror restart required
//}; //};
updateProcess(module) { updateProcess (module) {
let Result = { let Result = {
error: false, error: false,
updated: false, updated: false,
@ -130,13 +130,13 @@ class Updater {
} }
// restart rules (pm2 or npm start) // restart rules (pm2 or npm start)
restart() { restart () {
if (this.usePM2) this.pm2Restart(); if (this.usePM2) this.pm2Restart();
else this.npmRestart(); else this.npmRestart();
} }
// restart MagicMiror with "pm2" // restart MagicMiror with "pm2"
pm2Restart() { pm2Restart () {
Log.info("updatenotification: PM2 will restarting MagicMirror..."); Log.info("updatenotification: PM2 will restarting MagicMirror...");
Exec(`pm2 restart ${this.PM2}`, (err, std, sde) => { Exec(`pm2 restart ${this.PM2}`, (err, std, sde) => {
if (err) { if (err) {
@ -146,7 +146,7 @@ class Updater {
} }
// restart MagicMiror with "npm start" // restart MagicMiror with "npm start"
npmRestart() { npmRestart () {
Log.info("updatenotification: Restarting MagicMirror..."); Log.info("updatenotification: Restarting MagicMirror...");
const out = process.stdout; const out = process.stdout;
const err = process.stderr; const err = process.stderr;
@ -156,7 +156,7 @@ class Updater {
} }
// Check using pm2 // Check using pm2
check_PM2_Process() { check_PM2_Process () {
Log.info("updatenotification: Checking PM2 using..."); Log.info("updatenotification: Checking PM2 using...");
return new Promise((resolve) => { return new Promise((resolve) => {
commandExists("pm2") commandExists("pm2")
@ -191,7 +191,7 @@ class Updater {
} }
// Get the list of pm2 process // Get the list of pm2 process
PM2_GetList() { PM2_GetList () {
return new Promise((resolve) => { return new Promise((resolve) => {
Exec("pm2 jlist", (err, std, sde) => { Exec("pm2 jlist", (err, std, sde) => {
if (err) { if (err) {
@ -211,13 +211,13 @@ class Updater {
} }
// check if module is MagicMirror // check if module is MagicMirror
isMagicMirror(module) { isMagicMirror (module) {
if (module === "MagicMirror") return true; if (module === "MagicMirror") return true;
return false; return false;
} }
// search update module command // search update module command
applyCommand(module) { applyCommand (module) {
if (this.isMagicMirror(module.module)) return null; if (this.isMagicMirror(module.module)) return null;
let command = null; let command = null;
this.updates.forEach((updater) => { this.updates.forEach((updater) => {

View File

@ -20,7 +20,7 @@ Module.register("updatenotification", {
needRestart: false, needRestart: false,
updates: {}, updates: {},
start() { start () {
Log.info(`Starting module: ${this.name}`); Log.info(`Starting module: ${this.name}`);
this.addFilters(); this.addFilters();
setInterval(() => { setInterval(() => {
@ -29,16 +29,16 @@ Module.register("updatenotification", {
}, this.config.refreshInterval); }, this.config.refreshInterval);
}, },
suspend() { suspend () {
this.suspended = true; this.suspended = true;
}, },
resume() { resume () {
this.suspended = false; this.suspended = false;
this.updateDom(2); this.updateDom(2);
}, },
notificationReceived(notification) { notificationReceived (notification) {
switch (notification) { switch (notification) {
case "DOM_OBJECTS_CREATED": case "DOM_OBJECTS_CREATED":
this.sendSocketNotification("CONFIG", this.config); this.sendSocketNotification("CONFIG", this.config);
@ -50,7 +50,7 @@ Module.register("updatenotification", {
} }
}, },
socketNotificationReceived(notification, payload) { socketNotificationReceived (notification, payload) {
switch (notification) { switch (notification) {
case "REPO_STATUS": case "REPO_STATUS":
this.updateUI(payload); this.updateUI(payload);
@ -64,19 +64,19 @@ Module.register("updatenotification", {
} }
}, },
getStyles() { getStyles () {
return [`${this.name}.css`]; return [`${this.name}.css`];
}, },
getTemplate() { getTemplate () {
return `${this.name}.njk`; return `${this.name}.njk`;
}, },
getTemplateData() { getTemplateData () {
return { moduleList: this.moduleList, updatesList: this.updates, suspended: this.suspended, needRestart: this.needRestart }; return { moduleList: this.moduleList, updatesList: this.updates, suspended: this.suspended, needRestart: this.needRestart };
}, },
updateUI(payload) { updateUI (payload) {
if (payload && payload.behind > 0) { if (payload && payload.behind > 0) {
// if we haven't seen info for this module // if we haven't seen info for this module
if (this.moduleList[payload.module] === undefined) { if (this.moduleList[payload.module] === undefined) {
@ -94,7 +94,7 @@ Module.register("updatenotification", {
} }
}, },
addFilters() { addFilters () {
this.nunjucksEnvironment().addFilter("diffLink", (text, status) => { this.nunjucksEnvironment().addFilter("diffLink", (text, status) => {
if (status.module !== "MagicMirror") { if (status.module !== "MagicMirror") {
return text; return text;
@ -106,7 +106,7 @@ Module.register("updatenotification", {
}); });
}, },
updatesNotifier(payload, done = true) { updatesNotifier (payload, done = true) {
if (this.updates[payload.name] === undefined) { if (this.updates[payload.name] === undefined) {
this.updates[payload.name] = { this.updates[payload.name] = {
name: payload.name, name: payload.name,

View File

@ -7,7 +7,7 @@
* @param {Array.<string>} expectedResponseHeaders the expected HTTP headers to receive * @param {Array.<string>} expectedResponseHeaders the expected HTTP headers to receive
* @returns {Promise} resolved when the fetch is done. The response headers is placed in a headers-property (provided the response does not already contain a headers-property). * @returns {Promise} resolved when the fetch is done. The response headers is placed in a headers-property (provided the response does not already contain a headers-property).
*/ */
async function performWebRequest(url, type = "json", useCorsProxy = false, requestHeaders = undefined, expectedResponseHeaders = undefined) { async function performWebRequest (url, type = "json", useCorsProxy = false, requestHeaders = undefined, expectedResponseHeaders = undefined) {
const request = {}; const request = {};
let requestUrl; let requestUrl;
if (useCorsProxy) { if (useCorsProxy) {
@ -165,8 +165,7 @@ const formatTime = (config, time) => {
return date.format("HH:mm"); return date.format("HH:mm");
}; };
if (typeof module !== "undefined") if (typeof module !== "undefined") module.exports = {
module.exports = { performWebRequest,
performWebRequest, formatTime
formatTime };
};

View File

@ -49,7 +49,7 @@ WeatherProvider.register("envcanada", {
// Set config values (equates to weather module config values). Also set values pertaining to caching of // Set config values (equates to weather module config values). Also set values pertaining to caching of
// Today's temperature forecast (for use in the Forecast functions below) // Today's temperature forecast (for use in the Forecast functions below)
// //
setConfig: function (config) { setConfig (config) {
this.config = config; this.config = config;
this.todayTempCacheMin = 0; this.todayTempCacheMin = 0;
@ -61,7 +61,7 @@ WeatherProvider.register("envcanada", {
// //
// Called when the weather provider is started // Called when the weather provider is started
// //
start: function () { start () {
Log.info(`Weather provider: ${this.providerName} started.`); Log.info(`Weather provider: ${this.providerName} started.`);
this.setFetchedLocation(this.config.location); this.setFetchedLocation(this.config.location);
}, },
@ -69,7 +69,7 @@ WeatherProvider.register("envcanada", {
// //
// Override the fetchCurrentWeather method to query EC and construct a Current weather object // Override the fetchCurrentWeather method to query EC and construct a Current weather object
// //
fetchCurrentWeather() { fetchCurrentWeather () {
this.fetchData(this.getUrl(), "xml") this.fetchData(this.getUrl(), "xml")
.then((data) => { .then((data) => {
if (!data) { if (!data) {
@ -89,7 +89,7 @@ WeatherProvider.register("envcanada", {
// //
// Override the fetchWeatherForecast method to query EC and construct Forecast weather objects // Override the fetchWeatherForecast method to query EC and construct Forecast weather objects
// //
fetchWeatherForecast() { fetchWeatherForecast () {
this.fetchData(this.getUrl(), "xml") this.fetchData(this.getUrl(), "xml")
.then((data) => { .then((data) => {
if (!data) { if (!data) {
@ -109,7 +109,7 @@ WeatherProvider.register("envcanada", {
// //
// Override the fetchWeatherHourly method to query EC and construct Forecast weather objects // Override the fetchWeatherHourly method to query EC and construct Forecast weather objects
// //
fetchWeatherHourly() { fetchWeatherHourly () {
this.fetchData(this.getUrl(), "xml") this.fetchData(this.getUrl(), "xml")
.then((data) => { .then((data) => {
if (!data) { if (!data) {
@ -137,7 +137,7 @@ WeatherProvider.register("envcanada", {
// URL defaults to the English version simply because there is no language dependency in the data // URL defaults to the English version simply because there is no language dependency in the data
// being accessed. This is only pertinent when using the EC data elements that contain a textual forecast. // being accessed. This is only pertinent when using the EC data elements that contain a textual forecast.
// //
getUrl() { getUrl () {
return `https://dd.weather.gc.ca/citypage_weather/xml/${this.config.provCode}/${this.config.siteCode}_e.xml`; return `https://dd.weather.gc.ca/citypage_weather/xml/${this.config.provCode}/${this.config.siteCode}_e.xml`;
}, },
@ -145,7 +145,7 @@ WeatherProvider.register("envcanada", {
// Generate a WeatherObject based on current EC weather conditions // Generate a WeatherObject based on current EC weather conditions
// //
generateWeatherObjectFromCurrentWeather(ECdoc) { generateWeatherObjectFromCurrentWeather (ECdoc) {
const currentWeather = new WeatherObject(); const currentWeather = new WeatherObject();
// There are instances where EC will update weather data and current temperature will not be // There are instances where EC will update weather data and current temperature will not be
@ -216,7 +216,7 @@ WeatherProvider.register("envcanada", {
// Generate an array of WeatherObjects based on EC weather forecast // Generate an array of WeatherObjects based on EC weather forecast
// //
generateWeatherObjectsFromForecast(ECdoc) { generateWeatherObjectsFromForecast (ECdoc) {
// Declare an array to hold each day's forecast object // Declare an array to hold each day's forecast object
const days = []; const days = [];
@ -360,7 +360,7 @@ WeatherProvider.register("envcanada", {
// Generate an array of WeatherObjects based on EC hourly weather forecast // Generate an array of WeatherObjects based on EC hourly weather forecast
// //
generateWeatherObjectsFromHourly(ECdoc) { generateWeatherObjectsFromHourly (ECdoc) {
// Declare an array to hold each hour's forecast object // Declare an array to hold each hour's forecast object
const hours = []; const hours = [];
@ -416,7 +416,7 @@ WeatherProvider.register("envcanada", {
// the next Forecast element should be considered - i.e. look at Today *and* Tonight vs.Tonight-only // the next Forecast element should be considered - i.e. look at Today *and* Tonight vs.Tonight-only
// //
setMinMaxTemps(weather, foreGroup, today, fullDay, currentTemp) { setMinMaxTemps (weather, foreGroup, today, fullDay, currentTemp) {
const todayTemp = foreGroup[today].querySelector("temperatures temperature").textContent; const todayTemp = foreGroup[today].querySelector("temperatures temperature").textContent;
const todayClass = foreGroup[today].querySelector("temperatures temperature").getAttribute("class"); const todayClass = foreGroup[today].querySelector("temperatures temperature").getAttribute("class");
@ -498,7 +498,7 @@ WeatherProvider.register("envcanada", {
// the nightime forecast after a certain point in that specific scenario. // the nightime forecast after a certain point in that specific scenario.
// //
setPrecipitation(weather, foreGroup, today) { setPrecipitation (weather, foreGroup, today) {
if (foreGroup[today].querySelector("precipitation accumulation")) { if (foreGroup[today].querySelector("precipitation accumulation")) {
weather.precipitationAmount = foreGroup[today].querySelector("precipitation accumulation amount").textContent * 1.0; weather.precipitationAmount = foreGroup[today].querySelector("precipitation accumulation amount").textContent * 1.0;
weather.precipitationUnits = foreGroup[today].querySelector("precipitation accumulation amount").getAttribute("units"); weather.precipitationUnits = foreGroup[today].querySelector("precipitation accumulation amount").getAttribute("units");
@ -514,7 +514,7 @@ WeatherProvider.register("envcanada", {
// //
// Convert the icons to a more usable name. // Convert the icons to a more usable name.
// //
convertWeatherType(weatherType) { convertWeatherType (weatherType) {
const weatherTypes = { const weatherTypes = {
"00": "day-sunny", "00": "day-sunny",
"01": "day-sunny", "01": "day-sunny",

View File

@ -140,11 +140,11 @@ WeatherProvider.register("openmeteo", {
"et0_fao_evapotranspiration" "et0_fao_evapotranspiration"
], ],
fetchedLocation: function () { fetchedLocation () {
return this.fetchedLocationName || ""; return this.fetchedLocationName || "";
}, },
fetchCurrentWeather() { fetchCurrentWeather () {
this.fetchData(this.getUrl()) this.fetchData(this.getUrl())
.then((data) => this.parseWeatherApiResponse(data)) .then((data) => this.parseWeatherApiResponse(data))
.then((parsedData) => { .then((parsedData) => {
@ -162,7 +162,7 @@ WeatherProvider.register("openmeteo", {
.finally(() => this.updateAvailable()); .finally(() => this.updateAvailable());
}, },
fetchWeatherForecast() { fetchWeatherForecast () {
this.fetchData(this.getUrl()) this.fetchData(this.getUrl())
.then((data) => this.parseWeatherApiResponse(data)) .then((data) => this.parseWeatherApiResponse(data))
.then((parsedData) => { .then((parsedData) => {
@ -180,7 +180,7 @@ WeatherProvider.register("openmeteo", {
.finally(() => this.updateAvailable()); .finally(() => this.updateAvailable());
}, },
fetchWeatherHourly() { fetchWeatherHourly () {
this.fetchData(this.getUrl()) this.fetchData(this.getUrl())
.then((data) => this.parseWeatherApiResponse(data)) .then((data) => this.parseWeatherApiResponse(data))
.then((parsedData) => { .then((parsedData) => {
@ -202,7 +202,7 @@ WeatherProvider.register("openmeteo", {
* Overrides method for setting config to check if endpoint is correct for hourly * Overrides method for setting config to check if endpoint is correct for hourly
* @param {object} config The configuration object * @param {object} config The configuration object
*/ */
setConfig(config) { setConfig (config) {
this.config = { this.config = {
lang: config.lang ?? "en", lang: config.lang ?? "en",
...this.defaults, ...this.defaults,
@ -226,7 +226,7 @@ WeatherProvider.register("openmeteo", {
}, },
// Generate valid query params to perform the request // Generate valid query params to perform the request
getQueryParameters() { getQueryParameters () {
let params = { let params = {
latitude: this.config.lat, latitude: this.config.lat,
longitude: this.config.lon, longitude: this.config.lon,
@ -278,25 +278,23 @@ WeatherProvider.register("openmeteo", {
}, },
// Create a URL from the config and base URL. // Create a URL from the config and base URL.
getUrl() { getUrl () {
return `${this.config.apiBase}/forecast?${this.getQueryParameters()}`; return `${this.config.apiBase}/forecast?${this.getQueryParameters()}`;
}, },
// Transpose hourly and daily data matrices // Transpose hourly and daily data matrices
transposeDataMatrix(data) { transposeDataMatrix (data) {
return data.time.map((_, index) => return data.time.map((_, index) => Object.keys(data).reduce((row, key) => {
Object.keys(data).reduce((row, key) => { return {
return { ...row,
...row, // Parse time values as momentjs instances
// Parse time values as momentjs instances [key]: ["time", "sunrise", "sunset"].includes(key) ? moment.unix(data[key][index]) : data[key][index]
[key]: ["time", "sunrise", "sunset"].includes(key) ? moment.unix(data[key][index]) : data[key][index] };
}; }, {}));
}, {})
);
}, },
// Sanitize and validate API response // Sanitize and validate API response
parseWeatherApiResponse(data) { parseWeatherApiResponse (data) {
const validByType = { const validByType = {
current: data.current_weather && data.current_weather.time, current: data.current_weather && data.current_weather.time,
hourly: data.hourly && data.hourly.time && Array.isArray(data.hourly.time) && data.hourly.time.length > 0, hourly: data.hourly && data.hourly.time && Array.isArray(data.hourly.time) && data.hourly.time.length > 0,
@ -334,7 +332,7 @@ WeatherProvider.register("openmeteo", {
}, },
// Reverse geocoding from latitude and longitude provided // Reverse geocoding from latitude and longitude provided
fetchLocation() { fetchLocation () {
this.fetchData(`${GEOCODE_BASE}?latitude=${this.config.lat}&longitude=${this.config.lon}&localityLanguage=${this.config.lang}`) this.fetchData(`${GEOCODE_BASE}?latitude=${this.config.lat}&longitude=${this.config.lon}&localityLanguage=${this.config.lang}`)
.then((data) => { .then((data) => {
if (!data || !data.city) { if (!data || !data.city) {
@ -349,7 +347,8 @@ WeatherProvider.register("openmeteo", {
}, },
// Implement WeatherDay generator. // Implement WeatherDay generator.
generateWeatherDayFromCurrentWeather(weather) { generateWeatherDayFromCurrentWeather (weather) {
/** /**
* Since some units comes from API response "splitted" into daily, hourly and current_weather * Since some units comes from API response "splitted" into daily, hourly and current_weather
* every time you request it, you have to ensure to get the data from the right place every time. * every time you request it, you have to ensure to get the data from the right place every time.
@ -394,7 +393,7 @@ WeatherProvider.register("openmeteo", {
}, },
// Implement WeatherForecast generator. // Implement WeatherForecast generator.
generateWeatherObjectsFromForecast(weathers) { generateWeatherObjectsFromForecast (weathers) {
const days = []; const days = [];
weathers.daily.forEach((weather) => { weathers.daily.forEach((weather) => {
@ -422,7 +421,7 @@ WeatherProvider.register("openmeteo", {
}, },
// Implement WeatherHourly generator. // Implement WeatherHourly generator.
generateWeatherObjectsFromHourly(weathers) { generateWeatherObjectsFromHourly (weathers) {
const hours = []; const hours = [];
const now = moment(); const now = moment();
@ -457,7 +456,7 @@ WeatherProvider.register("openmeteo", {
}, },
// Map icons from Dark Sky to our icons. // Map icons from Dark Sky to our icons.
convertWeatherType(weathercode, isDayTime) { convertWeatherType (weathercode, isDayTime) {
const weatherConditions = { const weatherConditions = {
0: "clear", 0: "clear",
1: "mainly-clear", 1: "mainly-clear",
@ -542,7 +541,7 @@ WeatherProvider.register("openmeteo", {
}, },
// Define required scripts. // Define required scripts.
getScripts: function () { getScripts () {
return ["moment.js"]; return ["moment.js"];
} }
}); });

View File

@ -27,7 +27,7 @@ WeatherProvider.register("openweathermap", {
}, },
// Overwrite the fetchCurrentWeather method. // Overwrite the fetchCurrentWeather method.
fetchCurrentWeather() { fetchCurrentWeather () {
this.fetchData(this.getUrl()) this.fetchData(this.getUrl())
.then((data) => { .then((data) => {
let currentWeather; let currentWeather;
@ -46,7 +46,7 @@ WeatherProvider.register("openweathermap", {
}, },
// Overwrite the fetchWeatherForecast method. // Overwrite the fetchWeatherForecast method.
fetchWeatherForecast() { fetchWeatherForecast () {
this.fetchData(this.getUrl()) this.fetchData(this.getUrl())
.then((data) => { .then((data) => {
let forecast; let forecast;
@ -68,7 +68,7 @@ WeatherProvider.register("openweathermap", {
}, },
// Overwrite the fetchWeatherHourly method. // Overwrite the fetchWeatherHourly method.
fetchWeatherHourly() { fetchWeatherHourly () {
this.fetchData(this.getUrl()) this.fetchData(this.getUrl())
.then((data) => { .then((data) => {
if (!data) { if (!data) {
@ -92,7 +92,7 @@ WeatherProvider.register("openweathermap", {
* Overrides method for setting config to check if endpoint is correct for hourly * Overrides method for setting config to check if endpoint is correct for hourly
* @param {object} config The configuration object * @param {object} config The configuration object
*/ */
setConfig(config) { setConfig (config) {
this.config = config; this.config = config;
if (!this.config.weatherEndpoint) { if (!this.config.weatherEndpoint) {
switch (this.config.type) { switch (this.config.type) {
@ -116,14 +116,14 @@ WeatherProvider.register("openweathermap", {
/* /*
* Gets the complete url for the request * Gets the complete url for the request
*/ */
getUrl() { getUrl () {
return this.config.apiBase + this.config.apiVersion + this.config.weatherEndpoint + this.getParams(); return this.config.apiBase + this.config.apiVersion + this.config.weatherEndpoint + this.getParams();
}, },
/* /*
* Generate a WeatherObject based on currentWeatherInformation * Generate a WeatherObject based on currentWeatherInformation
*/ */
generateWeatherObjectFromCurrentWeather(currentWeatherData) { generateWeatherObjectFromCurrentWeather (currentWeatherData) {
const currentWeather = new WeatherObject(); const currentWeather = new WeatherObject();
currentWeather.date = moment.unix(currentWeatherData.dt); currentWeather.date = moment.unix(currentWeatherData.dt);
@ -142,7 +142,7 @@ WeatherProvider.register("openweathermap", {
/* /*
* Generate WeatherObjects based on forecast information * Generate WeatherObjects based on forecast information
*/ */
generateWeatherObjectsFromForecast(forecasts) { generateWeatherObjectsFromForecast (forecasts) {
if (this.config.weatherEndpoint === "/forecast") { if (this.config.weatherEndpoint === "/forecast") {
return this.generateForecastHourly(forecasts); return this.generateForecastHourly(forecasts);
} else if (this.config.weatherEndpoint === "/forecast/daily") { } else if (this.config.weatherEndpoint === "/forecast/daily") {
@ -155,7 +155,7 @@ WeatherProvider.register("openweathermap", {
/* /*
* Generate WeatherObjects based on One Call forecast information * Generate WeatherObjects based on One Call forecast information
*/ */
generateWeatherObjectsFromOnecall(data) { generateWeatherObjectsFromOnecall (data) {
if (this.config.weatherEndpoint === "/onecall") { if (this.config.weatherEndpoint === "/onecall") {
return this.fetchOnecall(data); return this.fetchOnecall(data);
} }
@ -167,7 +167,7 @@ WeatherProvider.register("openweathermap", {
* Generate forecast information for 3-hourly forecast (available for free * Generate forecast information for 3-hourly forecast (available for free
* subscription). * subscription).
*/ */
generateForecastHourly(forecasts) { generateForecastHourly (forecasts) {
// initial variable declaration // initial variable declaration
const days = []; const days = [];
// variables for temperature range and rain // variables for temperature range and rain
@ -241,7 +241,7 @@ WeatherProvider.register("openweathermap", {
* Generate forecast information for daily forecast (available for paid * Generate forecast information for daily forecast (available for paid
* subscription or old apiKey). * subscription or old apiKey).
*/ */
generateForecastDaily(forecasts) { generateForecastDaily (forecasts) {
// initial variable declaration // initial variable declaration
const days = []; const days = [];
@ -281,7 +281,7 @@ WeatherProvider.register("openweathermap", {
* Factors in timezone offsets. * Factors in timezone offsets.
* Minutely forecasts are excluded for the moment, see getParams(). * Minutely forecasts are excluded for the moment, see getParams().
*/ */
fetchOnecall(data) { fetchOnecall (data) {
let precip = false; let precip = false;
// get current weather, if requested // get current weather, if requested
@ -382,7 +382,7 @@ WeatherProvider.register("openweathermap", {
/* /*
* Convert the OpenWeatherMap icons to a more usable name. * Convert the OpenWeatherMap icons to a more usable name.
*/ */
convertWeatherType(weatherType) { convertWeatherType (weatherType) {
const weatherTypes = { const weatherTypes = {
"01d": "day-sunny", "01d": "day-sunny",
"02d": "day-cloudy", "02d": "day-cloudy",
@ -412,7 +412,7 @@ WeatherProvider.register("openweathermap", {
* *
* return String - URL params. * return String - URL params.
*/ */
getParams() { getParams () {
let params = "?"; let params = "?";
if (this.config.weatherEndpoint === "/onecall") { if (this.config.weatherEndpoint === "/onecall") {
params += `lat=${this.config.lat}`; params += `lat=${this.config.lat}`;

View File

@ -21,7 +21,7 @@ const OverrideWrapper = Class.extend({
notificationWeatherObject: null, notificationWeatherObject: null,
currentOverrideWeatherObject: null, currentOverrideWeatherObject: null,
init(baseProvider) { init (baseProvider) {
this.baseProvider = baseProvider; this.baseProvider = baseProvider;
// Binding the scope of current weather functions so any fetchData calls with // Binding the scope of current weather functions so any fetchData calls with
@ -33,43 +33,43 @@ const OverrideWrapper = Class.extend({
/* Unchanged Api Provider Methods */ /* Unchanged Api Provider Methods */
setConfig(config) { setConfig (config) {
this.baseProvider.setConfig(config); this.baseProvider.setConfig(config);
}, },
start() { start () {
this.baseProvider.start(); this.baseProvider.start();
}, },
fetchCurrentWeather() { fetchCurrentWeather () {
this.baseProvider.fetchCurrentWeather(); this.baseProvider.fetchCurrentWeather();
}, },
fetchWeatherForecast() { fetchWeatherForecast () {
this.baseProvider.fetchWeatherForecast(); this.baseProvider.fetchWeatherForecast();
}, },
fetchWeatherHourly() { fetchWeatherHourly () {
this.baseProvider.fetchEatherHourly(); this.baseProvider.fetchEatherHourly();
}, },
weatherForecast() { weatherForecast () {
this.baseProvider.weatherForecast(); this.baseProvider.weatherForecast();
}, },
weatherHourly() { weatherHourly () {
this.baseProvider.weatherHourly(); this.baseProvider.weatherHourly();
}, },
fetchedLocation() { fetchedLocation () {
this.baseProvider.fetchedLocation(); this.baseProvider.fetchedLocation();
}, },
setWeatherForecast(weatherForecastArray) { setWeatherForecast (weatherForecastArray) {
this.baseProvider.setWeatherForecast(weatherForecastArray); this.baseProvider.setWeatherForecast(weatherForecastArray);
}, },
setWeatherHourly(weatherHourlyArray) { setWeatherHourly (weatherHourlyArray) {
this.baseProvider.setWeatherHourly(weatherHourlyArray); this.baseProvider.setWeatherHourly(weatherHourlyArray);
}, },
setFetchedLocation(name) { setFetchedLocation (name) {
this.baseProvider.setFetchedLocation(name); this.baseProvider.setFetchedLocation(name);
}, },
updateAvailable() { updateAvailable () {
this.baseProvider.updateAvailable(); this.baseProvider.updateAvailable();
}, },
async fetchData(url, type = "json", requestHeaders = undefined, expectedResponseHeaders = undefined) { async fetchData (url, type = "json", requestHeaders = undefined, expectedResponseHeaders = undefined) {
this.baseProvider.fetchData(url, type, requestHeaders, expectedResponseHeaders); this.baseProvider.fetchData(url, type, requestHeaders, expectedResponseHeaders);
}, },
@ -79,7 +79,7 @@ const OverrideWrapper = Class.extend({
* Override to return this scope's * Override to return this scope's
* @returns {WeatherObject} The current weather object. May or may not contain overridden data. * @returns {WeatherObject} The current weather object. May or may not contain overridden data.
*/ */
currentWeather() { currentWeather () {
return this.currentOverrideWeatherObject; return this.currentOverrideWeatherObject;
}, },
@ -89,7 +89,7 @@ const OverrideWrapper = Class.extend({
* api provider fetchData implementation. * api provider fetchData implementation.
* @param {WeatherObject} currentWeatherObject - the api provider weather object * @param {WeatherObject} currentWeatherObject - the api provider weather object
*/ */
setCurrentWeather(currentWeatherObject) { setCurrentWeather (currentWeatherObject) {
this.currentOverrideWeatherObject = Object.assign(currentWeatherObject, this.notificationWeatherObject); this.currentOverrideWeatherObject = Object.assign(currentWeatherObject, this.notificationWeatherObject);
}, },
@ -101,7 +101,7 @@ const OverrideWrapper = Class.extend({
* notification. Represents information to augment the * notification. Represents information to augment the
* existing currentOverrideWeatherObject with. * existing currentOverrideWeatherObject with.
*/ */
notificationReceived(payload) { notificationReceived (payload) {
this.notificationWeatherObject = payload; this.notificationWeatherObject = payload;
// setCurrentWeather combines the newly received notification weather with // setCurrentWeather combines the newly received notification weather with

View File

@ -25,7 +25,7 @@ WeatherProvider.register("pirateweather", {
lon: 0 lon: 0
}, },
fetchCurrentWeather() { fetchCurrentWeather () {
this.fetchData(this.getUrl()) this.fetchData(this.getUrl())
.then((data) => { .then((data) => {
if (!data || !data.currently || typeof data.currently.temperature === "undefined") { if (!data || !data.currently || typeof data.currently.temperature === "undefined") {
@ -42,7 +42,7 @@ WeatherProvider.register("pirateweather", {
.finally(() => this.updateAvailable()); .finally(() => this.updateAvailable());
}, },
fetchWeatherForecast() { fetchWeatherForecast () {
this.fetchData(this.getUrl()) this.fetchData(this.getUrl())
.then((data) => { .then((data) => {
if (!data || !data.daily || !data.daily.data.length) { if (!data || !data.daily || !data.daily.data.length) {
@ -60,12 +60,12 @@ WeatherProvider.register("pirateweather", {
}, },
// Create a URL from the config and base URL. // Create a URL from the config and base URL.
getUrl() { getUrl () {
return `${this.config.apiBase}${this.config.weatherEndpoint}/${this.config.apiKey}/${this.config.lat},${this.config.lon}?units=si&lang=${this.config.lang}`; return `${this.config.apiBase}${this.config.weatherEndpoint}/${this.config.apiKey}/${this.config.lat},${this.config.lon}?units=si&lang=${this.config.lang}`;
}, },
// Implement WeatherDay generator. // Implement WeatherDay generator.
generateWeatherDayFromCurrentWeather(currentWeatherData) { generateWeatherDayFromCurrentWeather (currentWeatherData) {
const currentWeather = new WeatherObject(); const currentWeather = new WeatherObject();
currentWeather.date = moment(); currentWeather.date = moment();
@ -80,7 +80,7 @@ WeatherProvider.register("pirateweather", {
return currentWeather; return currentWeather;
}, },
generateWeatherObjectsFromForecast(forecasts) { generateWeatherObjectsFromForecast (forecasts) {
const days = []; const days = [];
for (const forecast of forecasts) { for (const forecast of forecasts) {
@ -114,7 +114,7 @@ WeatherProvider.register("pirateweather", {
}, },
// Map icons from Pirate Weather to our icons. // Map icons from Pirate Weather to our icons.
convertWeatherType(weatherType) { convertWeatherType (weatherType) {
const weatherTypes = { const weatherTypes = {
"clear-day": "day-sunny", "clear-day": "day-sunny",
"clear-night": "night-clear", "clear-night": "night-clear",

View File

@ -24,7 +24,7 @@ WeatherProvider.register("smhi", {
/** /**
* Implements method in interface for fetching current weather. * Implements method in interface for fetching current weather.
*/ */
fetchCurrentWeather() { fetchCurrentWeather () {
this.fetchData(this.getURL()) this.fetchData(this.getURL())
.then((data) => { .then((data) => {
const closest = this.getClosestToCurrentTime(data.timeSeries); const closest = this.getClosestToCurrentTime(data.timeSeries);
@ -40,7 +40,7 @@ WeatherProvider.register("smhi", {
/** /**
* Implements method in interface for fetching a multi-day forecast. * Implements method in interface for fetching a multi-day forecast.
*/ */
fetchWeatherForecast() { fetchWeatherForecast () {
this.fetchData(this.getURL()) this.fetchData(this.getURL())
.then((data) => { .then((data) => {
const coordinates = this.resolveCoordinates(data); const coordinates = this.resolveCoordinates(data);
@ -55,7 +55,7 @@ WeatherProvider.register("smhi", {
/** /**
* Implements method in interface for fetching hourly forecasts. * Implements method in interface for fetching hourly forecasts.
*/ */
fetchWeatherHourly() { fetchWeatherHourly () {
this.fetchData(this.getURL()) this.fetchData(this.getURL())
.then((data) => { .then((data) => {
const coordinates = this.resolveCoordinates(data); const coordinates = this.resolveCoordinates(data);
@ -71,7 +71,7 @@ WeatherProvider.register("smhi", {
* Overrides method for setting config with checks for the precipitationValue being unset or invalid * Overrides method for setting config with checks for the precipitationValue being unset or invalid
* @param {object} config The configuration object * @param {object} config The configuration object
*/ */
setConfig(config) { setConfig (config) {
this.config = config; this.config = config;
if (!config.precipitationValue || ["pmin", "pmean", "pmedian", "pmax"].indexOf(config.precipitationValue) === -1) { if (!config.precipitationValue || ["pmin", "pmean", "pmedian", "pmax"].indexOf(config.precipitationValue) === -1) {
Log.log(`invalid or not set: ${config.precipitationValue}`); Log.log(`invalid or not set: ${config.precipitationValue}`);
@ -84,7 +84,7 @@ WeatherProvider.register("smhi", {
* @param {object[]} times Array of time objects * @param {object[]} times Array of time objects
* @returns {object} The weatherdata closest to the current time * @returns {object} The weatherdata closest to the current time
*/ */
getClosestToCurrentTime(times) { getClosestToCurrentTime (times) {
let now = moment(); let now = moment();
let minDiff = undefined; let minDiff = undefined;
for (const time of times) { for (const time of times) {
@ -100,7 +100,7 @@ WeatherProvider.register("smhi", {
* Get the forecast url for the configured coordinates * Get the forecast url for the configured coordinates
* @returns {string} the url for the specified coordinates * @returns {string} the url for the specified coordinates
*/ */
getURL() { getURL () {
const formatter = new Intl.NumberFormat("en-US", { const formatter = new Intl.NumberFormat("en-US", {
minimumFractionDigits: 6, minimumFractionDigits: 6,
maximumFractionDigits: 6 maximumFractionDigits: 6
@ -115,7 +115,7 @@ WeatherProvider.register("smhi", {
* @param {object} weatherData Weatherdata to use for the calculation * @param {object} weatherData Weatherdata to use for the calculation
* @returns {number} The apparent temperature * @returns {number} The apparent temperature
*/ */
calculateApparentTemperature(weatherData) { calculateApparentTemperature (weatherData) {
const Ta = this.paramValue(weatherData, "t"); const Ta = this.paramValue(weatherData, "t");
const rh = this.paramValue(weatherData, "r"); const rh = this.paramValue(weatherData, "r");
const ws = this.paramValue(weatherData, "ws"); const ws = this.paramValue(weatherData, "ws");
@ -132,7 +132,7 @@ WeatherProvider.register("smhi", {
* @param {object} coordinates Coordinates of the locations of the weather * @param {object} coordinates Coordinates of the locations of the weather
* @returns {WeatherObject} The converted weatherdata at the specified location * @returns {WeatherObject} The converted weatherdata at the specified location
*/ */
convertWeatherDataToObject(weatherData, coordinates) { convertWeatherDataToObject (weatherData, coordinates) {
let currentWeather = new WeatherObject(); let currentWeather = new WeatherObject();
currentWeather.date = moment(weatherData.validTime); currentWeather.date = moment(weatherData.validTime);
@ -178,7 +178,7 @@ WeatherProvider.register("smhi", {
* @param {string} groupBy The interval to use for grouping the data (day, hour) * @param {string} groupBy The interval to use for grouping the data (day, hour)
* @returns {WeatherObject[]} Array of weatherobjects * @returns {WeatherObject[]} Array of weatherobjects
*/ */
convertWeatherDataGroupedBy(allWeatherData, coordinates, groupBy = "day") { convertWeatherDataGroupedBy (allWeatherData, coordinates, groupBy = "day") {
let currentWeather; let currentWeather;
let result = []; let result = [];
@ -227,7 +227,7 @@ WeatherProvider.register("smhi", {
* @param {object} data Response data from the weather service * @param {object} data Response data from the weather service
* @returns {{lon, lat}} the lat/long coordinates of the data * @returns {{lon, lat}} the lat/long coordinates of the data
*/ */
resolveCoordinates(data) { resolveCoordinates (data) {
return { lat: data.geometry.coordinates[0][1], lon: data.geometry.coordinates[0][0] }; return { lat: data.geometry.coordinates[0][1], lon: data.geometry.coordinates[0][0] };
}, },
@ -237,7 +237,7 @@ WeatherProvider.register("smhi", {
* @param {object[]} data Response data from the weather service * @param {object[]} data Response data from the weather service
* @returns {object[]} Given data with filled gaps * @returns {object[]} Given data with filled gaps
*/ */
fillInGaps(data) { fillInGaps (data) {
let result = []; let result = [];
for (let i = 1; i < data.length; i++) { for (let i = 1; i < data.length; i++) {
let to = moment(data[i].validTime); let to = moment(data[i].validTime);
@ -259,7 +259,7 @@ WeatherProvider.register("smhi", {
* @param {string} name The name of the property * @param {string} name The name of the property
* @returns {*} The value of the property in the weatherdata * @returns {*} The value of the property in the weatherdata
*/ */
paramValue(currentWeatherData, name) { paramValue (currentWeatherData, name) {
return currentWeatherData.parameters.filter((p) => p.name === name).flatMap((p) => p.values)[0]; return currentWeatherData.parameters.filter((p) => p.name === name).flatMap((p) => p.values)[0];
}, },
@ -271,7 +271,7 @@ WeatherProvider.register("smhi", {
* @param {boolean} isDayTime True if the icon should be for daytime, false for nighttime * @param {boolean} isDayTime True if the icon should be for daytime, false for nighttime
* @returns {string} The icon name for the MagicMirror * @returns {string} The icon name for the MagicMirror
*/ */
convertWeatherType(input, isDayTime) { convertWeatherType (input, isDayTime) {
switch (input) { switch (input) {
case 1: case 1:
return isDayTime ? "day-sunny" : "night-clear"; // Clear sky return isDayTime ? "day-sunny" : "night-clear"; // Clear sky

View File

@ -22,7 +22,7 @@ WeatherProvider.register("ukmetoffice", {
}, },
// Overwrite the fetchCurrentWeather method. // Overwrite the fetchCurrentWeather method.
fetchCurrentWeather() { fetchCurrentWeather () {
this.fetchData(this.getUrl("3hourly")) this.fetchData(this.getUrl("3hourly"))
.then((data) => { .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) { if (!data || !data.SiteRep || !data.SiteRep.DV || !data.SiteRep.DV.Location || !data.SiteRep.DV.Location.Period || data.SiteRep.DV.Location.Period.length === 0) {
@ -43,7 +43,7 @@ WeatherProvider.register("ukmetoffice", {
}, },
// Overwrite the fetchCurrentWeather method. // Overwrite the fetchCurrentWeather method.
fetchWeatherForecast() { fetchWeatherForecast () {
this.fetchData(this.getUrl("daily")) this.fetchData(this.getUrl("daily"))
.then((data) => { .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) { if (!data || !data.SiteRep || !data.SiteRep.DV || !data.SiteRep.DV.Location || !data.SiteRep.DV.Location.Period || data.SiteRep.DV.Location.Period.length === 0) {
@ -67,14 +67,14 @@ WeatherProvider.register("ukmetoffice", {
/* /*
* Gets the complete url for the request * Gets the complete url for the request
*/ */
getUrl(forecastType) { getUrl (forecastType) {
return this.config.apiBase + this.config.locationID + this.getParams(forecastType); return this.config.apiBase + this.config.locationID + this.getParams(forecastType);
}, },
/* /*
* Generate a WeatherObject based on currentWeatherInformation * Generate a WeatherObject based on currentWeatherInformation
*/ */
generateWeatherObjectFromCurrentWeather(currentWeatherData) { generateWeatherObjectFromCurrentWeather (currentWeatherData) {
const currentWeather = new WeatherObject(); const currentWeather = new WeatherObject();
const location = currentWeatherData.SiteRep.DV.Location; const location = currentWeatherData.SiteRep.DV.Location;
@ -119,7 +119,7 @@ WeatherProvider.register("ukmetoffice", {
/* /*
* Generate WeatherObjects based on forecast information * Generate WeatherObjects based on forecast information
*/ */
generateWeatherObjectsFromForecast(forecasts) { generateWeatherObjectsFromForecast (forecasts) {
const days = []; const days = [];
// loop round the (5) periods getting the data // loop round the (5) periods getting the data
@ -150,7 +150,7 @@ WeatherProvider.register("ukmetoffice", {
/* /*
* Convert the Met Office icons to a more usable name. * Convert the Met Office icons to a more usable name.
*/ */
convertWeatherType(weatherType) { convertWeatherType (weatherType) {
const weatherTypes = { const weatherTypes = {
0: "night-clear", 0: "night-clear",
1: "day-sunny", 1: "day-sunny",
@ -192,7 +192,7 @@ WeatherProvider.register("ukmetoffice", {
* @param {string} forecastType daily or 3hourly forecast * @param {string} forecastType daily or 3hourly forecast
* @returns {string} url * @returns {string} url
*/ */
getParams(forecastType) { getParams (forecastType) {
let params = "?"; let params = "?";
params += `res=${forecastType}`; params += `res=${forecastType}`;
params += `&key=${this.config.apiKey}`; params += `&key=${this.config.apiKey}`;

View File

@ -53,7 +53,7 @@ WeatherProvider.register("ukmetofficedatahub", {
}, },
// Build URL with query strings according to DataHub API (https://metoffice.apiconnect.ibmcloud.com/metoffice/production/api) // Build URL with query strings according to DataHub API (https://metoffice.apiconnect.ibmcloud.com/metoffice/production/api)
getUrl(forecastType) { getUrl (forecastType) {
let queryStrings = "?"; let queryStrings = "?";
queryStrings += `latitude=${this.config.lat}`; queryStrings += `latitude=${this.config.lat}`;
queryStrings += `&longitude=${this.config.lon}`; queryStrings += `&longitude=${this.config.lon}`;
@ -66,7 +66,7 @@ WeatherProvider.register("ukmetofficedatahub", {
// Build the list of headers for the request // Build the list of headers for the request
// For DataHub requests, the API key/secret are sent in the headers rather than as query strings. // For DataHub requests, the API key/secret are sent in the headers rather than as query strings.
// Headers defined according to Data Hub API (https://metoffice.apiconnect.ibmcloud.com/metoffice/production/api) // Headers defined according to Data Hub API (https://metoffice.apiconnect.ibmcloud.com/metoffice/production/api)
getHeaders() { getHeaders () {
return { return {
accept: "application/json", accept: "application/json",
"x-ibm-client-id": this.config.apiKey, "x-ibm-client-id": this.config.apiKey,
@ -75,7 +75,7 @@ WeatherProvider.register("ukmetofficedatahub", {
}, },
// Fetch data using supplied URL and request headers // Fetch data using supplied URL and request headers
async fetchWeather(url, headers) { async fetchWeather (url, headers) {
const response = await fetch(url, { headers: headers }); const response = await fetch(url, { headers: headers });
// Return JSON data // Return JSON data
@ -83,7 +83,7 @@ WeatherProvider.register("ukmetofficedatahub", {
}, },
// Fetch hourly forecast data (to use for current weather) // Fetch hourly forecast data (to use for current weather)
fetchCurrentWeather() { fetchCurrentWeather () {
this.fetchWeather(this.getUrl("hourly"), this.getHeaders()) this.fetchWeather(this.getUrl("hourly"), this.getHeaders())
.then((data) => { .then((data) => {
// Check data is usable // Check data is usable
@ -111,7 +111,7 @@ WeatherProvider.register("ukmetofficedatahub", {
}, },
// Create a WeatherObject using current weather data (data for the current hour) // Create a WeatherObject using current weather data (data for the current hour)
generateWeatherObjectFromCurrentWeather(currentWeatherData) { generateWeatherObjectFromCurrentWeather (currentWeatherData) {
const currentWeather = new WeatherObject(); const currentWeather = new WeatherObject();
// Extract the actual forecasts // Extract the actual forecasts
@ -152,7 +152,7 @@ WeatherProvider.register("ukmetofficedatahub", {
}, },
// Fetch daily forecast data // Fetch daily forecast data
fetchWeatherForecast() { fetchWeatherForecast () {
this.fetchWeather(this.getUrl("daily"), this.getHeaders()) this.fetchWeather(this.getUrl("daily"), this.getHeaders())
.then((data) => { .then((data) => {
// Check data is usable // Check data is usable
@ -180,7 +180,7 @@ WeatherProvider.register("ukmetofficedatahub", {
}, },
// Create a WeatherObject for each day using daily forecast data // Create a WeatherObject for each day using daily forecast data
generateWeatherObjectsFromForecast(forecasts) { generateWeatherObjectsFromForecast (forecasts) {
const dailyForecasts = []; const dailyForecasts = [];
// Extract the actual forecasts // Extract the actual forecasts
@ -225,14 +225,14 @@ WeatherProvider.register("ukmetofficedatahub", {
}, },
// Set the fetched location name. // Set the fetched location name.
setFetchedLocation: function (name) { setFetchedLocation (name) {
this.fetchedLocationName = name; this.fetchedLocationName = name;
}, },
// Match the Met Office "significant weather code" to a weathericons.css icon // Match the Met Office "significant weather code" to a weathericons.css icon
// Use: https://metoffice.apiconnect.ibmcloud.com/metoffice/production/node/264 // Use: https://metoffice.apiconnect.ibmcloud.com/metoffice/production/node/264
// and: https://erikflowers.github.io/weather-icons/ // and: https://erikflowers.github.io/weather-icons/
convertWeatherType(weatherType) { convertWeatherType (weatherType) {
const weatherTypes = { const weatherTypes = {
0: "night-clear", 0: "night-clear",
1: "day-sunny", 1: "day-sunny",

View File

@ -23,11 +23,11 @@ WeatherProvider.register("weatherbit", {
lon: 0 lon: 0
}, },
fetchedLocation: function () { fetchedLocation () {
return this.fetchedLocationName || ""; return this.fetchedLocationName || "";
}, },
fetchCurrentWeather() { fetchCurrentWeather () {
this.fetchData(this.getUrl()) this.fetchData(this.getUrl())
.then((data) => { .then((data) => {
if (!data || !data.data[0] || typeof data.data[0].temp === "undefined") { if (!data || !data.data[0] || typeof data.data[0].temp === "undefined") {
@ -44,7 +44,7 @@ WeatherProvider.register("weatherbit", {
.finally(() => this.updateAvailable()); .finally(() => this.updateAvailable());
}, },
fetchWeatherForecast() { fetchWeatherForecast () {
this.fetchData(this.getUrl()) this.fetchData(this.getUrl())
.then((data) => { .then((data) => {
if (!data || !data.data) { if (!data || !data.data) {
@ -67,7 +67,7 @@ WeatherProvider.register("weatherbit", {
* Overrides method for setting config to check if endpoint is correct for hourly * Overrides method for setting config to check if endpoint is correct for hourly
* @param {object} config The configuration object * @param {object} config The configuration object
*/ */
setConfig(config) { setConfig (config) {
this.config = config; this.config = config;
if (!this.config.weatherEndpoint) { if (!this.config.weatherEndpoint) {
switch (this.config.type) { switch (this.config.type) {
@ -88,12 +88,12 @@ WeatherProvider.register("weatherbit", {
}, },
// Create a URL from the config and base URL. // Create a URL from the config and base URL.
getUrl() { getUrl () {
return `${this.config.apiBase}${this.config.weatherEndpoint}?lat=${this.config.lat}&lon=${this.config.lon}&units=M&key=${this.config.apiKey}`; return `${this.config.apiBase}${this.config.weatherEndpoint}?lat=${this.config.lat}&lon=${this.config.lon}&units=M&key=${this.config.apiKey}`;
}, },
// Implement WeatherDay generator. // Implement WeatherDay generator.
generateWeatherDayFromCurrentWeather(currentWeatherData) { generateWeatherDayFromCurrentWeather (currentWeatherData) {
//Calculate TZ Offset and invert to convert Sunrise/Sunset times to Local //Calculate TZ Offset and invert to convert Sunrise/Sunset times to Local
const d = new Date(); const d = new Date();
let tzOffset = d.getTimezoneOffset(); let tzOffset = d.getTimezoneOffset();
@ -115,7 +115,7 @@ WeatherProvider.register("weatherbit", {
return currentWeather; return currentWeather;
}, },
generateWeatherObjectsFromForecast(forecasts) { generateWeatherObjectsFromForecast (forecasts) {
const days = []; const days = [];
for (const forecast of forecasts) { for (const forecast of forecasts) {
@ -135,7 +135,7 @@ WeatherProvider.register("weatherbit", {
}, },
// Map icons from Dark Sky to our icons. // Map icons from Dark Sky to our icons.
convertWeatherType(weatherType) { convertWeatherType (weatherType) {
const weatherTypes = { const weatherTypes = {
t01d: "day-thunderstorm", t01d: "day-thunderstorm",
t01n: "night-alt-thunderstorm", t01n: "night-alt-thunderstorm",

View File

@ -23,7 +23,7 @@ WeatherProvider.register("weatherflow", {
stationid: "" stationid: ""
}, },
fetchCurrentWeather() { fetchCurrentWeather () {
this.fetchData(this.getUrl()) this.fetchData(this.getUrl())
.then((data) => { .then((data) => {
const currentWeather = new WeatherObject(); const currentWeather = new WeatherObject();
@ -44,7 +44,7 @@ WeatherProvider.register("weatherflow", {
.finally(() => this.updateAvailable()); .finally(() => this.updateAvailable());
}, },
fetchWeatherForecast() { fetchWeatherForecast () {
this.fetchData(this.getUrl()) this.fetchData(this.getUrl())
.then((data) => { .then((data) => {
const days = []; const days = [];
@ -71,7 +71,7 @@ WeatherProvider.register("weatherflow", {
}, },
// Create a URL from the config and base URL. // Create a URL from the config and base URL.
getUrl() { getUrl () {
return `${this.config.apiBase}better_forecast?station_id=${this.config.stationid}&units_temp=c&units_wind=kph&units_pressure=mb&units_precip=mm&units_distance=km&token=${this.config.token}`; return `${this.config.apiBase}better_forecast?station_id=${this.config.stationid}&units_temp=c&units_wind=kph&units_pressure=mb&units_precip=mm&units_distance=km&token=${this.config.token}`;
} }
}); });

View File

@ -37,24 +37,24 @@ WeatherProvider.register("weathergov", {
stationObsURL: "tbd", stationObsURL: "tbd",
// Called to set the config, this config is the same as the weather module's config. // Called to set the config, this config is the same as the weather module's config.
setConfig: function (config) { setConfig (config) {
this.config = config; this.config = config;
this.config.apiBase = "https://api.weather.gov"; this.config.apiBase = "https://api.weather.gov";
this.fetchWxGovURLs(this.config); this.fetchWxGovURLs(this.config);
}, },
// Called when the weather provider is about to start. // Called when the weather provider is about to start.
start: function () { start () {
Log.info(`Weather provider: ${this.providerName} started.`); Log.info(`Weather provider: ${this.providerName} started.`);
}, },
// This returns the name of the fetched location or an empty string. // This returns the name of the fetched location or an empty string.
fetchedLocation: function () { fetchedLocation () {
return this.fetchedLocationName || ""; return this.fetchedLocationName || "";
}, },
// Overwrite the fetchCurrentWeather method. // Overwrite the fetchCurrentWeather method.
fetchCurrentWeather() { fetchCurrentWeather () {
if (!this.configURLs) { if (!this.configURLs) {
Log.info("fetchCurrentWeather: fetch wx waiting on config URLs"); Log.info("fetchCurrentWeather: fetch wx waiting on config URLs");
return; return;
@ -75,7 +75,7 @@ WeatherProvider.register("weathergov", {
}, },
// Overwrite the fetchWeatherForecast method. // Overwrite the fetchWeatherForecast method.
fetchWeatherForecast() { fetchWeatherForecast () {
if (!this.configURLs) { if (!this.configURLs) {
Log.info("fetchWeatherForecast: fetch wx waiting on config URLs"); Log.info("fetchWeatherForecast: fetch wx waiting on config URLs");
return; return;
@ -96,7 +96,7 @@ WeatherProvider.register("weathergov", {
}, },
// Overwrite the fetchWeatherHourly method. // Overwrite the fetchWeatherHourly method.
fetchWeatherHourly() { fetchWeatherHourly () {
if (!this.configURLs) { if (!this.configURLs) {
Log.info("fetchWeatherHourly: fetch wx waiting on config URLs"); Log.info("fetchWeatherHourly: fetch wx waiting on config URLs");
return; return;
@ -122,7 +122,7 @@ WeatherProvider.register("weathergov", {
/* /*
* Get specific URLs * Get specific URLs
*/ */
fetchWxGovURLs(config) { fetchWxGovURLs (config) {
this.fetchData(`${config.apiBase}/points/${config.lat},${config.lon}`) this.fetchData(`${config.apiBase}/points/${config.lat},${config.lon}`)
.then((data) => { .then((data) => {
if (!data || !data.properties) { if (!data || !data.properties) {
@ -162,12 +162,13 @@ WeatherProvider.register("weathergov", {
} }
}); });
}, },
/* /*
* Generate a WeatherObject based on hourlyWeatherInformation * Generate a WeatherObject based on hourlyWeatherInformation
* Weather.gov API uses specific units; API does not include choice of units * Weather.gov API uses specific units; API does not include choice of units
* ... object needs data in units based on config! * ... object needs data in units based on config!
*/ */
generateWeatherObjectsFromHourly(forecasts) { generateWeatherObjectsFromHourly (forecasts) {
const days = []; const days = [];
// variable for date // variable for date
@ -206,7 +207,7 @@ WeatherProvider.register("weathergov", {
* Weather.gov API uses specific units; API does not include choice of units * Weather.gov API uses specific units; API does not include choice of units
* ... object needs data in units based on config! * ... object needs data in units based on config!
*/ */
generateWeatherObjectFromCurrentWeather(currentWeatherData) { generateWeatherObjectFromCurrentWeather (currentWeatherData) {
const currentWeather = new WeatherObject(); const currentWeather = new WeatherObject();
currentWeather.date = moment(currentWeatherData.timestamp); currentWeather.date = moment(currentWeatherData.timestamp);
@ -236,14 +237,14 @@ WeatherProvider.register("weathergov", {
/* /*
* Generate WeatherObjects based on forecast information * Generate WeatherObjects based on forecast information
*/ */
generateWeatherObjectsFromForecast(forecasts) { generateWeatherObjectsFromForecast (forecasts) {
return this.fetchForecastDaily(forecasts); return this.fetchForecastDaily(forecasts);
}, },
/* /*
* fetch forecast information for daily forecast. * fetch forecast information for daily forecast.
*/ */
fetchForecastDaily(forecasts) { fetchForecastDaily (forecasts) {
// initial variable declaration // initial variable declaration
const days = []; const days = [];
// variables for temperature range and rain // variables for temperature range and rain
@ -306,7 +307,7 @@ WeatherProvider.register("weathergov", {
/* /*
* Convert the icons to a more usable name. * Convert the icons to a more usable name.
*/ */
convertWeatherType(weatherType, isDaytime) { convertWeatherType (weatherType, isDaytime) {
//https://w1.weather.gov/xml/current_obs/weather.php //https://w1.weather.gov/xml/current_obs/weather.php
// There are way too many types to create, so lets just look for certain strings // There are way too many types to create, so lets just look for certain strings

View File

@ -24,7 +24,7 @@ WeatherProvider.register("yr", {
currentForecastHours: 1 //1, 6 or 12 currentForecastHours: 1 //1, 6 or 12
}, },
start() { start () {
if (typeof Storage === "undefined") { if (typeof Storage === "undefined") {
//local storage unavailable //local storage unavailable
Log.error("The Yr weather provider requires local storage."); Log.error("The Yr weather provider requires local storage.");
@ -33,7 +33,7 @@ WeatherProvider.register("yr", {
Log.info(`Weather provider: ${this.providerName} started.`); Log.info(`Weather provider: ${this.providerName} started.`);
}, },
fetchCurrentWeather() { fetchCurrentWeather () {
this.getCurrentWeather() this.getCurrentWeather()
.then((currentWeather) => { .then((currentWeather) => {
this.setCurrentWeather(currentWeather); this.setCurrentWeather(currentWeather);
@ -45,7 +45,7 @@ WeatherProvider.register("yr", {
}); });
}, },
async getCurrentWeather() { async getCurrentWeather () {
const [weatherData, stellarData] = await Promise.all([this.getWeatherData(), this.getStellarData()]); const [weatherData, stellarData] = await Promise.all([this.getWeatherData(), this.getStellarData()]);
if (!stellarData) { if (!stellarData) {
Log.warn("No stellar data available."); Log.warn("No stellar data available.");
@ -73,7 +73,7 @@ WeatherProvider.register("yr", {
return this.getWeatherDataFrom(forecast, stellarData, weatherData.properties.meta.units); return this.getWeatherDataFrom(forecast, stellarData, weatherData.properties.meta.units);
}, },
getWeatherData() { getWeatherData () {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// If a user has several Yr-modules, for instance one current and one forecast, the API calls must be synchronized across classes. // If a user has several Yr-modules, for instance one current and one forecast, the API calls must be synchronized across classes.
// This is to avoid multiple similar calls to the API. // This is to avoid multiple similar calls to the API.
@ -99,7 +99,7 @@ WeatherProvider.register("yr", {
}); });
}, },
getWeatherDataFromYrOrCache(resolve, reject) { getWeatherDataFromYrOrCache (resolve, reject) {
localStorage.setItem("yrIsFetchingWeatherData", "true"); localStorage.setItem("yrIsFetchingWeatherData", "true");
let weatherData = this.getWeatherDataFromCache(); let weatherData = this.getWeatherDataFromCache();
@ -131,16 +131,16 @@ WeatherProvider.register("yr", {
} }
}, },
weatherDataIsValid(weatherData) { weatherDataIsValid (weatherData) {
return ( return (
weatherData && weatherData
weatherData.timeout && && weatherData.timeout
0 < moment(weatherData.timeout).diff(moment()) && && 0 < moment(weatherData.timeout).diff(moment())
(!weatherData.geometry || !weatherData.geometry.coordinates || !weatherData.geometry.coordinates.length < 2 || (weatherData.geometry.coordinates[0] === this.config.lat && weatherData.geometry.coordinates[1] === this.config.lon)) && (!weatherData.geometry || !weatherData.geometry.coordinates || !weatherData.geometry.coordinates.length < 2 || (weatherData.geometry.coordinates[0] === this.config.lat && weatherData.geometry.coordinates[1] === this.config.lon))
); );
}, },
getWeatherDataFromCache() { getWeatherDataFromCache () {
const weatherData = localStorage.getItem("weatherData"); const weatherData = localStorage.getItem("weatherData");
if (weatherData) { if (weatherData) {
return JSON.parse(weatherData); return JSON.parse(weatherData);
@ -149,7 +149,7 @@ WeatherProvider.register("yr", {
} }
}, },
getWeatherDataFromYr(currentDataFetchedAt) { getWeatherDataFromYr (currentDataFetchedAt) {
const requestHeaders = [{ name: "Accept", value: "application/json" }]; const requestHeaders = [{ name: "Accept", value: "application/json" }];
if (currentDataFetchedAt) { if (currentDataFetchedAt) {
requestHeaders.push({ name: "If-Modified-Since", value: currentDataFetchedAt }); requestHeaders.push({ name: "If-Modified-Since", value: currentDataFetchedAt });
@ -171,7 +171,7 @@ WeatherProvider.register("yr", {
}); });
}, },
getConfigOptions() { getConfigOptions () {
if (!this.config.lat) { if (!this.config.lat) {
Log.error("Latitude not provided."); Log.error("Latitude not provided.");
throw new Error("Latitude not provided."); throw new Error("Latitude not provided.");
@ -187,7 +187,7 @@ WeatherProvider.register("yr", {
return { lat, lon, altitude }; return { lat, lon, altitude };
}, },
getForecastUrl() { getForecastUrl () {
let { lat, lon, altitude } = this.getConfigOptions(); let { lat, lon, altitude } = this.getConfigOptions();
if (lat.includes(".") && lat.split(".")[1].length > 4) { if (lat.includes(".") && lat.split(".")[1].length > 4) {
@ -204,11 +204,11 @@ WeatherProvider.register("yr", {
return `${this.config.apiBase}/locationforecast/${this.config.forecastApiVersion}/complete?&altitude=${altitude}&lat=${lat}&lon=${lon}`; return `${this.config.apiBase}/locationforecast/${this.config.forecastApiVersion}/complete?&altitude=${altitude}&lat=${lat}&lon=${lon}`;
}, },
cacheWeatherData(weatherData) { cacheWeatherData (weatherData) {
localStorage.setItem("weatherData", JSON.stringify(weatherData)); localStorage.setItem("weatherData", JSON.stringify(weatherData));
}, },
getStellarData() { getStellarData () {
// If a user has several Yr-modules, for instance one current and one forecast, the API calls must be synchronized across classes. // If a user has several Yr-modules, for instance one current and one forecast, the API calls must be synchronized across classes.
// This is to avoid multiple similar calls to the API. // This is to avoid multiple similar calls to the API.
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -234,7 +234,7 @@ WeatherProvider.register("yr", {
}); });
}, },
getStellarDataFromYrOrCache(resolve, reject) { getStellarDataFromYrOrCache (resolve, reject) {
localStorage.setItem("yrIsFetchingStellarData", "true"); localStorage.setItem("yrIsFetchingStellarData", "true");
let stellarData = this.getStellarDataFromCache(); let stellarData = this.getStellarDataFromCache();
@ -292,7 +292,7 @@ WeatherProvider.register("yr", {
} }
}, },
getStellarDataFromCache() { getStellarDataFromCache () {
const stellarData = localStorage.getItem("stellarData"); const stellarData = localStorage.getItem("stellarData");
if (stellarData) { if (stellarData) {
return JSON.parse(stellarData); return JSON.parse(stellarData);
@ -301,7 +301,7 @@ WeatherProvider.register("yr", {
} }
}, },
getStellarDataFromYr(date, days = 1) { getStellarDataFromYr (date, days = 1) {
const requestHeaders = [{ name: "Accept", value: "application/json" }]; const requestHeaders = [{ name: "Accept", value: "application/json" }];
return this.fetchData(this.getStellarDataUrl(date, days), "json", requestHeaders) return this.fetchData(this.getStellarDataUrl(date, days), "json", requestHeaders)
.then((data) => { .then((data) => {
@ -314,7 +314,7 @@ WeatherProvider.register("yr", {
}); });
}, },
getStellarDataUrl(date, days) { getStellarDataUrl (date, days) {
let { lat, lon, altitude } = this.getConfigOptions(); let { lat, lon, altitude } = this.getConfigOptions();
if (lat.includes(".") && lat.split(".")[1].length > 4) { if (lat.includes(".") && lat.split(".")[1].length > 4) {
@ -345,11 +345,11 @@ WeatherProvider.register("yr", {
return `${this.config.apiBase}/sunrise/${this.config.sunriseApiVersion}/sun?lat=${lat}&lon=${lon}&date=${date}&offset=${utcOffsetPrefix}${hours}%3A${minutes}`; return `${this.config.apiBase}/sunrise/${this.config.sunriseApiVersion}/sun?lat=${lat}&lon=${lon}&date=${date}&offset=${utcOffsetPrefix}${hours}%3A${minutes}`;
}, },
cacheStellarData(data) { cacheStellarData (data) {
localStorage.setItem("stellarData", JSON.stringify(data)); localStorage.setItem("stellarData", JSON.stringify(data));
}, },
getWeatherDataFrom(forecast, stellarData, units) { getWeatherDataFrom (forecast, stellarData, units) {
const weather = new WeatherObject(); const weather = new WeatherObject();
weather.date = moment(forecast.time); weather.date = moment(forecast.time);
@ -370,7 +370,7 @@ WeatherProvider.register("yr", {
return weather; return weather;
}, },
convertWeatherType(weatherType, weatherTime) { convertWeatherType (weatherType, weatherTime) {
const weatherHour = moment(weatherTime).format("HH"); const weatherHour = moment(weatherTime).format("HH");
const weatherTypes = { const weatherTypes = {
@ -462,7 +462,7 @@ WeatherProvider.register("yr", {
return weatherTypes.hasOwnProperty(weatherType) ? weatherTypes[weatherType] : null; return weatherTypes.hasOwnProperty(weatherType) ? weatherTypes[weatherType] : null;
}, },
getForecastForXHoursFrom(weather) { getForecastForXHoursFrom (weather) {
if (this.config.currentForecastHours === 1) { if (this.config.currentForecastHours === 1) {
if (weather.next_1_hours) { if (weather.next_1_hours) {
return weather.next_1_hours; return weather.next_1_hours;
@ -490,7 +490,7 @@ WeatherProvider.register("yr", {
} }
}, },
fetchWeatherHourly() { fetchWeatherHourly () {
this.getWeatherForecast("hourly") this.getWeatherForecast("hourly")
.then((forecast) => { .then((forecast) => {
this.setWeatherHourly(forecast); this.setWeatherHourly(forecast);
@ -502,7 +502,7 @@ WeatherProvider.register("yr", {
}); });
}, },
async getWeatherForecast(type) { async getWeatherForecast (type) {
const [weatherData, stellarData] = await Promise.all([this.getWeatherData(), this.getStellarData()]); const [weatherData, stellarData] = await Promise.all([this.getWeatherData(), this.getStellarData()]);
if (!weatherData.properties.timeseries || !weatherData.properties.timeseries[0]) { if (!weatherData.properties.timeseries || !weatherData.properties.timeseries[0]) {
Log.error("No weather data available."); Log.error("No weather data available.");
@ -528,7 +528,7 @@ WeatherProvider.register("yr", {
return series; return series;
}, },
getHourlyForecastFrom(weatherData) { getHourlyForecastFrom (weatherData) {
const series = []; const series = [];
for (const forecast of weatherData.properties.timeseries) { for (const forecast of weatherData.properties.timeseries) {
@ -543,7 +543,7 @@ WeatherProvider.register("yr", {
return series; return series;
}, },
getDailyForecastFrom(weatherData) { getDailyForecastFrom (weatherData) {
const series = []; const series = [];
const days = weatherData.properties.timeseries.reduce(function (days, forecast) { const days = weatherData.properties.timeseries.reduce(function (days, forecast) {
@ -593,7 +593,7 @@ WeatherProvider.register("yr", {
return series; return series;
}, },
fetchWeatherForecast() { fetchWeatherForecast () {
this.getWeatherForecast("daily") this.getWeatherForecast("daily")
.then((forecast) => { .then((forecast) => {
this.setWeatherForecast(forecast); this.setWeatherForecast(forecast);

View File

@ -56,17 +56,17 @@ Module.register("weather", {
firstEvent: null, firstEvent: null,
// Define required scripts. // Define required scripts.
getStyles: function () { getStyles () {
return ["font-awesome.css", "weather-icons.css", "weather.css"]; return ["font-awesome.css", "weather-icons.css", "weather.css"];
}, },
// Return the scripts that are necessary for the weather module. // Return the scripts that are necessary for the weather module.
getScripts: function () { getScripts () {
return ["moment.js", "weatherutils.js", "weatherobject.js", this.file("providers/overrideWrapper.js"), "weatherprovider.js", "suncalc.js", this.file(`providers/${this.config.weatherProvider.toLowerCase()}.js`)]; return ["moment.js", "weatherutils.js", "weatherobject.js", this.file("providers/overrideWrapper.js"), "weatherprovider.js", "suncalc.js", this.file(`providers/${this.config.weatherProvider.toLowerCase()}.js`)];
}, },
// Override getHeader method. // Override getHeader method.
getHeader: function () { getHeader () {
if (this.config.appendLocationNameToHeader && this.weatherProvider) { if (this.config.appendLocationNameToHeader && this.weatherProvider) {
if (this.data.header) return `${this.data.header} ${this.weatherProvider.fetchedLocation()}`; if (this.data.header) return `${this.data.header} ${this.weatherProvider.fetchedLocation()}`;
else return this.weatherProvider.fetchedLocation(); else return this.weatherProvider.fetchedLocation();
@ -76,7 +76,7 @@ Module.register("weather", {
}, },
// Start the weather module. // Start the weather module.
start: function () { start () {
moment.locale(this.config.lang); moment.locale(this.config.lang);
if (this.config.useKmh) { if (this.config.useKmh) {
@ -101,7 +101,7 @@ Module.register("weather", {
}, },
// Override notification handler. // Override notification handler.
notificationReceived: function (notification, payload, sender) { notificationReceived (notification, payload, sender) {
if (notification === "CALENDAR_EVENTS") { if (notification === "CALENDAR_EVENTS") {
const senderClasses = sender.data.classes.toLowerCase().split(" "); const senderClasses = sender.data.classes.toLowerCase().split(" ");
if (senderClasses.indexOf(this.config.calendarClass.toLowerCase()) !== -1) { if (senderClasses.indexOf(this.config.calendarClass.toLowerCase()) !== -1) {
@ -126,7 +126,7 @@ Module.register("weather", {
}, },
// Select the template depending on the display type. // Select the template depending on the display type.
getTemplate: function () { getTemplate () {
switch (this.config.type.toLowerCase()) { switch (this.config.type.toLowerCase()) {
case "current": case "current":
return "current.njk"; return "current.njk";
@ -142,7 +142,7 @@ Module.register("weather", {
}, },
// Add all the data to the template. // Add all the data to the template.
getTemplateData: function () { getTemplateData () {
const currentData = this.weatherProvider.currentWeather(); const currentData = this.weatherProvider.currentWeather();
const forecastData = this.weatherProvider.weatherForecast(); const forecastData = this.weatherProvider.weatherForecast();
@ -162,7 +162,7 @@ Module.register("weather", {
}, },
// What to do when the weather provider has new information available? // What to do when the weather provider has new information available?
updateAvailable: function () { updateAvailable () {
Log.log("New weather information available."); Log.log("New weather information available.");
this.updateDom(0); this.updateDom(0);
this.scheduleUpdate(); this.scheduleUpdate();
@ -181,7 +181,7 @@ Module.register("weather", {
this.sendNotification("WEATHER_UPDATED", notificationPayload); this.sendNotification("WEATHER_UPDATED", notificationPayload);
}, },
scheduleUpdate: function (delay = null) { scheduleUpdate (delay = null) {
let nextLoad = this.config.updateInterval; let nextLoad = this.config.updateInterval;
if (delay !== null && delay >= 0) { if (delay !== null && delay >= 0) {
nextLoad = delay; nextLoad = delay;
@ -205,13 +205,13 @@ Module.register("weather", {
}, nextLoad); }, nextLoad);
}, },
roundValue: function (temperature) { roundValue (temperature) {
const decimals = this.config.roundTemp ? 0 : 1; const decimals = this.config.roundTemp ? 0 : 1;
const roundValue = parseFloat(temperature).toFixed(decimals); const roundValue = parseFloat(temperature).toFixed(decimals);
return roundValue === "-0" ? 0 : roundValue; return roundValue === "-0" ? 0 : roundValue;
}, },
addFilters() { addFilters () {
this.nunjucksEnvironment().addFilter( this.nunjucksEnvironment().addFilter(
"formatTime", "formatTime",
function (date) { function (date) {

View File

@ -16,10 +16,11 @@
* @external Moment * @external Moment
*/ */
class WeatherObject { class WeatherObject {
/** /**
* Constructor for a WeatherObject * Constructor for a WeatherObject
*/ */
constructor() { constructor () {
this.date = null; this.date = null;
this.windSpeed = null; this.windSpeed = null;
this.windFromDirection = null; this.windFromDirection = null;
@ -36,7 +37,7 @@ class WeatherObject {
this.feelsLikeTemp = null; this.feelsLikeTemp = null;
} }
cardinalWindDirection() { cardinalWindDirection () {
if (this.windFromDirection > 11.25 && this.windFromDirection <= 33.75) { if (this.windFromDirection > 11.25 && this.windFromDirection <= 33.75) {
return "NNE"; return "NNE";
} else if (this.windFromDirection > 33.75 && this.windFromDirection <= 56.25) { } else if (this.windFromDirection > 33.75 && this.windFromDirection <= 56.25) {
@ -79,11 +80,11 @@ class WeatherObject {
* action for. Useful only in tests, defaults to the current time. * action for. Useful only in tests, defaults to the current time.
* @returns {string} "sunset" or "sunrise" * @returns {string} "sunset" or "sunrise"
*/ */
nextSunAction(date = moment()) { nextSunAction (date = moment()) {
return date.isBetween(this.sunrise, this.sunset) ? "sunset" : "sunrise"; return date.isBetween(this.sunrise, this.sunset) ? "sunset" : "sunrise";
} }
feelsLike() { feelsLike () {
if (this.feelsLikeTemp) { if (this.feelsLikeTemp) {
return this.feelsLikeTemp; return this.feelsLikeTemp;
} }
@ -94,7 +95,7 @@ class WeatherObject {
* Checks if the weatherObject is at dayTime. * Checks if the weatherObject is at dayTime.
* @returns {boolean} true if it is at dayTime * @returns {boolean} true if it is at dayTime
*/ */
isDayTime() { isDayTime () {
const now = !this.date ? moment() : this.date; const now = !this.date ? moment() : this.date;
return now.isBetween(this.sunrise, this.sunset, undefined, "[]"); return now.isBetween(this.sunrise, this.sunset, undefined, "[]");
} }
@ -106,7 +107,7 @@ class WeatherObject {
* @param {number} lat latitude * @param {number} lat latitude
* @param {number} lon longitude * @param {number} lon longitude
*/ */
updateSunTime(lat, lon) { updateSunTime (lat, lon) {
const now = !this.date ? new Date() : this.date.toDate(); const now = !this.date ? new Date() : this.date.toDate();
const times = SunCalc.getTimes(now, lat, lon); const times = SunCalc.getTimes(now, lat, lon);
this.sunrise = moment(times.sunrise); this.sunrise = moment(times.sunrise);
@ -120,7 +121,7 @@ class WeatherObject {
* Especially 'moment' object is not immutable, so original 'date', 'sunrise', 'sunset' could be corrupted or changed by other modules. * Especially 'moment' object is not immutable, so original 'date', 'sunrise', 'sunset' could be corrupted or changed by other modules.
* @returns {object} plained object clone of original weatherObject * @returns {object} plained object clone of original weatherObject
*/ */
simpleClone() { simpleClone () {
const toFlat = ["date", "sunrise", "sunset"]; const toFlat = ["date", "sunrise", "sunset"];
let clone = { ...this }; let clone = { ...this };
for (const prop of toFlat) { for (const prop of toFlat) {

View File

@ -30,84 +30,84 @@ const WeatherProvider = Class.extend({
// All the following methods can be overwritten, although most are good as they are. // All the following methods can be overwritten, although most are good as they are.
// Called when a weather provider is initialized. // Called when a weather provider is initialized.
init: function (config) { init (config) {
this.config = config; this.config = config;
Log.info(`Weather provider: ${this.providerName} initialized.`); Log.info(`Weather provider: ${this.providerName} initialized.`);
}, },
// Called to set the config, this config is the same as the weather module's config. // Called to set the config, this config is the same as the weather module's config.
setConfig: function (config) { setConfig (config) {
this.config = config; this.config = config;
Log.info(`Weather provider: ${this.providerName} config set.`, this.config); Log.info(`Weather provider: ${this.providerName} config set.`, this.config);
}, },
// Called when the weather provider is about to start. // Called when the weather provider is about to start.
start: function () { start () {
Log.info(`Weather provider: ${this.providerName} started.`); Log.info(`Weather provider: ${this.providerName} started.`);
}, },
// This method should start the API request to fetch the current weather. // This method should start the API request to fetch the current weather.
// This method should definitely be overwritten in the provider. // This method should definitely be overwritten in the provider.
fetchCurrentWeather: function () { fetchCurrentWeather () {
Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchCurrentWeather method.`); 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 start the API request to fetch the weather forecast.
// This method should definitely be overwritten in the provider. // This method should definitely be overwritten in the provider.
fetchWeatherForecast: function () { fetchWeatherForecast () {
Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchWeatherForecast method.`); Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchWeatherForecast method.`);
}, },
// This method should start the API request to fetch the weather hourly. // This method should start the API request to fetch the weather hourly.
// This method should definitely be overwritten in the provider. // This method should definitely be overwritten in the provider.
fetchWeatherHourly: function () { fetchWeatherHourly () {
Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchWeatherHourly method.`); Log.warn(`Weather provider: ${this.providerName} does not subclass the fetchWeatherHourly method.`);
}, },
// This returns a WeatherDay object for the current weather. // This returns a WeatherDay object for the current weather.
currentWeather: function () { currentWeather () {
return this.currentWeatherObject; return this.currentWeatherObject;
}, },
// This returns an array of WeatherDay objects for the weather forecast. // This returns an array of WeatherDay objects for the weather forecast.
weatherForecast: function () { weatherForecast () {
return this.weatherForecastArray; return this.weatherForecastArray;
}, },
// This returns an object containing WeatherDay object(s) depending on the type of call. // This returns an object containing WeatherDay object(s) depending on the type of call.
weatherHourly: function () { weatherHourly () {
return this.weatherHourlyArray; return this.weatherHourlyArray;
}, },
// This returns the name of the fetched location or an empty string. // This returns the name of the fetched location or an empty string.
fetchedLocation: function () { fetchedLocation () {
return this.fetchedLocationName || ""; return this.fetchedLocationName || "";
}, },
// Set the currentWeather and notify the delegate that new information is available. // Set the currentWeather and notify the delegate that new information is available.
setCurrentWeather: function (currentWeatherObject) { setCurrentWeather (currentWeatherObject) {
// We should check here if we are passing a WeatherDay // We should check here if we are passing a WeatherDay
this.currentWeatherObject = currentWeatherObject; this.currentWeatherObject = currentWeatherObject;
}, },
// Set the weatherForecastArray and notify the delegate that new information is available. // Set the weatherForecastArray and notify the delegate that new information is available.
setWeatherForecast: function (weatherForecastArray) { setWeatherForecast (weatherForecastArray) {
// We should check here if we are passing a WeatherDay // We should check here if we are passing a WeatherDay
this.weatherForecastArray = weatherForecastArray; this.weatherForecastArray = weatherForecastArray;
}, },
// Set the weatherHourlyArray and notify the delegate that new information is available. // Set the weatherHourlyArray and notify the delegate that new information is available.
setWeatherHourly: function (weatherHourlyArray) { setWeatherHourly (weatherHourlyArray) {
this.weatherHourlyArray = weatherHourlyArray; this.weatherHourlyArray = weatherHourlyArray;
}, },
// Set the fetched location name. // Set the fetched location name.
setFetchedLocation: function (name) { setFetchedLocation (name) {
this.fetchedLocationName = name; this.fetchedLocationName = name;
}, },
// Notify the delegate that new weather is available. // Notify the delegate that new weather is available.
updateAvailable: function () { updateAvailable () {
this.delegate.updateAvailable(this); this.delegate.updateAvailable(this);
}, },
@ -119,7 +119,7 @@ const WeatherProvider = Class.extend({
* @param {Array.<string>} expectedResponseHeaders the expected HTTP headers to recieve * @param {Array.<string>} expectedResponseHeaders the expected HTTP headers to recieve
* @returns {Promise} resolved when the fetch is done * @returns {Promise} resolved when the fetch is done
*/ */
fetchData: async function (url, type = "json", requestHeaders = undefined, expectedResponseHeaders = undefined) { async fetchData (url, type = "json", requestHeaders = undefined, expectedResponseHeaders = undefined) {
const mockData = this.config.mockData; const mockData = this.config.mockData;
if (mockData) { if (mockData) {
const data = mockData.substring(1, mockData.length - 1); const data = mockData.substring(1, mockData.length - 1);

View File

@ -5,12 +5,13 @@
* MIT Licensed. * MIT Licensed.
*/ */
const WeatherUtils = { const WeatherUtils = {
/** /**
* Convert wind (from m/s) to beaufort scale * Convert wind (from m/s) to beaufort scale
* @param {number} speedInMS the windspeed you want to convert * @param {number} speedInMS the windspeed you want to convert
* @returns {number} the speed in beaufort * @returns {number} the speed in beaufort
*/ */
beaufortWindSpeed(speedInMS) { beaufortWindSpeed (speedInMS) {
const windInKmh = this.convertWind(speedInMS, "kmh"); const windInKmh = this.convertWind(speedInMS, "kmh");
const speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000]; const speeds = [1, 5, 11, 19, 28, 38, 49, 61, 74, 88, 102, 117, 1000];
for (const [index, speed] of speeds.entries()) { for (const [index, speed] of speeds.entries()) {
@ -29,7 +30,7 @@ const WeatherUtils = {
* @param {string} outputUnit - The unit system (imperial/metric) the return value should have. * @param {string} outputUnit - The unit system (imperial/metric) the return value should have.
* @returns {string} - A string with tha value and a unit postfix. * @returns {string} - A string with tha value and a unit postfix.
*/ */
convertPrecipitationUnit(value, valueUnit, outputUnit) { convertPrecipitationUnit (value, valueUnit, outputUnit) {
if (valueUnit === "%") return `${value.toFixed(0)} ${valueUnit}`; if (valueUnit === "%") return `${value.toFixed(0)} ${valueUnit}`;
let convertedValue = value; let convertedValue = value;
@ -52,7 +53,7 @@ const WeatherUtils = {
* @param {string} unit can be 'imperial' or 'metric' * @param {string} unit can be 'imperial' or 'metric'
* @returns {number} the converted temperature * @returns {number} the converted temperature
*/ */
convertTemp(tempInC, unit) { convertTemp (tempInC, unit) {
return unit === "imperial" ? tempInC * 1.8 + 32 : tempInC; return unit === "imperial" ? tempInC * 1.8 + 32 : tempInC;
}, },
@ -63,7 +64,7 @@ const WeatherUtils = {
* or 'metric' (mps) * or 'metric' (mps)
* @returns {number} the converted windspeed * @returns {number} the converted windspeed
*/ */
convertWind(windInMS, unit) { convertWind (windInMS, unit) {
switch (unit) { switch (unit) {
case "beaufort": case "beaufort":
return this.beaufortWindSpeed(windInMS); return this.beaufortWindSpeed(windInMS);
@ -82,7 +83,7 @@ const WeatherUtils = {
/* /*
* Convert the wind direction cardinal to value * Convert the wind direction cardinal to value
*/ */
convertWindDirection(windDirection) { convertWindDirection (windDirection) {
const windCardinals = { const windCardinals = {
N: 0, N: 0,
NNE: 22, NNE: 22,
@ -105,15 +106,15 @@ const WeatherUtils = {
return windCardinals.hasOwnProperty(windDirection) ? windCardinals[windDirection] : null; return windCardinals.hasOwnProperty(windDirection) ? windCardinals[windDirection] : null;
}, },
convertWindToMetric(mph) { convertWindToMetric (mph) {
return mph / 2.2369362920544; return mph / 2.2369362920544;
}, },
convertWindToMs(kmh) { convertWindToMs (kmh) {
return kmh * 0.27777777777778; return kmh * 0.27777777777778;
}, },
calculateFeelsLike(temperature, windSpeed, humidity) { calculateFeelsLike (temperature, windSpeed, humidity) {
const windInMph = this.convertWind(windSpeed, "imperial"); const windInMph = this.convertWind(windSpeed, "imperial");
const tempInF = this.convertTemp(temperature, "imperial"); const tempInF = this.convertTemp(temperature, "imperial");
let feelsLike = tempInF; let feelsLike = tempInF;
@ -121,16 +122,16 @@ const WeatherUtils = {
if (windInMph > 3 && tempInF < 50) { 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)); 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 && humidity > 40) { } else if (tempInF > 80 && humidity > 40) {
feelsLike = feelsLike
-42.379 + = -42.379
2.04901523 * tempInF + + 2.04901523 * tempInF
10.14333127 * humidity - + 10.14333127 * humidity
0.22475541 * tempInF * humidity - - 0.22475541 * tempInF * humidity
6.83783 * Math.pow(10, -3) * tempInF * tempInF - - 6.83783 * Math.pow(10, -3) * tempInF * tempInF
5.481717 * Math.pow(10, -2) * humidity * humidity + - 5.481717 * Math.pow(10, -2) * humidity * humidity
1.22874 * Math.pow(10, -3) * tempInF * tempInF * humidity + + 1.22874 * Math.pow(10, -3) * tempInF * tempInF * humidity
8.5282 * Math.pow(10, -4) * tempInF * humidity * humidity - + 8.5282 * Math.pow(10, -4) * tempInF * humidity * humidity
1.99 * Math.pow(10, -6) * tempInF * tempInF * humidity * humidity; - 1.99 * Math.pow(10, -6) * tempInF * tempInF * humidity * humidity;
} }
return ((feelsLike - 32) * 5) / 9; return ((feelsLike - 32) * 5) / 9;

814
package-lock.json generated
View File

@ -14,7 +14,7 @@
"command-exists": "^1.2.9", "command-exists": "^1.2.9",
"console-stamp": "^3.1.2", "console-stamp": "^3.1.2",
"envsub": "^4.1.0", "envsub": "^4.1.0",
"eslint": "^8.55.0", "eslint": "^8.56.0",
"express": "^4.18.2", "express": "^4.18.2",
"express-ipfilter": "^1.3.1", "express-ipfilter": "^1.3.1",
"feedme": "^2.0.2", "feedme": "^2.0.2",
@ -28,11 +28,10 @@
"socket.io": "^4.7.2" "socket.io": "^4.7.2"
}, },
"devDependencies": { "devDependencies": {
"eslint-config-prettier": "^9.1.0", "@stylistic/eslint-plugin": "^1.5.1",
"eslint-plugin-import": "^2.29.0", "eslint-plugin-import": "^2.29.1",
"eslint-plugin-jest": "^27.6.0", "eslint-plugin-jest": "^27.6.0",
"eslint-plugin-jsdoc": "^46.9.0", "eslint-plugin-jsdoc": "^46.9.1",
"eslint-plugin-prettier": "^5.0.1",
"express-basic-auth": "^1.2.1", "express-basic-auth": "^1.2.1",
"husky": "^8.0.3", "husky": "^8.0.3",
"jest": "^29.7.0", "jest": "^29.7.0",
@ -872,9 +871,9 @@
} }
}, },
"node_modules/@eslint/js": { "node_modules/@eslint/js": {
"version": "8.55.0", "version": "8.56.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
"integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
"engines": { "engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
} }
@ -1381,32 +1380,6 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/@pkgr/utils": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz",
"integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==",
"dev": true,
"dependencies": {
"cross-spawn": "^7.0.3",
"fast-glob": "^3.3.0",
"is-glob": "^4.0.3",
"open": "^9.1.0",
"picocolors": "^1.0.0",
"tslib": "^2.6.0"
},
"engines": {
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/unts"
}
},
"node_modules/@pkgr/utils/node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"dev": true
},
"node_modules/@selderee/plugin-htmlparser2": { "node_modules/@selderee/plugin-htmlparser2": {
"version": "0.11.0", "version": "0.11.0",
"resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz",
@ -1486,6 +1459,350 @@
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
}, },
"node_modules/@stylistic/eslint-plugin": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-1.5.1.tgz",
"integrity": "sha512-y7ynUMh5Hq1MhYApAccl1iuQem5Sf2JSEIjV/qsBfmW1WfRDs74V+0kLkcOn1Y600W3t8orIFrrEuWmJSetAgw==",
"dev": true,
"dependencies": {
"@stylistic/eslint-plugin-js": "1.5.1",
"@stylistic/eslint-plugin-jsx": "1.5.1",
"@stylistic/eslint-plugin-plus": "1.5.1",
"@stylistic/eslint-plugin-ts": "1.5.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"peerDependencies": {
"eslint": ">=8.40.0"
}
},
"node_modules/@stylistic/eslint-plugin-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.5.1.tgz",
"integrity": "sha512-iZF0rF+uOhAmOJYOJx1Yvmm3CZ1uz9n0SRd9dpBYHA3QAvfABUORh9LADWwZCigjHJkp2QbCZelGFJGwGz7Siw==",
"dev": true,
"dependencies": {
"acorn": "^8.11.2",
"escape-string-regexp": "^4.0.0",
"eslint-visitor-keys": "^3.4.3",
"espree": "^9.6.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"peerDependencies": {
"eslint": ">=8.40.0"
}
},
"node_modules/@stylistic/eslint-plugin-jsx": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-jsx/-/eslint-plugin-jsx-1.5.1.tgz",
"integrity": "sha512-JuX+jsbVdpZ6EZXkbxYr9ERcGc0ndSMFgOuwEPHhOWPZ+7F8JP/nzpBjrRf7dUPMX7ezTYLZ2a3KRGRNme6rWQ==",
"dev": true,
"dependencies": {
"@stylistic/eslint-plugin-js": "^1.5.1",
"estraverse": "^5.3.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"peerDependencies": {
"eslint": ">=8.40.0"
}
},
"node_modules/@stylistic/eslint-plugin-plus": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-plus/-/eslint-plugin-plus-1.5.1.tgz",
"integrity": "sha512-yxkFHsUgoqEf/j1Og0FGkpEmeQoqx0CMmtgoyZGr34hka0ElCy9fRpsFkLcwx60SfiHXspbvs2YUMXiWIffnjg==",
"dev": true,
"dependencies": {
"@typescript-eslint/utils": "^6.13.2"
},
"peerDependencies": {
"eslint": "*"
}
},
"node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/scope-manager": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.15.0.tgz",
"integrity": "sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.15.0",
"@typescript-eslint/visitor-keys": "6.15.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/types": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.15.0.tgz",
"integrity": "sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/typescript-estree": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.15.0.tgz",
"integrity": "sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.15.0",
"@typescript-eslint/visitor-keys": "6.15.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/utils": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.15.0.tgz",
"integrity": "sha512-eF82p0Wrrlt8fQSRL0bGXzK5nWPRV2dYQZdajcfzOD9+cQz9O7ugifrJxclB+xVOvWvagXfqS4Es7vpLP4augw==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "6.15.0",
"@typescript-eslint/types": "6.15.0",
"@typescript-eslint/typescript-estree": "6.15.0",
"semver": "^7.5.4"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0"
}
},
"node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/visitor-keys": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.15.0.tgz",
"integrity": "sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.15.0",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@stylistic/eslint-plugin-plus/node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@stylistic/eslint-plugin-plus/node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@stylistic/eslint-plugin-plus/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"node_modules/@stylistic/eslint-plugin-ts": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-1.5.1.tgz",
"integrity": "sha512-oXM1V7Jp8G9+udxQTy+Igo79LR2e5HXiWqlA/3v+/PAqWxniR9nJqJSBjtQKJTPsGplDqn/ASpHUOETP4EI/4A==",
"dev": true,
"dependencies": {
"@stylistic/eslint-plugin-js": "1.5.1",
"@typescript-eslint/utils": "^6.13.2"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"peerDependencies": {
"eslint": ">=8.40.0"
}
},
"node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/scope-manager": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.15.0.tgz",
"integrity": "sha512-+BdvxYBltqrmgCNu4Li+fGDIkW9n//NrruzG9X1vBzaNK+ExVXPoGB71kneaVw/Jp+4rH/vaMAGC6JfMbHstVg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.15.0",
"@typescript-eslint/visitor-keys": "6.15.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/types": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.15.0.tgz",
"integrity": "sha512-yXjbt//E4T/ee8Ia1b5mGlbNj9fB9lJP4jqLbZualwpP2BCQ5is6BcWwxpIsY4XKAhmdv3hrW92GdtJbatC6dQ==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/typescript-estree": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.15.0.tgz",
"integrity": "sha512-7mVZJN7Hd15OmGuWrp2T9UvqR2Ecg+1j/Bp1jXUEY2GZKV6FXlOIoqVDmLpBiEiq3katvj/2n2mR0SDwtloCew==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.15.0",
"@typescript-eslint/visitor-keys": "6.15.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/utils": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.15.0.tgz",
"integrity": "sha512-eF82p0Wrrlt8fQSRL0bGXzK5nWPRV2dYQZdajcfzOD9+cQz9O7ugifrJxclB+xVOvWvagXfqS4Es7vpLP4augw==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "6.15.0",
"@typescript-eslint/types": "6.15.0",
"@typescript-eslint/typescript-estree": "6.15.0",
"semver": "^7.5.4"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0"
}
},
"node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/visitor-keys": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.15.0.tgz",
"integrity": "sha512-1zvtdC1a9h5Tb5jU9x3ADNXO9yjP8rXlaoChu0DQX40vf5ACVpYIVIZhIMZ6d5sDXH7vq4dsZBT1fEGj8D2n2w==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.15.0",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@stylistic/eslint-plugin-ts/node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@stylistic/eslint-plugin-ts/node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@stylistic/eslint-plugin-ts/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"node_modules/@szmarczak/http-timer": { "node_modules/@szmarczak/http-timer": {
"version": "4.0.6", "version": "4.0.6",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
@ -2297,15 +2614,6 @@
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true "dev": true
}, },
"node_modules/big-integer": {
"version": "1.6.52",
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz",
"integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==",
"dev": true,
"engines": {
"node": ">=0.6"
}
},
"node_modules/bluebird": { "node_modules/bluebird": {
"version": "3.7.2", "version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
@ -2364,18 +2672,6 @@
"integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
"optional": true "optional": true
}, },
"node_modules/bplist-parser": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz",
"integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==",
"dev": true,
"dependencies": {
"big-integer": "^1.6.44"
},
"engines": {
"node": ">= 5.10.0"
}
},
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@ -2465,21 +2761,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/bundle-name": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz",
"integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==",
"dev": true,
"dependencies": {
"run-applescript": "^5.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/bytes": { "node_modules/bytes": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@ -3078,162 +3359,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/default-browser": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz",
"integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==",
"dev": true,
"dependencies": {
"bundle-name": "^3.0.0",
"default-browser-id": "^3.0.0",
"execa": "^7.1.1",
"titleize": "^3.0.0"
},
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/default-browser-id": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz",
"integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==",
"dev": true,
"dependencies": {
"bplist-parser": "^0.2.0",
"untildify": "^4.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/default-browser/node_modules/execa": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz",
"integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==",
"dev": true,
"dependencies": {
"cross-spawn": "^7.0.3",
"get-stream": "^6.0.1",
"human-signals": "^4.3.0",
"is-stream": "^3.0.0",
"merge-stream": "^2.0.0",
"npm-run-path": "^5.1.0",
"onetime": "^6.0.0",
"signal-exit": "^3.0.7",
"strip-final-newline": "^3.0.0"
},
"engines": {
"node": "^14.18.0 || ^16.14.0 || >=18.0.0"
},
"funding": {
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
"node_modules/default-browser/node_modules/get-stream": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
"dev": true,
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/default-browser/node_modules/human-signals": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
"integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==",
"dev": true,
"engines": {
"node": ">=14.18.0"
}
},
"node_modules/default-browser/node_modules/is-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
"integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
"dev": true,
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/default-browser/node_modules/mimic-fn": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
"integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/default-browser/node_modules/npm-run-path": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz",
"integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==",
"dev": true,
"dependencies": {
"path-key": "^4.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/default-browser/node_modules/onetime": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
"integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
"dev": true,
"dependencies": {
"mimic-fn": "^4.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/default-browser/node_modules/path-key": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/default-browser/node_modules/strip-final-newline": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
"integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/defer-to-connect": { "node_modules/defer-to-connect": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
@ -3256,18 +3381,6 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/define-lazy-prop": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
"integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/define-properties": { "node_modules/define-properties": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
@ -3717,14 +3830,14 @@
} }
}, },
"node_modules/eslint": { "node_modules/eslint": {
"version": "8.55.0", "version": "8.56.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
"integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1", "@eslint-community/regexpp": "^4.6.1",
"@eslint/eslintrc": "^2.1.4", "@eslint/eslintrc": "^2.1.4",
"@eslint/js": "8.55.0", "@eslint/js": "8.56.0",
"@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/config-array": "^0.11.13",
"@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8", "@nodelib/fs.walk": "^1.2.8",
@ -3770,18 +3883,6 @@
"url": "https://opencollective.com/eslint" "url": "https://opencollective.com/eslint"
} }
}, },
"node_modules/eslint-config-prettier": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
"integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
"dev": true,
"bin": {
"eslint-config-prettier": "bin/cli.js"
},
"peerDependencies": {
"eslint": ">=7.0.0"
}
},
"node_modules/eslint-import-resolver-node": { "node_modules/eslint-import-resolver-node": {
"version": "0.3.9", "version": "0.3.9",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
@ -3829,9 +3930,9 @@
} }
}, },
"node_modules/eslint-plugin-import": { "node_modules/eslint-plugin-import": {
"version": "2.29.0", "version": "2.29.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz",
"integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"array-includes": "^3.1.7", "array-includes": "^3.1.7",
@ -3850,7 +3951,7 @@
"object.groupby": "^1.0.1", "object.groupby": "^1.0.1",
"object.values": "^1.1.7", "object.values": "^1.1.7",
"semver": "^6.3.1", "semver": "^6.3.1",
"tsconfig-paths": "^3.14.2" "tsconfig-paths": "^3.15.0"
}, },
"engines": { "engines": {
"node": ">=4" "node": ">=4"
@ -3906,9 +4007,9 @@
} }
}, },
"node_modules/eslint-plugin-jsdoc": { "node_modules/eslint-plugin-jsdoc": {
"version": "46.9.0", "version": "46.9.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.9.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.9.1.tgz",
"integrity": "sha512-UQuEtbqLNkPf5Nr/6PPRCtr9xypXY+g8y/Q7gPa0YK7eDhh0y2lWprXRnaYbW7ACgIUvpDKy9X2bZqxtGzBG9Q==", "integrity": "sha512-11Ox5LCl2wY7gGkp9UOyew70o9qvii1daAH+h/MFobRVRNcy7sVlH+jm0HQdgcvcru6285GvpjpUyoa051j03Q==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@es-joy/jsdoccomment": "~0.41.0", "@es-joy/jsdoccomment": "~0.41.0",
@ -3919,7 +4020,7 @@
"esquery": "^1.5.0", "esquery": "^1.5.0",
"is-builtin-module": "^3.2.1", "is-builtin-module": "^3.2.1",
"semver": "^7.5.4", "semver": "^7.5.4",
"spdx-expression-parse": "^3.0.1" "spdx-expression-parse": "^4.0.0"
}, },
"engines": { "engines": {
"node": ">=16" "node": ">=16"
@ -3961,35 +4062,6 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true "dev": true
}, },
"node_modules/eslint-plugin-prettier": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz",
"integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==",
"dev": true,
"dependencies": {
"prettier-linter-helpers": "^1.0.0",
"synckit": "^0.8.5"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/prettier"
},
"peerDependencies": {
"@types/eslint": ">=8.0.0",
"eslint": ">=8.0.0",
"prettier": ">=3.0.0"
},
"peerDependenciesMeta": {
"@types/eslint": {
"optional": true
},
"eslint-config-prettier": {
"optional": true
}
}
},
"node_modules/eslint-scope": { "node_modules/eslint-scope": {
"version": "7.2.2", "version": "7.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
@ -5321,21 +5393,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-docker": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
"integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
"dev": true,
"bin": {
"is-docker": "cli.js"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-extglob": { "node_modules/is-extglob": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@ -5376,24 +5433,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/is-inside-container": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
"integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
"dev": true,
"dependencies": {
"is-docker": "^3.0.0"
},
"bin": {
"is-inside-container": "cli.js"
},
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-negative-zero": { "node_modules/is-negative-zero": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
@ -5542,33 +5581,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"dev": true,
"dependencies": {
"is-docker": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-wsl/node_modules/is-docker": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
"dev": true,
"bin": {
"is-docker": "cli.js"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/isarray": { "node_modules/isarray": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
@ -7349,24 +7361,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/open": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz",
"integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==",
"dev": true,
"dependencies": {
"default-browser": "^4.0.0",
"define-lazy-prop": "^3.0.0",
"is-inside-container": "^1.0.0",
"is-wsl": "^2.2.0"
},
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/optionator": { "node_modules/optionator": {
"version": "0.9.3", "version": "0.9.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
@ -8216,21 +8210,6 @@
"integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==",
"dev": true "dev": true
}, },
"node_modules/run-applescript": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz",
"integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==",
"dev": true,
"dependencies": {
"execa": "^5.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/run-parallel": { "node_modules/run-parallel": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@ -8672,9 +8651,9 @@
"dev": true "dev": true
}, },
"node_modules/spdx-expression-parse": { "node_modules/spdx-expression-parse": {
"version": "3.0.1", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz",
"integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"spdx-exceptions": "^2.1.0", "spdx-exceptions": "^2.1.0",
@ -9174,28 +9153,6 @@
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
"dev": true "dev": true
}, },
"node_modules/synckit": {
"version": "0.8.6",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.6.tgz",
"integrity": "sha512-laHF2savN6sMeHCjLRkheIU4wo3Zg9Ln5YOjOo7sZ5dVQW8yF5pPE5SIw1dsPhq3TRp1jisKRCdPhfs/1WMqDA==",
"dev": true,
"dependencies": {
"@pkgr/utils": "^2.4.2",
"tslib": "^2.6.2"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/unts"
}
},
"node_modules/synckit/node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
"dev": true
},
"node_modules/table": { "node_modules/table": {
"version": "6.8.1", "version": "6.8.1",
"resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz",
@ -9299,18 +9256,6 @@
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
}, },
"node_modules/titleize": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz",
"integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/tmpl": { "node_modules/tmpl": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@ -9382,10 +9327,22 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/ts-api-utils": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
"integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==",
"dev": true,
"engines": {
"node": ">=16.13.0"
},
"peerDependencies": {
"typescript": ">=4.2.0"
}
},
"node_modules/tsconfig-paths": { "node_modules/tsconfig-paths": {
"version": "3.14.2", "version": "3.15.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
"integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/json5": "^0.0.29", "@types/json5": "^0.0.29",
@ -9601,15 +9558,6 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/untildify": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
"integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/update-browserslist-db": { "node_modules/update-browserslist-db": {
"version": "1.0.13", "version": "1.0.13",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",

View File

@ -18,12 +18,12 @@
"test:e2e": "NODE_ENV=test jest --selectProjects e2e -i --forceExit", "test:e2e": "NODE_ENV=test jest --selectProjects e2e -i --forceExit",
"test:unit": "NODE_ENV=test jest --selectProjects unit", "test:unit": "NODE_ENV=test jest --selectProjects unit",
"test:prettier": "prettier . --check", "test:prettier": "prettier . --check",
"test:js": "eslint 'js/**/*.js' 'modules/default/**/*.js' 'clientonly/*.js' 'serveronly/*.js' 'translations/*.js' 'vendor/*.js' 'tests/**/*.js' 'config/*' --config .eslintrc.json", "test:js": "eslint 'js/**/*.js' 'modules/default/**/*.js' 'clientonly/*.js' 'serveronly/*.js' 'translations/*.js' 'vendor/*.js' 'tests/**/*.js' 'config/*'",
"test:css": "stylelint 'css/main.css' 'fonts/*.css' 'modules/default/**/*.css' 'vendor/*.css' --config .stylelintrc.json", "test:css": "stylelint 'css/main.css' 'fonts/*.css' 'modules/default/**/*.css' 'vendor/*.css' --config .stylelintrc.json",
"test:calendar": "node ./modules/default/calendar/debug.js", "test:calendar": "node ./modules/default/calendar/debug.js",
"config:check": "node js/check_config.js", "config:check": "node js/check_config.js",
"lint:prettier": "prettier . --write", "lint:prettier": "prettier . --write",
"lint:js": "eslint 'js/**/*.js' 'modules/default/**/*.js' 'clientonly/*.js' 'serveronly/*.js' 'translations/*.js' 'vendor/*.js' 'tests/**/*.js' 'config/*' --config .eslintrc.json --fix", "lint:js": "eslint 'js/**/*.js' 'modules/default/**/*.js' 'clientonly/*.js' 'serveronly/*.js' 'translations/*.js' 'vendor/*.js' 'tests/**/*.js' 'config/*' --fix",
"lint:css": "stylelint 'css/main.css' 'fonts/*.css' 'modules/default/**/*.css' 'vendor/*.css' --config .stylelintrc.json --fix", "lint:css": "stylelint 'css/main.css' 'fonts/*.css' 'modules/default/**/*.css' 'vendor/*.css' --config .stylelintrc.json --fix",
"lint:staged": "lint-staged", "lint:staged": "lint-staged",
"prepare": "[ -f node_modules/.bin/husky ] && husky install || echo no husky installed." "prepare": "[ -f node_modules/.bin/husky ] && husky install || echo no husky installed."
@ -49,11 +49,10 @@
}, },
"homepage": "https://magicmirror.builders", "homepage": "https://magicmirror.builders",
"devDependencies": { "devDependencies": {
"eslint-config-prettier": "^9.1.0", "@stylistic/eslint-plugin": "^1.5.1",
"eslint-plugin-import": "^2.29.0", "eslint-plugin-import": "^2.29.1",
"eslint-plugin-jest": "^27.6.0", "eslint-plugin-jest": "^27.6.0",
"eslint-plugin-jsdoc": "^46.9.0", "eslint-plugin-jsdoc": "^46.9.1",
"eslint-plugin-prettier": "^5.0.1",
"express-basic-auth": "^1.2.1", "express-basic-auth": "^1.2.1",
"husky": "^8.0.3", "husky": "^8.0.3",
"jest": "^29.7.0", "jest": "^29.7.0",
@ -76,7 +75,7 @@
"command-exists": "^1.2.9", "command-exists": "^1.2.9",
"console-stamp": "^3.1.2", "console-stamp": "^3.1.2",
"envsub": "^4.1.0", "envsub": "^4.1.0",
"eslint": "^8.55.0", "eslint": "^8.56.0",
"express": "^4.18.2", "express": "^4.18.2",
"express-ipfilter": "^1.3.1", "express-ipfilter": "^1.3.1",
"feedme": "^2.0.2", "feedme": "^2.0.2",

View File

@ -25,7 +25,7 @@ describe("AnimateCSS integration Test", () => {
*/ */
const doTest = async (animationIn, animationOut) => { const doTest = async (animationIn, animationOut) => {
await helpers.getDocument(); await helpers.getDocument();
let elem = await helpers.waitForElement(`.compliments`); let elem = await helpers.waitForElement(".compliments");
expect(elem).not.toBeNull(); expect(elem).not.toBeNull();
let styles = window.getComputedStyle(elem); let styles = window.getComputedStyle(elem);

View File

@ -4,13 +4,13 @@
*/ */
const mockError = (err) => { const mockError = (err) => {
if ( if (
err.includes("ECONNREFUSED") || err.includes("ECONNREFUSED")
err.includes("ECONNRESET") || || err.includes("ECONNRESET")
err.includes("socket hang up") || || err.includes("socket hang up")
err.includes("exports is not defined") || || err.includes("exports is not defined")
err.includes("write EPIPE") || || err.includes("write EPIPE")
err.includes("AggregateError") || || err.includes("AggregateError")
err.includes("ERR_SOCKET_CONNECTION_TIMEOUT") || err.includes("ERR_SOCKET_CONNECTION_TIMEOUT")
) { ) {
jest.fn(); jest.fn();
} else { } else {

View File

@ -2,6 +2,7 @@ const helpers = require("../helpers/global-setup");
const serverBasicAuth = require("../helpers/basic-auth"); const serverBasicAuth = require("../helpers/basic-auth");
describe("Calendar module", () => { describe("Calendar module", () => {
/** /**
* @param {string} element css selector * @param {string} element css selector
* @param {string} result expected number * @param {string} result expected number

View File

@ -1,6 +1,7 @@
const helpers = require("../helpers/global-setup"); const helpers = require("../helpers/global-setup");
describe("Compliments module", () => { describe("Compliments module", () => {
/** /**
* move similar tests in function doTest * move similar tests in function doTest
* @param {Array} complimentsArray The array of compliments. * @param {Array} complimentsArray The array of compliments.

View File

@ -125,7 +125,7 @@ describe("Translations", () => {
const mmm = { const mmm = {
name: "TranslationTest", name: "TranslationTest",
file(file) { file (file) {
return `http://localhost:3000/${file}`; return `http://localhost:3000/${file}`;
} }
}; };

View File

@ -1,6 +1,7 @@
const helpers = require("../helpers/global-setup"); const helpers = require("../helpers/global-setup");
describe("Calendar module", () => { describe("Calendar module", () => {
/** /**
* move similar tests in function doTest * move similar tests in function doTest
* @param {string} cssClass css selector * @param {string} cssClass css selector

View File

@ -1,6 +1,7 @@
const helpers = require("../helpers/global-setup"); const helpers = require("../helpers/global-setup");
describe("Compliments module", () => { describe("Compliments module", () => {
/** /**
* move similar tests in function doTest * move similar tests in function doTest
* @param {Array} complimentsArray The array of compliments. * @param {Array} complimentsArray The array of compliments.

View File

@ -163,7 +163,7 @@ describe("Translator", () => {
describe("load", () => { describe("load", () => {
const mmm = { const mmm = {
name: "TranslationTest", name: "TranslationTest",
file(file) { file (file) {
return `http://localhost:3000/translations/${file}`; return `http://localhost:3000/translations/${file}`;
} }
}; };

View File

@ -31,7 +31,7 @@ describe("server_functions tests", () => {
}; };
request = { request = {
url: `/cors?url=www.test.com` url: "/cors?url=www.test.com"
}; };
}); });

View File

@ -32,7 +32,7 @@ describe("Updatenotification", () => {
const { promisify } = require("util"); const { promisify } = require("util");
promisify.mockReturnValue(execMock); promisify.mockReturnValue(execMock);
const GitHelper = require(`../../../modules/default/updatenotification/git_helper`); const GitHelper = require("../../../modules/default/updatenotification/git_helper");
gitHelper = new GitHelper(); gitHelper = new GitHelper();
}); });

View File

@ -84,7 +84,7 @@ describe("Default modules utils tests", () => {
it("Returns object when data is received", async () => { it("Returns object when data is received", async () => {
urlToCall = "www.test.com"; urlToCall = "www.test.com";
fetchResponse = new Response('{"body": "some content"}'); fetchResponse = new Response("{\"body\": \"some content\"}");
const response = await performWebRequest(urlToCall); const response = await performWebRequest(urlToCall);
@ -93,7 +93,7 @@ describe("Default modules utils tests", () => {
it("Returns expected headers when data is received", async () => { it("Returns expected headers when data is received", async () => {
urlToCall = "www.test.com"; urlToCall = "www.test.com";
fetchResponse = new Response('{"body": "some content"}', { headers: { header1: "value1", header2: "value2" } }); fetchResponse = new Response("{\"body\": \"some content\"}", { headers: { header1: "value1", header2: "value2" } });
const response = await performWebRequest(urlToCall, "json", false, undefined, ["header1"]); const response = await performWebRequest(urlToCall, "json", false, undefined, ["header1"]);

View File

@ -1,7 +1,7 @@
const TestSequencer = require("@jest/test-sequencer").default; const TestSequencer = require("@jest/test-sequencer").default;
class CustomSequencer extends TestSequencer { class CustomSequencer extends TestSequencer {
sort(tests) { sort (tests) {
const orderPath = ["unit", "electron", "e2e"]; const orderPath = ["unit", "electron", "e2e"];
return tests.sort((testA, testB) => { return tests.sort((testA, testB) => {
let indexA = -1; let indexA = -1;