text: (function () {
    'use strict';
  
    function CalendarService($window, $http, $q) {
      var service = {};
  
      service.events = [];
  
      service.getCalendarEvents = function () {
        var deferred = $q.defer();
  
        service.events = [];
        if (typeof config.calendar != 'undefined' && typeof config.calendar.icals != 'undefined') {
          loadFile(config.calendar.icals).then(function () {
            deferred.resolve();
          });
        } else {
          deferred.reject("No iCals defined");
        }
  
        return deferred.promise;
      }
  
      var loadFile = function (urls) {
        var promises = [];
  
        angular.forEach(urls, function (url) {
          promises.push($http.get(url));
        });
  
        return $q.all(promises).then(function (data) {
          for (var i = 0; i < promises.length; i++) {
            parseICAL(data[i].data);
          }
        });
      }
  
      var makeDate = function (type, ical_date) {
        if (ical_date.endsWith('Z')) {
          return moment(ical_date, 'YYYYMMDDTHHmmssZ');
        }
  
        if (!type.endsWith('VALUE=DATE')) {
          return moment(ical_date, 'YYYYMMDDTHHmmss');
        } else {
          return moment(ical_date, 'YYYYMMDD');
        }
      }
  
      var parseICAL = function (data) {
        //Ensure cal is empty
        var events = [];
  
        //Clean string and split the file so we can handle it (line by line)
        var cal_array = data.replace(new RegExp("\\r", "g"), "").replace(/\n /g, "").split("\n");
  
        //Keep track of when we are activly parsing an event
        var in_event = false;
        //Use as a holder for the current event being proccessed.
        var cur_event = null;
        for (var i = 0; i < cal_array.length; i++) {
          var ln = cal_array[i];
  
          // Extract calendar name
          if (ln.startsWith('X-WR-CALNAME')) {
            var calendarName = ln.split(':')[1];
          }
  
          //If we encounted a new Event, create a blank event object + set in event options.
          if (!in_event && ln == 'BEGIN:VEVENT') {
            in_event = true;
            cur_event = {};
          }
          //If we encounter end event, complete the object and add it to our events array then clear it for reuse.
          if (in_event && ln == 'END:VEVENT') {
            in_event = false;
            cur_event.calendarName = calendarName;
            if (!contains(events, cur_event)) {
              events.push(cur_event);
            }
            cur_event = null;
          }
          //If we are in an event
          else if (in_event) {
            //var lntrim = ln.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
            //var lnsplit = lntrim.split(':');
            //type = lnsplit[0];
            //val = lnsplit[1];
  
            //Split the item based on the first ":"
            var idx = ln.indexOf(':');
            //Apply trimming to values to reduce risks of badly formatted ical files.
            var type = ln.substr(0, idx).replace(/^\s\s*/, '').replace(/\s\s*$/, ''); //Trim
            var val = ln.substr(idx + 1).replace(/^\s\s*/, '').replace(/\s\s*$/, '');
  
            //If the type is a start date, proccess it and store details
            if (type.startsWith('DTSTART')) {
              cur_event.start = makeDate(type, val);
              cur_event.startName = makeDate(type, val).calendar().toUpperCase();
            }
  
            //If the type is an end date, do the same as above
            else if (type.startsWith('DTEND')) {
              cur_event.end = makeDate(type, val);
  
              // Subtract one second so that single-day events endon the same day
              cur_event.endName = makeDate(type, val).subtract(1, 'seconds').calendar().toUpperCase();
            }
  
            if (cur_event.startName && cur_event.endName) {
              cur_event.label = cur_event.startName + " - " + cur_event.endName;
            }
  
            //Convert timestamp
            else if (type == 'DTSTAMP') {
              //val = makeDate(type, val);
            } else {
              val = val
                .replace(/\\r\\n/g, '<br />')
                .replace(/\\n/g, '<br />')
                .replace(/\\,/g, ',');
            }
  
            //Add the value to our event object.
            if (type !== 'SUMMARY' || (type == 'SUMMARY' && cur_event['SUMMARY'] == undefined)) {
              cur_event[type] = val;
            }
            var keys = Object.keys(cur_event);
            if (cur_event['SUMMARY'] !== undefined && cur_event['RRULE'] !== undefined &&
              (keys.some(function (k) { return ~k.indexOf("DTSTART") })) &&
              keys.some(function (k) { return ~k.indexOf("DTEND") })) {
              var options = new RRule.parseString(cur_event['RRULE']);
              options.dtstart = cur_event.start.toDate();
              var event_duration = cur_event.end.diff(cur_event.start, 'minutes');
              var rule = new RRule(options);
              var oneYear = new Date();
              oneYear.setFullYear(oneYear.getFullYear() + 1);
              var dates = rule.between(new Date(), oneYear, true, function (date, i) { return i < 10 });
              for (var date in dates) {
                var recuring_event = {};
                recuring_event.SUMMARY = cur_event.SUMMARY;
                var dt = new Date(dates[date]);
                var startDate = moment(dt);
                var endDate = moment(dt);
                endDate.add(event_duration, 'minutes');
                recuring_event.calendarName = calendarName;
                recuring_event.start = startDate;
                recuring_event.startName = startDate.calendar().toUpperCase();
                recuring_event.end = endDate;
                recuring_event.endName = endDate.subtract(1, 'seconds').calendar().toUpperCase();
  
                if (recuring_event.startName && recuring_event.endName) {
                  if (recuring_event.startName == recuring_event.endName) {
                    recuring_event.label = recuring_event.startName
                  } else {
                    recuring_event.label = recuring_event.startName + " - " + recuring_event.endName;
                  }
                }
  
                if (!contains(events, recuring_event)) {
                  events.push(recuring_event);
                }
              }
            }
          }
        }
        //Add all of the extracted events to the CalendarService
        service.events.push.apply(service.events, events);
      }
  
      var contains = function (input, obj) {
        var i = input.length;
        while (i--) {
          var current = input[i];
          if (obj.start.isValid()) {
            if (current.start.isSame(obj.start.toDate()) && current.SUMMARY === obj.SUMMARY) {
              return true;
            }
          }
        }
        return false;
      }
  
      Array.prototype.contains = function (obj) {
        var i = this.length;
        while (i--) {
          if (this[i] === obj) {
            return true;
          }
        }
        return false;
      }
  
      service.getEvents = function () {
        return service.events;
      }
  
      service.getFutureEvents = function () {
        var future_events = [],
          current_date = new moment(),
          end_date = new moment().add(config.calendar.maxDays, 'days');
  
        service.events.forEach(function (itm) {
          //If the event started before current time but ends after the current time or
          // if there is no end time and the event starts between today and the max number of days add it.
          if ((itm.end != undefined && (itm.end.isAfter(current_date) && itm.start.isBefore(current_date))) || itm.start.isBetween(current_date, end_date)) {
            future_events.push(itm);
          }
        });
        future_events = sortAscending(future_events);
        return future_events.slice(0, config.calendar.maxResults);
      }
  
      var sortAscending = function (events) {
        return events.sort(function (a, b) {
          var key1 = a.start;
          var key2 = b.start;
  
          if (key1.isBefore(key2)) {
            return -1;
          } else if (key1.isSame(key2)) {
            return 0;
          } else {
            return 1;
          }
        });
      }
  
      service.getPastEvents = function () {
        var past_events = [],
          current_date = new moment();
  
        service.events.forEach(function (itm) {
          //If the event ended before the current time, add it to the array to return.
          if (itm.end != undefined && itm.end.isBefore(current_date)) {
            past_events.push(itm);
          }
        });
        return past_events.reverse();
      }
  
      return service;
    }
  
    angular.module('SmartMirror')
      .factory('CalendarService', CalendarService);
  } ());
  