﻿
function $get(id) {
    return document.getElementById(id);
}

function $value(id) {
    return document.getElementById(id).value;
}

var toggle = {
	show: function(id) {
		$get(id).style.display = '';
	},
	hide: function(id) {
		$get(id).style.display = 'none';
	}
}

function common_onload() {

    if ($get('OutDay')) {
        fillDayDropDownList($get('OutDay'), $get('OutMonthYear'), dayOfWeekNames, null);
        fillDayDropDownList($get('ReturnDay'), $get('ReturnMonthYear'), dayOfWeekNames, null);
    }

    if ($get('CheckInDay')) fillDayDropDownList($get('CheckInDay'), $get('CheckInMonthYear'), dayOfWeekNames, selectableDays);
    if ($get('CheckOutDay')) fillDayDropDownList($get('CheckOutDay'), $get('CheckOutMonthYear'), dayOfWeekNames, selectableDays);

    $('#CheckInMonthYear').change(function() { monthYear_change(this, 'CheckInDay'); });
    $('#CheckOutMonthYear').change(function() { monthYear_change(this, 'CheckOutDay'); });

    $('#IsReturnLegRequired').change(function() { isReturnLegRequired_change(this, false); })
                            .each(function() { isReturnLegRequired_change(this, true); });

    $('#RouteGroup').change(function() { routeGroup_change(this, false); })
                    .each(function() { routeGroup_change(this, true); });

    $('#RouteGroupReturn').change(function() { routeGroupReturn_change(this); });

    $('#OutMonthYear').change(function() { monthYear_change(this, 'OutDay'); });
    $('#ReturnMonthYear').change(function() { monthYear_change(this, 'ReturnDay'); });

    $('#Vehicle').change( function() { vehicle_change(this); } ).change();
    
    $('#ChildAgeLink').click(function() { getChildAges(); return false; });
}

// fills "dayDropDown" with 1 entry for each day in month given by "monthYearDropDown" selected value
// uses "dayOfWeekNames" localised day-of-week names
function fillDayDropDownList(dayDropDown, monthYearDropDown, dayOfWeekNames, selectableDays) {

    var dateParts = monthYearDropDown.value.split('-');
    var year = dateParts[0];
    var month = dateParts[1]-1;

    var startOfMonth = new Date(year, month, 1);
    var totalDays = numDaysInMonth(year, month);
    var dow = startOfMonth.getDay();

    // save current selected day
	var selectedDay = parseInt(dayDropDown.options[dayDropDown.selectedIndex].value, 10);

    var selectedFound = false;
    var dayDiff = 100;
	dayDropDown.options.length = 0;
	for (day = 1; day <= totalDays; day++) {

        // if there's a selectable days list, only do day if it's listed
        if (!selectableDays || contains(selectableDays, dow)) {
		    dayDropDown.options.length += 1;
		    dayDropDown.options[dayDropDown.length-1] = new Option(day + ' ' + dayOfWeekNames[dow], day);

            // if previously added day had the smallest distance to the old selected day, 
            // make it the new selected day
		    if (!selectedFound && Math.abs(day - selectedDay) > dayDiff) {
		        dayDropDown.selectedIndex = dayDropDown.length-2;   //option before current one
		        selectedFound = true;
            }
            dayDiff = Math.abs(day - selectedDay);
        }
        dow = (dow < 6) ? dow + 1 : 0;
	}

    // if no selected item use last one
    if (!selectedFound) dayDropDown.selectedIndex = dayDropDown.length-1;
}

// returns the number of days in the month requested month
function numDaysInMonth(year, month) {

    var m2 = parseInt(month, 10) + 1;
    var y2 = parseInt(year, 10);

    if (m2 > 11) {
        m2 = 0;
        y2 += 1;
    }

    var d1 = new Date(year, month, 1);
    var d2 = new Date(y2, m2, 1);

    return Math.round( (d2 - d1) / (1000 * 60 * 60 * 24) );
}

