/*!
 * Google Calendar Events List v1.0.1
 *
 * Copyright (c) 2009 Jason Hubbard
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Date: 2009-10-12 20:40:05 -0700 (Mon, 12 Oct 2009)
 * Revision: 2
 */
 
(function($) {
  $.fn.gcal_events = function(o){
    
    var opts = {
      feedURL       :  'http://www.google.com/calendar/feeds/developer-calendar@google.com/public/basic',
      ctz           :  'America%2FNew_York',
      orderby       :  'starttime',
      sortorder     :  'ascending',
      max_results   :  15,
      singleevents  :  true,
      futureevents  :  false,
      start_min     :  '1970-01-01',
      start_max     :  '2031-01-01',
      display       :  'mini',
      no_event_msg  :  'No Events Listed',
      local_url     :  'calendar.html',
      tooltip_dir   :  'w',
      tooltip_id    :  'gcal_tooltip'
    };
    
    if(o) $.extend(opts, o);
    
    // process the feedURL just a bit to get the full list of events, based on the options
    opts.feedURL = opts.feedURL.replace(/\/basic$/, '/full');
    opts.feedURL = opts.feedURL+"?alt=json-in-script";
    opts.feedURL = opts.feedURL+"&ctz="+opts.ctz;
    opts.feedURL = opts.feedURL+"&orderby="+opts.orderby;
    opts.feedURL = opts.feedURL+"&sortorder="+opts.sortorder;
    opts.feedURL = opts.feedURL+"&max-results="+opts.max_results;
    opts.feedURL = opts.feedURL+"&singleevents="+opts.singleevents;
    opts.feedURL = opts.feedURL+"&futureevents="+opts.futureevents;
    opts.feedURL = opts.feedURL+"&start-min="+opts.start_min;
    opts.feedURL = opts.feedURL+"&start-max="+opts.start_max;
    opts.feedURL = opts.feedURL+"&callback=?";
    
    return this.each(function() {
      //Creating a reference to the object
      var obj = $(this);
      obj.append('<div class="feed_loading"></div>');
      // show the loading message...
      $('.feed_loading',obj).css('display','inline');
      // retrieve the events from the google calendar
      $.getJSON(opts.feedURL,
        function(data) {
          gcal_event_output = '';
          if (data.feed.entry) {
            gcal_event_output += '<script type="text/javascript">';
            gcal_event_output += '$(function() {$(".'+opts.tooltip_id+'").tipsy({gravity: "'+opts.tooltip_dir+'",fade: true});});';
            gcal_event_output += '</script>';
            gcal_event_output += '<ul class="feed_list">';
            $.each(data.feed.entry, function(i, entry) {
              var id = entry['gCal$uid']['value'],
                title = entry['title']['$t'],
                startStr = entry['gd$when'][0]['startTime'],
                start = startStr,
                end = entry['gd$when'][0]['endTime'],
                allDay = startStr.indexOf('T') == -1,
                classNames = [],
                location = entry['gd$where'][0]['valueString'],
                description = entry['content']['$t'],
                url;
              $.each(entry.link, function() {
                if (this.type == 'text/html') {
                  url = this.href;
                }
              });
              // check if all day - if so, fix the end date to make it inclusive
              // google calendar has the end date as the next day
              if (allDay) {
                // set the date object to the end date
                var cor = new Date(end);
                // subtract one day from it
                //cor.setDate(cor.getDate() - 1);
                // get the year month and date
                var tyr = cor.getFullYear();
                var tmh = ((cor.getMonth()+1) < 10 ? '0' : '') + (cor.getMonth()+1);
                var tdy = (cor.getDate() < 10 ? '0' : '') + cor.getDate();
                // build it in YYYY-MM-DD format
                var tend = tyr+'-'+tmh+'-'+tdy;
                end = tend;
              }
              /*if (allDay) {
                alert (start + ' \n ' + end);
              }*/
              // Build row HTML data and store in string
              gid = id;
              gurl = url;
              gtitle = title;
              gcontent = (description == '')?'':'<br />'+description;
              gstart = $.gcal_events.formatGCalTime(start);
              gend = $.gcal_events.formatGCalTime(end);
              if (gstart[0] == gend[0]){
                times = (allDay)?'':' from '+gstart[1]+' to '+gend[1];
                gdate = gstart[0]+times;
              } else {
                stime = (allDay)?'':gstart[1]+' on ';
                etime = (allDay)?'':gend[1]+' on ';
                gdate = 'From '+stime+gstart[0]+' to '+etime+gend[0];
              }
              gwhere = (location == '')?'':'<br />Where: '+location+'<br />';
              gbody = 'What: '+gtitle+'<br />When: '+gdate+gwhere+gcontent;
              
              switch (opts.display) {
                case 'mini':
                  mstart = (allDay)?((gstart[0] == gend[0])?gstart[0]:gstart[0]+' - '+gend[0]):gstart[0]+' at '+gstart[1];
                  gcal_data = $.gcal_events.mini(gid,gtitle,mstart,gbody,opts.local_url,opts.tooltip_id);
                  break;
                case 'full':
                  gcal_data = $.gcal_events.full(gid,gtitle,gdate,gbody);
                  break;
              };
              
              gcal_event_output += gcal_data;
            });
            
            gcal_event_output += '</ul>';
            
          } else {
            gcal_event_output += opts.no_event_msg;
          }
          
          // all done, hide the loading message...
          $('.feed_loading',obj).css('display','none');
          
          // update the passed object with the output
          obj.append(gcal_event_output);
        });
    });
  };
  
  $.gcal_events = {
    // static function to build a mini version of each event
    mini: function(feedID,feedTitle,feedDate,feedBody,feedLink,feedTooltipID) {
      output = '';
      output += '<li class="feed_item">';
      output += '<a class="'+feedTooltipID+'" href="'+ feedLink +'" title="'+ feedBody +'">';
      output += '<span class="feed_title_mini">'+ feedTitle + '</span><br />';
      output += '<span class="feed_date">'+ feedDate +'</span>';
      output += '</a></li>';
      return output;
    },
    // static function to build a full version of each event
    full: function(feedID,feedTitle,feedDate,feedBody) {
      output = '';
      output += '<li class="feed_item">';
      output += '<h4 class="feed_title" id="id_'+ feedID +'">'+ feedTitle + '</h4>';
      output += '<div class="feed_date">'+ feedDate +'</div>';
      output += '<div class="feed_body">'+ feedBody +'</div>';
      output += '</li>';
      return output;
    },
    formatGCalTime: function(gCalTime) {
      // text for regex matches
      var remtxt = gCalTime;
      function consume(retxt) {
        var match = remtxt.match(new RegExp('^' + retxt));
        if (match) {
          remtxt = remtxt.substring(match[0].length);
          return match[0];
        }
        return '';
      }
      // pad with zeros
      function zero_padding(n) {
        return (n < 10 ? '0' : '') + n;
      }
      // add proper ending to day number
      function postfix_num(n) {
        // add the appropriate ending to the day
        if (n > 10 && n < 20) return n+'th';
        return n+['st', 'nd', 'rd'][n%10-1] || n+'th';
      }
      // minutes of correction between gCalTime and GMT
      var totalCorrMins = 0;
      var year = consume('\\d{4}');
      consume('-?');
      var month = consume('\\d{2}');
      consume('-?');
      var dateMonth = consume('\\d{2}');
      var timeOrNot = consume('T');
      // if a DATE-TIME was matched in the regex
      if (timeOrNot == 'T') {
        var hours = consume('\\d{2}');
        consume(':?');
        var mins = consume('\\d{2}');
        consume('(:\\d{2})?(\\.\\d{3})?');
        var zuluOrNot = consume('Z');
        // if time from server is not already in GMT, calculate offset
        if (zuluOrNot != 'Z') {
          var corrPlusMinus = consume('[\\+\\-]');
          if (corrPlusMinus != '') {
            var corrHours = consume('\\d{2}');
            consume(':?');
            var corrMins = consume('\\d{2}');
            totalCorrMins = (corrPlusMinus=='-' ? 1 : -1) *
                (Number(corrHours) * 60 +
          (corrMins=='' ? 0 : Number(corrMins)));
          }
        }
        // get time since epoch and apply correction, if necessary
        // relies upon Date object to convert the GMT time to the local
        // timezone
        var originalDateEpoch = Date.UTC(year, month - 1, dateMonth, hours, mins);
        var gmtDateEpoch = originalDateEpoch + totalCorrMins * 1000 * 60;
        var ld = new Date(gmtDateEpoch);
        var year    = ld.getFullYear();
        var day     = ld.getDay();
        var month   = ld.getMonth();
        var daym    = postfix_num(ld.getDate());
        var hours   = ld.getHours() % 12 || 12;
        var minutes = zero_padding(ld.getMinutes());
        var seconds = zero_padding(ld.getSeconds());
        var dn      = ld.getHours() < 12 ? 'AM' : 'PM';
        var thedate = montharray[month]+" "+daym+", "+year;
        var thetime = hours+":"+minutes+" "+dn;
        var dateString = new Array();
        dateString.push(thedate,thetime);
      } else {
        // if only a DATE was matched
        var daym    = postfix_num(parseInt(dateMonth, 10));
        var thedate = montharray[month-1]+" "+daym+", "+year;
        var dateString = new Array();
        dateString.push(thedate,'All Day');
      }
      return dateString;
    }
  };
})(jQuery);