﻿//minimize it here: http://dean.edwards.name/packer/
//switch on options "Base62 encode" and "Shrink variables"
(function($) { $.fn.bindTo = function(data, options) { var defaults = { appendTo: null, root: "data", onBind: null, onBound: null, fill: false }; var options = $.extend(defaults, options); if ($.isFunction(options.onBind)) { options.onBind() } var template = "<!--bind.template-->" + ((options.fill) ? this.html() : this.parent().html()).replace(/%7B/g, "{").replace(/%7D/g, "}") + "<!--bind.template-->"; var repeaters = this.bindTo.findRepeaters(template); var fixedData = {}; fixedData[options.root] = data; var content = this.bindTo.traverse("bind.template", fixedData, repeaters["<!--bind.template-->"].template, repeaters, "bind.template"); if (options.fill) { this.html(content) } if (options.appendTo != null) { content = $(content).appendTo($(options.appendTo)) } if ($.isFunction(options.onBound)) { options.onBound(content, data) } return content }; $.extend($.fn.bindTo, { templates: {}, traverse: function(key, data, template, repeaters, parent) { if (typeof data == "string" || typeof data == "number" || typeof data == "boolean") { return template.replace(new RegExp("{" + key + "}", "ig"), data) } else { if (typeof data == "object") { if (typeof data.length == "undefined") { if (repeaters["<!--" + parent + "-->"].action) { template = $.fn.bindTo[repeaters["<!--" + parent + "-->"].action](template, data) || template } for (var item in data) { if (typeof data[item] == "object") { if (typeof repeaters["<!--" + item + "-->"] != "undefined") { var temp = $.fn.bindTo.traverse(item, data[item], repeaters["<!--" + item + "-->"].template, repeaters, item); template = template.replace("<!--" + item + "-->", temp) } } else { var temp = $.fn.bindTo.traverse(item, data[item], template, repeaters); template = temp } } return template } else { var listTemplate = ""; for (var item in data) { listTemplate += $.fn.bindTo.traverse(item, data[item], repeaters["<!--" + key + "-->"].template, repeaters, key) } return listTemplate } } } return "" }, findRepeaters: function(template) { $this = this; var templates = {}; var reg = new RegExp("<!--[.a-zA-Z1-9]*-->", "g"); var regAction = new RegExp("<!--action:[$.a-zA-Z1-9]*-->", "g"); var matches = (template.match(reg)); $.each(matches, function() { if (templates[this] != undefined) { return true } templates[this] = {}; var temp = template.substring(template.indexOf(this) + this.length, template.lastIndexOf(this)); var innerMatches = (temp.match(reg)) || []; $.each(innerMatches, function() { if (temp.indexOf(this) > -1) { var innerRepeater = temp.substring(temp.indexOf(this), temp.lastIndexOf(this) + this.length); temp = temp.replace(innerRepeater, this) } }); var actions = (temp.match(regAction)) || []; var key = this; $.each(actions, function() { var action = this.substring(11, this.length - 3); templates[key].action = action; temp = temp.replace(actions, "") }); templates[this].template = temp }); return templates } }) })(jQuery);
var TTHIBServices = {
    TTWSHotel: {
        methods: {
            getDescriptionForHotel: {
                params: {
                    iffCode: 1,
                    time: 1,
                    tourOperator: 1,
                    allowAltDescription: 1,
                    tourOperatorSetFilter: 1
                }
            },
            getShortDescriptionForHotel: {
                params: {
                    iffCode: 1,
                    time: 1,
                    tourOperator: 1,
                    allowAltDescription: 1,
                    tourOperatorSetFilter: 1
                }
            },
            getShortDescriptionsForHotel: {
                params: {
                    iffCode: 1,
                    time: 1,
                    times: 1,
                    tourOperator: 1,
                    allowAltDescription: 1,
                    tourOperatorSetFilter: 1
                }
            },
            getHotelAttributes: {
                params: {
                    iffCode: 1
                }
            }
        }
    },
    TTWSHotelRating: {
        methods: {
            doesHotelRatingExist: {
                params: {
                    language: 1,
                    iffCode: 1
                }
            },
            getHotelRatingStatistic: {
                params: {
                    language: 1,
                    iffCode: 1
                }
            },
            getHotelRatingsStatisticWithDetails: {
                params: {
                    language: 1,
                    iffCode: 1
                }
            },
            getHotelRatings: {
                params: {
                    language: 1,
                    iffCode: 1,
                    sort: 1,
                    rangeFrom: 1,
                    rangeTo: 1
                }
            },
            getHotelRatingsWithDetails: {
                params: {
                    language: 1,
                    iffCode: 1,
                    sort: 1,
                    rangeFrom: 1,
                    rangeTo: 1
                }
            },
            getPicturesFromTravellers: {
                params: {
                    language: 1,
                    iffCode: 1
                }
            }
        }
    },
    TTWSAlmanach: {
        methods: {
            getNavigationRegion: {
                params: {
                    regionRefId: 1,
                    iffCode: 1
                }
            },
            getRegionGeneralInfo: {
                params: {
                    regionRefId: 1,
                    iffCode: 1
                }
            },
            getRegionFoodAndDrink: {
                params: {
                    regionRefId: 1,
                    iffCode: 1
                }
            },
            getRegionActivities: {
                params: {
                    regionRefId: 1,
                    iffCode: 1
                }
            },
            getRegionOutAndAbout: {
                params: {
                    regionRefId: 1,
                    iffCode: 1
                }
            },
            getRegionCountryAndPeople: {
                params: {
                    regionRefId: 1,
                    iffCode: 1
                }
            },
            getRegionFactsAndFigures: {
                params: {
                    regionRefId: 1,
                    iffCode: 1
                }
            },
            getRegionSlideShow: {
                params: {
                    regionRefId: 1,
                    iffCode: 1
                }
            },
            getEmbassy: {
                params: {
                    regionRefId: 1,
                    iffCode: 1
                }
            },
            getMap: {
                params: {
                    iffCode: 1
                }
            }
        }
    },
    TTWSPOI: {
        methods: {
            getPOIs: {
                params: {
                    iffCode: 1,
                    poiType: 1
                }
            },
            getPOIDetail: {
                params: {
                    iffCode: 1,
                    poiRefID: 1
                }
            }
        }
    },
    TTWSWeather: {
        methods: {
            getClimaData: {
                params: {
                    iffCode: 1
                }
            },
            getWeatherForecast: {
                params: {
                    iffCode: 1
                }
            }
        }
    }
};

