/* Copyright Andreas Winkler, Fischer | Enterprises */

// constructor
Cal = function(id)
{
    // properties
    this.id;
    this.date = null;
    this.table = null;
    this.showWeekNumbers = false;
    this.showDaysFromOtherMonths = false;
    this.isPopup = true;
    this.container = $(document.createElement('div'));
    this.fdn = null;
    this.time = true;
    this.days = [];
    this.minYear = 1970;
    this.maxYear = 2999;
    this.activeContainer = null;
    this.target = null;
    this.format = '%d.%m.%Y %H:%M';

    this.container.addClassName('calendar');
    this.container.id = this.id = id;
    
    if (this.isPopup)
        this.container.addClassName('popup');
}

Cal.prototype.onSelect = function(cal, value) {
    this.target.value = value;
}

Cal.prototype.onClose = function() {
    this.container.hide();
}

Cal.prototype.open = function(date) {
    this.init(date ? new Date(Date.parseByFormat(date, this.format)) : new Date);
    $(this.id).show('block');
}

// create Cal within given element and date
Cal.prototype.create = function(d)
{
    p = $(document.getElementsByTagName('body')[0]);

    var cal = this;
    this.date = d ? new Date(d) : new Date();
    
    this.table = this.container.append('table');
    this.table.observe('mousedown', Cal.tabOnMouseDown);
    this.table.calender = this;
    
    var head = this.table.append('thead');
    
    var row = head.append('tr');
    
    var AC = function(t, s, ty)
    {
        var c = row.append('td');
        c.colSpan = s;
        c.addClassName('button');
        if (ty > 100) c.addClassName('nav');
        Cal._addCellEvents(c);
        c.calendar = cal;
        c.type = ty;
        c.innerHTML = t;
        return c;                  
    } 
    
    AC('?', 1, 99);
    
    var titleSpan = 6;
    this.isPopup && --titleSpan;
    
    this.title = AC('', titleSpan, 98).addClassName('title');
    
    if (this.isPopup)
    {
        this.title.style.cursor = 'move';
        AC('&#x00d7;', 1, 97);
    }
    
    row = head.append('tr').addClassName('head');
    
    AC('&#x00ab;', 1, 101);
    AC('&#x2039;', 1, 102);
    AC(Cal._L['today'], 3, 0);
    AC('&#x203a;', 1, 103);
    AC('&#x00bb;', 1, 104);

    row = head.append('tr').addClassName('days');
    
    for (var i = 0; i < 7; i++)
        c = row.append('td');
    
    this.fdn = row.firstChild;
    this.showWeekdays();
    
    this.body = this.table.append('tbody');
    
    for (i = 0; i < 6; i++)
    {
        row = this.body.append('tr');
        
        for (var j = 0; j < 7; j++)
        {
            c = row.append('td');
            c.calendar = this;
            Cal._addCellEvents(c);
        }
    }
    
    if (this.time)
    {
        row = this.body.append('tr').addClassName('time');
        
        c = row.append('td').addClassName('time');
        c.colSpan = 2;
        c.innerHTML = Cal._L['time'] || '&nbsp;';
        
        c = row.append('td').addClassName('hours').observe(
            'mousedown', Cal.timeOnMouseDown);
        c.type = 90;
        
        var h = this.timePart('hour', this.date.getHours(), 0, 23, this, c);
        
        row.append('td').innerHTML = ':';
        
        c = row.append('td').addClassName('minutes').observe(
            'mousedown', Cal.timeOnMouseDown);
        c.type = 91;
        
        var m = this.timePart('minute', this.date.getMinutes(), 0, 59, this, c);
        
        this.onSetTime = function() {
            var hrs = this.date.getHours();
            var mins = this.date.getMinutes();
            h.innerHTML = hrs < 10 ? ('0' + hrs) : hrs;
            m.innerHTML = mins < 10 ? ('0' + mins) : mins;
        }
        
        this.onUpdateTime = function() {
            var date = this.date;
            date.setHours(parseInt(h.innerHTML, 10));
            date.setMinutes(parseInt(m.innerHTML, 10));
            date.setFullYear(this.date.getFullYear());
            date.setMonth(this.date.getMonth());
            date.setDate(this.date.getDate());
            this.dateClicked = false;
            this.callHandler();
        }
    }
    else
        this.onSetTime = this.onUpdateTime = function() {};
    
    this.init(this.date);
    p.appendChild(this.container);
}

Cal._clickHandler = function(e, ev)
{     
    var c = Cal._C;
    var close = false;
    var newDate = false;

    if (c == null) return;

    if (typeof e.type == 'undefined') c.date.setDatePart(e.caldate);
    
    var date = new Date(c.date);
    
    var y = date.getFullYear();
    var m = date.getMonth();
    
    switch (e.type)
    {
        case 0: date.setDatePart(new Date()); break;
        case 101: if (y > c.minYear) date.setFullYear(--y); break;        
        case 102: if (m > 0 || y > c.minYear) date.setMonth(--m); break;
        case 103: if (m < 11 || y < c.maxYear) date.setMonth(++m); break;
        case 104: if (y < c.maxYear) date.setFullYear(++y); break;
        case 99: break;
        case 97: close = true; break;
    }
    
    if (e.type > 100 || e.type == 0)
    {
        c.init(date);
        return;
    }
    
    if (!date.equals(c.date))
    {
        c.date.setDatePart(date);
        newDate = true;
    }
    else if (!e.type)
        newDate = close = true;

    if (newDate)
        ev && c.onSelect && c.onSelect(c, c.date.print(c.format));
    
    if (close)
    {
        e.removeClassName('highlight');
        ev && c.onClose && c.onClose(c);
    }
}

