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 ();