function routeGroup_change(caller, isInitialising) {
    setSailingTimes(caller.value, '#OutTime');

    var ret = $('#RouteGroupReturn')[0];
    if (ret) {
        fillReturnRouteDropDown(isInitialising);
        setSailingTimes(ret.value, '#ReturnTime');
    }
    else {
        setSailingTimes(caller.value, '#ReturnTime');
    }
}

function routeGroupReturn_change(caller) {
    setSailingTimes(caller.value, '#ReturnTime');
}

function setSailingTimes(routeName, timeId) {

    var times = $(timeId)[0];
    if (!times || !sailingTimes) return;

    var selectedTime = times.value;
    
    times.options.length = 0;

    var timelist = sailingTimes[routeName];
    if (timelist) {
        // use specific times for route
        for (var i in timelist) {
            times.options[i] = new Option(timelist[i], timelist[i].replace(':', ''));
        }
    }
    else {
        // use general (hourly) times
        var s;
        for (var i2 = 0; i2 < 24; i2++) {
            s = '0' + i2;
            s = s.substring(s.length - 2, s.length);
            times.options[i2] = new Option(s + ':00', s + '00');
        }
    }
    // restore selected time
    setNearestSelectedTime(times, selectedTime);
}

function setNearestSelectedTime(select, value) {
    var mins, diff;
    var smallestDiff = 24 * 60;
    var smallestDiffIndex = 0;

    var selectedMins = toMinutes(value);

    for (var i = 0; i < select.options.length; i++) {
        mins = toMinutes(select.options[i].value);
        diff = Math.abs(mins - selectedMins);
        if (diff < smallestDiff) {
            smallestDiff = diff;
            smallestDiffIndex = i;
        }
    }
    select.selectedIndex = smallestDiffIndex;
}

function toMinutes(time) {
    var hours = parseInt(time.substr(0, 2), 10);
    var mins = parseInt(time.substr(2, 2), 10);
    return hours * 60 + mins;
}

function isReturnLegRequired_change(sender, isInitialising) {
    var s = '#ReturnDay, #ReturnMonthYear, #ReturnTime, #RouteGroupReturn'
    if (sender.value == 'true') {
        $(s).attr('disabled', '');
        if (!isInitialising) fillReturnRouteDropDown(false);
    }
    else $(s).attr('disabled', 'disabled');
}

function monthYear_change(sender, dayDropDownName) {

    // restrict to selectable days if accom date
    var days = (dayDropDownName=='CheckInDay' || dayDropDownName=='CheckInDay') ? days = selectableDays : null;

    fillDayDropDownList($get(dayDropDownName), sender, dayOfWeekNames, days);
}

function vehicle_change(caller) {
    var trailer = $('#Trailer')[0];
    if (!trailer) return;

    // disable trailer if disallowed for vehicle type
    var parts = caller.value.split('/');
    if (parts.length > 0) {
        var vehicle = parts[0];
        if (vehicle == 'CAR' || vehicle == 'CMP' || vehicle == 'MTC' || vehicle == 'MBS' || vehicle == 'VAN')
            trailer.disabled = false;
        else {
            trailer.selectedIndex = 0;
            trailer.disabled = true;
        }
    }
}

function numAdults_onchange(self)
{
    setMaxChildren(8-self.selectedIndex);
}

function setMaxChildren(maxChildren)
{
    var children = $get('NumChildren');

    if (children) {
        var numChildren = children.selectedIndex;

        var i;
        children.options.length = 0;
        for (i=0; i<maxChildren+1; i++) {
            children.options[i] = new Option(i, i);
        }

        // restore selected index (within new range)
        children.selectedIndex = (numChildren > maxChildren) ? maxChildren : numChildren;
    }
}

