MagicMirror/js/main.js

618 lines
18 KiB
JavaScript
Raw Normal View History

/* global Loader, defaults, Translator */
2014-02-19 16:45:36 +01:00
2016-03-24 17:19:32 +01:00
/* Magic Mirror
* Main System
*
2020-04-28 23:05:28 +02:00
* By Michael Teeuw https://michaelteeuw.nl
2016-03-24 17:19:32 +01:00
* MIT Licensed.
*/
var MM = (function () {
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
2020-08-01 17:06:46 +02:00
/**
* Create dom objects for all modules that are configured for a specific position.
2016-03-24 17:19:32 +01:00
*/
var createDomObjects = function () {
var domCreationPromises = [];
modules.forEach(function (module) {
if (typeof module.data.position !== "string") {
return;
}
2014-02-26 14:14:29 +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
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;
wrapper.appendChild(dom);
2014-02-24 16:35:48 +01:00
var moduleHeader = document.createElement("header");
moduleHeader.innerHTML = module.getHeader();
moduleHeader.className = "module-header";
dom.appendChild(moduleHeader);
if (typeof module.getHeader() === "undefined" || module.getHeader() !== "") {
2020-07-10 23:12:17 +02:00
moduleHeader.style.display = "none;";
} else {
moduleHeader.style.display = "block;";
2019-03-08 11:33:02 +01:00
}
2016-03-31 17:05:35 +02:00
var moduleContent = document.createElement("div");
moduleContent.className = "module-content";
dom.appendChild(moduleContent);
2016-03-31 17:05:35 +02:00
var domCreationPromise = updateDom(module, 0);
domCreationPromises.push(domCreationPromise);
domCreationPromise
.then(function () {
sendNotification("MODULE_DOM_CREATED", null, null, module);
})
.catch(Log.error);
});
2014-02-24 16:35:48 +01:00
2016-11-12 20:03:56 +01:00
updateWrapperStates();
Promise.all(domCreationPromises).then(function () {
sendNotification("DOM_OBJECTS_CREATED");
});
2016-03-24 17:19:32 +01:00
};
2014-04-21 16:51:21 +02:00
2020-08-01 17:06:46 +02:00
/**
2016-03-24 17:19:32 +01:00
* Select the wrapper dom object for a specific position.
*
2020-08-01 17:06:46 +02:00
* @param {string} position The name of the position.
*
* @returns {HTMLElement} the wrapper element
2016-03-24 17:19:32 +01:00
*/
var selectWrapper = function (position) {
var classes = position.replace("_", " ");
2016-03-24 17:19:32 +01:00
var parentWrapper = document.getElementsByClassName(classes);
if (parentWrapper.length > 0) {
2017-03-10 00:27:47 -03: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
2020-08-01 17:06:46 +02:00
/**
2016-03-24 17:19:32 +01:00
* Send a notification to all modules.
*
2020-08-01 17:06:46 +02:00
* @param {string} notification The identifier of the notification.
* @param {*} payload The payload of the notification.
* @param {Module} sender The module that sent the notification.
* @param {Module} [sendTo] The (optional) module to send the notification to.
2016-03-24 17:19:32 +01:00
*/
var sendNotification = function (notification, payload, sender, sendTo) {
for (var m in modules) {
var module = modules[m];
if (module !== sender && (!sendTo || module === sendTo)) {
2016-03-24 17:19:32 +01:00
module.notificationReceived(notification, payload, sender);
}
}
};
2020-08-01 17:06:46 +02:00
/**
2016-03-24 17:19:32 +01:00
* Update the dom for a specific module.
*
2020-08-01 17:06:46 +02:00
* @param {Module} module The module that needs an update.
* @param {number} [speed] The (optional) number of microseconds for the animation.
2018-01-01 10:55:39 -06:00
*
2020-08-01 17:06:46 +02:00
* @returns {Promise} Resolved when the dom is fully updated.
2016-03-24 17:19:32 +01:00
*/
var updateDom = function (module, speed) {
return new Promise(function (resolve) {
var newContentPromise = module.getDom();
var newHeader = module.getHeader();
2018-01-01 10:55:39 -06:00
if (!(newContentPromise instanceof Promise)) {
// convert to a promise if not already one to avoid if/else's everywhere
newContentPromise = Promise.resolve(newContentPromise);
}
2018-01-01 10:55:39 -06:00
newContentPromise
.then(function (newContent) {
var updatePromise = updateDomWithContent(module, speed, newHeader, newContent);
2018-01-01 10:55:39 -06:00
updatePromise.then(resolve).catch(Log.error);
})
.catch(Log.error);
2018-01-01 09:42:34 -06:00
});
};
2020-08-01 17:06:46 +02:00
/**
2018-01-01 10:38:00 -06:00
* Update the dom with the specified content
2018-01-01 10:55:39 -06:00
*
2020-08-01 17:06:46 +02:00
* @param {Module} module The module that needs an update.
* @param {number} [speed] The (optional) number of microseconds for the animation.
* @param {string} newHeader The new header that is generated.
* @param {HTMLElement} newContent The new content that is generated.
2018-01-01 10:55:39 -06:00
*
2020-08-01 17:06:46 +02:00
* @returns {Promise} Resolved when the module dom has been updated.
2018-01-01 10:38:00 -06:00
*/
var updateDomWithContent = function (module, speed, newHeader, newContent) {
return new Promise(function (resolve) {
2018-01-01 09:42:34 -06:00
if (module.hidden || !speed) {
updateModuleContent(module, newHeader, newContent);
resolve();
return;
}
2016-03-31 19:15:58 +02:00
2016-09-20 17:22:24 +02:00
if (!moduleNeedsUpdate(module, newHeader, newContent)) {
2018-01-01 09:42:34 -06:00
resolve();
2016-03-31 19:15:58 +02:00
return;
}
if (!speed) {
2016-09-20 17:22:24 +02:00
updateModuleContent(module, newHeader, newContent);
2018-01-01 09:42:34 -06:00
resolve();
2016-03-31 19:15:58 +02:00
return;
}
hideModule(module, speed / 2, function () {
2016-09-20 17:22:24 +02:00
updateModuleContent(module, newHeader, newContent);
2016-03-31 19:15:58 +02:00
if (!module.hidden) {
showModule(module, speed / 2);
}
2018-01-01 09:42:34 -06:00
resolve();
2016-03-31 19:15:58 +02:00
});
2018-01-01 09:42:34 -06:00
});
2016-03-31 19:15:58 +02:00
};
2020-08-01 17:06:46 +02:00
/**
2016-03-31 19:15:58 +02:00
* Check if the content has changed.
*
2020-08-01 17:06:46 +02:00
* @param {Module} module The module to check.
* @param {string} newHeader The new header that is generated.
* @param {HTMLElement} newContent The new content that is generated.
2016-03-31 19:15:58 +02:00
*
2020-08-01 17:06:46 +02:00
* @returns {boolean} True if the module need an update, false otherwise
2016-03-31 19:15:58 +02:00
*/
var moduleNeedsUpdate = function (module, newHeader, newContent) {
2016-03-31 17:05:35 +02:00
var moduleWrapper = document.getElementById(module.identifier);
if (moduleWrapper === null) {
return false;
}
2016-09-20 17:22:24 +02:00
var contentWrapper = moduleWrapper.getElementsByClassName("module-content");
var headerWrapper = moduleWrapper.getElementsByClassName("module-header");
2016-03-29 13:28:15 +02:00
2016-09-20 17:22:24 +02:00
var headerNeedsUpdate = false;
var contentNeedsUpdate = false;
2016-03-29 13:28:15 +02:00
2016-09-20 17:22:24 +02:00
if (headerWrapper.length > 0) {
2016-10-25 12:30:24 +02:00
headerNeedsUpdate = newHeader !== headerWrapper[0].innerHTML;
2016-09-20 17:22:24 +02:00
}
var tempContentWrapper = document.createElement("div");
tempContentWrapper.appendChild(newContent);
contentNeedsUpdate = tempContentWrapper.innerHTML !== contentWrapper[0].innerHTML;
return headerNeedsUpdate || contentNeedsUpdate;
2016-03-31 19:15:58 +02:00
};
2016-03-24 17:19:32 +01:00
2020-08-01 17:06:46 +02:00
/**
2016-03-31 19:15:58 +02:00
* Update the content of a module on screen.
*
2020-08-01 17:06:46 +02:00
* @param {Module} module The module to check.
* @param {string} newHeader The new header that is generated.
* @param {HTMLElement} newContent The new content that is generated.
2016-03-31 19:15:58 +02:00
*/
var updateModuleContent = function (module, newHeader, newContent) {
2016-03-31 19:15:58 +02:00
var moduleWrapper = document.getElementById(module.identifier);
if (moduleWrapper === null) {
return;
}
2016-09-20 17:22:24 +02:00
var headerWrapper = moduleWrapper.getElementsByClassName("module-header");
var contentWrapper = moduleWrapper.getElementsByClassName("module-content");
contentWrapper[0].innerHTML = "";
contentWrapper[0].appendChild(newContent);
headerWrapper[0].innerHTML = newHeader;
2020-07-10 23:12:17 +02:00
if (headerWrapper.length > 0 && newHeader) {
headerWrapper[0].style.display = "block";
2020-07-10 23:12:17 +02:00
} else {
headerWrapper[0].style.display = "none";
}
2016-03-31 19:15:58 +02:00
};
2016-03-24 17:19:32 +01:00
2020-08-01 17:06:46 +02:00
/**
2016-03-31 19:15:58 +02:00
* Hide the module.
*
2020-08-01 17:06:46 +02:00
* @param {Module} module The module to hide.
* @param {number} speed The speed of the hide animation.
* @param {Function} callback Called when the animation is done.
* @param {object} [options] Optional settings for the hide method.
2016-03-31 19:15:58 +02:00
*/
var hideModule = function (module, speed, callback, options) {
options = options || {};
// set lockString if set in options.
if (options.lockString) {
// Log.log("Has lockstring: " + options.lockString);
if (module.lockStrings.indexOf(options.lockString) === -1) {
module.lockStrings.push(options.lockString);
}
}
2016-03-31 19:15:58 +02:00
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
2016-04-01 10:25:54 +02:00
// below other modules. This works way better than adjusting
// the .display property.
2016-10-16 17:24:21 +02:00
moduleWrapper.style.position = "fixed";
2016-04-01 10:25:54 +02:00
updateWrapperStates();
if (typeof callback === "function") {
callback();
}
2016-03-31 19:15:58 +02:00
}, speed);
} else {
2018-06-07 16:31:49 +02:00
// invoke callback even if no content, issue 1308
if (typeof callback === "function") {
callback();
}
2018-06-07 07:59:07 -05:00
}
2016-03-31 19:15:58 +02:00
};
2020-08-01 17:06:46 +02:00
/**
2016-03-31 19:15:58 +02:00
* Show the module.
*
2020-08-01 17:06:46 +02:00
* @param {Module} module The module to show.
* @param {number} speed The speed of the show animation.
* @param {Function} callback Called when the animation is done.
* @param {object} [options] Optional settings for the show method.
2016-03-31 19:15:58 +02:00
*/
var showModule = function (module, speed, callback, options) {
options = options || {};
// remove lockString if set in options.
if (options.lockString) {
var index = module.lockStrings.indexOf(options.lockString);
if (index !== -1) {
module.lockStrings.splice(index, 1);
}
}
// Check if there are no more lockstrings set, or the force option is set.
// Otherwise cancel show action.
if (module.lockStrings.length !== 0 && options.force !== true) {
Log.log("Will not show " + module.name + ". LockStrings active: " + module.lockStrings.join(","));
callback(new Error("ERR_ACTIVE_LOCK_STRINGS"));
return;
}
2017-01-28 12:26:52 +01:00
module.hidden = false;
// If forced show, clean current lockstrings.
if (module.lockStrings.length !== 0 && options.force === true) {
Log.log("Force show of module: " + module.name);
module.lockStrings = [];
}
2016-03-31 19:15:58 +02:00
var moduleWrapper = document.getElementById(module.identifier);
if (moduleWrapper !== null) {
moduleWrapper.style.transition = "opacity " + speed / 1000 + "s";
2019-06-04 09:33:53 +02:00
// Restore the position. See hideModule() for more info.
2016-04-05 14:35:11 -04:00
moduleWrapper.style.position = "static";
2016-03-24 17:19:32 +01:00
updateWrapperStates();
// Waiting for DOM-changes done in updateWrapperStates before we can start the animation.
var dummy = moduleWrapper.parentElement.parentElement.offsetHeight;
moduleWrapper.style.opacity = 1;
2016-04-08 17:27:02 +02:00
clearTimeout(module.showHideTimer);
module.showHideTimer = setTimeout(function () {
if (typeof callback === "function") {
2021-01-28 07:33:02 +01:00
callback(null);
}
2016-03-31 19:15:58 +02:00
}, speed);
2020-03-19 19:03:25 +01:00
} else {
// invoke callback
if (typeof callback === "function") {
2021-01-28 07:33:02 +01:00
callback(null);
}
2016-03-31 19:15:58 +02:00
}
2016-03-24 17:19:32 +01:00
};
2020-08-01 17:06:46 +02:00
/**
* Checks for all positions if it has visible content.
* If not, if will hide the position to prevent unwanted margins.
2019-06-04 09:33:53 +02:00
* This method should be called by the show and hide methods.
2016-11-24 00:26:40 -03:00
*
* Example:
* If the top_bar only contains the update notification. And no update is available,
* the update notification is hidden. The top bar still occupies space making for
2016-11-24 00:26:40 -03:00
* an ugly top margin. By using this function, the top bar will be hidden if the
* update notification is not visible.
*/
var updateWrapperStates = function () {
var positions = ["top_bar", "top_left", "top_center", "top_right", "upper_third", "middle_center", "lower_third", "bottom_left", "bottom_center", "bottom_right", "bottom_bar", "fullscreen_above", "fullscreen_below"];
2016-11-24 00:26:40 -03:00
positions.forEach(function (position) {
2016-11-24 00:26:40 -03:00
var wrapper = selectWrapper(position);
var moduleWrappers = wrapper.getElementsByClassName("module");
var showWrapper = false;
Array.prototype.forEach.call(moduleWrappers, function (moduleWrapper) {
if (moduleWrapper.style.position === "" || moduleWrapper.style.position === "static") {
showWrapper = true;
2016-11-24 00:26:40 -03:00
}
});
wrapper.style.display = showWrapper ? "block" : "none";
});
};
2020-08-01 17:06:46 +02:00
/**
* Loads the core config and combines it with the system defaults.
2016-03-24 17:19:32 +01:00
*/
var loadConfig = function () {
// FIXME: Think about how to pass config around without breaking tests
/* eslint-disable */
2016-04-05 14:35:11 -04:00
if (typeof config === "undefined") {
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);
/* eslint-enable */
2016-03-24 17:19:32 +01:00
};
2020-08-01 17:06:46 +02:00
/**
2016-03-31 17:05:35 +02:00
* Adds special selectors on a collection of modules.
*
2020-08-01 17:06:46 +02:00
* @param {Module[]} modules Array of modules.
2016-03-31 17:05:35 +02:00
*/
var setSelectionMethodsForModules = function (modules) {
2020-08-01 17:06:46 +02:00
/**
* Filter modules with the specified classes.
*
2020-08-01 17:06:46 +02:00
* @param {string|string[]} className one or multiple classnames (array or space divided).
2016-03-31 17:05:35 +02:00
*
2020-08-01 17:06:46 +02:00
* @returns {Module[]} Filtered collection of modules.
2016-03-31 17:05:35 +02:00
*/
var withClass = function (className) {
2017-02-07 23:51:13 +01:00
return modulesByClass(className, true);
2016-03-31 17:05:35 +02:00
};
2020-08-01 17:06:46 +02:00
/**
* Filter modules without the specified classes.
*
2020-08-01 17:06:46 +02:00
* @param {string|string[]} className one or multiple classnames (array or space divided).
2016-03-31 17:05:35 +02:00
*
2020-08-01 17:06:46 +02:00
* @returns {Module[]} Filtered collection of modules.
2016-03-31 17:05:35 +02:00
*/
var exceptWithClass = function (className) {
2017-02-08 00:05:28 +01:00
return modulesByClass(className, false);
2016-03-31 17:05:35 +02:00
};
2020-08-01 17:06:46 +02:00
/**
* Filters a collection of modules based on classname(s).
2017-02-07 23:51:13 +01:00
*
2020-08-01 17:06:46 +02:00
* @param {string|string[]} className one or multiple classnames (array or space divided).
* @param {boolean} include if the filter should include or exclude the modules with the specific classes.
2017-02-07 23:51:13 +01:00
*
2020-08-01 17:06:46 +02:00
* @returns {Module[]} Filtered collection of modules.
2017-02-07 23:51:13 +01:00
*/
var modulesByClass = function (className, include) {
2017-02-08 00:05:28 +01:00
var searchClasses = className;
if (typeof className === "string") {
searchClasses = className.split(" ");
}
var newModules = modules.filter(function (module) {
2017-02-08 00:05:28 +01:00
var classes = module.data.classes.toLowerCase().split(" ");
for (var c in searchClasses) {
var searchClass = searchClasses[c];
if (classes.indexOf(searchClass.toLowerCase()) !== -1) {
return include;
}
}
return !include;
});
setSelectionMethodsForModules(newModules);
return newModules;
};
2017-02-07 23:51:13 +01:00
2020-08-01 17:06:46 +02:00
/**
2016-03-31 17:05:35 +02:00
* Removes a module instance from the collection.
*
2020-08-01 17:06:46 +02:00
* @param {object} module The module instance to remove from the collection.
2016-03-31 17:05:35 +02:00
*
2020-08-01 17:06:46 +02:00
* @returns {Module[]} Filtered collection of modules.
2016-03-31 17:05:35 +02:00
*/
var exceptModule = function (module) {
var newModules = modules.filter(function (mod) {
return mod.identifier !== module.identifier;
});
2016-03-31 17:05:35 +02:00
setSelectionMethodsForModules(newModules);
return newModules;
};
2020-08-01 17:06:46 +02:00
/**
2016-03-31 17:05:35 +02:00
* Walks thru a collection of modules and executes the callback with the module as an argument.
*
2020-08-01 17:06:46 +02:00
* @param {Function} callback The function to execute with the module as an argument.
2016-03-31 17:05:35 +02:00
*/
var enumerate = function (callback) {
modules.map(function (module) {
2016-03-31 17:05:35 +02:00
callback(module);
});
2016-03-31 17:05:35 +02: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 */
2020-08-01 17:06:46 +02:00
/**
2016-03-24 17:19:32 +01:00
* 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();
2020-07-04 22:02:39 +02:00
Log.setLogLevel(config.logLevel);
2016-05-11 12:38:41 +02:00
Translator.loadCoreTranslations(config.language);
2016-03-24 17:19:32 +01:00
Loader.loadModules();
},
2020-08-01 17:06:46 +02:00
/**
2016-03-24 17:19:32 +01:00
* Gets called when all modules are started.
*
2020-08-01 17:06:46 +02:00
* @param {Module[]} moduleObjects All module instances.
2016-03-24 17:19:32 +01:00
*/
modulesStarted: function (moduleObjects) {
2016-03-24 17:19:32 +01:00
modules = [];
2020-12-29 21:45:35 +01:00
moduleObjects.forEach((module) => modules.push(module));
2016-03-24 17:19:32 +01:00
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();
},
2020-08-01 17:06:46 +02:00
/**
2016-03-24 17:19:32 +01:00
* Send a notification to all modules.
*
2020-08-01 17:06:46 +02:00
* @param {string} notification The identifier of the notification.
* @param {*} payload The payload of the notification.
* @param {Module} sender The module that sent the notification.
2016-03-24 17:19:32 +01:00
*/
sendNotification: function (notification, payload, sender) {
2016-03-24 17:19:32 +01:00
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);
},
2020-08-01 17:06:46 +02:00
/**
2016-03-24 17:19:32 +01:00
* Update the dom for a specific module.
*
2020-08-01 17:06:46 +02:00
* @param {Module} module The module that needs an update.
* @param {number} [speed] The number of microseconds for the animation.
2016-03-24 17:19:32 +01:00
*/
updateDom: function (module, speed) {
2016-03-24 17:19:32 +01:00
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-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
},
2020-08-01 17:06:46 +02:00
/**
2016-03-31 17:06:39 +02:00
* Returns a collection of all modules currently active.
*
2020-08-01 17:06:46 +02:00
* @returns {Module[]} A collection of all modules currently active.
2016-03-31 17:06:39 +02:00
*/
getModules: function () {
2016-03-31 17:05:35 +02:00
setSelectionMethodsForModules(modules);
return modules;
2016-03-31 19:15:58 +02:00
},
2020-08-01 17:06:46 +02:00
/**
2016-03-31 19:15:58 +02:00
* Hide the module.
*
2020-08-01 17:06:46 +02:00
* @param {Module} module The module to hide.
* @param {number} speed The speed of the hide animation.
* @param {Function} callback Called when the animation is done.
* @param {object} [options] Optional settings for the hide method.
2016-03-31 19:15:58 +02:00
*/
hideModule: function (module, speed, callback, options) {
2016-04-01 10:44:17 +02:00
module.hidden = true;
hideModule(module, speed, callback, options);
2016-03-31 19:15:58 +02:00
},
2020-08-01 17:06:46 +02:00
/**
2016-03-31 19:15:58 +02:00
* Show the module.
*
2020-08-01 17:06:46 +02:00
* @param {Module} module The module to show.
* @param {number} speed The speed of the show animation.
* @param {Function} callback Called when the animation is done.
* @param {object} [options] Optional settings for the show method.
2016-03-31 19:15:58 +02:00
*/
showModule: function (module, speed, callback, options) {
2017-01-28 12:26:52 +01:00
// do not change module.hidden yet, only if we really show it later
showModule(module, speed, callback, options);
2016-03-24 17:19:32 +01:00
}
};
})();
2016-04-18 20:03:12 +02:00
// Add polyfill for Object.assign.
2019-06-05 09:32:10 +02:00
if (typeof Object.assign !== "function") {
(function () {
Object.assign = function (target) {
2016-05-03 19:09:38 -04:00
"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-04-18 20:03:12 +02:00
2016-03-24 17:19:32 +01:00
MM.init();