diff --git a/DailyXKCD.js b/DailyXKCD.js index cd2fd33..aacd390 100644 --- a/DailyXKCD.js +++ b/DailyXKCD.js @@ -2,10 +2,17 @@ Module.register("DailyXKCD", { // Default module config. defaults: { - dailyJsonUrl: "http://xkcd.com/info.0.json", - updateInterval: 10000 * 60 * 60, // 10 hours - invertColors: false - + dailyJsonUrl : "http://xkcd.com/info.0.json", + updateInterval : 10000 * 60 * 60, // 10 hours + invertColors : false, + titleFont : "bright large light", + altTextFont : "xsmall dimmed", + limitComicHeight : 450, + scrollInterval : 8000, // 8 seconds, + scrollRatio : 0.8, // scroll by 80% of visible height, + randomComic : false, + showAltText : false, + showTitle : true }, start: function() { @@ -16,7 +23,28 @@ Module.register("DailyXKCD", { this.dailyComicTitle = ""; this.dailyComicAlt = ""; + this.autoIntervals = []; + this.getComic(); + + if (this.config.scrollInterval < 3000) { + // animation takes 3 seconds + this.config.scrollInterval = 3000; + } + + // value should be between 0.0 and 1.0 + this.config.scrollRatio = Math.max(this.config.scrollRatio, 0.0); + this.config.scrollRatio = Math.min(this.config.scrollRatio, 1.0); + + if (this.config.limitComicHeight > 0) + { + var self = this; + // scroll comic up and down + this.addAutoSuspendingInterval(function() { + self.scrollComic(); + }, this.config.scrollInterval); + this.scrollProgress = 0; + } }, // Define required scripts. @@ -24,6 +52,11 @@ Module.register("DailyXKCD", { return ["moment.js"]; }, + // Define required styles. + getStyles: function() { + return ["xkcd.css"]; + }, + getComic: function() { Log.info("XKCD: Getting comic."); @@ -33,7 +66,6 @@ Module.register("DailyXKCD", { }, socketNotificationReceived: function(notification, payload) { - if (notification === "COMIC") { Log.info(payload.img); this.dailyComic = payload.img; @@ -41,7 +73,10 @@ Module.register("DailyXKCD", { this.dailyComicAlt = payload.alt; this.scheduleUpdate(); } + }, + notificationReceived: function(notification, payload, sender) { + this.checkUserPresence(notification, payload, sender); }, // Override dom generator. @@ -49,33 +84,134 @@ Module.register("DailyXKCD", { var wrapper = document.createElement("div"); var title = document.createElement("div"); - title.className = "bright large light"; + title.className = this.config.titleFont; title.innerHTML = this.dailyComicTitle; + if (this.config.showTitle) { + wrapper.appendChild(title); + } + var comicWrapper = document.createElement("div"); + comicWrapper.className = "xkcdcontainer"; + if (this.config.limitComicHeight > 0) + { + comicWrapper.style.maxHeight = this.config.limitComicHeight + "px"; + } var xkcd = document.createElement("img"); + xkcd.id = "xkcdcontent"; xkcd.src = this.dailyComic; - if (this.config.invertColors) { + if(this.config.invertColors){ xkcd.setAttribute("style", "-webkit-filter: invert(100%);") } + comicWrapper.appendChild(xkcd); + wrapper.appendChild(comicWrapper); - if (this.config.title) wrapper.appendChild(title); - - wrapper.appendChild(xkcd); - - if (this.config.altText) { + if (this.config.showAltText) { var alt = document.createElement("div"); - alt.className = "bright medium light"; + alt.className = this.config.altTextFont; alt.innerHTML = this.dailyComicAlt; wrapper.appendChild(alt); } - return wrapper; }, + /* suspend() + * This method is called when a module is hidden. + */ + suspend: function() { + this.scrollProgress = 0; + + // reset to beginning if module is suspended, so we start at the top + var scrollable = document.getElementById("xkcdcontent"); + scrollable.style.top = "0px"; + + for (var i = 0; i < this.autoIntervals.length; i++) + { + var current = this.autoIntervals[i]; + + if (current.interval) + { + clearInterval(current.interval); + + current.interval = null; + } + } + }, + + /* resume() + * This method is called when a module is shown. + */ + resume: function() { + for (var i = 0; i < this.autoIntervals.length; i++) + { + var current = this.autoIntervals[i]; + + if (!current.interval) + { + current.callback(); + + current.interval = setInterval(current.callback, current.time); + } + } + }, + + /* scrollComic + * Scrolls the comic down if needed + */ + scrollComic: function() { + var scrollable = document.getElementById("xkcdcontent"); + + var height = scrollable.naturalHeight; + + var top = 0; + if (this.config.limitComicHeight > 0 && height > this.config.limitComicHeight) + { + var currentHeight = this.scrollProgress * -this.config.limitComicHeight * this.config.scrollRatio; + var maxHeight = this.config.limitComicHeight - height; + top = Math.max(currentHeight, maxHeight); + } + scrollable.style.top = top + "px"; + scrollable.style.height = height + "px"; + if (top == this.config.limitComicHeight - height) + { + this.scrollProgress = -1; + } + + this.scrollProgress += 1; + }, + + /* checkUserPresence(notification, payload, sender) + * Use this method to conveniently suspend your module when no user is present. + */ + checkUserPresence: function(notification, payload, sender) { + if (sender && notification === "USER_PRESENCE") { + if (payload === true) + { + this.resume(); + } + else + { + this.suspend(); + } + } + }, + + /* addAutoSuspendingInterval(callback, time) + * Use instead of setInterval for automatic pause when on suspend. + * The callback is executed immediately once after the user returns. + */ + addAutoSuspendingInterval: function(callback, time) { + var newInterval = setInterval(callback, time); + this.autoIntervals.push({ + callback: callback, + interval: newInterval, + time: time + }); + }, + scheduleUpdate: function() { var self = this; diff --git a/README.md b/README.md index 24698e6..4545d1a 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,8 @@ A module for MagicMirror2 that displays the daily XKCD web comic. module: 'DailyXKCD', position: 'top_left', config: { - invertColors: true - title: true + invertColors: true, + title: true, altText: false } }, @@ -25,5 +25,12 @@ A module for MagicMirror2 that displays the daily XKCD web comic. | **Option** | **Description** | | --- | --- | | `invertColors` | Set to `true` to invert the colors of the comic to white on black for a darker feel. | -| `title` | Set to `true` to display the title of the comic. | -| `altText` | Set to `true` to display the alternate text of the comic. | +| `updateInterval` | Set to desired update interval (in ms), default is `3600000` (10 hours). | +| `showTitle` | Set to `true` to display the title of the comic. | +| `titleFont` | Set a custom font format, default is `large light bright`. To set the size use one of `xsmall small medium large xlarge`, for boldness one of `thin light regular bold`, and to adjust brightness one of `dimmed normal bright`. | +| `showAltText` | Set to `true` to show the alt text (tooltip on the original comic). | +| `altTextFont` | See `titleFont`, except for this is the formatting of the alt text. | +| `randomComic` | Set to `true`, if you want to see a random comic on days, when there is no new comic. | +| `limitComicHeight` | Set to limit the height of the comic (in px), default is `450`. The comic will scroll downwards every few seconds, if it is heigher. | +| `scrollInterval` | How often to scroll long comics (in ms), default is `8000` (every 8 seconds). | +| `scrollRatio` | Set how much of the visible height is being scrolled every time. The value should be between `0.0` and `1.0`, default is `0.8` so it scrolls down by 80%. | diff --git a/node_helper.js b/node_helper.js index 042b38c..d72bf58 100644 --- a/node_helper.js +++ b/node_helper.js @@ -12,20 +12,37 @@ module.exports = NodeHelper.create({ var self = this; console.log("Notification: " + notification + " Payload: " + payload); - if(notification === "GET_COMIC"){ + if(notification === "GET_COMIC") { var comicJsonUri = payload.config.dailyJsonUrl; + + var date = new Date(); + var dayOfWeek = date.getDay(); request(comicJsonUri, function (error, response, body) { if (!error && response.statusCode == 200) { - console.log(body); - self.sendSocketNotification("COMIC", JSON.parse(body)); - console.log(JSON.parse(body).img); - + if (!payload.config.randomComic) { + // if we are not replacing "old" comics with random ones + self.sendSocketNotification("COMIC", JSON.parse(body)); + return; + } + + // otherwise select a random comic based on day of week + if (dayOfWeek == 1 || dayOfWeek == 3 || dayOfWeek == 5) { + self.sendSocketNotification("COMIC", JSON.parse(body)); + } else { + var comic = JSON.parse(body); + var randomNumber = Math.floor((Math.random() * comic.num) + 1); + // use "randomNumber = 1732;" to test with long comic + var randomUrl = "http://xkcd.com/" + randomNumber + "/info.0.json"; + request(randomUrl, function (error, response, body) { + if (!error && response.statusCode == 200) { + self.sendSocketNotification("COMIC", JSON.parse(body)); + } + }); + } } }); - } - }, }); diff --git a/xkcd.css b/xkcd.css new file mode 100644 index 0000000..33ed2f4 --- /dev/null +++ b/xkcd.css @@ -0,0 +1,22 @@ +/* The basic container */ +.xkcdcontainer { + overflow: hidden; + + /* To make the height of the container exact. */ + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +#xkcdcontent { + white-space: nowrap; + position: relative; + overflow: hidden; /* Required to make ellipsis work */ + text-overflow: clip; + + top: 0%; + + -webkit-transition: top 3s, height 3s; + -moz-transition: top 3s, height 3s; + transition: top 3s, height 3s; +}