/**
* Форма поиска
*
* @version 4.1
* Правила встраивания поисковой формы на HTML-страницу:

Для доступа к возможностям поисковой формы на странице надо прописать ссылку для загрузки
данного скрипта, вот так: 

<script src="http://exat.ru/touronline/scripts/4/common.js" charset="Windows-1251"></script>

Ссылка для загрузки скрипта может располагаться в любом месте страницы, 
но ДО вызова конструктора объекта ExatSearchForm.

После закрытия тега </form> нужно создать объект ExatSearchForm
и вызвать его метод initForm().

Все элементы управления на поисковой форме необязательны, но если в ней
встретятся элементы управления с префиксом из имени формы, сконкатенированным
с одним из зарезервированных id, то методы объекта ExatSearchForm
будут соответствующим образом манипулировать их содержимым.

Теоретически на странице может быть несколько поисковых форм, но они должны быть 
с разными id, и для каждой должен быть создан свой экземпляр объекта ExatSearchForm.

Пример:

<script src="http://exat.ru/touronline/scripts/4/common.js" charset="Windows-1251"></script>
<form id="exat" method="post">
...
</form>
<script>
var esf = new ExatSearchForm('exat', '3v234564563')
esf.initForm()
</script>
*/


/** Конструктор класса ExatSearchForm.
 * @access public
 * @param formId   - значение атрибута id тега form, для которого конструируется
 *   объект. Загрузка HTML-текста формы в момент конструирования объекта необязательна.
 * @param clientId - зашифрованный идентификатор клиентских настроек
 * @param defaults - необязательный объект, значения по умолчанию для параметров
 *   поиска, которые будут использованы при отсутствии свойств параметра values
 *   метода initForm,либо при сбросе после ошибки. Поддерживаемые свойства:
 *   departureId       - код точки отправления. По умолчанию 64(Москва).
 *   countryId         - код страны. По умолчанию 0 (не выбрана).
 *   tourTypeIds       - коды туров через запятую. По умолчанию - все типы туров.
 *   countryIncTrans   - значение галочки "только с вкл. перелётом": 0=не зависит от страны,
 *     1=зависит от страны. По умолчанию 1.
 *   resortIds         - коды курортов через запятую. По умолчанию - все курорты.
 *   minDate           - дата тура "от" в формате "DD.MM.YYYY".
 *     Пустая строка (не путать с null или undefined) = без ограничений.
 *   minDateOffset     - смещение в днях от текущей даты для даты тура "от".
 *     По умолчанию - неделя вперёд от текущей даты. Игнорируется при наличии minDate.
 *   maxDate           - дата тура "до" в формате "DD.MM.YYYY".
 *     Пустая строка (не путать с null или undefined) = без ограничений.
 *   maxDateOffset     - смещение в днях от текущей даты для даты тура "до".
 *     По умолчанию - две недели вперёд от текущей даты. Игнорируется при наличии maxDate.
 *   minDuration       - длительность тура "от" в днях. По умолчанию - 8 дней (неделя). 0=[все]
 *   maxDuration       - длительность тура "до" в днях. По умолчанию - 8 дней (неделя). 0=[все]
 *   categoryIds       - коды категорий отелей через запятую. По умолчанию - все категории.
 *   hotelIds          - коды отелей через запятую. По умолчанию - все отели (ограничены только категориями).
 *   minAmount         - цена "от" в валюте currencyId.
 *   maxAmount         - цена "до" в валюте currencyId.
 *   currencyId        - код валюты. По умолчанию - доллары, если для страны нет своей валюты по умолчанию.
 *   foodTypeIds       - коды типов питания через запятую. По умолчанию - все типы питания.
 *   serviceIds        - коды услуг через запятую. По умолчанию - все услуги.
 *   accommodation     - размещение в виде строки ВЗР_РЕБ
 *   show123Acc        - 0 или 1: отображать ли размещение 1/2/3 взрослых
 *     в списке доступных размещений, если есть DBL. По умолчанию 1.
 *   transportRequired - 0 или 1: искать ли только туры с включённым перелётом.
 *     По умолчанию 1, но зависит от countryIncTrans
 *   resortFilter      - 0 или 1, поиск маршрутов только по выбранным курортам/странам, и ни по каким другим.
 * дополнительные поля, только для профессиональной версии:
 *   tourOperatorIds   - коды туроператоров через запятую. По умолчанию - все "предпочтительные" туроператоры.
 *   includeStop       - 0 или 1: показывать ли остановленные продажи. По умолчанию 0.
 *   limit       - количество строк в странице результатов. По умолчанию 20.
 *   showTourCount     - битовая маска, управляющая показом числа туров в опциях списков выбора (1 -- отправление, 2 -- страна, 4 -- курорт, 8 -- отель). По умолчанию 0.
 *	 resortDepth			 - глубина отображения дерева курортов (1 -- показывать только подкурорты страны первого уровня, напр. Болгария / Банско)
 * @param host - необязательный параметр, указывающий имя HTTP-хоста, с которого будут подгружаться данные.
 *   По умолчанию равен "http://exat.ru"
 * @param params - необязательный параметр (ассоциативный массив).
 *   params[newWindowParams] - свойства нового окна (2-ой аргумент метода window.open)
 *     По умолчанию равен "resizable=yes, scrollbars=yes"
 */
function ExatSearchForm(formId, clientId, defaults, host, params)
{
	this.clientId = clientId;
	this.formId = formId;
	this.host = (typeof(host)=='undefined') ? "http://exat.ru" : host;
	this.params = (typeof(params)=='undefined') ? { newWindowParams : 'resizable=yes, scrollbars=yes' } : params;
	window.exatForms[formId] = this;

	// константы
	
	this.ONE_DAY_MS = 1000 * 3600 * 24; //количество миллисекунд в сутках
	this.DEF_HOURS  = 4;
	this.MAX_TOUR_DAYS     = 22; // максимальное количество дней для генерации из суточных цен

	//специальные константы для случаев, когда ничего не выбрано
	this.ALL_TOUR_TYPES     = 0xffffffff;
	this.ALL_CATEGORIES     = 0xffffffff;
	this.ALL_FOOD_TYPES     = 0xffffffff;
	this.ALL_ACCOMMODATIONS = 0xffffffff;
	this.MAX_AMOUNT         = 999999999;

	this.ACC_TEXT = new Array( '1 / 2 / 3 взр',
		'1 взр',  '2 взр',  '3 взр',  '4 взр', '5 взр',  '6 взр',  '7 взр',  '8 взр',
		'9 взр', '10 взр', '11 взр', '12 взр','13 взр', '14 взр', '15 взр', '16 и более взр',
		'1 взр + 1 реб', '1 взр + 2 реб', '1 взр + 3 реб', '1 взр + 4 и более детей',
		'2 взр + 1 реб', '2 взр + 2 реб', '2 взр + 3 реб', '2 взр + 4 и более детей',
		'3 взр + 1 реб', '3 взр + 2 реб', '3 взр + 3 реб', '3 взр + 4 и более детей',
		'4 и более взр + 1 реб', '4 и более взр + 2 реб', '4 и более взр + 3 реб', '4 и более взр + 4 и более детей'
	);
	
	this.ACC_VAL = new Array(0, 
		'1_0', '2_0', '3_0', '4_0', '5_0', '6_0', '7_0', '8_0',
		'9_0','10_0','11_0','12_0','13_0','14_0','15_0','16_0',
		'1_1', '1_2', '1_3', '1_4', '2_1', '2_2', '2_3', '2_4',
		'3_1', '3_2', '3_3', '3_4', '4_1', '4_2', '4_3', '4_4');
		

	this.CAT_TEXT    = new Array('[все]', '5*', '4*', '3*', '2*', '1*', 'HV-1', 'HV-2', 'без кат.');
	this.CAT_VAL     = new Array(this.ALL_CATEGORIES, '5', '4', '3', '2', '1', '17', '16', '0');
	this.CAT_NEW_VAL = new Array(this.ALL_CATEGORIES, '11,10', '9,8', '7,6', '5,4', '3,2', '17', '16', '0');

	this.departureData    = null; //ассоциативный массив: код точки отправления=>Array(название,маска доступных типов туров, Array(код страны, ...))
	this.tourTypeData     = null; //ассоциативный массив: код типа тура=>название типа тура
	this.countryData      = null; //ассоциативный массив: код страны=>Array(название страны, маска типов туров)
	this.foodTypeData     = null; //ассоциативный массив: код типа питания=>название типа питания
	this.currencyData     = null; //ассоциативный массив: код валюты=>название валюты
	this.servicesData     = null; //ассоциативный массив: код услуги=>Array()
	this.tourOperatorData = null; //ассоциативный массив: код туроператора=>название туроператора
	this.resortData       = [];   //ассоциативный массив: 'x'+код точки отправления+'-'+код страны => Array()

	/* Идентификаторы элементов управления внутри формы,
	 * которыми умеет манипулировать ExatSearchForm
	 * Предполагается, что на странице перед каждым идентификатором
	 * будет префикс в виде идентификатора формы
	 *
	 * Элементы управления профессиональной версии (отображение стопов, технической информации, 
	 * переключатель старого/нового вида результатов поиска) здесь не отражены, 
	 * для них обработчики надо прописывать явно
	 */
	this.controls = [
		{name:'DepartureCtrl', onchange:1},
		{name:'TourTypeCtrl', onchange:1},
		{name:'CountryCtrl', onchange:1},
		{name:'ResortCtrl', onchange:1},
		{name:'MinDateCtrl', onchange:1},
		{name:'MaxDateCtrl', onchange:1},
		{name:'MinDurationCtrl', onchange:1},
		{name:'MaxDurationCtrl', onchange:1},
		{name:'MinAmountCtrl', onchange:1},
		{name:'MaxAmountCtrl', onchange:1},
		{name:'CurrencyCtrl', onchange:1},
		{name:'CategoryCtrl', onchange:1},
		{name:'HotelCtrl', onchange:1},
		{name:'FoodTypeCtrl', onchange:1},
		{name:'AccommodationCtrl', onchange:1},
		{name:'AgeCtrl', onchange:1},
		{name:'ServiceCtrl', onchange:1},
		{name:'TourOperatorCtrl', onchange:1},
		{name:'TransportRequiredCtrl', onclick:1},
		{name:'ResortFilterCtrl', onclick:1},
		{name:'LimitCtrl', onchange:1},
		{name:'SubmitCtrl', onclick:1},
		{name:'ResetCtrl', onclick:1}
	];

	this.initTimeout = 0; //сброс таймера начальной инициализации для initForm
	this.resortException = false; //специальный случай комбинации страна/тип тура/точка отправления, по которой нет туров
	this.nextNewWindowName = this.generateNewWindowName(); //заранее определяем имя нового окна для тестирования
 	this.prefTourOperators = ''; //список предпочтительных туроператоров, действующий, когда выбран пункт "все"

	//предусмотрим вариант отсутствия аргумента values совсем
	this.defaults = (typeof(defaults)=='undefined' || defaults==null) ? {} : defaults;
	
	if(this.defaults.departureId==null)       this.defaults.departureId = 64;/* Москва */
	if(this.defaults.countryId==null)         this.defaults.countryId = 0; 
	if(this.defaults.countryIncTrans==null)   this.defaults.countryIncTrans = 1;
	if(this.defaults.minDateOffset==null)     this.defaults.minDateOffset = 7;
	if(this.defaults.maxDateOffset==null)     this.defaults.maxDateOffset = 14;
	if(this.defaults.minDuration==null)       this.defaults.minDuration = 8;
	if(this.defaults.maxDuration==null)       this.defaults.maxDuration = 8;
	if(this.defaults.minAmount==null)         this.defaults.minAmount = 0;
	if(this.defaults.maxAmount==null)         this.defaults.maxAmount = this.MAX_AMOUNT;
	if(this.defaults.currencyId==null)        this.defaults.currencyId = 'undefined'; /* not USD */
	if(this.defaults.accommodation==null)     this.defaults.accommodation = 0;
	if(this.defaults.show123Acc==null)        this.defaults.show123Acc = 1;
	if(this.defaults.transportRequired==null) this.defaults.transportRequired = 1;
	if(this.defaults.resortFilter==null)      this.defaults.resortFilter = 0;
	if(this.defaults.includeStop==null)       this.defaults.includeStop = 0;
	if(this.defaults.limit==null)             this.defaults.limit = 20;
	if(this.defaults.showTourCount==null)			this.defaults.showTourCount = 0;
	if(this.defaults.resortDepth==null)				this.defaults.resortDepth = 1;
	//загрузка дополнительных скриптов в конце конструктора инициализации, иначе Opera выполняет сначала эти подгруженные скрипты, а потом уже остальную часть конструктора
	this.AutoRefreshTimeout = 720000; //Период автоматической перезагрузки данных формы во избежание устаревания
	this.loadPrefData();
  this.loadInitData();
}


