shop = function()
{
	var objects = new Object();
	var menus = new Object();
	var orderTable = null;
	var self = this;
	
	this.cart = new Array();
	this.formErrors = new Array();
	this.activeFormType = 'jur';
	this.activeStep = 1;
	
	var requestTimeout = null;
	
	this.registerObject = function(objectInfo)
	{
		objects[objectInfo.Code] = this.calculate(objectInfo);
		return this;
	}
	
	this.getObject = function(code)
	{
		return objects[code];
	}
	
	this.getRegistredObjects = function()
	{
		return objects;
	}
	
	this.getRegistredObjectsCount = function()
	{
		return objects.Length;
	}
	
	this.registerOrderTable = function(htmlTableId)
	{
		orderTable = $('#' + htmlTableId).get(0);
		return this;
	}
	
	// устаревшее
	this.getTypesArray = function(code)
	{
		obj = objects[code];
		res = new Array();
		for (var ptype in obj.PeriodTypes)
		{
			res.push(obj.PeriodTypes[ptype].Label);
		}
		return res;
	}
	
	// вместо getTypesArray
	this.getTypesHash = function(code)
	{
		var obj = objects[code];
		var res = new Object();
		var firstKey = null;
		for (var ptype in obj.PeriodTypes)
		{
			if (!firstKey)
				firstKey = obj.PeriodTypes[ptype].Code;
			res[obj.PeriodTypes[ptype].Code] = obj.PeriodTypes[ptype].Label;
		}
		res.getFirstKey = function(){return firstKey;}
		
		return res;
	}
	
	this.getPeriodsHashForType = function(code, typeKey)
	{
		var obj = objects[code];
		var res = new Object();
		var firstKey = null;
		var defaultKey = null;
		for (var ptype in obj.PeriodTypes)
		{
			if (ptype == typeKey)
			{
				for (var period in obj.PeriodTypes[ptype].Periods)
				{
					if (!firstKey)
						firstKey = obj.PeriodTypes[ptype].Periods[period].Code;
					if (obj.PeriodTypes[ptype].Periods[period].isDefault)
						defaultKey = obj.PeriodTypes[ptype].Periods[period].Code;
					res[obj.PeriodTypes[ptype].Periods[period].Code] = obj.PeriodTypes[ptype].Periods[period].Name
						+ ' <span>(' + self.prepareSum(Math.round(obj.PeriodTypes[ptype].Periods[period].Price * obj.PeriodTypes[ptype].Periods[period].ObjectsCount
									* (obj.PeriodTypes[ptype].Periods[period].Nds+1))) + " руб.)</span>";
				}
			}
		}
		if (!defaultKey)
		{
			defaultKey = firstKey;
		}
		res.getFirstKey = function(){return firstKey;}
		res.getDefaultKey = function(){return defaultKey;}
		
		return res;
	}
	
	this.buildMenu = function(code)
	{
		obj = objects[code];
		if (obj.PeriodsCount > 1)
		{
			var types = shopInstance.getTypesHash(code);
			var xselect = new xSelect(code, types, undefined, {optionsAsHtml: true, clickEvents: true});
			var periodsHash = self.getPeriodsHashForType(code, types.getFirstKey());
			var subxselect = new xSelect(code + '_period', periodsHash,
					periodsHash.getDefaultKey(), {optionsAsHtml: true, clickEvents: true});
			xselect.change = function()
			{
				var key = xselect.getKey();
				var periodsHash = self.getPeriodsHashForType(code, key);
				subxselect.setOptions(periodsHash);
				subxselect.setKey(periodsHash.getDefaultKey());
				if (objects[code].PeriodTypes[key].Comment != '')
					$('#' + code + '_typeComment').html(objects[code].PeriodTypes[key].Comment).fadeIn('slow');
				else
					$('#' + code + '_typeComment').html('').fadeOut('fast');
			};
			subxselect.change = function()
			{
				var keyType = xselect.getKey();
				var keyPeriod = subxselect.getKey();
				if (objects[code].PeriodTypes[keyType].Periods[keyPeriod].Comment != '')
					$('#' + code + '_periodComment').html(objects[code].PeriodTypes[keyType].Periods[keyPeriod].Comment).fadeIn('slow');
				else
					$('#' + code + '_periodComment').fadeOut('fast');
			};
			xselect.render($('#' + code + '_menu'), 200);
			subxselect.render($('#' + code + '_submenu'), 200);
			
			menus[ocode] = {'main': xselect, 'sub': subxselect};
		}
		else
		{
			menus[ocode] = {'main': null, 'sub': null};
		}
		
		return this;
	}
	
	this.bindButton = function(code)
	{
		$('#' + code + '_add').bind('click', function() {self.addToCart(code);});
	}
	
	// ----- Step1 ------
	
	this.addToCart = function(code)
	{
		obj = objects[code];
		var lineObj = null;
		if (obj.PeriodsCount > 1) // если периодов больше одного, инфу берем из выпадающих меню
		{
			var periodTypeKey = menus[code].main.getKey();
			var periodKey = menus[code].sub.getKey();
			lineObj = {
					Code: obj.Code + '_' + periodKey,
					Object: obj,
					Period: obj.PeriodTypes[periodTypeKey].Periods[periodKey],
					PeriodType: obj.PeriodTypes[periodTypeKey],
					Count: 1
			};
		}
		else // покупка (если периодов меньше двух, инфу берем из объекта)
		{
			var ptype = obj.PeriodTypes[this.getHashKeys(obj.PeriodTypes)[0]];
			var period = ptype.Periods[this.getHashKeys(ptype.Periods)[0]];
			lineObj = {
					Code: obj.Code + '_' + period.Code,
					Object: obj,
					Period: period,
					PeriodType: ptype,
					Count: 1
			};
		}
		
		// Проверяем наличие товара в корзине
		if (this.cart.length > 0)
		{
			for (var i = 0; i < this.cart.length; i++)
			{
				if (this.cart[i].Code == lineObj.Code) // товар уже в корзине
				{
					alert('Товар уже находится в списке заказов. Если вы хотите заказать несколько экземпляров товара, то просто установите нужное количество в ячейке "Кол-во" в списке "Ваш заказ" напротив данного товара.');
					//orderTable.tBodies[0].rows[i].scrollIntoView();
					//$('html,body').scrollTo(orderTable.tBodies[0].rows[i], 500);
					$('html,body').scrollTo($(orderTable.tBodies[0].rows[i]).offset().top - this.getWindowSize().height / 2 + orderTable.tBodies[0].rows[i].clientHeight, 500);
					$(orderTable.tBodies[0].rows[i].cells[3]).animate({backgroundColor:"#EE0000"}, 800).animate({backgroundColor:"#FFFFFF"}, 1000);
					return this;
				}
			}
		}
		
		// Добавляем товар в корзину
		this.cart.push(lineObj);
		
		// Вставка в таблицу
		if (this.cart.length == 1) // удаляем строку, сообщающую, что товаров в корзине нет
		{
			orderTable.tBodies[0].deleteRow(0);
		}
		
		var index = this.cart.length - 1;
		var rowObj = this.cart[index];
		var newRow = orderTable.tBodies[0].insertRow(-1);
		newRow.insertCell(-1).innerHTML = (index + 1) + '.'; // Порядковый номер
		newRow.insertCell(-1).innerHTML = (rowObj.PeriodType.ActionName == '' ? 
				rowObj.PeriodType.Prefix.substring(0, 1).toUpperCase() + rowObj.PeriodType.Prefix.substring(1)
				: rowObj.PeriodType.ActionName + ' ' + rowObj.PeriodType.Prefix)
			+ ' ' + rowObj.Object.Name
			+ (rowObj.Period.FullName == '' ? '' : ', ' + rowObj.Period.FullName); // Название объекта-периода
		newRow.insertCell(-1).innerHTML = self.prepareSum(Math.round(rowObj.Period.Price * rowObj.Period.ObjectsCount * (rowObj.Period.Nds+1))); // Цена
		$(newRow.insertCell(-1)).append($('<input type="text" value="'+rowObj.Count+'" size="3" maxlength="3" />') // Количество
				.bind('change mouseup mousedown keypress keydown keyup', function(){var vx=this.value;do{var v=vx;vx=vx.replace(/\D/,'');}while(v!=vx);if(vx!=this.value)this.value=vx;self.setCountInCart(parseInt(this.parentNode.parentNode.cells[0].innerHTML) - 1, this.value)}));
		newRow.insertCell(-1).innerHTML = self.prepareSum(Math.round(rowObj.Period.Price * rowObj.Period.ObjectsCount * (rowObj.Period.Nds+1)) * rowObj.Count); // Стоиомость
		var d = $('<input type="button" value=" x " />') // Удаление
			.bind('click', function(eventObject){
				var id=parseInt(this.parentNode.parentNode.cells[0].innerHTML)-1;
				if(confirm('Удалить "'+self.cutString(this.parentNode.parentNode.cells[1].innerHTML, 40)+'" из корзины?'))
					self.removeFromCart(id);
			});
		$(newRow.insertCell(-1)).append(d);
		//$('html,body').animate({scrollTop: newRow.offset().top}, 500);
		$('html,body').scrollTo($(newRow).offset().top - this.getWindowSize().height / 2 + newRow.clientHeight, 500);
		$('td', newRow).each(function(){this.style.display='none';$(this).fadeIn('normal')});
		
		this.recalculateCartSum();
		
		return this;
	}
	
	this.removeFromCart = function(id)
	{
		// для каждой ячейки запускаем скрытие, на первой ячейке ставим "событие" на завершение скрытия
		// на удаление строки из таблицы и пересчет суммы в корзине
		this.cart.splice(id, 1);
		$('td', orderTable.tBodies[0].rows[id]).each(function(){
			$(this).fadeOut('normal', function(){
				if(this.cellIndex==5)
				{
					// Opera считает строки (сквозная нумерация) так: хедер, футер, бади; остальные так: хедер, бади, футер.
					self.removeFromCartFinish(this.parentNode.rowIndex-($.browser.opera?2:1));
				}
			})
		});
		
		return this;
	}
	
	this.removeFromCartFinish = function(id)
	{
		orderTable.tBodies[0].deleteRow(id);
		if (this.cart.length > 0)
		{
			for (var i = 0; i < orderTable.tBodies[0].rows.length; i++) // пересчет индексов
			{
				orderTable.tBodies[0].rows[i].cells[0].innerHTML = (i + 1) + '.';
			}
		}
		else
		{
			//orderTable.tBodies[0].insertRow(-1).innerHTML = '<td align="center" colSpan="6">Выберите товары в списке выше...</td>';
			// гавноIE глючит, если добавлять строку целиком через innerHtml.
			var cell = orderTable.tBodies[0].insertRow(-1).insertCell(-1);
			cell.innerHTML='Выберите товары в списке выше...';
			cell.colSpan=6;
			cell.align='center';
		}
		
		this.recalculateCartSum();
		
		return this;
	}
	
	this.setCountInCart = function(id, count)
	{
		var rowObj = this.cart[id];
		rowObj.Count = count;
		//orderTable.tBodies[0].rows[id].cells[3].innerHTML = count;
		// Пересчет суммы
		orderTable.tBodies[0].rows[id].cells[4].innerHTML = self.prepareSum(Math.round(rowObj.Period.Price * rowObj.Period.ObjectsCount
				* (rowObj.Period.Nds+1)) * count);
		
		// Пересчет общей суммы
		this.recalculateCartSum();
		
		return this;
	}
	
	this.recalculateCartSum = function()
	{
		var sum = 0;
		for (var i = 0; i < this.cart.length; i++)
		{
			var rowObj = this.cart[i];
			sum += Math.round(rowObj.Period.Price * rowObj.Period.ObjectsCount * (rowObj.Period.Nds+1)) * rowObj.Count
		}
		$('#cart_orders_sum', orderTable).html(self.prepareSum(sum));
	}
	
	// ----- Step 2 ------
	
	this.toStep1 = function()
	{
		$('#step3').hide('fast'); // скрыть шаг 3
		$('#step2').hide('fast', function() // скрыть шаг 2
		{
			$('#step2-2').hide('fast'); // скрыть кусок шага 2
			$('#step1').show('fast'); // показать шаг 1
			$.scrollTo('#shop'); // скролить ва начало
			$('input', orderTable).each(function(){this.disabled=false;}); // включить контролы таблицы
			$('#shop_order2').show(); // показать кнопку "вперед к шагу 2"
			$('#shop_order1').hide(); // скрыть кнопку "назад к шагу 1"
			$('#order_table_box').show(); // если вдруг пришли с 3го шага, вернуть таблицу
		});
		
		this.activeStep = 1;
		// no history: backward
	}
	
	this.toStep2 = function()
	{
		// А в корзине что-нибудь есть?
		if (this.cart.length < 1)
		{
			alert('Чтобы оформить заказ, добавьте в него хотябы один товар. Для этого выберите подходящий товар выше и нажмите кнопку "+ Добавить в заказ".');
			$.history.load('step1');
			return;
		}
		
		$('#step3').hide('fast');
		$('#step1').hide('fast', function()
		{
			$('#step2-2').show('fast');
			$('#step2').show('fast');
			$.scrollTo('#step2-2');
			$('input', orderTable).each(function(){this.disabled=true;});
			$('#shop_order1').show();
			$('#shop_order2').hide();
			$('#order_table_box').show();
		});
		
		this.activeStep = 2;
		//$.history.load('step2'); // forward
	}
	
	this.toStep3 = function()
	{
		// если корзина пуста, то скорее всего юзер просто тыкнул кнопку F5.
		if (this.cart.length < 1)
		{
			// шлем его на 1й шаг
			$.history.load('step1');
			return;
		}
		
		$('#step1').hide('fast');
		$('#step2').hide('fast', function()
		{
			$('#step2-2').hide('fast');
			$('#step3').show('fast', function(){$.scrollTo('#step3');});
			$('#shop_order2').hide();
			$('#shop_order1').hide();
			$('#order_table_box').hide();
		});
		
		this.activeStep = 3;
	}
	
	this.switchFormType = function(newType)
	{
		if (newType == 'jur')
		{
			$('#shop_order_jur_organization_line, #shop_order_jur_position_line, #shop_order_jur_fax_line').show();
			$('input#order_jur').get(0).checked = false;
			$('input#order_fiz').get(0).checked = true;
			$('#shop_order_type_select_fiz').removeClass('radio_active');
			$('#shop_order_type_select_fiz').addClass('radio_passive');
			$('#shop_order_type_select_jur').removeClass('radio_passive');
			$('#shop_order_type_select_jur').addClass('radio_active');
		}
		else
		{
			$('#shop_order_jur_organization_line, #shop_order_jur_position_line, #shop_order_jur_fax_line').hide();
			$('input#order_jur').get(0).checked = true;
			$('input#order_fiz').get(0).checked = false;
			$('#shop_order_type_select_fiz').removeClass('radio_passive');
			$('#shop_order_type_select_fiz').addClass('radio_active');
			$('#shop_order_type_select_jur').removeClass('radio_active');
			$('#shop_order_type_select_jur').addClass('radio_passive');
		}
		this.activeFormType = newType;
	}
	
	this.commitOrderForm = function()
	{
		// проверка и вывод ошибок
		if (!this.checkOrderForm())
			return false;
		
		// добавляем в форму из заказа
		var form = $('#shop_order_jur');
		$('#hidden_feilds', form).remove();
		var parent = $('<div id="hidden_feilds"></div>');
		for (var i=0; i < this.cart.length; i++)
		{
			var newEl = $('<input type="hidden" name="form[cart][]" />');
			newEl.val(this.cart[i].Code);
			parent.append(newEl);
			var newEl = $('<input type="hidden" name="form[cart_count][]" />');
			newEl.val(this.cart[i].Count);
			parent.append(newEl);
		}
		var newEl = $('<input type="hidden" name="form[type]" />');
		newEl.val(this.activeFormType);
		parent.append(newEl);
		form.append(parent);
		
		// шлем данные
		var formData = $("#shop_order_jur").serialize();
		$('#shop_order4').hide();
		$('#shop_order3_process').show();
		$.ajax({
			type: "POST",
			url: "./save/",
			data: formData,
			timeout: 10000,
			dataType: 'json',
			success: function(data, textStatus){
				alert('Заказ отправлен в отдел подписки. Спасибо!');
				$('#shop_order4').show();
				$('#shop_order3_process').hide();
				$('#order_number').text(data.orderNo);
				
				$.history.load('step3');
				if (self.activeFormType == 'jur')
				{
					$('#order_get_invoice').show().get(0).href='/subscribe/document/invoice/' + data.orderCode + '/';
					$('#order_get_bank').hide();
				}
				else
				{
					$('#order_get_bank').show().get(0).href='/subscribe/document/bank/' + data.orderCode + '/';
					$('#order_get_invoice').hide();
				}
			},
			error: function(XMLHttpRequest, textStatus, errorThrown){
				if (textStatus == "timeout")
				{
					alert('Извините, произошла ошибка при отправке заказа. Попробуйте отправить заказ еще раз или сообщите о проблеме администратору сайта (через форму обратной связи).');
				}
				else //if (textStatus == "error" || textStatus == "parsererror" || textStatus == "notmodified")
				{
					alert('Извините, во время сохранения заказа на сервере произошла ошибка. Попробуйте отправить заказ еще раз или сообщите о проблеме администратору сайта (через форму обратной связи).');
				}
				
				$('#shop_order4').show();
				$('#shop_order3_process').hide();
			}
		});
		/*
		$.post('./save/', formData, function(data)
		{
			if (requestTimeout)
				clearTimeout(requestTimeout);
			
			if (data.isError)
			{
				alert('Извините, во время сохранения заказа на сервере произошла ошибка. Сервер вернул следующий ответ: ' + data.message + '. Попробуйте отправить заказ еще раз или сообщите о проблеме администратору сайта (через форму обратной связи).');
				$('#shop_order4').get(0).disabled = false;
				$('#shop_order3_process').hide();
				return;
			}
			else
			{
				alert('Заказ успешно добавлен в базу. Спасибо!');
				$('#shop_order4').get(0).disabled = false;
				$('#shop_order3_process').hide();
				$('#order_number').text(data.orderNo);
			}
			
			$.history.load('step3');
			if (self.activeFormType == 'jur')
			{
				$('#order_get_invoice').show().get(0).href='/subscribe/document/invoice/' + data.orderCode + '/';
				$('#order_get_bank').hide();
			}
			else
			{
				$('#order_get_bank').show().get(0).href='/subscribe/document/bank/' + data.orderCode + '/';
				$('#order_get_invoice').hide();
			}
		}, "json");
		
		// async
		requestTimeout = setTimeout(function(){
			alert('Извините, произошла ошибка при отправке заказа. Попробуйте отправить заказ еще раз или сообщите о проблеме администратору сайта (через форму обратной связи).');
			$('#shop_order4').get(0).disabled = false;
			$('#shop_order3_process').hide();
		}, 20000);*/
	}
	
	this.checkOrderForm = function()
	{
		this.flushErrors();
		
		
		var form = $('#shop_order_jur');
		var email = $('#shop_order_jur_email', form);
		if (email.val() != '' && ! /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/.test(email.val()))
		{
			this.addError(email, 'Указанный e-mail не соответствует формату адресов электронной почты');
		}
		if (email.val() != '' && email.val().length > 250)
		{
			this.addError(email, 'Поле "Электронная почта" не должно содержать более 250 символов');
		}
		var address = $('#shop_order_jur_address', form);
		if (address.val() == '')
		{
			this.addError(address, 'Должно быть заполнено поле адрес');
		}
		if (address.val() != '' && address.val().length > 500)
		{
			this.addError(address, 'Поле "Почтовый адрес" не должно содержать более 500 символов');
		}
		var phone = $('#shop_order_jur_phone', form);
		if (phone.val() != '' && !this.checkPhone(phone.val()))
		{
			this.addError(phone, 'Введенный номер не соответствует формату');
		}
		if (phone.val() != '' && phone.val().length > 500)
		{
			this.addError(phone, 'Поле "Телефон" не должно содержать более 500 символов');
		}
		if (this.activeFormType == 'jur')
		{
			var fax = $('#shop_order_jur_fax', form);
			if (fax.val() != '' && !this.checkPhone(fax.val()))
			{
				this.addError(fax, 'Введенный номер не соответствует формату');
			}
			if (fax.val() != '' && fax.val().length > 500)
			{
				this.addError(fax, 'Поле "Факс" не должно содержать более 500 символов');
			}
		}
		var name = $('#shop_order_jur_name', form);
		if (name.val() != '' && name.val().length > 250)
		{
			this.addError(name, 'Поле "Фамилия Имя Отчество" не должно содержать более 250 символов');
		}
		var position = $('#shop_order_jur_position', form);
		if (position.val() != '' && position.val().length > 100)
		{
			this.addError(position, 'Поле "Должность" не должно содержать более 100 символов');
		}
		var position = $('#shop_order_jur_position', form);
		if (position.val() != '' && position.val().length > 250)
		{
			this.addError(position, 'Поле "Наименование организации" не должно содержать более 250 символов');
		}
		var comment = $('#shop_order_jur_comment', form);
		if (comment.val() != '' && comment.val().length > 2000)
		{
			this.addError(comment, 'Поле "Комментарии к заказу" не должно содержать более 2000 символов');
		}
		if (this.hasErrors())
		{
			this.showErrors();
			return false;
		}
		else
		{
			return true;
		}
	}
	
	this.checkPhone = function(phone)
	{
		// (017) 290-76-93, (017) 2907693, (017)2907693, (017) 290-7693
		var rePhoneNumber = new RegExp(/^\(\d{2,6}\)\s?((\d){1,4}-?){1,3}$/);
		var phoneArr = phone.split(',');
		for (var i=0; i < phoneArr.length; i++)
		{
			var s = phoneArr[i].replace(/^\s+|\s+$/g, ""); // trim
			if (!rePhoneNumber.test(s))
			{
				return false
			}
		}
		return true;
	}
	
	this.addError = function(feild, text)
	{
		this.formErrors.push({feild: feild, text: text});
		return this;
	}
	
	this.hasErrors = function()
	{
		return this.formErrors.length > 0;
	}
	
	this.flushErrors = function()
	{
		for (var i=0; i < this.formErrors.length; i++)
		{
			var el = this.formErrors[i].feild.parent();
			el.css('background-color', 'transparent');
			$('.error', el).text('').hide();
		}
		this.formErrors = new Array();
		return this;
	}
	
	this.showErrors = function()
	{
		alert('Извините, вы заполнили форму с неточностями. Исправьте, пожалуйста, ошибки в помеченных полях и нажмите кнопку еще раз.');
		for (var i=0; i < this.formErrors.length; i++)
		{
			var el = this.formErrors[i].feild.parent();
			el.animate({backgroundColor:"#ffd1d2"}, 1000);
			$('.error', el).append($('<div>'+this.formErrors[i].text+'</div>')).show();
		}
		return this;
	}
	
	this.toggleTooltip = function(relativeToElementId, tooltipBoxId)
	{
		var tt = $('#' + tooltipBoxId);
		if (tt.css('display') == 'none')
		{
			var relativeToElement = $('#' + relativeToElementId);
			var offset = relativeToElement.offset();
			tt.css('top', offset.top + relativeToElement.height() + 8)
				.css('left', offset.left)
				.fadeIn('slow');
			$(document.body).one('click', function(){self.toggleTooltip('', tooltipBoxId)});
		}
		else
		{
			tt.fadeOut('fast');
		}
	}
	
	// ----- Service ------
	
	this.prepareSum = function(sum)
	{
		if (Math.abs(sum) < 1000)
			return sum.toString();
		var str = sum.toString();
		var start = 0;
		var end = str.length;
		if (str.substr(0, 1) == '-')
			start = 1;
		if (str.indexOf('.') > -1)
			end = str.indexOf('.');
		var substr = str.substr(start, end);
		var processedStr = "";
		for (var i = 0; i < (substr.length) / 3; i++)
		{
			if ((i+1)*3 > substr.length)
				processedStr = "&nbsp;" + substr.substr(0, substr.length % 3) + processedStr;
			else
				processedStr = "&nbsp;" + substr.substr(substr.length - (i+1)*3, 3) + processedStr;
		}
		return str.substr(0, start) + processedStr.substr(6) + str.substr(end);
	}
	
	/**
	 * Считает количество тепов периодов и периодов в объекте подписки.
	 * Возвращает этот объект.
	 * 
	 * @param obj Object
	 * @return Object
	 */
	this.calculate = function(obj)
	{
		obj.PeriodTypesCount = 0;
		obj.PeriodsCount = 0;
		for (var periodType in obj.PeriodTypes)
		{
			obj.PeriodTypesCount++;
			var i = 0;
			for (var period in obj.PeriodTypes[periodType].Periods)
			{
				i++;
			}
			obj.PeriodTypes[periodType].PeriodsCount = i;
			obj.PeriodsCount += i;
		}
		return obj;
	}
	
	/**
	 * Возвращает массив ключей хэша
	 * 
	 * @param hash Object
	 * @return Array
	 */
	this.getHashKeys = function(hash)
	{
		result = new Array();
		for (var key in hash)
		{
			result.push(key);
		}
		
		return result;
	}
	
	/**
	 * Урезает заданную строку до данного количества символов.
	 * Если строка была уменьшена, в конец подставляются три точки (...).
	 * Урезание происходит таким образом, чтобы возвращаемая строка была
	 * гарантированно длиной не больше length.
	 * 
	 * @param str string
	 * @param length int
	 * @return string
	 */
	this.cutString = function(str, length)
	{
		if (str.length > length)
		{
			str = str.substring(0, length-3) + "...";
		}
		return str;
	}
	
	/**
	 * Кроссбраузерно вычисляет размер откна браузера
	 */
	this.getWindowSize = function()
	{
		if(typeof(window.innerWidth) == 'number')
		{
			//Non-IE
			return {width: window.innerWidth, height: window.innerHeight}
		}
		else if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight))
		{
			//IE 6+ in 'standards compliant mode'
			return {width: document.documentElement.clientWidth, height: document.documentElement.clientHeight};
		}
		else if (document.body && ( document.body.clientWidth || document.body.clientHeight))
		{
			//IE 4 compatible
			return {width: document.body.clientWidth, height: document.body.clientHeight};
		}
	}
	
    function callback(hash)
	{
    	if(hash && hash != 'step1')
    	{
			// restore state
    		if (hash == 'step2')
    		{
    			self.toStep2();
    		}
    		else if (hash == 'step3')
    		{
    			self.toStep3();
    		}
    		else
    			0; // пока некуда больше
		}
    	else
    	{
			// start page
    		if (self.activeStep != 1)
    			self.toStep1();
    		// else -- при инициализации делать ничего не надо
		}
	}
	$(document).ready(function() {
		$.history.init(callback);
	});
}

shopInstance = null;