		Impleo = {};
		Impleo.Calendar = Class.create();
		
		// Class Settings
		Impleo.Calendar.firstDayOfWeek = 1;

		// Class Members
		Impleo.Calendar.DateRange = Class.create({
			beginDate: null,
			endDate: null,
			initialize: function(beginDate, endDate){
				var today = Date.today();
				if(beginDate == 0){
					beginDate = new Date(0);
					beginDate.setDate(1);
					beginDate.setDate(-(beginDate.getDay()) + 1 + Impleo.Calendar.firstDayOfWeek)
				}
				this.beginDate = new Date(beginDate);
				this.endDate = new Date(endDate);
			},
			contains: function(date){
				return date.between(this.beginDate, this.endDate)
			}
		});
		// Class methods
		
		Impleo.Calendar.isDateDisabled = function(calendar, date){
			var disabled = false;
			calendar.disabledDates.each(function(item){
				disabled = item.contains(date.clearTime());
			});
			return disabled;
		};
        Impleo.Calendar.onDateSelection = function(event) {
            //this.calendar.source.value = this.date;
			this.calendar.setSelectedDate(this.date);
            this.calendar.update(this.calendar.getSelectedDate());
			//this.calendar.addDisabledDateRange(new Impleo.Calendar.DateRange(0, this.calendar.getSelectedDate()));
			//alert(this.calendar.container.id);
			if(this.calendar.inline){
				this.calendar.hide();
			}

			if(this.calendar.onDateSelection){
				this.calendar.onDateSelection(this.calendar);
			}
        };
		Impleo.Calendar.browse = function(calendar, addMonths){
			var date = calendar.getVisibleDate();
			date.setDate(1);
			date.setMonth(date.getMonth() + addMonths);
			calendar.setVisibleDate(date);
			calendar.update(date);
		};
		Impleo.Calendar.browseTo = function(calendar, date){
			debug("Impleo.Calendar.browseTo: " + calendar.container.id);
			date.setDate(1);
			date.setMonth(date.getMonth());
			calendar.setVisibleDate(date);
			calendar.update(calendar.getVisibleDate());
		};
		
		Impleo.Calendar.getMonthSelector = function(calendar){
			var selector;
			//calendar.container.select(".monthSelector").each(function(item){
																		
			calendar.container.select(".monthSelector").each(function(item){
				selector = item;
			});
										

			return selector;
		};
		Impleo.Calendar.getYearSelector = function(calendar){
			var selector;
			calendar.container.select(".yearSelector").each(function(item){
				selector = item;
			});
			return selector;
		};
		Impleo.Calendar.disable = function(calendar){
			//calendar.container.firstChild.blindUp() //style.display = "none";
			calendar.container.fade({from:1.0, to:0.3}) //style.display = "none";
			calendar.container.select(".day").each(function(item) {
				item.stopObserving("mouseup");
				item.addClassName("cursorAuto");
			});
			calendar.getMonthSelector().disable();
			calendar.getYearSelector().disable();
			if(calendar.onDisable){
				calendar.onDisable(this.calendar);
			}
		};
		Impleo.Calendar.enable = function(calendar){
			//calendar.container.firstChild.blindDown() //style.display = "block";
			calendar.container.fade({from:0.3, to:1.0}) //style.display = "none";
			calendar.container.select(".day").each(function(item) {
				item.removeClassName("cursorAuto");
			});
			calendar.getMonthSelector().enable();
			calendar.getYearSelector().enable();
			calendar.update(calendar.getSelectedDate());
			if(calendar.onEnable){
				calendar.onEnable(this.calendar);
			}
		};


        Impleo.Calendar.update = function(calendar, date) {
			var selectedDate = calendar.getSelectedDate();
			while(calendar.isDateDisabled(selectedDate)){
				calendar.setSelectedDate(selectedDate.addDays(1));
			};
            var year = date.getFullYear();
            var month = date.getMonth();
            var day = date.getDate();

			date.setDate(1)
            date.setDate(-(date.getDay()) + 1 + Impleo.Calendar.firstDayOfWeek)

//			calendar.container.select(".monthName").each(function(item){
//				item.update(Date.CultureInfo.monthNames[month] + " " + year);
//			});
			Impleo.Calendar.getMonthSelector(calendar).selectedIndex = month;
			Impleo.Calendar.getYearSelector(calendar).selectedIndex = year - new Date().getFullYear();
			
            calendar.container.select(".day").each(function(item) {
			    if(calendar.isDateDisabled(date)){
					item.addClassName("disabled")
					item.stopObserving("mouseup");
			    }else{
					item.removeClassName("disabled")
                	item.observe("mouseup", Impleo.Calendar.onDateSelection);
				};
			   
                if (date.getMonth() != month) {
                    item.addClassName("adjacentMonth");
                } else {
                    item.removeClassName("adjacentMonth");
                }
                if (date.getDay() == Impleo.Calendar.firstDayOfWeek) {
                    item.previous(".weekNumber").update("v" + date.getWeekOfYear());
                }
                item.date = new Date(date);
                if (item.date.setHours(0, 0, 0, 0) == selectedDate.setHours(0, 0, 0, 0)) {
                    item.addClassName("selected");
                } else {
                    item.removeClassName("selected");
                }
                item.calendar = calendar;
                item.update(date.getDate());
                date.setDate(date.getDate() + 1)
            });
        };
        
        Impleo.Calendar.template = function() {
			var o = new Element("div");
            var el = new Element("div", {"class":"calendar"});
            el.addClassName("days");

			Element.insert(el, new Element("div", {"class":"previousMonth"}).update("&lt;"));
		 	Element.insert(el, new Element("div", {"class":"nextMonth"}).update("&gt;"));
		 	sel = new Element("div", {"class":"selectMonth", "style":"text-align:center;margin-bottom:4px;"});
			
			var mSel = new Element("select", {"class":"monthSelector", "tabIndex":-1});
			Date.CultureInfo.monthNames.each(function(item, index){
				Element.insert(mSel, new Element("option", {"value":index}).update(item));										  
			});
			Element.insert(sel, mSel);
			
			var ySel = new Element("select", {"class":"yearSelector", "tabIndex":-1});
			var yDate = new Date();
			Element.insert(ySel, new Element("option").update(yDate.getFullYear()));										  
			Element.insert(ySel, new Element("option").update(yDate.addYears(1).getFullYear()));										  
			Element.insert(ySel, new Element("option").update(yDate.addYears(1).getFullYear()));										  
			Element.insert(sel, ySel);
			
			Element.insert(el, sel);
			

            var weekDayNames = new Element("div", { "class": "week" });
			for (var weekDays = -1; weekDays < 7; weekDays++){
				var weekDay = new Element("div", {"class": "weekDayName"});
				if(weekDays >= 0){
					var dayIndex = weekDays + Impleo.Calendar.firstDayOfWeek;
					if(dayIndex > 6 ){
						dayIndex = 0;	
					}
					weekDay.update(Date.CultureInfo.abbreviatedDayNames[dayIndex]);
				}
				Element.insert(weekDayNames, weekDay);
			}
			Element.insert(el, weekDayNames);

            for (var w = 0; w < 6; w++) {
                var week = new Element("div", { "class": "week" });
                var weekNumber = new Element("div", { "class": "weekNumber" });
                Element.insert(week, weekNumber);
                for (var d = 0; d < 7; d++) {
                    var day = new Element("div", { "class": "day" });
                    Element.insert(week, day);
                }
                Element.insert(el, week);
            };
			Element.insert(o, el)
            return (o.innerHTML);
        };
		
		// Instance properties and methods
        Impleo.Calendar.prototype = {
            container: null,
			inline: null,
            source: null,
			visibleDate: null,
			disabledDates: [],
			hideTimeout: null,
			visible: false,
			hideAfter: function(ms){
				this.hideTimeout = setTimeout(this.hide.bind(this), ms);
			},
			hide: function(){
				this.container.hide();
				this.visible = false;
			},
			show: function(){
				this.container.show();
				this.visible = true;
			},
            template: function() {
                return Impleo.Calendar.template();
            },
            update: function(date) {
                Impleo.Calendar.update(this, date);
            },
            getSelectedDate: function() {
                //return new Date(this.source.value);
				if(Date.parse(this.source.value)){
					return Date.parse(this.source.value);
				}else{
					return new Date();
				}
            },
            setSelectedDate: function(date) {
                this.source.value = date.toString('yyyy-MM-dd');
            },
			getVisibleDate: function(){
                return new Date(this.visibleDate);
			},
			setVisibleDate: function(date){
                this.visibleDate = new Date(date);
			},
			disable: function(){
				Impleo.Calendar.disable(this);
			},
			enable: function(){
				Impleo.Calendar.enable(this);
			},
			getMonthSelector : function(){
				return Impleo.Calendar.getMonthSelector(this);
			},
			getYearSelector : function(){
				return Impleo.Calendar.getYearSelector(this);
			},
            initialize: function(settings) {
				var calendar = this;
                if (settings) {
                    if (settings.container) this.container = $(settings.container);
                    if (settings.source) this.source = $(settings.source);
                    if (settings.template) this.template = settings.template;
					if (settings.onDateSelection) this.onDateSelection = settings.onDateSelection;
					if (settings.onEnable) this.onEnable = settings.onEnable;
					if (settings.onDisable) this.onDisable = settings.onDisable;
					if (settings.disabledDates) this.disabledDates = settings.disabledDates;
					if (settings.inline){
						this.inline = true;
						this.hide();
						this.container.style.position = "absolute";
					};
                };
                Element.insert(this.container, this.template());
				this.visibleDate = this.getSelectedDate();

				this.container.select(".previousMonth").each(function(item){
					item.observe("mouseup", function(){
						Impleo.Calendar.browse(calendar, -1);	
					});
				});
				this.container.select(".nextMonth").each(function(item){
					item.observe("mouseup", function(){
						Impleo.Calendar.browse(calendar, 1);								 
					});
				});
				
				if(this.inline){
					this.source.observe('change', function(){
						if(Date.parse(this.getValue())){
							Impleo.Calendar.update(calendar, Date.parse(this.getValue()));				
						}
					});
					this.source.observe('focus', function(){
						calendar.container.style.left = this.viewportOffset().left-5;
						calendar.container.style.top = this.viewportOffset().top+this.getHeight()-1;
						calendar.show();										
					});
					this.source.observe('blur', function(event){
						calendar.hideAfter(200);
					});
					document.observe('click', function(event){
						var element = event.findElement(".calendar");
						if(element != undefined){
							clearTimeout(calendar.hideTimeout);
						};
					});
				}
				
				this.getMonthSelector().observe("change", function(){
				   Impleo.Calendar.browseTo(calendar, new Date(Impleo.Calendar.getYearSelector(calendar).getValue(), this.getValue(), 1));
				});
				Impleo.Calendar.getYearSelector(calendar).observe("change", function(){
				   Impleo.Calendar.browseTo(calendar, new Date(this.getValue(), Impleo.Calendar.getMonthSelector(calendar).getValue(), 1));
				});

				//this.addDisabledDateRange(new Impleo.Calendar.DateRange(0, Date.today()));
                this.update(this.getSelectedDate());
				debug(this.container.id + ": disabledDates.length: " + this.disabledDates.length);
			},
			addDisabledDateRange: function(dateRange){
				debug("addDisabledDateRange to: " + this.container.id + ", length before: " + this.disabledDates.length);
				this.disabledDates.push(dateRange);
                this.update(this.getSelectedDate());
				debug("addDisabledDateRange to: " + this.container.id + ", length after: " + this.disabledDates.length);
			},
			isDateDisabled : function(date){
				return Impleo.Calendar.isDateDisabled(this, date);
			},
			onDateSelection : null,
			onDisable : null,
			onEnable : null
        };
		
		
		function debug(row){
			//$("debug").setValue($("debug").getValue() + "\n" + row);
		}