ExatSearchForm.prototype.loadPrefData = function() {
	if(typeof(exatPrefData) == 'undefined') {
  	exatPrefData = [];
  }
	//если динамические настройки предпочтения ещё не загружены для предыдущей формы, загружаем
	if(typeof(exatPrefData[this.clientId]=='undefined')) {
		document.write('<script type="text/javascript" src="'+this.host+'/touronline/?jsapp=getpreference&ver=4&client_id='+this.clientId+'" charset="Windows-1251"></script>');
	}
}


ExatSearchForm.prototype.loadInitData = function() {
	// Последний выполненный запрос будет автоматически повторяться, чтобы данные не устаревали
	if (this.LoadResortTimeout != null) {
		clearTimeout(this.LoadResortTimeout);
	}
	if (this.LoadInitTimeout != null) {
		clearTimeout(this.LoadInitTimeout);
	}
	this.LoadInitTimeout = setTimeout('exatForms.' + this.formId + '.loadInitData()', this.AutoRefreshTimeout);

	//загрузим начальные общие справочники
	var s = document.createElement('script');
	var url = this.host
		+ '/touronline/?jsapp=getjs&ver=4&file=data&form_id=' 
		+ this.formId
		+ '&showTourCount='
	  + this.defaults.showTourCount
		+ (this.clientId ? '&client_id=' + this.clientId : '');
	s.src = url;
	s.charset = 'Windows-1251';
	document.body.insertBefore(s, document.body.firstChild);
}


ExatSearchForm.prototype.getInitVal = function(name) {
	if(this.values!=undefined && this.values[name]!=null) {
		return this.values[name];
	}
	else {
		return this.defaults[name];
	}
}


ExatSearchForm.prototype.getInitArr = function(name) {
	if(this.values!=undefined && this.values[name]!=null) {
		if(this.values[name]=="")
			return [];
		else
			return String(this.values[name]).split(",");
	}
	else if(this.defaults!=undefined && this.defaults[name]!=null) {
		if(this.defaults[name]=="")
			return [];
		else
			return String(this.defaults[name]).split(",");
	}
	else {
		return [];
	}
}

/** Инициализация всех элементов управления формы.
 * @access public
 * @param values - необязательный ассоциативный массив данных инициализации формы.
 *  См. описание параметра defaults конструктора
 */
ExatSearchForm.prototype.initForm = function(values)
{
	// запомним значения, на случай, если придётся откладывать initForm по таймауту
	if(typeof(values)!='undefined' && values!=null) {
		this.values = values;
	}
	var reason = '';
	if(typeof(exatPrefData)=='undefined' || typeof(exatPrefData[this.clientId])=='undefined') {
		reason = "не загружены предпочтения клиента с client_id=" + this.clientId;
	}
	else if(this.tourOperatorData == null) {
		reason = "не загружены начальные данные (data.js)";
	}

	if (reason!='') {
		if(this.initTimeout >= 150) {
			throw new Error('Не удалось инициализировать поисковую форму в течении 30 сек.' +
				' Причина: ' + reason +
				'. Попробуйте перезагрузить страницу (удерживая клавишу Ctrl, нажмите F5).' +
				' В случае повторного появления ошибки ' +
				'обратитесь в службу технической поддержки');
			return;
		}
		else {
			this.initTimeout++;
			setTimeout('window.exatForms[\''+this.formId+'\'].initForm()', 200);
			return; 
		}
	}

	this.form = document.getElementById(this.formId);
	if(!this.form) {
		this.reportError('Форма с id="'+this.formId+'" не найдена');
		return;
	}
	
	// запрет сабмита по нажатие Enter в текстовом поле формы, так как в этом случае не запускается обработчик
	this.form.onsubmit=function(){return false;};

	this.departureId = this.getInitVal('departureId');
	this.countryId = this.getInitVal('countryId');

	this.resortIds = [];
	var ids = this.getInitArr('resortIds');
	for(var i=0; i<ids.length; ++i) {
		this.resortIds[Number(ids[i])] = true;
	}

	this.tourTypeMask = 0;
	var ids = this.getInitArr('tourTypeIds');
	for(i=0; i<ids.length; ++i) {
		this.tourTypeMask |= ids[i];
	}
	if(!this.tourTypeMask) {
		this.tourTypeMask=this.ALL_TOUR_TYPES;
	}
	
	var dNow = new Date();
	dNow.setHours(this.DEF_HOURS);
	dNow.setMinutes(0);
	dNow.setSeconds(0);
	dNow.setMilliseconds(0);
	
	var minDate = this.getInitVal('minDate');
	if(minDate == null) {
		this.minTimestamp = Number(dNow.getTime()
			+ this.ONE_DAY_MS * this.getInitVal('minDateOffset'));
	}
	else if(minDate == ''){
		this.minTimestamp = 0;
	}
	else {
		// у нас граница суток считается в 4 часа ночи, что бы не было проблем со сдвигом времени
		this.minTimestamp = exatToTime(minDate)+this.DEF_HOURS*1000*3600;
	}
	
	var maxDate = this.getInitVal('maxDate');
	if(maxDate == null) {
		this.maxTimestamp = Number(dNow.getTime()
			+ this.ONE_DAY_MS * this.getInitVal('maxDateOffset'));
	}
	else if(maxDate == ''){
		this.maxTimestamp = 0;
	}
	else {
		this.maxTimestamp = exatToTime(maxDate) +this.DEF_HOURS*1000*3600;
	}
	
	this.minDuration = Number(this.getInitVal('minDuration'));
	this.maxDuration = Number(this.getInitVal('maxDuration'));

	this.minAmount = Number(this.getInitVal('minAmount'));
	this.maxAmount = Number(this.getInitVal('maxAmount'));

	this.foodTypeMask = 0;
	var ids = this.getInitArr('foodTypeIds');
	for(var i = 0; i < ids.length; ++i) {
		this.foodTypeMask |= 1<<(ids[i]-1);
	}
	if(!this.foodTypeMask) {
		this.foodTypeMask=this.ALL_FOOD_TYPES;
	}

	this.categoryMask = 0;
	var ids = this.getInitArr('categoryIds');
	for(var i = 0; i < ids.length; ++i) {
		this.categoryMask |= 1<<ids[i];
	}
	if(!this.categoryMask) {
		this.categoryMask=this.ALL_CATEGORIES;
	}

	this.hotelIds = this.getInitArr('hotelIds');
	this.serviceIds = this.getInitArr('serviceIds');
	this.show123Acc = this.getInitVal('show123Acc');

	this.accommodationId = 0;
	var acc = this.getInitVal('accommodation');
	if(acc!=0 && acc.indexOf('_')==-1)
		acc=acc+'_0';
	for(i = 0; i < this.ACC_VAL.length; ++i) {
		if(acc == this.ACC_VAL[i]) {
			this.accommodationId = i;
			break;
		}
	}
	this.ages = '';

	this.transportRequired = this.getInitVal('transportRequired');
	this.countryIncTrans   = this.getInitVal('countryIncTrans');
	this.resortFilter      = this.getInitVal('resortFilter');
	this.includeStop       = this.getInitVal('includeStop');
	this.limit             = this.getInitVal('limit');
	this.showTourCount     = this.getInitVal('showTourCount');
	this.resortDepth			 = this.getInitVal('resortDepth');

	this.resetTourOperatorEvent = false;

	this.categoryRange      = this.ALL_CATEGORIES;      //доступные для выбора категориии в зависимости от выбранных курортов
	this.foodTypeRange      = this.ALL_FOOD_TYPES;      //доступные для выборп типы питания в зависимости от выбранных курортов
	this.accommodationRange = this.ALL_ACCOMMODATIONS;  //доступные для выбора размещения в зависимости от выбранного курорта
	this.accommodationHandSelectedId = -1;               //вручную выбранное размещение из массива this.ACC_TEXT

	this.clearControls();
	
	this.initDepartureCtrl();
	this.initTourTypeCtrl();
	this.initCountryCtrl();
	this.initServicesCtrl();
	this.initCurrencyCtrl();
	
	if(this.resortException) {
		var c = this.form.elements[this.formId + 'ResortCtrl'];
		if(c) {
			c.appendChild(exatCreateOptionElement('', 'Нет туров по маршруту.'));
			c.appendChild(exatCreateOptionElement('', 'Выберите другой маршрут'));
			c.appendChild(exatCreateOptionElement('', 'или другой тип тура.'));
			c.disabled = true;
		}
	}

	if (this.countryId) {
		if (this.resortData['x' + this.departureId + '-' + this.countryId]) {
			this.fillResort();
		} else {
			this.asyncLoadResort();
		}
	}
	this.initTransportRequiredCtrl();
	this.initResortFilterCtrl();
	this.initLimitCtrl();

	var ctrl = this.form.elements[this.formId + 'MinAmountCtrl'];
	if(ctrl) {
		ctrl.value = (this.minAmount>0) ? this.minAmount : '';
	}

	ctrl = this.form.elements[this.formId + 'MaxAmountCtrl'];
	if(ctrl) {
		ctrl.value = (this.maxAmount < this.MAX_AMOUNT && this.maxAmount>0) ? this.maxAmount : '';
	}
	//установка обработчиков событий
	for (var i = 0; i < this.controls.length; i++) {
		var c = this.form.elements[this.formId + this.controls[i].name];

		if(c) {
			if(!c.onchange && this.controls[i].onchange) {
				c.onchange = new Function(
					"window.exatForms['" + this.formId + "'].on"
					+ this.controls[i].name + "Change" + "()");
			}
			if(!c.onclick && this.controls[i].onclick) {
				c.onclick = new Function(
					"window.exatForms['" + this.formId + "'].on"
					+ this.controls[i].name + "Click" + "()");
			}
		}
	}
	
	this.enableControls();
	this.resortException = false;
	
	if(typeof(this.values)!='undefined') {
		delete this.values;
	}

	this.initTimeout = 0;
}


