
(function () {

    TRAVEL.Date = function () {

        var jsdate;
        if (arguments.length == 1) {
            jsdate = arguments[0];
        } else if (arguments.length == 3) {
            jsdate = new Date(arguments[0], arguments[1]-1, arguments[2]);
        }

        var year = jsdate.getFullYear();
        var month = jsdate.getMonth() + 1;
        var day = jsdate.getDate();

        var dayOfWeek = 0 == (dayOfWeek = jsdate.getDay()) ? 7 : dayOfWeek;


        this.getYear = function () {
            return year;
        };

        this.getMonth = function () {
            return month;
        };

        this.getDay = function () {
            return day;
        };

        this.getDayOfWeek = function () {
            return dayOfWeek;
        };

        this.isBefore = function (date) {
            return (year < date.getYear()) || (year === date.getYear() && month < date.getMonth()) || (year === date.getYear() && month === date.getMonth() && day < date.getDay());
        };

        this.isAfter = function (date) {
            return (year > date.getYear()) || (year === date.getYear() && month > date.getMonth()) || (year === date.getYear() && month === date.getMonth() && day > date.getDay());
        };

        this.equals = function (date) {
            return year === date.getYear() && month === date.getMonth() && day === date.getDay();
        };

        this.getDaysBetween = function (day2) {
            var i1 = new Date(year, month - 1, day, 0, 0, 0, 0);
            var i2 = new Date(day2.getYear(), day2.getMonth() - 1, day2.getDay(), 0, 0, 0, 0);

            return Math.abs(Math.floor(( (i2.getTime() - i2.getTimezoneOffset() * 60 * 1000) - (i1.getTime() - i1.getTimezoneOffset() * 60 * 1000) ) / (1000 * 60 * 60 * 24)));
        };

        this.minusDays = function (days) {
            var temp = new Date(year, month - 1, day, 0, 0, 0, 0).getTime();
            return new TRAVEL.Date(new Date(temp - days * 1000 * 60 * 60 * 24));
        };

        this.plusDays = function (days) {
            var temp = new Date(year, month - 1, day, 0, 0, 0, 0).getTime();
            return new TRAVEL.Date(new Date(temp + days * 1000 * 60 * 60 * 24));
        };

        this.toString = function () {
            return year + '-' + TRAVEL.Zero(month) + '-' + TRAVEL.Zero(day);
        };

        return this;
    };

    TRAVEL.Zero = function (number) {
        if (number < 10)
            return '0' + number;
        else
            return '' + number;
    };

    TRAVEL.HourMinute = function (hourArg, minuteArg) {

        var hour = hourArg;
        var minute = minuteArg;

        this.getHour = function () {
            return hour;
        };

        this.getMinute = function () {
            return minute;
        };

        this.equals = function (hourMinute) {
            return hour === hourMinute.getHour() && minute === hourMinute.getMinute();
        };

        this.toString = function () {
            return TRAVEL.Zero(hour) + ':' + TRAVEL.Zero(minute);
        };

        return this;
    };

    TRAVEL.MinutesBetween = function (fromDate, fromTime, toDate, toTime) {
        var from = new Date(fromDate.getYear(), fromDate.getMonth() - 1, fromDate.getDay(), fromTime.getHour(), fromTime.getMinute(), 0);
        var to = new Date(toDate.getYear(), toDate.getMonth() - 1, toDate.getDay(), toTime.getHour(), toTime.getMinute(), 0);

        return Math.abs(Math.floor( ( to.getTime() - from.getTime() ) / (1000 * 60)));
    };

})();

(function () {

    TRAVEL.BOOKINGACCESSORIES = TRAVEL.BOOKINGACCESSORIES || {};

    TRAVEL.BOOKINGACCESSORIES.isInArray = function (array, element) {
        for (var i = 0; i < array.length; i++) {
            if (array[i] == element)
                return true;
        }
        return false;
    };

    TRAVEL.BOOKINGACCESSORIES.addToArray = function (array, element) {
        if (!TRAVEL.BOOKINGACCESSORIES.isInArray(array, element))
            array.push(element);
    };

    TRAVEL.BOOKINGACCESSORIES.removeFromArray = function (array, element) {
        var i = 0;
        while (i < array.length) {
            if (array[i] == element)
                array.splice(i, 1);
            else
                i++;
        }
    };

    TRAVEL.BOOKINGACCESSORIES.getPeriodForDay = function (periodsModel, day) {
        for (var i = 0; i < periodsModel.length; i++) {
            var start = new TRAVEL.Date(periodsModel[i].startY, periodsModel[i].startM, periodsModel[i].startD);
            var end = new TRAVEL.Date(periodsModel[i].endY, periodsModel[i].endM, periodsModel[i].endD);

            if (start.isAfter(day) || end.isBefore(day))
                continue;

            if (periodsModel[i].daysOn == undefined || periodsModel[i].daysOn.length == 0)
                continue;

            var dayNumber = start.getDaysBetween(day);

            if (periodsModel[i].periodicity != 0)
                dayNumber = dayNumber % (7 * periodsModel[i].periodicity);

            var daysOnArr = periodsModel[i].daysOn.split(',');

            for (var j = 0; j < daysOnArr.length; j++)
                if (dayNumber === parseInt(daysOnArr[j], 10))
                    return periodsModel[i];
        }
         return null;
    };

    TRAVEL.BOOKINGACCESSORIES.getTimeRecordForTime = function (period, time) {
        for (var i = 0; i < period.timeRecords.length; i++) {
            if ((time === null && period.timeRecords[i].timeH === undefined && period.timeRecords[i].timeM === undefined) ||
                (time !== null && period.timeRecords[i].timeH === time.getHour() && period.timeRecords[i].timeM === time.getMinute()) ) {
                return period.timeRecords[i];
            }
        }
        return null;
    };

    TRAVEL.BOOKINGACCESSORIES.getPreviousPossible = function (periodsModel, date, time) {

        var periodForDay = TRAVEL.BOOKINGACCESSORIES.getPeriodForDay(periodsModel, date);

        if (periodForDay === null)
            throw new Error('There\'s no period for date');

        var timeRecordIndex = null;
        for (var i = 0; i < periodForDay.timeRecords.length; i++) {
            if ((time === null && periodForDay.timeRecords[i].timeH === undefined && periodForDay.timeRecords[i].timeM === undefined) ||
                (time !== null && periodForDay.timeRecords[i].timeH === time.getHour() && periodForDay.timeRecords[i].timeM === time.getMinute()) ) {
                timeRecordIndex = i;
                break;
            }
        }

        if (timeRecordIndex === null)
            throw new Error('There\'s no time record for time');

        if (timeRecordIndex > 0) {
            return {
                date : date,
                time : new HourMinute(periodForDay.timeRecords[timeRecordIndex].timeH, periodForDay.timeRecords[timeRecordIndex].timeM)
            };
        } else {
            var prevDay = date.minusDays(1);
            var periodForPreviousDay = TRAVEL.BOOKINGACCESSORIES.getPeriodForDay(periodsModel, prevDay);
            if (periodForPreviousDay === null)
                return null;
            return {
                date : prevDay,
                time : new TRAVEL.HourMinute(periodForPreviousDay.timeRecords[periodForPreviousDay.timeRecords.length-1].timeH, periodForPreviousDay.timeRecords[periodForPreviousDay.timeRecords.length-1].timeM)
            };
        }
    };

})();