function search_onsubmit(sender) {

    // accom search
    if ($get('HotelLocation')) {

        if (!hasAccomSearchLocation()) return false;
        if (!doesHotelLocationUseValidCharacters()) return false;

        if (formHasUserRoomSelection()) {
            if (!doesHotelHaveRooms()) return false;
            if (!isHotelRoomCapacityCorrectForNumPax()) return false;
            if (!isAtLeastOneAdultPerHotelRoom()) return false;
        }
        
        // if there are hotel date controls in form ...
        if ( ($get('hotelDatesGroup') && $get('hotelDatesGroup').style.display!='none') || $get('AccomSearchAgain')) {

            // validate hotel form date values
            var checkInDate = createDateFromFormControls('CheckInMonthYear', 'CheckInDay');
            if (!isCheckInDateInFuture(checkInDate, 1)) return false;

            // only test checkout date if exists (form might be using duration instead of checkout date)
            if ($get('CheckOutMonthYear')) {
                var checkOutDate = createDateFromFormControls('CheckOutMonthYear', 'CheckOutDay');
                if (!isCheckOutDateAfterCheckInDate(checkInDate, checkOutDate)) return false;
            }
        }
    }

    // ferry search
    if ($get('RouteGroup')) {

        // get sailing dates
        var outDate = createDateTimeFromFormControls('OutMonthYear', 'OutDay', 'OutTime');
        var returnDate = (isReturnLegSelected())
                            ? createDateTimeFromFormControls('ReturnMonthYear', 'ReturnDay', 'ReturnTime')
                            : null;

        // validate ferry form values
        if (!isRouteSelected()) return false;
        if (!isDepartDateFarEnoughAhead(outDate)) return false;
        if (!isReturnDateAfterDepartDate(outDate, returnDate)) return false;
        if (!doesFootPaxOnlyRouteHaveFootPaxSelected()) return false;

        // user must confirm it's OK if sailing same day out & return
        if (isReturnOnSameDay()) { if (!isOKToReturnOnSameDay()) return false; }

        // lodr dove-boul foot pax warning
        warnIfLodrDoboFootPax();

        // vehicle rules/warnings
        var vehicle = $('#Vehicle').val();
        var numPax = parseInt($('#NumAdults').val(), 10) + parseInt($('#NumChildren').val(), 10);
        if (!checkBicycleRules(vehicle, numPax)) return false;
        if (!checkMotorcycleRules(vehicle, numPax)) return false;
    }

    // all searches
    if (!doAllChildrenHaveAges()) return false;

    // disable hotel dates if they weren't visible to the user, so they don't get posted
    if ($get('HotelLocation')) {
        if ($get('accomCheckInRow') && $get('accomCheckInRow').style.display == 'none') {
            $get('CheckInDay').disabled = true;
            $get('CheckInMonthYear').disabled = true;
            if ($get('CheckOutDay')) {
                $get('CheckOutDay').disabled = true;
                $get('CheckOutMonthYear').disabled = true;
            }
            if ($get('AccomDuration')) $get('AccomDuration').disabled = true;
        }
    }

    // post form
    sender.action = 'results.aspx';
    sender.submit();
    return true;
}

function isRouteSelected() {
    var badRoute = $value('RouteGroup')=='' || $value('RouteGroup').substring(0,1)=='*';
    if (!showAlertIf( badRoute, searchMessages.noRoute)) {
        $get('RouteGroup').focus();
        return false;
    }
    return true;
}

function isOKToReturnOnSameDay() {
    return confirm(searchMessages.isSameDayOK);
}

function isReturnOnSameDay() {
    return (isReturnLegSelected() &&
            $value('OutDay')== $value('ReturnDay') && 
            $value('OutMonthYear')== $value('ReturnMonthYear'));
}

function isDepartDateFarEnoughAhead(outDate) {
    var MIN_HOURS_BEFORE_SAILING = 2;
    var now = new Date();
    if (!showAlertIf( outDate < now.setHours(now.getHours() + MIN_HOURS_BEFORE_SAILING), searchMessages.departDateInPast)) {
        $get('OutDay').focus();
        return false;
    }
    return true;
}