/** Подготовить массив данных поискового запроса клиентской версии
 * Каждый элемент массива - пара (имя, строка)
 * В строке может быть несколько значений через запятую
 * Можно вызывать для самостоятельной отправки запроса, не через функцию submitForm
 * Совпадает по именам полей с сервисом xml_tours, кроме двух доп. полей client_id и lcc
 * @access public
 * @return объект, где свойства содержат данные поискового запроса
 */
ExatSearchForm.prototype.getSubmitData = function()
{
	var q = [
		['departureId', this.departureId],
		['lcc',         this.countryId],
		['currencyId',  this.currencyId],
		['maxGenDays',  this.MAX_TOUR_DAYS],
		['limit',       this.limit]
	];

	if(this.form.client_id){
		this.form.client_id.value=this.clientId;
	}else{
		q.push(['client_id',  this.clientId]);
	}

	if(this.transportRequired > 0) {
		q.push(['transportRequired',  1]);
	}

	if(this.resortFilter > 0) {
		q.push(['selectedPlacesOnly',  1]);
	}

	if(this.includeStop > 0) {
		q.push(['includeStop',  1]);
	}

	if(!this.resortIds || !this.resortIds.length) {
		q.push(['placeGroupId[]', this.countryId ]);
		q.push(['placeItemId[]', this.countryId ]);
	}
	else {
		var resortStr = exatJoinNumbers(this.resortIds);
		if(resortStr.length) {
			q.push(['placeGroupId[]', resortStr ]);
			q.push(['placeItemId[]', resortStr ]);
		}
		else {
			q.push(['placeGroupId[]', this.countryId ]);
			q.push(['placeItemId[]', this.countryId ]);
		}
	}
	if(this.tourTypeMask!=this.ALL_TOUR_TYPES) {
		var a = '';
		for(var i=0; i<31; ++i) {
			if(this.tourTypeMask & (1<<i)) {
				a += (a.length ? ',' : '') + (1<<i);
			}
		}
		q.push(['tourTypeId[]', a]);
	}
	if(this.categoryMask!=this.ALL_CATEGORIES) {
		var a = '';
		for(var i=0; i<31; ++i) {
			if(this.categoryMask & (1<<i)) {
				a += (a.length ? ',' : '') + i;
			}
		}
		q.push(['categoryId[]', a]);
	}
	if(this.foodTypeMask!=this.ALL_FOOD_TYPES) {
		var a = '';
		for(var i=0; i<31; ++i) {
			if(this.foodTypeMask & (1<<i)) {
				a += (a.length ? ',' : '') + (i+1);
			}
		}
		q.push(['foodTypeId[]', a]);
	}
	if(this.minTimestamp > 0) {
		q.push(['minDate', exatFromTime(this.minTimestamp)]);
	}
	if(this.maxTimestamp > 0) {
		q.push(['maxDate', exatFromTime(this.maxTimestamp)]);
	}
	if(this.minDuration > 0) {
		q.push(['minDuration', this.minDuration]);
	}
	if(this.maxDuration > 0) {
		q.push(['maxDuration', this.maxDuration]);
	}
	if(this.minAmount > 0) {
		q.push(['minAmount', this.minAmount]);
	}
	if(this.maxAmount!=this.MAX_AMOUNT) {
		q.push(['maxAmount', this.maxAmount]);
	}
	if(this.accommodationId > 0) {
		q.push(['accommodation', this.ACC_VAL[this.accommodationId]]);
	}
	if(this.ages && this.ages.length) {
		q.push(['ages[]', this.ages]);
	}
	var hotels = exatJoinNumbers(this.hotelIds);//у отелей могут быть отрицательные id, поэтому свойство length массива проверять нельзя
	if(hotels.length) {
		q.push(['hotelId[]', hotels]);
	}
	if(this.serviceIds.length) {
		q.push(['serviceId[]', exatJoinNumbers(this.serviceIds)]);
	}
	if(this.tourOperatorIds && this.tourOperatorIds.length) {
		var str = exatJoinNumbers(this.tourOperatorIds);
		if(str.length) {
			q.push(['tourOperatorId[]', str]);
		}
	}
	else {
		if(this.prefTourOperators.length > 0) {
			q.push(['tourOperatorId[]', this.prefTourOperators]);
		}
	}
	return q;
}

/** Сконвертировать объект с параметрами запроса в строку суффикса
 * @access public
 * @param sdata объект с параметрами для конвертации в строку запроса,
 *   полученный, например, через getSubmitData
 * @return URL suffix to append to search query
 */
ExatSearchForm.prototype.getSubmitSuffix = function(sdata)
{
	var result = '';
	for(var i=0; i< sdata.length; ++i) {
		result += '&' + sdata[i][0] + '=' + sdata[i][1];
	}
	return result;
}

/** Получить стандартный префикс для action
 * @access public
 * @return строка префикса
 */
ExatSearchForm.prototype.getSubmitPrefix = function()
{
	return this.host + '/touronline/result-v2.php?';
}

/** Отправить поисковый запрос
 * @access public
 * @param openNewWindow - если true, то будет открыто новое окно
 * @param customAction - если не пуст, то запрос будет отправлен на указнный action url
 */
ExatSearchForm.prototype.submitForm = function(openNewWindow, customAction)
{
	if (!this.checkParameters()) {
		return false;
	}
	holder=window.document.location.protocol+'//'+window.document.location.hostname+'/';
	this.form.action = (customAction ? customAction : this.getSubmitPrefix())
		+ this.getSubmitSuffix(this.getSubmitData())
		+'&holder='+str2code(holder) //escape(window.document.location)
	;
	if(openNewWindow) {
		var wnd  = window.open('', this.nextNewWindowName, this.params['newWindowParams']); 
	
		wnd.document.write('Пожалуйста, подождите. Идет поиск туров...');
		wnd.focus();
		this.form.target = this.nextNewWindowName;
		this.nextNewWindowName = this.generateNewWindowName();
	}
	this.form.submit();
}

ExatSearchForm.prototype.onSubmitCtrlClick = function()
{
	this.submitForm(true);
}

ExatSearchForm.prototype.onResetCtrlClick = function()
{
	this.initForm();
}

ExatSearchForm.prototype.resetForm = function()
{
	this.initForm();
}

ExatSearchForm.prototype.generateNewWindowName = function()
{
	return 'result_' + exatGenRandomStr(10);
}


/** Динамическая асинхронная загрузка данных курортов и др. после выбора страны.
 * @access protected, но предназначена для вызова только из скриптов системы "Ехать!"
 */
ExatSearchForm.prototype.asyncLoadResort = function() {
	// Последний выполненный запрос будет автоматически повторяться, чтобы данные не устаревали
	if (this.LoadResortTimeout != null) {
		clearTimeout(this.LoadResortTimeout);
	}
	if (this.LoadInitTimeout != null) {
		clearTimeout(this.LoadInitTimeout);
	}
	this.LoadResortTimeout = setTimeout('exatForms.' + this.formId + '.asyncLoadResort()', this.AutoRefreshTimeout);

	var s = document.createElement('script');
	var url = this.host + '/touronline/?jsapp=getjs&ver=4&file=data-'
		+ this.departureId + '-' + this.countryId
		+ '&form_id=' + this.formId
	  + '&showTourCount=' + this.showTourCount
		+ (this.clientId ? '&client_id=' + this.clientId : '');
	s.src = url;
	s.charset = 'Windows-1251';
	document.body.insertBefore(s, document.body.firstChild);
}

/** Заполнение списка курортов и зависимых от них данных.
 * @access public, но предназначена для вызова только из скриптов системы "Ехать!"
 */
ExatSearchForm.prototype.fillResort = function()
{
	this.initResortCtrl();
	this.initLCDCtrl();
	this.initCategoryCtrl();
	this.initFoodTypeCtrl();
	this.initAccommodationCtrl();
	this.initHotelCtrl();
	this.initTourOperatorCtrl();

	this.enableControls();
}

/** Заполнение данных, привязанных к текущим выбранным точке отправления и стране
 * @access public, но для вызова только из скриптов "Ехать!"
 * @param departureId - код точки отправления
 * @param countryId - код страны
 * @param propName - имя свойства, куда вписать массив данных
 * @param propVal - данные
 */
ExatSearchForm.prototype.setResortParam = function(departureId, countryId, propName, propVal) {
	var id = 'x' + departureId + '-' + countryId;
	if (!this.resortData[id]) {
		this.resortData[id] = [];
	}
	this.resortData[id][propName] = propVal;
}

/** Получить данные свойства, учитывая выбранную точку отправления и страну
 * @access protected
 * @param propName имя свойства
 * @return null, если не выбрана страна или данные ещё не загружены, иначе -
 *   значение свойства с именем propName
 */
ExatSearchForm.prototype.getResortData = function(propName)
{
	if (!this.countryId) {
		return null;
	}

	var id = 'x' + this.departureId + '-' + this.countryId;
	if (!this.resortData[id]) {
		this.asyncLoadResort();//отдадим команду на загрузку данных
		return null;//но не будем дожидаться загрузки
	}

	return this.resortData[id][propName];
}

/** Возвратить строку символов с объёдинёнными битами дат/длительностей по курортам
 * В каждом символе используется только шесть бит
 * @param indx 0=даты, 1=длительности
 * @return строка с символами с установленными битами
 */
ExatSearchForm.prototype.getString6 = function(indx) 
{
	var r = this.getResortData('lcd');
	if(r==null) {
		reportWarning("Не выбрана страна или не загружены данные курортов");
		return '';
	}

	var res = '';
	var allResorts = true; //выбраны [все] курорты
	for(var resortId in this.resortIds) {
		if(resortId != Number(resortId)) continue;
		allResorts = false;
		break;
	}
	
	for(resortId in r) {
		if(resortId != Number(resortId)) continue;
		if(allResorts || this.resortIds[resortId]) {
			var str = r[resortId][indx];
			if (str.length < res.length) {
				// Если строка с упакованными датами короче текущего значения res, дополним ее пустышками
				var padLength = res.length - str.length;
				var padString = "";
				for (var i = 0; i < padLength; i++) {
					padString += '?';
				}
        str += padString;
			}
			var len = str.length;
			if(res=='') {
				for (var i = 0; i < len; i++) {
					res += '?';
				}
			}
			var buffer = '';
			for (var j = 0; j < len; j++) {
				buffer += String.fromCharCode(((str.charCodeAt(j) - 63) | (res.charCodeAt(j) - 63)) + 63);
			}
			res = buffer;
		}
	}

	return res;
}

/** Заполнить список значений single select "точка отправления" и выбрать одно
 * в соответствии с this.departureId
 * @access protected
 */
