/**
 * creates and displays the pricegrid
 * see http://wiki.vidado.com/index.php/Pricegrid
 * 
 * @author Thorsten Rueschenbeck <rueschenbeck@traveltainment.de>
 * @copyright  2010 Traveltainment GmbH
 */


/**
 * constructor
 */
function Pricegrid() {
    this.imgPath = '/kunden_parameter/' + ordner_name + '/ibe_v3/images/pricegrid';
    this.overlayApi = null;
    this.countTotalRows = 0;
    
    // options for configStartDate (start date of the pricegrid): 
    // 'departure': choosen departure date
    // 'firstweek': first week with offer
    // 'firstdate': first offer
    
    // default configuration
    this.includeAlternatives = false;
    this.forceAlternativesAtZeroOffers = true;
    this.configStartDate = 'departure';
    this.displayEmptyCols = false;
    this.independentGrids = false;
    this.newRequestOnWeekAhead = true;
}

/**
 * @param boolean forceAlternatives (optional)
 */
Pricegrid.prototype.display = function(forceAlternatives) {
    var offers = this.getOffers();
    var duration = new Array();
    var startDate = this.getStartDate(null);
    var setBackIndependent;
    var weekAhead;
    
    if (typeof console != 'undefined' && typeof console.debug == 'function') {
//        console.debug(pStrecke);
//        console.debug(this.includeAlternatives);
//        console.debug(offers.length);
//        if (offers.length < 1) {
//            console.debug('no offers');
//        }
    }
    
    this.countTotalRows = 0;
    if (offers.length < 1) {
        // handling for no results
        if (this.includeAlternatives || !this.forceAlternativesAtZeroOffers) {
            pStrecke.noResults();
            if (this.newRequestOnWeekAhead) {
                jQuery('#idListContent').append(
                      '<div class="pricegrid_weekchoice">'
                    + '<a href="javascript:void(0);" class="earlier"><img src="' + this.imgPath + '/arrow_left.png" /><span>Eerder</span></a>'
                    + '<a href="javascript:void(0);" class="later"><span>Later</span><img src="' + this.imgPath + '/arrow_right.png" /></a>'
                    + '</div>'
                );
                if (jQuery('#' + pStrecke.config.idForm).find('#weekAhead').val() > 0) {
                    jQuery('#idListContent').find('.pricegrid_weekchoice a.earlier').click(function() {
                        pricegrid.weekAhead(-1, duration);
                    });
                } else {
                    jQuery('#idListContent').find('.pricegrid_weekchoice a.earlier').addClass('disabled');
                } 
                jQuery('#idListContent').find('.pricegrid_weekchoice a.later').click(function() {
                    pricegrid.weekAhead(+1, duration);
                });
            }
            if (forceAlternatives === true) {
                this.includeAlternatives = false;
            }
        } else {
            this.includeAlternatives = true;
            this.display(true);
        }
        return;
    } else if (forceAlternatives) {
        this.displayAltMessage();
    }
    
    if (!this.independentGrids) {
        jQuery('#idListContent').css('marginBottom', '25px');
    }
    jQuery('#idListContent').find('div').not('.pricegrid_alt_message').remove();
    
    jQuery.each(offers, function(index, offer) {
        if (jQuery.inArray(offer.dauer, duration) == -1) {
            duration.push(offer.dauer);
        }
    });
    duration.sort(this.numSort);
    
    // display grids per durations
    for (var i = 0; i < duration.length; i++) {
        if (this.newRequestOnWeekAhead && !this.independentGrids) {
            this.independentGrids = true;
            setBackIndependent = true;
        }
        if (this.newRequestOnWeekAhead
            && this.getNextDate(duration[i], startDate, 0, 0) > this.getNextDate(null, startDate, 0, 6)) {
            if (setBackIndependent) {
                this.independentGrids = false;
            }
            continue;
        }
        if (setBackIndependent) {
            this.independentGrids = false;
        }
        this.displayPricegridPerDuration(duration[i]);
    }
    
    // insert header / footer
    if (!this.independentGrids) {
        weekAhead = jQuery('#' + pStrecke.config.idForm).find('#weekAhead').val() ? parseInt(jQuery('#' + pStrecke.config.idForm).find('#weekAhead').val()) : 0;
        if (this.newRequestOnWeekAhead) {
            weekAhead = 0;
        }
        // header
        jQuery('div[id^=pricegrid_] > table').first().find('tr[class^=pricegrid_]').first().before(this.getHeader(null, weekAhead));
        // footer
        if (this.countTotalRows >= 10) {
            // show header again at bottom
            var headerElem = this.getHeader(null, weekAhead);
            jQuery(headerElem).find('.pricegrid_weekchoice').addClass('header_bottom');
            jQuery('div[id^=pricegrid_] > table').last().find('tr[class^=pricegrid_]').last().after(headerElem);
        }
        jQuery('div[id^=pricegrid_] > table').last().find('tr[class^=pricegrid_]').last().after(this.getFooter());
        
        // work around for IE
        for (var i = 1; i <= 23; i++) {
            if (jQuery('#pricegrid_period_' + i).length) {
                // set the offset to its own value; this refresh solves the problem
                jQuery('#pricegrid_period_left_' + i).offset(jQuery('#pricegrid_period_left_' + i).offset());
                jQuery('#pricegrid_period_right_' + i).offset(jQuery('#pricegrid_period_right_' + i).offset());
            }
        }
    }
    
    if (forceAlternatives === true) {
        this.includeAlternatives = false;
    }
}