(function () {

    function applyDiscount (price, discount, percent, volume) {
        if (percent == 0 && volume != 0)
            return Math.max(0, price + (discount ? (-1) : 1) * volume);
        if (percent != 0 && volume == 0)
            return price * (1 + (discount ? (-1) : 1) * percent / 100);
        return price;
    };

    function applyDiscounts ( price, discountsArray, applicationType ) {
        var discounts = $.grep(discountsArray, function(n) { return n != null; } );

        if ( discounts.length == 0 )
            return price;

        if ( applicationType == 'COMBINE' ) {
            $.each(discounts, function() { price = applyDiscount( price, this.chargeType == 'DISCOUNT', this.percent, this.volume); });
            return price;
        } else if ( applicationType == 'HIGHEST' ) {
            var prices = [];
            $.each(discounts, function(i) { prices[i] = applyDiscount( price, this.chargeType == 'DISCOUNT', this.percent, this.volume); });

            var priceMin = Number.MAX_VALUE;
            $.each(prices, function() { priceMin = this < priceMin ? this : priceMin; });

            return priceMin;
        }

        return price;
    };

    function findHighestDiscount (price, discountsArray) {
        if ( discountsArray.length == 0 )
            return null;
        var minPrice = Number.MAX_VALUE, minIndex;
        $.each( discountsArray, function( i ) {
            var newprice = applyDiscount( price, this.charge == 'DISCOUNT', this.percent, this.volume );
            if ( minPrice > newprice ) {
                minIndex = i;
                minPrice = newprice;
            }
        } );
        return discountsArray[minIndex];
    };

    /**
     * bookingModel = {
     *      dates : [ ... ],            // dates for booking
     *      extras : [ ... ],           // extras for booking
     *      userDiscounts : [ ... ],    // user selected discounts ids
     *      data : { ... },             // product data
     *      pq,                         // adults quantity
     *      p1,                         // children quantity
     *      p2,                         // senior quantity
     *      p3,                         // student quantity
     *      price,                      // product price
     *      extrasPrice,                // exstras price
     *      totalPrice,                 // total price
     *      hasIndividualPricing        // individual pricing attribute
     *  }
     */
    TRAVEL.BookingModel = function (productdata) {

        var self = this;

        TRAVEL.Observable2(this);

        this.dates = [];
        this.extras = [];
        this.userDiscounts = [];
        this.data = productdata;
        this.pq = 1;
        this.p1q = 0;
        this.p2q = 0;
        this.p3q = 0;
        this.price = null;
        this.extrasPrice = null;
        this.totalPrice = null;
        this.hasIndividualPricing = null;

        $.each(productdata.extras, function () {
            self.extras.push({
                extra : this,
                id : this.id,
                p : 0,
                q : 0
            });
        });

        this.countPrices = function () {

            var nowDate = new TRAVEL.Date(
                                self.data.year,
                                self.data.month,
                                self.data.dayOfMonth);
            var nowTime = new TRAVEL.HourMinute(
                                self.data.hour,
                                self.data.minute);

            var startTime;
            if (self.data.startHour != undefined && self.data.startMinute != undefined) {
                startTime = new TRAVEL.HourMinute(self.data.startHour, self.data.startMinute);
            }

            var gq = self.pq +
                     self.p1q +
                     self.p2q +
                     self.p3q; // group quantity

            var hasIndividualPricing = false;

            self.price = 0;
            self.extrasPrice = 0;

            for (var i = 0; i < self.dates.length; i++) {
                var timeRecord = self.dates[i].timeRecord;

                var manyDaysDiscounts = [];
                var manyPersonsDiscounts = [];
                var earlyBookingDiscounts = [];
                var lastMinuteDiscounts = [];
                var otherDiscount = [];
                var childDiscounts = [];
                var studentDiscounts = [];
                var seniorDiscounts = [];

                for (var j = 0; j < timeRecord.discounts.length; j++) {
                    var discount = timeRecord.discounts[j];
                    if (discount.type == 'MANY_DAYS_DISCOUNT' && discount.from <= self.dates.length && (discount.to == 0 || discount.to >= self.dates.length)) {
                        manyDaysDiscounts.push(discount);
                    }
                    if (discount.type == 'MANY_PERSONS_DISCOUNT' && discount.from <= gq && (discount.to == 0 || discount.to >= gq)) {
                        manyPersonsDiscounts.push(discount);
                    }
                    if (discount.type == 'EARLY_BOOKING_DISCOUNT' && nowDate.getDaysBetween(self.dates[i].date) >= discount.from) {
                        earlyBookingDiscounts.push(discount);
                    }
                    if (discount.type == 'LAST_MINUTE_DISCOUNT' && TRAVEL.MinutesBetween(nowDate, nowTime, self.dates[i].date, self.dates[i].time != undefined ? self.dates[i].time : startTime) <= (24 * 60 * discount.days + 60 * discount.hours + discount.minutes)) {
                        lastMinuteDiscounts.push(discount);
                    }
                    if (discount.type == 'CHILD_DISCOUNT') {
                        childDiscounts.push(discount);
                    }
                    if (discount.type == 'STUDENT_DISCOUNT') {
                        studentDiscounts.push(discount);
                    }
                    if (discount.type == 'SENIOR_DISCOUNT') {
                        seniorDiscounts.push(discount);
                    }
                    if (discount.type == 'OTHER_DISCOUNT' && TRAVEL.BOOKINGACCESSORIES.isInArray(self.userDiscounts, discount.id)) {
                        otherDiscount.push(discount);
                    }
                }

                var firstLevelDiscounts = [
                        findHighestDiscount(timeRecord.priceInclVat, manyDaysDiscounts),
                        findHighestDiscount(timeRecord.priceInclVat, manyPersonsDiscounts),
                        findHighestDiscount(timeRecord.priceInclVat, earlyBookingDiscounts),
                        findHighestDiscount(timeRecord.priceInclVat, lastMinuteDiscounts),
                        findHighestDiscount(timeRecord.priceInclVat, otherDiscount)
                ];

                var p, p1, p2, p3;
                p = applyDiscounts(timeRecord.priceInclVat, firstLevelDiscounts, timeRecord.applicationType ? timeRecord.applicationType : 'HIGHEST');

                if (self.data.priceCalculationType != 'PER_PACKET' && !hasIndividualPricing)
                    hasIndividualPricing = childDiscounts.length + studentDiscounts.length + seniorDiscounts.length > 0;

                if (hasIndividualPricing) {
                    childDiscounts = [ findHighestDiscount(p, childDiscounts) ];
                    studentDiscounts = [ findHighestDiscount(p, studentDiscounts) ];
                    seniorDiscounts = [ findHighestDiscount(p, seniorDiscounts) ];

                    p1 = applyDiscounts(p, childDiscounts, 'COMBINE'); // child price
                    p2 = applyDiscounts(p, seniorDiscounts, 'COMBINE'); // senior price
                    p3 = applyDiscounts(p, studentDiscounts, 'COMBINE'); // student price
                } else {
                    p1 = p2 = p3 = p;
                }

                self.price += self.pq * p + self.p1q * p1 + self.p2q * p2 + self.p3q * p3;
            }

            $.each(self.extras, function () {
                self.extrasPrice += this.p = this.q * this.extra.price / 100;
            });

            self.totalPrice = self.price + self.extrasPrice;

            self.hasIndividualPricing = hasIndividualPricing;

            if (!self.hasIndividualPricing)
                self.p1q = self.p2q = self.p3q = 0;

            self.notify('displayModel', self);
        };

        this.clear = function () {
            this.dates = [];
            $.each(this.extras, function () { this.q = 0; });
            this.userDiscounts = [];
            this.pq = 1;
            this.p1q = 0;
            this.p2q = 0;
            this.p3q = 0;
            this.price = null;
            this.extrasPrice = null;
            this.totalPrice = null;

            this.notify('modelChanged');
        };

        this.attachObserver('modelChanged', function () {
            self.countPrices();
        });
    };

})();