function isReturnDateAfterDepartDate(outDate, returnDate) {
    if (returnDate==null) return true;

    var MIN_HOURS_BETWEEN_SAILINGS = 2;
    if (!showAlertIf( returnDate < outDate.setHours(outDate.getHours() + MIN_HOURS_BETWEEN_SAILINGS), searchMessages.returnSailingAfterDepart.replace('<#>', MIN_HOURS_BETWEEN_SAILINGS))) {
        $get('ReturnDay').focus();
        return false;
    }
    return true;
}

function isCheckInDateInFuture(checkInDate, minDaysAhead) {
    var earliest = new Date();
    earliest.setDate(earliest.getDate() + minDaysAhead - 1);
    if (!showAlertIf( checkInDate < earliest, searchMessages.checkInDateInFuture.replace('<#>', earliest.toLocaleDateString())) ) {
        $get('CheckInDay').focus();
        return false;
    }
    return true;
}

function isCheckOutDateAfterCheckInDate(checkInDate, checkOutDate) {
    if (!showAlertIf( checkInDate >= checkOutDate, searchMessages.checkOutDateAfterCheckInDate)) {
        $get('CheckOutDay').focus();
        return false;
    }
    return true;
}

function isHotelRoomCapacityCorrectForNumPax() {
    var numPax = parseInt($value('NumAdults'), 10) + parseInt($value('NumChildren'), 10) - getNumInfants();
    var roomPax = numPeopleForHotelRoom('RoomType1', 'NumRooms1') + numPeopleForHotelRoom('RoomType2', 'NumRooms2');

    if (!showAlertIf( numPax != roomPax, searchMessages.wrongRoomCapacityForPax.replace('<#1>', numPax).replace('<#2>', roomPax).replace('-newline-', '\n') )) {
        $get('RoomType1').focus();
        return false;
    }
    return true;
}

function isAtLeastOneAdultPerHotelRoom() {
    var numAdults = parseInt($value('NumAdults'), 10);
    var numRooms = numHotelRooms('RoomType1', 'NumRooms1') + numHotelRooms('RoomType2', 'NumRooms2');

    if ( !showAlertIf(numAdults < numRooms, searchMessages.adultForEachRoom) ) {
        $get('NumRooms1').focus();
        return false;
    }
    return true;
}

// returns number of infants (age 0 or 1) in child ages list
function getNumInfants() {
    var childAgeString = $value('ChildAges');
    if (childAgeString == '') return 0;

    var numInfants = 0;
    var childAges = childAgeString.split(',');

    for (var i in childAges) {
        if (childAges[i]=='0' || childAges[i]=='1') numInfants++;
    }

    return numInfants;
}

// returns person capacity of selected room type & quantity
function numPeopleForHotelRoom(selectRoomType, selectNumRooms) {
    var roomCapacity = hotelRoomCapacities[$value(selectRoomType)];
    if (roomCapacity==undefined) return 0;
    
    var personsPerRoom = parseInt(roomCapacity, 10);
    var numRooms = parseInt($value(selectNumRooms), 10);
    return personsPerRoom * numRooms;
}

// returns number of hotel rooms for specified room dropdowns
function numHotelRooms(selectRoomType, selectNumRooms) {
    if ($value(selectRoomType)=='') return 0;
    return parseInt($value(selectNumRooms), 10);
}

// check that there is an age for each child
function doAllChildrenHaveAges() {
    var numChildren = parseInt($value('NumChildren'), 10);
    var childAgeString = $value('ChildAges');

    var childAges;
    var numAges;
    if (childAgeString == '') {
        childAges = [];
        numAges = 0;
    }
    else {
        childAges = childAgeString.split(',');
        numAges = childAges.length;
    }

    // trim child ages to numChildren (if larger)
    childAges = childAges.slice(0, numChildren);
    $get('ChildAges').value = childAges.join(',');

    // must be 1 age for every child
    if (numAges < numChildren) {
        alert(searchMessages.childAgeMessage);
        $get('NumChildren').focus();
        return false;
    };

    return true;
}

