/* global Log, Loader, Module, config, defaults */ /* jshint -W020 */ /* Magic Mirror * Main System * * By Michael Teeuw http://michaelteeuw.nl * MIT Licensed. */ var MM = (function() { var modules = []; /* Private Methods */ /* createDomObjects() * Create dom objects for all modules that * are configured for a specific position. */ var createDomObjects = function() { for (var m in modules) { var module = modules[m]; if (typeof module.data.position === 'string') { var wrapper = selectWrapper(module.data.position); var dom = document.createElement("div"); dom.id = module.identifier; dom.className = module.name; if (typeof module.data.classes === 'string') { dom.className = dom.className + ' ' + module.data.classes; } dom.opacity = 0; wrapper.appendChild(dom); if (typeof module.data.header !== 'undefined' && module.data.header !== '') { 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); updateDom(module, 500); } } sendNotification('DOM_OBJECTS_CREATED'); }; /* selectWrapper(position) * Select the wrapper dom object for a specific position. * * argument position string - The name of the position. */ var selectWrapper = function(position) { var classes = position.replace('_',' '); var parentWrapper = document.getElementsByClassName(classes); if (parentWrapper.length > 0) { var wrapper = parentWrapper[0].getElementsByClassName('container'); if (wrapper.length > 0) { return wrapper[0]; } } }; /* 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) { 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) { var moduleWrapper = document.getElementById(module.identifier); var contentWrapper = moduleWrapper.getElementsByClassName('module-content')[0]; var tempWrapper = document.createElement('div'); tempWrapper.appendChild(newContent); return tempWrapper.innerHTML !== contentWrapper.innerHTML; }; /* 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); var contentWrapper = moduleWrapper.getElementsByClassName('module-content')[0]; contentWrapper.innerHTML = null; contentWrapper.appendChild(content); }; /* 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; setTimeout(function() { moduleWrapper.style.display = 'none'; if (typeof callback === 'function') { callback(); } }, 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"; moduleWrapper.style.opacity = 1; moduleWrapper.style.display = 'block'; setTimeout(function() { if (typeof callback === 'function') { callback(); } }, speed); } }; /* loadConfig() * Loads the core config and combines it with de system defaults. */ var loadConfig = function() { if (typeof config === 'undefined') { config = defaults; Log.error('Config file is missing! Please create a config file.'); return; } config = Object.assign(defaults, config); }; /* setSelectionMethodsForModules() * Adds special selectors on a collection of modules. * * argument modules array - Array of modules. */ var setSelectionMethodsForModules = function(modules) { /* withClass(className) * filters a collection of modules based on classname(s). * * 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; if (typeof className === 'string') { searchClasses = className.split(' '); } for (var m in modules) { var module = modules[m]; var classes = module.data.classes.toLowerCase().split(' '); for (var c in searchClasses) { var searchClass = searchClasses[c]; if (classes.indexOf(searchClass.toLowerCase()) !== -1) { newModules.push(module); } } } setSelectionMethodsForModules(newModules); return newModules; }; /* exceptWithClass(className) * filters a collection of modules based on classname(s). (NOT) * * 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; if (typeof className === 'string') { searchClasses = className.split(' '); } for (var m in modules) { var module = modules[m]; var classes = module.data.classes.toLowerCase().split(' '); 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); } } setSelectionMethodsForModules(newModules); return newModules; }; /* exceptModule(module) * Removes a module instance from the collection. * * 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. * * 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); } }; 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}); } }; return { /* Public Methods */ /* init() * Main init method. */ init: function() { Log.info('Initializing MagicMirror.'); loadConfig(); Loader.loadModules(); }, /* modulesStarted(moduleObjects) * Gets called when all modules are started. * * argument moduleObjects array - All module instances. */ modulesStarted: function(moduleObjects) { modules = []; for (var m in moduleObjects) { var module = moduleObjects[m]; modules[module.data.index] = module; } Log.info('All modules started!'); sendNotification('ALL_MODULES_STARTED'); 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) { Log.error('sendNotification: Missing arguments.'); return; } if (typeof notification !== 'string') { Log.error('sendNotification: Notification should be a string.'); return; } if (!(sender instanceof Module)) { Log.error('sendNotification: Sender should be a module.'); 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)) { Log.error('updateDom: Sender should be a module.'); return; } // Further implementation is done in the private method. updateDom(module, speed); }, /* getModules(module, speed) * Returns a collection of all modules currently active. * * return array - A collection of all modules currently active. */ getModules: function() { setSelectionMethodsForModules(modules); return modules; }, /* 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) { hideModule(module, speed, function() { module.hidden = true; if (typeof callback === 'function') { callback(); } }); }, /* 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); } }; })(); MM.init();