/**
 * Booking widget
 *
 * @author dettier, Mista K.
 */

(function () {

    var assets;

	/**
	 * Constructor
	 *
	 * @param  target  {DomNode}
	 */
    TRAVEL.Booking = function (target) {

        var self;
        var root;
        var data;

        var h;

        var calendar;
        var infoBlock;
        var step2;
        var results;

        // inject assets
        assets = TRAVEL.Assets.Booking;

        function updateInfoPanel(model) {
            var obj = [];

            var priceCalculationType = {
                'PER_PACKET': assets.priceperpacket,
                'PER_PERSON': assets.priceperperson
            };

            if (h.timeContainer) {
                obj.push('<div class="booking-info-wrapper">',
                    '<div class="info-common">',
                        '<h3>', assets.typebook, '</h3>');
                if (model.answerTime) {
                    obj.push('<p class="request">', assets.requestforbuy, '<br /><span>', assets.answertime, ' ', model.answerTime, '</span></p>');
                }
                if (checkManyTimes(model.calendar.days)) {
                    obj.push('<p class="manytimes">', assets.manypricesatday, '</p>');
                } else {
                    obj.push('<p class="manytimes">', assets.onepriceatday, '</p>');
                }
                if (model.priceCalculationType && priceCalculationType[model.priceCalculationType]) {
                    obj.push('<p class="perpacket">', priceCalculationType[model.priceCalculationType], '</p>');
                }
                obj.push('</div>',
                        '<div class="info-price hidden">', '</div>',
                    '</div>');
                $(h.timeContainer).empty().append(obj.join(''));
            }

        }

        var bookingModel;

        var book = function (newdata) {
            var i, found = false;
            for (i = 0; i < bookingModel.dates.length; i++) {
                if (bookingModel.dates[i].date.equals(newdata.date) && (bookingModel.dates[i].time == null && newdata.time == null || bookingModel.dates[i].time.equals(newdata.time))) {
                    bookingModel.dates.splice(i, 1);
                    bookingModel.notify("modelChanged");
                    found = true;
                    break;
                }
            }
            if (!found) {
                bookingModel.dates.push({
                    date : newdata.date,
                    time : newdata.time,
                    av : newdata.av,
                    period : newdata.period,
                    timeRecord : newdata.timeRecord
                });
                bookingModel.notify("modelChanged");
            }
        };

        var addToShoppingCart = function () {
            var ajaxBookingLink = baseUrl + "ajaxCommand.service?command=travel.JSONBookingActionCommand";

            var obj = {
                id: data.id,
                bookings: [],
                extras: [],
                pq: bookingModel.pq,
                p1q: bookingModel.p1q,
                p2q: bookingModel.p2q,
                p3q: bookingModel.p3q,
                userDiscounts: bookingModel.userDiscounts
            };

            var i;
            for (i = 0; i < bookingModel.dates.length; i++) {
                obj.bookings.push({
                    'day':      bookingModel.dates[i].date.getDay(),
                    'month':    bookingModel.dates[i].date.getMonth(),
                    'year':     bookingModel.dates[i].date.getYear(),
                    'hour':     bookingModel.dates[i].time != null ? bookingModel.dates[i].time.getHour() : undefined,
                    'minute':   bookingModel.dates[i].time != null ? bookingModel.dates[i].time.getMinute() : undefined
                });
            }

            for (i = 0; i < bookingModel.extras.length; i++) {
                if (bookingModel.extras[i].q > 0)
                    obj.extras.push({
                        'id':       bookingModel.extras[i].id,
                        'q':        bookingModel.extras[i].q
                    });
            }

            TRAVEL.postJSON(ajaxBookingLink, obj, function (model) {
                if (model.success) {
                    document.getElementById("shcq").innerHTML = model.shcq;
                }

                bookingModel.clear();
                results.create(model);
            });
        };

        function checkManyTimes(days) {
            var i, stat = false;
            for (i = 1; i < 31; i++) {
                if (days[i] && days[i].times && days[i].times.length) {
                    stat = true;
                    break;
                }
            }
            return stat;
        }

        this.create = function (model, allowClose) {

            data = model;

            bookingModel = new TRAVEL.BookingModel(data);

            var arr = [];

            arr.push('<div class="booking clearer">',
                    '<div class="step step1">',
                        '<div class="fold-up-step', ((allowClose && allowClose === true) ? '' : ' hidden') ,'">Fold this step</div>',
                        '<h2 class="step-name">', assets.choosebookingdate, '</h2>',
                        '<div class="ftl col1">',
                            '<div id="#calendarContainer" class="calendar">', '</div>',
                        '</div>',
                        '<div id="#timeContainer" class="ftl col2">', '</div>',
                        '<div class="postclear">', '</div>',
                    '</div>',
                    '<div id="#infoContainer" class="step info hidden">', '</div>', // TODO: show discount info
                    '<div id="#step2Container" class="step step2">', '</div>',
                    '<div id="#resultsContainer" class="step step2">', '</div>',
                '</div>');

            h = $.template(arr.join(''), root, 'append');

            updateInfoPanel(model);

            var discountsObservable = new TRAVEL.Observable2();

//            var popup = new TRAVEL.BookingTimePopup(discountsObservable);
//            popup.attachObserver("onBook", book);

//            infoBlock = new TRAVEL.BookingInfoBlock(h.infoContainer, discountsObservable);
//            infoBlock.create();

            calendar = new TRAVEL.Calendar(h.calendarContainer, bookingModel, discountsObservable);
            calendar.create(model);
            calendar.attachObserver("onBook", book);

            bookingModel.attachObserver('displayModel', function (bookingModel) {
                calendar.updateBooking(bookingModel);
            });

//            calendar.attachObserver("onBookManyTimes", function (newdata) {
//
//                var i, j, timestring;
//                var bm = bookingModel, times = newdata.data.times;
//                var nd = newdata.data.date.getDay(), nm = newdata.data.date.getMonth(), ny = newdata.data.date.getYear();
//
//                for (j = 0; j < times.length; j++) {
//                    times[j].booked = false;
//                }
//
//                for (i = 0; i < bm.dates.length; i++) {
//                    if (bm.dates[i].date.getYear() == ny && bm.dates[i].date.getMonth() == nm && bm.dates[i].date.getDay() == nd) {
//                        timestring = bm.dates[i].timestring;
//                        for (j = 0; j < times.length; j++) {
//                            if (bm.dates[i].time == null)
//                                continue;
//                            if (times[j].hour == bm.dates[i].time.getHour() && times[j].minute == bm.dates[i].time.getMinute()) {
//                                times[j].booked = true;
//                                break;
//                            }
//                        }
//                    }
//                }
//
//                popup.create(newdata.data, newdata.eventObj);
//            });

            step2 = new TRAVEL.AddToShoppingCart(h.step2Container);
            step2.create(bookingModel);
            step2.attachObserver("onAddToShoppingCart", addToShoppingCart);

            results = new TRAVEL.BookingResults(h.resultsContainer);

            $(".fold-up-step", root).click(function () {
                self.notify("onFoldUpClick");
            });
        };

        self = this;
        root = target;

        TRAVEL.Observable2(this);
    };

    /**
     * Constructor
     *
     * @param  target  {DomNode}
     */
    TRAVEL.Calendar = function(target, bookingModel, observable) {

        var dt = TRAVEL.DateTime;

        var self;
        var root;

        var discountsObservable = observable;

        var getDateOnly = function (d) {
            var tempDate;
            if(typeof d  === 'undefined') {
                tempDate = new Date();
            } else {
                tempDate = new Date(d);
            }
            tempDate.setHours(0,0,0,0);
            return tempDate;
        };

        var getWeekNumber = function (date, dowOffset) {
            dowOffset = typeof(dowOffset) == 'int' ? dowOffset : 0;
            var newYear = new Date(date.getFullYear(), 0, 1);
            var day = newYear.getDay() - dowOffset;
            day = day >= 0 ? day : day + 7;
            var daynum = Math.floor((date.getTime() - newYear.getTime() -
                                     (date.getTimezoneOffset() - newYear.getTimezoneOffset()) * 60000) / 86400000) + 1;
            var weeknum;
            if (day <= 4) {
                weeknum = Math.floor((daynum + day - 1) / 7) + 1;
                if (weeknum > 52) {
                    var nYear = new Date(date.getFullYear() + 1, 0, 1);
                    var nday = nYear.getDay() - dowOffset;
                    nday = nday >= 0 ? nday : nday + 7;
                    weeknum = nday <= 4 ? 1 : 53;
                }
            } else {
                weeknum = Math.floor((daynum + day - 1) / 7);
            }
            return weeknum;
        };

        var ajaxLink = baseUrl + 'ajaxCommand.service?command=travel.ChangeBookingDateCommand';

        var changeMonth = function (month, year) {
            TRAVEL.postJSON(ajaxLink, { id: bookingModel.data.id, action: 'setmonth', month: month, year: year }, function (model) {
                $(root).empty();
                self.create(model);
                self.updateBooking(bookingModel);
            });
        };

        var prevMonthClick = function () {
            TRAVEL.postJSON(ajaxLink, { id: bookingModel.data.id, action: 'prevmonth' }, function (model) {
                $(root).empty();
                self.create(model);
                self.updateBooking(bookingModel);
            });
        };

        var nextMonthClick = function () {
            TRAVEL.postJSON(ajaxLink, { id: bookingModel.data.id, action: 'nextmonth' }, function(model) {
                $(root).empty();
                self.create(model);
                self.updateBooking(bookingModel);
            });
        };

        var model = null;
        var h;

        this.create = function (modelArg) {

            model = modelArg;

            var startOfMonth = getDateOnly(new Date(model.calendar.year, model.calendar.month-1, 1));
            var endOfMonth = getDateOnly(new Date(model.calendar.year, model.calendar.month, 0));

            var cellDate = new Date(startOfMonth);
            var cellDay = cellDate.getDay() - 1;
            if(cellDay < 0) {
                cellDay = cellDay + 7;
            }
            cellDate.setDate(cellDate.getDate() - cellDay); // start of week

            var arr = [
                '<table>',
                    '<tbody>',
                        '<tr class="date-nav">',
                            '<td>&nbsp;</td>',
                            '<td colspan="2" style="text-align:left;"><span id="#prevMonth"><span class="arrow">&lsaquo;</span> ', dt.getMonthOfYearText(model.calendar.month - 1), '</span></td>',
                            '<td colspan="3" class="center">',
                                '%s, %4d'.sprintf(dt.getMonthOfYearText(model.calendar.month), model.calendar.year),
                            '</td>',
                            '<td colspan="2" style="text-align:right;"><span id="#nextMonth">', dt.getMonthOfYearText(model.calendar.month + 1), ' <span class="arrow">&rsaquo;</span></span></td>',
                        '</tr>',
                        '<tr class="day">',
                            '<td>', assets.week, '</td>',
                            '<td><div>', dt.getDayOfWeekText(0), '</div></td>',
                            '<td><div>', dt.getDayOfWeekText(1), '</div></td>',
                            '<td><div>', dt.getDayOfWeekText(2), '</div></td>',
                            '<td><div>', dt.getDayOfWeekText(3), '</div></td>',
                            '<td><div>', dt.getDayOfWeekText(4), '</div></td>',
                            '<td><div>', dt.getDayOfWeekText(5), '</div></td>',
                            '<td><div>', dt.getDayOfWeekText(6), '</div></td>',
                        '</tr>'
                    ];

            var weeksCountToShow = Math.ceil((1 + (endOfMonth.getTime() - cellDate.getTime()) / (1000 * 60 * 60 * 24)) / 7);

            for (var n = 1; n <= weeksCountToShow; n++) {
                arr.push(
                    '<tr id="#row" class="cells">',
                        '<td class="weeknum">', '<div>', getWeekNumber(cellDate).toFixed(0), '</div>', '</td>');

                for (var i = 0; i < 7; i++, cellDate.setDate(cellDate.getDate() + 1)) {
                    arr.push(
                        '<td '+(cellDate.getMonth() == startOfMonth.getMonth() ? 'id="#day'+cellDate.getDate()+'"' : 'class="out-of-month"')+'>',
                            '<div class="cell">',
                                '<div>', cellDate.getDate(), '</div>',
                                '<div class="price">', '</div>',
                            '</div>',
                        '</td>');
                }

                arr.push('</tr>');
            }

            arr.push('<tr id="#insertBeforeElement" class="period-nav">',
                            '<td>&nbsp;</td>',
                            '<td colspan="7">',
                                '&nbsp;',
                                '<div id="#prevPricingMonth" class="ftl"><span><span class="arrow">&lsaquo;</span> ', assets.earlierperiod, '</span></div>',
                                '<div id="#nextPricingMonth" class="ftr"><span>', assets.nextperiod, ' <span class="arrow">&rsaquo;</span></span></div>',
                            '</td>',
                        '</tr>',
                    '</tbody>',
                '</table>');

            h = $.template(arr.join(''), root, 'append');

            h.prevMonth.onclick = function () { prevMonthClick(); };
            h.nextMonth.onclick = function () { nextMonthClick(); };

            if (model.calendar.prevPricingMonth != undefined && model.calendar.prevPricingYear != undefined)
                h.prevPricingMonth.onclick = function () { changeMonth(model.calendar.prevPricingMonth, model.calendar.prevPricingYear); };
            else
                h.prevPricingMonth.style.visibility = "hidden";

            if (model.calendar.nextPricingMonth != undefined && model.calendar.nextPricingYear != undefined)
                h.nextPricingMonth.onclick = function () { changeMonth(model.calendar.nextPricingMonth, model.calendar.nextPricingYear); };
            else
                h.nextPricingMonth.style.visibility = "hidden";

            for (var j in model.calendar.days) {
                var k = model.calendar.days[j];

                var e = h['day'+j], p;

                $(e).data('date', new TRAVEL.Date(model.calendar.year, model.calendar.month, j));
                $(e).data('status', k.status);

                if (k.status == "before" || k.status == "outofperiods") {
                } else if (k.status == "outofstock") {
                    $(e).addClass('period').addClass('out-of-stock');
                    $(e.firstChild.lastChild).html(assets.outofstock);
                } else if (k.status == "many") {
                    $(e).addClass('period').addClass('available');

                    p = {
                        date : new TRAVEL.Date(model.calendar.year, model.calendar.month, j)
                    };
                    p.period = TRAVEL.BOOKINGACCESSORIES.getPeriodForDay(model.calendar.pricingPeriods, p.date);
                    p.times = k.times;

                    var hasDiscounts = false;

                    var pricemin = Number.POSITIVE_INFINITY;
                    for (var z = 0; z < p.period.timeRecords.length; z++) {
                        pricemin = p.period.timeRecords[z].priceInclVat < pricemin ? p.period.timeRecords[z].priceInclVat : pricemin;
                        if (p.period.timeRecords[z].discounts.length > 0)
                            hasDiscounts = true;
                    }

                    if (hasDiscounts)
                        $(e.firstChild).addClass('hasdiscount');

                    $(e.firstChild.lastChild).html( TRAVEL.formatMoney(pricemin, true, true) );

                    $(e).data('to-pass', p);

                    $(e)
                        .click( function (eventObj) {

                                var data = $(this).data('to-pass');

                                var popup = new TRAVEL.BookingTimePopup(discountsObservable);
                                popup.attachObserver("onBook", function (data) {
                                    self.notify("onBook", data);
                                });

                                for (j = 0; j < data.times.length; j++)
                                    data.times[j].booked = false;

                                for (var i = 0; i < bookingModel.dates.length; i++)
                                    if (bookingModel.dates[i].date.equals(data.date))
                                        for (var j = 0; j < data.times.length; j++) {
                                            if (bookingModel.dates[i].time == null)
                                                continue;
                                            if (data.times[j].hour == bookingModel.dates[i].time.getHour() && data.times[j].minute == bookingModel.dates[i].time.getMinute()) {
                                                data.times[j].booked = true;
                                            }
                                        }

                                popup.create(data, eventObj);
                            });

                } else if (k.status == "one") {
                    if (k.av === 0) {
                        $(e).addClass('period').addClass('out-of-stock');
                        $(e.firstChild.lastChild).html(assets.outofstock);
                    } else {
                        $(e).addClass('period').addClass('available');

                        p = {
                            date : new TRAVEL.Date(model.calendar.year, model.calendar.month, j),
                            time : null,
                            av : k.av
                        };
                        p.period = TRAVEL.BOOKINGACCESSORIES.getPeriodForDay(model.calendar.pricingPeriods, p.date);
                        p.timeRecord = TRAVEL.BOOKINGACCESSORIES.getTimeRecordForTime(p.period, null);

                        if (p.timeRecord.discounts.length > 0)
                            $(e.firstChild).addClass('hasdiscount');

                        $(e.firstChild.lastChild).html(TRAVEL.formatMoney(p.timeRecord.priceInclVat, true, true));

                        $(e).data('to-pass', p);

                        $(e)
                            .click( function () {
                                    if (!$(this).hasClass('trail') && !$(this).hasClass('disable'))
                                        self.notify("onBook", $(this).data('to-pass'));
                                })
                            .mouseover( function () {
                                    discountsObservable.notify("showDiscount", $(this).data('to-pass'));
                                })
                            .mouseout( function () {
                                    discountsObservable.notify("showInformation");
                                });
                    }
                }
            }
        };

        this.updateBooking = function (bookingModel) {
            var j, n;

            for (j in model.calendar.days) {

                var e = h['day' + j];

                var edate = $(e).data('date');

                if (model.manyTimes === false) {
                    var d = model.durationH + model.durationM > 0 ? model.durationD : model.durationD - 1;

                    var tr1 = edate.minusDays(d);
                    var tr2 = edate.minusDays(1);

                    var dis1 = edate.plusDays(1);
                    var dis2 = edate.plusDays(d);

                    $(e).removeClass('book').removeClass('trail').removeClass('disable');

                    for (n = 0; n < bookingModel.dates.length; n++) {
                        if (bookingModel.dates[n].date.equals(edate)) {
                            $(e).addClass('book');
                            break;
                        }

                        if (!bookingModel.dates[n].date.isBefore(tr1) && !bookingModel.dates[n].date.isAfter(tr2)) {
                            $(e).addClass('trail').removeClass('disable');
                            break;
                        }

                        if (!bookingModel.dates[n].date.isBefore(dis1) && !bookingModel.dates[n].date.isAfter(dis2)) {
                            if (!$(e).hasClass('trail') && $(e).hasClass('available'))
                                $(e).addClass('disable');
                        }
                    }
                } else {
                    $(e).removeClass('book').removeClass('trail').removeClass('disable');

                    for (n = 0; n < bookingModel.dates.length; n++) {
                        if (bookingModel.dates[n].date.equals(edate)) {
                            $(e).addClass('book');
                            break;
                        }
                    }
                }
            }
        };

        self = this;
        root = target;

        TRAVEL.Observable2(this);
    };

    /**
	 * Constructor
	 *
	 * @param  target  {DomNode}
	 */
    TRAVEL.AddToShoppingCart = function (target) {

        var self;
        var root;

        // inject assets
        assets = TRAVEL.Assets.Booking;

        function getDateTimeString(model) {

            var i, j;
            var beginIndex, prevIndex;
            var date, intermediateDate;
            var s = [], d = [];

            var compareDate = function (a, b) {
                return a - b;
            };

            for (i = 0; i < model.length; i++) {
                date = new Date(model[i].date.getYear(), model[i].date.getMonth()-1, model[i].date.getDay());
                d.push(date);
                if (model[i].duration > 1) {
                    for (j = 1; j < model[i].duration; j++) {
                        intermediateDate = new Date(date);
                        intermediateDate.setDate(intermediateDate.getDate() + j);
                        d.push(intermediateDate);
                    }
                }
            }
            d.sort(compareDate);

            beginIndex = 0;
            prevIndex = 0;
            for (i = 0; i < d.length; i++) {
                // cast to integer
                var datePrev = +d[prevIndex];
                var dateCur = +d[i];

                // check interval
                if (datePrev + 86400000 < dateCur) {
                    // save interval
                    s.push(TRAVEL.DateTime.getDateInterval(d[beginIndex], d[prevIndex]));
                    beginIndex = i;
                }
                prevIndex = i;
            }

            if (prevIndex === d.length - 1) {
                s.push(TRAVEL.DateTime.getDateInterval(d[beginIndex], d[prevIndex]));
            }

            return s.join(', ');
        }

        this.create = function(model) {

            var arr = [
                    '<div class="ftl col1" id="#step2" style="display:none;">',
                        '<div class="order">',
                            '<div class="headrow clearer">',
                                '<div class="productname">', assets.productname, '</div>',
                                '<div class="pieces">', assets.pices, '</div>',
                                '<div class="cost">', assets.totalcost, '</div>',
                            '</div>',
                            '<div class="orderrow clearer">',
                                '<div class="orderinfo clearer">',
                                    '<div class="productname">', model.data.name, '</div>',
                                    '<div class="pieces" id="#commonPricing">', '<input type="text" id="#q" class="incdec" />', '</div>',
                                    '<div class="cost" id="#price">', '</div>',
                                '</div>',
                                '<div class="orderdate">',
                                    '<div class="discounts" style="visibility:hidden"><span>' + assets.showdiscounts + '</span></div>',
                                    '<span id="#datesString">', '</span>',
                                '</div>',
                                '<div class="pricing-wrapper" id="#individualPricing">',
                                    '<div class="pricing clearer">',
                                        '<div class="redcross" title="Remove discounts" style="visibility:hidden">Remove discounts</div>',
                                        '<div class="column">',
                                            '<div class="individual adult">', '<span>', assets.adults, '</span>', '</div>',
                                            '<input type="text" id="#q0" class="incdec" />',
                                        '</div>',
                                        '<div class="column">',
                                            '<div class="individual child">', '<span>', assets.children, '</span>', '</div>',
                                            '<input type="text" id="#q1" class="incdec" />',
                                        '</div>',
                                        '<div class="column">',
                                            '<div class="individual senior">', '<span>', assets.seniors, '</span>', '</div>',
                                            '<input type="text" id="#q2" class="incdec" />',
                                        '</div>',
                                        '<div class="column">',
                                            '<div class="individual student">', '<span>', assets.students, '</span>', '</div>',
                                            '<input type="text" id="#q3" class="incdec" />',
                                        '</div>',
                                    '</div>',
                                '</div>',
                            '</div>',
                            '<div class="clearer extras" id="#extras">', '</div>',
                            '<div class="clearer otherdiscounts" id="#otherdiscounts">', '</div>',
                            '<div class="clearer">',
                                '<div id="#totalprice" style="background-color:#FFFFFF;padding:15px 25px 20px 0px;text-align:right;font-size:22px;color:#FF6A04">', '</div>',
                            '</div>',
                            '<div id="#insertBeforeElement" class="ordersubmit clearer">',
                                '<div class="submit">',
                                    '<div class="ftl">',
                                        '<table class="btn btn20"><tr>',
                                            '<td class="btn-left"><span></span></td>',
                                            '<td class="btn-center">',
                                                '<span unselectable="on"><button id="#add" type="button" class="btn-text">', assets.shoppingcartbutton, '</button></span>',
                                            '</td>',
                                            '<td class="btn-right"><span></span></td>',
                                        '</tr></table>',
                                    '</div>',
                                    '<div class="ftl cancel">',
                                        '<table class="btn btn20"><tr>',
                                            '<td class="btn-left"><span></span></td>',
                                            '<td class="btn-center">',
                                                '<span unselectable="on"><button id="#cancel" type="button" class="btn-text">', assets.cancelbutton, '</button></span>',
                                            '</td>',
                                            '<td class="btn-right"><span></span></td>',
                                        '</tr></table>',
                                    '</div>',
                                '</div>',
                                '<div id="#availabilityError" class="info" style="display:none;">', assets.quantitynotavail, '</div>',
                            '</div>',
                        '</div>',
                    '</div>',
                    '<div class="postclear"></div>'
                    ];

            var h = $.template(arr.join(''), root, 'append');

            h.add.onclick = function() { self.notify("onAddToShoppingCart"); };
            h.cancel.onclick = function() {
                model.clear();
            };

            $(h.q).incdec({min: 1, max: 100});
            $(h.q0).incdec({min: 0, max: 100});
            $(h.q1).incdec({min: 0, max: 100});
            $(h.q2).incdec({min: 0, max: 100});
            $(h.q3).incdec({min: 0, max: 100});

            function makeInt(minumum, defaultvalue) {
                if (minumum == undefined)
                    minimum = 1;
                if (defaultvalue == undefined)
                    defaultvalue = minimum;
                var value = parseInt(this.value, 10);
                value = (isNaN(value)) ? defaultvalue : ((value < minumum) ? minumum : value);
                this.value = value;
                return value;
            }

            function checkAvailability() {
                var gq = model.pq + model.p1q + model.p2q + model.p3q;

                if (gq == 0) {
                    $(h.availabilityError).hide();
                    $(h.add).attr('disabled', true);
                } else {
                    var err = false;
                    for (var i = 0; i < model.dates.length; i++)
                        err = err || (gq > model.dates[i].av);

                    if (err) {
                        $(h.availabilityError).show();
                        $(h.add).attr('disabled', true);
                    } else {
                        $(h.availabilityError).hide();
                        $(h.add).attr('disabled', false);
                    }
                }
            }

            $(h.q).change(function () {
                model.pq = makeInt.call(this, 1, 1);
                model.p1q = 0;
                model.p2q = 0;
                model.p3q = 0;
                model.notify("modelChanged");
            });

            $(h.q0).change(function () {
                model.pq = makeInt.call(this, 0, 1);
                model.notify("modelChanged");
            });

            $(h.q1).change(function () {
                model.p1q = makeInt.call(this, 0, 0);
                model.notify("modelChanged");
            });

            $(h.q2).change(function () {
                model.p2q = makeInt.call(this, 0, 0);
                model.notify("modelChanged");
            });

            $(h.q3).change(function () {
                model.p3q = makeInt.call(this, 0, 0);
                model.notify("modelChanged");
            });

            model.attachObserver("displayModel", function (bookingModel) {
                if (bookingModel.dates.length == 0) {
                    $(h.step2).hide();
                    return;
                } else {
                    $(h.step2).show();
                }

                h.datesString.innerHTML = assets.orderto.sprintf(getDateTimeString(bookingModel.dates));

                if (bookingModel.hasIndividualPricing) {
                    $(h.commonPricing).css('visibility', 'hidden');
                    $(h.individualPricing).show();
                    h.q0.value = bookingModel.pq;
                    h.q1.value = bookingModel.p1q;
                    h.q2.value = bookingModel.p2q;
                    h.q3.value = bookingModel.p3q;
                } else {
                    $(h.commonPricing).css('visibility', 'visible');
                    $(h.individualPricing).hide();
                    h.q.value = bookingModel.pq;
                }

                checkAvailability();

                h.price.innerHTML = TRAVEL.formatMoney(bookingModel.price, true, true);

                h.totalprice.innerHTML = 'Total: ' + TRAVEL.formatMoney(bookingModel.totalPrice, true, true);
            });

            TRAVEL.OtherDiscountsBooking(h.otherdiscounts, model).create();
            TRAVEL.BookingExtras(h.extras, model).create();
        };

        self = this;
        root = target;

        TRAVEL.Observable2(this);
    };

    /**
     * Constuctor
     *
     * @param target
     */
    TRAVEL.BookingInfoBlock = function (target, observable) {

        var self;
        var root;

        var assets = TRAVEL.Assets.Booking;

        var discountsObservable = observable;

        var h;

        var groupDiscounts = function (discounts) {
            var result = {};
            $.each(discounts, function () {
                if (result[this.type] == undefined)
                    result[this.type] = [];
                result[this.type].push(this);
            });
            return result;
        };

        var discountTypes = {
            'MANY_DAYS_DISCOUNT' : assets.manydaysdiscount,
            'MANY_PERSONS_DISCOUNT' : assets.manypersonsdiscount,
            'EARLY_BOOKING_DISCOUNT' : assets.earlybookingdiscount,
            'LAST_MINUTE_DISCOUNT' : assets.lastminutediscount,
            'OTHER_DISCOUNT' : assets.otherdiscount,
            'CHILD_DISCOUNT' : assets.childdiscount,
            'STUDENT_DISCOUNT' : assets.studentdiscount,
            'SENIOR_DISCOUNT' : assets.seniordiscount
        };

        var getDiscountString = function (discount) {

            var lastPart = function (discount) {
              return (discount.chargeType == 'INCREASE' ? '+' : '-') + (discount.percent != 0 ? discount.percent + '%' : discount.volume + ' SEK');
            };

            if (discount.type == 'MANY_DAYS_DISCOUNT')
                return discount.to == 0 ? assets.info_many_days_discount.sprintf(discount.from, '&#8734', lastPart(discount)) : assets.info_many_days_discount.sprintf(discount.from, discount.to, lastPart(discount));
            else if (discount.type == 'MANY_PERSONS_DISCOUNT')
                return discount.to == 0 ? assets.info_many_persons_discount.sprintf(discount.from, '&#8734', lastPart(discount)) : assets.info_many_persons_discount.sprintf(discount.from, discount.to, lastPart(discount));
            else if (discount.type == 'EARLY_BOOKING_DISCOUNT')
                return assets.info_early_booking_discount.sprintf(discount.from, lastPart(discount));
            else if (discount.type == 'LAST_MINUTE_DISCOUNT')
                return assets.info_last_minute_discount.sprintf(discount.days, discount.hours, discount.minutes, lastPart(discount));
            else if (discount.type == 'OTHER_DISCOUNT')
                return '%s %s'.sprintf(discount.name, lastPart(discount));
            else if (discount.type == 'CHILD_DISCOUNT')
                return assets.info_child_discount.sprintf(discount.from, discount.to, lastPart(discount));
            else if (discount.type == 'STUDENT_DISCOUNT')
                return assets.info_student_discount.sprintf(discount.criteria, lastPart(discount));
            else if (discount.type == 'SENIOR_DISCOUNT')
                return assets.info_senior_discount.sprintf(discount.from, lastPart(discount));
        };

        var showDiscountsInfo = function (object) {

            h.title.innerHTML = assets.discountson.sprintf(TRAVEL.DateTime.getMonthOfYearText(object.date.getMonth()) + ' ' + object.date.getDay());

            $(h.info).addClass('hidden');
            $(h.discounts).removeClass('hidden');

            if (object.time != null)
                h.title.innerHTML += ' ' + object.time;

            while (h.discountTable.rows.length > 0)
                h.discountTable.deleteRow(0);

            if (object.timeRecord.discounts == null || object.timeRecord.discounts.length == 0) {
                $(h.nodiscounts).removeClass('hidden');
                $(h.discountTable).addClass('hidden');
                $(h.applicationType).addClass('hidden');
            } else {
                $(h.nodiscounts).addClass('hidden');
                $(h.discountTable).removeClass('hidden');
                $(h.applicationType).removeClass('hidden').html(object.timeRecord.applicationType == 'COMBINE' ? assets.combineddiscounts : assets.highestdiscount);

                var grouped = groupDiscounts(object.timeRecord.discounts);
                var counter = 0;
                var table = h.discountTable;
                $.each(grouped, function (i, discounts) {
                    var row = table.rows.length > 0 ? table.rows[table.rows.length-1] : null;
                    var cell;
                    if (row == null || counter % 3 == 0) {
                        row = table.insertRow(table.rows.length);
                        cell = row.insertCell(0);
                        cell = row.insertCell(1);
                        cell = row.insertCell(2);
                        $.each(row.cells, function () {
                            $(this).addClass('discountcell');
                        });
                    }
                    cell = row.cells[counter % 3];
                    var arr = [ '<div class="discountcell"><div class="discountname">', discountTypes[i], '</div>', '<div class="discountinfo">' ];
                    $.each(discounts, function () {
                        arr.push(getDiscountString(this));
                        arr.push('<br/>');
                    });
                    arr.push('</div></div>');
                    cell.innerHTML = arr.join('');

                    counter++;
                });
            }
        };

        var showCommonInfo = function () {
            h.title.innerHTML = assets.informationtitle;

            $(h.info).removeClass('hidden');
            $(h.discounts).addClass('hidden');
        };

        this.create = function () {
            var arr = [
                '<div id="#main" class="info-body">',
                    '<div class="info-cap"><div class="info-minheight">',
                        '<h3 id="#title"></h3>',
                        '<div id="#info" class="info-message hidden">',
                            TRAVEL.para(assets.information),
                        '</div>',
                        '<div id="#discounts" class="hidden">',
                            '<div id="#nodiscounts" class="hidden">', assets.nodiscounts, '</div>',
                            '<table id="#discountTable" class="discounttable">', '<tbody>', '</tbody>', '</table>',
                            '<div id="#applicationType" class="discountinfo"></div>',
                        '</div>',
                    '</div></div>',
                '</div>'
            ];
            h = $.template(arr.join(''), root, 'append');

            discountsObservable.attachObserver("showDiscount", function (object) {
                showDiscountsInfo(object);
            });

            discountsObservable.attachObserver("showInformation", function () {
                showCommonInfo();
            });

            showCommonInfo();
        };

        self = this;
        root = target;
    };

     /**
         * Constructor
         *
         * @param  target  {DomNode}
         */
     TRAVEL.BookingResults = function (root) {
         var self = this;

         this.create = function (model) {

             $(root).empty();

             var text;
             if (model.success) {
                 if (model.count == 0)
                     text = assets.shoppingcarterror;
                 else if (model.count == 1)
                     text = assets.shoppingcart1item;
                 else
                     text = assets.shoppingcartitems.sprintf(model.count);
             }
             else {
                 text = assets.shoppingcarterror;
             }

             var obj = [];

             obj.push('<div class="gotoshoppingcart-wrapper">',
                         '<h2 class="step-name">' + text + '</h2>');
             if (model.success && model.count > 0) {
                 obj.push('<div class="gotoshoppingcart">',
                             '<table class="btn btn20"><tr>',
                                 '<td class="btn-left"><span></span></td>',
                                 '<td class="btn-center">',
                                     '<span unselectable="on"><button type="button" class="btn-text">', assets.gotoshoppingcart, '</button></span>',
                                 '</td>',
                                 '<td class="btn-right"><span></span></td>',
                             '</tr></table>',
                         '</div>');
             }
             obj.push('</div>');

             $(root).append(obj.join(''));

             $('div.gotoshoppingcart .btn', root).click(function () {
                 window.location = TRAVEL.LinkProvider.shoppingcart;
             });

         };
     };


    /**
     * Popup for "Many times a day" case
     */
    TRAVEL.BookingTimePopup = function (observable) {

        var closeTimer = null;
        var self = this;

        var discountsObservable = observable;

        function cancelTimer() {
            if (closeTimer) {
                window.clearTimeout(closeTimer);
                closeTimer = null;
            }
        }

        /**
         * Show popup
         * model.times[i].timestring - time value
         * model.times[i].[a-c] - prices
         *
         * @param model
         * @param event  {DOMEvent}  Event object after mouse click event
         */
        this.create = function (model, eventObj) {
            // cancel destroy timer if present
            cancelTimer();

            var i;

            var obj = ['<table class="table"><tbody>',
                    '<tr>',
                        '<th class="c1"><div>', assets.time, '</div></th>',
                        '<th class="c2"><div>', assets.price, '</div></th>',
                    '</tr>'
            ];
            for (i = 0; i < model.times.length; i++) {
                model.times[i].timeRecord = TRAVEL.BOOKINGACCESSORIES.getTimeRecordForTime(model.period, new TRAVEL.HourMinute(model.times[i].hour, model.times[i].minute));
                obj.push('<tr class="',
                            (i === 0 ? 'first ' : ''),
                            (model.times[i].booked ? 'selected' : ''),
                            (model.times[i].available == 0 ? 'outofstock' : ''),
                        '">',
                        '<td class="c1"><div>', assets.timeshort.length === 0 ? '' : assets.timeshort+' ', model.times[i].timestring, '</div></td>',
                        '<td class="c2"><div>', assets.priceshort.length === 0 ? '' : assets.priceshort+' ', TRAVEL.formatMoney(model.times[i].timeRecord.priceInclVat, true, true), '</div></td>',
                    '</tr>');
            }
            obj.push('</tbody></table>');

            var ele = document.getElementById('booking-popup');
            if (!ele) {
                ele = document.createElement('div');
                $(ele).attr('id', 'booking-popup');
            } else {
                $(ele).empty();
            }
            $(ele).append(obj.join('')).appendTo('body');

            if (eventObj) {
                $(ele).css({'top': eventObj.pageY, 'left': eventObj.pageX});
            }

            var rows = $('tr:gt(0)', ele);
            for (i = 0; i < rows.length; i++) {
                (function (object, model, time) {

                    var timeToPass = new TRAVEL.HourMinute(time.hour, time.minute);
                    var period = model.period;
                    var objectToPass = {
                        date : model.date,
                        time : timeToPass,
                        av : time.av,
                        period : period,
                        timeRecord : TRAVEL.BOOKINGACCESSORIES.getTimeRecordForTime(period, timeToPass)
                    };


                    $('div', object)
                        .mouseover(function () {
                            discountsObservable.notify("showDiscount", objectToPass);
                        })
                        .mouseout(function () {
                            discountsObservable.notify("showInformation");
                        })
                        .click(function () {
                            self.notify('onBook', objectToPass);
                            self.destroy();
                        });
                })(rows.get(i), model, model.times[i]);
            }

            $(ele).hover(cancelTimer, function () {
                cancelTimer();
                closeTimer = window.setTimeout(self.destroy, 2000);
            });

        };

        this.destroy = function () {
            discountsObservable.notify("showInformation");
            var ele = document.getElementById('booking-popup');
            if (ele) {
                $(ele).remove();
            }
            closeTimer = null;
        };

        TRAVEL.Observable2(this);
    };
})();