function doesFootPaxOnlyRouteHaveFootPaxSelected() {
    var route = $value('RouteGroup');

    var footPaxRoutes = ['LondonBourgSM','LondonMoutiers','AshfordBourgSM','AshfordMoutiers','UKAllSkiResorts','UKParis','UKDisney'];
    if (contains(footPaxRoutes, route)) {
        if ($value('Vehicle').substring(0, 3) != 'FOO') {

            alert(searchMessages.footPaxOnly);
            if ($get('Vehicle').tagName=='SELECT') $get('Vehicle').focus()
            return false;
        }
    }
    return true;
}

function checkBicycleRules(vehicle, numPax) {

    if (vehicle.substr(0, 3) != 'BIC') return true;
    if ($('#RouteGroup').val().substr(0, 4) != 'LODR') return true;

    if (numPax > 2) {
        alert(searchMessages.multipleBicycleError);
        return false;
    }
    if (numPax > 1) {
        return confirm(searchMessages.multipleBicycleWarn);
    }
    return true;
}

function checkMotorcycleRules(vehicle, numPax) {

    if (vehicle.substr(0, 3) != 'MTC') return true;

    if (numPax > 2 && $('#RouteGroup').val().substr(0, 4) == 'LODR') {
        alert(searchMessages.multipleMotorcycleError);
        return false;
    }
    if (numPax > 1)
        return confirm(searchMessages.multipleMotorcycleWarn);

    return true;
}

function warnIfLodrDoboFootPax() {

    var route = $("#RouteGroup").val();
    if (route != 'LODRDoveBoul' && route != 'LODRBoulDove') return;

    var route2 = $('#RouteGroupReturn').val();
    if (route2 != 'LODRDoveBoul' && route2 != 'LODRBoulDove') return;

    if ($('#Vehicle').val().substr(0, 3) != 'FOO') return;

    alert(searchMessages.lodrDoboFootPax);
}

function doesHotelHaveRooms() {

    if ($value('HotelLocation') != '') {
        if ($value('RoomType1') == '' || $value('NumRooms1') == '0') {
            alert(searchMessages.hotelButNoRooms);
            $get('RoomType1').focus();
            return false;
        }
        if ($value('RoomType2') != '' && $value('NumRooms2') == '0') {
            alert(searchMessages.hotelButNoRooms);
            $get('NumRooms2').focus();
            return false;
        }
    }
    return true;
}

function formHasUserRoomSelection() {
    // true if there is a 'RoomType1' element which is a dropdown
    return ($get('RoomType1') && $get('RoomType1').tagName=='SELECT');
}

function hasAccomSearchLocation() {
    if (!showAlertIf(($value('HotelLocation')==''), searchMessages.noAccomSearchLocation)) {
        $get('HotelLocation').focus();
        return false;
    }
    return true;
}

function doesHotelLocationUseValidCharacters() {
    //var rgx = /^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ÷\s\-,&']+$/;
    var rgx = /^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890ÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ÷\s\-,_&\(\)\.\@\*\~\|\+\=\-\#\']+$/;
    
    var isMatch = rgx.test($value('HotelLocation'));
    if (!showAlertIf(!isMatch, searchMessages.hotelCharacters)) {
        $get('HotelLocation').focus();
        return false;
    }
    return true;
}

// returns Date object for form values
function createDateFromFormControls(monthYearControl, dayControl) {
    var yearMonth = $value(monthYearControl).split('-');
    var year = yearMonth[0];
    var month = parseInt(yearMonth[1], 10)-1;
    var day = $value(dayControl);

    return new Date(year, month, day);
}

