diff --git a/CHANGELOG.md b/CHANGELOG.md index d999b4bc..2b32b8fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,22 +2,28 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -## [2.2.3] - 2018-01-06 +## [2.3.0] - Unreleased ### Added +- Add system notification `MODULE_DOM_CREATED` for notifying each module when their Dom has been fully loaded. - Add types for module. +*This release is scheduled to be released on 2018-04-01.* + ## [2.2.2] - 2018-01-02 ### Added - Add missing `package-lock.json`. +### Changed + +- Changed Electron dependency to v1.7.10. + ## [2.2.1] - 2018-01-01 ### Fixed - - Fixed linting errors. ## [2.2.0] - 2018-01-01 diff --git a/js/main.js b/js/main.js index f499da62..0e1e5239 100644 --- a/js/main.js +++ b/js/main.js @@ -19,38 +19,39 @@ var MM = (function() { * 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 = "module " + 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; - moduleHeader.className = "module-header"; - dom.appendChild(moduleHeader); - } - - var moduleContent = document.createElement("div"); - moduleContent.className = "module-content"; - dom.appendChild(moduleContent); - - updateDom(module, 0); + modules.forEach(module => { + if (typeof module.data.position !== "string") { + return; } - } + + 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 = "module " + 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; + moduleHeader.className = "module-header"; + dom.appendChild(moduleHeader); + } + + var moduleContent = document.createElement("div"); + moduleContent.className = "module-content"; + dom.appendChild(moduleContent); + + updateDom(module, 0).then(() => { + sendNotification("MODULE_DOM_CREATED", null, null, module); + }).catch(Log.error); + }); updateWrapperStates(); @@ -79,11 +80,11 @@ var MM = (function() { * argument notification string - The identifier of the notification. * argument payload mixed - The payload of the notification. * argument sender Module - The module that sent the notification. + * argument sendTo Module - The module to send the notification to. (optional) */ - var sendNotification = function(notification, payload, sender) { - for (var m in modules) { - var module = modules[m]; - if (module !== sender) { + var sendNotification = function(notification, payload, sender, sendTo) { + for (var module of modules) { + if (module !== sender && (!sendTo || module === sendTo)) { module.notificationReceived(notification, payload, sender); } } @@ -94,19 +95,53 @@ var MM = (function() { * * argument module Module - The module that needs an update. * argument speed Number - The number of microseconds for the animation. (optional) + * + * return Promise - Resolved when the dom is fully updated. */ var updateDom = function(module, speed) { - var newContent = module.getDom(); - var newHeader = module.getHeader(); + return new Promise((resolve) => { + var newContentPromise = module.getDom(); + var newHeader = module.getHeader(); - if (!module.hidden) { + if (!(newContentPromise instanceof Promise)) { + // convert to a promise if not already one to avoid if/else's everywhere + newContentPromise = Promise.resolve(newContentPromise); + } + + newContentPromise.then((newContent) => { + var updatePromise = updateDomWithContent(module, speed, newHeader, newContent); + + updatePromise.then(resolve).catch(Log.error); + }).catch(Log.error); + }); + }; + + /* updateDomWithContent(module, speed, newHeader, newContent) + * Update the dom with the specified content + * + * argument module Module - The module that needs an update. + * argument speed Number - The number of microseconds for the animation. (optional) + * argument newHeader String - The new header that is generated. + * argument newContent Domobject - The new content that is generated. + * + * return Promise - Resolved when the module dom has been updated. + */ + var updateDomWithContent = function(module, speed, newHeader, newContent) { + return new Promise((resolve) => { + if (module.hidden || !speed) { + updateModuleContent(module, newHeader, newContent); + resolve(); + return; + } if (!moduleNeedsUpdate(module, newHeader, newContent)) { + resolve(); return; } if (!speed) { updateModuleContent(module, newHeader, newContent); + resolve(); return; } @@ -115,16 +150,16 @@ var MM = (function() { if (!module.hidden) { showModule(module, speed / 2); } + resolve(); }); - } else { - updateModuleContent(module, newHeader, newContent); - } + }); }; /* moduleNeedsUpdate(module, newContent) * Check if the content has changed. * * argument module Module - The module to check. + * argument newHeader String - The new header that is generated. * argument newContent Domobject - The new content that is generated. * * return bool - Does the module need an update? @@ -152,6 +187,7 @@ var MM = (function() { * Update the content of a module on screen. * * argument module Module - The module to check. + * argument newHeader String - The new header that is generated. * argument newContent Domobject - The new content that is generated. */ var updateModuleContent = function(module, newHeader, newContent) { diff --git a/js/module.js b/js/module.js index 464509ee..57bdde4f 100644 --- a/js/module.js +++ b/js/module.js @@ -78,34 +78,33 @@ var Module = Class.extend({ * This method can to be subclassed if the module wants to display info on the mirror. * Alternatively, the getTemplete method could be subclassed. * - * return domobject - The dom to display. + * return DomObject | Promise - The dom or a promise with the dom to display. */ getDom: function () { - var div = document.createElement("div"); - var template = this.getTemplate(); - var templateData = this.getTemplateData(); + return new Promise((resolve) => { + var div = document.createElement("div"); + var template = this.getTemplate(); + var templateData = this.getTemplateData(); - // Check to see if we need to render a template string or a file. - if (/^.*((\.html)|(\.njk))$/.test(template)) { - // the template is a filename - this.nunjucksEnvironment().render(template, templateData, function (err, res) { - if (err) { - Log.error(err) - } + // Check to see if we need to render a template string or a file. + if (/^.*((\.html)|(\.njk))$/.test(template)) { + // the template is a filename + this.nunjucksEnvironment().render(template, templateData, function (err, res) { + if (err) { + Log.error(err) + } - // The inner content of the div will be set after the template is received. - // This isn't the most optimal way, but since it's near instant - // it probably won't be an issue. - // If it gives problems, we can always add a way to pre fetch the templates. - // Let's not over optimise this ... KISS! :) - div.innerHTML = res; - }); - } else { - // the template is a template string. - div.innerHTML = this.nunjucksEnvironment().renderString(template, templateData); - } + div.innerHTML = res; - return div; + resolve(div); + }); + } else { + // the template is a template string. + div.innerHTML = this.nunjucksEnvironment().renderString(template, templateData); + + resolve(div); + } + }); }, /* getHeader() diff --git a/modules/README.md b/modules/README.md index 76973996..0b5c2d7c 100644 --- a/modules/README.md +++ b/modules/README.md @@ -230,11 +230,12 @@ notificationReceived: function(notification, payload, sender) { } ```` -**Note:** the system sends two notifications when starting up. These notifications could come in handy! +**Note:** the system sends three notifications when starting up. These notifications could come in handy! - `ALL_MODULES_STARTED` - All modules are started. You can now send notifications to other modules. - `DOM_OBJECTS_CREATED` - All dom objects are created. The system is now ready to perform visual changes. +- `MODULE_DOM_CREATED` - This module's dom has been fully loaded. You can now access your module's dom objects. #### `socketNotificationReceived: function(notification, payload)` diff --git a/package-lock.json b/package-lock.json index 0a68929a..639dbb6a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,9 +1,14 @@ { "name": "magicmirror", - "version": "2.2.2", + "version": "2.3.0-dev", "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/node": { + "version": "7.0.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.51.tgz", + "integrity": "sha512-h5u7FnEnG+Fn44HfknTTvu199FzFWVSo97ToSRWvXl1F11UfN6wGnE7exUy23pFfDn+CeluvEoCoe4l2eCVC3g==" + }, "JSV": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz", @@ -1335,10 +1340,11 @@ "dev": true }, "electron": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/electron/-/electron-1.4.15.tgz", - "integrity": "sha1-6syv4/Va3gKnRrcGrBS0PbbHzPg=", + "version": "1.7.10", + "resolved": "https://registry.npmjs.org/electron/-/electron-1.7.10.tgz", + "integrity": "sha1-Oj6D2WX9f6/kc76N349HJWG2JT0=", "requires": { + "@types/node": "7.0.51", "electron-download": "3.3.0", "extract-zip": "1.6.5" } @@ -1456,9 +1462,9 @@ } }, "es6-promise": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.1.1.tgz", - "integrity": "sha512-OaU1hHjgJf+b0NzsxCg7NdIYERD6Hy/PEmFLTjw+b65scuisG3Kt4QoTvJ66BBkPZ581gr0kpoVzKnxniM8nng==" + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.2.tgz", + "integrity": "sha512-LSas5vsuA6Q4nEdf9wokY5/AJYXry98i0IzXsv49rYsgDGDNDPbqAYR1Pe23iFxygfbGZNR/5VrHXBCh2BhvUQ==" }, "escape-html": { "version": "1.0.3", @@ -5823,7 +5829,7 @@ "integrity": "sha1-ebs7RFbdBPGOvbwNcDodHa7FEF0=", "requires": { "debug": "2.6.7", - "es6-promise": "4.1.1" + "es6-promise": "4.2.2" } }, "supports-color": { diff --git a/package.json b/package.json index 61fbcb61..4c42c44c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "magicmirror", - "version": "2.2.2", + "version": "2.3.0-dev", "description": "The open source modular smart mirror platform.", "main": "js/electron.js", "scripts": { @@ -54,7 +54,7 @@ "dependencies": { "body-parser": "^1.18.2", "colors": "^1.1.2", - "electron": "1.4.15", + "electron": "^1.7.10", "express": "^4.16.2", "express-ipfilter": "0.3.1", "feedme": "latest",