function TTHIBService(options) {
    this.opts = $.extend({}, TTHIBService.defaults, options);

    var self = this;
    
    for (var service in TTHIBServices) {
        (function() {
            self[service] = {};
            
            for (var method in TTHIBServices[service].methods) {
                (function() {
                    var serviceName = service;
                    var methodName = method;
                    var paramList = TTHIBServices[service].methods[method].params;

                    self[serviceName][methodName] = function() {
                    
                        if (arguments.length == 0) {
                            return;
                        }
                        
                        var callback = arguments[0];
                        var query = self.opts.proxyScriptURL + "?customerID=" + self.opts.KID;

                        query += "&country=" + self.opts.country;
                        query += "&wsName=" + serviceName;
                        query += "&methodName=" + methodName;

                        var i = 1;
                        
                        for (var param in paramList) {
                            if (i > arguments.length - 1)
                                break;
                                
                            if (typeof(arguments[i]) != 'undefined' && arguments[i] != "") {
                                query += "&" + param + "=" + arguments[i];
                            }
                            
                            i++;
                        };
                        
                        if (query.indexOf("language=") < 0) {
                            query += "&language=" + self.opts.language;
                        }
                        
                        query += "&callback=?";

                        $.getJSON(query, {}, callback);
                    };
                })();
            };
        })();
    };
};

TTHIBService.defaults = {
    KID: 0,
    country: "en",
    language: "en",
    proxyScriptURL: "HIBProxy.aspx"
};

/// <reference path="jquery-1.3.2-vsdoc2.js" />     

