Advertisement
Guest User

Untitled

a guest
Jul 11th, 2017
126
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* global Module */
  2.  
  3. /* Magic Mirror
  4.  * Module: Calendar
  5.  *
  6.  * By Michael Teeuw http://michaelteeuw.nl
  7.  * MIT Licensed.
  8.  */
  9.  
  10. Module.register("calendar", {
  11.  
  12.   // Define module defaults
  13.   defaults: {
  14.     maximumEntries: 10, // Total Maximum Entries
  15.     maximumNumberOfDays: 365,
  16.     displaySymbol: true,
  17.     defaultSymbol: "calendar", // Fontawesome Symbol see http://fontawesome.io/cheatsheet/
  18.     displayRepeatingCountTitle: false,
  19.     defaultRepeatingCountTitle: "",
  20.     maxTitleLength: 25,
  21.     wrapEvents: false, // wrap events to multiple lines breaking at maxTitleLength
  22.     fetchInterval: 5 * 60 * 1000, // Update every 5 minutes.
  23.     animationSpeed: 2000,
  24.     fade: true,
  25.     urgency: 7,
  26.     timeFormat: "relative",
  27.     dateFormat: "MMM Do",
  28.     fullDayEventDateFormat: "MMM Do",
  29.     getRelative: 6,
  30.     fadePoint: 0.25, // Start on 1/4th of the list.
  31.     hidePrivate: false,
  32.     colored: false,
  33.     calendars: [{
  34.       symbol: "calendar",
  35.       url: "http://www.calendarlabs.com/templates/ical/US-Holidays.ics",
  36.     }, ],
  37.     titleReplace: {
  38.       "De verjaardag van ": "",
  39.       "'s birthday": ""
  40.     },
  41.     broadcastEvents: true,
  42.     excludedEvents: []
  43.   },
  44.  
  45.   // Define required scripts.
  46.   getStyles: function() {
  47.     return ["calendar.css", "font-awesome.css"];
  48.   },
  49.  
  50.   // Define required scripts.
  51.   getScripts: function() {
  52.     return ["moment.js"];
  53.   },
  54.  
  55.   // Define required translations.
  56.   getTranslations: function() {
  57.     // The translations for the default modules are defined in the core translation files.
  58.     // Therefor we can just return false. Otherwise we should have returned a dictionary.
  59.     // If you're trying to build your own module including translations, check out the documentation.
  60.     return false;
  61.   },
  62.  
  63.   // Override start method.
  64.   start: function() {
  65.     Log.log("Starting module: " + this.name);
  66.  
  67.     // Set locale.
  68.     moment.locale(config.language);
  69.  
  70.     switch (config.timeFormat) {
  71.       case 12:
  72.         {
  73.           moment.updateLocale(config.language, {
  74.             longDateFormat: {
  75.               LT: "h:mm A"
  76.             }
  77.           });
  78.           break;
  79.         }
  80.       case 24:
  81.         {
  82.           moment.updateLocale(config.language, {
  83.             longDateFormat: {
  84.               LT: "hh:mm"
  85.             }
  86.           });
  87.           break;
  88.         }
  89.         // If config.timeFormat was not given (or has invalid format) default to locale default
  90.       default:
  91.         {
  92.           break;
  93.         }
  94.     }
  95.  
  96.     for (var c in this.config.calendars) {
  97.       var calendar = this.config.calendars[c];
  98.       calendar.url = calendar.url.replace("webcal://", "http://");
  99.  
  100.       var calendarConfig = {
  101.         maximumEntries: calendar.maximumEntries,
  102.         maximumNumberOfDays: calendar.maximumNumberOfDays
  103.       };
  104.  
  105.       // we check user and password here for backwards compatibility with old configs
  106.       if (calendar.user && calendar.pass) {
  107.         calendar.auth = {
  108.           user: calendar.user,
  109.           pass: calendar.pass
  110.         }
  111.       }
  112.  
  113.       this.addCalendar(calendar.url, calendar.auth, calendarConfig);
  114.     }
  115.  
  116.     this.calendarData = {};
  117.     this.loaded = false;
  118.   },
  119.  
  120.   // Override socket notification handler.
  121.   socketNotificationReceived: function(notification, payload) {
  122.     if (notification === "CALENDAR_EVENTS") {
  123.       if (this.hasCalendarURL(payload.url)) {
  124.         this.calendarData[payload.url] = payload.events;
  125.         this.loaded = true;
  126.  
  127.         if (this.config.broadcastEvents) {
  128.           this.broadcastEvents();
  129.         }
  130.       }
  131.     } else if (notification === "FETCH_ERROR") {
  132.       Log.error("Calendar Error. Could not fetch calendar: " + payload.url);
  133.     } else if (notification === "INCORRECT_URL") {
  134.       Log.error("Calendar Error. Incorrect url: " + payload.url);
  135.     } else {
  136.       Log.log("Calendar received an unknown socket notification: " + notification);
  137.     }
  138.  
  139.     this.updateDom(this.config.animationSpeed);
  140.   },
  141.  
  142.   // Override dom generator.
  143.   getDom: function() {
  144.  
  145.     var events = this.createEventList();
  146.     var wrapper = document.createElement("table");
  147.     wrapper.className = "small";
  148.  
  149.     if (events.length === 0) {
  150.       wrapper.innerHTML = (this.loaded) ? this.translate("EMPTY") : this.translate("LOADING");
  151.       wrapper.className = "small dimmed";
  152.       return wrapper;
  153.     }
  154.  
  155.     for (var e in events) {
  156.       var event = events[e];
  157.  
  158.       var excluded = false;
  159.       for (var f in this.config.excludedEvents) {
  160.         var filter = this.config.excludedEvents[f];
  161.         if (event.title.toLowerCase().includes(filter.toLowerCase())) {
  162.           excluded = true;
  163.           break;
  164.         }
  165.       }
  166.  
  167.       if (excluded) {
  168.         continue;
  169.       }
  170.  
  171.       var eventWrapper = document.createElement("tr");
  172.  
  173.       if (this.config.colored) {
  174.         eventWrapper.style.cssText = "color:" + this.colorForUrl(event.url);
  175.       }
  176.  
  177.       eventWrapper.className = "normal";
  178.  
  179.       if (this.config.displaySymbol) {
  180.         var symbolWrapper = document.createElement("td");
  181.         symbolWrapper.className = "symbol align-right";
  182.         var symbols = this.symbolsForUrl(event.url);
  183.         if (typeof symbols === "string") {
  184.           symbols = [symbols];
  185.         }
  186.  
  187.         for (var i = 0; i < symbols.length; i++) {
  188.           var symbol = document.createElement("span");
  189.           symbol.className = "fa fa-" + symbols[i];
  190.           if (i > 0) {
  191.             symbol.style.paddingLeft = "5px";
  192.           }
  193.           symbolWrapper.appendChild(symbol);
  194.         }
  195.         eventWrapper.appendChild(symbolWrapper);
  196.       }
  197.  
  198.       var titleWrapper = document.createElement("td"),
  199.         repeatingCountTitle = "";
  200.  
  201.       if (this.config.displayRepeatingCountTitle) {
  202.  
  203.         repeatingCountTitle = this.countTitleForUrl(event.url);
  204.  
  205.         if (repeatingCountTitle !== "") {
  206.           var thisYear = new Date(parseInt(event.startDate)).getFullYear(),
  207.             yearDiff = thisYear - event.firstYear;
  208.  
  209.           repeatingCountTitle = ", " + yearDiff + ". " + repeatingCountTitle;
  210.         }
  211.       }
  212.  
  213.       titleWrapper.innerHTML = this.titleTransform(event.title) + repeatingCountTitle;
  214.  
  215.       if (!this.config.colored) {
  216.         titleWrapper.className = "title bright";
  217.       } else {
  218.         titleWrapper.className = "title";
  219.       }
  220.  
  221.       eventWrapper.appendChild(titleWrapper);
  222.  
  223.       var timeWrapper = document.createElement("td");
  224.       //console.log(event.today);
  225.       var now = new Date();
  226.       // Define second, minute, hour, and day variables
  227.       var oneSecond = 1000; // 1,000 milliseconds
  228.       var oneMinute = oneSecond * 60;
  229.       var oneHour = oneMinute * 60;
  230.       var oneDay = oneHour * 24;
  231.       if (event.fullDayEvent) {
  232.         if (event.today) {
  233.           timeWrapper.innerHTML = this.capFirst(this.translate("TODAY"));
  234.         } else if (event.startDate - now < oneDay && event.startDate - now > 0) {
  235.           timeWrapper.innerHTML = this.capFirst(this.translate("TOMORROW"));
  236.         } else if (event.startDate - now < 2 * oneDay && event.startDate - now > 0) {
  237.           if (this.translate("DAYAFTERTOMORROW") !== "DAYAFTERTOMORROW") {
  238.             timeWrapper.innerHTML = this.capFirst(this.translate("DAYAFTERTOMORROW"));
  239.           } else {
  240.             timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
  241.           }
  242.         } else {
  243.           /* Check to see if the user displays absolute or relative dates with their events
  244.            * Also check to see if an event is happening within an 'urgency' time frameElement
  245.            * For example, if the user set an .urgency of 7 days, those events that fall within that
  246.            * time frame will be displayed with 'in xxx' time format or moment.fromNow()
  247.            *
  248.            * Note: this needs to be put in its own function, as the whole thing repeats again verbatim
  249.            */
  250.           if (this.config.timeFormat === "absolute") {
  251.             if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * oneDay))) {
  252.               // This event falls within the config.urgency period that the user has set
  253.               timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
  254.             } else {
  255.               timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.fullDayEventDateFormat));
  256.             }
  257.           } else {
  258.             timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
  259.           }
  260.         }
  261.       } else {
  262.         if (event.startDate >= new Date()) {
  263.           if (event.startDate - now < 2 * oneDay) {
  264.             // This event is within the next 48 hours (2 days)
  265.             if (event.startDate - now < this.config.getRelative * oneHour) {
  266.               // If event is within 6 hour, display 'in xxx' time format or moment.fromNow()
  267.               timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
  268.             } else {
  269.               // Otherwise just say 'Today/Tomorrow at such-n-such time'
  270.               timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").calendar());
  271.             }
  272.           } else {
  273.             /* Check to see if the user displays absolute or relative dates with their events
  274.              * Also check to see if an event is happening within an 'urgency' time frameElement
  275.              * For example, if the user set an .urgency of 7 days, those events that fall within that
  276.              * time frame will be displayed with 'in xxx' time format or moment.fromNow()
  277.              *
  278.              * Note: this needs to be put in its own function, as the whole thing repeats again verbatim
  279.              */
  280.             if (this.config.timeFormat === "absolute") {
  281.               if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * oneDay))) {
  282.                 // This event falls within the config.urgency period that the user has set
  283.                 timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
  284.               } else {
  285.                 timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.dateFormat));
  286.               }
  287.             } else {
  288.               timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
  289.             }
  290.           }
  291.         } else {
  292.           timeWrapper.innerHTML = this.capFirst(
  293.             this.translate("RUNNING", {
  294.               fallback: this.translate("RUNNING") + " {timeUntilEnd}",
  295.               timeUntilEnd: moment(event.endDate, "x").fromNow(true)
  296.             })
  297.           );
  298.         }
  299.       }
  300.       //timeWrapper.innerHTML += ' - '+ moment(event.startDate,'x').format('lll');
  301.       //console.log(event);
  302.       timeWrapper.className = "time light";
  303.       eventWrapper.appendChild(timeWrapper);
  304.  
  305.       wrapper.appendChild(eventWrapper);
  306.  
  307.       // Create fade effect.
  308.       if (this.config.fade && this.config.fadePoint < 1) {
  309.         if (this.config.fadePoint < 0) {
  310.           this.config.fadePoint = 0;
  311.         }
  312.         var startingPoint = events.length * this.config.fadePoint;
  313.         var steps = events.length - startingPoint;
  314.         if (e >= startingPoint) {
  315.           var currentStep = e - startingPoint;
  316.           eventWrapper.style.opacity = 1 - (1 / steps * currentStep);
  317.         }
  318.       }
  319.     }
  320.  
  321.     return wrapper;
  322.   },
  323.  
  324.   /* hasCalendarURL(url)
  325.    * Check if this config contains the calendar url.
  326.    *
  327.    * argument url string - Url to look for.
  328.    *
  329.    * return bool - Has calendar url
  330.    */
  331.   hasCalendarURL: function(url) {
  332.     for (var c in this.config.calendars) {
  333.       var calendar = this.config.calendars[c];
  334.       if (calendar.url === url) {
  335.         return true;
  336.       }
  337.     }
  338.  
  339.     return false;
  340.   },
  341.  
  342.   /* createEventList()
  343.    * Creates the sorted list of all events.
  344.    *
  345.    * return array - Array with events.
  346.    */
  347.   createEventList: function() {
  348.     var events = [];
  349.     var today = moment().startOf("day");
  350.     for (var c in this.calendarData) {
  351.       var calendar = this.calendarData[c];
  352.       for (var e in calendar) {
  353.         var event = calendar[e];
  354.         if (this.config.hidePrivate) {
  355.           if (event.class === "PRIVATE") {
  356.             // do not add the current event, skip it
  357.             continue;
  358.           }
  359.         }
  360.         event.url = c;
  361.         event.today = event.startDate >= today && event.startDate < (today + 24 * 60 * 60 * 1000);
  362.         events.push(event);
  363.       }
  364.     }
  365.  
  366.  
  367.     events = events.filter(function(a) {
  368.       return a.categories.indexOf('zur Information') != -1;
  369.     });
  370.     events.sort(function(a, b) {
  371.  
  372.       return a.startDate - b.startDate;
  373.     });
  374.  
  375.     return events.slice(0, this.config.maximumEntries);
  376.   },
  377.  
  378.   /* createEventList(url)
  379.    * Requests node helper to add calendar url.
  380.    *
  381.    * argument url string - Url to add.
  382.    */
  383.   addCalendar: function(url, auth, calendarConfig) {
  384.     this.sendSocketNotification("ADD_CALENDAR", {
  385.       url: url,
  386.       maximumEntries: calendarConfig.maximumEntries || this.config.maximumEntries,
  387.       maximumNumberOfDays: calendarConfig.maximumNumberOfDays || this.config.maximumNumberOfDays,
  388.       fetchInterval: this.config.fetchInterval,
  389.       auth: auth
  390.     });
  391.   },
  392.  
  393.   /* symbolsForUrl(url)
  394.    * Retrieves the symbols for a specific url.
  395.    *
  396.    * argument url string - Url to look for.
  397.    *
  398.    * return string/array - The Symbols
  399.    */
  400.   symbolsForUrl: function(url) {
  401.     return this.getCalendarProperty(url, "symbol", this.config.defaultSymbol);
  402.   },
  403.  
  404.   /* colorForUrl(url)
  405.    * Retrieves the color for a specific url.
  406.    *
  407.    * argument url string - Url to look for.
  408.    *
  409.    * return string - The Color
  410.    */
  411.   colorForUrl: function(url) {
  412.     return this.getCalendarProperty(url, "color", "#fff");
  413.   },
  414.  
  415.   /* countTitleForUrl(url)
  416.    * Retrieves the name for a specific url.
  417.    *
  418.    * argument url string - Url to look for.
  419.    *
  420.    * return string - The Symbol
  421.    */
  422.   countTitleForUrl: function(url) {
  423.     return this.getCalendarProperty(url, "repeatingCountTitle", this.config.defaultRepeatingCountTitle);
  424.   },
  425.  
  426.   /* getCalendarProperty(url, property, defaultValue)
  427.    * Helper method to retrieve the property for a specific url.
  428.    *
  429.    * argument url string - Url to look for.
  430.    * argument property string - Property to look for.
  431.    * argument defaultValue string - Value if property is not found.
  432.    *
  433.    * return string - The Property
  434.    */
  435.   getCalendarProperty: function(url, property, defaultValue) {
  436.     for (var c in this.config.calendars) {
  437.       var calendar = this.config.calendars[c];
  438.       if (calendar.url === url && calendar.hasOwnProperty(property)) {
  439.         return calendar[property];
  440.       }
  441.     }
  442.  
  443.     return defaultValue;
  444.   },
  445.  
  446.   /* shorten(string, maxLength)
  447.    * Shortens a string if it's longer than maxLength.
  448.    * Adds an ellipsis to the end.
  449.    *
  450.    * argument string string - The string to shorten.
  451.    * argument maxLength number - The max length of the string.
  452.    * argument wrapEvents - Wrap the text after the line has reached maxLength
  453.    *
  454.    * return string - The shortened string.
  455.    */
  456.   shorten: function(string, maxLength, wrapEvents) {
  457.     if (wrapEvents) {
  458.       var temp = "";
  459.       var currentLine = "";
  460.       var words = string.split(" ");
  461.  
  462.       for (var i = 0; i < words.length; i++) {
  463.         var word = words[i];
  464.         if (currentLine.length + word.length < 25 - 1) { // max - 1 to account for a space
  465.           currentLine += (word + " ");
  466.         } else {
  467.           if (currentLine.length > 0) {
  468.             temp += (currentLine + "<br>" + word + " ");
  469.           } else {
  470.             temp += (word + "<br>");
  471.           }
  472.           currentLine = "";
  473.         }
  474.       }
  475.  
  476.       return temp + currentLine;
  477.     } else {
  478.       if (string.length > maxLength) {
  479.         return string.slice(0, maxLength) + "&hellip;";
  480.       } else {
  481.         return string;
  482.       }
  483.     }
  484.   },
  485.  
  486.   /* capFirst(string)
  487.    * Capitalize the first letter of a string
  488.    * Return capitalized string
  489.    */
  490.  
  491.   capFirst: function(string) {
  492.     return string.charAt(0).toUpperCase() + string.slice(1);
  493.   },
  494.  
  495.   /* titleTransform(title)
  496.    * Transforms the title of an event for usage.
  497.    * Replaces parts of the text as defined in config.titleReplace.
  498.    * Shortens title based on config.maxTitleLength and config.wrapEvents
  499.    *
  500.    * argument title string - The title to transform.
  501.    *
  502.    * return string - The transformed title.
  503.    */
  504.   titleTransform: function(title) {
  505.     for (var needle in this.config.titleReplace) {
  506.       var replacement = this.config.titleReplace[needle];
  507.  
  508.       var regParts = needle.match(/^\/(.+)\/([gim]*)$/);
  509.       if (regParts) {
  510.         // the parsed pattern is a regexp.
  511.         needle = new RegExp(regParts[1], regParts[2]);
  512.       }
  513.  
  514.       title = title.replace(needle, replacement);
  515.     }
  516.  
  517.     title = this.shorten(title, this.config.maxTitleLength, this.config.wrapEvents);
  518.     return title;
  519.   },
  520.  
  521.   /* broadcastEvents()
  522.    * Broadcasts the events to all other modules for reuse.
  523.    * The all events available in one array, sorted on startdate.
  524.    */
  525.   broadcastEvents: function() {
  526.     var eventList = [];
  527.     for (var url in this.calendarData) {
  528.       var calendar = this.calendarData[url];
  529.       for (var e in calendar) {
  530.         var event = cloneObject(calendar[e]);
  531.         delete event.url;
  532.         eventList.push(event);
  533.  
  534.       }
  535.     }
  536.  
  537.         console.log('list before filter has ' + events.length + ' events');
  538.  
  539.     eventList = eventList.filter(function(a) {
  540.       var reference = ('zur Information');
  541.       return a.categories.indexOf(reference) != -1;
  542.     });
  543.         console.log('list after filter has ' + events.length + ' events');
  544.     eventList.sort(function(a, b) {
  545.       return a.startDate - b.startDate;
  546.     });
  547.  
  548.         this.sendNotification("CALENDAR_EVENTS", eventList);
  549.  
  550.        }
  551.      });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement