diff --git a/README.md b/README.md index 33077642..1d79c219 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,17 @@ The following wiki links are helpful in the configuration of your MagicMirror² - [Configuring the Raspberry Pi](https://github.com/MichMich/MagicMirror/wiki/Configuring-the-Raspberry-Pi) - [Auto Starting MagicMirror](https://github.com/MichMich/MagicMirror/wiki/Auto-Starting-MagicMirror) +#### Updating you MagicMirror² + +If you want to update your MagicMirror² to the latest version, use your terminal to go to your Magic Mirror folder and type the following command: + +```` +git pull +```` + +If you changed nothing more than the config or the modules, this should work without any problems. +Type `git status` to see your changes, if there are any, you can reset them with `git reset --hard`. After that, git pull should be possible. + ## Configuration 1. Duplicate `config/config.js.sample` to `config/config.js`. diff --git a/index.html b/index.html index a6d7265c..d08291cc 100644 --- a/index.html +++ b/index.html @@ -33,6 +33,7 @@ + diff --git a/installers/raspberry.sh b/installers/raspberry.sh index aa9e6410..4f1d2e2c 100644 --- a/installers/raspberry.sh +++ b/installers/raspberry.sh @@ -18,7 +18,7 @@ echo "Installing helper tools ..." sudo apt-get install curl wget build-essential unzip || exit ARM=$(uname -m) # Determine which Pi is running. -NODE_LATEST="v5.10.1" # Set the latest version here. +NODE_LATEST="v5.11.0" # Set the latest version here. DOWNLOAD_URL="https://nodejs.org/dist/latest/node-$NODE_LATEST-linux-$ARM.tar.gz" # Construct the download URL. echo "Installing Latest Node.js ..." diff --git a/js/loader.js b/js/loader.js index 2553a187..2dbd40b2 100644 --- a/js/loader.js +++ b/js/loader.js @@ -144,9 +144,11 @@ var Loader = (function() { Log.log("Scripts loaded for: " + module.name); mObj.loadStyles(function() { Log.log("Styles loaded for: " + module.name); - - moduleObjects.push(mObj); - callback(); + mObj.loadTranslations(function() { + Log.log("Translations loaded for: " + module.name); + moduleObjects.push(mObj); + callback(); + }); }); }); diff --git a/js/module.js b/js/module.js index 9be652bf..f0b0103d 100644 --- a/js/module.js +++ b/js/module.js @@ -52,6 +52,15 @@ var Module = Class.extend({ return []; }, + /* getTranslations() + * Returns a map of translation files the module requires to be loaded. + * + * return Map - A map with langKeys and filenames. + */ + getTranslations: function() { + return {}; + }, + /* getDom() * This method generates the dom which needs to be displayed. This method is called by the Magic Mirror core. * This method needs to be subclassed if the module wants to display info on the mirror. @@ -146,7 +155,7 @@ var Module = Class.extend({ }, /* file(file) - * Retrieve the path to a module fike. + * Retrieve the path to a module file. * * argument file string - Filename. * @@ -204,6 +213,32 @@ var Module = Class.extend({ loadNextScript(); }, + /* loadScripts() + * Load all required scripts by requesting the MM object to load the files. + * + * argument callback function - Function called when done. + */ + loadTranslations: function(callback) { + var self = this; + var translations = this.getTranslations(); + var translationFile = translations && (translations[config.language.toLowerCase()] || translations.en) || undefined; + if(translationFile) { + Translator.load(this, translationFile, callback); + } else { + callback(); + } + }, + + /* translate(key, defaultValue) + * Request the translation for a given key. + * + * argument key string - The key of the string to translage + * argument defaultValue string - The default value if no translation was found. (Optional) + */ + translate: function(key, defaultValue) { + return Translator.translate(this, key) || defaultValue || ''; + }, + /* updateDom(speed) * Request an (animated) update of the module. * diff --git a/js/translator.js b/js/translator.js new file mode 100644 index 00000000..3ef18ed9 --- /dev/null +++ b/js/translator.js @@ -0,0 +1,57 @@ +/* exported Translator */ +/* Magic Mirror + * Translator (l10n) + * + * By Christopher Fenner http://github.com/CFenner + * MIT Licensed. + */ +var Translator = (function() { + return { + translations: {}, + /* translate(module, key) + * Load a translation for a given key for a given module. + * + * argument module Module - The module to load the translation for. + * argument key string - The key of the text to translate. + */ + translate: function(module, key) { + if(this.translations[module.name]) { + return this.translations[module.name][key]; + } + return undefined; + }, + /* load(module, file, callback) + * Load a translation file (json) and remember the data. + * + * argument module Module - The module to load the translation file for. + * argument file string - Path of the file we want to load. + * argument callback function - Function called when done. + */ + load: function(module, file, callback) { + var self = this; + if(!this.translations[module.name]) { + this._loadJSON(module.file(file), function(json) { + self.translations[module.name] = json; + callback(); + }); + } + }, + /* _loadJSON(file, callback) + * Load a JSON file via XHR. + * + * argument file string - Path of the file we want to load. + * argument callback function - Function called when done. + */ + _loadJSON: function(file, callback) { + var xhr = new XMLHttpRequest(); + xhr.overrideMimeType("application/json"); + xhr.open('GET', file, true); + xhr.onreadystatechange = function () { + if (xhr.readyState == 4 && xhr.status == "200") { + callback(JSON.parse(xhr.responseText)); + } + }; + xhr.send(null); + } + }; +})(); diff --git a/modules/default/calendar/README.md b/modules/default/calendar/README.md index 92574415..df0bb0d2 100644 --- a/modules/default/calendar/README.md +++ b/modules/default/calendar/README.md @@ -126,10 +126,16 @@ The following properties can be configured: todayText - Text to display when an event is planned for today.
+ Text to display when a fullday event is planned for today.

Default value: 'Today' + + tomorrowText + Text to display when a fullday event is planned for tomorrow.
+
Default value: 'Tomorrow' + + runningText Text to display when an event is still running.
diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index 0b5627ff..c1ffc492 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -29,12 +29,6 @@ Module.register("calendar",{ titleReplace: { "De verjaardag van ": "" }, - loadingText: "Loading events …", - emptyCalendarText: "No upcoming events.", - - // TODO: It would be nice if there is a way to get this from the Moment.js locale. - todayText: "Today", - runningText: "Ends in" }, // Define required scripts. @@ -47,6 +41,15 @@ Module.register("calendar",{ return ["moment.js"]; }, + // Define required translations. + getTranslations: function() { + return { + en: "translations/en.json", + de: "translations/de.json", + nl: "translations/nl.json" + }; + }, + // Override start method. start: function() { Log.log("Starting module: " + this.name); @@ -90,7 +93,7 @@ Module.register("calendar",{ wrapper.className = "small"; if (events.length === 0) { - wrapper.innerHTML = (this.loaded) ? this.config.emptyCalendarText : this.config.loadingText; + wrapper.innerHTML = (this.loaded) ? this.translate("EMPTY") : this.translate("LOADING"); wrapper.className = "small dimmed"; return wrapper; } @@ -117,11 +120,17 @@ Module.register("calendar",{ var timeWrapper = document.createElement("td"); //console.log(event.today); + var now = new Date(); if (event.fullDayEvent) { - timeWrapper.innerHTML = (event.today) ? this.config.todayText : moment(event.startDate,"x").fromNow(); + if (event.today) { + timeWrapper.innerHTML = this.translate("TODAY"); + } else if (event.startDate - now < 24 * 60 * 60 * 1000) { + timeWrapper.innerHTML = this.translate("TOMORROW"); + } else { + timeWrapper.innerHTML = moment(event.startDate,"x").fromNow(); + } } else { if (event.startDate >= new Date()) { - var now = new Date(); if (event.startDate - now > 48 * 60 * 60 * 1000) { // if the event is no longer than 2 days away, display the absolute time. timeWrapper.innerHTML = moment(event.startDate,"x").fromNow(); @@ -129,7 +138,7 @@ Module.register("calendar",{ timeWrapper.innerHTML = moment(event.startDate,"x").calendar(); } } else { - timeWrapper.innerHTML = this.config.runningText + ' ' + moment(event.endDate,"x").fromNow(true); + timeWrapper.innerHTML = this.translate("RUNNING") + ' ' + moment(event.endDate,"x").fromNow(true); } } // timeWrapper.innerHTML = moment(event.startDate,'x').format('lll'); diff --git a/modules/default/calendar/translations/de.json b/modules/default/calendar/translations/de.json new file mode 100644 index 00000000..53530085 --- /dev/null +++ b/modules/default/calendar/translations/de.json @@ -0,0 +1,7 @@ +{ + "TODAY": "Heute" + , "TOMORROW": "Morgen" + , "RUNNING": "Endet in" + , "LOADING": "Lade Termine …" + , "EMPTY": "Keine Termine." +} diff --git a/modules/default/calendar/translations/en.json b/modules/default/calendar/translations/en.json new file mode 100644 index 00000000..4dcf3a62 --- /dev/null +++ b/modules/default/calendar/translations/en.json @@ -0,0 +1,7 @@ +{ + "TODAY": "Today" + , "TOMORROW": "Tomorrow" + , "RUNNING": "Ends in" + , "LOADING": "Loading events …" + , "EMPTY": "No upcoming events." +} diff --git a/modules/default/calendar/translations/nl.json b/modules/default/calendar/translations/nl.json new file mode 100644 index 00000000..547f3845 --- /dev/null +++ b/modules/default/calendar/translations/nl.json @@ -0,0 +1,7 @@ +{ + "TODAY": "Vandaag" + , "TOMORROW": "Morgen" + , "RUNNING": "Eindigd over" + , "LOADING": "Bezig met laden …" + , "EMPTY": "Geen geplande afspraken." +} diff --git a/package.json b/package.json index 70970ec0..e815b2f1 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "snyk": "^1.13.2", "socket.io": "latest", "valid-url": "latest", - "walk": "latest" + "walk": "latest", + "rrule": "latest" } }