/**
 * callback function to sort numbers
 */
Pricegrid.prototype.numSort = function(a, b) {
    return a - b;
}

/**
 * display pricegrid of given duration
 * 
 * @param duration 
 * @param boolean showHeader
 * @param boolean showFooter
 */
Pricegrid.prototype.displayPricegridPerDuration = function(duration) {
    var pricegridElem;
    var startDate = this.getStartDate(duration);
    var weekAhead;
    if (typeof console != 'undefined' && typeof console.debug == 'function') {
//        console.debug('pricegrid_' + duration + ': ' + this.getOffers(duration).length + ' offers');
    }

    pricegridElem = document.createElement('table');
    jQuery(pricegridElem).addClass('pricegrid');
    if (!this.independentGrids) {
        jQuery(pricegridElem).css('marginBottom', '0');
    }
    
    // header
    if (this.independentGrids) {
        weekAhead = jQuery('#' + pStrecke.config.idForm).find('#weekAhead_' + duration).val() ? parseInt(jQuery('#' + pStrecke.config.idForm).find('#weekAhead_' + duration).val()) : 0;
        jQuery(pricegridElem).append(this.getHeader(duration, weekAhead));
    } else if (this.newRequestOnWeekAhead) {
        weekAhead = 0;
    } else {
        weekAhead = jQuery('#' + pStrecke.config.idForm).find('#weekAhead').val() ? parseInt(jQuery('#' + pStrecke.config.idForm).find('#weekAhead').val()) : 0;
    }
    
    // body
    var bodyRowElem;
    var topBodyFlag = 0;
    var tourOperators = this.getTourOperators(duration);
    var cssRow = 0;
    var rowSpan = 0;
    var lastRowClass;
    for (tourOperator in tourOperators) {
        bodyRowElem = document.createElement('tr')
        cssRow++;
        lastRowClass = (tourOperator == tourOperators.length - 1) ? ' lastRow' : '';
        jQuery(bodyRowElem).addClass((cssRow % 2 == 0 ? 'pricegrid_bodyrow_even' : 'pricegrid_bodyrow_odd'));
        
        // duration (border left)
        if (topBodyFlag == 0) {
            var durationLabel = this.formatDurationToString(duration);
            durationLabel = durationLabel.split(" ");
            var durationUnit = durationLabel.pop();
            var durationValue = durationLabel.join("");
            jQuery(bodyRowElem).html(
                  '<td id="pricegrid_period_' + duration + '" class="pricegrid_period pricegrid_period_left">'
                + '  <div id="pricegrid_period_left_' + duration + '" class="pricegrid_period">'
                + '    <img class="top" src="' + this.imgPath + '/border_left_top.png" />'
//                + '    <img class="middle" src="' + this.imgPath + '/border_left_middle.png" />'
                + '    <img class="bottom" src="' + this.imgPath + '/border_left_bottom.png" />'
                + '    <div>'
                + '      <span class="' + ((durationValue.length < 4) ? 'pricegrid_period_value' : 'pricegrid_period_value_small') + '">' + durationValue + '</span>'
                + '      <br />'
                + '      <span class="pricegrid_period_unit">' + durationUnit + '</span>'
                + '    </div>'
                + '  </div>'
                + '</td>'
            );
        }
        
        // tour operator icon
        operator = tourOperators[tourOperator].split('#');
        operatorLogo = operator[0].split("-");
        if (operatorLogo[1]) {
            operatorIcon = KID + '-' + operatorLogo[0];
        } else {
            operatorIcon = operatorLogo[0];
        }
        
        // proposal
        jQuery(bodyRowElem).append(
              '<td class="pricegrid_proposal' + lastRowClass + '">'
            + '  <img class="pricegrid_touroperator_icon" src="http://nl.images.traveltainment.eu/images/content/va_logos/cp/small/' + operatorIcon + '.gif" alt="' + operator[1] + '" title="Touroperator: ' + operator[1] + '" border="0" height="15" />'
            + '  <div class="pricegrid_proposal">'
            + '    <div>' + operator[2] + '</div>'
            + '    <div class="pricegrid_accomodation">' + operator[3] + '</div>'
            + '  </div>'
            + '</td>'
        );
        
        // pricegrid
        var groupedOffers = this.groupByTourOperatorAndDate(duration);
        if (this.newRequestOnWeekAhead) {
            // check if there are offers for this touroperator/roomtype/boardtype in this week
            var noOffersThisWeek = true;
            for (var i = 0; i < 7; i++) {
                if (groupedOffers[tourOperators[tourOperator]][this.formatDate(this.getNextDate(duration, startDate, weekAhead, i), 'json')]) {
                    noOffersThisWeek = false;
                    break;
                }
            }
            if (noOffersThisWeek) {
                // do not add row
                continue;
            }
        }
        this.countTotalRows++;
        rowSpan++;
        var offers;
        for (var i = 0; i < 7; i++) {
            offers = groupedOffers[tourOperators[tourOperator]][this.formatDate(this.getNextDate(duration, startDate, weekAhead, i), 'json')];
            if (!offers) {
                jQuery(bodyRowElem).append(
                      '<td class="pricegrid_noprice' + lastRowClass + '">'
                    + '  <table class="pricegrid_price_table"><tr><td class="pricegrid_noprice_noprice">-</td></tr></table>'
                    + '</td>'
                );
            } else {
                var cheapestOffer = null;
                for (var j = 0;  j < offers.length; j++) {
                    if (cheapestOffer === null || cheapestOffer.price > offers[j].price) {
                        cheapestOffer = offers[j];
                    }
                }
                var priceClassSuffix = 'normal';
                if (cheapestOffer.price == this.getBestPrice(duration)) {
                    priceClassSuffix = 'bestprice';
                } else if (cheapestOffer.angebot.substring(0, 18) == 'Lastminute Angebot') {
                    priceClassSuffix = 'bargain';
                }
                var identifier = duration + '_' + cssRow + '_' + i;
                jQuery(bodyRowElem).append(
                      '<td class="pricegrid_price' + lastRowClass + '">'
                    + '  <div>'
                    + '    <a id="pricegrid_link_' + identifier + '" href="javascript:void(0);" onclick="pricegrid.checkVakanz(\'' + cheapestOffer.bookingId + '\', \'' + identifier + '\');return false;" class="pricegrid_hover_' + priceClassSuffix + '">'
                    + '      <span id="pricegrid_pricevalue_' + identifier + '" class="pricegrid_pricevalue_hover">&euro; ' + (cheapestOffer.price) + '</span>'
                    + '    </a>'
                    + '    <table class="pricegrid_price_table"><tr><td class="pricegrid_price_' + priceClassSuffix + '"><span class="pricegrid_pricevalue">&euro; ' + (cheapestOffer.price) + '</span></td></tr></table>'
                    + '  </div>'
                    + '</td>'
                );
                
                // set hover/unhover
                jQuery(bodyRowElem).find('#pricegrid_link_' + identifier).mouseenter(function() {
                    pricegrid.hoverPrice(jQuery(this));
                });
                jQuery(bodyRowElem).find('#pricegrid_link_' + identifier).mouseleave(function() {
                    pricegrid.unhoverPrice(jQuery(this));
                });
                
                // set tooltip
                jQuery(bodyRowElem).find('#pricegrid_link_' + identifier).attr('bookingId', cheapestOffer.bookingId);
                jQuery(bodyRowElem).find('#pricegrid_link_' + identifier).tooltip({
                    bodyHandler: function() {return pricegrid.getTooltip(jQuery(this), duration)},
                    delay: 0,
                    fade: 250,
                    // opacity is set in css
                    showURL: false,
                    track: true
                });
            }
        }
        
        // border right
        if (topBodyFlag == 0) {
            jQuery(bodyRowElem).append(
                  '<td class="pricegrid_period pricegrid_period_right">'
                + '  <div id="pricegrid_period_right_' + duration + '" class="pricegrid_period">'
                + '    <img class="top" src="' + this.imgPath + '/border_right_top.png" />'
                + '    <img class="middle" src="' + this.imgPath + '/border_right_middle.png" />'
                + '    <img class="bottom" src="' + this.imgPath + '/border_right_bottom.png" />'
                + '  </div>'
                + '</td>'
            );
            var topBodyFlag = 1;
        }
        jQuery(pricegridElem).append(bodyRowElem);
    }
    jQuery(pricegridElem).find('.pricegrid_period_left').attr('rowspan', rowSpan);
    jQuery(pricegridElem).find('.pricegrid_period_right').attr('rowspan', rowSpan);
    
    // footer
    if (this.independentGrids) {
        jQuery(pricegridElem).append(this.getFooter());
    }
    
    // display pricegrid
    if (!jQuery('#idListContent #pricegrid_' + duration)[0]) {
        jQuery('#idListContent').append('<div id="pricegrid_' + duration + '" class="pricegrid_container"></div>');
    }
    jQuery('#idListContent #pricegrid_' + duration).html(pricegridElem);
    
    // set height of pricegrid border dynamically
    var periodHeight = jQuery('#pricegrid_period_' + duration).height();
    jQuery('#pricegrid_period_left_' + duration).height(periodHeight);
    jQuery('#pricegrid_period_right_' + duration).height(periodHeight);
}

