﻿//-----------------------------------------------------------------------------
// XCalView
//
// Copyright 2005-2010 - Xcential Group LLC.
//
//-----------------------------------------------------------------------------

XCalView.prototype = new XControl;
XCalView.prototype.constructor = XCalView;

//=============================================================================
// Constructor

function XCalView(
   id,
   create
)
{
   id = (id == null) ? null : id;
   create = (create == null) ? true : false;

   if (create)
      return new XCalView(id, false);

   //--------------------------------------------------------------------------
   // Private Interface

   //--------------------------------------------------------------------------
   // Privileged Interface

   this.valueOf = function()
   {

      return oSpec;
   }

   //--------------------------------------------------------------------------

   this.setObjectValue = function(
      id
   )
   {
      id = (id == null) ? null : id;

      oSpec = XCalView.getSpecFrom(id);

      return oSpec;
   }

   //--------------------------------------------------------------------------
   // Initialization

   var oSpec = this.setObjectValue(id);

   XControl.controls[oSpec.id] = this;

}

XCalView.prototype.objectClass = "XCalView";

//=============================================================================
// Static Interface

XCalView.getSpecFrom = function(
   id
)
{
   id = (id == null) ? XUtils.generateId("urn:xcential-com:calview:", XUtils.SCHEME_RANDOM) : id;

   var spec = new Array();
   spec.id = id;
   spec.className = "XCalView";
   spec.htmlNode = false;
   spec.visible = true;
   spec.left = 10;
   spec.top = 10;
   spec.xCalDoc = null;
   spec.showDays = false;
   spec.month = XDate().getMonth();
   spec.year = XDate().getFullYear();
   spec.categories = [];
   spec.hasFocus = false;

   return spec;
}

//=============================================================================
// Event Handlers

XCalView.prototype.onEventMouseOver = null;
XCalView.prototype.onEventMouseOut = null;
XCalView.prototype.onMonthChange = null;
XCalView.prototype.onYearChange = null;

//-----------------------------------------------------------------------------

XCalView.prototype.doEventMouseOver = function(
   event,
   docId,
   eventId
)
{
   event = (event == null) ? window.event : event;

   try
   {

      if (this.onEventMouseOver)
         this.onEventMouseOver(event, docId, eventId);

   }
   catch (error)
   {
      XApp.logEvent(XApp.EVENT_ERROR, error);
      XMsg.alert(error);
   }

}

//-----------------------------------------------------------------------------

XCalView.prototype.doEventMouseOut = function(
   event,
   docId,
   eventId
)
{
   event = (event == null) ? window.event : event;

   try
   {

      if (this.onEventMouseOut)
         this.onEventMouseOut(event, docId, eventId);

   }
   catch (error)
   {
      XApp.logEvent(XApp.EVENT_ERROR, error);
      XMsg.alert(error);
   }

}

//-----------------------------------------------------------------------------

XCalView.prototype.doPreviousMonth = function(
   event
)
{
   event = (event == null) ? window.event : event;

   try
   {

      var spec = this.valueOf();

      spec.month--;
      if (spec.month < 0)
      {
         spec.month = 11;
         this.setYear(this.getYear()-1);
      }

      this.redraw();
      //spec.htmlNode.scrollIntoView(false);

      if (this.onMonthChange)
         this.onMonthChange(this);

   }
   catch (error)
   {
      XApp.logEvent(XApp.EVENT_ERROR, error);
      XMsg.alert(error);
   }

}

//-----------------------------------------------------------------------------

XCalView.prototype.doNextMonth = function(
   event
)
{
   event = (event == null) ? window.event : event;

   try
   {

      var spec = this.valueOf();

      spec.month++;
      if (spec.month > 11)
      {
         spec.month = 0;
         this.setYear(this.getYear()+1);
      }

      this.redraw();
      //spec.htmlNode.scrollIntoView(false);

      if (this.onMonthChange)
         this.onMonthChange(this);

   }
   catch (error)
   {
      XApp.logEvent(XApp.EVENT_ERROR, error);
      XMsg.alert(error);
   }

}

