/*
           ,---------------------------------------------------------.
          /  Simple calendar displaying months and days               )
         /   Easy to integrate into Ajax style applications          /
        /                                                           /
       (            Ioan Sameli, 04.2007                           /
        `--\   /--------------------------------------------------´
           |  /
            \|
             `
           (")-''-(").___,.~-´'^`-._
            \O_ O  )   `-.  (     ).`-.__.´)
            (_Y_.)/  ._   )  `._ `. `´-..-´
          _,.`--´_,.-_/  /~-`_.´ ,´
         (il).-'´  (li).´  ((!.-´
         
             _________________________________________________
    ________|                                                 |_______
    \       |                                                 |      /
     \      |                                                 |     /
     /      |_________________________________________________|     \
    /__________)                                            (________\


	Exemple of integration:

	function demoCalendar() {
		
		// Create new calendar object
		var demoCal = new tsrCalendar;	
		demoCal.cTarget = 'calendar';
		
		// Action when a day is clicked
		demoCal.dateClick = function(d) {
			alert(d.title + ' clicked.');
		}
		
		// Function that will be called on each calendar display
		// Highlight randomly the days
		demoCal.onRender = function() {
			for (var i = 1; i < 31; i++) {demoCal.highLightDay(i+'-'+this.cMonth+'-'+this.cYear,'hl-'+((i+4)%5));}
			this.render();
		}
		
		// Finally display the calendar
		demoCal.onRender();
	}     
         
*/






  /************************/
 /*****   CALENDAR   *****/
