2020-05-03 18:59:26 +02:00
/* global Class, cloneObject, Loader, MMSocket, nunjucks, Translator */
2016-03-24 17:19:32 +01:00
2024-08-12 22:52:43 +02:00
/*
* Module Blueprint.
2020-08-01 16:31:42 +02:00
* @typedef {Object} Module
2016-03-24 17:19:32 +01:00
*/
2021-07-14 15:06:23 +02:00
const Module = Class . extend ( {
2024-01-01 15:38:08 +01:00
2024-08-12 22:52:43 +02:00
/**
2025-11-13 15:08:47 -06:00
*********************************************************
* All methods (and properties) below can be overridden. *
*********************************************************
2024-08-12 22:52:43 +02:00
*/
2016-03-24 17:19:32 +01:00
2022-01-26 23:47:51 +01:00
// Set the minimum MagicMirror² module version for this module.
2016-10-13 16:42:15 +02:00
requiresVersion : "2.0.0" ,
2016-03-24 17:19:32 +01:00
// Module config defaults.
defaults : { } ,
2016-04-08 17:27:02 +02:00
// Timer reference used for showHide animation callbacks.
showHideTimer : null ,
2024-08-12 22:52:43 +02:00
/*
* Array to store lockStrings. These strings are used to lock
* visibility when hiding and showing module.
*/
2016-10-13 15:00:59 +02:00
lockStrings : [ ] ,
2024-08-12 22:52:43 +02:00
/*
* Storage of the nunjucks Environment,
* This should not be referenced directly.
* Use the nunjucksEnvironment() to get it.
*/
2017-09-28 16:11:25 +02:00
_nunjucksEnvironment : null ,
2020-07-28 16:48:03 +02:00
/**
* Called when the module is instantiated.
2016-03-24 17:19:32 +01:00
*/
2024-01-01 15:38:08 +01:00
init ( ) {
2016-03-24 17:19:32 +01:00
} ,
2020-07-28 16:48:03 +02:00
/**
* Called when the module is started.
2016-03-24 17:19:32 +01:00
*/
2024-01-01 15:38:08 +01:00
async start ( ) {
2023-04-04 20:44:32 +02:00
Log . info ( ` Starting module: ${ this . name } ` ) ;
2016-03-24 17:19:32 +01:00
} ,
2020-07-28 16:48:03 +02:00
/**
2016-03-24 17:19:32 +01:00
* Returns a list of scripts the module requires to be loaded.
2020-08-01 16:31:42 +02:00
* @returns {string[]} An array with filenames.
2016-03-24 17:19:32 +01:00
*/
2024-01-01 15:38:08 +01:00
getScripts ( ) {
2016-03-24 17:19:32 +01:00
return [ ] ;
} ,
2020-07-28 16:48:03 +02:00
/**
2016-03-24 17:19:32 +01:00
* Returns a list of stylesheets the module requires to be loaded.
2020-08-01 16:31:42 +02:00
* @returns {string[]} An array with filenames.
2016-03-24 17:19:32 +01:00
*/
2024-01-01 15:38:08 +01:00
getStyles ( ) {
2016-03-24 17:19:32 +01:00
return [ ] ;
} ,
2020-07-28 16:48:03 +02:00
/**
2016-04-21 01:03:26 +02:00
* Returns a map of translation files the module requires to be loaded.
*
2020-07-28 16:48:03 +02:00
* return Map<String, String> -
2025-09-23 06:27:29 +02:00
* @returns {Map} A map with langKeys and filenames.
2016-04-21 01:03:26 +02:00
*/
2024-01-01 15:38:08 +01:00
getTranslations ( ) {
2016-05-11 12:38:41 +02:00
return false ;
2016-04-21 01:03:26 +02:00
} ,
2020-07-28 16:48:03 +02:00
/**
2022-01-26 23:09:26 +01:00
* Generates the dom which needs to be displayed. This method is called by the MagicMirror² core.
2025-11-13 15:08:47 -06:00
* This method can to be overridden if the module wants to display info on the mirror.
* Alternatively, the getTemplate method could be overridden.
2020-07-28 16:48:03 +02:00
* @returns {HTMLElement|Promise} The dom or a promise with the dom to display.
2016-03-24 17:19:32 +01:00
*/
2024-01-01 15:38:08 +01:00
getDom ( ) {
2021-03-23 22:40:24 +01:00
return new Promise ( ( resolve ) => {
const div = document . createElement ( "div" ) ;
const template = this . getTemplate ( ) ;
const templateData = this . getTemplateData ( ) ;
2018-01-01 10:55:39 -06:00
2018-01-01 09:42:34 -06:00
// Check to see if we need to render a template string or a file.
2024-01-01 15:38:08 +01:00
if ( ( /^.*((\.html)|(\.njk))$/ ) . test ( template ) ) {
2018-01-01 09:42:34 -06:00
// the template is a filename
2021-03-23 22:40:24 +01:00
this . nunjucksEnvironment ( ) . render ( template , templateData , function ( err , res ) {
2018-01-01 09:42:34 -06:00
if ( err ) {
2019-06-05 10:23:58 +02:00
Log . error ( err ) ;
2018-01-01 09:42:34 -06:00
}
div . innerHTML = res ;
resolve ( div ) ;
} ) ;
} else {
// the template is a template string.
2021-03-23 22:40:24 +01:00
div . innerHTML = this . nunjucksEnvironment ( ) . renderString ( template , templateData ) ;
2018-01-01 09:42:34 -06:00
resolve ( div ) ;
}
} ) ;
2016-03-24 17:19:32 +01:00
} ,
2020-07-28 16:48:03 +02:00
/**
* Generates the header string which needs to be displayed if a user has a header configured for this module.
2022-01-26 23:09:26 +01:00
* This method is called by the MagicMirror² core, but only if the user has configured a default header for the module.
2025-11-13 15:08:47 -06:00
* This method needs to be overridden if the module wants to display modified headers on the mirror.
2020-07-28 16:48:03 +02:00
* @returns {string} The header to display above the header.
2016-09-20 17:22:24 +02:00
*/
2024-01-01 15:38:08 +01:00
getHeader ( ) {
2016-09-20 17:22:24 +02:00
return this . data . header ;
} ,
2020-07-28 16:48:03 +02:00
/**
* Returns the template for the module which is used by the default getDom implementation.
2025-11-13 15:08:47 -06:00
* This method needs to be overridden if the module wants to use a template.
* It can either return a template string, or a template filename.
2017-09-28 16:11:25 +02:00
* If the string ends with '.html' it's considered a file from within the module's folder.
2020-07-28 16:48:03 +02:00
* @returns {string} The template string of filename.
2017-09-28 16:11:25 +02:00
*/
2024-01-01 15:38:08 +01:00
getTemplate ( ) {
2023-04-04 20:44:32 +02:00
return ` <div class="normal"> ${ this . name } </div><div class="small dimmed"> ${ this . identifier } </div> ` ;
2017-09-28 16:11:25 +02:00
} ,
2020-07-28 16:48:03 +02:00
/**
* Returns the data to be used in the template.
2025-11-13 15:08:47 -06:00
* This method needs to be overridden if the module wants to use a custom data.
2020-07-28 16:48:03 +02:00
* @returns {object} The data for the template
2017-09-28 16:11:25 +02:00
*/
2024-01-01 15:38:08 +01:00
getTemplateData ( ) {
2019-06-05 10:23:58 +02:00
return { } ;
2017-09-28 16:11:25 +02:00
} ,
2020-07-28 16:48:03 +02:00
/**
2022-01-26 23:09:26 +01:00
* Called by the MagicMirror² core when a notification arrives.
2020-07-28 16:48:03 +02:00
* @param {string} notification The identifier of the notification.
2025-09-23 06:27:29 +02:00
* @param {object} payload The payload of the notification.
2020-08-01 16:31:42 +02:00
* @param {Module} sender The module that sent the notification.
2016-03-24 17:19:32 +01:00
*/
2024-01-01 15:38:08 +01:00
notificationReceived ( notification , payload , sender ) {
2016-03-24 17:19:32 +01:00
if ( sender ) {
2025-10-21 20:32:48 +02:00
Log . debug ( ` ${ this . name } received a module notification: ${ notification } from sender: ${ sender . name } ` ) ;
2016-03-24 17:19:32 +01:00
} else {
2025-10-21 20:32:48 +02:00
Log . debug ( ` ${ this . name } received a system notification: ${ notification } ` ) ;
2016-03-24 17:19:32 +01:00
}
} ,
2020-07-28 16:48:03 +02:00
/**
2017-09-29 11:05:59 +02:00
* Returns the nunjucks environment for the current module.
2017-09-28 16:11:25 +02:00
* The environment is checked in the _nunjucksEnvironment instance variable.
2020-07-28 16:48:03 +02:00
* @returns {object} The Nunjucks Environment
2017-09-28 16:11:25 +02:00
*/
2024-01-01 15:38:08 +01:00
nunjucksEnvironment ( ) {
2019-06-05 09:32:10 +02:00
if ( this . _nunjucksEnvironment !== null ) {
2017-09-28 16:11:25 +02:00
return this . _nunjucksEnvironment ;
}
2020-05-11 22:22:32 +02:00
this . _nunjucksEnvironment = new nunjucks . Environment ( new nunjucks . WebLoader ( this . file ( "" ) , { async : true } ) , {
2017-10-18 13:49:03 +02:00
trimBlocks : true ,
lstripBlocks : true
} ) ;
2020-07-28 16:48:03 +02:00
2021-03-23 22:40:24 +01:00
this . _nunjucksEnvironment . addFilter ( "translate" , ( str , variables ) => {
return nunjucks . runtime . markSafe ( this . translate ( str , variables ) ) ;
2017-09-28 16:11:25 +02:00
} ) ;
return this . _nunjucksEnvironment ;
} ,
2020-07-28 16:48:03 +02:00
/**
* Called when a socket notification arrives.
* @param {string} notification The identifier of the notification.
2025-09-23 06:27:29 +02:00
* @param {object} payload The payload of the notification.
2016-03-30 12:20:46 +02:00
*/
2024-01-01 15:38:08 +01:00
socketNotificationReceived ( notification , payload ) {
2023-04-04 20:44:32 +02:00
Log . log ( ` ${ this . name } received a socket notification: ${ notification } - Payload: ${ payload } ` ) ;
2016-03-30 12:20:46 +02:00
} ,
2016-03-24 17:19:32 +01:00
2021-03-23 22:40:24 +01:00
/**
2020-07-28 16:48:03 +02:00
* Called when the module is hidden.
2016-05-11 13:49:40 +02:00
*/
2024-01-01 15:38:08 +01:00
suspend ( ) {
2023-04-04 20:44:32 +02:00
Log . log ( ` ${ this . name } is suspended. ` ) ;
2016-05-11 13:49:40 +02:00
} ,
2021-03-23 22:40:24 +01:00
/**
2020-07-28 16:48:03 +02:00
* Called when the module is shown.
2016-05-11 13:49:40 +02:00
*/
2024-01-01 15:38:08 +01:00
resume ( ) {
2023-04-04 20:44:32 +02:00
Log . log ( ` ${ this . name } is resumed. ` ) ;
2016-05-11 13:49:40 +02:00
} ,
2024-08-12 22:52:43 +02:00
/**
2025-11-13 15:08:47 -06:00
***********************************************
* The methods below should not be overridden. *
***********************************************
2024-08-12 22:52:43 +02:00
*/
2016-03-24 17:19:32 +01:00
2020-07-28 16:48:03 +02:00
/**
2016-03-24 17:19:32 +01:00
* Set the module data.
2021-03-23 22:40:24 +01:00
* @param {object} data The module data
2016-03-24 17:19:32 +01:00
*/
2024-01-01 15:38:08 +01:00
setData ( data ) {
2016-03-24 17:19:32 +01:00
this . data = data ;
this . name = data . name ;
this . identifier = data . identifier ;
2016-03-31 19:15:58 +02:00
this . hidden = false ;
2023-10-01 20:13:41 +02:00
this . hasAnimateIn = false ;
this . hasAnimateOut = false ;
2016-03-24 17:19:32 +01:00
2020-09-22 00:26:24 +02:00
this . setConfig ( data . config , data . configDeepMerge ) ;
2016-03-24 17:19:32 +01:00
} ,
2020-07-28 16:48:03 +02:00
/**
2016-03-24 17:19:32 +01:00
* Set the module config and combine it with the module defaults.
2020-07-28 16:48:03 +02:00
* @param {object} config The combined module config.
2020-12-29 18:48:45 +01:00
* @param {boolean} deep Merge module config in deep.
2016-03-24 17:19:32 +01:00
*/
2024-01-01 15:38:08 +01:00
setConfig ( config , deep ) {
2020-09-22 00:26:24 +02:00
this . config = deep ? configMerge ( { } , this . defaults , config ) : Object . assign ( { } , this . defaults , config ) ;
2016-03-24 17:19:32 +01:00
} ,
2020-07-28 16:48:03 +02:00
/**
* Returns a socket object. If it doesn't exist, it's created.
2016-03-30 12:20:46 +02:00
* It also registers the notification callback.
2020-07-28 16:48:03 +02:00
* @returns {MMSocket} a socket object
2016-03-30 12:20:46 +02:00
*/
2024-01-01 15:38:08 +01:00
socket ( ) {
2016-04-05 14:35:11 -04:00
if ( typeof this . _socket === "undefined" ) {
2020-05-02 10:39:09 +02:00
this . _socket = new MMSocket ( this . name ) ;
2016-03-30 12:20:46 +02:00
}
2021-03-23 22:40:24 +01:00
this . _socket . setNotificationCallback ( ( notification , payload ) => {
this . socketNotificationReceived ( notification , payload ) ;
2016-03-30 12:20:46 +02:00
} ) ;
return this . _socket ;
} ,
2020-07-28 16:48:03 +02:00
/**
2016-04-20 23:13:31 +02:00
* Retrieve the path to a module file.
2020-07-28 16:48:03 +02:00
* @param {string} file Filename
* @returns {string} the file path
2016-03-24 17:19:32 +01:00
*/
2024-01-01 15:38:08 +01:00
file ( file ) {
2023-04-04 20:44:32 +02:00
return ` ${ this . data . path } / ${ file } ` . replace ( "//" , "/" ) ;
2016-03-24 17:19:32 +01:00
} ,
2020-07-28 16:48:03 +02:00
/**
2016-03-24 17:19:32 +01:00
* Load all required stylesheets by requesting the MM object to load the files.
2023-04-04 20:44:32 +02:00
* @returns {Promise<void>}
2016-03-24 17:19:32 +01:00
*/
2024-01-01 15:38:08 +01:00
loadStyles ( ) {
2023-04-04 20:44:32 +02:00
return this . loadDependencies ( "getStyles" ) ;
2016-03-24 17:19:32 +01:00
} ,
2020-07-28 16:48:03 +02:00
/**
2016-03-24 17:19:32 +01:00
* Load all required scripts by requesting the MM object to load the files.
2023-04-04 20:44:32 +02:00
* @returns {Promise<void>}
2016-03-24 17:19:32 +01:00
*/
2024-01-01 15:38:08 +01:00
loadScripts ( ) {
2023-04-04 20:44:32 +02:00
return this . loadDependencies ( "getScripts" ) ;
2016-03-24 17:19:32 +01:00
} ,
2020-07-28 16:48:03 +02:00
/**
2017-02-07 23:51:13 +01:00
* Helper method to load all dependencies.
2020-07-28 16:48:03 +02:00
* @param {string} funcName Function name to call to get scripts or styles.
2023-04-04 20:44:32 +02:00
* @returns {Promise<void>}
2017-02-07 23:51:13 +01:00
*/
2024-01-01 15:38:08 +01:00
async loadDependencies ( funcName ) {
2021-03-23 22:40:24 +01:00
let dependencies = this [ funcName ] ( ) ;
2017-02-08 00:05:28 +01:00
2023-04-04 20:44:32 +02:00
const loadNextDependency = async ( ) => {
2017-02-08 00:05:28 +01:00
if ( dependencies . length > 0 ) {
2021-03-23 22:40:24 +01:00
const nextDependency = dependencies [ 0 ] ;
2023-04-04 20:44:32 +02:00
await Loader . loadFileForModule ( nextDependency , this ) ;
dependencies = dependencies . slice ( 1 ) ;
await loadNextDependency ( ) ;
2017-02-08 00:05:28 +01:00
} else {
2023-04-04 20:44:32 +02:00
return Promise . resolve ( ) ;
2017-02-08 00:05:28 +01:00
}
} ;
2023-04-04 20:44:32 +02:00
await loadNextDependency ( ) ;
2017-02-08 00:05:28 +01:00
} ,
2017-02-07 23:51:13 +01:00
2020-07-28 16:48:03 +02:00
/**
* Load all translations.
2023-07-01 21:17:31 +02:00
* @returns {Promise<void>}
2016-04-21 01:03:26 +02:00
*/
2024-01-01 15:38:08 +01:00
async loadTranslations ( ) {
2021-01-29 22:25:49 +01:00
const translations = this . getTranslations ( ) || { } ;
const language = config . language . toLowerCase ( ) ;
const languages = Object . keys ( translations ) ;
const fallbackLanguage = languages [ 0 ] ;
2016-05-11 12:38:41 +02:00
2021-01-29 22:34:12 +01:00
if ( languages . length === 0 ) {
2021-02-06 22:55:59 +01:00
return ;
2020-05-11 22:22:32 +02:00
}
2016-05-11 12:38:41 +02:00
2021-01-29 22:34:12 +01:00
const translationFile = translations [ language ] ;
const translationsFallbackFile = translations [ fallbackLanguage ] ;
2016-05-11 12:38:41 +02:00
2021-01-29 22:34:12 +01:00
if ( ! translationFile ) {
2023-04-04 20:44:32 +02:00
return Translator . load ( this , translationsFallbackFile , true ) ;
2021-01-29 22:34:12 +01:00
}
2023-04-04 20:44:32 +02:00
await Translator . load ( this , translationFile , false ) ;
if ( translationFile !== translationsFallbackFile ) {
return Translator . load ( this , translationsFallbackFile , true ) ;
}
2016-04-21 01:03:26 +02:00
} ,
2020-07-28 16:48:03 +02:00
/**
2017-04-25 23:15:34 +03:00
* Request the translation for a given key with optional variables and default value.
2020-07-28 16:48:03 +02:00
* @param {string} key The key of the string to translate
* @param {string|object} [defaultValueOrVariables] The default value or variables for translating.
* @param {string} [defaultValue] The default value with variables.
* @returns {string} the translated key
2016-10-13 16:42:15 +02:00
*/
2024-01-01 15:38:08 +01:00
translate ( key , defaultValueOrVariables , defaultValue ) {
2020-05-11 22:22:32 +02:00
if ( typeof defaultValueOrVariables === "object" ) {
2017-04-25 23:15:34 +03:00
return Translator . translate ( this , key , defaultValueOrVariables ) || defaultValue || "" ;
}
return Translator . translate ( this , key ) || defaultValueOrVariables || "" ;
2016-04-21 01:03:26 +02:00
} ,
2020-07-28 16:48:03 +02:00
/**
2016-03-24 17:19:32 +01:00
* Request an (animated) update of the module.
2023-10-01 20:13:41 +02:00
* @param {number|object} [updateOptions] The speed of the animation or object with for updateOptions (speed/animates)
2016-03-24 17:19:32 +01:00
*/
2024-01-01 15:38:08 +01:00
updateDom ( updateOptions ) {
2023-10-01 20:13:41 +02:00
MM . updateDom ( this , updateOptions ) ;
2016-03-24 17:19:32 +01:00
} ,
2020-07-28 16:48:03 +02:00
/**
2016-03-24 17:19:32 +01:00
* Send a notification to all modules.
2020-07-28 16:48:03 +02:00
* @param {string} notification The identifier of the notification.
2025-09-23 06:27:29 +02:00
* @param {object} payload The payload of the notification.
2016-03-24 17:19:32 +01:00
*/
2024-01-01 15:38:08 +01:00
sendNotification ( notification , payload ) {
2016-03-24 17:19:32 +01:00
MM . sendNotification ( notification , payload , this ) ;
2016-03-30 12:20:46 +02:00
} ,
2020-07-28 16:48:03 +02:00
/**
2016-03-30 12:20:46 +02:00
* Send a socket notification to the node helper.
2020-07-28 16:48:03 +02:00
* @param {string} notification The identifier of the notification.
2025-09-23 06:27:29 +02:00
* @param {object} payload The payload of the notification.
2016-03-30 12:20:46 +02:00
*/
2024-01-01 15:38:08 +01:00
sendSocketNotification ( notification , payload ) {
2016-03-30 12:20:46 +02:00
this . socket ( ) . sendNotification ( notification , payload ) ;
2016-03-31 19:15:58 +02:00
} ,
2020-07-28 16:48:03 +02:00
/**
2016-03-31 19:15:58 +02:00
* Hide this module.
2020-07-28 16:48:03 +02:00
* @param {number} speed The speed of the hide animation.
2025-09-23 06:27:29 +02:00
* @param {Promise} callback Called when the animation is done.
2020-08-01 16:31:42 +02:00
* @param {object} [options] Optional settings for the hide method.
2016-03-31 19:15:58 +02:00
*/
2024-01-01 15:38:08 +01:00
hide ( speed , callback , options = { } ) {
2023-07-01 21:17:31 +02:00
let usedCallback = callback || function ( ) { } ;
let usedOptions = options ;
2016-10-13 15:00:59 +02:00
if ( typeof callback === "object" ) {
2023-07-01 21:17:31 +02:00
Log . error ( "Parameter mismatch in module.hide: callback is not an optional parameter!" ) ;
usedOptions = callback ;
usedCallback = function ( ) { } ;
2016-10-13 15:00:59 +02:00
}
2020-05-11 22:22:32 +02:00
MM . hideModule (
2021-03-23 22:40:24 +01:00
this ,
2020-05-11 22:22:32 +02:00
speed ,
2021-03-23 22:40:24 +01:00
( ) => {
this . suspend ( ) ;
2023-07-01 21:17:31 +02:00
usedCallback ( ) ;
2020-05-11 22:22:32 +02:00
} ,
2023-07-01 21:17:31 +02:00
usedOptions
2020-05-11 22:22:32 +02:00
) ;
2016-03-31 19:15:58 +02:00
} ,
2020-07-28 16:48:03 +02:00
/**
2016-03-31 19:15:58 +02:00
* Show this module.
2020-07-28 16:48:03 +02:00
* @param {number} speed The speed of the show animation.
2025-09-23 06:27:29 +02:00
* @param {Promise} callback Called when the animation is done.
2020-08-01 16:31:42 +02:00
* @param {object} [options] Optional settings for the show method.
2016-03-31 19:15:58 +02:00
*/
2024-01-01 15:38:08 +01:00
show ( speed , callback , options ) {
2023-07-01 21:17:31 +02:00
let usedCallback = callback || function ( ) { } ;
let usedOptions = options ;
2016-10-13 15:00:59 +02:00
if ( typeof callback === "object" ) {
2023-07-01 21:17:31 +02:00
Log . error ( "Parameter mismatch in module.show: callback is not an optional parameter!" ) ;
usedOptions = callback ;
usedCallback = function ( ) { } ;
2016-10-13 15:00:59 +02:00
}
2020-12-29 18:48:45 +01:00
MM . showModule (
this ,
speed ,
2021-02-06 21:22:13 +01:00
( ) => {
this . resume ( ) ;
2023-07-01 21:17:31 +02:00
usedCallback ( ) ;
2020-12-29 18:48:45 +01:00
} ,
2023-07-01 21:17:31 +02:00
usedOptions
2020-12-29 18:48:45 +01:00
) ;
2016-03-24 17:19:32 +01:00
}
} ) ;
2020-12-29 18:48:45 +01:00
/**
2025-10-14 22:44:37 +02:00
* Merging MagicMirror² (or other) default/config script by `@bugsounet`
2020-12-29 18:48:45 +01:00
* Merge 2 objects or/with array
*
* Usage:
2020-09-21 23:17:41 +02:00
* -------
* this.config = configMerge({}, this.defaults, this.config)
* -------
2020-12-29 18:48:45 +01:00
* arg1: initial object
2020-09-21 23:17:41 +02:00
* arg2: config model
* arg3: config to merge
* -------
* why using it ?
* Object.assign() function don't to all job
* it don't merge all thing in deep
* -> object in object and array is not merging
* -------
2020-12-29 18:48:45 +01:00
*
* Todo: idea of Mich determinate what do you want to merge or not
* @param {object} result the initial object
* @returns {object} the merged config
2020-09-21 23:17:41 +02:00
*/
2024-01-01 15:38:08 +01:00
function configMerge ( result ) {
2021-03-23 22:40:24 +01:00
const stack = Array . prototype . slice . call ( arguments , 1 ) ;
let item , key ;
2020-09-21 23:17:41 +02:00
while ( stack . length ) {
item = stack . shift ( ) ;
for ( key in item ) {
if ( item . hasOwnProperty ( key ) ) {
if ( typeof result [ key ] === "object" && result [ key ] && Object . prototype . toString . call ( result [ key ] ) !== "[object Array]" ) {
if ( typeof item [ key ] === "object" && item [ key ] !== null ) {
result [ key ] = configMerge ( { } , result [ key ] , item [ key ] ) ;
} else {
result [ key ] = item [ key ] ;
}
} else {
result [ key ] = item [ key ] ;
}
}
}
}
return result ;
2020-09-22 00:26:24 +02:00
}
2020-09-21 23:17:41 +02:00
2016-03-31 13:05:23 +02:00
Module . definitions = { } ;
2016-10-13 16:42:15 +02:00
Module . create = function ( name ) {
// Make sure module definition is available.
if ( ! Module . definitions [ name ] ) {
return ;
}
2016-03-31 13:05:23 +02:00
2021-03-23 22:40:24 +01:00
const moduleDefinition = Module . definitions [ name ] ;
const clonedDefinition = cloneObject ( moduleDefinition ) ;
2016-03-31 13:05:23 +02:00
// Note that we clone the definition. Otherwise the objects are shared, which gives problems.
2021-03-23 22:40:24 +01:00
const ModuleClass = Module . extend ( clonedDefinition ) ;
2016-03-31 13:05:23 +02:00
return new ModuleClass ( ) ;
} ;
2020-07-28 16:48:03 +02:00
Module . register = function ( name , moduleDefinition ) {
if ( moduleDefinition . requiresVersion ) {
2023-04-04 20:44:32 +02:00
Log . log ( ` Check MagicMirror² version for module ' ${ name } ' - Minimum version: ${ moduleDefinition . requiresVersion } - Current version: ${ window . mmVersion } ` ) ;
2021-04-25 22:11:51 +02:00
if ( cmpVersions ( window . mmVersion , moduleDefinition . requiresVersion ) >= 0 ) {
2020-07-28 16:48:03 +02:00
Log . log ( "Version is ok!" ) ;
} else {
2023-04-04 20:44:32 +02:00
Log . warn ( ` Version is incorrect. Skip module: ' ${ name } ' ` ) ;
2020-07-28 16:48:03 +02:00
return ;
}
}
2023-04-04 20:44:32 +02:00
Log . log ( ` Module registered: ${ name } ` ) ;
2020-07-28 16:48:03 +02:00
Module . definitions [ name ] = moduleDefinition ;
} ;
2021-07-14 15:06:23 +02:00
window . Module = Module ;
2020-07-27 14:24:30 +02:00
/**
2020-05-11 22:22:32 +02:00
* Compare two semantic version numbers and return the difference.
2020-07-27 14:24:30 +02:00
* @param {string} a Version number a.
* @param {string} b Version number b.
2020-07-28 16:48:03 +02:00
* @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
2020-05-11 22:22:32 +02:00
*/
2024-01-01 15:38:08 +01:00
function cmpVersions ( a , b ) {
2021-03-23 22:40:24 +01:00
const regExStrip0 = /(\.0+)+$/ ;
const segmentsA = a . replace ( regExStrip0 , "" ) . split ( "." ) ;
const segmentsB = b . replace ( regExStrip0 , "" ) . split ( "." ) ;
const l = Math . min ( segmentsA . length , segmentsB . length ) ;
for ( let i = 0 ; i < l ; i ++ ) {
let diff = parseInt ( segmentsA [ i ] , 10 ) - parseInt ( segmentsB [ i ] , 10 ) ;
2016-10-13 16:42:15 +02:00
if ( diff ) {
return diff ;
}
}
return segmentsA . length - segmentsB . length ;
}