/*******************************************************
 * DmCalendar 1.1 - Календарь и поле ввода даты
 *                  (JavaScript версия)
 *
 * Copyright (c) 2008, Dema (Dema.ru)
 * Лицензия LGPL для любого использования
 *
 *******************************************************/
 (function($){
	$.fn.extend({
				DmCalendar: function(onDateSelected)
				{
					return this.each(function(){
						if($.DmCalendar.fields[this.tagName.toUpperCase()])
							DmDateField(this, onDateSelected);
						else
							this.calendar	= new DmCalendar(this, onDateSelected);
					});
				}
			});
	// Объект настройки календаря
	$.DmCalendar				= 
	{
		// Версия
		version:		1.1,
		// Символ для форматирования даты
		joindate:		'.',
		// Дни недели
		dayofweek:		[ 'пн', 'вт', 'ср', 'чт', 'пт', 'сб', 'вс' ],
		// Месяцы
		monthname:		[ 'январь', 'февраль', 'март', 'апрель', 'май', 'июнь', 'июль', 'август', 'сентябрь', 'октябрь', 'ноябрь', 'декабрь' ],
		// Выходные дни
		dayoffwork:		[ true, false, false, false, false, false, true],
		// Какие тэги являются полями ввода
		fields:			{ 'INPUT': true, 'TEXTAREA': true },
		// Календарь перерисовывается при перемещении мышки по календарю или только по клику
		changeonover:	false
	};
			
	// Объект, отвечающий за функционирование экземпляра календаря
	function DmCalendar(container, onDateSelected, mousefocus)
	{
		// Меремещение мышой переводит фокус на календарь
		var mousefocus			= mousefocus==null?true:mousefocus;
		var now;									// Сегодня
		var sd;										// Активированная дата
		var cd;										// Выбранная дата
		var alle				= {};				// Словарь элементов
		var de					= [];				// Массив элементов дней
		var me					= [];				// Массив элементов месяцев
		var ye					= [];				// Массив элементов годов
		var ne					= null;				// Элемент "сегодня"
		var ce					= null;				// Элемент "выбрано"
		var calendar			= null;				// Элемент календаря

		var handlers			=					// Набор обработчиков событий
		{
			ondateselected:		onDateSelected		// Обработчик события выбора даты
		};
		
		function init(container)
		{
			// Инициализируем текущей датой
			now					= cDate(new Date());
			sd					= cDate(now);
			cd					= cDate(now);
			// Инициализация контейнера
			container			= $(container)
									.html(getHTML())
									.click(function(){ return false; });
			calendar			= $('#calendar', container)
									.keydown(onKey)
									.focus(onFocus)
									.blur(onBlur)
									.attr('tabIndex', 0)
									.mouseover($.DmCalendar.changeonover?onCOver:onCOverF)
									.mouseout(onCOut)
									.click(onCClick);
			// Инициализация элемента "сегодня"
			ne					= $('#now', container)
									.attr('colSpan', 5);
			alle['now']			= ne;
			// Инициализация элемента "Выбрано"
			ce					= $('#cur', container)
									.attr('colSpan', 4);
			alle['cur']			= ce;
			// Инициализация дней недели
			$('td[id=t]', container).each(function(i){
				$(this)
					.attr('className', i>=5?"t h":"t")
					.text($.DmCalendar.dayofweek[i]);
			});
			// Инициализация месяцев
			$('td[id=m]', container).each(function(i){
				me[i]			= $(this)
									.text($.DmCalendar.monthname[i])
									.attr('id', 'm'+i);
				me[i].month		= i;
				alle['m'+i]		= me[i];
			});
			// Инициализация годов
			$('td[id=y]', container).each(function(i){
				ye[i]			= $(this)
									.attr('rowSpan', (i==4?3:(i==3 || i==5?2:1)))
									.attr('id', 'y'+i);
				alle['y'+i]		= ye[i];
			});
			// Инициализация дат
			$('td[id=d]', container).each(function(i){
				de[i]			= $(this)
									.attr('rowSpan', 2)
									.attr('id', 'd'+i);
				de[i].itday		= true;
				alle['d'+i]		= de[i];
			});
			// Показываем текущую дату
			showYear();
			showMonth();
			showDate();
		}
		// Отображение столбца годов
		function showYear()
		{
			$.each(ye, showYearItem);
		}
		function showYearItem(i)
		{
			var y				= cd.getFullYear() - 4 + i;
			var cn				= cY[y==sd.getFullYear()?'y':'n'];
			if(this.cn!=cn)
			{
				this.cn			= cn;
				this.attr('className', cn);
			}
			if(this.year!=y)
			{
				this.year		= y;
				this.text(y);
			}
		}
		// Отображение столбца месяцев
		function showMonth()
		{
			$.each(me, showMonthItem);
		}
		function showMonthItem(i)
		{
			var cn				= cM[i==sd.getMonth()?'y':'n'];
			if(this.cn!=cn)
			{
				this.cn			= cn;
				this.attr('className', cn);
			}
		}
		// Отображение дней
		function showDate(fullredraw)
		{
			fullredraw			= fullredraw!=false;
			var _d				= fullredraw?sd:cd;
			var d				= cDate(_d.getFullYear(), _d.getMonth(), 1);
			var edo				= cDate(sd.getFullYear(), sd.getMonth(), sd.getDate());
			var cn;
			d.setDate(d.getDate()-(d.getDay()>0?d.getDay()-1:6));
			$.each(de, function(){
				cn				= cD[isHDate(d)][isCMDate(_d, d)][isSDate(edo, d)];
				if(this.cn!=cn)
				{
					this.attr('className', cn);
					this.cn		= cn;
				}
				if(fullredraw)
					setValues(this, d, d.getDate());
				d.setDate(d.getDate()+1);
			});
			setValues(ne, now, 'today: '+fDate(now));
			setValues(ce, edo, 'chosen: '+fDate(edo));
		}
		// Обработчики мыши
		function onCOver(e)		{ var el = alle[e.target.id]; if(el==null) return; selectDate(el.year, el.month, el.date, false, el.itday==true); if(mousefocus) calendar.focus(); return false; }
		function onCOverF(e)	{ if(mousefocus) calendar.focus(); return false; }
		function onCOut(e)		{ var el = alle[e.target.id]; if(el==null) return; selectDate(null, null, null, false, el.itday==true); return false; }
		function onCClick(e)	{ var el = alle[e.target.id]; if(el==null) return; selectDate(el.year, el.month, el.date, true, false); if(mousefocus) calendar.focus(); return false; }
		// Обработчик клавиатуры
		function onKey(event)
		{
			var d, m, y;
			switch(event.keyCode)
			{
				case 37: // влево
					if(event.shiftKey)
						y		= cd.getFullYear()-1;
					else
						d		= cd.getDate()-1;
					break;
				case 38: // вверх
					if(event.shiftKey)
						m		= cd.getMonth()-1;
					else
						d		= cd.getDate()-7;
					break;
				case 39: // вправо
					if(event.shiftKey)
						y		= cd.getFullYear()+1;
					else
						d		= cd.getDate()+1;
					break;
				case 40: // вниз
					if(event.shiftKey)
						m		= cd.getMonth()+1;
					else
						d		= cd.getDate()+7;
					break;
				case 13: // Enter - подтвердить выбор
					y			= sd.getFullYear();
					m			= sd.getMonth();
					d			= sd.getDate();
					break;
				case 36: // Home - показать текущую дату
					y			= now.getFullYear();
					m			= now.getMonth();
					d			= now.getDate();
					break;
			}
			if(y!=null || m!=null || d!=null)
			{
				selectDate(y, m, d, true, false, y==null && m==null && d!=null);
				return false;
			}
		}
		// Обработчик фокуса
		function onFocus()
		{
			calendar.addClass('focus');
		}
		function onBlur(e)
		{
			calendar.removeClass('focus');
		}
		// Выбрать дату и зафиксировать выбор, если это нужно
		function selectDate(y, m, d, fix, onlyd, nofin)
		{
			nofin				= nofin==null?false:nofin;
			var ny				= y==null || isNaN(y);
			var nm				= m==null || isNaN(m);
			var nd				= d==null || isNaN(d);
			var fin				= false;
			if(onlyd)
				fix				= false;
			var od				= fix?cd:sd;
			sd					= cDate(
									ny?cd.getFullYear():y,
									nm?cd.getMonth():m, 
									nd?cd.getDate():d
								   );
			if(fix)
				cd				= cDate(sd);
			nd					= od.getDate()    !=sd.getDate();
			ny					= od.getFullYear()!=sd.getFullYear();
			nm					= od.getMonth()   !=sd.getMonth();
			if(!onlyd)
			{
				fin				= fix && !ny && !nm/* && !nd*/ && !nofin; // Делаем так, что бы на дате кликать один раз, а стрелки переводили бы нормально
				if(ny)	showYear();
				if(nm)	showMonth();
			}
			if(nd || ny || nm)	showDate(!onlyd);
			fireDateSelected(sd, fin?2:fix?1:0, nd, nm, ny);
		}
		function fireDateSelected(d, sel, cd, cm, cy)
		{
			if(handlers.ondateselected!=null)
				handlers.ondateselected(d.getDate(), d.getMonth()+1, d.getFullYear(), sel, cd, cm, cy, fDate(d));
		}
		// Установить дату
		this.setDate			= function(y, m, d)
		{
			if(m==null && d==null)
			{
				m				= y.getMonth();
				d				= y.getDate();
				y				= y.getFullYear();
			}
			sd					= cDate(y, m, d);
			cd					= cDate(y, m, d);
			showYear();
			showMonth();
			showDate();
		};
		// Получить дату
		this.getDate			= function()
		{
			return cd;
		};
		// Получить текстовое представление даты
		this.getDateStr			= function()
		{
			return fDate(cd);
		};
		// Регистрация обработчиков событий
		this.setHandler			= function(newhandler, eventname)
		{
			eventname			= eventname?eventname:"ondateselected";
			handlers[eventname]	= newhandler;
		};
		// Поехали!
		init(container);
	}
	
	// Функция для инициализации поля ввода даты - можно посадить на любое поле ввода
	function DmDateField(input, onDateChanged)
	{
		var data	= {
						// Запоминаем контрол
						field:			$(input),
						// Плавающий div
						popup:			null,
						// Календарь
						calendar:		null,
						// Флаг показа фрейма
						visible:		false,
						// Обработчик изменения данных
						cOnDateChanged:	onDateChanged
					  };
		// Последнее введенное значение
		data.lastvalue			= data.field.val();
		// Старое зафиксированное значение
		data.oldvalue			= data.lastvalue;
		// Обработчик события изменения даты в календаре
		data.onDateChanged		= function(d, m, y, sel, cd, cm, cy, ds)
		{
			if(sel==2)
			{
				fieldVal(data, ds);
				hidePopup(data);
			}
			if(data.cOnDateChanged!=null)
				data.cOnDateChanged(d, m, y, sel, cd, cm, cy, ds);
		};
		// Обработчики кнопок
		data.field.keydown(function(event)
		{
			return onKeyDown(event, data);
		}).keyup(function(event)
		{
			return onKeyUp(event, data);
		});
		data.field[0].show		= function()
		{
			showPopup(data);
		};
		
		$(function(){
			$('#' + $(input).attr("id") + 'Link').click(function(){
				imgClick = true;
				showPopup(data);
			});
		});
		
		// На всякий случай
		return data;
	}

	//=== Служебные (PRIVATE) методы и поля ===
	var DateFieldRE				= /([+-]?)(\d+)([^\d]+(\d+)([^\d]+(\d+))?)?/;
	// Кэш html календаря
	var html					= null;
	// CSS классы для месяцев
	var cM						= { 'y': 'y s', 'n': 'y'};
	// CSS классы для годов
	var cY						= { 'y': 'm s', 'n': 'm'};
	// CSS классы для дней
	//  = cD[isHDate][isCMDate][isSDate]
	var cD						= { 'y':{ 'y':{ 'y': 'd h s', 'n': 'd h' }, 'n':{ 'y': 'd h nc s', 'n': 'd h nc' } },
									'n':{ 'y':{ 'y': 'd s',   'n': 'd'   }, 'n':{ 'y': 'd nc s',   'n': 'd nc'   } }
								  };
	// Проверка, относится ли дата к текущему выбранному месяцу
	function isCMDate(sdo, d)	{ return sdo.getMonth()==d.getMonth() && sdo.getFullYear()==d.getFullYear()?'y':'n'; }
	// Проверка, является ли дата выходным днем
	function isHDate(d)			{ return $.DmCalendar.dayoffwork[d.getDay()]?'y':'n'; }
	// Проверка, является ли дата выбранной
	function isSDate(edo, d)	{ return edo.getTime()==d.getTime()?'y':'n'; }
	// Скопировать дату из объекта в элемент
	function setValues(el, d, txt)
	{
		el.date					= d.getDate();
		el.month				= d.getMonth();
		el.year					= d.getFullYear();
		if(txt!=null)
			el.text(txt);
	}
	// Форматирование даты
	function fDate(y, m, d)
	{
		if(m==null && d==null)
		{
			m					= y.getMonth();
			d					= y.getDate();
			y					= y.getFullYear();
		}
		m++;
		return [(d<10?'0'+d:d), (m<10?'0'+m:m), y].join($.DmCalendar.joindate);
	}
	// Создание/копирование объекта даты
	function cDate(y, m, d)
	{
		if(m==null && d==null)
			return new Date(y.getFullYear(), y.getMonth(), y.getDate(), 0, 0, 0, 0);
		return new Date(y, m, d, 0, 0, 0, 0);
	}
	// Получить HTML календаря
	function getHTML()
	{
		if(html==null)
			html				= '<table border="0" cellpadding="1" cellspacing="0" id="calendar" class="t">$1$3<td id="ph">&nbsp;</td>$4$a$4$a$a$7$6$9$4$7$6$9$4$a$4$2$1<td id="now"></td><td id="cur"></td>$2</table>'
									.replace(/\$a/g, '$8$9')
									.replace(/\$9/g, '$2$1$6')
									.replace(/\$8/g, '$7$6$4')
									.replace(/\$7/g, '$2$1$5')
									.replace(/\$3/g, '$3$3$3$3$3$3$3')
									.replace(/\$5/g, '$5$5$5$5$5$5$5')
									.replace(/\$1/g, '<tr>')
									.replace(/\$2/g, '</tr>')
									.replace(/\$3/g, '<td id="t"></td>')
									.replace(/\$4/g, '<td id="y"></td>')
									.replace(/\$5/g, '<td id="d"></td>')
									.replace(/\$6/g, '<td id="m"></td>');
		return html;
	}
	
	// Экземпляр календаря у нас будет один для всех элементов на странице, это - контейнер
	var popup					= null;
	// Это сам календарь
	var calendar				= null;
	// Текущий активный элемент
	var active					= null;
	
	var imgClick 			= false;
	// При клике за пределами календаря - скрываем текущий
	$(document).click(function(){
		if(!imgClick) hidePopup(active);
		imgClick = false;
	});
	
	// Обработка изменения страниц для пересчета положения календаря
	$(window).scroll(correctPos).resize(correctPos);
	$(document).scroll(correctPos);
	// Показать фрейм
	function showPopup(data)
	{
		if(data.visible)
			return;
		// Скрываем предыдущий
		if(active!=null)
			hidePopup(active);
		// Создаем контейнер и календарь, если это нужно
		if(popup==null)
		{
			popup				= $('<div style="position:absolute;display:none;" class="calendarpopup"></div>');
			$(document.body).append(popup);
			calendar			= new DmCalendar(popup, null, false);
			if(popup.bgiframe)
				popup.bgiframe();
		}
		// Регистрируем обработчик
		calendar.setHandler(data.onDateChanged);
		data.visible			= true;
		// Вычисляем положение
		setPos(data);
		// Показываем
		popup.slideDown('fast');
		active					= data;
		ValueToCalendar(data);
	}
	// Обработчик событий изменения окна
	function correctPos()
	{
		setPos(active);
	}
	// Вычисление и позиционирование календаря
	function setPos(data)
	{
		if(data==null || !data.visible)
			return;
		var off	= data.field.offset();
		var widthCorr = popup.width() - data.field.width();
		popup.css({ top: off.top + data.field.outerHeight(), left: off.left - widthCorr});
	}
	// Спрятать фрейм
	function hidePopup(data)
	{
		if(data==null || !data.visible || popup==null)
			return;
		popup.fadeOut('fast');
		data.visible			= false;
		if(active==data)
		{
			active				= null;
			calendar.setHandler(null);
		}
	}
	// Установить новое значение или получить текущее
	function fieldVal(data, newvalue)
	{
		if(newvalue==null)
			return data.field.val();
		data.field.val(data.oldvalue=data.lastvalue=newvalue);
	}
	// Обработчик кнопок
	function onKeyDown(event, data)
	{
		if(event.keyCode==13 || event.keyCode==9)
		{
			var vis				= data.visible;
			hidePopup(data);
			if(event.keyCode==9 && fieldVal(data)=='')
			{
				data.oldvalue	= data.lastvalue = '';
				return true;
			}
			if(vis)
				fieldVal(data, calendar.getDateStr());
			if(event.keyCode==13)
			{
				event.cancelBubble	= true;
				return false;
			}
		}
	}
	// Обработка введенного значения
	function onKeyUp(event, data)
	{
		if(event.keyCode==13 || event.keyCode==9)
		{
			if(data.cOnDateChanged!=null)
			{
				var d			= calendar.getDate();
				fieldVal(data, calendar.getDateStr());
				data.cOnDateChanged(d.getDate(), d.getMonth()+1, d.getFullYear(), 2, false, false, false, fDate(d));
			}
			return false;
		}
		if(event.keyCode==27)
		{
			fieldVal(data, data.lastvalue);
			hidePopup(data);
			return false;
		}
		return ValueToCalendar(data);
	}
	// Обработка введенного пользователем значения
	function ValueToCalendar(data)
	{
		var v					= data.field.val();
		if(v==data.oldvalue)
			return;
		data.oldvalue			= v;
		v						= v.match(DateFieldRE);
		var d					= {};
		if(v!=null)
		{
			d.rd				= v[1]=='-'?-1:v[1]=='+'?1:null;
			d.d					= parseInt(v[2], 10);
			d.m					= parseInt(v[4], 10);
			d.y					= parseInt(v[6], 10);
		}
		if(d.rd==null && !isNaN(d.y))
		{
			if(0<=d.y && d.y<=32)
				d.y				= 2000+d.y;
			else if(d.y<=99)
				d.y				= 1900+d.y;
			else if(d.y<1000)
				d.y				= d.y*10;
		}
		var od					= new Date();
		if(!isNaN(d.y)) od.setYear(d.rd!=null?od.getFullYear()+d.rd*d.y:d.y);
		if(!isNaN(d.m)) od.setMonth(d.rd!=null?od.getMonth()+d.rd*d.m:(d.m-1));
		if(!isNaN(d.d)) od.setDate(d.rd!=null?od.getDate()+d.rd*d.d:d.d);
		showPopup(data);
		if(active==data)
			calendar.setDate(od);
		if(data.cOnDateChanged!=null)
			data.cOnDateChanged(od.getDate(), od.getMonth()+1, od.getFullYear(), 0, true, true, true, fDate(od));
		return false;
	}
})(jQuery);

/* ~T1~
    $Date: 2010-02-05 18:45:46 +0300 (Fri, 05 Feb 2010) $
$Revision: 296 $
  $Author: Dema $
 $HeadURL: https://localhost:8443/svn/Personal/trunk/Dema.ru/wwwroot/_static/script/jquery.dmcalendar-1.1.js $
*/