ExatSearchForm.prototype.initDepartureCtrl = function()
{
	var c = this.form.elements[this.formId +'DepartureCtrl'];
	if (!c || c.nodeName != 'SELECT') {
		return 1;
	}
	
	exatClearCtrl(c);

	var arr = this.departureData;
	var f = false;

	for (var i in arr) {
		if(i!=Number(i)) continue; //пропустим нечисловые свойства
		var o = exatCreateOptionElement(i, arr[i][0]);
	
		if (i == this.departureId) {
			f = true;
			o.setAttribute('selected', 'selected');
		}
		c.appendChild(o);
	}

	if (!f){
		//не нашли, выбираем первую попавшуюся точку отправления
		if(c.firstChild) {
			c.firstChild.setAttribute('selected', 'selected');
			this.departureId = c.firstChild.value;
		}
		else {
			this.departureId = 0;
			this.reportError("Нет доступных точек отправления");
		}
	}
}

/** Заполнить список значений single/multi select "типы туров"
 * и выбрать одно или несколько значений в соответствии с this.tourTypeMask
 * @access protected
 */
ExatSearchForm.prototype.initTourTypeCtrl = function()
{
	var c = this.form.elements[this.formId + 'TourTypeCtrl'];

	if(!c) {
		return 1;
	}

	exatClearCtrl(c);

	//битовая маска доступных типов туров в текущей точке отправления
	var availableTourTypes = this.departureData[this.departureId][1];

	var firstElement = exatCreateOptionElement(0, '[все]');
	if(this.tourTypeMask == this.ALL_TOUR_TYPES) {
		firstElement.setAttribute('selected', true);
	}

	c.appendChild(firstElement);
	var allSelected = true;

	//в tourTypeId установлен всегда только один бит
	for(var tourTypeId in this.tourTypeData) {
		if (tourTypeId!=Number(tourTypeId) || !(tourTypeId & availableTourTypes)) {
			continue;
		}

		var o = exatCreateOptionElement(tourTypeId, this.tourTypeData[tourTypeId]);

		if (!(tourTypeId & this.tourTypeMask)) {
			this.tourTypeMask &= ~tourTypeId;
			allSelected = false;
		}

		c.appendChild(o);
	}
	
	if(allSelected) {
		exatSelectChild(firstElement);
	}
	else {
		for (var opt = c.firstChild.nextSibling; opt; opt = opt.nextSibling) {
			if(this.tourTypeMask & opt.value) {
				exatSelectChild(opt);
			}
		}
	}
}

/** Заполнить список значений для single select "страна" и выбрать одно значение
 * в соответствии с this.countryId
 * @access protected
 */
ExatSearchForm.prototype.initCountryCtrl = function()
{
	var c = this.form.elements[this.formId + 'CountryCtrl'];
	if(!c) {
		return;
	}
	exatClearCtrl(c);

	var f = false;

	var availableCountryIds = this.departureData[this.departureId][2];

	c.appendChild(exatCreateOptionElement(0, 'выберите страну'));
	for (var i = 0; i <availableCountryIds.length; ++i) {
		var countryId = availableCountryIds[i];
		if (!(this.countryData[countryId] && this.countryData[countryId][1] & this.tourTypeMask)) {
			continue;
		}

		var o = exatCreateOptionElement(countryId, this.countryData[countryId][0]);
		
		if(countryId == this.countryId)
		{
			f = true;
			o.setAttribute('selected', 'selected');
		}

		c.appendChild(o);
	}

	if (!f) {
		this.countryId = 0;
	}

	//this.resortIds = [];
}

/** Установить значение checkbox "только с включённым перелётом"
 * в соответствии с this.transportRequired, поменяв перед этим при необходимости
 * значение  и this.transportRequired в соответствии с  this.countryIncTrans
 * @access protected
 */
ExatSearchForm.prototype.initTransportRequiredCtrl = function()
{
	if(this.countryId > 0 && this.countryIncTrans > 0 &&
		 typeof(exatPrefData[this.clientId].prefIncludeCheckbox) != 'undefined' &&
	   typeof(exatPrefData[this.clientId].prefIncludeCheckbox[this.countryId]) != 'undefined')
	{
		this.transportRequired = exatPrefData[this.clientId].prefIncludeCheckbox[this.countryId] ? 1 : 0;
	} else {
		this.transportRequired = this.defaults.transportRequired;
	}

	var c = this.form.elements[this.formId+'TransportRequiredCtrl'];

	if(c) {
		exatSetCheckedValue(c, this.transportRequired);
	}
}

/** Заполнить список значений single/multi select "курорты" и выбрать одно
 * или несколько значений в соответствии с массивом this.resortIds
 * @access protected
 */
ExatSearchForm.prototype.initResortCtrl = function() {
	var c = this.form.elements[this.formId+'ResortCtrl'];

	if(!c) {
		return 1;
	}

	exatClearCtrl(c);

	if(!Number(this.countryId)) {
		return 0;
	}

	this.foodTypeRange      = this.ALL_FOOD_TYPES;
	this.categoryRange      = this.ALL_CATEGORIES;
	this.accommodationRange = this.ALL_ACCOMMODATIONS;
	
	var resortData = this.getResortData('resorts');
	if (!resortData) {
		return 0;
	}
	

	var firstElement = exatCreateOptionElement(this.countryId, '[все]');
	firstElement.setAttribute('selected', true);

	c.appendChild(firstElement);

	var filled = false;

	for (var resortId in resortData) {
		if (resortId!=Number(resortId) || !(resortData[resortId][1] & this.tourTypeMask)){
			continue;
		}

		if (resortData[resortId][4] && (resortData[resortId][4].length > ((this.resortDepth + 1) * 7))) {
			// отображать курорт не нужно, задана меньшая глубина дерева
			continue;
		}

		filled = true;
		var opt = exatCreateOptionElement(resortId, resortData[resortId][0]);


		if(this.resortIds[resortId]) {
			opt.setAttribute('selected', 'selected');
			this.resortIds[resortId] = 2;
		}
		var optt = c.appendChild(opt);
		if (resortData[resortId][0].charAt(0) != String.fromCharCode(160)) {
			optt.style.fontWeight = "bold";
			//optt.style.color = "blue";

		}		
	}

	//удалим из resortIds невыбранные курорты
	for(resortId in this.resortIds) {
		if(resortId == Number(resortId) && this.resortIds[resortId] != 2) {
			delete this.resortIds[resortId];
		}
	}

	this.resortException = false;

	if (!filled) {
		this.resortException = true;
		return -1;
	}

	return 0;
}

/** Установить значение checkbox "туры только в выбранные курорты"
 * в соответствии с this.resortFilter
 * @access protected
 */
ExatSearchForm.prototype.initResortFilterCtrl = function()
{
	var c = this.form.elements[this.formId+'ResortFilterCtrl'];

	if(!c) {
		return 1;
	}
	
	if (c && c.nodeName == 'INPUT' && c.getAttribute('type') == 'checkbox') {
		c.checked = this.resortFilter;
	}
}


/** Заполнить список значений single select'ов "дата от"/"до",
 * длительность от"/"до" и выбрать значения в соответствии
 * с this.minTimestamp, this.maxTimestamp, this.minDuration, this.maxDuration,
 * а также this.transportRequired
 * @access protected
 */
ExatSearchForm.prototype.initLCDCtrl = function()
{
	var oCl, oCh;
	var cl, ch;
	var ddstr;

	var symb;
	var tmpDate;
	var val, name;

	var d0         = this.getResortData('date');
	if(d0==null) { //данные не подгружены (возможно, туров нет в этой связке точка отправления/страна)
		return;
	}
	var dNow       = new Date();

	// Даты
	d0.setHours(this.DEF_HOURS);
	d0.setMinutes(0);
	d0.setSeconds(0);
	d0.setMilliseconds(0);
	
	dNow.setHours(this.DEF_HOURS);
	dNow.setMinutes(0);
	dNow.setSeconds(0);
	dNow.setMilliseconds(0);
	
	var zeroTime = d0.getTime();
	var nowTime  = dNow.getTime();

	cl = this.form.elements[this.formId + 'MinDateCtrl'];
	ch = this.form.elements[this.formId + 'MaxDateCtrl'];

	if(cl) {
		exatClearCtrl(cl);
		oCl = exatCreateOptionElement(0, '[все]');
		cl.appendChild(oCl);
	}
	
	if (ch) {
		exatClearCtrl(ch);
		oCh = exatCreateOptionElement(0, '[все]');
		ch.appendChild(oCh);
	}

	var loSel = null;
	var hiSel = null;

	ddstr  = this.getString6(0);

	for (j = 0; j < ddstr.length; j++) {
		
		symb = ddstr.charCodeAt(j) - 63;
		
		if (symb) {
			for (var l = 1; l < 7; l++) {
				if (Math.pow(2, l - 1) & symb) {
					tmpTime     = zeroTime + this.ONE_DAY_MS * (j * 6 + l);
					val         = tmpTime;
					name        = exatGetNiceDate(tmpTime);

					if(cl) {
						oCl = cl.appendChild(exatCreateOptionElement(val, name));
						if(this.minTimestamp > 0 
							&& tmpTime <= this.minTimestamp
						) {
							//пытаемся выбрать предыдущую дату "от", если нет - то ближайшую более раннюю
							loSel = oCl;
						}
					}
	
					if(ch) {
						oCh = ch.appendChild(exatCreateOptionElement(val, name));
						if(this.maxTimestamp > 0 && !hiSel && tmpTime >= this.maxTimestamp) {
							//пытаемся выбрать предыдущую дату "до", если нет - то ближайшую более позднюю
							hiSel = oCh;
						}
					}
				}
			}
		}
	}
	if(cl) {
		loSel = loSel ? loSel : cl.firstChild;
		this.minTimestamp = loSel.value;
		exatSelectChild(loSel);
	}
	if(ch) {
		hiSel = hiSel ? hiSel : ch.firstChild;
		this.maxTimestamp = hiSel.value;
		exatSelectChild(hiSel ? hiSel : ch.firstChild);
	}

	
	// Продолжительности

	cl = this.form.elements[this.formId + 'MinDurationCtrl'];
	ch = this.form.elements[this.formId + 'MaxDurationCtrl'];
	
	if(cl) {
		exatClearCtrl(cl);
		oCl = exatCreateOptionElement(0, '[все]');
		cl.appendChild(oCl);
	}
	
	if (ch) {
		exatClearCtrl(ch);
		oCh = exatCreateOptionElement(0, '[все]');
		ch.appendChild(oCh);
	}

	ddstr = this.getString6(1);

	loSel = null;
	hiSel = null;

	if (this.transportRequired || ((ddstr.charCodeAt(0) - 63) & 0x03) == 0) {
		//если "только с перелётом" или нет туров на 1/2 дня (т.е. суточных)
		for (var j = 0; j < ddstr.length; j++) {
			symb = ddstr.charCodeAt(j) - 63;
			if (symb) {
				
				//пропустим в for'e туры на 1 день, т.к. это признак "суток",
				//а "сутки" у нас сейчас автоматически складываются
				for (var m = 1 + (j?0:1); m < 7; m++) {
					
					
					if ( (1 << (m - 1)) & symb) {
						
						
						
						val  = (j * 6 + m);
						name = val;

						if(cl) {
							oCl = exatCreateOptionElement(val, name);
							if(this.minDuration > 0 && val <= this.minDuration) {
								loSel = oCl;
							}
							cl.appendChild(oCl);
						}
						
						if(ch) {
							oCh = exatCreateOptionElement(val, name);
							if(this.maxDuration > 0 && !hiSel && val >= this.maxDuration) {
								hiSel = oCh;
							}
							ch.appendChild(oCh);
						}
					}
				}
			}
		}
	}
	else { 
		for(var j = 2; j <= this.MAX_TOUR_DAYS; j++) {
			if(cl) {
				oCl = exatCreateOptionElement(j, j);
				if (this.minDuration > 0 && j <= this.minDuration) {
					loSel = oCl;
				}
				cl.appendChild(oCl);
			}
			
			if(ch) {
				oCh = exatCreateOptionElement(j, j);
				if (this.maxDuration > 0 && !hiSel && j >= this.maxDuration) {
					hiSel = oCh;
				}
				ch.appendChild(oCh);
			}
		}
		
	}

	if(cl) {
		loSel = loSel ? loSel : cl.firstChild;
		this.minDuration = loSel.value;
		exatSelectChild(loSel);
	}
	if(ch) {
		hiSel = hiSel ? hiSel : ch.firstChild;
		this.maxDuration = hiSel.value;
		exatSelectChild(hiSel ? hiSel : ch.firstChild);
	}

	return true;
}