Cal._setValidYear = function(date, min, max)
{
    if (date.getFullYear() < min)
        date.setFullYear(min);
    else if (date.getFullYear() > max)
        date.setFullYear(max);
    
    return date;
}

Cal._calculateFirstDay = function(date, fd)
{
    date.setDate(1);
    var d = (date.getDay() - fd) % 7;
    if (d < 0) d += 7;
    date.setDate(-d);
    date.setDate(date.getDate() + 1);
    return date;
}

Cal.prototype.initRow = function(i, r, date)
{
    var c = r.firstChild;
    
    r.className = 'days';

    var hd = false, d, pos = this.days[i] = [];
        
    for (var j = 0; j < 7; ++j, c = c.nextSibling, date.setDate(d + 1))
    {
        d = date.getDate();
        c.className = 'day';
        c.pos = i << 4 | j;
        pos[j] = c;

        if (date.getMonth() == this.date.getMonth())
        {
            c.currentMonth = true;
            hd = true;
                
            if (d == date.getDate())
                this.currentDateElement = c.addClassName('selected');  
        }
        else
        {
            if (this.showDaysFromOtherMonth)
            {
                c.addClassName('om');
                c.currentMonth = false;
            }
            else
            {
                c.className = 'empty';
                c.innerHTML = '&nbsp;';
                c.disabled = true;
                continue;
            }
        }
        
        c.innerHTML = d;
        c.caldate = new Date(date);
            
        if (date.getFullYear() == this.date.getFullYear() && 
            date.getMonth() == this.date.getMonth() && d == this.date.getDate())
            c.addClassName('today');
                
        if (Cal._L['weekend'].indexOf(date.getDay().toString()) != -1)
            c.addClassName(c.currentMode ? 'weekend' : 'otherWeekend');
    }
        
    if (!(hd || this.showDaysFromOtherMonth))
        r.className = 'empty';   
}

Cal.prototype.init = function(date)
{
    this.table.hide();
    this.date = new Date(Cal._setValidYear(date));
    date = Cal._calculateFirstDay(date, 1);    
    var r = this.body.firstChild;
    
    for (var i = 0; i < 6; ++i, r = r.nextSibling)
        this.initRow(i, r, date);
    
    this.title.innerHTML = Cal._M[this.date.getMonth()] + ', ' + 
        this.date.getFullYear();
    this.onSetTime();
    this.table.show('block');    
}

Cal.prototype.timePart = function(c, i, rs, re, cal, cell)
{
    var p = cell.append('span').addClassName(c);
    p.innerHTML = i;
    p.calendar = cal;
    p.type = 50;
    p._range = [];
    if (typeof rs != 'number')
        p._range = rs;
    else
        for (var i = rs; i <= re; ++i)
            p._range[p._range.length] = (i < 10 && re >= 10 ? '0' : '') + i;
    
    Cal._addCellEvents(p);
    return p;
}

Cal._addCellEvents = function(e) 
{
    e.observe('mouseover', Cal.dayOnMouseOver);
    e.observe('mousedown', Cal.dayOnMouseDown);
    e.observe('mouseout', Cal.dayOnMouseOut);
        
    if (isIE)
    {
        e.observe('dblclick', Cal.dayOnMouseDoubleClick);
        e.setAttribute('unselectable', true);
    }
}

Cal.dayOnMouseOver = function(ev)
{
    var e = Event.element(ev);
    
    if (e.innerHTML != '&nbsp;')
        e.addClassName('highlight');
  
    return Event.stop(ev);
}

Cal.dayOnMouseOut = function(ev)
{
    Event.element(ev).removeClassName('highlight');
    
    return Event.stop(ev);
}

Cal.dayOnMouseDown = function(ev)
{
    var e = Event.element(ev);

    if (e.disabled)
        return false;
    
    var c = e.calendar;
    Cal._C = c;
    
    if (e.type !== 98)
    {
        if (e.type == 50)
        {
            e._current = e.innerHTML;
            document.observe('mousemove', Cal.tabOnMouseOver);
        }
        else
            document.observe(isIE5 ? 'mousemove' : 'mouseover', Cal.tabOnMouseOver);
        
        e.addClassName('highlight').addClassName('active');
        document.observe('mouseup', Cal.tabOnMouseUp);
    }
    else
        c._dragStart(ev);
    
    return Event.stop(ev);
}

Cal.tabOnMouseOver = function(ev)
{
    return Event.stop(ev);
}

