function Diagram(x, y, w, h) {
// \|/Константы для расчетов\|/ //
	//Расстояние между столбцами
	var stI = 2;
	//Расстояние между элементами
	var stE = 2;
	//Минимальная ширина столбца
	var szE = 4;
	//Высота мигалки
	var szB = 10;
	//Высота номера
	var szN = 17;
	//Минимальная высота статуса
	var szP = 40;
	//Интервал обновления
	var timeCycle = 1000;
// /|\Константы для расчетов/|\ //
	//Основной прямоугольник
	var rAll = new Rectangle(x, y, w, h);
	//Массив элементов под процессы
	var rProc = [];
	//Массив элементов под мигалки
	var rBlink = [];
	//Массив элементов под номера
	var rNumb = [];
	//Массив элементов под время начала процесса
	var rDStart = [];
	//Массив элементов под время конца процесса
	var rDEnd = [];
	//Флаг поворения цикла
	var cCycle = false;
	//Индекс цикла для управления
	var wCycle = -1;
	/*Проценты поднимаются снизу вверх (false)
	 *или слева направо (true)*/
	var rotate = false;
	//Расчет минимальной ширины
	this.minBoardW = function () {
		return (stW + 1) * (rProc.length + 1) + (rProc.length * 5);
	};
	//Расчет минимальной высоты
	this.minBoardH = function () {
		return (stH + 1) * 4 + szHP + szHB + szHN;
	};
	/*Функции для работы с координатами
	* основного прямоугольника */
	this.X = function (val) {
		if (val === undefined) return rAll().X();
		rAll().X(val);
	};
	this.Y = function (val) {
		if (val === undefined) return rAll().Y();
		rAll().Y(val);
	};
	this.W = function (val) {
		if (val === undefined) return rAll().W();
		rAll().W(val);
	};
	this.H = function (val) {
		if (val === undefined) return rAll().H();
		rAll().H(val);
	};
	//Функция изменения начальной координаты и размеров прямоугольника
	this.RectParam = function (_x, _y, _w, _h) {
		rAll.RectParam(_x, _y, _w, _h);
	};
	//Функция изменения прямоугольника по другому прямоугольнику
	this.Rect = function (val) {
		if (val === undefined) return rAll;
		rAll.Rect(val);
	};
	//Количество процессов
	this.ProcCount = function () {
		return rProc.length;
	};
	//Установка нового процесса
	this.AddProc = function (index, proc, blink, numb) {
		var idx = parseInt(index);
		idx = (isNaN(idx) || idx < 0 || idx >= rProc.length) ? -1 : idx;
		var date = new Date();
		if (idx == -1) {
			rProc.push(proc);
			rBlink.push(blink);
			rNumb.push(numb);
			rDStart.push(date);
			rDEnd.push(date);
		} else {
			rProc.splice(idx, 0, proc);
			rBlink.splice(idx, 0, blink);
			rNumb.splice(idx, 0, numb);
			rDStart.splice(idx, 0, date);
			rDEnd.splice(idx, 0, date);
		}
	};
	//Изменение процесса
	this.ChangeProc = function (index, proc, blink, numb) {
		var idx = parseInt(index);
		idx = (isNaN(idx) || idx < 0 || idx >= rProc.length) ? -1 : idx;
		var date = new Date();
		if (idx == -1) return;
		rProc.splice(idx, 1, proc);
		rBlink.splice(idx, 1, blink);
		rNumb.splice(idx, 1, numb);
		rDStart.splice(idx, 1, date);
		rDEnd.splice(idx, 1, date);
	};
	//Удаление процесса
	this.DelStove = function (index) {
		if (rProc.length == 0) return;
		var idx = parseInt(index);
		idx = (isNaN(idx) || idx < 0 || idx >= rProc.length) ? -1 : idx;
		if (idx == -1) {
			rProc.pop();
			rBlink.pop();
			rNumb.pop();
			rDStart.pop();
			rDEnd.pop();
		} else {
			rProc.splice(idx, 1);
			rBlink.splice(idx, 1);
			rNumb.splice(idx, 1);
			rDStart.splice(idx, 1);
			rDEnd.splice(idx, 1);
		}
	};
	//Установка времени начала процесса
	this.StartDate = function (index, date) {
		var idx = parseInt(index);
		idx = (isNaN(idx) || idx < 0 || idx >= rDStart.length) ? -1 : idx;
		if (idx == -1) return;
		rDStart[idx] = date;
	};
	//Установка времени конца процесса
	this.EndDate = function (index, date) {
		var idx = parseInt(index);
		idx = (isNaN(idx) || idx < 0 || idx >= rDStart.length) ? -1 : idx;
		if (idx == -1) return;
		rDEnd[idx] = date;
	};
	//Изменение параметров процесса
	this.ChangeStatProc = function (index, stat, percent) {
		var idx = parseInt(index);
		if (!isNaN(idx) && idx >= 0 && idx < rProc.length) {
			rProc[idx].Status(stat);
			rProc[idx].Percent(percent);
    }
	};
	//Изменение параметров мигалки
	this.ChangeStatBlink = function (index, stat) {
		var idx = parseInt(index);
		if (!isNaN(idx) && idx >= -1 && idx < rBlink.length)
			rBlink[idx].Status(stat);
	};
	//Изменение параметров номера
	this.ChangeStatNumb = function (index, prostoy) {
		var idx = parseInt(index);
		if (!isNaN(idx) && idx >= 0 && idx < rNumb.length)
			rNumb[idx].Prostoy(prostoy);
	};
	//Изменение направления отрисовки процентов
	this.Rotate = function (val) {
		if (val === undefined) return rotate;
		rotate = (typeof val === 'boolean') ? val : false;
		for (var i = 0; i < rProc.length; i++) {
			rProc[i].Rotate(rotate);
			rNumb[i].Rotate(rotate);
			rBlink[i].Rotate(rotate);
		}
	};
	//Заполнение диаграммы по умолчанию
	this.BuildDefault = function () {
		while (rProc.length < 48) {
			rProc.push(new ProcessColumn(0, 0, 0, 0));
			rProc[rProc.length - 1].BuildDefault();
			rBlink.push(new BlinkStatus(0, 0, 0, 0));
			rBlink[rProc.length - 1].BuildDefault();
			rNumb.push(new NumberColumn(0, 0, 0, 0, rProc.length));
			var date = new Date;
			rDStart.push(date);
			rDEnd.push(date);
		}
	};
	//Перерасчет
	this.Rebuild = function () {
		var x = rAll.X() + stE + 1;
		var y = rAll.Y() + stE + 1;
		var wAll = rAll.W() - (rProc.length + 1) * (stE + 1);
		var hAll = rAll.H() - (rProc.length + 1) * (stE + 1);
		for (var i = 0; i < rProc.length; i++) {
			var xP = 0, xB = 0, xN = 0;
			var yP = 0, yB = 0, yN = 0;
			var wP = 0, wB = 0, wN = 0;
			var hP = 0, hB = 0, hN = 0;
			if (!rotate) {
				xP = x; xB = x; xN = x;
				var w = Math.ceil(wAll / (rProc.length - i));
				w = (w < szE) ? szE : w;
				wAll -= w;
				wP = w; wB = w; wN = w;
				hB = szB; hN = szN;
				hP = rAll.H() - (stI + 1) * 4 - szB - szN;
				hP = (hP < szP) ? szP : hP;
				yP = y;
				yB = yP + hP + (stI + 1);
				yN = yB + szB + (stI + 1);
			} else {
				yP = y; yB = y; yN = y;
				var h = Math.ceil(hAll / (rProc.length - i));
				h = (h < szE) ? szE : h;
				hAll -= h;
				hP = h; hB = h; hN = h;		
				wB = szB; wN = szN;
				wP = rAll.W() - (stI + 1) * 4 - szB - szN;
				wP = (wP < szP) ? szP : wP;
				xP = x;
				xB = xP + wP + (stI + 1);
				xN = xB + szB + (stI + 1);
				xN = x;
				xB = xN + wN + (stI + 1);
				xP = xB + szB + (stI + 1);
			}
			rProc[i].RectParam(xP, yP, wP, hP);
			rBlink[i].RectParam(xB, yB, wB, hB);
			rNumb[i].RectParam(xN, yN, wN, hN);
			if (!rotate)
				x += wP + stE + 1;
			else
				y += hP + stE + 1;				
		}
	};
	//Печать
	this.Print = function (a) {
		updateCycle();
		this.Rotate(rotate);
		this.Rebuild();
		a.clearRect(rAll.X(), rAll.Y(), rAll.W(), rAll.H());
		a.fillStyle = "#ddd";
		a.fillRect(rAll.X(), rAll.Y(), rAll.W(), rAll.H());
		for (var i = 0; i < rProc.length; i++) {
			rProc[i].Print(a);
			rBlink[i].Print(a);
			rNumb[i].Print(a);
		}
		a.strokeStyle = "#333";
		a.strokeRect(rAll.X(), rAll.Y(), rAll.W(), rAll.H());
	};
	//Печать текста
	this.PrintText = function (a) {
		for (var i = 0; i < rProc.length; i++) {
			rProc[i].PrintText(a);
			rBlink[i].PrintText(a);
		}
	};
	//Обновление процентов
	function updateCycle(currDate) {
		var date = (currDate == undefined) ? new Date() : currDate;
		var cycle = false;
		for (var i = 0; i < rProc.length; i++) {
			if (rDStart[i] == rDEnd[i]) continue;
			var d = rDEnd[i] - rDStart[i];
			var t = date - rDStart[i];
			d = 100 * (date - rDStart[i]) / d;
			rProc[i].Percent(d);
			rNumb[i].Prostoy(d >= 100);
			cycle = cycle || d < 100;
		}
		cCycle = cCycle && cycle;
		date.setMilliseconds(date.getMilliseconds() + timeCycle);
		if (cCycle)
			if (currDate == undefined)
				wCycle = setTimeout(updateCycle, timeCycle);
			else
				wCycle = setTimeout(updateCycle, timeCycle, date);
	};
	//Запуск цикла обновлений процентов
	this.Cycle = function (start, currDate) {
		if (start == undefined || !(typeof start === 'boolean')) return cCycle;
		if (start) {
			this.CheckDate();
			clearTimeout(wCycle);
			cCycle = true;
			updateCycle(currDate);
		}
	};
	//Проверка времени для работы цикла
	this.CheckDate = function () {
		for (var i = 0; i < rProc.length; i++) {
			if (rDStart[i] > rDEnd[i]) {
				var tmp = rDStart[i];
				rDStart[i] = rDEnd[i];
				rDEnd[i] = tmp;
			}
		}
	};
};