/**
* Schedule Calendar, Ext.js extensions.
* Ext.ux.Schedule.Calendar
* version 0.9.1
*
* last updated 2009/05/15
* copyright info@plasmasphere.net http://www.plasmasphere.net/
*/

Ext.ux.ScheduleCalendar = Ext.extend(Ext.Component, {
	todayText: "Today",
	okText: "&#160;OK&#160;",
	cancelText: "Cancel",
	todayTip: "{0} (Spacebar)",
	minText: "This date is before the minimum date",
	maxText: "This date is after the maximum date",
	format: "m/d/y",
	disabledDaysText: "Disabled",
	disabledDatesText: "Disabled",
	constrainToViewport: true,
	monthNames: Date.monthNames,
	dayNames: Date.dayNames,
	nextText: "Next Month (Control+Right)",
	prevText: "Previous Month (Control+Left)",
	monthYearText: "Choose a month (Control+Up/Down to move years)",
	startDay: 0,
	showToday: false,

	displayCheckFomart: "Y/m/d", // calendar cell put, only
	displayTimeFormat: "H:i",
	queryFormat: "Y-m",
	scheduleFormat: "{startdate} {subject}",// schedule display format

	minWidth: 400,
	minHeight: 300,
	width: 400,
	height: 300,

	id: null,
	enableMouseWheele: false,
	showMonthPicker: false,
	cellScrollable: true, // cell autoscroll enable

	compactListing: false, // google calendar like
	compactFormat: "{0}",

	autoFit: false, // google calendar like, compornent width & height is fit parentnode width & height, use Viewport of parentnode, parentnode is body not running
	margins: "0 0 0 0",
	dataFormat: [ "sequencial", "subject", "startdate", "enddate" ], // data format, default set. enddate is not defined(...)
	mode: "remote", // default remote, remote || local if data[object] true
	queryName: "query", // get schedule query name

	loadMask: true,

	initComponent: function(){
		Ext.ux.ScheduleCalendar.superclass.initComponent.call(this);
		this.store = Ext.StoreMgr.lookup(this.store);

		this.value = this.value ? this.value.clearTime(): new Date().clearTime();
		this.addEvents(
			"scheduleselect",
			"dayclick",
			"select",
			"beforechange"
		);
		if(this.handler){
			this.on("dayclick", this.handler,  this.scope || this);
		}
		this.initDisabledDays();
		if(this.autoFit) {
			Ext.EventManager.onWindowResize(this.fireResize, this);
		}
	},

	initDisabledDays: function(){
		if(!this.disabledDatesRE && this.disabledDates){
			var dd = this.disabledDates;
			var re = "(?:";
			for(var i = 0; i < dd.length; i++){
				re += dd[i];
				if(i != dd.length-1) re += "|";
			}
			this.disabledDatesRE = new RegExp(re + ")");
		}
	},

	setDisabledDates: function(dd){
		if(Ext.isArray(dd)){
			this.disabledDates = dd;
			this.disabledDatesRE = null;
		}else{
			this.disabledDatesRE = dd;
		}
		this.initDisabledDays();
		this.update(this.value, true);
	},

	setDisabledDays: function(dd){
		this.disabledDays = dd;
		this.update(this.value, true);
	},

	setMinDate: function(dt){
		this.minDate = dt;
		this.update(this.value, true);
	},
	
	setMaxDate: function(dt){
		this.maxDate = dt;
		this.update(this.value, true);
	},
	
	setValue: function(value){
		var old = this.value;
		this.value = value.clearTime(true);
		if(this.el){
			if(old.getMonth() != this.value.getMonth()) {
				if(this.store) {
					this.doLoad(this.value);
				}
			}
			this.update(this.value);
		}
	},

	getValue: function(){
		return this.value;
	},

	// private
	focus: function(){
		if(this.el){
			this.update(this.activeDate);
		}
	},

	// private
	onRender: function(container, position){
		var m = [
			 '<table cellspacing="0">',
				'<tr><td class="x-xcalendar-left"><a href="javascript:void(0);" title="' + this.prevText + '">&#160;</a></td><td class="x-xcalendar-middle" align="center"></td><td class="x-xcalendar-right"><a href="javascript:void(0);" title="' + this.nextText + '">&#160;</a></td></tr>',
				'<tr><td colspan="3"><table class="x-xcalendar-inner" cellspacing="0"><thead><tr>'];
		var dn = this.dayNames;
		for(var i = 0; i < 7; i++){
			var d = this.startDay+i;
			if(d > 6){
				d = d-7;
			}
			m.push('<th><span>', dn[d].substr(0,1), "</span></th>");
		}
		m[m.length] = "</tr></thead><tbody><tr>";
		for(var i = 0; i < 42; i++) {
			if(i % 7 == 0 && i != 0){
				m[m.length] = "</tr><tr>";
			}
			m[m.length] = '<td><div><a href="javascript:void(0);" hidefocus="on" class="x-xcalendar-date" tabIndex="1"><em><span></span></em></a><ul class="x-xcalendar-schedules"></ul></div></td>';
		}
		m.push("</tr></tbody></table></td></tr>", 
				this.showToday ? '<tr><td colspan="3" class="x-xcalendar-bottom" align="center"></td></tr>': "", 
				'</table><div class="x-xcalendar-mp"></div>');

		var el = document.createElement("div");
		el.className = "x-xcalendar";
		if (!el.id) {
			el.id = this.getId();
		}

		el.innerHTML = m.join("");
		var margins = this.parseMargins( this.margins );
		el.style.marginTop = margins.top + "px";
		el.style.marginRight = margins.right + "px";
		el.style.marginBottom = margins.bottom + "px";
		el.style.marginLeft = margins.left + "px";

		el.style.minWidth = this.minWidth + "px";
		el.style.minHeight = this.minHeight + "px";

		if(!position || position == undefined) {
			position = null;
		}
		container.dom.insertBefore(el, position);

		this.el = Ext.get(el);

		if( this.autoFit ) {
			this.el.setWidth( ( this.minWidth > this.el.parent().getComputedWidth() ? ( this.minWidth - margins.right - margins.left ) : ( this.el.parent().getComputedWidth() - margins.right - margins.left ) ) );
			this.el.setHeight( ( this.minHeight > this.el.parent().getComputedHeight() ? ( this.minHeight - margins.top - margins.bottom ) : ( this.el.parent().getComputedHeight() - margins.top - margins.bottom ) ) );
		} else {
			(this.width != "" ? this.el.setWidth(this.width - margins.left - margins.right) : this.el.setWidth(this.el.getComputedWidth() - margins.left - margins.right) );
			(this.height != "" ? this.el.setHeight(this.height - margins.top - margins.bottom) : this.el.setHeight(this.el.getComputedHeight() - margins.top - margins.left) );
		}

		this.eventEl = Ext.get(el.firstChild);
		this.leftClickRpt = new Ext.util.ClickRepeater(this.el.child("td.x-xcalendar-left a"), {
			handler: this.showPrevMonth,
			scope: this,
			preventDefault:true,
			stopDefault:true
		});
		this.rightClickRpt = new Ext.util.ClickRepeater(this.el.child("td.x-xcalendar-right a"), {
			handler: this.showNextMonth,
			scope: this,
			preventDefault:true,
			stopDefault:true
		});
		if ( this.enableMouseWheele ) {
			this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
		}
		this.monthPicker = this.el.down("div.x-xcalendar-mp");
		this.monthPicker.enableDisplayMode("block");
		var kn = new Ext.KeyNav(this.eventEl, {
			"left": function(e){
				e.ctrlKey ?
					this.showPrevMonth() :
					this.update(this.activeDate.add("d", -1));
			},
			"right": function(e){
				e.ctrlKey ?
					this.showNextMonth() :
					this.update(this.activeDate.add("d", 1));
			},
			"up": function(e){
				e.ctrlKey ?
					this.showNextYear() :
					this.update(this.activeDate.add("d", -7));
			},
			"down": function(e){
				e.ctrlKey ?
					this.showPrevYear() :
					this.update(this.activeDate.add("d", 7));
			},
			"pageUp": function(e){
				this.showNextMonth();
			},
			"pageDown": function(e){
				this.showPrevMonth();
			},
			"enter": function(e){
				e.stopPropagation();
				return true;
			},
			scope: this
		});
		this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-xcalendar-date"});
		this.el.unselectable();
		this.cells = this.el.select("table.x-xcalendar-inner tbody td");
		this.textNodes = this.el.query("table.x-xcalendar-inner tbody span");
		this.scheduleNodes = this.el.query("table.x-xcalendar-inner tbody ul");
		this.mbtn = new Ext.Button({
			text: "&#160;",
			tooltip: ( this.showMonthPicker == true ? this.monthYearText: false ),
			renderTo: this.el.child("td.x-xcalendar-middle", true)
		});
		if ( this.showMonthPicker == true ) {
			this.mbtn.on("click", this.showMonthPicker, this);
			this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
		}
		if(this.showToday){
			this.todayKeyListener = this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.selectToday,  this);
			var today = (new Date()).dateFormat(this.format);
			this.todayBtn = new Ext.Button({
				renderTo: this.el.child("td.x-xcalendar-bottom", true),
				text: String.format(this.todayText, today),
				tooltip: String.format(this.todayTip, today),
				handler: this.selectToday,
				scope: this
			});
		}
		if(Ext.isIE){
			this.el.repaint();
		}
		if(!this.store) {
			this.update(this.value);
		}
	},

	// private
	createMonthPicker: function(){
		if(this.showMonthPicker == true ) {
			if(!this.monthPicker.dom.firstChild){
				var buf = ['<table border="0" cellspacing="0">'];
				for(var i = 0; i < 6; i++){
					buf.push(
						'<tr><td class="x-xcalendar-mp-month"><a href="javascript:void(0);">', this.monthNames[i].substr(0, 3), '</a></td>',
						'<td class="x-xcalendar-mp-month x-xcalendar-mp-sep"><a href="javascript:void(0);">', this.monthNames[i+6].substr(0, 3), '</a></td>',
						i == 0 ?
						'<td class="x-xcalendar-mp-ybtn" align="center"><a class="x-xcalendar-mp-prev"></a></td><td class="x-xcalendar-mp-ybtn" align="center"><a class="x-xcalendar-mp-next"></a></td></tr>' :
						'<td class="x-xcalendar-mp-year"><a href="javascript:void(0);"></a></td><td class="x-xcalendar-mp-year"><a href="javascript:void(0);"></a></td></tr>'
					);
				}
				buf.push(
					'<tr class="x-xcalendar-mp-btns"><td colspan="4"><button type="button" class="x-xcalendar-mp-ok">',
						this.okText,
						'</button><button type="button" class="x-xcalendar-mp-cancel">',
						this.cancelText,
						'</button></td></tr>',
					"</table>"
				);
				this.monthPicker.update(buf.join(""));
				this.monthPicker.on("click", this.onMonthClick, this);
				this.monthPicker.on("dblclick", this.onMonthDblClick, this);
				this.mpMonths = this.monthPicker.select("td.x-xcalendar-mp-month");
				this.mpYears = this.monthPicker.select("td.x-xcalendar-mp-year");
				this.mpMonths.each(function(m, a, i){
					i += 1;
					if((i%2) == 0){
						m.dom.xmonth = 5 + Math.round(i * .5);
					}else{
						m.dom.xmonth = Math.round((i-1) * .5);
					}
				});
			}
		}
	},

	// private
	showMonthPicker: function(){
		this.createMonthPicker();
		var size = this.el.getSize();
		this.monthPicker.setSize(size);
		this.monthPicker.child("table").setSize(size);
		this.mpSelMonth = (this.activeDate || this.value).getMonth();
		this.updateMPMonth(this.mpSelMonth);
		this.mpSelYear = (this.activeDate || this.value).getFullYear();
		this.updateMPYear(this.mpSelYear);
		this.monthPicker.slideIn("t", {duration:.2});
	},

	// private
	updateMPYear: function(y){
		this.mpyear = y;
		var ys = this.mpYears.elements;
		for(var i = 1; i <= 10; i++){
			var td = ys[i-1], y2;
			if((i%2) == 0){
				y2 = y + Math.round(i * .5);
				td.firstChild.innerHTML = y2;
				td.xyear = y2;
			}else{
				y2 = y - (5-Math.round(i * .5));
				td.firstChild.innerHTML = y2;
				td.xyear = y2;
			}
			this.mpYears.item(i-1)[y2 == this.mpSelYear ? "addClass": "removeClass"]("x-xcalendar-mp-sel");
		}
	},

	// private
	updateMPMonth: function(sm){
		this.mpMonths.each(function(m, a, i){
			m[m.dom.xmonth == sm ? "addClass": "removeClass"]("x-xcalendar-mp-sel");
		});
	},

	// private
	selectMPMonth: function(m){
	},

	// private
	onMonthClick: function(e, t){
		e.stopEvent();
		var el = new Ext.Element(t), pn;
		if(el.is("button.x-xcalendar-mp-cancel")){
			this.hideMonthPicker();
		}
		else if(el.is("button.x-xcalendar-mp-ok")){
			var d = new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate());
			if(d.getMonth() != this.mpSelMonth){
				// "fix" the JS rolling date conversion if needed
				d = new Date(this.mpSelYear, this.mpSelMonth, 1).getLastDateOfMonth();
			}
			this.update(d);
			this.hideMonthPicker();
		}
		else if(pn = el.up("td.x-xcalendar-mp-month", 2)){
			this.mpMonths.removeClass("x-xcalendar-mp-sel");
			pn.addClass("x-xcalendar-mp-sel");
			this.mpSelMonth = pn.dom.xmonth;
		}
		else if(pn = el.up("td.x-xcalendar-mp-year", 2)){
			this.mpYears.removeClass("x-xcalendar-mp-sel");
			pn.addClass("x-xcalendar-mp-sel");
			this.mpSelYear = pn.dom.xyear;
		}
		else if(el.is("a.x-xcalendar-mp-prev")){
			this.updateMPYear(this.mpyear-10);
		}
		else if(el.is("a.x-xcalendar-mp-next")){
			this.updateMPYear(this.mpyear+10);
		}
	},

	// private
	onMonthDblClick: function(e, t){
		e.stopEvent();
		var el = new Ext.Element(t), pn;
		if(pn = el.up("td.x-xcalendar-mp-month", 2)){
			this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
			this.hideMonthPicker();
		}
		else if(pn = el.up("td.x-xcalendar-mp-year", 2)){
			this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
			this.hideMonthPicker();
		}
	},

	// private
	hideMonthPicker: function(disableAnim){
		if(this.monthPicker){
			if(disableAnim === true){
				this.monthPicker.hide();
			}else{
				this.monthPicker.slideOut("t", {duration:.2});
			}
		}
	},

	// private
	showPrevMonth: function(e){
		this.activeDate = this.activeDate.add("mo", -1);
		if(this.store) {
			this.doLoad(this.activeDate);
		} else {
			this.update(this.activeDate, true);
		}
	},

	// private
	showNextMonth: function(e){
		this.activeDate = this.activeDate.add("mo", 1);
		if(this.store) {
			this.doLoad(this.activeDate);
		} else {
			this.update(this.activeDate, true);
		}
	},

	// private
	showPrevYear: function(){
		this.activeDate = this.activeDate.add("y", -1);
		if(this.store) {
			this.doLoad(this.activeDate);
		} else {
			this.update(this.activeDate, true);
		}
	},

	// private
	showNextYear: function(){
		this.activeDate = this.activeDate.add("y", 1);
		if(this.store) {
			this.doLoad(this.activeDate);
		} else {
			this.update(this.activeDate, true);
		}
	},

	// private
	handleMouseWheel: function(e){
		if( this.enableMouseWheele ) {
			var delta = e.getWheelDelta();
			if(delta > 0){
				this.showPrevMonth();
				e.stopEvent();
			} else if(delta < 0){
				this.showNextMonth();
				e.stopEvent();
			}
		}
	},

	// private
	handleDateClick: function(e, t){
		e.stopEvent();
		if(t.parentNode.dateValue && !Ext.fly(t.parentNode).hasClass("x-xcalendar-disabled")){
			this.setValue(new Date(t.parentNode.dateValue));
			this.fireEvent("select", this, this.value);
		}
		var time = this.value.format( this.displayCheckFomart ) + " " + new Date().format( this.displayTimeFormat );
		this.fireEvent("dayclick", this, new Date( time ) );
	},

	// private
	getScheduleId: function( event, element ) {
		this.fireEvent( "scheduleselect", { dom: element, parameter: Ext.get(element).parent().dom.scheduleId } );
	},

	// private
	selectToday: function(){
		if(this.todayBtn && !this.todayBtn.disabled){
			this.setValue(new Date().clearTime());
			this.fireEvent("select", this, this.value);
		}
	},

	// private
	update: function(date, forceRefresh){
		var vd = this.activeDate;
		this.activeDate = date;
		if(!forceRefresh && vd && this.el){
			var t = date.getTime();
			if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
				this.cells.removeClass("x-xcalendar-selected");
				this.cells.each(function(c){
				   if(c.dom.firstChild.dateValue == t){
						c.addClass("x-xcalendar-selected");
						setTimeout(function(){
							try{c.dom.firstChild.focus();}catch(e){}
						}, 50);
						return false;
					}
				});
				return;
			}
		}
		var days = date.getDaysInMonth();
		var firstOfMonth = date.getFirstDateOfMonth();
		var startingPos = firstOfMonth.getDay()-this.startDay;
		if(startingPos <= this.startDay){
			startingPos += 7;
		}
		var pm = date.add("mo", -1);
		var prevStart = pm.getDaysInMonth()-startingPos;
		var cells = this.cells.elements;
		var textEls = this.textNodes;
		var scheduleEls = this.scheduleNodes;
		days += startingPos;
		// convert everything to numbers so it"s fast
		var day = 86400000;
		var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
		var today = new Date().clearTime().getTime();
		var sel = date.clearTime().getTime();
		var min = this.minDate ? this.minDate.clearTime(): Number.NEGATIVE_INFINITY;
		var max = this.maxDate ? this.maxDate.clearTime(): Number.POSITIVE_INFINITY;
		var ddMatch = this.disabledDatesRE;
		var ddText = this.disabledDatesText;
		var ddays = this.disabledDays ? this.disabledDays.join(""): false;
		var ddaysText = this.disabledDaysText;
		var format = this.format;
		if(this.showToday){
			var td = new Date().clearTime();
			var disable = (td < min || td > max || 
				(ddMatch && format && ddMatch.test(td.dateFormat(format))) || 
				(ddays && ddays.indexOf(td.getDay()) != -1));
						
			this.todayBtn.setDisabled(disable);
			this.todayKeyListener[disable ? "disable": "enable"]();
		}
		var setCellClass = function(cal, cell){
			cell.title = "";
			var t = d.getTime();
			cell.firstChild.dateValue = t;
			if(t == today){
				cell.className += " x-xcalendar-today";
				cell.title = cal.todayText;
			}
			if(t == sel){
				cell.className += " x-xcalendar-selected";
				setTimeout(function(){
					try{cell.firstChild.focus();}catch(e){}
				}, 50);
			}
			// disabling
			if(t < min) {
				cell.className = " x-xcalendar-disabled";
				cell.title = cal.minText;
				return;
			}
			if(t > max) {
				cell.className = " x-xcalendar-disabled";
				cell.title = cal.maxText;
				return;
			}
			if(ddays){
				if(ddays.indexOf(d.getDay()) != -1){
					cell.title = ddaysText;
					cell.className = " x-xcalendar-disabled";
				}
			}
			if(ddMatch && format){
				var fvalue = d.dateFormat(format);
				if(ddMatch.test(fvalue)){
					cell.title = ddText.replace("%0", fvalue);
					cell.className = " x-xcalendar-disabled";
				}
			}
		};

		// replace array create, -> "{" String "}"
		var replace = new Array();
		for( var x = 0; x < this.dataFormat.length; x++ ) {
			replace.push("{" + this.dataFormat[x] + "}");
		}

		// data formated
		if(!this.store){
			this.mode = "local";
			var formatedData = new Ext.data.SimpleStore({
				"id": 0,
				fields: this.dataFormat,
				data: this.data
			});
		} else if(Ext.isArray(this.store.data.items)){
			var data = [];
			for(var i=0;i<this.store.data.items.length;i++) {
				data[i] = [];
				for(var j in this.store.data.items[i].data) {
					data[i].push(this.store.data.items[i].data[j]);
				}
			}
			var formatedData = new Ext.data.SimpleStore({
				fields: this.dataFormat,
				data: data
			});
		}

		var i = 0;
		for(; i < startingPos; i++) {
			textEls[i].innerHTML = (++prevStart);
			d.setDate(d.getDate()+1);
			cells[i].className = "x-xcalendar-prevday";
			setCellClass(this, cells[i]);
			for ( var j = ( formatedData.data.items.length - 1 ); j >= 0; j-- ) {
				if ( d.format(this.displayCheckFomart) == new Date(formatedData.data.items[j].data.startdate).format(this.displayCheckFomart) ) {
					var crtEl = Ext.DomHelper.insertFirst( Ext.get( scheduleEls[i] ), { tag: "li", cls: "x-xcalendar-schedules-item", children:[ { tag: "a", href: "javascript:void(0);", hidefocus: "on", cls: "x-xcalendar-schedule", html: this.str_replace( replace, new Array( formatedData.data.items[j].data.sequencial, formatedData.data.items[j].data.subject, new Date( formatedData.data.items[j].data.startdate ).format( this.displayTimeFormat ), new Date( formatedData.data.items[j].data.enddate ).format( this.displayTimeFormat ) ), this.scheduleFormat )  } ] } );
				}
			}
			Ext.get(scheduleEls[i]).parent().setStyle({ height: Math.ceil(this.el.getComputedHeight() / 7 ) + "px" });
			if( this.cellScrollable == true ) {
				Ext.get(scheduleEls[i]).parent().dom.style.overflowY = "auto";
				Ext.get(scheduleEls[i]).parent().dom.style.overflowX = "hidden";
			} else {
				Ext.get(scheduleEls[i]).parent().setStyle({ overflow: "hidden" });
			}
		}
		for(; i < days; i++){
			var intDay = i - startingPos + 1;
			textEls[i].innerHTML = (intDay);
			d.setDate(d.getDate()+1);
			cells[i].className = "x-xcalendar-active";
			setCellClass(this, cells[i]);
			for ( var j = ( formatedData.data.items.length - 1 ); j >= 0; j-- ) {
				if ( d.format(this.displayCheckFomart) == new Date(formatedData.data.items[j].data.startdate).format(this.displayCheckFomart) ) {
					var crtEl = Ext.DomHelper.insertFirst( Ext.get( scheduleEls[i] ), { tag: "li", cls: "x-xcalendar-schedules-item", children:[ { tag: "a", href: "javascript:void(0);", hidefocus: "on", cls: "x-xcalendar-schedule", html: this.str_replace( replace, new Array( formatedData.data.items[j].data.sequencial, formatedData.data.items[j].data.subject, new Date( formatedData.data.items[j].data.startdate ).format( this.displayTimeFormat ), new Date( formatedData.data.items[j].data.enddate ).format( this.displayTimeFormat ) ), this.scheduleFormat )  } ] } );
					crtEl.scheduleId = formatedData.data.items[j].data.sequencial;
				}
			}
			Ext.get(scheduleEls[i]).parent().setStyle({ height: Math.ceil(this.el.getComputedHeight() / 7 ) + "px" });
			if( this.cellScrollable == true ) {
				Ext.get(scheduleEls[i]).parent().dom.style.overflowY = "auto";
				Ext.get(scheduleEls[i]).parent().dom.style.overflowX = "hidden";
			} else {
				Ext.get(scheduleEls[i]).parent().setStyle({ overflow: "hidden" });
			}
			for ( var l = 0; l < Ext.DomQuery.select("a", Ext.get(scheduleEls[i]).dom).length; l++ ) {
				Ext.get( Ext.DomQuery.select("a", Ext.get(scheduleEls[i]).dom )[l] ).on( "click", this.getScheduleId, this );
			}
			if(this.compactListing) {
				if( Ext.get(scheduleEls[i]).parent().dom.scrollHeight > Ext.get(scheduleEls[i]).parent().dom.clientHeight && Ext.DomQuery.select("a", Ext.get(scheduleEls[i]).dom).length > 1 ) {
					Ext.DomHelper.overwrite( Ext.get(scheduleEls[i]), { tag: "li", cls: "x-xcalendar-schedules-item-overflow", html: Ext.DomQuery.select("a", Ext.get(scheduleEls[i]).dom).length + "件" } );
				}
			}
		}
		var extraDays = 0;
		for(; i < 42; i++) {
			textEls[i].innerHTML = (++extraDays);
			d.setDate(d.getDate()+1);
			cells[i].className = "x-xcalendar-nextday";
			setCellClass(this, cells[i]);
			for ( var j = ( formatedData.data.items.length - 1 ); j >= 0; j-- ) {
				if ( d.format(this.displayCheckFomart) == new Date(formatedData.data.items[j].data.startdate).format(this.displayCheckFomart) ) {
					var crtEl = Ext.DomHelper.insertFirst( Ext.get( scheduleEls[i] ), { tag: "li", cls: "x-xcalendar-schedules-item", children:[ { tag: "a", href: "javascript:void(0);", hidefocus: "on", cls: "x-xcalendar-schedule", html: this.str_replace( replace, new Array( formatedData.data.items[j].data.sequencial, formatedData.data.items[j].data.subject, new Date( formatedData.data.items[j].data.startdate ).format( this.displayTimeFormat ), new Date( formatedData.data.items[j].data.enddate ).format( this.displayTimeFormat ) ), this.scheduleFormat )  } ] } );
				}
			}
			Ext.get(scheduleEls[i]).parent().setStyle({ height: Math.ceil(this.el.getComputedHeight() / 7 ) + "px" });
			if( this.cellScrollable == true ) {
				Ext.get(scheduleEls[i]).parent().dom.style.overflowY = "auto";
				Ext.get(scheduleEls[i]).parent().dom.style.overflowX = "hidden";
			} else {
				Ext.get(scheduleEls[i]).parent().setStyle({ overflow: "hidden" });
			}
		}
		this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
		if(!this.internalRender){
			var main = this.el.dom.firstChild;
			var w = main.offsetWidth;
			this.el.setWidth(w + this.el.getBorderWidth("lr"));
			Ext.fly(main).setWidth(w);
			this.internalRender = true;
			// opera does not respect the auto grow header center column
			// then, after it gets a width opera refuses to recalculate
			// without a second pass
			if(Ext.isOpera && !this.secondPass){
				main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
				this.secondPass = true;
				this.update.defer(10, this, [date]);
			}
		}
	},

	// private
	beforeDestroy: function() {
		if(this.rendered){
			Ext.destroy(
				this.leftClickRpt,
				this.rightClickRpt,
				this.monthPicker,
				this.eventEl,
				this.mbtn,
				this.todayBtn
			);
		}
	},

	parseMargins: function(v){
		var ms = v.split(' ');
		var len = ms.length;
		if(len == 1){
			ms[1] = ms[0];
			ms[2] = ms[0];
			ms[3] = ms[0];
		}
		if(len == 2){
			ms[2] = ms[0];
			ms[3] = ms[1];
		}
		return {
			top:parseInt(ms[0], 10) || 0,
			right:parseInt(ms[1], 10) || 0,
			bottom:parseInt(ms[2], 10) || 0,
			left:parseInt(ms[3], 10) || 0
		};
	},

	afterRender: function(){
		Ext.ux.ScheduleCalendar.superclass.afterRender.call(this);
		if(this.store){
			this.LoadMask = new Ext.LoadMask(this.el, Ext.apply({store:this.store}));
			this.LoadMask = new Ext.LoadMask(this.el, Ext.apply({store:this.store}));
			this.setStore(this.store, true);
		}
	},

	setStore: function(store, initial){
		if(!initial && this.store){
			this.store.un("beforeload", this.onBeforeLoad, this);
			this.store.un("datachanged", this.refresh, this);
			this.store.un("clear", this.refresh, this);
		}
		if(store){
			store = Ext.StoreMgr.lookup(store);
			store.on("beforeload", this.onBeforeLoad, this);
			store.on("datachanged", this.refresh, this);
			store.on("clear", this.refresh, this);
		}
		this.store = store;
		if(store){
			this.refresh();
		}
	},

	refresh: function(){
		this.el.remove();
		this.onRender(this.container);
		if(this.activeDate) {
			this.update(this.activeDate, true);
		} else {
			this.update(this.value);
		}
	},

	onBeforeLoad: function() {
		if(this.loadMask && this.store){
			this.LoadMask = new Ext.LoadMask(this.el, Ext.apply({store:this.store}));
		}
	},

	doLoad: function(date) {
		var params = {};
		params[this.queryName] = date.format(this.queryFormat);
		if(this.fireEvent("beforechange", this, params) !== false){
			this.store.load({params:params});
		}
	},

	fireResize: function(w, h){
		this.fireEvent("resize", this);
		var margins = this.parseMargins( this.margins );
		this.el.setWidth( ( this.minWidth > this.el.parent().getComputedWidth() ? ( this.minWidth - margins.right - margins.left ) : ( this.el.parent().getComputedWidth() - margins.right - margins.left ) ) );
		this.el.setHeight( ( this.minHeight > this.el.parent().getComputedHeight() ? ( this.minHeight - margins.top - margins.bottom ) : ( this.el.parent().getComputedHeight() - margins.top - margins.bottom ) ) );
		for( var i = 0; i < Ext.query("table.x-xcalendar-inner td div", Ext.get(this.el).dom).length; i++ ) {
			Ext.get( Ext.query("table.x-xcalendar-inner td div", Ext.get(this.el).dom)[i] ).setStyle({ height: Math.ceil(this.el.getComputedHeight() / 7 ) + "px" });
		}
	},

	// Replace all occurrences of the search string with the replacement string
	// 
	// +    discuss at: http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_str_replace/
	// +       version: 812.1017
	// +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
	// +   improved by: Gabriel Paderni
	// +   improved by: Philip Peterson
	// +   improved by: Simon Willison (http://simonwillison.net)
	// +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
	// +   bugfixed by: Anton Ongson
	// +      input by: Onno Marsman
	// +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
	// +    tweaked by: Onno Marsman
	// *     example 1: str_replace(' ', '.', 'Kevin van Zonneveld');
	// *     returns 1: 'Kevin.van.Zonneveld'
	// *     example 2: str_replace(['{name}', 'l'], ['hello', 'm'], '{name}, lars');
	// *     returns 2: 'hemmo, mars'
	str_replace: function( search, replace, subject ) {
		var f = search, r = replace, s = subject;
		var ra = r instanceof Array, sa = s instanceof Array, f = [].concat(f), r = [].concat(r), i = (s = [].concat(s)).length;
		while (j = 0, i--) {
			if (s[i]) {
				while (s[i] = (s[i]+'').split(f[j]).join(ra ? r[j] || "": r[0]), ++j in f){};
			}
		};
		return sa ? s: s[0];
	}
});
Ext.reg("xcalendar", Ext.ux.ScheduleCalendar);