Cal.tabOnMouseUp = function(ev)
{
    var e = Event.element(ev);
    
    Cal._clickHandler(e, ev);

    e.stopObserving('mouseup', Cal.tabOnMouseUp);
    e.stopObserving('mouseover', Cal.tabOnMouseOver);
    e.stopObserving('mousemove', Cal.tabOnMouseMove);

    Cal._C = null;

    return Event.stop(ev);
}

Cal.tabOnMouseDown = function(ev)
{
    return Event.stop(ev);
}

Cal.prototype.showWeekdays = function()
{
    var c = this.fdn;
    
    for (var i = 0; i < 7; ++i)
    {
        c.className = 'day name';
        var real = (i + 1) % 7;
        
        if (i)
        {
            c.type = 60;
            c.calendar = this;
            c.fdow = real;
            Cal._addCellEvents(c);
        }    
        
        if (Cal._L['weekend'].indexOf(real.toString()) != -1)
            c.addClassName('weekend');
        
        c.innerHTML = Cal._D[real];
        c = c.nextSibling;
    }
};

// position calendar in the left bottom corner of the given element
Cal.prototype.position = function(e)
{
    this.target = e;
    
    var p = e.getAbsPos();
    
    this.container.style.left = p.x + 'px';
    this.container.style.top = (p.y + e.offsetHeight) + 'px';
    
    var rs = this.table.select('tr');
    
    for (var i = 0; i < rs.lenght; i++)
    {
        var cs = rs[i].removeClassName('highlight').select('td');
        
        for (var j = 0; j < cs.lenght; j++)
            cs[j].removeClassName('highlight').removeClassName('active');
    }
}

// date prototypes
Date.prototype.daysInMonth = function(m, y) {
    return new Date(y, m, 0).getDate();
}

Date.prototype.equals = function(date) {
    return Date.parse(this) == Date.parse(date);
}

Date.prototype.setDatePart = function(date) {
    this.setDate(date.getDate()); 
    this.setFullYear(date.getFullYear()); 
    this.setMonth(date.getMonth());
}

Date.parseByFormat = function(date, format)
{
    var m = parseInt(date.substr(format.indexOf('%m'), 2), 10) - 1;
    var d = parseInt(date.substr(format.indexOf('%d'), 2), 10);
    var y = parseInt(date.substr(format.indexOf('%Y'), 4));
    
    // add 2 to index because of 4-digit year value -> fix that!
    var h = parseInt(date.substr(format.indexOf('%H') + 2, 2), 10);
    var min = parseInt(date.substr(format.indexOf('%M') + 2, 2), 10);
    
    return new Date(y, m, d, h, min, 0);
}

Date.prototype.print = function(str) {
  var m = this.getMonth();
	var d = this.getDate();
	var y = this.getFullYear();
	var w = this.getDay();
	var h = this.getHours();
  var min = this.getMinutes();
  var s = {};

	s["%a"] = Cal._D[w];
	s["%A"] = Cal._FD[w];
	s["%b"] = Cal._SM[m];
	s["%B"] = Cal._M[m];
	s["%C"] = 1 + Math.floor(y / 100);
	s["%d"] = (d < 10) ? ("0" + d) : d;
	s["%e"] = d;
	s["%H"] = (h < 10) ? ("0" + h) : h;
	s["%k"] = h;
	s["%m"] = (m < 9) ? ("0" + (1 + m)) : (1 + m);
	s["%M"] = (min < 10) ? ("0" + min) : min;
	s["%s"] = Math.floor(this.getTime() / 1000);
	s["%u"] = w + 1;
	s["%w"] = w;
	s["%Y"] = y;

	var exp = /%./g;
	
  if (!(isIE5 || isKHtml))
      return str.replace(exp, function(p) { return s[p] || p; });	
	
	var m = str.match(exp);
	
	for (var i = 0; i < a.length; i++)
	{
      var t = s[a[i]];
      
      if (t)
      {
          exp = new RegExp(a[i], 'g');
          str = str.replace(exp, t);
      }
  }
  
  return str;
};

Cal._L = {};
Cal._L['today'] = 'heute';
Cal._L['weekend'] = '0,6';

Cal._M = new Array('J\u00e4nner', 'Februar', 'M\u00e4rz', 'April', 'Mai', 
    'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember');

Cal._SM = new Array('Jan', 'Feb', 'Mar', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 
    'Sep', 'Okt', 'Nov', 'Dez');

Cal._D = new Array('So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa');

Cal._FD = new Array('Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 
    'Freitag', 'Samstag');
    
Cal._setup = function(id, target, btn, date, format)
{
    var c = new Cal(id);
    c.create(date);
    c.format = format;
    c.position($(target));
    $(btn).observe('click', function(e) { c.open($(target).value);(c, target)} );
    return c;
}

window.onload = function() {
    //Cal._setup('cal_1', 'lalala', 'ola', '', '%d.%m.%Y %H:%M');
    //Cal._setup('cal_2', 'lalala1', 'ola1', '', '%d.%m.%Y %H:%M');    
}

