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
/ * M a g i c M i r r o r
* 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
/ * c r e a t e D o m O b j e c t s ( )
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
/ * s e l e c t W r a p p e r ( p o s i t i o n )
* 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
/ * s e n d N o t i f i c a t i o n ( n o t i f i c a t i o n , p a y l o a d , s e n d e r )
* 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 ) ;
}
}
} ;
/ * u p d a t e D o m ( m o d u l e , s p e e d )
* 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 ) ;
}
} ;
/ * m o d u l e N e e d s U p d a t e ( m o d u l e , n e w C o n t e n t )
* 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
/ * m o d u l e N e e d s U p d a t e ( m o d u l e , n e w C o n t e n t )
* 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
/ * h i d e M o d u l e ( m o d u l e , s p e e d , c a l l b a c k )
* 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 ) ;
}
} ;
/ * s h o w M o d u l e ( m o d u l e , s p e e d , c a l l b a c k )
* 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
} ;
/ * l o a d C o n f i g ( )
* 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
/ * s e t S e l e c t i o n M e t h o d s F o r M o d u l e s ( )
* 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 ) {
/ * w i t h C l a s s ( c l a s s N a m e )
* 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 ;
} ;
/ * e x c e p t W i t h C l a s s ( c l a s s N a m e )
* 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 ;
} ;
/ * e x c e p t M o d u l e ( m o d u l e )
* 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 ;
} ;
/ * e n u m e r a t e ( c a l l b a c k )
* 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 */
/ * i n i t ( )
* 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 ( ) ;
} ,
/ * m o d u l e s S t a r t e d ( m o d u l e O b j e c t s )
* 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 ( ) ;
} ,
/ * s e n d N o t i f i c a t i o n ( n o t i f i c a t i o n , p a y l o a d , s e n d e r )
* 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 ) ;
} ,
/ * u p d a t e D o m ( m o d u l e , s p e e d )
* 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
/ * g e t M o d u l e s ( m o d u l e , s p e e d )
* 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
} ,
/ * h i d e M o d u l e ( m o d u l e , s p e e d , c a l l b a c k )
* 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
} ,
/ * s h o w M o d u l e ( m o d u l e , s p e e d , c a l l b a c k )
* 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 ( ) ;