/**
 * get header for pricegrid
 * 
 * @param duration
 * @param weekAhead
 */
Pricegrid.prototype.getHeader = function(duration, weekAhead) {
    var startDate = this.getStartDate(duration);
    var headerElem = document.createElement('tr');
    var weekChoiceText = new Array('week eerder', 'week later');
    
    if (!this.displayEmptyCols) {
        weekChoiceText = new Array('Eerder', 'Later');
    }
    
    // earlier/later buttons
    jQuery(headerElem).addClass('pricegrid_headerrow');
    jQuery(headerElem).html(
          '<td class="pricegrid_noborder">&nbsp;</td>'
        + '<td class="pricegrid_weekchoice">'
        +   '<a href="javascript:void(0);" class="earlier">'
        +     '<img src="' + this.imgPath + '/arrow_left.png" /><span>' + weekChoiceText[0] + '</span>'
        +   '</a>'
        +   '<a href="javascript:void(0);" class="later">'
        +     '<span>' + weekChoiceText[1] + '</span><img src="' + this.imgPath + '/arrow_right.png" />'
        +   '</a>'
        + '</td>'
    );
    if (!this.newRequestOnWeekAhead || jQuery('#' + pStrecke.config.idForm).find('#weekAhead').val() > 0) {
        jQuery(headerElem).find('.pricegrid_weekchoice a.earlier').click(function() {
            pricegrid.weekAhead(-1, duration);
        });
    } else {
        jQuery(headerElem).find('.pricegrid_weekchoice a.earlier').addClass('disabled');
    } 
    jQuery(headerElem).find('.pricegrid_weekchoice a.later').click(function() {
        pricegrid.weekAhead(+1, duration);
    });
    
    // dates
    for(i = 0; i < 7; i++) {
        jQuery(headerElem).append(
              '<td class="pricegrid_date">'
            + '  ' + this.formatDate(this.getNextDate(duration, startDate, weekAhead, i), 'header')
            + '</td>'
        );
    }
    jQuery(headerElem).find('.pricegrid_date').last().addClass('pricegrid_noborder');
    jQuery(headerElem).append('<td class="pricegrid_noborder">&nbsp;</td>');
    
    return headerElem;
}