/** Заполнить список значений single/multi select "категория отеля",
 * в соответствии с this.categoryIds
 * @access protected
 */
ExatSearchForm.prototype.initCategoryCtrl = function()
{
	var c = this.form.elements[this.formId + 'CategoryCtrl'];
	
	if(!c) {
		return;
	}

	exatClearCtrl(c);
	
	firstElement = exatCreateOptionElement(this.CAT_VAL[0], this.CAT_TEXT[0]);
	if(this.categoryMask==this.ALL_CATEGORIES || this.categoryMask==0) {
		exatSelectChild(firstElement);
	}

	c.appendChild(firstElement);

	var mask = 0;

	for (var i  = 1; i < this.CAT_TEXT.length; i++) {
		var element = exatCreateOptionElement(this.CAT_NEW_VAL[i], this.CAT_TEXT[i]);

		var vals = this.CAT_NEW_VAL[i].split(",");
		for(var j=0; j <vals.length; ++j) {
			if (this.categoryMask != this.ALL_CATEGORIES && (1 << vals[j] & this.categoryMask)) {
				exatSelectChild(element);
				mask |= 1 << vals[j];
			}
		}
		c.appendChild(element);
	}
}

/** Заполнить список значений single/multi select "типы питания",
 * в соответствии с this.foodTypeMask и доступными типами питания
 * @access protected
 */
ExatSearchForm.prototype.initFoodTypeCtrl = function()
{
	var c = this.form.elements[this.formId + 'FoodTypeCtrl'];

	if(!c) { //this control is optional
		return;
	}

	exatClearCtrl(c);

	var availableFoodTypes = this.getResortData('foodTypes');
	
	if (!availableFoodTypes) {
		return;
	}
	
	firstElement = exatCreateOptionElement(this.ALL_FOOD_TYPES, '[все]');
	c.appendChild(firstElement);

	var m = 0;

	//собираем маску доступных типов питания в зависимости от выбранных типов туров
	for (var tourTypeId in availableFoodTypes) {
		if (tourTypeId==Number(tourTypeId) && (this.tourTypeMask & tourTypeId)) {
			m |= availableFoodTypes[tourTypeId];
		}
	}

	//ограничиваем маску только питанием, доступным в выбранных курортах
	m &= this.foodTypeRange;

	var newMask       = 0;
	var selectedCount = 0;

	//заполняем новую маску выбранных типов питания newMask
	for(var foodTypeId in this.foodTypeData) {
		if(foodTypeId != Number(foodTypeId)) {
			continue; // пропускаем нечисловые свойства, которые могут появиться
			          // из-за прототипирования
		}
		if(m & (1<<(foodTypeId-1))) {
			o = exatCreateOptionElement(foodTypeId, this.foodTypeData[foodTypeId]);
			c.appendChild(o);
			if(this.foodTypeMask & (1<<(foodTypeId-1))) {
				newMask |= (1<<(foodTypeId-1));
				selectedCount++;
			}
		}
	}
	if (selectedCount == (c.length - 1)) {
		exatSelectChild(c.firstChild);
		newMask = this.ALL_FOOD_TYPES;
	}
	else {
		for (o = c.firstChild; o; o=o.nextSibling) {
			if(newMask & (1 << (o.getAttribute('value')-1) )) {
				exatSelectChild(o);
			}
		}
	}
	this.foodTypeMask = newMask;
}

/** Заполнить список значений single select "размещение",
 * в соответствии с this.accommodationId и доступными размещениями
 * @access protected
 */
ExatSearchForm.prototype.initAccommodationCtrl = function()
{
	var c = this.form.elements[this.formId + 'AccommodationCtrl'];

	if(!c) {
		return;
	}

	exatClearCtrl(c);


	var availableAccommodations = this.getResortData('accommodations');
	if (!availableAccommodations) {
		return;
	}

	var m = 0;
	
	//собираем маску доступных размещений в зависимости от выбранных типов туров
	for (var tourTypeId in availableAccommodations) {
		if (tourTypeId==Number(tourTypeId) && (this.tourTypeMask & tourTypeId)) {
			m |= availableAccommodations[tourTypeId];
		}
	}

	//ограничиваем маску только размещениями, доступными в выбранных курортах
	m &= this.accommodationRange;

	var firstId = null;
	
	if ( this.show123Acc && (m & 2)) {
		// поиск по 1/2/3 приведёт к каким-то результатам, если есть 2 взр.
		o = exatCreateOptionElement(this.ACC_VAL[0], this.ACC_TEXT[0]);
		c.appendChild(o);
		firstId = 0;
	}

	var newId = null;
	
	for (i = 0; i < 32; i++) {
		if (m & (1 << i)) {
			if(firstId==null) {
				firstId = i+1;
			}
			o = exatCreateOptionElement(i+1, this.ACC_TEXT[i+1]);
			if(this.accommodationHandSelectedId >=0) {
				if(this.accommodationHandSelectedId==i+1) {
					o.setAttribute("selected","selected");
					newId = i+1;
				}
			}
			else {
				if(this.accommodationId==i+1) {
					o.setAttribute("selected","selected");
					newId = i+1;
				}
			}
			c.appendChild(o);
		}
	}

	if (newId!=null) {
		this.accommodationId = newId;
	}
	else {
		if(firstId != null) {
			this.accommodationId = firstId;
		}
		else {
			this.accommodationId = 0;
		}
	}
}

/** Заполнить список значений single/multi select "отель",
 * в соответствии с this.hotelIds и доступными отелями
 * @access protected
 */
ExatSearchForm.prototype.initHotelCtrl = function()
{
	var c = this.form.elements[this.formId+'HotelCtrl'];

	if(!c) {
		return;
	}

	exatClearCtrl(c);
	var availableHotels = this.getResortData('hotels');
	if (!availableHotels) {
		return;
	}
	
	var f  = false; //признак уже встретившейся фортуны
	var hotelSelected = false; //был ли выбран хотя бы один отель
	var tm = this.tourTypeMask;
	var fm = this.foodTypeMask & this.foodTypeRange;
	var am = this.accommodationRange;
	var cm = (this.categoryMask == this.ALL_CATEGORIES) ? 0xffffffff : this.categoryMask;

	firstElement = exatCreateOptionElement('', '[все]');
	c.appendChild(firstElement);

	var allResortsSelected = true;
	for(var resortId in this.resortIds) {
		if(resortId != Number(resortId)) continue;
		allResortsSelected = false;
		break;
	}
	var strHotelIds=","+this.hotelIds.join(",")+",";
	for (var i =0; i < availableHotels.length; ++i) {
		var item = availableHotels[i];

		if (!((1 << item[2]) & cm)) {
			continue;
		}

		if (!(item[3] & tm) || !(item[4] & fm) || !(item[5] & am)) {
			continue;
		}
		
		// если выбран конкретный курорт или курорты, проверить, находится ли отель в нем или в одном из его подкурортов
		if (!allResortsSelected && !this.resortIds[item[6]]) {
			var availableResorts = this.getResortData('resorts');
			var hotelPlace = item[6];
			if (availableResorts[hotelPlace] && availableResorts[hotelPlace][4]) {
			  var hotelPlacePath = new String(availableResorts[hotelPlace][4].toString());
			} else {
				// курорт, где размещен отель, не найден или не имеет placePath (некорректные данные с сервера)
				continue; 
			}
			// перебираем все выбранные курорты; если путь к отелю включает путь к выбранному курорту в позиции 0, то отель считается принадлежащим курорту и не пропускается
			var skipThisHotel = true;
	    for(var resortId in this.resortIds) {
		    if(resortId != Number(resortId)) continue;
				if (availableResorts[resortId] && availableResorts[resortId][4]) {
					var currResortPlacePath = availableResorts[resortId][4];
					if (hotelPlacePath.indexOf(currResortPlacePath) == 0) {
						skipThisHotel = false;
						continue;
					}
				}
			}
			if (skipThisHotel == true) {
				continue;
			}
		}

		var val = item[0];
		if (isNaN(val)) {//фортуна
			if (!f) {
				f = true;
				val = 0;
				o = exatCreateOptionElement(val, "[фортуна]");
			}
			else {
				continue;
			}
		}
		else {
			o = exatCreateOptionElement(val, item[1]);
		}
	
		if(strHotelIds.indexOf(","+val+",") !== -1){
			o.setAttribute("selected", "selected");
			hotelSelected = true;
			this.hotelIds[val] = 2; //to show it's used
		}
		c.appendChild(o);
	}

	//удалим из hotelIds невыбранные курорты
	for(hotelId in this.hotelIds) {
		if(hotelId == Number(hotelId) && this.hotelIds[hotelId] != 2) {
			delete this.hotelIds[hotelId];
		}
	}

	
	if(!hotelSelected) {
		//ни один отель не был выбран, тогда выбираем пункт "[все]"
		exatSelectChild(c.firstChild);
	}
}

/** Заполнить список значений single select "валюта",
 * в соответствии с this.currencyId и предпочтениями
 * @access protected
 */
ExatSearchForm.prototype.initCurrencyCtrl = function()
{
	var c = this.form.elements[this.formId + 'CurrencyCtrl'];
	if(!c) {
		return;
	}

	exatClearCtrl(c);
	
	var o;
	var prefCur;

	if(this.currencyId)
	{
		prefCur = this.currencyId;
	}
	if( this.countryId 
		&& typeof(exatPrefData[this.clientId].prefCurrency) != 'undefined' 
		&& typeof(exatPrefData[this.clientId].prefCurrency[this.countryId]) != 'undefined')
	{
		prefCur = exatPrefData[this.clientId].prefCurrency[this.countryId];
	}
	
	if(typeof(this.defaults.currencyId) != 'undefined'
		&& this.defaults.currencyId>0
	)
		prefCur = this.defaults.currencyId;
	for(var currencyId in this.currencyData) {
		if(currencyId != Number(currencyId)) {
			continue;
		}
		o = exatCreateOptionElement(currencyId, this.currencyData[currencyId]);

		if(prefCur && prefCur == currencyId) {
			exatSelectChild(o);
		}

		c.appendChild(o);
	}
  this.currencyId = Number(c.options[c.selectedIndex].value);
}

