/** * @license                                      * jQuery Tools @VERSION Dateinput - <input type="date" /> for humans *  * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE. *  * http://flowplayer.org/tools/form/dateinput/ * * Since: Mar 2010 * Date: @DATE  */(function($) {		/* TODO: 		 preserve today highlighted	*/	$.tools = $.tools || {version: '@VERSION'};	var instances = [], 		 tool, 		 // h=72, j=74, k=75, l=76, down=40, left=37, up=38, right=39		 KEYS = [75, 76, 38, 39, 74, 72, 40, 37],		 LABELS = {};	tool = $.tools.dateinput = {		conf: { 			format: 'mm/dd/yy',			selectors: false,			yearRange: [-5, 5],			lang: 'en',			offset: [0, 0],			speed: 0,			firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...			min: undefined,			max: undefined,			trigger: false,			css: {				prefix: 'cal',				input: 'date',				// ids				root: 0,				head: 0,				title: 0, 				prev: 0,				next: 0,				month: 0,				year: 0, 				days: 0,				body: 0,				weeks: 0,				today: 0,						current: 0,				// classnames				week: 0, 				off: 0,				sunday: 0,				focus: 0,				disabled: 0,				trigger: 0			}  		},		localize: function(language, labels) {			$.each(labels, function(key, val) {				labels[key] = val.split(",");					});			LABELS[language] = labels;			}	};	tool.localize("en", {		months: 		 'January,February,March,April,May,June,July,August,September,October,November,December', 		shortMonths: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec',  		days: 		 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday', 		shortDays: 	 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'	  	});//{{{ private functions	// @return amount of days in certain month	function dayAm(year, month) {		return 32 - new Date(year, month, 32).getDate();			} 	function zeropad(val, len) {		val = '' + val;		len = len || 2;		while (val.length < len) { val = "0" + val; }		return val;	}  	// thanks: http://stevenlevithan.com/assets/misc/date.format.js 	var Re = /d{1,4}|m{1,4}|yy(?:yy)?|"[^"]*"|'[^']*'/g, tmpTag = $("<a/>");	function format(date, fmt, lang) {	  var d = date.getDate(),			D = date.getDay(),			m = date.getMonth(),			y = date.getFullYear(),			flags = {				d:    d,				dd:   zeropad(d),				ddd:  LABELS[lang].shortDays[D],				dddd: LABELS[lang].days[D],				m:    m + 1,				mm:   zeropad(m + 1),				mmm:  LABELS[lang].shortMonths[m],				mmmm: LABELS[lang].months[m],				yy:   String(y).slice(2),				yyyy: y			};		var ret = fmt.replace(Re, function ($0) {			return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);		});		// a small trick to handle special characters		return tmpTag.html(ret).html();	}	function integer(val) {		return parseInt(val, 10);		} 	function isSameDay(d1, d2)  {		return d1.getFullYear() === d2.getFullYear() && 			d1.getMonth() == d2.getMonth() &&			d1.getDate() == d2.getDate(); 	}	function parseDate(val) {		if (!val) { return; }		if (val.constructor == Date) { return val; } 		if (typeof val == 'string') {			// rfc3339?			var els = val.split("-");					if (els.length == 3) {				return new Date(integer(els[0]), integer(els[1]) -1, integer(els[2]));			}				// invalid offset			if (!/^-?\d+$/.test(val)) { return; }			// convert to integer			val = integer(val);		}		var date = new Date();		date.setDate(date.getDate() + val);		return date; 	}//}}}	function Dateinput(input, conf)  { 		// variables		var self = this,  			 now = new Date(),			 css = conf.css,			 labels = LABELS[conf.lang],			 root = $("#" + css.root),			 title = root.find("#" + css.title),			 trigger,			 pm, nm, 			 currYear, currMonth, currDay,			 value = input.attr("data-value") || conf.value || input.val(), 			 min = input.attr("min") || conf.min,  			 max = input.attr("max") || conf.max,			 opened;		// zero min is not undefined 	 		if (min === 0) { min = "0"; }		// use sane values for value, min & max				value = parseDate(value) || now;		min   = parseDate(min || conf.yearRange[0] * 365);		max   = parseDate(max || conf.yearRange[1] * 365);		// check that language exists		if (!labels) { throw "Dateinput: invalid language: " + conf.lang; }		// Replace built-in date input: NOTE: input.attr("type", "text") throws exception by the browser		if (input.attr("type") == 'date') {			var tmp = $("<input/>");			$.each("class,disabled,id,maxlength,name,readonly,required,size,style,tabindex,title,value".split(","), function(i, attr)  {				tmp.attr(attr, input.attr(attr));					});						input.replaceWith(tmp);			input = tmp;		}		input.addClass(css.input);		var fire = input.add(self);		// construct layout		if (!root.length) {			// root			root = $('<div><div><a/><div/><a/></div><div><div/><div/></div></div>')				.hide().css({position: 'absolute'}).attr("id", css.root);						// elements			root.children()				.eq(0).attr("id", css.head).end() 				.eq(1).attr("id", css.body).children()					.eq(0).attr("id", css.days).end()					.eq(1).attr("id", css.weeks).end().end().end()				.find("a").eq(0).attr("id", css.prev).end().eq(1).attr("id", css.next);		 				  			// title			title = root.find("#" + css.head).find("div").attr("id", css.title);			// year & month selectors			if (conf.selectors) {								var monthSelector = $("<select/>").attr("id", css.month),					 yearSelector = $("<select/>").attr("id", css.year);								title.html(monthSelector.add(yearSelector));			}									// day titles			var days = root.find("#" + css.days); 			// days of the week			for (var d = 0; d < 7; d++) { 				days.append($("<span/>").text(labels.shortDays[(d + conf.firstDay) % 7]));			}			$("body").append(root);		}			// trigger icon		if (conf.trigger) {			trigger = $("<a/>").attr("href", "#").addClass(css.trigger).click(function(e)  {				self.show();				return e.preventDefault();			}).insertAfter(input);			}		// layout elements		var weeks = root.find("#" + css.weeks);		yearSelector = root.find("#" + css.year);		monthSelector = root.find("#" + css.month);//{{{ pick		function select(date, conf, e) {  			// current value			value 	 = date;			currYear  = date.getFullYear();			currMonth = date.getMonth();			currDay	 = date.getDate();							// change			e = e || $.Event("api");			e.type = "change";			fire.trigger(e, [date]); 			if (e.isDefaultPrevented()) { return; }			// formatting						input.val(format(date, conf.format, conf.lang));			// store value into input			input.data("date", date);			self.hide(e); 		}//}}}//{{{ onShow		function onShow(ev) {			ev.type = "onShow";			fire.trigger(ev);			$(document).bind("keydown.d", function(e) {				if (e.ctrlKey) { return true; }								var key = e.keyCode;			 				// backspace clears the value				if (key == 8) {					input.val("");					return self.hide(e);					}				// esc key				if (key == 27) { return self.hide(e); }										if ($(KEYS).index(key) >= 0) {					if (!opened) { 						self.show(e); 						return e.preventDefault();					} 					var days = $("#" + css.weeks + " a"), 						 el = $("." + css.focus),						 index = days.index(el);					el.removeClass(css.focus);					if (key == 74 || key == 40) { index += 7; }					else if (key == 75 || key == 38) { index -= 7; }												else if (key == 76 || key == 39) { index += 1; }					else if (key == 72 || key == 37) { index -= 1; }					if (index > 41) {						 self.addMonth();						 el = $("#" + css.weeks + " a:eq(" + (index-42) + ")");					} else if (index < 0) {						 self.addMonth(-1);						 el = $("#" + css.weeks + " a:eq(" + (index+42) + ")");					} else {						 el = days.eq(index);					}					el.addClass(css.focus);					return e.preventDefault();				}				// pageUp / pageDown				if (key == 34) { return self.addMonth(); }										if (key == 33) { return self.addMonth(-1); }				// home				if (key == 36) { return self.today(); } 				// enter				if (key == 13) {					if (!$(e.target).is("select")) {						$("." + css.focus).click(); 					} 				}				return $([16, 17, 18, 9]).index(key) >= 0;  							});			// click outside dateinput			$(document).bind("click.d", function(e) {									var el = e.target;				if (!$(el).parents("#" + css.root).length && el != input[0] && (!trigger || el != trigger[0])) {					self.hide(e);				}			}); 		}//}}}		$.extend(self, {//{{{  show			show: function(e) {				if (input.attr("readonly") || input.attr("disabled") || opened) { return; }				// onBeforeShow				e = e || $.Event();				e.type = "onBeforeShow";				fire.trigger(e);				if (e.isDefaultPrevented()) { return; }				$.each(instances, function() {					this.hide();					});				opened = true;				// month selector				monthSelector.unbind("change").change(function() {					self.setValue(yearSelector.val(), $(this).val());						});				// year selector				yearSelector.unbind("change").change(function() {					self.setValue($(this).val(), monthSelector.val());						});				// prev / next month				pm = root.find("#" + css.prev).unbind("click").click(function(e) {					if (!pm.hasClass(css.disabled)) {							self.addMonth(-1);					}					return false;				});				nm = root.find("#" + css.next).unbind("click").click(function(e) {					if (!nm.hasClass(css.disabled)) {						self.addMonth();					}					return false;				});	 				// set date				self.setValue(value);				 				// show calendar				var pos = input.offset();				// iPad position fix				if (/iPad/i.test(navigator.userAgent)) {					pos.top -= $(window).scrollTop();				}				root.css({ 					top: pos.top + input.outerHeight({margins: true}) + conf.offset[0], 					left: pos.left + conf.offset[1] 				});				if (conf.speed) {					root.show(conf.speed, function() {						onShow(e);								});					} else {					root.show();					onShow(e);				}				return self;			}, //}}}//{{{  setValue			setValue: function(year, month, day)  {				var date = integer(month) >= -1 ? new Date(integer(year), integer(month), integer(day || 1)) : 					year || value;								if (date < min) { date = min; }				else if (date > max) { date = max; }				year = date.getFullYear();				month = date.getMonth();				day = date.getDate(); 				// roll year & month				if (month == -1) {					month = 11;					year--;				} else if (month == 12) {					month = 0;					year++;				} 				if (!opened) { 					select(date, conf);					return self; 				} 								currMonth = month;				currYear = year;				// variables				var tmp = new Date(year, month, 1 - conf.firstDay), begin = tmp.getDay(),					 days = dayAm(year, month),					 prevDays = dayAm(year, month - 1),					 week;	 				// selectors				if (conf.selectors) { 					// month selector					monthSelector.empty();					$.each(labels.months, function(i, m) {											if (min < new Date(year, i + 1, -1) && max > new Date(year, i, 0)) {							monthSelector.append($("<option/>").html(m).attr("value", i));						}					});					// year selector					yearSelector.empty();							var yearNow = now.getFullYear();					for (var i = yearNow + conf.yearRange[0];  i < yearNow + conf.yearRange[1]; i++) {						if (min <= new Date(i + 1, -1, 1) && max > new Date(i, 0, 0)) {							yearSelector.append($("<option/>").text(i));						}					}							monthSelector.val(month);					yearSelector.val(year);				// title				} else {					title.html(labels.months[month] + " " + year);					} 	   				// populate weeks				weeks.empty();								pm.add(nm).removeClass(css.disabled); 				// !begin === "sunday"				for (var j = !begin ? -7 : 0, a, num; j < (!begin ? 35 : 42); j++) { 					a = $("<a/>");					if (j % 7 === 0) {						week = $("<div/>").addClass(css.week);						weeks.append(week);								}										if (j < begin)  { 						a.addClass(css.off); 						num = prevDays - begin + j + 1;						date = new Date(year, month-1, num);					} else if (j >= begin + days)  {						a.addClass(css.off);							num = j - days - begin + 1;						date = new Date(year, month+1, num);					} else  { 						num = j - begin + 1;						date = new Date(year, month, num);  						// current date						if (isSameDay(value, date)) {							a.attr("id", css.current).addClass(css.focus);						// today						} else if (isSameDay(now, date)) {							a.attr("id", css.today);						}	 					}					// disabled					if (min && date < min) {						a.add(pm).addClass(css.disabled);											}					if (max && date > max) {						a.add(nm).addClass(css.disabled);											}					a.attr("href", "#" + num).text(num).data("date", date);										week.append(a);				}				// date picking									weeks.find("a").click(function(e) {					var el = $(this); 					if (!el.hasClass(css.disabled)) {  						$("#" + css.current).removeAttr("id");						el.attr("id", css.current);	 						select(el.data("date"), conf, e);					}					return false;				});				// sunday				if (css.sunday) {					weeks.find(css.week).each(function() {						var beg = conf.firstDay ? 7 - conf.firstDay : 0;						$(this).children().slice(beg, beg + 1).addClass(css.sunday);							});					} 				return self;			}, 	//}}}			setMin: function(val, fit) {				min = parseDate(val);				if (fit && value < min) { self.setValue(min); }				return self;			},			setMax: function(val, fit) {				max = parseDate(val);				if (fit && value > max) { self.setValue(max); }				return self;			}, 			today: function() {				return self.setValue(now);				},			addDay: function(amount) {				return this.setValue(currYear, currMonth, currDay + (amount || 1));					},			addMonth: function(amount) {				return this.setValue(currYear, currMonth + (amount || 1), currDay);				},			addYear: function(amount) {				return this.setValue(currYear + (amount || 1), currMonth, currDay);				},			hide: function(e) {				 				if (opened) {  					// onHide 					e = $.Event();					e.type = "onHide";					fire.trigger(e);					$(document).unbind("click.d").unbind("keydown.d");					// cancelled ?					if (e.isDefaultPrevented()) { return; }					// do the hide					root.hide();					opened = false;				}				return self;			},			getConf: function() {				return conf;				},			getInput: function() {				return input;				},			getCalendar: function() {				return root;				},			getValue: function(dateFormat) {				return dateFormat ? format(value, dateFormat, conf.lang) : value;				},			isOpen: function() {				return opened;				}		}); 		// callbacks			$.each(['onBeforeShow','onShow','change','onHide'], function(i, name) {			// configuration			if ($.isFunction(conf[name]))  {				$(self).bind(name, conf[name]);				}			// API methods							self[name] = function(fn) {				if (fn) { $(self).bind(name, fn); }				return self;			};		});		// show dateinput & assign keyboard shortcuts		input.bind("focus click", self.show).keydown(function(e) {			var key = e.keyCode;			// open dateinput with navigation keyw			if (!opened &&  $(KEYS).index(key) >= 0) {				self.show(e);				return e.preventDefault();			} 			// allow tab			return e.shiftKey || e.ctrlKey || e.altKey || key == 9 ? true : e.preventDefault();   		}); 		// initial value 				if (parseDate(input.val())) { 			select(value, conf);		}	} 	$.expr[':'].date = function(el) {		var type = el.getAttribute("type");		return type && type == 'date' || !!$(el).data("dateinput");	};	$.fn.dateinput = function(conf) {   		// already instantiated		if (this.data("dateinput")) { return this; } 		// configuration		conf = $.extend(true, {}, tool.conf, conf);				// CSS prefix		$.each(conf.css, function(key, val) {			if (!val && key != 'prefix') { 				conf.css[key] = (conf.css.prefix || '') + (val || key);			}		});				var els;		this.each(function() {												var el = new Dateinput($(this), conf);			instances.push(el);			var input = el.getInput().data("dateinput", el);			els = els ? els.add(input) : input;			});				return els ? els : this;			}; }) (jQuery); 