/**
 * get footer for pricegrid
 */
Pricegrid.prototype.getFooter = function() {
    var footerElem = document.createElement('tr');
    jQuery(footerElem).html(
          '<td class="pricegrid_noborder" colspan="2">&nbsp;</td>'
        + '<td class="pricegrid_footer pricegrid_noborder" colspan="7">'
        + '  <img src="' + this.imgPath + '/price_bestprice_icon.gif" /> <span>Laagste prijs</span> <img src="' + this.imgPath + '/price_normal_icon.gif" /> <span>Normale prijs</span> <img src="' + this.imgPath + '/price_bargain_icon.gif" /> <span>Aanbieding</span> <img src="' + this.imgPath + '/price_noprice_icon.png" /> <span>Niet beschikbaar</span>'
        + '</td>'
        + '<td class="pricegrid_noborder">&nbsp;</td>'
    );
    return footerElem;
}

/**
 * @param timestamp to format 
 * @param type of format
 * @return formatted date
 */
Pricegrid.prototype.formatDate = function(timestamp, type) {
    var date = new Date(timestamp * 1000);
    var formatDate;
    var weekDays = ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'];
    var monthShort = ['jan', 'feb', 'maa', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'];
    
    if (!timestamp) {
        return '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - ';
    }
    switch (type) {
        case 'header':
            // date in custom format, e.g. "feb '10 di 9"
            formatDate = 
                  '<b>' + weekDays[date.getDay()]
                + ' ' + date.getDate() + '</b>'
                + '<br />' + monthShort[date.getMonth()]
                + ' &lsquo;' + String(date.getFullYear()).substr(2, 2);
            break;
        case 'json':
            // date in "Y-n-j" format, e.g. "2010-4-9"
            formatDate = date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate();
            break;
        case 'tooltip':
            // date in "j M Y", e.g. "16 aug 2010"
            formatDate = date.getDate() + ' ' + monthShort[date.getMonth()] + ' ' + date.getFullYear();
            break;
        default:
            formatDate = timestamp;
            break;
    }
    return formatDate;
}

/**
 * @param duration must be string  
 */
Pricegrid.prototype.formatDurationToString = function(duration) {
    if (!duration) {
        return "Alle keuzes";
    }
    switch (duration) {
        case '1':  return '1 nacht';
        case '7':  return '1 week';
        case '14': return '2 weken'; 
        case '21': return '3 weken';
        default:   return duration + ' nachten';
    }
}

/**
 * returns either all offers or only offers with given duration
 * 
 * @param duration (optional)
 * @return array of offers
 */
Pricegrid.prototype.getOffers = function(duration) {
    var offers = new Array();
    
    if (!pStrecke.terminObjects || pStrecke.terminObjects.length < 1) {
        return new Array();
    }
    
    for (var i in pStrecke.terminObjects) {
        if ((this.includeAlternatives || pStrecke.terminObjects[i].getData().alternativTermin == '0')
            && (!duration || pStrecke.terminObjects[i].getData().dauer == duration)) {
            offers.push(pStrecke.terminObjects[i].getData());
        }
    }

    return offers;
}

/**
 * @param duration
 * @return array of offers grouped by tour operators and dates
 */
Pricegrid.prototype.groupByTourOperatorAndDate = function(duration) {
    var groupedResults = new Object();
    var tourOperators = this.getTourOperators(duration);
    var offers = this.getOffers(duration);
    
    for (var i in tourOperators) {
        groupedResults[tourOperators[i]] = new Object();
        for (var j in offers) {
            if (this.getOperatorString(offers[j]) == tourOperators[i]) {
                if (!groupedResults[tourOperators[i]][this.formatDate(offers[j].dateFromTimestamp, 'json')]) {
                    groupedResults[tourOperators[i]][this.formatDate(offers[j].dateFromTimestamp, 'json')] = new Array();
                }
                groupedResults[tourOperators[i]][this.formatDate(offers[j].dateFromTimestamp, 'json')].push(offers[j]);
            }
        }
    }
    return groupedResults;
}

/**
 * @param duration
 * @return array of tour operators
 */
Pricegrid.prototype.getTourOperators = function(duration) {
    var offers = this.getOffers(duration);
    var tourOperators = new Array();
    
    for (var i in offers) {
        if (jQuery.inArray(this.getOperatorString(offers[i]), tourOperators) == -1) {
            tourOperators.push(this.getOperatorString(offers[i]));
        }
    }
    tourOperators.sort(this.sortOperators);

    return tourOperators;
}

/**
 * @param offer
 * @return string of operator information
 */
Pricegrid.prototype.getOperatorString = function(offer) {
    return offer.tourOperator + '#' + offer.longNameVa + '#' + this.translateRoomType(offer.zimmer) + '#' + this.translateBoarding(offer.verpflegungFull);
}

/**
 * callback function for sorting operators
 * 
 * @return sorted operators (Neckermann first)
 */
Pricegrid.prototype.sortOperators = function(a, b) {
    var opA = a.split('#')[0];
    var opB = b.split('#')[0];
    
    if (opA == opB) {
        return 0;
    } else if (opA == 'NVN') {
        return -1;
    } else if (opB == 'NVN') {
        return 1;
    }
    return opA > opB ? 1 : -1;       
}

/**
 * @param duration (optional)
 * @return bestPrice of all offers
 */
Pricegrid.prototype.getBestPrice = function(duration) {
    var bestPrice = 0;
    var setBackAlternatives = false;
    
    if (this.getOffers(duration).length < 1 && !this.includeAlternatives && this.forceAlternativesAtZeroOffers) {
        this.includeAlternatives = true;
        setBackAlternatives = true;
    }
    jQuery.each(this.getOffers(duration), function(index, offer) {
        if (bestPrice == 0 || bestPrice > offer.price) {
            bestPrice = parseInt(offer.price);
        }
    });
    if (setBackAlternatives) {
        this.includeAlternatives = false;
    }
    return bestPrice;
}

/**
 * called by onmouseenter event of price display
 */
Pricegrid.prototype.hoverPrice = function(priceLinkElem) {
    var pricegridColorMapper = {'normal':'#00ACCE', 'bestprice':'#E670AB', 'bargain':'#EC9600'};
    var priceElem = priceLinkElem.find('span');
    var priceClassSuffix = priceLinkElem.attr('class').split('_').pop();
    
    priceElem.css('color', pricegridColorMapper[priceClassSuffix]);
    priceElem.css('display', 'block');
    priceElem.css('position', 'relative');
    priceElem.css('textAlign', 'center');
    priceElem.css('top', '6px');
    priceElem.css('zIndex', '3');
    
    priceLinkElem.css('backgroundImage', 'url(' + this.imgPath + '/price_' + priceClassSuffix + '_hover.png)');
}

/**
 * called by onmouseleave event of price display
 */
Pricegrid.prototype.unhoverPrice = function(priceLinkElem) {
    priceLinkElem.css('backgroundImage', 'none');
    priceLinkElem.find('span').hide();
}

Pricegrid.prototype.getTooltip = function(elem, duration) {
    var offer;
    var tooltip = '';
    var hotelName = pStrecke.terminHotelData.hotelName;
    var strStars = '';
    var operatorLogo;
    var operatorIcon;
    var travellers = jQuery('#personen').val().split(';');
    var personLabel;
    var setBackAlternatives = false;
    
    if (this.getOffers(duration).length < 1 && !this.includeAlternatives && this.forceAlternativesAtZeroOffers) {
        this.includeAlternatives = true;
        setBackAlternatives = true;
    }
    jQuery.each(this.getOffers(duration), function(index, tmpOffer) {
        if (tmpOffer.bookingId == elem.attr('bookingId')) {
            offer = tmpOffer;
            return false;
        }
    });
    if (setBackAlternatives) {
        this.includeAlternatives = false;
    }
    
    // hotel name
    if (hotelName.length > 25) {
        hotelName = hotelName.substring(0, 25 - 3) + '...';
    }
    
    // stars
    for (var j = 0; j < 5; j++) {
        if (j < parseInt(pStrecke.terminHotelData.stars)) {
            strStars += '<img class="hotelStar" src="/kunden_parameter/' + ordner_name + '/ibe_v3/images/ibe/stern_aktiv.gif" />';         
        } else {
            strStars += '<img class="hotelStar" src="/kunden_parameter/' + ordner_name + '/ibe_v3/images/ibe/stern.gif" />';  
        }
    }
    
    // tour operator icon
    operatorLogo = offer.tourOperator.split("-");
    if (operatorLogo[1]) {
        operatorIcon = KID + '-' + operatorLogo[0];
    } else {
        operatorIcon = operatorLogo[0];
    }
    
    // count travellers
    var countTravellers = 0;
    for (var i = 0; i < travellers.length; i++) {
        if (travellers[i].length == 0) {
            break;
        }
        countTravellers++;
    }
    
    tooltip += '<div class="pricegrid_tooltip_left">';
    tooltip += '    <h1>' + hotelName + '</h1>';
    tooltip += '    <div class="stars">' + strStars + '</div>';
    tooltip += '    <div class="departureDate">Vertrekdatum ' + this.formatDate(offer.dateFromTimestamp, 'tooltip') + '</div>';
    tooltip += '    <div class="row">';
    tooltip += '        <div class="left">';
    tooltip += '            <img class="smallpic" src="' + this.imgPath + '/tooltip_travellers.png" /><span>' + countTravellers + ' ' + (countTravellers == 1 ? 'persoon' : 'personen') + '</span>';
    tooltip += '        </div>';
    tooltip += '        <div class="next">';
    tooltip += '            <img src="' + this.imgPath + '/tooltip_duration.png" /><span>' + offer.dauer + ' ' + (offer.dauer == 1 ? 'nacht' : 'nachten') + '</span>';
    tooltip += '        </div>';
    tooltip += '    </div>';
    tooltip += '    <div class="row">';
    tooltip += '        <img src="' + this.imgPath + '/tooltip_roomtype.png" /><span>' + this.translateRoomType(offer.zimmer) + '</span>';
    tooltip += '    </div>';
    tooltip += '    <div class="row">';
    tooltip += '        <img src="' + this.imgPath + '/tooltip_boardtype.png" /><span>' + this.translateBoarding(offer.verpflegungFull) + '</span>';
    tooltip += '    </div>';
    tooltip += '    <div class="row">';
    tooltip += '        <img class="touroperator" src="http://nl.images.traveltainment.eu/images/content/va_logos/cp/small/' + operatorIcon + '.gif" /><span>' + offer.longNameVa + '</span>';
    tooltip += '    </div>';
    tooltip += '    <div class="row">';
    tooltip += '        <h2>Vertrekluchthaven</h2>';
    tooltip += '        <div>';
    tooltip += '            <img src="' + this.imgPath + '/tooltip_airport.png" /><span>' + offer.airport.nameLong + '<span>';
    tooltip += '        </div>';
    tooltip += '    </div>';
//    if (offer.angebot.substring(0, 18) == 'Lastminute Angebot') {
//        tooltip += '    <div class="row">';
//        tooltip += '        <h2>Aanbieding</h2>';
//        tooltip += '        <div>Relevant info! Met meer informatie over het hotel let op tekst bijvoorbeeld</div>';
//        tooltip += '    </div>';
//    }
    tooltip += '</div>';
    tooltip += '<div class="pricegrid_tooltip_right">';
    tooltip += '    <div class="travellers">';
    for (var i = 0; i < countTravellers; i++) {
        if (parseInt(travellers[i]) >= 25) {
            personLabel = 'volwassene';
        } else if (parseInt(travellers[i]) >= 1 && parseInt(travellers[i]) <= 16) {
            personLabel = 'kind';
        }
        
        tooltip += '        <div class="row">';
        tooltip += '            1 ' + personLabel + ' <span class="price">&#8364 ' + number_format(offer.price, 2, ',', '') + '</span>';
        tooltip += '        </div>';
    }
    tooltip += '    </div>';
    tooltip += '    <div class="discount_note">';
    tooltip += '        <strong>Let op:</strong> deze prijs is nog exclusief kinderkorting!';
    tooltip += '    </div>';
    tooltip += '    <div class="total_price">';
    tooltip += '        Totaal: <span class="price"><strong>&#8364 ' + number_format(offer.price * countTravellers, 2, ',', '') + '</strong></span>';
    tooltip += '    </div>';
    tooltip += '</div>';
    
    return tooltip;
}

/**
 * shifts the pricegrid to future or past
 * 
 * @param weeks to shift pricegrid (positive or negative)
 * @param duration
 */
Pricegrid.prototype.weekAhead = function(weeks, duration) {
    var idForm = '#' + pStrecke.config.idForm;
    var weekAhead;
    var newWeekAhead;
    var lastDateOfWeek;
    var startDate = this.formatDate(this.getStartDate(null), 'json');
    var newStartDate;
    var newReturnDate = terminRueck;
    var setBackAlternatives = false;
    var searchDuration;
    
    if (this.independentGrids) {
        if (!jQuery(idForm + ' #weekAhead_' + duration).val()) {
            jQuery(idForm).append('<input id="weekAhead_' + duration + '" type="hidden" value="0" name="weekAhead_' + duration + '" />');
        }
        jQuery(idForm + ' #weekAhead_' + duration).val(parseInt(jQuery(idForm + ' #weekAhead_' + duration).val()) + weeks);
        this.displayPricegridPerDuration(duration, true, true);
        return;
    } else {
        if (!jQuery(idForm + ' #weekAhead').val()) {
            jQuery(idForm).append('<input id="weekAhead" type="hidden" value="0" name="weekAhead" />');
        }
        weekAhead = parseInt(jQuery(idForm + ' #weekAhead').val());
        newWeekAhead = weekAhead + weeks;
        jQuery(idForm + ' #weekAhead').val(newWeekAhead);
        if (!this.newRequestOnWeekAhead) {
            this.display();
            return;
        }
    }
    
    // new request on week ahead
    if (weeks < 0 && jQuery(idForm + ' #backwardsStartDate_' + startDate).val()) {
        var newStartDateTmp;
        newStartDateTmp = jQuery(idForm + ' #backwardsStartDate_' + startDate).val();
        newStartDateTmp = newStartDateTmp.split('-');
        newStartDateTmp = new Date(newStartDateTmp[0], newStartDateTmp[1] - 1, newStartDateTmp[2]);
        newStartDate = newStartDateTmp.getTime() / 1000;
    } else if (weeks < 0) {
        newStartDate = this.getStartDate(null) - 24*60*60 * 7;
    } else {
        if (this.getOffers(duration).length < 1 && !this.includeAlternatives && this.forceAlternativesAtZeroOffers) {
            this.includeAlternatives = true;
            setBackAlternatives = true;
        }
        var i;
        for (i = 6; i >= 0; i--) {
            if (this.getNextDate(null, startDate, 0, i)) {
                lastDateOfWeek = this.getNextDate(null, startDate, 0, i);
                break;
            }
        }
        if (setBackAlternatives) {
            this.includeAlternatives = false;
        }
        if (!lastDateOfWeek) {
            lastDateOfWeek = this.getStartDate(null);
        }
        newStartDate = lastDateOfWeek + 24*60*60 * (7 - i);
        // avoid start date greater than return date
        if (newStartDate > parseInt(terminRueck)) {
            newStartDate = parseInt(terminRueck) + 24*60*60;
            if (jQuery('#dauer').val() == '-1' || jQuery('#dauer').val() == '13') {
                searchDuration = 23;
            } else {
                searchDuration = jQuery('#dauer').val().split('-')[1];
            }
            newReturnDate = newStartDate + 24*60*60 * (searchDuration - 1);
        }
    }
    if (weeks > 0) {
        if (!jQuery(idForm + ' #backwardsStartDate_' + this.formatDate(newStartDate, 'json')).val()) {
            jQuery(idForm).append('<input id="backwardsStartDate_' + this.formatDate(newStartDate, 'json') + '" type="hidden" value="0" name="backwardsStartDate_' + this.formatDate(newStartDate, 'json') + '" />');
        }
        jQuery(idForm + ' #backwardsStartDate_' + this.formatDate(newStartDate, 'json')).val(startDate);
    }
    
    // set new start and return date
    pCalenderFrom.setDate(newStartDate);
    pCalenderTo.setDate(newReturnDate);
    terminHin = newStartDate;
    terminRueck = newReturnDate;   
    $('#termin').val(newStartDate);
    $('#ruecktermin').val(newReturnDate);
    
    // submit search form
    window.setTimeout("sendSearchForm()", 20);
}

/**
 * displays a waiting animation and calls function to check the offer
 * 
 * @param bookingId of the offer to check
 * @param identifier of the html-element, that has been clicked to check the offer.
 */
Pricegrid.prototype.checkVakanz = function(bookingId, identifier) {
    var setBackAlternatives, duration, offer, vakrob_string;

    // Mark clicked HTML-Element
    pricegrid.unhoverPrice(jQuery('a#pricegrid_link_' + identifier));
    jQuery('#tooltip').fadeOut("fast");
    jQuery('a#pricegrid_link_' + identifier)
        .attr('markedForVacancyCheck', 'marked')
        .unbind('mouseenter')
        .unbind('mouseleave')
        .unbind('mouseover')
        .unbind('mouseout')
        .removeAttr('onclick');

    jQuery('a#pricegrid_link_' + identifier)
        .next()
        .find("span")
        .css('display', 'none')
        .after('<div class="vakloadingani" style=""><img alt="" src="/kunden_parameter/thomascook_nl/ibe_v3/images/pricegrid/vakanz_loader.gif" align="center"></div>');

    // Store original vacancy information (for rechecking on booking page)
    duration = identifier.split("_");
    duration = duration[0];
    if (this.getOffers(duration).length < 1 && !this.includeAlternatives && this.forceAlternativesAtZeroOffers) {
        this.includeAlternatives = true;
        setBackAlternatives = true;
    }
    jQuery.each(this.getOffers(duration), function(index, tmpOffer) {
        if (tmpOffer.bookingId == bookingId) {
            offer = tmpOffer;
            return false;
        }
    });
    if (setBackAlternatives) {
        this.includeAlternatives = false;
    }

    // Store Date:
    jQuery("#vakrob_vaksel").val(offer.dateFromTimestamp + "#" + offer.airport.nameLong + "#" + pStrecke.iff);
    
    // proceed vakancy check
    pTermine.vakanz(bookingId);
}

/**
 * temporary function until i18n is implemented
 * 
 * @param boarding as german string
 * @return translated boarding string
 */
Pricegrid.prototype.translateBoarding = function(boarding) {
    var i18nBoarding;
    switch (boarding) {
        case 'Laut Programm': i18nBoarding = 'Volgens programma'; break;
        case 'nur \xDCbernachtung': i18nBoarding = 'Logies'; break;
        case '\xDCbernachtung mit Fr\xFChst\xFCck': i18nBoarding = 'Logies met ontbijt'; break;
        case 'Halbpension': i18nBoarding = 'Halfpension'; break;
        case 'Vollpension': i18nBoarding = 'Volpension'; break;
        case 'All Inclusive': i18nBoarding = 'All Inclusive'; break;
        case 'Vollpension Plus': i18nBoarding = "Volppension plus"; break;
        case 'Halbpension Plus': i18nBoarding = "Halfpension plus"; break;
        case 'All Inclusive Plus': i18nBoarding = "All Inclusive plus"; break;
        default: i18nBoarding = 'Volgens programma'; break;
    }
    return i18nBoarding;
}

/**
 * temporary function until i18n is implemented
 * 
 * @param roomType as (partly) german string
 * @return translated roomType string
 */
Pricegrid.prototype.translateRoomType = function(roomType) {
    roomType = roomType.replace(/mit Meerblick oder seitl. Meerblick/, 'met zeezicht of zijwaarts zeezicht');
    roomType = roomType.replace(/mit seitl. Meerblick/, 'met zijwaarts zeezicht');
    roomType = roomType.replace(/mit Meerblick/, 'met zeezicht');
    
    return roomType;
}

/**
 * @param duration (optional)
 * @return timestamp of start date of the pricegrid
 */
Pricegrid.prototype.getStartDate = function(duration) {
    var startDate;
    
    if (this.getOffers(duration).length < 1 || this.configStartDate == 'departure') {
        return parseInt(terminHin);
    }
    
    jQuery.each(this.getOffers(duration), function(index, offer) {
        if (!startDate || startDate > offer.dateFromTimestamp) {
            startDate = parseInt(offer.dateFromTimestamp);
        }
    });
    
    if (this.configStartDate == 'firstweek') {
        startDate = parseInt(terminHin) + Math.floor((startDate - terminHin) / (7*24*60*60)) * (7*24*60*60);
    }

    return startDate;
}

/**
 * @param duration
 * @param timestamp startDate
 * @param int weekAhead
 * @param int offset in days
 * @return next date
 */
Pricegrid.prototype.getNextDate = function(duration, startDate, weekAhead, offset) {
    var offers;
    var dates = new Array();
    
    if (this.displayEmptyCols) {
        return startDate + (weekAhead * 7*24*60*60) + (offset * 24*60*60);
    }

    offers = this.independentGrids ? this.getOffers(duration) : this.getOffers();
    jQuery.each(offers, function(index, offer) {
        if (jQuery.inArray(parseInt(offer.dateFromTimestamp), dates) == -1) {
            dates.push(parseInt(offer.dateFromTimestamp));
        }
    });
    dates.sort();
    
    return dates[weekAhead * 7 + offset];
}

Pricegrid.prototype.displayAltMessage = function() {
    if (!jQuery('#pricegrid_alt_message').length) {
        var altMessage = document.createElement('div');
        
        jQuery(altMessage).attr('id', 'pricegrid_alt_message');
        jQuery(altMessage).addClass('pricegrid_alt_message');
        jQuery(altMessage).html('We hebben helaas geen aanbiedingen die voldoen aan uw criteria. Dit zijn onze alternatieven:');
        jQuery('#idListContent').prepend(altMessage);
    }
    jQuery('#pricegrid_alt_message').show();
}
    
/**
 * call instance
 */
var pricegrid = new Pricegrid(); 


function number_format (number, decimals, dec_point, thousands_sep) {
    var exponent = "";
    var numberstr = number.toString ();
    var eindex = numberstr.indexOf ("e");
     
    if (eindex > -1) {
        exponent = numberstr.substring (eindex);
        number = parseFloat (numberstr.substring (0, eindex));
    }

    if (decimals != null) {
        var temp = Math.pow (10, decimals);
        number = Math.round (number * temp) / temp;
    }
     
    var sign = number < 0 ? "-" : "";
    var integer = (number > 0 ? Math.floor (number) : Math.abs (Math.ceil (number))).toString ();
    
    var fractional = number.toString ().substring (integer.length + sign.length);
    dec_point = dec_point != null ? dec_point : ".";
    fractional = decimals != null && decimals > 0 || fractional.length > 1 ? (dec_point + fractional.substring (1)) : "";
    if (decimals != null && decimals > 0) {
        for (i = fractional.length - 1, z = decimals; i < z; ++i) {
            fractional += "0";
        }
    }

    thousands_sep = (thousands_sep != dec_point || fractional.length == 0) ? thousands_sep : null;
    if (thousands_sep != null && thousands_sep != "") {
        for (var i = integer.length - 3; i > 0; i -= 3) {
            integer = integer.substring (0 , i) + thousands_sep + integer.substring (i);
        }
    }

    return sign + integer + fractional + exponent;
}


var countRecs = 0;
function array2json(arr) {
    var parts   = [];
    var is_list = (Object.prototype.toString.apply(arr) === '[object Array]');
    
    countRecs++;
    if (countRecs > 5) {
        return;
    }
    for(var key in arr) {
        var value = arr[key];
        if(typeof value == "object") {                  // Custom handling for arrays
            if(is_list) parts.push(array2json(value));  /* :RECURSION: */
            else parts[key] = array2json(value);        /* :RECURSION: */
        } else if (typeof value != "function") {
            var str = "";
            if(!is_list) str = '"' + key + '":';

            //Custom handling for multiple data types
            if(typeof value == "number") str += value;  // Numbers
            else if(value === false) str += 'false';    // The booleans
            else if(value === true) str += 'true';
            else str += '"' + value + '"';              // All other things

            parts.push(str);
        }
    }
    var json = parts.join(",");

    if(is_list) return '[' + json + ']';                // Return numerical JSON
    return '{' + json + '}';                            // Return associative JSON
}