/** Заполнить список значений multi select "услуги",
 * и выбрать некоторые в соответствии с this.serviceIds
 * @access protected
 */
ExatSearchForm.prototype.initServicesCtrl = function()
{
	var c = this.form.elements[this.formId+'ServiceCtrl'];

	if(!c) {
		return;
	}

	exatClearCtrl(c);
	
	var noServicesSelected = true;
	for(var serviceId in this.serviceIds) {
		if(serviceId == Number(serviceId)) {
			noServicesSelected = false;
			break;
		}
	}

	firstElement = exatCreateOptionElement('', '[все]');
	if(noServicesSelected) {
		firstElement.setAttribute('selected', true);
	}

	c.appendChild(firstElement);
	for (var serviceGroupId in this.servicesData) {
		if(serviceGroupId != Number(serviceGroupId)) {
			continue;
		}
		var g = exatCreateGroupElement(this.servicesData[serviceGroupId][0]);
		var sarr = this.servicesData[serviceGroupId][1];

		for (var i = 0; i < sarr.length; ++i) {
			if(typeof(sarr[i])!='object') {
				continue;
			}
			var opt = exatCreateOptionElement(sarr[i][1], sarr[i][0]);
			if(this.serviceIds[sarr[i][1]]) {
				opt.setAttribute('selected', true);
			}
			g.appendChild(opt);
		}

		c.appendChild(g);
	}
}

/** Заполнить список значений multi select "туроператоры",
 * и выбрать некоторых в соответствии с this.tourOperatorIds и с предыдущими значениями контрола "Туроператоры"
 * @access protected
 */
ExatSearchForm.prototype.initTourOperatorCtrl = function()
{
	var c = this.form.elements[this.formId + 'TourOperatorCtrl'];

	if(!c) {
		return;
	}

	var prevTo = new Array();

	if (c.firstChild && !c.firstChild.selected && !this.resetTourOperatorEvent) {
		for(o = c.firstChild.nextSibling; o; o = o.nextSibling)	{
			if(o.selected) {
				prevTo[o.value] = 2;
			} else {
				prevTo[o.value] = 1;
			}
		}		
	}

	var allTourOperatorsSelected = true;
	for(var toId in this.tourOperatorIds) {
		if(toId == Number(toId)) {
			allTourOperatorsSelected = false;
			prevTo[toId] = 2;
		}
	}
	
	exatClearCtrl(c);
	this.tourOperatorIds = [];

	var availableTourOperators = this.getResortData('tourOperators');
	
	if (!availableTourOperators) {
		return;
	}
	
	var tm = this.tourTypeMask;
	var fm = this.foodTypeMask & this.foodTypeRange;
	var am = this.accommodationRange;

 	var usePrefTo       = false;
 	var resortToCnt     = 0;
 	this.prefTourOperators = '';

	if( typeof(exatPrefData[this.clientId].arrPrefTo) != 'undefined' &&
		exatPrefData[this.clientId].arrPrefTo.length > 0)
	{
		usePrefTo = true;
	}

	var f = 0; //количество выбранных туроператоров
	var allInPref = true; //все имеющиеся туроператоры - в предпочтениях, поэтому можно this.prefTourOperators сделать пустой

	var firstElement = exatCreateOptionElement('', '[все]');
	if(allTourOperatorsSelected) {
		firstElement.setAttribute('selected', true);
	}

	c.appendChild(firstElement);

	for (var i = 0; i < availableTourOperators.length; ++i) {
		var item = availableTourOperators[i];
		var toId = item[0];
		
		if (!(item[1] & tm) || !(item[2] & fm) || !(item[3] & am)) {
			continue;
		}

		resortToCnt++;
		
		if (usePrefTo && !exatPrefData[this.clientId].arrPrefTo[toId]){
			allInPref = false;
			continue;
		}

		this.prefTourOperators += (this.prefTourOperators.length > 0 ) ? (','+ toId) : toId;

		element = exatCreateOptionElement(toId, this.tourOperatorData[toId]);

		if(prevTo.length > 0 && (prevTo[toId] == 2 || !prevTo[toId])) {
			//выбираем всех вновь появившихся туроператоров и тех, кто был выбран на пред. этапе
			exatSelectChild(element);
			this.tourOperatorIds[toId] = true;
			f++;
		}

		c.appendChild(element);
	}
	
	if(allInPref) {
		this.prefTourOperators = '';
	}

	if (c.length == 1) {
		c.removeChild(c.firstChild);
	} else if (f > 0 && f < (c.length - 1)) {
		exatUnselectChild(c.firstChild);
	} else if (f > 0 && f == (c.length - 1)) {
		var o;
		
		for(o = c.firstChild.nextSibling; o; o = o.nextSibling) {
			exatUnselectChild(o);
			delete this.tourOperatorIds[o.value];
		}
	}
}

/** Заполнить список значений single select "размер страницы"
 * и выбрать пункт, соответствующий this.limit
 * @access protected
 */
ExatSearchForm.prototype.initLimitCtrl = function()
{
	var c = this.form.elements[this.formId + 'LimitCtrl'];
	if(!c) {
		return;
	}

	exatClearCtrl(c);
	
	var limits = [20,100,500,1000];

	for(var i=0; i<limits.length; ++i) {
		o = exatCreateOptionElement(limits[i], limits[i] + ' туров');

		if(limits[i] == this.limit) {
			exatSelectChild(o);
		}
		
		c.appendChild(o);
	}
	this.limit = Number(c.options[c.selectedIndex].value);
}

/** Включить все элементы управления формы
 * @access protected
 */
ExatSearchForm.prototype.enableControls = function()
{
	for (var i = 0; i < this.controls.length; i++) {
		if(this.controls[i].name == 'ResortCtrl' && this.resortException) {
			continue;
		}
		var ctrl = this.form.elements[this.formId + this.controls[i].name];
		if(ctrl && this.controls[i].name == 'AgeCtrl' && this.getChildren() == 0) {
		    ctrl.disabled = true;
		    continue;
		}
		if(ctrl) {
			//TODO: Opera иногда не прорисовывает select'ы после disabled=false, и надо их выбирать заново
			/*
			var selIdx;
			if(ctrl.options && ctrl.type=='select-one') {
				selIdx = ctrl.selectedIndex;
			}
			*/
			ctrl.disabled = false;

			/*
			if(ctrl.options) {
				if(ctrl.type=='select-one') {
					ctrl.selectedIndex = selIdx;
				}
				else {
					for(var j = 0; j<ctrl.options.length; ++j) {
						if(ctrl.options[j].selected) {
							ctrl.options[j].selected = true;
						}
					}
				}
			}
			*/
		}
	}
}

/** Отключить все элементы управления формы
 * @access protected
 */
ExatSearchForm.prototype.disableControls = function()
{
	for (var i = 0; i < this.controls.length; i++) {
		var ctrl = this.form.elements[this.formId + this.controls[i].name];
		if(ctrl) {
			ctrl.disabled = true;
		}
	}
}
/** обработчик события onChange для single select id=_DepartureCtrl
 * может быть вызван сразу после конструктора
 */
ExatSearchForm.prototype.onDepartureCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var c = this.form.elements[this.formId + 'DepartureCtrl'];
	if (!c) {
		return;
	}
	this.setDepartureId(c.value);
}

ExatSearchForm.prototype.setDepartureId = function(departureId)
{
	this.disableControls();
	this.departureId = departureId;
	this.resetTourOperatorEvent = true;

	this.initTourTypeCtrl();
	this.initCountryCtrl();
	if(this.initResortCtrl() == -1) {
		this.initForm();
		return;
	}
	this.initCategoryCtrl();
	this.initFoodTypeCtrl();
	this.initAccommodationCtrl();
	this.initHotelCtrl();
	this.initTourOperatorCtrl();

	this.resetTourOperatorEvent = false;

	this.enableControls();
}

/** обработчик события onChange для single или multi select id=_TourTypeCtrl
 */
ExatSearchForm.prototype.onTourTypeCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var ctrl = this.form.elements[this.formId + 'TourTypeCtrl'];
	if(!ctrl) {
		return;
	}
	
	var mask = 0;
	for (var o = ctrl.firstChild; o; o = o.nextSibling) {
		if (!o.selected) {
			continue;
		}
		if(o.getAttribute('value')=='0') {
			mask = this.ALL_TOUR_TYPES;
			break;
		}
		mask |= o.getAttribute('value');
	}
	this.setTourTypeMask(mask);
}

ExatSearchForm.prototype.setTourTypeMask = function(mask)
{
	this.disableControls();

	this.resetTourOperatorEvent = true;

	this.tourTypeMask = mask;

	this.initCountryCtrl();
	
	if(this.initResortCtrl() == -1) {
		this.initForm({resortIds:'-1'});
		return;
	}
	this.initCategoryCtrl();
	this.initFoodTypeCtrl();
	this.initAccommodationCtrl();
	this.initHotelCtrl();
	this.initTourOperatorCtrl();

	this.resetTourOperatorEvent = false; 

	this.enableControls();
}

/** обработчик события onChange для single select id=_CountryCtrl
 */
ExatSearchForm.prototype.onCountryCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var c = this.form.elements[this.formId + 'CountryCtrl'];
	if (!c) {
		return;
	}
	this.setCountryId(c.value);
}	

ExatSearchForm.prototype.setCountryId = function(countryId)
{
	this.disableControls();

	if(countryId != this.countryId) {
		this.countryId = Number(countryId);
		this.resortIds = [];
		this.hotelIds = [];
	}
	
	this.resetTourOperatorEvent = true; 

	if (!Number(this.countryId)) {
		this.initForm();

		this.enableControls();
		return;
	}

	this.initCurrencyCtrl();
	this.initTransportRequiredCtrl();

	if (this.resortData['x' + this.departureId + '-' + this.countryId]) {
		this.fillResort();
	} else {
		this.asyncLoadResort();
	}
	
	this.resetTourOperatorEvent = false;
}

/** обработчик события onChange для single или multi select name=_ResortCtrl
 */
ExatSearchForm.prototype.onResortCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var ctrl = this.form.elements[this.formId + 'ResortCtrl'];
	if(!ctrl) {
		return;
	}

	this.disableControls();
	
	var resortIds = [];

	for (var o = ctrl.firstChild; o; o = o.nextSibling) {
		if (!o.selected) {
			continue;
		}

		var id = o.getAttribute('value');
		
		if (id == this.countryId) {
			resortIds = [];
			break;
		}

		resortIds[id] = true;
	}
	
	this.setResortIds(resortIds);
}