//=============================================================================
// Public Interface

XCalView.prototype.setClass = function(
   className
)
{
   className = (className == null) ? "XCalView" : className;

   var spec = this.valueOf();
   spec.className = className;

   return className;
}

//-----------------------------------------------------------------------------

XCalView.prototype.hasFocus = function()
{

   var spec = this.valueOf();

   return spec.hasFocus;
}

//-----------------------------------------------------------------------------

XCalView.prototype.clear = function()
{

   this.array = null;

}

//-----------------------------------------------------------------------------

XCalView.prototype.setDoc = function(
   refOrDoc
)
{
   refOrDoc = (refOrDoc == null) ? null : refOrDoc;

   var spec = this.valueOf();
   spec.xCalDoc = (typeof(refOrDoc) == "string") ? XCalDoc(refOrDoc) : refOrDoc;

   if (this.onMonthChange)
      this.onMonthChange(this);
   if (this.onYearChange)
      this.onYearChange(this);

   return spec.xCalDoc;
}

//-----------------------------------------------------------------------------

XCalView.prototype.getXDoc = function()
{

   var spec = this.valueOf();

   return spec.xCalDoc;
}

//-----------------------------------------------------------------------------

XCalView.prototype.refresh = function()
{

   var spec = this.valueOf();

   this.array = null;
   if (spec.xCalDoc)
      spec.xCalDoc.refresh();

}

//-----------------------------------------------------------------------------

XCalView.prototype.showDays = function()
{

   var spec = this.valueOf();

   spec.showDays = true;

}

//-----------------------------------------------------------------------------

XCalView.prototype.hideDays = function()
{

   var spec = this.valueOf();

   spec.showDays = false;

}

//-----------------------------------------------------------------------------

XCalView.prototype.setMonth = function(
   month
)
{
   month = (month == null) ? XDate().getMonth() : month;

   var spec = this.valueOf();

   spec.month = month;

   if (this.onMonthChange)
      this.onMonthChange(this);

   return spec.month;
}

//-----------------------------------------------------------------------------

XCalView.prototype.getMonth = function()
{

   var spec = this.valueOf();

   return spec.month;
}

//-----------------------------------------------------------------------------

XCalView.prototype.setYear = function(
   year
)
{
   year = (year == null) ? XDate().getFullYear() : year;

   var spec = this.valueOf();

   spec.year = year;

   if (spec.xCalDoc)
   {
      var url = spec.xCalDoc.getURL();
      if (url)
      {
         url = url.replace(/year=[^\&]*\&?/i,"");
         url += (((/\?/).test(url)) ? "&" : "?") + "year=" + spec.year;
         this.setDoc(url);
      }
   }

   if (this.onYearChange)
      this.onYearChange(this);

   return spec.year;
}

//-----------------------------------------------------------------------------

XCalView.prototype.getYear = function()
{

   var spec = this.valueOf();

   return spec.year;
}

//-----------------------------------------------------------------------------

XCalView.prototype.setPosition = function(
   left,
   top
)
{
   left = (left == null) ? null : left;
   top = (top == null) ? null : top;

   var spec = this.valueOf();

   spec.left = (left) ? left : spec.left;
   spec.top = (top) ? top : spec.top;

   if (spec.htmlNode)
   {
      if (top || left)
         this.applyPositioning();
      else
         XUI(spec.htmlNode).setRuntimeStyle("position", "static");
   }

   return spec.htmlNode;
}

//-----------------------------------------------------------------------------

XCalView.prototype.setHighlightCategories = function(
   categories
)
{

   var spec = this.valueOf();

   spec.highlightCategories = categories;

   return spec.highlightCategories;
}

//-----------------------------------------------------------------------------

