/*
dhtmlxScheduler v.4.1.0 Stardard

This software is covered by GPL license. You also can obtain Commercial or Enterprise license to use it in non-GPL project - please contact sales@dhtmlx.com. Usage without proper license is prohibited.

(c) Dinamenta, UAB.
*/
scheduler._temp_matrix_scope = function(){



scheduler.matrix = {};
scheduler._merge=function(a,b){
	for (var c in b)
		if (typeof a[c] == "undefined")
			a[c]=b[c];
};
scheduler.createTimelineView=function(obj){
	scheduler._skin_init();

	scheduler._merge(obj,{
		section_autoheight: true,
		name:"matrix",
		x:"time",
		y:"time",
		x_step:1,
		x_unit:"hour",
		y_unit:"day",
		y_step:1,
		x_start:0,
		x_size:24,
		y_start:0,
		y_size:	7,
		render:"cell",
		dx:200,
		dy:50,
		event_dy: scheduler.xy.bar_height-5,
		event_min_dy: scheduler.xy.bar_height-5,
		resize_events: true,
		fit_events: true,
		show_unassigned: false,
		second_scale: false,
		round_position: false,
		_logic: function(render_name, y_unit, timeline) {
			var res = {};
			if(scheduler.checkEvent("onBeforeSectionRender")) {
				res = scheduler.callEvent("onBeforeSectionRender", [render_name, y_unit, timeline]);
			}
			return res;
		}
	});
	obj._original_x_start = obj.x_start;

	//first and last hours are applied only to day based timeline
	if (obj.x_unit != "day") obj.first_hour = obj.last_hour = 0;
	//correction for first and last hour
	obj._start_correction = obj.first_hour?obj.first_hour*60*60*1000:0;
	obj._end_correction = obj.last_hour?(24-obj.last_hour)*60*60*1000:0;

	if (scheduler.checkEvent("onTimelineCreated")) {
		scheduler.callEvent("onTimelineCreated", [obj]);
	}

	var old = scheduler.render_data;
	scheduler.render_data = function(evs, mode) {
		if (this._mode == obj.name) {
			//repaint single event, precision is not necessary
			if (mode && !obj.show_unassigned && obj.render != "cell") {
				for (var i = 0; i < evs.length; i++) {
					this.clear_event(evs[i]);
					this.render_timeline_event.call(this.matrix[this._mode], evs[i], true);
				}
			} else {
				scheduler._renderMatrix.call(obj, true, true);
			}
		} else
			return old.apply(this, arguments);
	};

	scheduler.matrix[obj.name]=obj;
	scheduler.templates[obj.name+"_cell_value"] = function(ar){ return ar?ar.length:""; };
	scheduler.templates[obj.name+"_cell_class"] = function(arr){ return ""; };
	scheduler.templates[obj.name+"_scalex_class"] = function(date){ return ""; };
	scheduler.templates[obj.name+"_second_scalex_class"] = function(date){ return ""; };

	scheduler.templates[obj.name+"_scaley_class"] = function(section_id, section_label, section_options){ return ""; };
	scheduler.templates[obj.name+"_scale_label"] = function(section_id, section_label, section_options){ return section_label; };

	scheduler.templates[obj.name+"_tooltip"] = function(a,b,e){ return e.text; };
	scheduler.templates[obj.name+"_date"] = function(datea, dateb){
		if ( (datea.getDay()==dateb.getDay() && dateb-datea < (24*60*60*1000)) ||
			+datea == +scheduler.date.date_part(new Date(dateb)) ||
			(+scheduler.date.add(datea, 1, "day") == +dateb && dateb.getHours() === 0 && dateb.getMinutes() === 0) )
			return scheduler.templates.day_date(datea);
		if ( (datea.getDay() != dateb.getDay() && dateb-datea < (24*60*60*1000)) ) {
			return scheduler.templates.day_date(datea)+" &ndash; "+scheduler.templates.day_date(dateb);
		}
		return scheduler.templates.week_date(datea, dateb);
	};

	scheduler.templates[obj.name+"_scale_date"] = scheduler.date.date_to_str(obj.x_date||scheduler.config.hour_date);
	scheduler.templates[obj.name+"_second_scale_date"] = scheduler.date.date_to_str((obj.second_scale && obj.second_scale.x_date)?obj.second_scale.x_date:scheduler.config.hour_date);

	scheduler.date["add_" + obj.name] = function(date, step, c) {
		var resulting_date = scheduler.date.add(date, (obj.x_length || obj.x_size) * step * obj.x_step, obj.x_unit);
		if (obj.x_unit == "minute" || obj.x_unit == "hour") {
			var size = (obj.x_length || obj.x_size);
			var converted_step = (obj.x_unit == "hour") ? obj.x_step*60 : obj.x_step;
			if((converted_step * size) % (24 * 60)){
				//if scroll hours within the day
				if ( +scheduler.date.date_part(new Date(date)) == +scheduler.date.date_part(new Date(resulting_date )) ) {
					obj.x_start += step*size;
				} else {
					// total steps starting from 0
					var total_steps = ( (24 * 60) / (size * converted_step) ) - 1;
					var steps_offset = Math.round(total_steps * size);

					if (step > 0) {
						obj.x_start = obj.x_start - steps_offset;
					} else {
						obj.x_start = steps_offset + obj.x_start;
					}
				}
			}
		}
		return resulting_date;
	};
	scheduler.date[obj.name+"_start"] = function(date) {
		var func = scheduler.date[obj.x_unit+"_start"] || scheduler.date.day_start;
		var start_date = func.call(scheduler.date, date);
		start_date = scheduler.date.add(start_date, obj.x_step*obj.x_start, obj.x_unit);
		return start_date;
	};


	scheduler.callEvent("onOptionsLoad",[obj]);

	//init custom wrappers
	scheduler[obj.name+"_view"]=function(){
		scheduler._renderMatrix.apply(obj, arguments);
	};

	//enable drag for non-cell modes
	var temp_date = new Date();
	var step_diff = (scheduler.date.add(temp_date, obj.x_step, obj.x_unit).valueOf() - temp_date.valueOf()); // "minute" + step in ms
	scheduler["mouse_"+obj.name]=function(pos){ //mouse_coord handler
		//get event object
		var ev = this._drag_event;
		if (this._drag_id){
			ev = this.getEvent(this._drag_id);
			this._drag_event._dhx_changed = true;
		}

		pos.x-=obj.dx;
		var summ = 0, xind = 0;
		var ratio,
			column_width;
		for (xind; xind <= this._cols.length-1; xind++) {

			column_width = this._cols[xind];
			summ += column_width;
			if (summ>pos.x){ //index of section
				ratio = (pos.x-(summ-column_width))/column_width;
				ratio = (ratio < 0) ? 0 : ratio;
				break;
			}
		}

		if(obj.round_position){
			// in case of click, mouse position will be always rounded to start date of the cell
			// when dragging - position can be rounded to the start date of the next column, in order to improve the usability
			// edge = 1 - always return start date of current cell
			// 0.5 - round to next cell if mouse in the right half of cell
			var edge = 1;
			if(scheduler.getState().drag_mode && scheduler.getState().drag_mode != "move"){
				edge = 0.5;//rounding for resize
			}
			if(ratio >= edge){
				xind++;
			}
			ratio = 0;
		}

		//border cases
		if (xind === 0 && this._ignores[0]) {
			xind = 1; ratio = 0; 
			while (this._ignores[xind]) xind++;
		} else if ( xind == this._cols.length && this._ignores[xind-1]) {
			xind = this._cols.length-1; ratio = 0; 
			while (this._ignores[xind]) xind--;
			xind++;
		}


		pos.x =  0;
		pos.force_redraw = true;
		pos.custom = true;

		var end_date;
		// if our event is at the end of the view
		if(xind >= obj._trace_x.length) {
			end_date = scheduler.date.add(obj._trace_x[obj._trace_x.length-1], obj.x_step, obj.x_unit);
			if (obj._end_correction)
				end_date = new Date(end_date-obj._end_correction);
		} else {
			var timestamp_diff = ratio * column_width * obj._step + obj._start_correction;
			end_date = new Date(+obj._trace_x[xind]+timestamp_diff);
		}

		// as we can simply be calling _locate_cell_timeline
		if (this._drag_mode == "move" && this._drag_id && this._drag_event) { 
			var ev = this.getEvent(this._drag_id);
			var drag_event = this._drag_event;

			pos._ignores = (this._ignores_detected || obj._start_correction || obj._end_correction);
			if (!drag_event._move_delta) {
				drag_event._move_delta = (ev.start_date-end_date)/60000;
				if (this.config.preserve_length && pos._ignores){
					drag_event._move_delta = this._get_real_event_length(ev.start_date,end_date, obj);
					drag_event._event_length = this._get_real_event_length(ev.start_date,ev.end_date, obj);
				}
			}

			
			

			//preserve visible size of event
			if (this.config.preserve_length && pos._ignores){
				var ev_length = drag_event._event_length;//this._get_real_event_length(ev.start_date, ev.end_date, obj);
				var current_back_shift = this._get_fictional_event_length(end_date, drag_event._move_delta, obj, true);
				end_date = new Date(end_date - current_back_shift);
			} else {
				// converting basically to start_date
				end_date = scheduler.date.add(end_date, drag_event._move_delta, "minute");
			}
		}

		if (this._drag_mode == "resize" && ev){
			if(this._drag_from_start && +end_date> +ev.end_date){
				this._drag_from_start = false;
			}else if(!this._drag_from_start && +end_date < +ev.start_date){
				this._drag_from_start = true;
			}
			pos.resize_from_start = this._drag_from_start;
		}

		if (obj.round_position) {
			switch(this._drag_mode) {
				case "move":
				 	if (!this.config.preserve_length){
						end_date = get_rounded_date.call(obj, end_date, false);
						// to preserve original start and end dates
						if(obj.x_unit == "day")//only make sense for whole-day cells
							pos.custom = false;
					}
					break;
				case "resize":
					if(this._drag_event){
						// will save and use resize position only once
						if (this._drag_event._resize_from_start === null || this._drag_event._resize_from_start === undefined) {
							this._drag_event._resize_from_start = pos.resize_from_start;
						}
						pos.resize_from_start = this._drag_event._resize_from_start;
						end_date = get_rounded_date.call(obj, end_date, !this._drag_event._resize_from_start);
					}
					break;
			}
		}

		this._resolve_timeline_section(obj, pos);
		if(pos.section){
			// update draggable event with current section
			this._update_timeline_section({pos:pos, event:this.getEvent(this._drag_id), view:obj});
		}
		pos.y = Math.round((end_date-this._min_date)/(1000*60*this.config.time_step));
		pos.shift = this.config.time_step; //step_diff;


		return pos;
	};
};

scheduler._get_timeline_event_height = function(ev, config){
	var section = ev[config.y_property]; // section id
	var event_height = config.event_dy;
	if (config.event_dy == "full") {
		if (config.section_autoheight) {
			event_height = config._section_height[section] - 6;
		} else {
			event_height = config.dy - 3;
		}
	}

	if (config.resize_events) {
		event_height = Math.max(Math.floor(event_height / ev._count), config.event_min_dy);
	}
	return event_height;
};
scheduler._get_timeline_event_y = function(order, event_height){
	var sorder = order;
	var y = 2+sorder*event_height+(sorder?(sorder*2):0); // original top + number_of_events * event_dy + default event top/bottom borders
	if (scheduler.config.cascade_event_display) {
		y =2+sorder*scheduler.config.cascade_event_margin+(sorder?(sorder*2):0);
	}
	return y;
};

scheduler.render_timeline_event = function(ev, attach){
	var section = ev[this.y_property]; // section id
	if (!section)
		return ""; // as we may await html

	var sorder = ev._sorder;

	var x_start = _getX(ev, false, this);
	var x_end = _getX(ev, true, this);

	var event_height = scheduler._get_timeline_event_height(ev, this);

	var hb = event_height - 2;// takes into account css sizes (border/padding)
	if (!ev._inner && this.event_dy == "full") {
		hb=(hb+2)*(ev._count-sorder)-2;
	}

	var y = scheduler._get_timeline_event_y(ev._sorder, event_height);

	var section_height = event_height+y+2;
	if(!this._events_height[section] || (this._events_height[section] < section_height)){
		this._events_height[section] = section_height;
	}

	var cs = scheduler.templates.event_class(ev.start_date,ev.end_date,ev);
	cs = "dhx_cal_event_line "+(cs||"");

	var bg_color = (ev.color?("background:"+ev.color+";"):"");
	var color = (ev.textColor?("color:"+ev.textColor+";"):"");
	var text = scheduler.templates.event_bar_text(ev.start_date,ev.end_date,ev);

	var html='<div event_id="'+ev.id+'" class="'+cs+'" style="'+bg_color+''+color+'position:absolute; top:'+y+'px; height: '+hb+'px; left:'+x_start+'px; width:'+Math.max(0,x_end-x_start)+'px;'+(ev._text_style||"")+'">';
	if (scheduler.config.drag_resize && !scheduler.config.readonly) {
		var dhx_event_resize = 'dhx_event_resize';
		html += ("<div class='"+dhx_event_resize+" "+dhx_event_resize+"_start' style='height: "+hb+"px;'></div><div class='"+dhx_event_resize+" "+dhx_event_resize+"_end' style='height: "+hb+"px;'></div>");
	}
	html += (text+'</div>');

	if (!attach) 
		return html;
	else {
		var d = document.createElement("DIV");
		d.innerHTML = html;
		var ind = this.order[section];
		var parent = scheduler._els["dhx_cal_data"][0].firstChild.rows[ind].cells[1].firstChild;
		
		scheduler._rendered.push(d.firstChild);
		parent.appendChild(d.firstChild);
	}
};
function trace_events(){
	//minimize event set
	var evs = scheduler.get_visible_events();
	var matrix =[];
	for (var i=0; i < this.y_unit.length; i++) 
		matrix[i]=[];

	//next code defines row for undefined key
	//most possible it is an artifact of incorrect configuration
	if (!matrix[y])
		matrix[y]=[];

	for (var i=0; i < evs.length; i++) {
		var y = this.order[evs[i][this.y_property]];
		var x = 0; 
		while (this._trace_x[x+1] && evs[i].start_date>=this._trace_x[x+1]) x++;
		while (this._trace_x[x] && evs[i].end_date>this._trace_x[x]) {
			if (!matrix[y][x]) matrix[y][x]=[];
			matrix[y][x].push(evs[i]);
			x++;
		}
	}
	return matrix;
}
// function used to get X (both start and end) coordinates for timeline bar view
function _getX(ev, isEndPoint, config) {
	var x = 0;
	var step = config._step;
	var round_position = config.round_position;

	var column_offset = 0;
	var date = (isEndPoint) ? ev.end_date : ev.start_date;

	if(date.valueOf()>scheduler._max_date.valueOf())
		date = scheduler._max_date;
	var delta = date - scheduler._min_date_timeline;

	if (delta > 0){
		var index = scheduler._get_date_index(config, date);
		if (scheduler._ignores[index])
			round_position=true;

		for (var i = 0; i < index; i++) {
			x += scheduler._cols[i];
		}

		var column_date = scheduler.date.add(scheduler._min_date_timeline, scheduler.matrix[scheduler._mode].x_step*index, scheduler.matrix[scheduler._mode].x_unit);
		if (!round_position) {
			delta = date - column_date;
			if (config.first_hour || config.last_hour){
				delta = delta - config._start_correction;
				if (delta < 0) delta = 0;
				column_offset = Math.round(delta/step);
				if (column_offset > scheduler._cols[index])
					column_offset = scheduler._cols[index];
			} else {
				column_offset = Math.round(delta/step);
			}
		} else {
			if (+date > +column_date && isEndPoint) {
				column_offset = scheduler._cols[index];
			}
		}
	}
	if (isEndPoint) {
		// special handling for "round" dates which match columns and usual ones
		if (delta !== 0 && !round_position) {
			x += column_offset-12;
		} else {
			x += column_offset-14;
		}
	} else {
		x += column_offset+1;
	}
	return x;
}
function get_rounded_date(date, isEndDate) {
	var index = scheduler._get_date_index(this, date);
	var rounded_date = this._trace_x[index];
	if (isEndDate && (+date != +this._trace_x[index])) {
		rounded_date = (this._trace_x[index+1]) ? this._trace_x[index+1] : scheduler.date.add(this._trace_x[index], this.x_step, this.x_unit);
	}
	return new Date(rounded_date);
}
function get_events_html(evs) {
	var html = "";
	if (evs && this.render != "cell"){
		evs.sort(this.sort || function(a,b){
			if(a.start_date.valueOf()==b.start_date.valueOf())
				return a.id>b.id?1:-1;
			return a.start_date>b.start_date?1:-1;
		});
		var stack=[];
		var evs_length = evs.length;
		// prepare events for render
		for (var j=0; j<evs_length; j++){
			var ev = evs[j];
			ev._inner = false;

			var ev_start_date = (this.round_position) ? get_rounded_date.apply(this, [ev.start_date, false]) : ev.start_date;
			var ev_end_date = (this.round_position) ? get_rounded_date.apply(this, [ev.end_date, true]) : ev.end_date;

			// cutting stack from the last -> first event side
			while (stack.length) {
				var stack_ev = stack[stack.length-1];
				if (stack_ev.end_date.valueOf() <= ev_start_date.valueOf()) {
					stack.splice(stack.length-1,1);
				} else {
					break;
				}
			}

			// cutting stack from the first -> last event side
			var sorderSet = false;
			for(var p=0; p<stack.length; p++){
				var t_ev = stack[p];
				if(t_ev.end_date.valueOf() <= ev_start_date.valueOf()){
					sorderSet = true;
					ev._sorder=t_ev._sorder;
					stack.splice(p,1);
					ev._inner=true;
					break;
				}
			}


			if (stack.length)
				stack[stack.length-1]._inner=true;


			if (!sorderSet) {
				if (stack.length) {
					if (stack.length <= stack[stack.length - 1]._sorder) {
						if (!stack[stack.length - 1]._sorder)
							ev._sorder = 0;
						else
							for (var h = 0; h < stack.length; h++) {
								var _is_sorder = false;
								for (var t = 0; t < stack.length; t++) {
									if (stack[t]._sorder == h) {
										_is_sorder = true;
										break;
									}
								}
								if (!_is_sorder) {
									ev._sorder = h;
									break;
								}
							}
						ev._inner = true;
					}
					else {
						var _max_sorder = stack[0]._sorder;
						for (var w = 1; w < stack.length; w++)
							if (stack[w]._sorder > _max_sorder)
								_max_sorder = stack[w]._sorder;
						ev._sorder = _max_sorder + 1;
						ev._inner = false;
					}
				}
				else
					ev._sorder = 0;
			}

			stack.push(ev);

			if (stack.length>(stack.max_count||0)) {
				stack.max_count=stack.length;
				ev._count=stack.length;
			}
			else {
				ev._count=(ev._count)?ev._count:1;
			}
		}
		// fix _count for every event
		for (var m=0; m < evs.length; m++) {
			evs[m]._count = stack.max_count;
		}
		// render events
		for (var v=0; v<evs_length; v++) {
			html+=scheduler.render_timeline_event.call(this, evs[v], false);
		}
	}
	return html;
}
	

function y_scale(d) {
	var html = "<table style='table-layout:fixed;' cellspacing='0' cellpadding='0'>";
	var evs=[];
	if(scheduler._load_mode)
		scheduler._load();
	if (this.render == "cell")
		evs = trace_events.call(this);
	else {
		var tevs = scheduler.get_visible_events();
		var order = this.order;

		for (var j = 0; j < tevs.length; j++) {
			var tev = tevs[j];
			var tev_section = tev[this.y_property];
			var index = this.order[ tev_section ];

			if (this.show_unassigned && !tev_section) {
				for (var key in order) {
					if (order.hasOwnProperty(key)) {
						index = order[key];
						if (!evs[index]) evs[index] = [];
						var clone = scheduler._lame_copy({}, tev);
						clone[this.y_property] = key;
						evs[index].push(clone);
					}
				}
			} else {
				// required as we could have index of not displayed section or "undefined"
				if (!evs[index]) evs[index] = [];
				evs[index].push(tev);
			}
		}
	}

	var summ = 0; 
	for (var i=0; i < scheduler._cols.length; i++)
		summ+=scheduler._cols[i];

	var step = new Date();
	var realcount = scheduler._cols.length-scheduler._ignores_detected;
	step = ((scheduler.date.add(step, this.x_step*realcount, this.x_unit)-step)-(this._start_correction + this._end_correction)*realcount)/summ;
	this._step = step;
	this._summ = summ;

	var heights = scheduler._colsS.heights=[]; 

	this._events_height = {};
	this._section_height = {};
	for (var i=0; i<this.y_unit.length; i++){

		var stats = this._logic(this.render, this.y_unit[i], this); // obj with custom style

		scheduler._merge(stats, {
			height: this.dy
		});

		//autosize height, if we have a free space
		if(this.section_autoheight) {
			if (this.y_unit.length * stats.height < d.offsetHeight) {
				stats.height = Math.max(stats.height, Math.floor((d.offsetHeight - 1) / this.y_unit.length));
			}
			this._section_height[this.y_unit[i].key] = stats.height;
		}

		scheduler._merge(stats, {
			//section 1
			tr_className: "",
			style_height: "height:"+stats.height+"px;",
			style_width: "width:"+(this.dx-1)+"px;",
			td_className: "dhx_matrix_scell"+((scheduler.templates[this.name+"_scaley_class"](this.y_unit[i].key, this.y_unit[i].label, this.y_unit[i]))?" "+scheduler.templates[this.name+"_scaley_class"](this.y_unit[i].key, this.y_unit[i].label, this.y_unit[i]):''),
			td_content: scheduler.templates[this.name+'_scale_label'](this.y_unit[i].key, this.y_unit[i].label, this.y_unit[i]),
			//section 2
			summ_width: "width:"+summ+"px;",
			//section 3
			table_className: ''
		});

		// generating events html in a temporary file, calculating their height
		var events_html = get_events_html.call(this, evs[i]);

		if(this.fit_events){
			var rendered_height = this._events_height[this.y_unit[i].key]||0;
			stats.height = (rendered_height>stats.height)?rendered_height:stats.height;
			stats.style_height = "height:"+stats.height+"px;";
			this._section_height[this.y_unit[i].key] = stats.height;
		}

		// section 1
		html+="<tr class='"+stats.tr_className+"' style='"+stats.style_height+"'><td class='"+stats.td_className+"' style='"+stats.style_width+" height:"+(stats.height-1)+"px;'>"+stats.td_content+"</td>";

		if (this.render == "cell"){
			for (var j=0; j < scheduler._cols.length; j++) {
				if (scheduler._ignores[j])
					html+="<td></td>";
				else
					html+="<td class='dhx_matrix_cell "+scheduler.templates[this.name+"_cell_class"](evs[i][j],this._trace_x[j],this.y_unit[i])+"' style='width:"+(scheduler._cols[j]-1)+"px'><div style='width:"+(scheduler._cols[j]-1)+"px'>"+scheduler.templates[this.name+"_cell_value"](evs[i][j],this._trace_x[j],this.y_unit[i])+"</div></td>";
			}
		} else {
			//section 2
			html+="<td><div style='"+stats.summ_width+" "+stats.style_height+" position:relative;' class='dhx_matrix_line'>";

			// adding events
			html += events_html;

			//section 3
				html+="<table class='"+stats.table_className+"' cellpadding='0' cellspacing='0' style='"+stats.summ_width+" "+stats.style_height+"' >";
			for (var j=0; j < scheduler._cols.length; j++){
				if (scheduler._ignores[j])
					html+="<td></td>";
				else
					html+="<td class='dhx_matrix_cell "+scheduler.templates[this.name+"_cell_class"](evs[i],this._trace_x[j],this.y_unit[i])+"' style='width:"+(scheduler._cols[j]-1)+"px'><div style='width:"+(scheduler._cols[j]-1)+"px'></div></td>";
			}
			html+="</table>";
			html+="</div></td>";
		}
		html+="</tr>";
	}
	html += "</table>";
	this._matrix = evs;
	//d.scrollTop = 0; //fix flickering in FF;  disabled as it was impossible to create dnd event if scroll was used (window jumped to the top)
	d.innerHTML = html;

	scheduler._rendered = [];
	var divs = scheduler._obj.getElementsByTagName("DIV");
	for (var i=0; i < divs.length; i++)
		if (divs[i].getAttribute("event_id"))
			scheduler._rendered.push(divs[i]);

	this._scales = {};
	for (var i=0; i < d.firstChild.rows.length; i++) {
		heights.push(d.firstChild.rows[i].offsetHeight);
		var unit_key = this.y_unit[i].key;
		var scale = this._scales[unit_key] = (scheduler._isRender('cell')) ? d.firstChild.rows[i] : d.firstChild.rows[i].childNodes[1].getElementsByTagName('div')[0];
		scheduler.callEvent("onScaleAdd", [scale, unit_key]);
	}
}
function x_scale(h){
	var current_sh = scheduler.xy.scale_height;
	var original_sh = this._header_resized||scheduler.xy.scale_height;
	scheduler._cols=[];	//store for data section, each column width
	scheduler._colsS={height:0}; // heights of the y sections
	this._trace_x =[]; // list of dates per cells
	var summ = scheduler._x - this.dx - scheduler.xy.scroll_width; //border delta, whole width
	var left = [this.dx]; // left margins, initial left margin
	var header = scheduler._els['dhx_cal_header'][0];
	header.style.width = (left[0]+summ)+'px';

	scheduler._min_date_timeline = scheduler._min_date;

	var preserve = scheduler.config.preserve_scale_length;
	var start = scheduler._min_date;
	scheduler._process_ignores(start, this.x_size, this.x_unit, this.x_step, preserve);

	var size = this.x_size + (preserve ? scheduler._ignores_detected : 0);
	if (size != this.x_size)
		scheduler._max_date = scheduler.date.add(scheduler._min_date, size*this.x_step, this.x_unit);
	
	var realcount = size - scheduler._ignores_detected;
	for (var k=0; k<size; k++){
		// dates calculation
		this._trace_x[k]=new Date(start);
		start = scheduler.date.add(start, this.x_step, this.x_unit);

		// position calculation
		if (scheduler._ignores[k]){
			scheduler._cols[k]=0;
			realcount++;
		} else {
			scheduler._cols[k]=Math.floor(summ/(realcount-k));
		}

		summ -= scheduler._cols[k];
		left[k+1] = left[k] + scheduler._cols[k];
	}
	h.innerHTML = "<div></div>";

	if(this.second_scale){
		// additional calculations
		var mode = this.second_scale.x_unit;
		var control_dates = [this._trace_x[0]]; // first control date
		var second_cols = []; // each column width of the secondary row
		var second_left = [this.dx, this.dx]; // left margins of the secondary row
		var t_index = 0; // temp index
		for (var l = 0; l < this._trace_x.length; l++) {
			var date = this._trace_x[l];
			var res = is_new_interval(mode, date, control_dates[t_index]);

			if(res) { // new interval
				++t_index; // starting new interval
				control_dates[t_index] = date; // updating control date as we moved to the new interval
				second_left[t_index+1] = second_left[t_index];
			}
			var t = t_index+1;
			second_cols[t_index] = scheduler._cols[l] + (second_cols[t_index]||0);
			second_left[t] += scheduler._cols[l];
		}

		h.innerHTML = "<div></div><div></div>";
		var top = h.firstChild;
		top.style.height = (original_sh)+'px'; // actually bottom header takes 21px
		var bottom = h.lastChild;
		bottom.style.position = "relative";

		for (var m = 0; m < control_dates.length; m++) {
			var tdate = control_dates[m];
			var scs = scheduler.templates[this.name+"_second_scalex_class"](tdate);
			var head=document.createElement("DIV"); head.className="dhx_scale_bar dhx_second_scale_bar"+((scs)?(" "+scs):"");
			scheduler.set_xy(head,second_cols[m]-1,original_sh-3,second_left[m],0); //-1 for border, -3 = -2 padding -1 border bottom
			head.innerHTML = scheduler.templates[this.name+"_second_scale_date"](tdate);
			top.appendChild(head);
		}
	}

	scheduler.xy.scale_height = original_sh; // fix for _render_x_header which uses current scale_height value
	h = h.lastChild; // h - original scale
	for (var i=0; i<this._trace_x.length; i++){
		if (scheduler._ignores[i])
			continue;

		start = this._trace_x[i];
		scheduler._render_x_header(i, left[i], start, h);
		var cs = scheduler.templates[this.name+"_scalex_class"](start);
		if (cs)	
			h.lastChild.className += " "+cs;
	}
	scheduler.xy.scale_height = current_sh; // restoring current value

	var trace = this._trace_x;
	h.onclick = function(e){
		var pos = locate_hcell(e);
		if (pos)
			scheduler.callEvent("onXScaleClick",[pos.x, trace[pos.x], e||event]);
	};
	h.ondblclick = function(e){
		var pos = locate_hcell(e);
		if (pos)
			scheduler.callEvent("onXScaleDblClick",[pos.x, trace[pos.x], e||event]);
	};
}
function is_new_interval(mode, date, control_date){ // mode, date to check, control_date for which period should be checked
	switch(mode) {
	 case "hour":
			return ((date.getHours() != control_date.getHours()) || is_new_interval("day", date, control_date));
		case "day":
			return !(date.getDate() == control_date.getDate() && date.getMonth() == control_date.getMonth() && date.getFullYear() == control_date.getFullYear());
		case "week":
			return !(scheduler.date.getISOWeek(date) == scheduler.date.getISOWeek(control_date) && date.getFullYear() == control_date.getFullYear());
		case "month":
			return !(date.getMonth() == control_date.getMonth() && date.getFullYear() == control_date.getFullYear());
		case "year":
			return !(date.getFullYear() == control_date.getFullYear());
		default:
			return false; // same interval
	}
}
function set_full_view(mode){
	if (mode){
		scheduler.set_sizes();
		scheduler._init_matrix_tooltip();

		//we need to have day-rounded scales for navigation
		//in same time, during rendering scales may be shifted
		var temp = scheduler._min_date;
		x_scale.call(this,scheduler._els["dhx_cal_header"][0]);
		y_scale.call(this,scheduler._els["dhx_cal_data"][0]);
		scheduler._min_date = temp;
		scheduler._els["dhx_cal_date"][0].innerHTML=scheduler.templates[this.name+"_date"](scheduler._min_date, scheduler._max_date);
		if (scheduler._mark_now) {
			scheduler._mark_now();
		}
	}

	// hide tooltip if it is displayed
	hideToolTip();
}


function hideToolTip(){ 
	if (scheduler._tooltip){
		scheduler._tooltip.style.display = "none";
		scheduler._tooltip.date = "";
	}
}
function showToolTip(obj,pos,offset){ 
	if (obj.render != "cell") return;
	var mark = pos.x+"_"+pos.y;		
	var evs = obj._matrix[pos.y][pos.x];
	
	if (!evs) return hideToolTip();
	
	evs.sort(function(a,b){ return a.start_date>b.start_date?1:-1; });

	if (scheduler._tooltip){
		if (scheduler._tooltip.date == mark) return;
		scheduler._tooltip.innerHTML="";
	} else {
		var t = scheduler._tooltip = document.createElement("DIV");
		t.className = "dhx_year_tooltip";
		document.body.appendChild(t);
		t.onclick = scheduler._click.dhx_cal_data;
	}
	
	var html = "";

	for (var i=0; i<evs.length; i++){
		var bg_color = (evs[i].color?("background-color:"+evs[i].color+";"):"");
		var color = (evs[i].textColor?("color:"+evs[i].textColor+";"):"");
		html+="<div class='dhx_tooltip_line' event_id='"+evs[i].id+"' style='"+bg_color+""+color+"'>";
		html+="<div class='dhx_tooltip_date'>"+(evs[i]._timed?scheduler.templates.event_date(evs[i].start_date):"")+"</div>";
		html+="<div class='dhx_event_icon icon_details'>&nbsp;</div>";
		html+=scheduler.templates[obj.name+"_tooltip"](evs[i].start_date, evs[i].end_date,evs[i])+"</div>";
	}

	scheduler._tooltip.style.display="";   
	scheduler._tooltip.style.top = "0px";

	if (document.body.offsetWidth-offset.left-scheduler._tooltip.offsetWidth < 0)
		scheduler._tooltip.style.left = offset.left-scheduler._tooltip.offsetWidth+"px";
	else
		scheduler._tooltip.style.left = offset.left+pos.src.offsetWidth+"px";

	scheduler._tooltip.date = mark;
	scheduler._tooltip.innerHTML = html;

	if (document.body.offsetHeight-offset.top-scheduler._tooltip.offsetHeight < 0)
		scheduler._tooltip.style.top= offset.top-scheduler._tooltip.offsetHeight+pos.src.offsetHeight+"px";
	else
		scheduler._tooltip.style.top= offset.top+"px";
}

scheduler._matrix_tooltip_handler = function(e){
	var obj = scheduler.matrix[scheduler._mode];
	if (!obj || obj.render != "cell")
		return;
	if (obj){
		var pos = scheduler._locate_cell_timeline(e);
		var e = e || event;
		var src = e.target||e.srcElement;
		if (pos)
			return showToolTip(obj,pos,getOffset(pos.src));
	}
	hideToolTip();
};
scheduler._init_matrix_tooltip = function() {
	scheduler._detachDomEvent(scheduler._els["dhx_cal_data"][0], "mouseover", scheduler._matrix_tooltip_handler);
	dhtmlxEvent(scheduler._els["dhx_cal_data"][0], "mouseover", scheduler._matrix_tooltip_handler);
};

scheduler._renderMatrix = function(mode, refresh) {
	if (!refresh)
		scheduler._els['dhx_cal_data'][0].scrollTop=0;

	scheduler._min_date = scheduler.date[this.name+"_start"](scheduler._date);
	scheduler._max_date = scheduler.date.add(scheduler._min_date, this.x_size*this.x_step, this.x_unit);
	scheduler._table_view = true;
	if (this.second_scale) {
		if (mode && !this._header_resized) {
			this._header_resized = scheduler.xy.scale_height;
			scheduler.xy.scale_height *= 2;
			scheduler._els['dhx_cal_header'][0].className += " dhx_second_cal_header";
		}
		if (!mode && this._header_resized) {
			scheduler.xy.scale_height /= 2;
			this._header_resized = false;
			var header = scheduler._els['dhx_cal_header'][0];
			header.className = header.className.replace(/ dhx_second_cal_header/gi,"");
		}
	}
	set_full_view.call(this,mode);
};

function html_index(el) {
	var p = el.parentNode.childNodes;
	for (var i=0; i < p.length; i++) 
		if (p[i] == el) return i;
	return -1;
}
function locate_hcell(e){
	e = e||event;
	var trg = e.target?e.target:e.srcElement;
	while (trg && trg.tagName != "DIV")
		trg=trg.parentNode;
	if (trg && trg.tagName == "DIV"){
		var cs = trg.className.split(" ")[0];
		if (cs == "dhx_scale_bar")
			return { x:html_index(trg), y:-1, src:trg, scale:true };
	}
}
scheduler._locate_cell_timeline = function(e){
	e = e||event;
	var trg = e.target?e.target:e.srcElement;

	var res = {};
	var view = scheduler.matrix[scheduler._mode];
	var pos = scheduler.getActionData(e);

	for (var xind = 0; xind < view._trace_x.length-1; xind++) {
		// | 8:00, 8:30 | 8:15 should be checked against 8:30
		// clicking at the most left part of the cell, say 8:30 should create event in that cell, not previous one
		if (+pos.date < view._trace_x[xind+1])
			break;
	}

	res.x = xind;
	res.y = view.order[pos.section];
	var diff = scheduler._isRender('cell') ? 1 : 0;
	res.src = view._scales[pos.section] ? view._scales[pos.section].getElementsByTagName('td')[xind+diff] : null;

	var isScale = false;
	while (res.x === 0 && trg.className != "dhx_cal_data" && trg.parentNode) {
		if (trg.className.split(" ")[0] == "dhx_matrix_scell") {
			isScale = true;
			break;
		} else {
			trg = trg.parentNode;
		}
	}
	if (isScale) { // Y scale
		res.x = -1;
		res.src = trg;
		res.scale = true;
	}

	return res;
};

var old_click = scheduler._click.dhx_cal_data;
scheduler._click.dhx_marked_timespan = scheduler._click.dhx_cal_data = function(e){
	var ret = old_click.apply(this,arguments);
	var obj = scheduler.matrix[scheduler._mode];
	if (obj){
		var pos = scheduler._locate_cell_timeline(e);
		if (pos){
			if (pos.scale)
				scheduler.callEvent("onYScaleClick",[pos.y, obj.y_unit[pos.y], e||event]);
			else
				scheduler.callEvent("onCellClick",[pos.x, pos.y, obj._trace_x[pos.x], (((obj._matrix[pos.y]||{})[pos.x])||[]), e||event]);
		}
	}
	return ret;
};

scheduler.dblclick_dhx_matrix_cell = function(e){
	var obj = scheduler.matrix[scheduler._mode];
	if (obj){
		var pos = scheduler._locate_cell_timeline(e);
		if (pos){
			if (pos.scale)
				scheduler.callEvent("onYScaleDblClick",[pos.y, obj.y_unit[pos.y], e||event]);
			else
				scheduler.callEvent("onCellDblClick",[pos.x, pos.y, obj._trace_x[pos.x], (((obj._matrix[pos.y]||{})[pos.x])||[]), e||event]);
		}
	}
};

var old_dblclick_marked_timespan = scheduler.dblclick_dhx_marked_timespan || function(){};
scheduler.dblclick_dhx_marked_timespan = function(e){
	var obj = scheduler.matrix[scheduler._mode];
	if (obj)
		return scheduler.dblclick_dhx_matrix_cell(e);
	else
		return old_dblclick_marked_timespan.apply(this,arguments);
};

scheduler.dblclick_dhx_matrix_scell = function(e){
	return scheduler.dblclick_dhx_matrix_cell(e);
};

scheduler._isRender = function(mode){
	return (scheduler.matrix[scheduler._mode] && scheduler.matrix[scheduler._mode].render == mode);
};

scheduler.attachEvent("onCellDblClick", function (x, y, a, b, event){
	if (this.config.readonly|| (event.type == "dblclick" && !this.config.dblclick_create)) return;
	
	var obj = scheduler.matrix[scheduler._mode];
	var event_options = {};
	event_options.start_date = obj._trace_x[x];
	event_options.end_date = (obj._trace_x[x+1]) ? obj._trace_x[x+1] : scheduler.date.add(obj._trace_x[x], obj.x_step, obj.x_unit);

	if (obj._start_correction)
		event_options.start_date = new Date(event_options.start_date*1 + obj._start_correction);
	if (obj._end_correction)
		event_options.end_date = new Date(event_options.end_date - obj._end_correction);

	event_options[obj.y_property] = obj.y_unit[y].key;
	scheduler.addEventNow(event_options, null, event);
});	

scheduler.attachEvent("onBeforeDrag", function (event_id, mode, native_event_object){
	return !scheduler._isRender("cell");
});
scheduler.attachEvent("onEventChanged", function(id, ev) {
	ev._timed = this.isOneDayEvent(ev);
});
var old_render_marked_timespan = scheduler._render_marked_timespan;
scheduler._render_marked_timespan = function(options, area, unit_id, min_date, max_date) {
	if (!scheduler.config.display_marked_timespans)
		return [];

	if (scheduler.matrix && scheduler.matrix[scheduler._mode]) {
		if (scheduler._isRender('cell'))
			return;

		var view_opts = scheduler._lame_copy({}, scheduler.matrix[scheduler._mode]);
		//timespans must always use actual position, not rounded
		view_opts.round_position = false;
		var blocks = [];

		var units = [];
		var areas = [];
		var section = options.sections ? (options.sections.units || options.sections.timeline) : null;
		if (!unit_id) {  // should draw for every unit...
			var order = view_opts.order;
			if (section) { // ...or for only section if mentioned in configuration of timespan
				if (order.hasOwnProperty(section)) {
					units.push(section);
					areas.push(view_opts._scales[section]);
				}
			}else{
				for (var key in order) {
					if (order.hasOwnProperty(key)) {
						units.push(key);
						areas.push(view_opts._scales[key]);
					}
				}
			}
		} else {
			areas = [area];
			units = [unit_id];
		}

		var min_date = min_date ? new Date(min_date) : scheduler._min_date;
		var max_date = max_date ? new Date(max_date) : scheduler._max_date;
		var dates = [];

		if (options.days > 6) {
			var specific_date = new Date(options.days);
			if (scheduler.date.date_part(new Date(min_date)) <= +specific_date && +max_date >= +specific_date)
				dates.push(specific_date);
		} else {
			dates.push.apply(dates, scheduler._get_dates_by_index(options.days));
		}

		var zones = options.zones;
		var css_classes = scheduler._get_css_classes_by_config(options);

		for (var j=0; j<units.length; j++) {
			area = areas[j];
			unit_id = units[j];
			
			for (var i=0; i<dates.length; i++) {
				var date = dates[i];
				for (var k=0; k<zones.length; k += 2) {
					var zone_start = zones[k];
					var zone_end = zones[k+1];
					var start_date = new Date(+date + zone_start*60*1000);
					var end_date = new Date(+date + zone_end*60*1000);

					if (!(min_date < end_date && max_date > start_date))
						continue;

					var block = scheduler._get_block_by_config(options);
					block.className = css_classes;

					var start_pos = _getX({start_date: start_date}, false, view_opts)-1;
					var end_pos = _getX({start_date: end_date}, false, view_opts)-1;
					var width = Math.max(1, end_pos - start_pos - 1);
					var height = view_opts._section_height[unit_id]-1;

					block.style.cssText = "height: "+height+"px; left: "+start_pos+"px; width: "+width+"px; top: 0;";

					area.insertBefore(block, area.firstChild);
					blocks.push(block);
				}
			}
		}

		return blocks;

	} else {
		 return old_render_marked_timespan.apply(scheduler, [options, area, unit_id]);
	}
};

var old_append_mark_now = scheduler._append_mark_now;
scheduler._append_mark_now = function(day_index, now) {
	if (scheduler.matrix && scheduler.matrix[scheduler._mode]) {
		var n_date = scheduler._currentDate();
		var zone_start = scheduler._get_zone_minutes(n_date);
		var options = {
			days: +scheduler.date.date_part(n_date),
			zones: [zone_start, zone_start+1],
			css: "dhx_matrix_now_time",
			type: "dhx_now_time"
		};
		return scheduler._render_marked_timespan(options);
	} else {
		return old_append_mark_now.apply(scheduler, [day_index, now]);
	}
};

scheduler.attachEvent("onScaleAdd", function(scale, unit_key) {
	var timespans = scheduler._marked_timespans;

	if (timespans && scheduler.matrix && scheduler.matrix[scheduler._mode]) {
		var mode = scheduler._mode;

		var min_date = scheduler._min_date;
		var max_date = scheduler._max_date;
		var global_data = timespans["global"];

		for (var t_date = scheduler.date.date_part(new Date(min_date)); t_date < max_date; t_date = scheduler.date.add(t_date, 1, "day")) {
			var day_value = +t_date;
			var day_index = t_date.getDay();
			var r_configs = [];

			var day_types = global_data[day_value]||global_data[day_index];
			r_configs.push.apply(r_configs, scheduler._get_configs_to_render(day_types));

			if (timespans[mode] && timespans[mode][unit_key]) {
				var z_config = [];
				var unit_types = scheduler._get_types_to_render(timespans[mode][unit_key][day_index], timespans[mode][unit_key][day_value]);
				z_config.push.apply(z_config, scheduler._get_configs_to_render(unit_types));
				if(z_config.length)
					r_configs = z_config;
			}

			for (var i=0; i<r_configs.length; i++) {
				var config = r_configs[i];
				var day = config.days;
				if (day < 7) {
					day = day_value;
					//specify min/max timespan dates, otherwise it can be rendered multiple times in some configurations
					scheduler._render_marked_timespan(config, scale, unit_key, t_date, scheduler.date.add(t_date, 1, "day"));
					day = day_index;
				} else {
					scheduler._render_marked_timespan(config, scale, unit_key, t_date, scheduler.date.add(t_date, 1, "day"));
				}
			}
		}
	}
});

scheduler._resolve_timeline_section = function(view, pos){
	var yind = 0;
	var summ = 0;
	for (yind; yind < this._colsS.heights.length; yind++) {
		summ += this._colsS.heights[yind];
		if (summ > pos.y)
			break;
	}

	if(!view.y_unit[yind]) {
		yind=view.y_unit.length-1;
	}
	if(this._drag_event && !this._drag_event._orig_section){
		this._drag_event._orig_section = view.y_unit[yind].key;
	}

	pos.fields = {};
	if (yind >= 0 && view.y_unit[yind]) {
		pos.section = pos.fields[view.y_property] = view.y_unit[yind].key;
	}
};
scheduler._update_timeline_section = function(action){
	var view = action.view,
		event = action.event,
		pos = action.pos;

	if (event) {
		if(event[view.y_property] != pos.section){
			var line_height = this._get_timeline_event_height(event, view);
			event._sorder = this._get_dnd_order(event._sorder, line_height, view._section_height[pos.section]);
		}
		event[view.y_property] = pos.section;
	}
};
scheduler._get_date_index=function(config, date) {
	var index = 0;
	var trace_x = config._trace_x;
	while (index < trace_x.length-1 && +date >= +trace_x[index+1]) {
		index++;
	}
	return index;
};

scheduler.attachEvent("onBeforeTodayDisplayed", function() {
	for(var i in scheduler.matrix){
		var obj = scheduler.matrix[i];
		obj.x_start = obj._original_x_start;
	}
	return true;
});

scheduler.attachEvent("onOptionsLoad",function(){
	for(var i in scheduler.matrix){
		var obj = scheduler.matrix[i];

		obj.order = {};
		scheduler.callEvent('onOptionsLoadStart', []);
		for(var i=0; i<obj.y_unit.length;i++)
			obj.order[obj.y_unit[i].key]=i;
		scheduler.callEvent('onOptionsLoadFinal', []);
		if (scheduler._date && obj.name == scheduler._mode)
			scheduler.setCurrentView(scheduler._date, scheduler._mode);
	}
});

scheduler.attachEvent("onSchedulerResize",function(){
	if (scheduler.matrix[this._mode]){
		var obj = scheduler.matrix[this._mode];
		scheduler._renderMatrix.call(obj, true, true);
		return false;
	}
	return true;
});

scheduler.attachEvent("onBeforeDrag",function(id, drag_mode, e){
	if(drag_mode == 'resize'){
		var trg = e.target || e.srcElement;
		if((trg.className||"").indexOf("dhx_event_resize_end") < 0){
			scheduler._drag_from_start = true;
		}else{
			scheduler._drag_from_start = false;
		}
	}

	return true;
});

};
scheduler._temp_matrix_scope();