ExatSearchForm.prototype.setResortIds = function(resortIds)
{
	this.resortIds = resortIds;
	
	if (!resortIds.length) {
		this.foodTypeRange      = this.ALL_FOOD_TYPES;
		this.accommodationRange = this.ALL_ACCOMMODATIONS;
	} else {
		var l = this.getResortData('resorts');
		var fr = 0;
		var ar = 0;

		for (resortId in this.resortIds) {
			if(resortId != Number(resortId)) {
				continue;
			}
			if (resortId == this.countryId) {
				fr = this.ALL_FOOD_TYPES;
				ar = this.ALL_ACCOMMODATIONS;
			} else {
				if(fr != this.ALL_FOOD_TYPES) {
					fr |= l[resortId][2];
				}
				if(ar != this.ALL_ACCOMMODATIONS) {
					ar |= l[resortId][3];
				}
			}
		}

		this.foodTypeRange      = fr;
		this.accommodationRange = ar;
	}

	this.hotelIds = []; //TODO: когда курорты будут деревом, необзательно выбор другого курорта должен сбрасывать выбор отелей

	this.disableControls();
	this.initLCDCtrl();
	this.initCategoryCtrl();
	this.initFoodTypeCtrl();
	this.initAccommodationCtrl();
	this.initHotelCtrl();
	this.initTourOperatorCtrl();
	this.enableControls();
}

/** обработчик события onChange для single или multi select name=_CategoryCtrl
 */
ExatSearchForm.prototype.onCategoryCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var ctrl = this.form.elements[this.formId+'CategoryCtrl'];
	if(!ctrl) {
		return;
	}
	
	var mask = 0;
	for (var o = ctrl.firstChild; o; o = o.nextSibling) {
		if (!o.selected) {
			continue;
		}
		if(o.getAttribute('value') == this.ALL_CATEGORIES) {
			this.setCategoryMask(this.ALL_CATEGORIES);
			return;
		}

		var vals = o.getAttribute('value').split(",");
		for(var j=0; j<vals.length; ++j) {
			mask |= 1 << vals[j];
		}
	}
	this.setCategoryMask(mask);
}


ExatSearchForm.prototype.setCategoryMask = function(mask)
{
	this.disableControls();
	this.categoryMask = mask;
	this.initHotelCtrl();
	this.initTourOperatorCtrl();
	this.enableControls();
}

/** обработчик события onChange для single или multi select name=_FoodTypeCtrl
 */
ExatSearchForm.prototype.onFoodTypeCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var ctrl = this.form.elements[this.formId + 'FoodTypeCtrl'];
	if(!ctrl) {
		return;
	}
	
	var mask = 0;
	for (var o = ctrl.firstChild; o; o = o.nextSibling) {
		if (!o.selected) {
			continue;
		}
		var n = new Number(o.getAttribute('value'));
		if(n == this.ALL_FOOD_TYPES) {
			mask = this.ALL_FOOD_TYPES;
			break;
		}
		
		mask |= 1 << (n-1);
	}
	this.setFoodTypeMask(mask);
}


ExatSearchForm.prototype.setFoodTypeMask = function(mask)
{
	this.disableControls();
	this.foodTypeMask = mask;
	this.initHotelCtrl();
	this.initTourOperatorCtrl();
	this.enableControls();
}

/** обработчик события onChange для single select name=accommodationCtrl
 */
ExatSearchForm.prototype.onAccommodationCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var ctrl = this.form.elements[this.formId+'AccommodationCtrl'];
	if(!ctrl) {
		return;
	}

	this.disableControls();

	this.accommodationId = ctrl.value;
	this.accommodationHandSelectedId = this.accommodationId;

	this.initHotelCtrl();
	this.initTourOperatorCtrl();

	this.enableControls();
}

ExatSearchForm.prototype.onAgeCtrlChange = function()
{
	if(!this.form) {
		return;
	}
}

ExatSearchForm.prototype.onHotelCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var ctrl = this.form.elements[this.formId+'HotelCtrl'];
	
	var opt = ctrl.firstChild;
	if(!opt) {
		return;
	}

	this.hotelIds = [];
	
	if((opt.value=='' || opt.value==0)) {
		if(opt.selected) {
			//спец. значение "[все]"
			return;
		}
		else {
			opt = opt.nextSibling;
		}
	}

	for (; opt; opt = opt.nextSibling) {
		if (!opt.selected) {
			continue;
		}
		this.hotelIds[opt.value] = true;
	}
}

ExatSearchForm.prototype.onResortFilterCtrlClick = function()
{
	if(!this.form) {
		return;
	}
	var ctrl = this.form.elements[this.formId+'ResortFilterCtrl'];
	if(!ctrl) {
		return;
	}
	this.resortFilter = ctrl.checked;
}

ExatSearchForm.prototype.onTransportRequiredCtrlClick = function()
{
	if(!this.form) {
		return;
	}
	var ctrl = this.form.elements[this.formId+'TransportRequiredCtrl'];
	if(!ctrl) {
		return;
	}
	this.transportRequired = ctrl.checked;

	if(this.countryId > 0) {
		//если страна уже выбрана, то перезаполняем список курортов и т.д.
		this.disableControls();
		this.initLCDCtrl();
		this.enableControls();
	}
}

ExatSearchForm.prototype.onMinDateCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var minCtrl = this.form.elements[this.formId + 'MinDateCtrl'];
	var maxCtrl = this.form.elements[this.formId + 'MaxDateCtrl'];
	if(minCtrl) {
		this.minTimestamp = minCtrl.value;
	}

	if (minCtrl && maxCtrl && (minCtrl.value == 0 || minCtrl.value > maxCtrl.value)) {
		maxCtrl.value = minCtrl.value;
		this.maxTimestamp = this.minTimestamp;
	}
}

ExatSearchForm.prototype.onMaxDateCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var minCtrl = this.form.elements[this.formId + 'MinDateCtrl'];
	var maxCtrl = this.form.elements[this.formId + 'MaxDateCtrl'];

	if(maxCtrl) {
		this.maxTimestamp = maxCtrl.value;
	}

	if (minCtrl && maxCtrl && maxCtrl.value != 0 && minCtrl.value > maxCtrl.value) {
		minCtrl.value = maxCtrl.value;
		this.minTimestamp = this.maxTimestamp;
	}
}

ExatSearchForm.prototype.onMinDurationCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var minCtrl = this.form.elements[this.formId + 'MinDurationCtrl'];
	var maxCtrl = this.form.elements[this.formId + 'MaxDurationCtrl'];

	if(!minCtrl) {
		return;
	}

	this.minDuration = minCtrl.value;

	if(maxCtrl && (minCtrl.value == 0 || parseInt(minCtrl.value) > parseInt(maxCtrl.value))) {
		maxCtrl.value = minCtrl.value;
		this.maxDuration = this.minDuration;
	}
}

ExatSearchForm.prototype.onMaxDurationCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var minCtrl = this.form.elements[this.formId + 'MinDurationCtrl'];
	var maxCtrl = this.form.elements[this.formId + 'MaxDurationCtrl'];
	if(!maxCtrl) {
		return;
	}

	this.maxDuration = maxCtrl.value;
	
	if (minCtrl && maxCtrl.value!=0 && parseInt(minCtrl.value) > parseInt(maxCtrl.value)) {
		minCtrl.value = maxCtrl.value;
		this.minDuration = this.maxDuration;
	}
}

ExatSearchForm.prototype.onMinAmountCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var ctrl = this.form.elements[this.formId + 'MinAmountCtrl'];
	this.minAmount = ctrl.value;
}

ExatSearchForm.prototype.onMaxAmountCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var ctrl = this.form.elements[this.formId + 'MaxAmountCtrl'];
	this.maxAmount = ctrl.value;
}

ExatSearchForm.prototype.onCurrencyCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var ctrl = this.form.elements[this.formId + 'CurrencyCtrl'];
	this.currencyId = ctrl.value;
}

ExatSearchForm.prototype.onServiceCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var ctrl = this.form.elements[this.formId+'ServiceCtrl'];
	if(!ctrl) {
		throw new Error("Can't find element with id="+this.formId + "ServiceCtrl");
		return;
	}
	
	if(!ctrl.options.length) {
		return;
	}
	var opt = ctrl.options[0];

	this.serviceIds = [];
	
	if((opt.value=='' || opt.value==0)) {
		if(opt.selected) {
			//спец. значение "[все]"
			return;
		}
	}

	for (var i=1; i<ctrl.options.length; ++i) {
		if (!ctrl.options[i].selected) {
			continue;
		}
		this.serviceIds[ctrl.options[i].value] = true;
	}
}

ExatSearchForm.prototype.onTourOperatorCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var ctrl = this.form.elements[this.formId+'TourOperatorCtrl'];
	
	var opt = ctrl.firstChild;
	if(!opt) {
		return;
	}

	this.tourOperatorIds = [];
	
	if((opt.value=='' || opt.value==0)) {
		if(opt.selected) {
			//спец. значение "[все]"
			return;
		}
		else {
			opt = opt.nextSibling;
		}
	}

	for (; opt; opt = opt.nextSibling) {
		if (!opt.selected) {
			continue;
		}
		this.tourOperatorIds[opt.value] = true;
	}
}


ExatSearchForm.prototype.onLimitCtrlChange = function()
{
	if(!this.form) {
		return;
	}
	var ctrl = this.form.elements[this.formId+'LimitCtrl'];
	
	var opt = ctrl.firstChild;
	if(!opt) {
		return;
	}

	for (; opt; opt = opt.nextSibling) {
		if (opt.selected) {
			this.limit = opt.value;
			break;
		}
	}
}


ExatSearchForm.prototype.onShowTechInfoCtrlClick = function()
{
	if(!this.form) {
		return;
	}
	var ctrl = this.form.elements[this.formId + 'ShowTechInfoCtrl'];
	var c = this.form.elements[this.formId + 'ShowTourOperatorCtrl'];
	var s = ctrl.checked;
	
	c.checked  = s;
	c.disabled = s;
}

ExatSearchForm.prototype.onShowTourOperatorCtrlClick = function()
{
	if(!this.form) {
		return;
	}
	var c = this.form.elements[this.formId + 'ShowTourOperatorCtrl'];
}

ExatSearchForm.prototype.onIncludeStopCtrlClick = function()
{
	if(!this.form) {
		return;
	}
	var c = this.form.elements[this.formId + 'IncludeStopCtrl'];
	this.includeStop = c.checked ? 1 : 0;
}

ExatSearchForm.prototype.clearControls = function()
{
	for (var i = 0; i < this.controls.length; i++) {
		var c = this.form.elements[this.formId + this.controls[i].name];

		if(c) {
			if (c.nodeName == 'SELECT') {
				exatClearCtrl(c);
	
				for (var o = c.firstChild; o; o = o.nextSibling) {
					exatUnselectChild(o);
				}
			}
			else if (c.nodeName == 'INPUT') {
				var t = c.getAttribute('type');
				
				if (t == 'text') {
					c.value = '';
				}
			}
		}
	}
}

/** Отображение сообщения об ошибке в случае отсутствии обязательных параметров
 * @return true если ok, false если есть ошибки
 */