XCalView.prototype.getArray = function()
{

   var spec = this.valueOf();

   // Note: The array accumulates events from documents that are loaded. If
   //       multiple calendars are loaded (i.e. for different years), then
   //       the events accumulate rather than clearing for each year. That
   //       way, events can be defined that cross over years. If a different
   //       behavior is desired, then different calendar objects should be
   //       created.

   if (this.array == null)
   {
      this.array = new Array();
      this.array.docs = new Array();
   }

   if (spec.xCalDoc)
   {
      var xCalDoc = spec.xCalDoc;
      var docId = xCalDoc.getRoot().getChildValue("identifier");
      if (this.array.docs[docId] == null)
      {
         var events = xCalDoc.X$$("//xcal:Event");
         for (var i=0; i<events.length; i++)
         {
            var event = events.X$(i, XCalEvent);
            var startDay = event.getStartsAt(XDate).getDayNum();
            var endDay = event.getEndsAt(XDate).getDayNum();
            for (var day=startDay; day<=endDay; day++)
            {
               if (this.array[day] == null)
                  this.array[day] = new Array();
               var eventId = event.getId();
               if (this.array[day][eventId] == null)
               {
                  this.array[day][eventId] = event;
                  this.array[day].push(eventId);
               }
            }
         }

         this.array.docs[docId] = xCalDoc;
      }
   }

   return this.array;
}


//-----------------------------------------------------------------------------

XCalView.prototype.getEventHTML = function(
   docId,
   eventId
)
{

   var spec = this.valueOf();

   var eventHTML = "";
   var height = 50;
   var xCalDoc = this.array.docs[docId];
   if (xCalDoc)
   {
      var event = xCalDoc.X$(eventId, XCalEvent);
      eventHTML += "<div class=\"XCalEventHeader TextTitle Background2\">" + event.getSummary() + "</div>";
      eventHTML += "<table style=\"border-collapse: collapse\">";
      var status = event.getStatus();
      if (XString(status).isSomething())
      {
         eventHTML += "<tr><td class=\"XCalEventLabel Text4M\">Status:</td><td class=\"Text4M\">" + XString(status).toTitleCase() + "</td><tr>";
         height += 20;
      }
      var location = event.getLocation();
      if (XString(location).isSomething())
      {
         eventHTML += "<tr><td class=\"XCalEventLabel Text4M\">Location:</b><td class=\"Text4M\">" + XString(location).toTitleCase(XString.FULL_SENTENCE) + "</td></tr>";
         height += 20;
      }
      var when = null;
      var startsAt = event.getStartsAt();
      var endsAt = event.getEndsAt();
      var duration = event.getDuration();
      if (duration == "P1D")
         when = XDate(startsAt).toString(XDate.STYLE_MONTH_DAY_YEAR) + " (All day)";
      if (endsAt != startsAt)
         when = XDate(startsAt).toString(XDate.STYLE_MONTH_DAY_YEAR) + " - " + XDate(endsAt).toString(XDate.STYLE_MONTH_DAY_YEAR);
      else
         when = XDate(startsAt).toString(XDate.STYLE_MONTH_DAY_YEAR);
      if (XString(when).isSomething())
      {
         eventHTML += "<tr><td class=\"XCalEventLabel Text4M\">When:</b><td class=\"Text4M\">" + when + "</td></tr>";
         height += 20;
      }
      var categories = event.getCategories();
      if (categories.length > 0)
      {
         eventHTML += "<tr><td class=\"XCalEventLabel Text4M\">Categories:</td><td class=\"Text4M\">" + XArray(categories).toString(XArray.STYLE_COMMA_SEPARATED) + "</td></tr>";
         height += 40;
      }
      eventHTML += "</table>"
   }

   var eventHTML = new String(eventHTML);
   eventHTML.width = 400;
   eventHTML.height = height;

   return eventHTML;
}

//-----------------------------------------------------------------------------