/************************/

	// Arrays with the title week days and months.
	var daysTitle = [null, 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.', 'dim.'];
	var daysFullTitle = [null, 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi', 'Dimanche'];
	var monthsList = [null, 'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'];

	var tsrCalendar = function() {

		// by default, get the date of today.
		var cDate = new Date();
		this.cMonth = cDate.getMonth() + 1;
		this.cYear = cDate.getFullYear();
		
		// Render the calendar in this HTML element (by Id)
		this.cTarget = 'calendar';
		
		// The table cellspacing can be set with this property, default is 0
		this.cellSpacing = 0;

		// Will contain all the highlighted days and the title of the days
		this.highLightDays = {};
		this.titleDays = {};
		
		// The function called when a user clics on a day
		this.dateClick = function(d) {alert('Action not set !\n\n' + d);}
		
		// The function called when a user changes month/year
		this.onRender = function(){};

		// Call this to hightlight a day, second argument can specify a second class
		this.highLightDay = function(hDate, hlCLass) {
			if (typeof hlCLass == 'undefined') hlCLass = 1
			this.highLightDays[hDate] = hlCLass;
		};
		
		this.setDayTitle = function(tDate, dTitle) {
			this.titleDays[tDate] = dTitle;
		}

		// Removes all the highlighting by resetting the object
		this.removeHighlight = function() {
			this.highLightDays = {};
		};

		// Render the calendar, using DOM
		// It's long to create the structure with DOM, but the code is pretty simple...
		this.render = function() {

			// Create a closure of the current calendar, to use in onclick function
			var cClosure = this;

			// Functions alias, just to save bytes
			var ne = function(v){var l = document.createElement(v);l.ac=l.appendChild;return l;}
			var nt = function(v){return document.createTextNode(v)}
			
			// Element that contains the whole calendar
			var elCalendar = ne('div');
			
			// one row with month with links to next/previous month...
			var elDiv = ne('div');
			elDiv.className = 'nav';
			var elA = ne('a'); // Create a link to change month, if on first month, changes year
			elA.href = '#';
			elA.onclick = function() {
				if (cClosure.cMonth > 1) cClosure.cMonth--;
				else {
					cClosure.cMonth = 12;
					cClosure.cYear--;
				}
				cClosure.onRender();
				return false;
			};
			elA.className = 'prevMonth';
			elA.ac(nt('<<'));
			elDiv.ac(elA);

			var elA = ne('a'); // Same link but the other way
			elA.href = '#';
			elA.onclick = function() {
				if (cClosure.cMonth < 12) cClosure.cMonth++;
				else {
					cClosure.cMonth = 1;
					cClosure.cYear++;
				}
				cClosure.onRender();
				return false;
			};
			elA.className = 'nextMonth';
			elA.ac(nt('>>'));
			elDiv.ac(elA);


			var elStrong = ne('strong');
			elStrong.ac(nt(monthsList[this.cMonth] + ' ' + this.cYear));
			elDiv.ac(elStrong);		
			
			elCalendar.ac(elDiv);	
			
			// Create table
			var elTable = ne('table');
			elTable.cellSpacing = this.cellSpacing;
			
			// Calendar table header
			var elThead = ne('thead');

			// Columns header with days of week
			var elTr = ne('tr');
			elTr.className = 'days';
			for (var i = 1; i < 8; i++) {
				var elDiv = ne('td');
				elDiv.ac(nt(daysTitle[i]));
				elTr.ac(elDiv);
			}
			elThead.ac(elTr);
			
			// Finally, put the header in the table
			elTable.ac(elThead);
			
			// Use a date to know how many days are in the month and on with week day the month begins
			var cDate = new Date();
			cDate.setFullYear(this.cYear);
			cDate.setMonth(this.cMonth -1);
			cDate.setDate(1);
			var firstDayWeek = ((cDate.getDay() + 6) % 7);

			// Set a date at more than 31 days, the date will be next month, and by counting back 
			// the days it is possible to know the month length
			cDate.setDate(32);
			var lastDayMonth = 32 - cDate.getDate();

			var totalCells = lastDayMonth + firstDayWeek;

			// Trick to fill the last row of the table
			if (totalCells % 7) totalCells += 7 - (totalCells % 7);
			
			// The table won't display in MSIE unless the content is in a tbody...
			var elTbody = ne('tbody');

			// Big loop to build each cell of each row of the table
			for (var i = 1; i <= totalCells; i++) {

				var day = i - firstDayWeek;

				// Rows have 7 cells, one for each day. Create a new <tr> each 7 cells.
				if (i % 7 == 1) var elTr = ne('tr');				
				
				// If the cell is within the range of the days of the current month, display the day number
				var elTd = ne('td');
				if (i > firstDayWeek && day <= lastDayMonth) {
					
					var hl = this.highLightDays[day+'-'+this.cMonth+'-'+this.cYear];
					var dayTitle = this.titleDays[day+'-'+this.cMonth+'-'+this.cYear];
					var dateTitle = daysFullTitle[((i - 1) % 7) + 1]+' '+day+' '+monthsList[this.cMonth]+' '+this.cYear;
					
					var todayDate = new Date();
					var today = (day+'-'+this.cMonth+'-'+this.cYear) == (todayDate.getDate()+'-'+(todayDate.getMonth()+1)+'-'+todayDate.getFullYear());


					// Create the link with the action
					var elA = ne('a');					
					elA.title = dayTitle || dateTitle;
					elA.href = '#';
					elA.onclick = bindFunc(cClosure.dateClick,{
						'highlight': hl,
						'title':     elTd.title,
						'today':     today,
						'dateTitle': dateTitle,
						'dayTitle':  dayTitle,
						'day':       day,
						'month':     this.cMonth,
						'year':      this.cYear
					});
					elA.ac(nt(day));
					if (hl) elA.className = 'highlighted' + (hl == 1 ? '' : ' '+hl);
					if (today) elA.className += (elA.className ? ' ' : '') + 'today';

					elTd.ac(elA);
					
				}
				
				// First and last cells have special classes
				if (i % 7 == 1) elTd.className = 'first';
				if (i % 7 == 0) elTd.className = 'last';
				
				elTr.ac(elTd);

				// Each 7 cells, close the row
				if (i % 7 == 1) elTbody.ac(elTr);
			}
			
			// Add the body to the table
			elTable.ac(elTbody);
			elCalendar.ac(elTable);
			
			// And put the table in the page
			if ($(this.cTarget).firstChild) $(this.cTarget).replaceChild(elCalendar, $(this.cTarget).firstChild);
			else document.getElementById(this.cTarget).appendChild(elCalendar);

		}
	}
	
	// To avoid creating closure. I don't know if it's the best way, but it works.
	function bindFunc(f,r){return function() {return f(r)}}