// returns Date object (with time) for form values
function createDateTimeFromFormControls(monthYearControl, dayControl, timeControl) {
    var yearMonth = $value(monthYearControl).split('-');
    var year = yearMonth[0];
    var month = parseInt(yearMonth[1], 10)-1;
    var day = $value(dayControl);
    var time = $value(timeControl);
    var hours = time.substring(0, 2);
    var mins = time.substring(2, 4);
    
    return new Date(year, month, day, hours, mins);
}

function isReturnLegSelected() {
    return ($('#IsReturnLegRequired').val() == 'true');
}

// if condition true, shows alert & returns false
function showAlertIf(condition, message) {
    if (condition) {
        alert(message);
        return false;
    }
    return true;
}

// call child-ages popup
function getChildAges() {
    var numChildren = $value('NumChildren');
    if (numChildren == 0) alert(searchMessages.noChildrenSpecified);
    else window.open('child-ages.aspx?count='+numChildren+'&language='+languageCode,'ChildAges','toolbars=no,scrollbars=no,location=no,statusbars=no,menubars=no,resizable=0,width=325,height=400');
}

function AltCityTownLink_onclick(sender) {
    $get('HotelLocation').value = sender.innerHTML;
    search_onsubmit($get('SearchAgainForm'));
}

function AltAccomLink_onclick(searchText) {
    $get('HotelLocation').value = searchText;
    search_onsubmit($get('SearchAgainForm'));
}

// returns true if array contains value
function contains(arr, value) {
    if (!arr) return false;
    for (var i in arr) if (arr[i]==value) return true; 
    return false;
}

// fill return routes dropdown based on outbound route
function fillReturnRouteDropDown(isInitialising) {

    if (!isReturnLegSelected()) return;

    var route1 = $get('RouteGroup');
    var route1in = $get('RouteGroupReturn');
    var routes = routeCombinations[route1.value];

    var savedValue = route1in.value;

    // copy '(select route)' initial option
    route1in.options.length = 0
    route1in.options[0] = new Option(route1.options[0].innerHTML, route1.options[0].value);

    // nothing selected, just copy first option (not a route)
    if (route1.value == '') return;

    // if no return routes list. copy all out routes to return routes list
    if (!routes) {
        var j = 1;
        for (i = 1; i < route1.options.length; i++) {
            var option = route1.options[i];
            route1in.options[j++] = new Option(option.innerHTML, option.value);
        }
    }
    else {
        // copy routes that are valid return routes
        for (i = 0; i < routes.length; i++) {
            var name = routes[i];

            var text = null;
            for (j = 0; j < route1.options.length; j++) {
                if (route1.options[j].value == name) {
                    text = route1.options[j].innerHTML;
                    break;
                }
            }
            if (text) {
                route1in.options[i + 1] = new Option(text, name);
            }
        }
    }

    // set return-route selected item to reverse of outbound-route
    var ports = route1.options[route1.selectedIndex].innerHTML.split(' - ');
    var reverseRoute = ports[1] + ' - ' + ports[0];
    var index = innerHTMLInCollection(reverseRoute, route1in);
    if (index) route1in.selectedIndex = index;

    // if initialising, and there was a saved index, and it's still there, restore it
    if (isInitialising && savedValue != '') {
        var i2 = valueInCollection(savedValue, route1in.options);
        if (i2) route1in.selectedIndex = i2;
    }
}

// returns index of "item" in array "a", or -1 if not found
function arrayIndex(a, item) {
    for (var i = 0; i < a.length; i++) if (a[i] == item) return i;
    return -1;
}

// returns index of _string_to_find_ in _source_collection_.innerHTML,
// or null if not found
function innerHTMLInCollection(string_to_find, source_collection) {
    for (i = 0; i < source_collection.length; i++) {
        if (source_collection[i].innerHTML == string_to_find) return i;
    }
    return null;
}

// returns index of _string_to_find_ in _source_collection_,
// or null if not found
function valueInCollection(string_to_find, source_collection) {
    for (i = 0; i < source_collection.length; i++) {
        if (source_collection[i].value == string_to_find) return i;
    }
    return null;
}