ExatSearchForm.prototype.checkParameters = function()
{
	var c;

	if(!Number(this.countryId)) {
		alert('Выберите cтрану');
		return false;
	}

	if (Number(this.minTimestamp) && Number(this.maxTimestamp) && Number(this.minTimestamp) > Number(this.maxTimestamp)) {
		alert('Начальная дата больше конечной');
		return false;
	}

	if (Number(this.maxDuration) && Number(this.minDuration) > Number(this.maxDuration)) {
		alert('Задан некорректный диапазон количества дней');
		return false;
	}

	c = this.form.elements[this.formId+'TourOperatorCtrl'];

	if (c && c.length == 0) {
		var s = 'Список туроператоров пуст. Это означает, что среди туроператоров, обслуживающих маршрут, нет' + 
				' ни одного из предпочитаемых Вами. Попробуйте настроить список предпочтительных туроператоров' +
				' при помощи ссылки "настроить список" над списком туроператоров или изменить маршрут.';
		alert(s);
		return false;
	}

	c = this.form.elements[this.formId+'AgeCtrl'];
	if (c && c.value.length > 0) {
		var ageS = c.value;
		var ageL = ageS.length;

		var ageN = 1;
		var age1 = '';
		var age2 = '';
		var sep  = 0;

		for (var i = 0; i < ageL; i++) {
			var ch = ageS.charAt(i);

			if (ch >= '0' && ch <= '9') {
				if (sep && age1.length > 0) {
					ageN++;
					age1 += ',';
					age2  = '';
					sep   = 0;
				}

				age1 += ch;
				age2 += ch;

				if (age2 > 25) {
					alert('Возраст ребенка должен быть до 25 лет включительно.');
					c.focus();
					return false;
				}
			}
			else {
				sep = 1;
			}
		}
		this.ages = age1;
		var chnum = this.getChildren();

		if(chnum > 0 && ageN > chnum) {
			alert('Введено больше возрастов, чем выбрано детей.\nУдалите лишние возрасты.');
			c.focus();
			return false;
		}
	}
	return true;
}

/** Поиск по коду тура
 * @access public
 */
ExatSearchForm.prototype.searchTpid = function(val, newWindow)
{
	if (val.length) {
		var url = '/touronline/tpid_search.php?tpid=' + val;

		var st = this.form.elements[this.formId + "ShowTechInfoCtrl"];
		var so = this.form.elements[this.formId + "ShowTourOperatorCtrl"];

		if (st && st.checked) {
			url += '&show_tech_info=1';
		} if (so && so.checked) {
			url += '&show_operator=1';
		}

		exatShowWindow(url, 700, 500);
	} else {
		alert('Укажите код тура');
	}
}

/** показать сообщение о предупреждении (нефатальной ошибке), если оно не пустое
 * @access protected
 */
ExatSearchForm.prototype.reportWarning = function(msg)
{
	if (msg != '') {
		this.showMessage('Предупреждение : ' + msg);
	} else {
		this.showMessage('Неизвестная ошибка');
	}
}

/** показать сообщение о фатальной ошибке, если оно не пустое
 * @access protected
 */
ExatSearchForm.prototype.reportError = function(msg)
{
	if (msg != '') {
		this.showMessage('Фатальная ошибка : ' + msg);
	} else {
		this.showMessage('Неизвестная фатальная ошибка');
	}
}

/** показать сообщение, если оно не пустое
 * @access protected
 */
ExatSearchForm.prototype.showMessage = function(msg)
{
	if(msg) {
		alert(msg);
		return true;
	}
	else {
		return false;
	}
}

ExatSearchForm.prototype.getChildren = function()
{
	var children = new Array(0,0);
	var accom = this.ACC_VAL[this.accommodationId];
	if (accom != Number(accom)) children = accom.split('_');
	return Number(children[1]);
}

/* глобальные утилитарные функции */

function exatGenRandomStr(strlen)
{
	str = '';
	for (i = 0; i < strlen; i++) {
		str += String.fromCharCode(Math.random()*25 + 97);
	}
	return str;
}

function exatShowWindow(url, width, height)
{
	size = '';

	if(width && height) {
		size = ',width=' + width + ',height=' + height;
	}

	var strInitParam='directories=no,status=yes,scrollbars=yes,resizable=yes,menubar=no,toolbar=no';


	exat_wndname     = exatGenRandomStr(10);
	var description = window.open('', exat_wndname, strInitParam+size);

	description.document.write('Пожалуйста, подождите. Идет загрузка...');

	if (url) {
		description.document.location=url;
	}
	description.focus();

	return description;
}
// возвращена функция с оригинальным названием для совместимости 
// by Eduard Pakaln
function w(url, width, height)
{
	return exatShowWindow(url, width, height);
}

function exatGetNiceDate(dateObj)
{
	dateObj = new Date(dateObj);

	var daysOfWeek = new Array('вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб');
	var months     = new Array('янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек');
	var day        = dateObj.getDate();
	
	return daysOfWeek[dateObj.getDay()] + ' ' + (day < 10 ? '0' : '') + day.toString() + ' ' + months[dateObj.getMonth()];
}

/** Упаковка-распаковка строк
 */
function str2code(str_in) {
	var tmp="";
	var i;
	var tmp_c="";
	str_in=str_in.toString();
	for(i = 0; i < str_in.length; i++) {
		tmp_c = str_in.charCodeAt(i) - 23;
		tmp += ''+(tmp_c<10 ? '0'+tmp_c : tmp_c);
	}
//alert(code2str(tmp));
	var substr_len=14, out="", len_bit_tmp=0;
	while(tmp!=''){	
		len_bit_tmp=tmp.length<substr_len ? tmp.length : substr_len;
		out+='-'+parseInt(tmp.substring(0, len_bit_tmp)).toString(36);
		tmp=tmp.substring(len_bit_tmp);
	}
	out='5v'+out.substring(1);
	return out;
}

// reconvert for future
function code2str(num_in) {
	var str_out = "";
	for(i = 0; i < num_in.length; i += 2) {
		num_in2 = parseInt(num_in.substr(i,[2])) + 23;
		num_in2 = unescape('%' + num_in2.toString(16));
		str_out += num_in2;
	}
	return str_out;
}


/** Преобразование кол-ва миллисекунд с 01.01.1970 в строку 'DD.MM.YYYY'
 */
function exatFromTime(time)
{
	var dateObj = new Date();
	dateObj.setTime(time);

	var day = dateObj.getDate();
	var mon = dateObj.getMonth() + 1;
	var year = dateObj.getFullYear();
	
	return (day < 10 ? '0' : '') + day.toString() + '.' + (mon < 10 ? '0' : '') + mon.toString() + '.' + year.toString();
}

/** Преобразование значения 'DD.MM.YYYY' в кол-во миллисекунд с 01.01.1970
 * @return кол-во миллисекунд с 01.01.1970 либо null, если ошибка в формате.
 */
function exatToTime(dd_mm_yyyy)
{
	var d = dd_mm_yyyy.split(".");

	if (d.length != 3) { 
		return null;
	}

	if ((d[0] > 31) || (d[1] > 12) || (d[2].length !== 4)) {
		return null;
	}

	res = new Date(d[2], d[1] - 1, d[0])
	
	return res.getTime();
}


/** Преобразование значения 'DD.MM.YYYY' элемента управления ctrl
 *   в кол-во миллисекунд с 1 января 1970 года
 */
function exatCheckDate(ctrl)
{
	if (ctrl && ctrl.value.length) {
		var res = exatToTime(ctrl.value);

		if (res==null) { 
			ctrl.focus();
			return false;
		}
		else {
			return res;
		}
	}

	return 0;
}


/**
 * Получение значения установлено/сброшено объекта. Объект может быть input(checkbox, hidden, text)
 * Сделано на случай когда на сайте некоторые компоненты делают скрытыми
 *
 * @access global
 * @param obj
 * @return bool or null on error
 */
function exatGetCheckedValue(obj)
{
	var checkedValue;

	if(obj.nodeName == 'INPUT'){
		switch (obj.type) {
			case 'text':
			case 'hidden':
				checkedValue = obj.value;
				break;
			case 'checkbox':
				checkedValue = obj.checked;
				break;
			default:
				checkedValue = null;	
		}
	} else {
		checkedValue = null;
	}

	return checkedValue;
}
/**
 * Установка значения установлено/сброшено объекта. Объект может быть input(checkbox, hidden, text)
 * Сделано на случай когда на сайте некоторые компоненты делают скрытыми
 *
 * @access global
 * @param obj
 * @param value
 * @return bool
 */
function exatSetCheckedValue(obj, value)
{
	if (obj.nodeName == 'INPUT') {
		switch (obj.type) {
			case 'text':
			case 'hidden':
				obj.value = value;
				ret = true;
				break;
			case 'checkbox':
				obj.checked = value ? true : false;
				ret = true;
				break;
			default:
				ret  = false;	
		}
	} else {
		ret = false;
	}

	return ret;
}

/** Объединить числовые значения массива в строку через запятую
 */
function exatJoinNumbers(arr)
{
	var s = '';
	for(var i in arr) {
		if(i != Number(i)) {
			continue;
		}
		s += (s.length ? ',' : '') + i;
	}
	return s;
}

function exatCreateGroupElement(l)
{
	var g = document.createElement('optgroup');
	g.setAttribute('label', l);
	return g;
}

function exatCreateOptionElement(v, n)
{
	var o = document.createElement('option');
	var t = document.createTextNode(n);

	o.setAttribute('value', v);
	o.appendChild(t);

	return o;
}

function exatClearCtrl(c)
{
	// чтобы очистка поменьше тормозила и визуально не выбирала элементы,
	// сначала выберем первый элемент, а затем удалим всё с конца

	if(c && c.firstChild) {
		if(c.nodeName=='SELECT') {
			c.selectedIndex=-1; //чтобы в multiselect сбросить выделение совсем
		}
		else {
			c.firstChild.setAttribute('selected', true);
		}
		do {
			c.removeChild(c.lastChild);
		}
		while(c.lastChild);
	}
}

/** Кроссбраузерный выбор опции в селекте
 */
function exatSelectChild(chd) {
	chd.setAttribute('selected', true);//для IE, в FF не работает
	if(!chd.selected) {
		chd.selected = true;//работает только в FF, в IE вызывает ошибку
	}
}

function exatUnselectChild(chd) {
	chd.setAttribute('selected', false);//для IE, в FF не работает
	if(chd.selected) {
		chd.selected = false;//работает только в FF, в IE вызывает ошибку
	}
}

//глобальный реестр поисковых форм на странице. Заполняется конструктором.
if(typeof(exatForms)=='undefined') {
	exatForms = [];
}

// функция для парсинга параметров GET
function getSearchParams(){
	var tmp = new Array();		// два вспомагательных
	var tmp2 = new Array();		// массива
	var param = new Array();
	
	var get = location.search;	// строка GET запроса
	if(get != '')
	{
		tmp = (get.substr(1)).split('&');	// разделяем переменные
		for(var i=0; i < tmp.length; i++)
		{
			tmp2 = tmp[i].split('=');		// массив param будет содержать
			param[tmp2[0]] = tmp2[1];		// пары ключ(имя переменной)->значение
		}
	}
	return param;
}
