2016-03-24 17:19:32 +01:00
/* global Log, Loader, Module, config, defaults */
/* jshint -W020 */
2014-02-19 16:45:36 +01:00
2016-03-24 17:19:32 +01:00
/* Magic Mirror
* Main System
*
* By Michael Teeuw http://michaelteeuw.nl
* MIT Licensed.
*/
2014-02-19 16:45:36 +01:00
2016-03-24 17:19:32 +01:00
var MM = ( function ( ) {
2014-02-19 16:45:36 +01:00
2016-03-24 17:19:32 +01:00
var modules = [ ] ;
2014-02-24 16:35:48 +01:00
2016-03-24 17:19:32 +01:00
/* Private Methods */
2014-02-19 16:45:36 +01:00
2016-03-24 17:19:32 +01:00
/* createDomObjects()
2016-04-03 19:52:13 +02:00
* Create dom objects for all modules that
2016-03-24 17:19:32 +01:00
* are configured for a specific position.
*/
var createDomObjects = function ( ) {
for ( var m in modules ) {
var module = modules [ m ] ;
2016-04-03 19:52:13 +02:00
2016-04-05 14:35:11 -04:00
if ( typeof module . data . position === "string" ) {
2014-02-26 14:14:29 +01:00
2016-03-24 17:19:32 +01:00
var wrapper = selectWrapper ( module . data . position ) ;
2016-03-29 13:28:15 +02:00
var dom = document . createElement ( "div" ) ;
dom . id = module . identifier ;
dom . className = module . name ;
2016-03-31 17:05:35 +02:00
2016-04-05 14:35:11 -04:00
if ( typeof module . data . classes === "string" ) {
dom . className = "module " + dom . className + " " + module . data . classes ;
2016-03-31 17:05:35 +02:00
}
dom . opacity = 0 ;
2016-03-24 17:19:32 +01:00
wrapper . appendChild ( dom ) ;
2014-02-24 16:35:48 +01:00
2016-04-05 14:35:11 -04:00
if ( typeof module . data . header !== "undefined" && module . data . header !== "" ) {
2016-03-31 17:05:35 +02:00
var moduleHeader = document . createElement ( "header" ) ;
moduleHeader . innerHTML = module . data . header ;
dom . appendChild ( moduleHeader ) ;
}
var moduleContent = document . createElement ( "div" ) ;
moduleContent . className = "module-content" ;
dom . appendChild ( moduleContent ) ;
2016-04-01 17:49:43 +02:00
updateDom ( module , 0 ) ;
2016-03-24 17:19:32 +01:00
}
}
2014-02-24 16:35:48 +01:00
2016-04-05 14:35:11 -04:00
sendNotification ( "DOM_OBJECTS_CREATED" ) ;
2016-03-24 17:19:32 +01:00
} ;
2014-04-21 16:51:21 +02:00
2016-03-24 17:19:32 +01:00
/* selectWrapper(position)
* Select the wrapper dom object for a specific position.
*
* argument position string - The name of the position.
*/
var selectWrapper = function ( position ) {
2016-04-05 14:35:11 -04:00
var classes = position . replace ( "_" , " " ) ;
2016-03-24 17:19:32 +01:00
var parentWrapper = document . getElementsByClassName ( classes ) ;
if ( parentWrapper . length > 0 ) {
2016-04-05 14:35:11 -04:00
var wrapper = parentWrapper [ 0 ] . getElementsByClassName ( "container" ) ;
2016-03-24 17:19:32 +01:00
if ( wrapper . length > 0 ) {
return wrapper [ 0 ] ;
}
}
} ;
2014-02-19 17:02:17 +01:00
2016-03-24 17:19:32 +01:00
/* sendNotification(notification, payload, sender)
* Send a notification to all modules.
*
* argument notification string - The identifier of the noitication.
* argument payload mixed - The payload of the notification.
* argument sender Module - The module that sent the notification.
*/
var sendNotification = function ( notification , payload , sender ) {
for ( var m in modules ) {
var module = modules [ m ] ;
if ( module !== sender ) {
module . notificationReceived ( notification , payload , sender ) ;
}
}
} ;
/* updateDom(module, speed)
* Update the dom for a specific module.
*
* argument module Module - The module that needs an update.
* argument speed Number - The number of microseconds for the animation. (optional)
*/
var updateDom = function ( module , speed ) {
2016-03-31 19:15:58 +02:00
var newContent = module . getDom ( ) ;
if ( ! module . hidden ) {
if ( ! moduleNeedsUpdate ( module , newContent ) ) {
return ;
}
if ( ! speed ) {
updateModuleContent ( module , newContent ) ;
return ;
}
hideModule ( module , speed / 2 , function ( ) {
updateModuleContent ( module , newContent ) ;
if ( ! module . hidden ) {
showModule ( module , speed / 2 ) ;
}
} ) ;
} else {
updateModuleContent ( module , newContent ) ;
}
} ;
/* moduleNeedsUpdate(module, newContent)
* Check if the content has changed.
*
* argument module Module - The module to check.
* argument newContent Domobject - The new content that is generated.
*
* return bool - Does the module need an update?
*/
var moduleNeedsUpdate = function ( module , newContent ) {
2016-03-31 17:05:35 +02:00
var moduleWrapper = document . getElementById ( module . identifier ) ;
2016-04-05 14:35:11 -04:00
var contentWrapper = moduleWrapper . getElementsByClassName ( "module-content" ) [ 0 ] ;
2016-03-29 13:28:15 +02:00
2016-04-05 14:35:11 -04:00
var tempWrapper = document . createElement ( "div" ) ;
2016-03-29 13:28:15 +02:00
tempWrapper . appendChild ( newContent ) ;
2016-03-31 19:15:58 +02:00
return tempWrapper . innerHTML !== contentWrapper . innerHTML ;
} ;
2016-03-24 17:19:32 +01:00
2016-03-31 19:15:58 +02:00
/* moduleNeedsUpdate(module, newContent)
* Update the content of a module on screen.
*
* argument module Module - The module to check.
* argument newContent Domobject - The new content that is generated.
*/
var updateModuleContent = function ( module , content ) {
var moduleWrapper = document . getElementById ( module . identifier ) ;
2016-04-05 14:35:11 -04:00
var contentWrapper = moduleWrapper . getElementsByClassName ( "module-content" ) [ 0 ] ;
2016-03-24 17:19:32 +01:00
2016-03-31 19:15:58 +02:00
contentWrapper . innerHTML = null ;
contentWrapper . appendChild ( content ) ;
} ;
2016-03-24 17:19:32 +01:00
2016-03-31 19:15:58 +02:00
/* hideModule(module, speed, callback)
* Hide the module.
*
* argument module Module - The module to hide.
* argument speed Number - The speed of the hide animation.
* argument callback function - Called when the animation is done.
*/
var hideModule = function ( module , speed , callback ) {
var moduleWrapper = document . getElementById ( module . identifier ) ;
if ( moduleWrapper !== null ) {
moduleWrapper . style . transition = "opacity " + speed / 1000 + "s" ;
moduleWrapper . style . opacity = 0 ;
2016-03-24 17:19:32 +01:00
2016-04-08 17:27:02 +02:00
clearTimeout ( module . showHideTimer ) ;
module . showHideTimer = setTimeout ( function ( ) {
2016-04-01 10:25:54 +02:00
// To not take up any space, we just make the position absolute.
// since it's fade out anyway, we can see it lay above or
// below other modules. This works way better than adjusting
// the .display property.
2016-04-05 14:35:11 -04:00
moduleWrapper . style . position = "absolute" ;
2016-04-01 10:25:54 +02:00
2016-04-05 14:35:11 -04:00
if ( typeof callback === "function" ) { callback ( ) ; }
2016-03-31 19:15:58 +02:00
} , speed ) ;
}
} ;
/* showModule(module, speed, callback)
* Show the module.
*
* argument module Module - The module to show.
* argument speed Number - The speed of the show animation.
* argument callback function - Called when the animation is done.
*/
var showModule = function ( module , speed , callback ) {
var moduleWrapper = document . getElementById ( module . identifier ) ;
if ( moduleWrapper !== null ) {
moduleWrapper . style . transition = "opacity " + speed / 1000 + "s" ;
2016-04-01 10:25:54 +02:00
// Restore the postition. See hideModule() for more info.
2016-04-05 14:35:11 -04:00
moduleWrapper . style . position = "static" ;
2016-03-31 19:15:58 +02:00
moduleWrapper . style . opacity = 1 ;
2016-03-24 17:19:32 +01:00
2016-04-08 17:27:02 +02:00
clearTimeout ( module . showHideTimer ) ;
module . showHideTimer = setTimeout ( function ( ) {
2016-04-05 14:35:11 -04:00
if ( typeof callback === "function" ) { callback ( ) ; }
2016-03-31 19:15:58 +02:00
} , speed ) ;
2016-04-01 10:25:54 +02:00
2016-03-31 19:15:58 +02:00
}
2016-03-24 17:19:32 +01:00
} ;
/* loadConfig()
* Loads the core config and combines it with de system defaults.
*/
var loadConfig = function ( ) {
2016-04-05 14:35:11 -04:00
if ( typeof config === "undefined" ) {
2016-03-24 17:19:32 +01:00
config = defaults ;
2016-04-05 14:35:11 -04:00
Log . error ( "Config file is missing! Please create a config file." ) ;
2016-03-24 17:19:32 +01:00
return ;
}
config = Object . assign ( defaults , config ) ;
} ;
2016-03-31 17:05:35 +02:00
/* setSelectionMethodsForModules()
* Adds special selectors on a collection of modules.
2016-04-03 19:52:13 +02:00
*
2016-03-31 17:05:35 +02:00
* argument modules array - Array of modules.
*/
var setSelectionMethodsForModules = function ( modules ) {
/* withClass(className)
* filters a collection of modules based on classname(s).
2016-04-03 19:52:13 +02:00
*
2016-03-31 17:05:35 +02:00
* argument className string/array - one or multiple classnames. (array or space devided)
*
* return array - Filtered collection of modules.
*/
var withClass = function ( className ) {
var newModules = [ ] ;
var searchClasses = className ;
2016-04-05 14:35:11 -04:00
if ( typeof className === "string" ) {
searchClasses = className . split ( " " ) ;
2016-03-31 17:05:35 +02:00
}
for ( var m in modules ) {
var module = modules [ m ] ;
2016-04-05 14:35:11 -04:00
var classes = module . data . classes . toLowerCase ( ) . split ( " " ) ;
2016-03-31 17:05:35 +02:00
for ( var c in searchClasses ) {
var searchClass = searchClasses [ c ] ;
if ( classes . indexOf ( searchClass . toLowerCase ( ) ) !== - 1 ) {
newModules . push ( module ) ;
}
2016-04-03 19:52:13 +02:00
}
2016-03-31 17:05:35 +02:00
}
setSelectionMethodsForModules ( newModules ) ;
return newModules ;
} ;
/* exceptWithClass(className)
* filters a collection of modules based on classname(s). (NOT)
2016-04-03 19:52:13 +02:00
*
2016-03-31 17:05:35 +02:00
* argument className string/array - one or multiple classnames. (array or space devided)
*
* return array - Filtered collection of modules.
*/
var exceptWithClass = function ( className ) {
var newModules = [ ] ;
var searchClasses = className ;
2016-04-05 14:35:11 -04:00
if ( typeof className === "string" ) {
searchClasses = className . split ( " " ) ;
2016-03-31 17:05:35 +02:00
}
for ( var m in modules ) {
var module = modules [ m ] ;
2016-04-05 14:35:11 -04:00
var classes = module . data . classes . toLowerCase ( ) . split ( " " ) ;
2016-03-31 17:05:35 +02:00
var foundClass = false ;
for ( var c in searchClasses ) {
var searchClass = searchClasses [ c ] ;
if ( classes . indexOf ( searchClass . toLowerCase ( ) ) !== - 1 ) {
foundClass = true ;
break ;
}
}
if ( ! foundClass ) {
newModules . push ( module ) ;
2016-04-03 19:52:13 +02:00
}
2016-03-31 17:05:35 +02:00
}
setSelectionMethodsForModules ( newModules ) ;
return newModules ;
} ;
/* exceptModule(module)
* Removes a module instance from the collection.
2016-04-03 19:52:13 +02:00
*
2016-03-31 17:05:35 +02:00
* argument module Module object - The module instance to remove from the collection.
*
* return array - Filtered collection of modules.
*/
var exceptModule = function ( module ) {
var newModules = [ ] ;
for ( var m in modules ) {
var mod = modules [ m ] ;
if ( mod . identifier !== module . identifier ) {
newModules . push ( mod ) ;
}
}
setSelectionMethodsForModules ( newModules ) ;
return newModules ;
} ;
/* enumerate(callback)
* Walks thru a collection of modules and executes the callback with the module as an argument.
2016-04-03 19:52:13 +02:00
*
2016-03-31 17:05:35 +02:00
* argument callback function - The function to execute with the module as an argument.
*/
var enumerate = function ( callback ) {
for ( var m in modules ) {
var module = modules [ m ] ;
callback ( module ) ;
}
} ;
2016-04-05 14:35:11 -04:00
if ( typeof modules . withClass === "undefined" ) { Object . defineProperty ( modules , "withClass" , { value : withClass , enumerable : false } ) ; }
if ( typeof modules . exceptWithClass === "undefined" ) { Object . defineProperty ( modules , "exceptWithClass" , { value : exceptWithClass , enumerable : false } ) ; }
if ( typeof modules . exceptModule === "undefined" ) { Object . defineProperty ( modules , "exceptModule" , { value : exceptModule , enumerable : false } ) ; }
if ( typeof modules . enumerate === "undefined" ) { Object . defineProperty ( modules , "enumerate" , { value : enumerate , enumerable : false } ) ; }
2016-03-31 17:05:35 +02:00
} ;
2016-03-31 19:15:58 +02:00
2016-03-24 17:19:32 +01:00
return {
/* Public Methods */
/* init()
* Main init method.
*/
init : function ( ) {
2016-04-05 14:35:11 -04:00
Log . info ( "Initializing MagicMirror." ) ;
2016-03-24 17:19:32 +01:00
loadConfig ( ) ;
Loader . loadModules ( ) ;
} ,
/* modulesStarted(moduleObjects)
* Gets called when all modules are started.
*
* argument moduleObjects array<Module> - All module instances.
*/
modulesStarted : function ( moduleObjects ) {
modules = [ ] ;
for ( var m in moduleObjects ) {
var module = moduleObjects [ m ] ;
modules [ module . data . index ] = module ;
}
2016-04-05 14:35:11 -04:00
Log . info ( "All modules started!" ) ;
sendNotification ( "ALL_MODULES_STARTED" ) ;
2016-03-24 17:19:32 +01:00
createDomObjects ( ) ;
} ,
/* sendNotification(notification, payload, sender)
* Send a notification to all modules.
*
* argument notification string - The identifier of the noitication.
* argument payload mixed - The payload of the notification.
* argument sender Module - The module that sent the notification.
*/
sendNotification : function ( notification , payload , sender ) {
if ( arguments . length < 3 ) {
2016-04-05 14:35:11 -04:00
Log . error ( "sendNotification: Missing arguments." ) ;
2016-03-24 17:19:32 +01:00
return ;
}
2016-04-05 14:35:11 -04:00
if ( typeof notification !== "string" ) {
Log . error ( "sendNotification: Notification should be a string." ) ;
2016-03-24 17:19:32 +01:00
return ;
}
if ( ! ( sender instanceof Module ) ) {
2016-04-05 14:35:11 -04:00
Log . error ( "sendNotification: Sender should be a module." ) ;
2016-03-24 17:19:32 +01:00
return ;
}
// Further implementation is done in the private method.
sendNotification ( notification , payload , sender ) ;
} ,
/* updateDom(module, speed)
* Update the dom for a specific module.
*
* argument module Module - The module that needs an update.
* argument speed Number - The number of microseconds for the animation. (optional)
*/
updateDom : function ( module , speed ) {
if ( ! ( module instanceof Module ) ) {
2016-04-05 14:35:11 -04:00
Log . error ( "updateDom: Sender should be a module." ) ;
2016-03-24 17:19:32 +01:00
return ;
}
2016-04-03 19:52:13 +02:00
2016-03-24 17:19:32 +01:00
// Further implementation is done in the private method.
updateDom ( module , speed ) ;
2016-03-31 17:05:35 +02:00
} ,
2016-03-31 17:06:39 +02:00
/* getModules(module, speed)
* Returns a collection of all modules currently active.
*
* return array - A collection of all modules currently active.
*/
2016-03-31 17:05:35 +02:00
getModules : function ( ) {
setSelectionMethodsForModules ( modules ) ;
return modules ;
2016-03-31 19:15:58 +02:00
} ,
/* hideModule(module, speed, callback)
* Hide the module.
*
* argument module Module - The module hide.
* argument speed Number - The speed of the hide animation.
* argument callback function - Called when the animation is done.
*/
hideModule : function ( module , speed , callback ) {
2016-04-01 10:44:17 +02:00
module . hidden = true ;
hideModule ( module , speed , callback ) ;
2016-03-31 19:15:58 +02:00
} ,
/* showModule(module, speed, callback)
* Show the module.
*
* argument module Module - The module show.
* argument speed Number - The speed of the show animation.
* argument callback function - Called when the animation is done.
*/
showModule : function ( module , speed , callback ) {
module . hidden = false ;
showModule ( module , speed , callback ) ;
2016-03-24 17:19:32 +01:00
}
} ;
} ) ( ) ;
2016-04-18 20:03:12 +02:00
// Add polyfill for Object.assign.
if ( typeof Object . assign != 'function' ) { ( function ( ) { Object . assign = function ( target ) { 'use strict' ; if ( target === undefined || target === null ) { throw new TypeError ( 'Cannot convert undefined or null to object' ) ; } var output = Object ( target ) ; for ( var index = 1 ; index < arguments . length ; index ++ ) { var source = arguments [ index ] ; if ( source !== undefined && source !== null ) { for ( var nextKey in source ) { if ( source . hasOwnProperty ( nextKey ) ) { output [ nextKey ] = source [ nextKey ] ; } } } } return output ; } ; } ) ( ) ; }
2016-03-24 17:19:32 +01:00
MM . init ( ) ;