var TTHIB = {
    defaults: {
        KID: "", 
        proxyScriptURL: "http://hib.traveltainment.co.uk/Ajax/HIBProxy.aspx",
        showErrorAlerts: true,

        events: {
            beforeShortDescriptionLoad: function(placeholders, iffs) { },
            afterShortDescriptionLoad: function(placeholders, response) { },
            beforeAttributesLoad: function(placeholder, iff) { },
            afterAttributesLoad: function(placeholder, response) { },
            beforeSubmenuChange: function(placeholder, hibParameter) { },
            afterSubmenuChange: function(placeholder, subMenuType, response) { },
            beforeContentChange: function(placeholder, hibParameter) { },
            afterContentChange: function(placeholder, contentType, response) { }
        },
        general: {
            subMenuElementIDPrefix: "hibSubMenu_",
            contentElementIDPrefix: "hibContent_"
        },
        hotelShortDescription: {
            show: true,
            elementIDPrefix: "hotelShortDesc_",
            templateID: "hotelShortDescTemplate",         
            maxLength: 255,
            maxHIBLength: 255 // MAXIMUM HOTEL SHORT DESCRIPTION LENGTH RETURNED FROM GERMAN HIB (CONTENT WEB SERVICE)
        },
        hotelAttributes: {
            show: false,
            elementIDPrefix: "hotelAttribs_",
            templateID: "hotelAttributesTemplate"
        },
        mainMenu: {
            show: true,
            elementIDPrefix: "hibMainMenu_",
            templateID: "hibMainMenuTemplate",
            unselectedItemClass: "mainMenuItemSelected",
            selectedItemClass: "mainMenuItemSelected"
        },
        hotelFullDescription:
            {
                subMenuElementIDPrefix: "hibSubMenu_",
                subMenuTemplateID: "hotelInfoSubMenuTemplate",
                unselectedItemClass: "hotelSubMenuItem",
                selectedItemClass: "hotelSubMenuItemSelected",
                contentElementIDPrefix: "hibContent_",
                contentTemplateID: "hotelInfoContentTemplate"
            },
        regionInfo:
            {
                subMenuElementIDPrefix: "hibSubMenu_",
                subMenuTemplate: "regionInfoMenuTemplate",
                subMenuSortOrder: { activities: 3, countryAndPeople: 5, factsAndFigures: 6, foodAndDrink: 2, generalInfo: 1, outAndAbout: 4, slideShow: 7 },
                contentElementIDPrefix: "hibContent_",
                factsAndFiguresTemplate: "regionfactsAndFiguresTemplate",
                slideShowTemplate: "regionSlideShowTemplate",
                generalInfoTemplate: "regionGeneralInfoTemplate"

            },
        areaInfo:
            {
                subMenuElementIDPrefix: "hibSubMenu_",
                subMenuTemplate: "areaInfoSubMenuTemplate",
                contentElementIDPrefix: "hibContent_",
                areaInfoListTemplate: "areaInfoListTemplate",
                areaInfoDetailsTemplate: "areaInfoDetailsTemplate",
                poiTypeNames: {
                    "H": "Hotel",
                    "S": "Beach",
                    "L": "Landscape",
                    "SW": "Sightseeing",
                    "O": "Village",
                    "P": "Promenade",
                    "Y": "Marina",
                    "T": "Tourism",
                    "F": "Amusement Park",
                    "A": "Sport",
                    "K": "Church",
                    "E": "Shopping Mall",
                    "R": "Restaurant",
                    "N": "Nightlife",
                    "V": "Traffic",
                    "PE": "People",
                    "FE": "Celebration",
                    "IMP": "Impression",
                    "WK": "World Heritage"
                }
            },
        map: {
            subMenuElementIDPrefix: "hibSubMenu_",
            subMenuTemplate: "mapSubMenu",
            contentElementIDPrefix: "hibContent_",
            mapTemplate: "mapTemplate",
            mapCssClass: "hibMap",
            markerCssClass: "hibMapMarker",
            markerAnchor: {
                vertical: "top", //top, middle, bottom
                horizontal: "left"//left,middle,right
            },
            markerSize: {
                width: 32,
                height: 32
            }
        },
        weather: {
            subMenuElementIDPrefix: "hibSubMenu_",
            subMenuTemplate: "weatherSubMenu",
            contentElementIDPrefix: "hibContent_",
            forecastTemplate: "forecastTemplate",
            climateTemplate: "climateTemplate"
        }
    },

    initialize: function(options) {
        TTHIB.initializeForHotels(options);
        //TTHIB._initialize(options);
    },
    
    initializeForRegions: function(options) {
        TTHIB._initialize(options);
    },
    
    initializeForHotels: function(options) {
       TTHIB._initialize(options);
       TTHIB._RenderHIBForHotels();
    },
    
    _initialize: function(options) {
        TTHIB.customOptions = options;
        window.$opt = $.extend(true, {}, TTHIB.defaults, TTHIB.customOptions);
        
        if ($opt.hotelShortDescription.maxLength > $opt.hotelShortDescription.maxHIBLength && $opt.showErrorAlerts) {
            alert("hotelShortDescription: maxLength = " + $opt.hotelShortDescription.maxLength + ". Maximum allowed is " + $opt.hotelShortDescription.maxHIBLength + "." );
        }
        
        TTHIB.tthibservice = new TTHIBService({ KID: $opt.KID, proxyScriptURL: $opt.proxyScriptURL });      
    },

    _RenderHIBForHotels: function() {
        TTHIB.ttHotelIFFs = new Array();
        
        var iffString = '', tourOperatorString = '', timesString = '';
        
        $("*[id ^= " + $opt.hotelShortDescription.elementIDPrefix + "]").each(function() {          
            if (this.className != "") {
                var arrayOfClasses = this.className.split(" ");
                
                for (var i in arrayOfClasses) {
                    if (arrayOfClasses[i].substr(0, 1) == "$") {
                        arrayOfClasses[i] = arrayOfClasses[i].substr(1);  //REMOVE $
                                           
                        var arrayOfVariables = arrayOfClasses[i].split("_");
                        
                        switch(arrayOfVariables[0]) {
                            case 'IFF':             TTHIB.ttHotelIFFs.push(arrayOfVariables[1]);
                                                    iffString += arrayOfVariables[1] + "|";
                                                    break;
                                                    
                            case 'TourOperator':    tourOperatorString += arrayOfVariables[1] + "|";
                                                    break;
                                                                                
                            case 'Time':            timesString += TTHIB._ConvertTimestampToDate(this.id, arrayOfVariables[1]) + "|";
                                                    break;
                                                    
                            default:                if ($opt.showErrorAlerts) {
                                                        alert("Unknown class parameter '" + arrayOfVariables[0] + "' for ID " + this.id);
                                                    }
                                                    break;  
                        }
                    }
                };

                tourOperatorString = TTHIB._AlignParameters(iffString, tourOperatorString, "|");
                timesString = TTHIB._AlignParameters(iffString, timesString, "|");               
            }
        });
        
        if (TTHIB.ttHotelIFFs.length > 0) {
            if ($opt.hotelShortDescription.show) {
                TTHIB.GetShortDescriptions(iffString, timesString, tourOperatorString);
            }
                
            if ($opt.hotelAttributes.show) {
                TTHIB.GetHotelAttributes();
            }
                
            if ($opt.mainMenu.show) {
                TTHIB.GenerateMainMenu();
            }
        }
    },
    
    //CONVERT A UNIX TIMESTAMP INTO A WEB SERVICE 'time' PARAMETER
    //
    //NOTE: EXAMPLES SHOWN IN THE WEB CONTENT DOCUMENT INCLUDE THE TIME. HOWEVER, THE RETURNED PROPERTY
    //      'catalogsReferenceTime', WHICH IS SET FROM THE 'time' PARAMETER, ALWAYS SHOWS A TIME OF 'T00:00:00'
    //      SO I PRESUME THE TIME IS NOT SIGNIFICANT, SO NOT SENDING IT
    _ConvertTimestampToDate: function(callMarkerDesc, timestamp) {
        var parsedTimestamp = parseInt(timestamp);
        var timeString = "";
        
        if (isNaN(parsedTimestamp)) {
            if ($opt.showErrorAlerts) {
                alert("Invalid Timestamp '" + timestamp + "' for " + callMarkerDesc);
            }
        } else {
            var d = new Date( (parsedTimestamp * 1000) );
            timeString = d.getFullYear() + "-" + ( d.getMonth() + 1) + "-" + d.getDate();
        }
        
        return timeString;
    },
    
    //PIPE DELIMITED STRINGS CAN BE SENT TO THE WEB SERVICE. 
    //EACH DELIMITED VALUE WITHIN A STRING IS PROCESSED WITH THE SAME POSITIONED VALUE IN ALL THE OTHER DELIMITED STRINGS.
    //THIS FUNCTION TAKE A PARENT PARAMETER STRING, INSERTING DELIMITER(S) INTO THE CHILD PARAMETER STRING AS NECCESSARY 
    //TO ALIGN THE PARENT AND CHILD PARAMETER STRINGS 
    _AlignParameters: function(parentParam, childParam, delimiter) {
        var numParentParams = (parentParam.split("|").length - 1);
        var numChildParams = (childParam.split("|").length - 1);
                
        if (numParentParams > numChildParams) {
            var numDelimsToAdd = numParentParams - numChildParams;
            
            for (var i=0; i < numDelimsToAdd; i++) {
                childParam += delimiter;
            }
        }
        
        return childParam;       
    },
    
    /* GENERAL FUNCTIONS */
    _BindData: function(templateID, placeholderID, data) {
        var placeholderID = "#" + placeholderID;
        var template = $("#" + templateID).html();
        
        if (template !== null) {
            $(placeholderID).html("");
            $(template).bindTo(data, { appendTo: placeholderID });
        } else {
            if ($opt.showErrorAlerts) {
                alert("No template for \"" + templateID + "\"");
            }
        }
    },
    
    /* SHORT DESCRIPTIONS AND ATTRIBUTES */
    GetShortDescriptions: function(hotelIffs, times, tourOperators) {
        $opt.events.beforeShortDescriptionLoad($("*[id ^= " + $opt.hotelShortDescription.elementIDPrefix + "]"), TTHIB.ttHotelIFFs);
        TTHIB.tthibservice.TTWSHotel.getShortDescriptionsForHotel(TTHIB.OnShortDescriptionsReceived, hotelIffs, "", times, tourOperators);
    },
    
    OnShortDescriptionsReceived: function(d) {
        var isError = TTHIB.isErrorFound(d);
        
        if (!isError) {
            for (var i = 0; i < TTHIB.ttHotelIFFs.length; i++) {
                var descObj = d.result.descriptionsForHotel[i];
                
                if (descObj.description != "") {                   
                    retDescObj = TTHIB.ExtractShortDescription(descObj.description, $opt.hotelShortDescription.maxLength, TTHIB.ttHotelIFFs[i]);
                    descObj.description = retDescObj.desc;
                    
                    if (typeof(d.result.HotelDescTruncated) == "undefined") {
                        d.result.HotelDescTruncated = new Array();
                    }
                    
                    d.result.HotelDescTruncated[ TTHIB.ttHotelIFFs[i] ] = retDescObj.hotelDescTruncated;                    
                    TTHIB._BindData($opt.hotelShortDescription.templateID, $opt.hotelShortDescription.elementIDPrefix + TTHIB.ttHotelIFFs[i], descObj);                   
                } else {
                    d.result.ErrorType = "NoHotelDesc";
                    
                    if (!TTHIB.isErrorFound(d)) {
                        d.result.ErrorDetails = new Array();                       
                    }
                    
                    d.result.ErrorDetails[ TTHIB.ttHotelIFFs[i] ] = "No short hotel description for IFF " + TTHIB.ttHotelIFFs[i] + ".";
                }
            };
        }
        
        $opt.events.afterShortDescriptionLoad($("*[id ^= " + $opt.hotelShortDescription.elementIDPrefix + "]"),  d);
    },
    
    truncateHotelDescription: function(desc, maxLength) {        
        //NOTE: -1 = UNLIMITED
        if (maxLength == -1 || desc.length <= maxLength) {
            return { desc: desc, hotelShortDescTruncated: false };
        }
                  
        desc = desc.substring(0, maxLength - 1);
        return { desc: desc, hotelShortDescTruncated: true };
    },
    
    ExtractShortDescription: function(desc, maxLength, iff) {
            //REMOVE BOLD TAGS AT THE BEGINNING OF THE DESCRIPTION, WHICH JUST CONTAIN ZERO OR MORE SPACES
        desc = desc.replace(/^<b>\s*<\/b>/ig, "");
        
            //REPLACE BOLD TAGS, WHICH JUST CONTAIN ZERO OR MORE SPACES, WITH A BLANK SPACE
        desc = desc.replace(/<b>\s*<\/b>/ig, " ");
        
            //REMOVE A BOLD TAG CONTAINING 'GeneralDesc'
        desc = desc.replace(/<b>GeneralDesc<\/b>/ig, "");
       
            //REPLACE REMAINING BOLD TAGS WITH A BLANK SPACE
        desc = desc.replace(/(<b>|<\/b>)/ig, " ");
        
            //REPLACE LINK BREAKS TAGS WITH A BLANK SPACE
        desc = desc.replace(/(<br>|<br \/>)/ig, " ");
                   
            //REPLACE OPENING LIST TAGS WITH A SPACE
        desc = desc.replace(/<li>/ig, " ");
        
            //REPLACE CLOSING LIST TAGS WITH A COMMA
        desc = desc.replace(/\s*<\/li>/ig, ",");
                
            //REMOVE ANY PARTIAL TAGS FROM THE END OF THE SHORT DESCRIPTION
        desc = desc.replace(/<[a-z]*$/ig, "");
        
        var descObj =  TTHIB.truncateHotelDescription(desc, maxLength);
        return descObj;
    },
            
    GetHotelAttributes: function() {
        for (var i = 0; i < TTHIB.ttHotelIFFs.length; i++) {
            $opt.events.beforeAttributesLoad($("#" + $opt.hotelAttributes.elementIDPrefix + TTHIB.ttHotelIFFs[i]), TTHIB.ttHotelIFFs[i]);
            TTHIB.tthibservice.TTWSHotel.getHotelAttributes(TTHIB.OnHotelAttributesReceived, TTHIB.ttHotelIFFs[i]);
        };
    },
    
    OnHotelAttributesReceived: function(d) {   
        var isError = TTHIB.isErrorFound(d);
             
        if (!isError) {
            var retObj = new Array();
            
            for (var group in d.result) {
                if (typeof (d.result[group]) == 'object') {
                    var groupObj = { groupItems: new Array() };
                    
                    for (var attr in d.result[group]) {
                        if (attr.indexOf('item') < 0 && d.result[group][attr] === true) {
                            var propName = "item" + attr.substring(0,1).toUpperCase() + attr.substring(1);
                            groupObj.groupItems.push({ attribute: attr, attributeText: d.result[group][propName] });
                        }
                    };
                    
                    if (groupObj.groupItems.length > 0) {
                        groupObj.group = group;
                        groupObj.groupName = d.result["item" + group.substring(0,1).toUpperCase() + group.substring(1)];
                        retObj.push(groupObj);
                    }
                }
            };
            
            if (retObj.length > 0) {
                TTHIB._BindData($opt.hotelAttributes.templateID, $opt.hotelAttributes.elementIDPrefix + d.parameters.iffCode, retObj);
            } else {
                d.result.ErrorType = "NoHotelAttr";
                d.result.ErrorDetails = "No hotel attributes for IFF " + d.parameters.iffCode + ".";
            }
        }
        
        $opt.events.afterAttributesLoad($("#" + $opt.hotelAttributes.elementIDPrefix + d.parameters.iffCode), d);
    },

    /* MAIN MENU */
    GenerateMainMenu: function() {
        if (TTHIB.ttHotelIFFs.length <= 0) {
            return;
        }
        
        for (var i = 0; i < TTHIB.ttHotelIFFs.length; i++) {
            TTHIB._BindData($opt.mainMenu.templateID, $opt.mainMenu.elementIDPrefix + TTHIB.ttHotelIFFs[i], { iff: TTHIB.ttHotelIFFs[i] });
        };
    },

    /* HOTEL INFORMATION */
    ShowHotelInformation: function(iff, operator, time) {
        $opt.events.beforeSubmenuChange($("#" + $opt.hotelFullDescription.subMenuElementIDPrefix + iff), iff);
        $opt.events.beforeContentChange($("#" + $opt.hotelFullDescription.contentElementIDPrefix + iff), iff);
        
        if (typeof(time) != 'undefined') {
            time = TTHIB._ConvertTimestampToDate("ShowHotelInformation", time);
        } else {
            time="";
        }
        
        TTHIB.tthibservice.TTWSHotel.getDescriptionForHotel(TTHIB.OnHotelInformationReceived, iff, time, operator);
    },
    
    OnHotelInformationReceived: function(d) {
        var isError = TTHIB.isErrorFound(d);
        
        if (!isError) {
            var results = d.result;
            var gotHotelData = false;
            
            for (var i = 0; i < results.allTourOperatorsInHotel.length; i++) {
                if (results.tourOperator != null) {
                    results.allTourOperatorsInHotel[i].itemClass =
                        results.allTourOperatorsInHotel[i].tourOperatorShort == results.tourOperator.tourOperatorShort ?
                            $opt.hotelFullDescription.selectedItemClass : $opt.hotelFullDescription.unselectedItemClass;
                } else {
                    results.allTourOperatorsInHotel[i].itemClass = $opt.hotelFullDescription.unselectedItemClass;
                }
                
                results.allTourOperatorsInHotel[i].iff = d.parameters.iffCode;
            };
                
            if (results.description != "") {
                gotHotelData = true;
            } else {
                d.result.ErrorType = "NoHotelDesc";
                d.result.ErrorDetails = "No hotel description";

                //NOTE: A REQUEST FOR A SPECIFIC TOUR OPERATOR CODE MAY RETURN A 'tourOperator' NODE
                //      FOR A DIFFERENT TOUR OPERATOR CODE, AS A TOUR OPERATOR MAY USE MORE THAN ONE TOUR OPERATOR CODE.
                //      EXAMPLE: FLEETWAY ENCOMPASSES THE TOUR OPERATOR CODES 'XS2S', 'XH4U', 'XLCB' AND 'XOHG'
                if (typeof(d.parameters.tourOperator) != 'undefined') {
                    d.result.ErrorDetails += " for tour operator " + d.parameters.tourOperator;
                } else {
                    d.result.ErrorDetails += " for the default tour operator";
                }
                
                d.result.ErrorDetails += " and IFF " + d.parameters.iffCode;
            }
            
            results.description = TTHIB.ExtractFullDescription(results.description);
            
            //IF A LIST OF TOUR OPERATORS ARE IN THE HOTEL - SHOW THE TOUR OPERATOR SUB MENU AND FLAG THAT HOTEL DATA HAS BEEN RECEIVED
            if (results.allTourOperatorsInHotel.length > 0) {
                TTHIB._BindData($opt.hotelFullDescription.subMenuTemplateID, $opt.hotelFullDescription.subMenuElementIDPrefix + d.parameters.iffCode, results.allTourOperatorsInHotel);
                gotHotelData = true;
            }
            
            //ADD iffCode TO THE RESULTS - FOR USE IN THE SLIDESHOW
            d.result.hibParameter = d.parameters.iffCode;
            
            TTHIB._BindData($opt.hotelFullDescription.contentTemplateID, $opt.hotelFullDescription.contentElementIDPrefix + d.parameters.iffCode, results);
            
            if (!gotHotelData) {
                d.result.ErrorType = "NoHotelInfo";
                d.result.ErrorDetails = "No hotel information for IFF " + d.parameters.iffCode;
            }
        }
        
        $opt.events.afterSubmenuChange($("#" + $opt.hotelFullDescription.subMenuElementIDPrefix + d.parameters.iffCode), $opt.hotelFullDescription.subMenuTemplateID, d);
        
        if (!TTHIB.isErrorFound(d)) {            
            $opt.events.afterContentChange($("#" + $opt.hotelFullDescription.contentElementIDPrefix + d.parameters.iffCode), $opt.hotelFullDescription.contentTemplateID, d);
        }
    },
        
    ExtractFullDescription: function(description) {
        if (description != "") {
                //REMOVE BOLD TAGS AT THE BEGINNING OF THE DESCRIPTION, WHICH JUST CONTAIN ZERO OR MORE SPACES
            description = description.replace(/^<b>\s*<\/b>/ig, "");
            
                //REPLACE OPENING BOLD AND STRONG TAGS WITH A SPACE, CLOSING TABLE COLUMN AND ROW, 
                //OPENING TABLE ROW AND COLUMN, AND AN OPENING BOLD TAG
            description = description.replace(/(<b>|<strong>)/ig, "&nbsp;</td></tr><tr><td class=\"hib_hotel_heading\"><b>");
            
                //REPLACE CLOSING BOLD AND STRONG TAGS WITH A CLOSING BOLD TAG, CLOSING TABLE COLUMN AND ROW,
                //AND AN OPENING TABLE ROW AND COLUMN
            description = description.replace(/(<\/b>|<\/strong>)/ig, "</b></td></tr><tr><td class=\"hib_hotel_text\">");
        }
        
        description = "<table id=\"hotel_details\"><tr><td>" + description + "</td></tr></table>";
        return description;
    },

    /* REGION INFORMATION */
    ShowRegionMenu: function(iff) {
        TTHIB.ShowRegionMenuForIff(iff);
    },
      
    ShowRegionMenuForRefId: function(refId) {
        TTHIB._ShowRegionMenu(refId, 'ForRefId');
    },
    
    ShowRegionMenuForIff: function(iff) {
        TTHIB._ShowRegionMenu(iff, 'ForIff');
    },
    
    _ShowRegionMenu: function(hibParameter, hibParameterType) {
        var hibArg1 = hibParameterType == 'ForRefId' ? hibParameter : "";
        var hibArg2 = hibParameterType == 'ForIff' ? hibParameter : "";
        
        $opt.events.beforeSubmenuChange($("#" + $opt.regionInfo.subMenuElementIDPrefix + hibParameter), hibParameter);
        TTHIB.tthibservice.TTWSAlmanach.getNavigationRegion(TTHIB.OnRegionMenuReceived, hibArg1, hibArg2);
    },
    
    OnRegionMenuReceived: function(d) {    
        var isError = TTHIB.isErrorFound(d);         
        var hibParameterType = typeof(d.parameters.iffCode) == "undefined" ? 'ForRefId' : 'ForIff';      
        var hibParameter = (hibParameterType == 'ForRefId' ? d.parameters.regionRefId : d.parameters.iffCode);
        
        if (!isError) {
            var menuObj = new Array();
            
            for (var prop in d.result) {
                if (prop.indexOf("menuItem") === 0) {
                    var itemName = prop.replace(/menuItem/ig, "");
                    itemName = itemName.substring(0,1).toLowerCase() + itemName.substring(1);
                    
                    if (d.result[itemName]) {
                        menuObj.push({ 'itemName': itemName, displayText: d.result[prop], order: $opt.regionInfo.subMenuSortOrder[itemName], onClickFunction: "TTHIB.ShowRegionDetails" + hibParameterType + "('" + hibParameter + "','" + prop.replace(/menuItem/ig, "") + "');", hibParameter: hibParameter });
                    }
                };
            };
            
            if (menuObj.length > 0) {
                menuObj.sort(function(a, b) { return a.order - b.order; });
                TTHIB._BindData($opt.regionInfo.subMenuTemplate, $opt.regionInfo.subMenuElementIDPrefix + hibParameter, menuObj);
            } else {
                d.result.ErrorType = "NoRegionInfo";
                d.result.ErrorDetails = "No region menu " + hibParameterType + " " + hibParameter;
            }
        }
           
        $opt.events.afterSubmenuChange($("#" + $opt.regionInfo.subMenuElementIDPrefix + hibParameter), $opt.regionInfo.subMenuTemplate, d);
    },
    
    ShowRegionDetails: function(iff, detailName) {
        TTHIB.ShowRegionDetailsForIff(iff, detailName);
    },
    
    ShowRegionDetailsForRefId: function(refId, detailName) {
        TTHIB._ShowRegionDetails(refId, detailName, 'ForRefId');
    },
    
    ShowRegionDetailsForIff: function(iff, detailName) {
        TTHIB._ShowRegionDetails(iff, detailName, 'ForIff');
    },
    
    _ShowRegionDetails: function(hibParameter, detailName, hibParameterType) {
        var hibArg1 = hibParameterType == 'ForRefId' ? hibParameter : "";
        var hibArg2 = hibParameterType == 'ForIff' ? hibParameter : "";
        
        $opt.events.beforeContentChange($("#" + $opt.regionInfo.contentElementIDPrefix + hibParameter), hibParameter);
        var detailName = detailName.substring(0,1).toUpperCase() + detailName.substring(1);
        TTHIB.tthibservice.TTWSAlmanach["getRegion" + detailName](TTHIB.OnRegionDetailsReceived, hibArg1, hibArg2);
    },
    
    OnRegionDetailsReceived: function(d) {
        var isError = TTHIB.isErrorFound(d);
        
        if (!isError) {
            var hibParameter = typeof(d.parameters.iffCode) == "undefined" ? d.parameters.regionRefId : d.parameters.iffCode;
                    
            var templateID = d.result.headLine == "Facts & figures" ? $opt.regionInfo.factsAndFiguresTemplate :
            (d.result.headLine == "Slide show" ? $opt.regionInfo.slideShowTemplate : $opt.regionInfo.generalInfoTemplate);
            
            if (templateID == $opt.regionInfo.generalInfoTemplate) {
                TTHIB._addImageUrls(d.result.thumbnail);
            } else {
                if (templateID == $opt.regionInfo.slideShowTemplate) {
                    TTHIB._addImageUrls(d.result.slideShowImages);
                    
                    //ADD hibParameter TO THE RESULTS FOR USE IN THE SLIDESHOW
                    d.result.hibParameter = hibParameter;
                }
            }
            
            TTHIB._BindData(templateID, $opt.regionInfo.contentElementIDPrefix + hibParameter, d.result);
        }
        
        $opt.events.afterContentChange($("#" + $opt.regionInfo.contentElementIDPrefix + hibParameter), templateID, d);
    },

    /* AREA INFORMATION */
    ShowAreaInformation: function(iff) {
        $opt.events.beforeSubmenuChange($("#" + $opt.areaInfo.subMenuElementIDPrefix + iff), iff);
        $opt.events.beforeContentChange($("#" + $opt.areaInfo.contentElementIDPrefix + iff), iff);
        TTHIB.tthibservice.TTWSPOI.getPOIs(TTHIB.OnAreaInformationReceived, iff);
    },
    
    OnAreaInformationReceived: function(d) {
        var isError = TTHIB.isErrorFound(d);
        
        if (TTHIB.poisCache == null) {
            TTHIB.poisCache = {};
        }
        
        if (!isError) {
            var list = {};
            
            for (var i = 0; i < d.result.pois.length; i++) {
                d.result.pois[i].iff = d.parameters.iffCode;
                var poiType = d.result.pois[i]["poiType"];
                
                if (list[poiType] == null) {
                    list[poiType] = { 'poiType': poiType, poiTypeName: $opt.areaInfo.poiTypeNames[poiType], poisList: new Array() };
                }
                
                list[poiType].poisList.push(d.result.pois[i]);
            };         
            
            if (d.result.pois.length > 0) {    
                TTHIB.poisCache[d.parameters.iffCode] = list;       
                TTHIB.ShowPOIsMenu(d.parameters.iffCode);
                
                //INITIAL LIST TO BE BASED ON THE FIRST POINT OF INTEREST TYPE, RETRIEVED FROM THE WEB SERVICE
                TTHIB.ShowPOIsList(d.parameters.iffCode, d.result.pois[0]["poiType"]);
                
                //INITIAL LIST TO BE BASED ON THE FIRST POINT OF INTEREST TYPE, IN TYPE ORDER
                //TTHIB.ShowPOIsList(d.parameters.iffCode, d.result.poiTypes.poiTypes.split('|')[0]);
            } else {
                d.result.ErrorType = "NoAreaInfo";
                d.result.ErrorDetails = "No area information for IFF " + d.parameters.iffCode + ".";
            }
        }
        
        $opt.events.afterSubmenuChange($("#" + $opt.areaInfo.subMenuElementIDPrefix + d.parameters.iffCode), $opt.areaInfo.subMenuTemplate, d);
        
        if (!TTHIB.isErrorFound(d)) {
            $opt.events.afterContentChange($("#" + $opt.areaInfo.contentElementIDPrefix + d.parameters.iffCode), $opt.areaInfo.areaInfoListTemplate, d);
        }
    },
    
    ShowPOIsMenu: function(iff) {
        var poisList = new Array();
        
        //NOTE: THE ORDER OF THE MENU ITEMS IS CURRENTLY BASED ON THE ORDER OF THE POINTS OF INTEREST TYPES, 
        //      RETRIEVED FROM THE WEB SERVICE. THIS ORDER CAN VARY BETWEEN EACH HOTEL
        for (var poi in TTHIB.poisCache[iff]) {
            poisList.push(TTHIB.poisCache[iff][poi]);
            poisList[poisList.length - 1].iff = iff;
        };
        
        TTHIB._BindData($opt.areaInfo.subMenuTemplate, $opt.areaInfo.subMenuElementIDPrefix + iff, poisList);
    },
    
    ShowPOIsList: function(iff, poiType) {
        TTHIB._BindData($opt.areaInfo.areaInfoListTemplate, $opt.areaInfo.contentElementIDPrefix + iff, TTHIB.poisCache[iff][poiType]);
    },
    
    ShowPOIDetails: function(iff, poiRefId) {
        $opt.events.beforeContentChange($("#" + $opt.areaInfo.contentElementIDPrefix + iff), iff);
        TTHIB.tthibservice.TTWSPOI.getPOIDetail(TTHIB.OnPOIDetailsReceived, iff, poiRefId);
    },
    
    OnPOIDetailsReceived: function(d) {
        var isError = TTHIB.isErrorFound(d);
        
        if (!isError) {
            d.result.poiListItem = TTHIB._FindPOIInCache(d.parameters.iffCode, d.parameters.poiRefID);
            TTHIB._BindData($opt.areaInfo.areaInfoDetailsTemplate, $opt.areaInfo.contentElementIDPrefix + d.parameters.iffCode, d.result);
        }
        
        $opt.events.afterContentChange($("#" + $opt.areaInfo.contentElementIDPrefix + d.parameters.iffCode), $opt.areaInfo.areaInfoDetailsTemplate, d);
    },
    
    _FindPOIInCache: function(iff, poiRefID) {
        for (var type in TTHIB.poisCache[iff]) {
            for (var i = 0; i < TTHIB.poisCache[iff][type].poisList.length; i++) {
                if (TTHIB.poisCache[iff][type].poisList[i].poiRefID == poiRefID)
                    return TTHIB.poisCache[iff][type].poisList[i];
            };
        };
    },

    /* MAP */
    ShowMap: function(iff) {
        $opt.events.beforeSubmenuChange($("#" + $opt.map.subMenuElementIDPrefix + iff), iff);
        $opt.events.beforeContentChange($("#" + $opt.map.contentElementIDPrefix + iff), iff);
        TTHIB.tthibservice.TTWSAlmanach.getMap(TTHIB.OnMapReceived, iff);
    },
    
    OnMapReceived: function(d) {
        var isError = TTHIB.isErrorFound(d);
        
        if (!isError) {         
            d.result.mapHTML = TTHIB._generateMapHTML(d.parameters.iffCode, d.result.image.name, d.result.mapPositionY, d.result.mapPositionX, $opt.map.mapCssClass, $opt.map.markerCssClass);
            TTHIB._BindData($opt.map.subMenuTemplate, $opt.map.subMenuElementIDPrefix + d.parameters.iffCode, d.result);
            TTHIB._BindData($opt.map.mapTemplate, $opt.map.contentElementIDPrefix + d.parameters.iffCode, d.result);
        } else {
            //IF A MAP WEB SERVICE QUERY BY IFF FAILS DUE TO NO MAP BEING AVAILABLE, 'ErrorDetails' IS RETURNED, BUT AN EXCEPTION IS NOT TRIGGERED
            //AND A NORMAL RESPONSE IS RETURNED FROM THE PROXY. THEREFORE, ADD THE 'ErrorType' TO THE RESPONSE FOR THE 'after' EVENT
            if (typeof(d.result.ErrorType) == "undefined") {
                d.result.ErrorType = "NoMapInfo";
                d.result.ErrorDetails = "No map for IFF " + d.parameters.iffCode + ".";
            }
        }
        
        $opt.events.afterSubmenuChange($("#" + $opt.map.subMenuElementIDPrefix + d.parameters.iffCode), $opt.map.subMenuTemplate, d);
        
        if (!TTHIB.isErrorFound(d)) {
            $opt.events.afterContentChange($("#" + $opt.map.contentElementIDPrefix + d.parameters.iffCode), $opt.map.mapTemplate, d);
        }
    },

    _generateMapHTML: function(iff, mapURL, markerTop, markerLeft, mapCssClass, markerCssClass) {
        var mapHTML = "<div id=\"mapDiv_" + iff + "\" style=\"position: relative; z-index: 1;\" class=\"" + mapCssClass + "\">";
        
        if (markerTop != 0 || markerLeft != 0) {
            mapHTML += "<div id=\"markerDiv_" + iff + "\"  style=\"position: absolute; z-index: 10; border-width:0px;\" class=\"" + markerCssClass + "\">&nbsp;</div>";
        }
        
        mapHTML += "<img style=\"border-width: 0px;\" src=\"http://images.traveltainment.de" + mapURL + "\" alt=\"map\" />";
        mapHTML += "</div>";
        
        if (markerTop != 0 || markerLeft != 0) {
            mapHTML += "<script type='text/javascript'>";
            mapHTML += "TTHIB._setMarkerPosition(\"markerDiv_" + iff + "\"," + markerTop + "," + markerLeft + ");";
            mapHTML += "</script>";
        }
        
        return mapHTML;
    },
            
    
    _setMarkerPosition: function(markerID, markerTop, markerLeft) {
        markerID = "#" + markerID;
        var width = $opt.map.markerSize.width;
        var height = $opt.map.markerSize.height;
        
        var xShift = 0;
        
        switch ($opt.map.markerAnchor.horizontal) {
            case "middle": xShift = width / 2; break;
            case "right": xShift = width; break;
            default: xShift = 0; break;
        };
        
        var yShift = 0;
        
        switch ($opt.map.markerAnchor.vertical) {
            case "middle": yShift = height / 2; break;
            case "bottom": yShift = height; break;
            default: yShift = 0; break;
        };
        
        var newPos = { top: markerTop - yShift, left: markerLeft - xShift };
        $(markerID).css(newPos);
    },

    /* WEATHER */
    ShowWeather: function(iff) {
        $opt.events.beforeSubmenuChange($("#" + $opt.weather.subMenuElementIDPrefix + iff), iff);
        TTHIB._BindData($opt.weather.subMenuTemplate, $opt.weather.subMenuElementIDPrefix + iff, { 'iff': iff });
        $opt.events.beforeContentChange($("#" + $opt.weather.contentElementIDPrefix + iff), iff);
        TTHIB.ShowForecast(iff);
    },
    
    ShowForecast: function(iff) {
        $opt.events.beforeContentChange($("#" + $opt.weather.contentElementIDPrefix + iff), iff);
        TTHIB.tthibservice.TTWSWeather.getWeatherForecast(TTHIB.OnWeatherForecastReceived, iff);
    },
    
    OnWeatherForecastReceived: function(d) {
        var isError = TTHIB.isErrorFound(d);
        
        if (!isError) {        
            if (d.result.weatherID != "") {
                if (d.result.weatherForecastsOneDay.length > 0) {                   
                    for (var i = 0; i < d.result.weatherForecastsOneDay.length; i++) {
                        if (d.result.weatherForecastsOneDay[i].temperatureWater == -999) {
                            d.result.weatherForecastsOneDay[i].temperatureWater = "-";
                        }
                    };
                    
                    TTHIB._addObjectToResult(d.result, d.result.weatherForecastsOneDay[0], "todayWeatherForecastsOneDay", "today");
                    d.result.weatherForecastsOneDay.shift();    //Remove today's weather forecast from the array
                } 
                     
                TTHIB._BindData($opt.weather.forecastTemplate, $opt.weather.contentElementIDPrefix + d.parameters.iffCode, d.result);
            } else {
                d.result.ErrorType = "NoWeatherInfo";
                d.result.ErrorDetails = "No weather forecast information for IFF " + d.parameters.iffCode + ".";
            }
        }
        
        $opt.events.afterContentChange($("#" + $opt.weather.contentElementIDPrefix + d.parameters.iffCode), $opt.weather.forecastTemplate, d);
    },
    
    ShowClimateData: function(iff) {
        $opt.events.beforeContentChange($("#" + $opt.weather.contentElementIDPrefix + iff), iff);
        TTHIB.tthibservice.TTWSWeather.getClimaData(TTHIB.OnClimateDataReceived, iff);
    },
    
    OnClimateDataReceived: function(d) {
        var isError = TTHIB.isErrorFound(d);
        
        if (!isError) {
            if (d.result.weatherID != "") {
                var climaProps = new Array("climaDaysOfRain", "climaHoursOfSunshine", "climaMaximumTemperature",
                                            "climaMinimumTemperature", "climaRelativeHumidity", "climaSeaTemperature");
                var i;
                
                for (var i in climaProps) {               
                    if (d.result[climaProps[i]].length > 0) {
                        d.result[climaProps[i]] = TTHIB._setClimateTable(d.result[climaProps[i]]);
                    }
                };

                TTHIB._BindData($opt.weather.climateTemplate, $opt.weather.contentElementIDPrefix + d.parameters.iffCode, d.result);
            } else {
                d.result.ErrorType = "NoClimateInfo";
                d.result.ErrorDetails = "No climate information for IFF " + d.parameters.iffCode + ".";
            }
        }
        
        $opt.events.afterContentChange($("#" + $opt.weather.contentElementIDPrefix + d.parameters.iffCode), $opt.weather.climateTemplate, d);
    },
    
    //THIS FUNCTION IS NEEDED TO RESTRUCTURE THE CLIMATE ARRAYS, SO THAT A JBIND TEMPLATE CAN REFERENCE THEM
    //AS JBIND CAN ONLY REFERENCE AN ARRAY VIA A PROPERTY, NOT VIA A DIRECTLY EG. AN ARRAY OF INTEGERS
    _setClimateTable: function(oldClimateObj) {
        var newClimateObj = new Array();
        
        for (var i = 0; i < oldClimateObj.length; i++) {
            if (oldClimateObj[i] == -999) {
                oldClimateObj[i] = "-";
            }
            
            newClimateObj[i] = { 'month': oldClimateObj[i] };
        };
        
        return newClimateObj;
    },
    
    //JBIND CANNOT REFER TO A SPECIFIC OBJECT IN AN ARRAY, THEREFORE CREATE A SEPARATE OBJECT WITH THE ARRAY INDEX NEEDED
    _addObjectToResult: function(resultObj, arrayObj, newObjName, newObjPropPrefix) {
        if (typeof(arrayObj) != 'undefined') {
            var propertyUpper;
            var anObj = new Object();
            
            for (var property in arrayObj) {
                //SET THE FIRST CHARACTER OF THE PROPERTY TO UPPERCASE, AS THE PROPERTY PREFIX WILL BE ADDED TO IT
                propertyUpper = property.substr(0, 1).toUpperCase() + property.substr(1);
                anObj[newObjPropPrefix + propertyUpper] = arrayObj[property];                   
            }; 
                    
            resultObj[newObjName] = anObj;
        }
    },
    
    //SOME IMAGES HAVE THREE DIFFERENT SIZES INDICATED BY 'low', 'middle' AND 'high' AS PART OF THE URL, ONE
    //OF WHICH WILL BE RETURNED BY THE WEB SERVICE. 
    //THIS FUNCTION ADDS THE OTHER TWO URLS TO THE OBJECT RETURNED BY THE WEB SERVICE AND RE-NAMES THE RETURNED 
    //IMAGE URL PROPERTY NAME TO RELATE TO THE IMAGE SIZE IT CONTAINS
    _addImageUrls: function(imageObj) {
        for (var property in imageObj) {
            if (typeof (imageObj[property]) == 'object') {
                TTHIB._addImageUrls(imageObj[property]);
            }
            
            if (property == "name") {
                var imgSearch = new Array("/low/", "/middle/", "/high/");
                var imgFound = -1;
                
                //DETERMINE WHICH IMAGE SIZE HAS BEEN RETURNED BY THE WEB SERVICE
                for (var i in imgSearch) {
                    if (imageObj[property].indexOf(imgSearch[i]) != -1) {
                        imgFound = i;
                        break;
                    }
                };
                
                //IF SIZE IS PART OF THE IMAGE URL, ADD IMAGE URL PROPERTIES FOR EACH SIZE TYPE IN imgSearch
                if (imgFound != -1) {                    
                    for (var i in imgSearch) {
                        //CREATE NEW IMAGE PROPERTY
                        imageObj[ imgSearch[i].replace(/\//ig, "") + property.substr(0, 1).toUpperCase() + property.substr(1) ] = imageObj[property].replace(imgSearch[imgFound], imgSearch[i]);
                    };
                    
                    //REMOVE THE ORIGINAL IMAGE PROPERTY - AS THERE IS A NEW ONE WITH A NEW SIZE SPECIFIC PROPERTY NAME
                    delete imageObj[property];
                }
            }
        };
    },
    
    isErrorFound: function(d) {
        return typeof(d.result.ErrorDetails) != "undefined" ? true : false;
    }
};