XCalView.prototype.toHTML = function(
   state
)
{
   state = (state == null) ? [] : state;

   //--------------------------------------------------------------------------

   function getDayClasses(
      weekday,
      isHoliday
   )
   {
      isHoliday = (isHoliday == null) ? false : isHoliday;

      return (weekday == 0 || weekday == 6 || isHoliday) ? "Background1" : "Background0";
   }

   //--------------------------------------------------------------------------

   function getHoliday(
      events
   )
   {

      if (events == null)
         return null;

      for (var i=0; i<events.length; i++)
      {
         var event = events[events[i]];
         if (event.isHoliday())
            return event.getSummary();
      }

      return null;
   }

   //--------------------------------------------------------------------------

   function getMaxEventCount(
      eventsForWeek
   )
   {

      function countEvents(
         events
      )
      {
         var eventCount = 0;
         for (var i=0; i<events.length; i++)
         {
            var event = events[events[i]];
            if (!event.isHoliday()) eventCount++;
         }

         return eventCount;
      }

      var maxEvents = 0;
      for (var weekday=0; weekday<7; weekday++)
      {
         if (eventsForWeek[weekday])
         {
            var eventCount = countEvents(eventsForWeek[weekday]);
            if (eventCount > maxEvents)
               maxEvents = eventCount;
         }
      }

      return maxEvents;
   }

   //--------------------------------------------------------------------------

   function getEvent(
      events,
      index
   )
   {

      var eventCount = -1;
      for (var i=0; i<events.length; i++)
      {
         var event = events[events[i]];
         if (!event.isHoliday())
            eventCount++;
         if (eventCount == index)
            return event;
      }

      return null;
   }

   //--------------------------------------------------------------------------

   function hasEvent(
      events,
      id
   )
   {

      return (events && events[id] != null) ? true : false;
   }

   //--------------------------------------------------------------------------

   function getEventClasses(
      event,
      highlightCategories
   )
   {
      highlightCategories = (highlightCategories == null) ? [] : highlightCategories;

      var classes = "";

      var categories = event.getCategories();
      for (var i=0; i<categories.length; i++)
      {
         category = categories[i];
         for (j=0; j<highlightCategories.length; j++)
         {
            if (category == highlightCategories[j])
            {
               classes += "Highlight ";
               return classes;
            }
         }
      }

      if (event.getStatus() != XCalEvent.STATUS_CANCELLED)
      {
         classes += "Background2";
         return classes;
      }

      return classes;
   }

   //--------------------------------------------------------------------------

   var spec = this.valueOf();

   var firstDay = XDate(spec.year + "-" + ((spec.month < 9) ? "0" : "") + Number(spec.month+1) + "-01");
   var firstDayNum = firstDay.getDayNum();
   var firstWeekday = firstDay.getWeekday();
   var numDaysInMonth = XDate.getNumDaysInMonth(spec.month, spec.year);

   var calArray = this.getArray();

   var controlHTML = "<div " +
      "id=\"" + spec.id + "\" " +
      "class=\"" + spec.className + "\" " +
      ((spec.tabIndex != null) ? "tabindex=\"" + spec.tabIndex + "\" " : "") +
      "style=\"" +
         ((spec.left) ? "left: " + XUtils.dimText(spec.left) + "; " : "") +
         ((spec.top) ? "top: " + XUtils.dimText(spec.top) + "; " : "") +
      "\" " +
   ">\n";

   controlHTML += "<table class=\"" + spec.className + "Table\">\n";
   controlHTML += "   <tr>";
   for (var day=0; day<7; day++)
   {
      controlHTML += "<th class=\"" + spec.className + "Day Header\">" + XDate.DAYS[day] + "</th>";
   }
   controlHTML += "</tr>\n";
   if (spec.showDays)
   {
      var day = 1;
      for (var week=0; week<6; week++)
      {
         controlHTML += "   <tr>";
         var weekDates = new Array();
         var eventsForWeek = new Array();
         var holidays = new Array();
         for (var weekday=0; weekday<7; weekday++)
         {
            weekDates[weekday] = ((week > 0 || weekday >= firstWeekday) && day <= numDaysInMonth) ? day++ : null;
            eventsForWeek[weekday] = (weekDates[weekday] == null) ? null : calArray[firstDayNum+weekDates[weekday]-1]
            holidays[weekday] = (weekDates[weekday] == null) ? null : getHoliday(eventsForWeek[weekday]);
            var isHoliday = (holidays[weekday] != null) ? true : false;
            controlHTML += "<td " +
               "class=\"" + spec.className + "DayTop " + getDayClasses(weekday, isHoliday) + "\" " +
               ">";
            controlHTML += (weekDates[weekday]) ? weekDates[weekday] : "&#160;";
            controlHTML += (holidays[weekday]) ? " <span class=\"Text4S\">" + holidays[weekday] + "</span>" : "";
            controlHTML += "</td>";
         }
         controlHTML += "</tr>\n";
         var maxEventsThisWeek = getMaxEventCount(eventsForWeek);
         for (var eventNum=0; eventNum<maxEventsThisWeek; eventNum++)
         {
            controlHTML += "   <tr>";
            for (var weekday=0; weekday<7; weekday++)
            {
               var isHoliday = (holidays[weekday] != null) ? true : false;
               if (eventsForWeek[weekday] != null)
               {
                  var event = getEvent(eventsForWeek[weekday], eventNum);
                  if (event != null)
                  {
                     var docId = event.getDocId();
                     var eventId = event.getId();
                     var colspan = 1;
                     if (weekday != 0 && hasEvent(eventsForWeek[weekday-1], eventId))
                        colspan = 0;
                     else
                     {
                        for (nextDay = weekday+1; nextDay<7; nextDay++)
                        {
                           if (hasEvent(eventsForWeek[nextDay], eventId))
                              colspan++;
                           else
                              break;
                        }
                     }
                     if (colspan > 0)
                     {
                        controlHTML += "<td " +
                           "class=\"" + spec.className + "Event Text4S " + getEventClasses(event, spec.highlightCategories) + "\" " +
                           "colspan=\"" + colspan + "\" " +
                           "onmouseover=\"XControl.controls['" + spec.id + "'].doEventMouseOver(event, '" + docId + "', '" + eventId + "')\" " +
                           "onmouseout=\"XControl.controls['" + spec.id + "'].doEventMouseOut(event, '" + docId + "', '" + eventId + "')\" " +
                        ">";
                        controlHTML += (event.getStatus() == XCalEvent.STATUS_CANCELLED) ? "<del class=\"Deletion\" cite=\"mailto:Deletion\">" : "";
                        controlHTML += event.getSummary();
                        controlHTML += (event.getStatus() == XCalEvent.STATUS_CANCELLED) ? "</del >" : "";
                        controlHTML += (event.getStatus() == XCalEvent.STATUS_CONFIRMED) ? "<br/><span class=\"InlineBlock Confirmed\">&#160;Confirmed&#160;</span>" : "";
                        controlHTML += (event.getStatus() == XCalEvent.STATUS_TENTATIVE) ? "" : "";
                        controlHTML += (event.getStatus() == XCalEvent.STATUS_CANCELLED) ? "<br/><span class=\"InlineBlock Cancelled\">&#160;Cancelled&#160;</span>" : "";
                        controlHTML += "</td>";
                     }
                  }
                  else
                     controlHTML += "<td " +
                        "class=\"" + spec.className + "DayMiddle " + getDayClasses(weekday, isHoliday) + "\" " +
                        ">&#160;</td>";
               }
               else
                  controlHTML += "<td " +
                     "class=\"" + spec.className + "DayMiddle " + getDayClasses(weekday, isHoliday) + "\" " +
                     ">&#160;</td>";
            }
            controlHTML += "</tr>\n";
         }
         controlHTML += "   <tr>";
         for (var weekday=0; weekday<7; weekday++)
         {
            var isHoliday = (holidays[weekday] != null) ? true : false;
            controlHTML += "<td " +
               "class=\"" + spec.className + "DayBottom " + getDayClasses(weekday, isHoliday) + "\" " +
               ">&#160;</td>";
         }
         controlHTML += "</tr>\n";
         if (day > numDaysInMonth)
            break;
      }
   }
   controlHTML += "</table>\n";
   controlHTML += "<br/>\n";
   controlHTML += "</div>\n";

   return controlHTML;
}

//=============================================================================
