/**
 * Copyright (c) 2006, Opera Software ASA
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Opera Software ASA nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY OPERA SOFTWARE ASA AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL OPERA SOFTWARE ASA AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

var weekNumberName='Wno';

var allNameOfWeekDays=
[ 
  "Mo",
  "Tu", 
  "We", 
  "Th", 
  "Fr", 
  "Sa", 
  "Su"
];
var allNameOfMonths=
[
  "January",
  "February",
  "March",
  "April",
  "May",
  "June",
  "July",
  "August",
  "September",
  "October",
  "November",
  "December"
];

/**
  * Class to generate markup for a calendar.
  * If a date is clicked, a bubbling event with the name 'calendarEvent' is released on the container element.
  * @constructor
  * @param container a html element as container for the calendar 
  * @param numberOfMonth the number of months to gemerate. Default 1.
  * @param withWeekNumbers a boolean to indicate if the weeknumbers shall be displayed. Default false.
  * @param european a boolean to indicate if the calendar shall be displayed in european mode. Default false.
  */

var Calendar=function (container, numberOfMonth, withWeekNumbers, european)
{
  var currentMonth=(new Date).getMonth();
  var currentYear=(new Date).getFullYear();
  var container=container;
  var numberOfMonth=numberOfMonth||1;
  var withWeekNumbers=(withWeekNumbers&&true)||false;
  var europeanMode=(european&&true)||false;
  var getWeekNumber=function(date)
  {
    var newYear=new Date(date.getFullYear(), 0,1);
    var correction=1;
    var time=date.getTime();
    if(europeanMode)
    {
      time-=(((date.getDay()+13)%7)*24*60*60*1000);
      var day=newYear.getDay();
      if(day==0||day==5||day==6) correction=0;
    }
    else
    {
      time-=((date.getDay())*24*60*60*1000);
    }
    var weeknumber=Math.ceil((time-newYear.getTime())/(7*24*60*60*1000))+correction;
    if(weeknumber) return weeknumber;
    else return getWeekNumber(new Date(date.getFullYear(), -1, 31));
  }
  var createMonth=function(date)
  {
    var month=date.getMonth();
    var year=date.getFullYear();
    var i=1;
    var dateRun=new Date(year, month, 1, 0, 0, 0);
    var today=new Date();
    today.setHours(0,0,0);
    today=today.toString();
    var weekday=dateRun.getDay();
    var table=container.add('div', '', 'className', 'calendar').add('table');
    var headrow=table.add('thead').add('tr');
    headrow.add('th', allNameOfMonths[month], 'colSpan', 4);
    headrow.add('th', year, 'colSpan', (withWeekNumbers?4:3));
    var tbody=table.add('tbody', '', 'year', year, 'month', month);
    var row=tbody.add('tr');
    var td=null;
    var captionText='';
    if(withWeekNumbers)
    {
      row.add('td', weekNumberName);
    }
    var k=0, name='';
    for(; name=allNameOfWeekDays[k]; k++)
    {
      if(!europeanMode) name=allNameOfWeekDays[(k+6)%7];
      row.add('td', name);
    }
    row=tbody.add('tr');
    if(withWeekNumbers)
    {
      row.add('td', getWeekNumber(dateRun), 'className', 'weeknumber');
    }
    if(!europeanMode) weekday=(weekday+1)%7;
    while(i%7!=weekday)
    {
      row.add('td');
      i++;
    }
    var currentDay=1;
    var isCurrentMonth=true;
    while(isCurrentMonth)
    {
      td=row.add('td', currentDay, 'className', 'day');
      if(dateRun.toString()==today) td.addClass('today');
      if(dateRun.getDay()==0) td.addClass('holiday');
      dateRun.setDate(++currentDay);
      isCurrentMonth=(dateRun.getMonth()==month);
      if(i%7==0)
      {
        row=tbody.add('tr');
        if(withWeekNumbers && isCurrentMonth)
        {
          row.add('td', getWeekNumber(dateRun), 'className', 'weeknumber');
        }
      }
      if(isCurrentMonth)
      {
        i++;
      }
    }
    while((i++)%7!=0) 
    {
      row.add('td');
    }

  }
  container.onclick=function(event)
  {
    var ele=event.target, day=0;
    if(/^td$/i.test(ele.nodeName) && /day/.test(ele.className) && ele.firstChild && (day=ele.firstChild.nodeValue))
    {
      var event=document.createEvent('Event');
      event.initEvent('calendarEvent', true, false);
      event.data=new Date(ele.parentNode.parentNode.year, ele.parentNode.parentNode.month, parseInt(day));
      container.dispatchEvent(event);
    }
  }
  /** to draw the calendar in the container*/
  this.update=function()
  {
    container.innerHTML='';
    var i=0;
    for( ;i<numberOfMonth; i++)
    {
      createMonth(new Date(currentYear,currentMonth+i,01));
    }
  }
  /** shift the current month for one month */
  this.next=function()
  {
    currentMonth++;
    this.update();
  }
  /** shift the current month back for one month */
  this.previous=function()
  {
    currentMonth--;
    this.update();
  }
  /** increase the number of months to display by one  */
  this.more=function()
  {
    numberOfMonth++;
    this.update();
  }
  /** decrease the number of months to display by one  */
  this.less=function()
  {
    if(--numberOfMonth<1) numberOfMonth=1;
    this.update();
  }
  /** set the display mode regarding european or american mode */
  this.setEuropeanMode=function(bol)
  {
    europeanMode=bol;
  }
  /** to set the current month
   * @param number the current month
   */
  this.setMonth=function(month)
  {
    currentMonth=month;
  }
  /** to set the current year
   * @param number the current year
   */
  this.setYear=function(year)
  {
    currentYear=year;
  }

}