PageSourceSearch

https://paginehoreca.it/plugins/booking/static/lib.js

js paginehoreca.it collected 2026-10-08 20:55:51 UTC 181,599 bytes, 4,011 lines download raw bytes

1
2/******************************************************************************
3 *
4 *	PROJECT: Flynax Classifieds Software
5 *	VERSION: 4.8.2
6 *	LISENSE: FL5S1Z14Y14Q - https://www.flynax.com/flynax-software-eula.html
7 *	PRODUCT: Classified Ads Script
8 *	DOMAIN : paginehoreca.it
9 *	FILE   : LIB.JS
10 *
11 *	This script is a commercial software and any kind of using it must be
12 *	coordinate with Flynax Owners Team and be agree to Flynax License Agreement
13 *
14 *	This block may not be removed from this file or any other files with out
15 *	permission of Flynax respective owners.
16 *
17 *	Copyrights Flynax Classifieds Software | 2022
18 *	https://www.flynax.com
19 *
20 ******************************************************************************/
21
22var bookingClass = function(){
23    var self = this;
24
25    /**
26     * Showing errors
27     * @param {string} error - Message of error
28    */
29    this.errorShow = function(error) {
30        printMessage('error', error);
31        self.book_color(true);
32    };
33
34    /**
35     * Selection days on calendar
36     * @param {string} o_id - Mktime of date
37     */
38    this.xSelect = function(o_id) {
39        if (bookingConfigs['deny'] === 0) {
40            flUtil.loadStyle(rlConfig.tpl_base + 'components/popup/popup.css');
41            flUtil.loadScript(rlConfig.tpl_base + 'components/popup/_popup.js', function() {
42                let $loginForm = $('#login_modal_source > *').clone();
43                $loginForm.find('.caption_padding').hide();
44
45                $('body').popup({
46                    click  : false,
47                    content: $loginForm,
48                    caption: lang.booking_login_popup_title,
49                    width  : 300,
50                    onShow : function ($popup) {
51                        // Prevent closing the popup by click on label with checkbox
52                        if ($popup.find('.remember-me')) {
53                            $popup.find('input#css_INPUT_1').attr('id', 'css_INPUT_99999');
54                            $popup.find('label[for="css_INPUT_1"]').attr('for', 'css_INPUT_99999');
55                        }
56                    }
57                });
58            });
59
60            return false;
61        }
62
63        var result, start_book, end_book, mass_bind_checkin;
64
65        if (bookingConfigs.Booking_type === 'time_range') {
66            // first selection
67            if (!bookingConfigs['s_id']) {
68                if ($(bookingConfigs['day_prefix'] + o_id).hasClass('booked')) {
69                    self.errorShow(lang['booked_day_text']);
70                } else if ($(bookingConfigs['day_prefix'] + o_id).hasClass('closed')) {
71                    self.errorShow(lang['closed_day_text']);
72                } else {
73                    mass_bind_checkin = bookingConfigs.bind_checkin !== ''
74                    ? bookingConfigs['bind_checkin'].split(',')
75                    : false;
76
77                    if (mass_bind_checkin && !in_array(self.getDateByTimeZone(o_id, 'ddd'), mass_bind_checkin)) {
78                        self.errorShow(lang.check_in_only_text.replace('{days}', bookingConfigs.bindCheckinLocale));
79                    } else {
80                        bookingConfigs.s_id     = o_id;
81                        start_book              = self.getDateByTimeZone(o_id);
82                        bookingConfigs.db_start = Number(o_id);
83                        self.bUpdate(o_id);
84
85                        if (rlPageInfo.controller === 'booking_requests' && bookingConfigs.request_id) {
86                            bookingConfigs.start_date_value = start_book;
87                            self.stepsHandler('update_period', 'update');
88                        } else {
89                            self.stepsHandler(
90                                'details',
91                                'show',
92                                {
93                                    start_book: start_book,
94                                    total_days: 1,
95                                }
96                            );
97                        }
98
99                        self.updateEscortAvailabilityData();
100
101                        // Reset fields in booking details
102                        var $type_obj = $('[name="escort_rates"]');
103                        $type_obj.find('option:selected').removeAttr('selected');
104                        $type_obj.find('option[value="-1"]').attr('selected', 'selected');
105                        $type_obj.trigger('change');
106
107                        var $time_obj = $('[name="f[booking_check_out]"]');
108                        self.selectFirstActiveOption($time_obj);
109                        $time_obj.addClass('disabled').attr('disabled', 'disabled');
110
111                        var $duration_obj = $('[name="escort_duration"]');
112                        self.selectFirstActiveOption($duration_obj);
113                        $duration_obj.addClass('disabled').attr('disabled', 'disabled');
114                        // Reset escort fields in booking details end
115
116                        // Update date in booking details
117                        var new_date = self.getDateByTimeZone(bookingConfigs['s_id']);
118
119                        var html = '<a id="change_date_button" href="javascript:void(0)">';
120                        html += lang['booking_button_select_date'] + '</a>';
121
122                        $('div.field.start_date').html('<b>' + new_date + '</b>' + html);
123
124                        $('#change_date_button').click(function(){
125                            self.escortChangeDate();
126                        });
127
128                        $('#modal_block div.inner > div.close').trigger('click');
129                    }
130                }
131            } else {
132                bookingConfigs['s_id'] = 0;
133
134                if (rlPageInfo['controller'] !== 'booking_order') {
135                    $('#booking_message_obj').fadeOut('fast');
136                }
137
138                self.book_color(true);
139            }
140        } else {
141            // period selected
142            if (bookingConfigs['first'] === 1) {
143                if (o_id === bookingConfigs['s_id']) {
144                    self.book_color(true);
145                    bookingConfigs['first'] = 0;
146                    return;
147                }
148
149                var mass_bind_checkout = bookingConfigs['bind_checkout'] !== ''
150                ? bookingConfigs['bind_checkout'].split(',')
151                : false;
152
153                if (mass_bind_checkout
154                    && ((!in_array(self.getDateByTimeZone(o_id, 'ddd'), mass_bind_checkout) && o_id > bookingConfigs['s_id'])
155                        || (!in_array(self.getDateByTimeZone(bookingConfigs['s_id'], 'ddd'), mass_bind_checkout) && o_id < bookingConfigs['s_id']))
156                ) {
157                    self.errorShow(lang.check_out_only_text.replace('{days}', bookingConfigs.bindCheckoutLocale));
158                } else {
159                    let daysInPeriod = self.getCountDaysBetween2Timestamps(o_id, bookingConfigs.s_id);
160
161                    if (daysInPeriod < bookingConfigs.min_bl && bookingConfigs.min_bl > 0) {
162                        self.errorShow(
163                            lang.min_bl_text.replace(
164                                '{nights}',
165                                '<span class="daySize">' + parseInt(bookingConfigs.min_bl) + '</span>'
166                            )
167                        )
168                    } else if (daysInPeriod > bookingConfigs['max_bl'] && bookingConfigs['max_bl'] > 0) {
169                        self.errorShow(
170                            lang.max_bl_text.replace(
171                                '{nights}',
172                                '<span class="daySize">' + parseInt(bookingConfigs.max_bl) + '</span>'
173                            )
174                        )
175                    } else {
176                        if (bookingConfigs['s_id'] > o_id) {
177                            result     = self.bUpdate(o_id, bookingConfigs['s_id'], '-');
178                            start_book = self.getDateByTimeZone(o_id);
179                            end_book   = self.getDateByTimeZone(bookingConfigs['s_id']);
180
181                            bookingConfigs['db_start'] = o_id;
182                            bookingConfigs['db_end'] = bookingConfigs['s_id'];
183                        } else {
184                            result     = self.bUpdate(o_id, bookingConfigs['s_id'], '+');
185                            start_book = self.getDateByTimeZone(bookingConfigs['s_id']);
186                            end_book   = self.getDateByTimeZone(o_id);
187
188                            bookingConfigs['db_start'] = bookingConfigs['s_id'];
189                            bookingConfigs['db_end'] = o_id;
190                        }
191
192                        if (result === true) {
193                            if (rlPageInfo.controller === 'booking_requests' && bookingConfigs['request_id']) {
194                                bookingConfigs['start_date_value'] = start_book;
195                                bookingConfigs['end_date_value']   = end_book;
196
197                                self.stepsHandler('update_period', 'update');
198                            } else {
199                                self.stepsHandler(
200                                    'details',
201                                    'show',
202                                    {
203                                        start_book : start_book,
204                                        end_book   : end_book,
205                                        total_days : daysInPeriod,
206                                    }
207                                );
208                            }
209                        } else {
210                            self.errorShow(lang['already_booked_text']);
211                        }
212                    }
213                }
214
215                bookingConfigs['first'] = 0;
216            } else {
217                if ($(bookingConfigs['day_prefix'] + o_id).hasClass('booked')) {
218                    self.errorShow(lang['booked_day_text']);
219                } else if ($(bookingConfigs['day_prefix'] + o_id).hasClass('closed')) {
220                    self.errorShow(lang['closed_day_text']);
221                } else {
222                    mass_bind_checkin = bookingConfigs['bind_checkin'] !== ''
223                    ? bookingConfigs['bind_checkin'].split(',')
224                    : false;
225
226                    if (mass_bind_checkin && !in_array(self.getDateByTimeZone(o_id, 'ddd'), mass_bind_checkin)) {
227                        self.errorShow(lang.check_in_only_text.replace('{days}
227', bookingConfigs.bindCheckinLocale));
228                    } else {
229                        if (bookingConfigs['s_id'] > 0) {
230                            bookingConfigs['s_id'] = bookingConfigs['cur_id'];
231                        }
232
233                        self.book_color(true);
234                        self.bUpdate(o_id);
235                        $('#booking_message_obj').fadeOut('fast');
236
237                        if (rlPageInfo.controller === 'booking_order') {
238                            $('.booking_message_clone').hide();
239                        }
240
241                        bookingConfigs['first'] = 1;
242                        bookingConfigs['s_id'] = o_id;
243                        bookingConfigs['cur_id'] = o_id;
244                    }
245                }
246            }
247        }
248    };
249
250    /**
251     * Update colors of days on calendar
252     * @param {int}     o_id
253     * @param {int}     s_id
254     * @param {string}  mod             - Maybe "+" or "-"
255     * @param {boolean} disable_recount - Paint calendar without recount of amount
256     */
257    this.bUpdate = function(o_id, s_id, mod, disable_recount) {
258        if (bookingConfigs.first === 1) {
259            let daysInPeriod = bookingConfigs.Booking_type === 'time_range'
260                ? 1
261                : self.getCountDaysBetween2Timestamps(o_id, s_id);
262
263            let currentDate = s_id;
264            for (let i = 0; i < daysInPeriod; i++) {
265                currentDate = mod === '+'
266                    ? self.addDaysToTimestamp(currentDate)
267                    : self.subtractDaysFromTimestamp(currentDate);
268
269                let $day = $(bookingConfigs.day_prefix + currentDate);
270                if ($day.hasClass('booked') || $day.hasClass('closed') || $day.hasClass('unavailable')) {
271                    return false;
272                }
273
274                bookingConfigs.selected[bookingConfigs.index] = currentDate;
275                bookingConfigs.index++;
276            }
277
278            self.book_color(false, false, mod, disable_recount);
279        } else {
280            if (!disable_recount && !bookingConfigs.in_popup) {
281                $('#booking_message').html('');
282                self.stepsHandler('fields', 'hide');
283            }
284
285            $(bookingConfigs.day_prefix + o_id).addClass(
286                bookingConfigs.Booking_type !== 'time_range' ? 'daySelect start' : 'daySelect'
287            );
288            bookingConfigs.selected[bookingConfigs.index] = o_id;
289            bookingConfigs.index++;
290        }
291
292        return true;
293    };
294
295    /**
296     * Change color of selected day in calendar and count total price of selected range
297     * @param {boolean} erase - Reset of selection
298     * @param {boolean} st
299     * @param {string}  mod
300     * @param {boolean} disable_recount - Paint calendar without recount of amount
301     */
302    this.book_color = function(erase, st, mod, disable_recount) {
303        var iteration = 1;
304        var calc = 0;
305        var bDebug = '';
306        var count_selected_days = bookingConfigs['selected'].length;
307        var count_selected_days_without_last = count_selected_days - 1;
308        var price = 0;
309
310        if (bookingConfigs['selected']) {
311            for (var id = 0; id < count_selected_days; id++) {
312                var $dayContainer = $(bookingConfigs['day_prefix'] + bookingConfigs['selected'][id]);
313                $dayContainer[erase === true ? 'removeClass' : 'addClass']('daySelect');
314
315                // add special classes for first and last days of selection
316                if (erase) {
317                    $dayContainer.removeClass('start end');
318                } else {
319                    if (id === 0) {
320                        $dayContainer.addClass(
321                            bookingConfigs['selected'][id + 1] > bookingConfigs['selected'][id] ? 'start' : 'end'
322                        );
323                    } else if (id === count_selected_days - 1) {
324                        if (bookingConfigs['selected'][id + 1]) {
325                            $dayContainer.addClass(
326                                bookingConfigs['selected'][id + 1] > bookingConfigs['selected'][id] ? 'end' : 'start'
327                            );
328                        } else {
329                            $dayContainer.addClass(
330                                bookingConfigs['selected'][id - 1] < bookingConfigs['selected'][id] ? 'end' : 'start'
331                            );
332                        }
333                    }
334                }
335
336                if (bookingConfigs['first'] === 1 && !disable_recount) {
337                    // listing rate range
338                    for (var idR in bookingConfigs['usRange']) {
339                        price = parseFloat(bookingConfigs['usRange'][idR][2].split('|')[0]);
340
341                        if (bookingConfigs['fixed_range'] === 1) {
342                            if (mod === '+') {
343                                if (iteration !== count_selected_days) {
344                                    if (bookingConfigs['selected'][0] >= bookingConfigs['usRange'][idR][0]
345                                        && bookingConfigs['selected'][0] <= bookingConfigs['usRange'][idR][1]
346                                    ) {
347                                        bDebug += parseFloat(bookingConfigs['usRange'][idR][2].split('|')[0] ) + ' + ';
348                                        bookingConfigs['total_cost'] += price;
349                                    }
350                                }
351                            } else {
352                                if (iteration !== 1) {
353                                    if (bookingConfigs['selected'][count_selected_days_without_last] >= bookingConfigs['usRange'][idR][0]
354                                        && bookingConfigs['selected'][count_selected_days_without_last] <= bookingConfigs['usRange'][idR][1]
355                                    ) {
356                                        bDebug += price + ' + ';
357                                        bookingConfigs['total_cost'] += price;
358                                    }
359                                }
360                            }
361                        } else {
362                            if (bookingConfigs['selected'][id] >= bookingConfigs['usRange'][idR][0]
363                                && bookingConfigs['selected'][id] <= bookingConfigs['usRange'][idR][1]
364                            ) {
365                                if (mod === '+') {
366                                    if (iteration !== count_selected_days) {
367                                        bDebug += price + ' + ';
368                                        bookingConfigs['total_cost'] += price;
369                                        calc++;
370                                    }
371                                } else {
372                                    if (iteration !== 1) {
373                                        bDebug += price + ' + ';
374                                        bookingConfigs['total_cost'] += price;
375                                        calc++;
376                                    }
377                                }
378                            }
379                        }
380                    }
381                }
382                iteration++;
383            }
384
385            if (bookingConfigs['first'] === 1 && !disable_recount) {
386                if (bookingConfigs['fixed_range'] === 0) {
387                    bDebug += bookingConfigs['booking_debug']
388                    ? '( ' + count_selected_days_without_last + ' - ' + calc + ' ) * ' + bookingConfigs['defPrice']
389                    : '';
390                    bookingConfigs['total_cost'] += (count_selected_days_without_last - calc) * bookingConfigs['defPrice'];
391                } else {
392                    if (bookingConfigs['total_cost'] === 0) {
393                        bDebug += bookingConfigs['booking_debug']
394                        ? count_selected_days_without_last + ' * ' + bookingConfigs['defPrice']
395                        : '';
396                        bookingConfigs['total_cost'] += count_selected_days_without_last * bookingConfigs['defPrice'];
397                    }
398                }
399
400                if (bookingConfigs['booking_debug']) {
401                    console.log(bDebug);
402                }
403                bookingConfigs['total_cost'] = self.str2money(bookingConfigs['total_cost']);
404            }
405
406            if (erase === true) {
407                bookingConfigs['selected'] = [];
408                bookingConfigs['index'] = 0;
409                bookingConfigs['total_cost'] = 0;
410            }
411
412            if (st === true) {
413                if (bookingConfigs['selected'][0] !== '') {
414                    $(bookingConfigs['day_prefix'] + bookingConfigs['selected'][0]).addClass('daySelect');
415                }
416            }
417        }
418    };
419
420    /**
421     * Change price by money format
422     * @param  {string} price - Price
423     * @return {string}       - Formatted price
424     */
425    this.str2money = function(price) {
426        price = parseFloat(price);
427
428        if (!price) {
429            return false;
430        }
431
432        var price_split = price.toFixed(2).split('.');
433        var price_int = self.strrev(price_split[0]);
434        var price_cents = (price_split[1] ? price_split[1] : '') === '00' ? '' : '.' + price_split[1];
435        var len = price_int.length;
436        var val = '';
437
438        for (var i = 0; i <= len; i++) {
439            val += price_int.charAt(i);
440            if (((i + 1) % 3 === 0) && (i + 1 < len)) {
441                val += bookingConfigs['price_delimiter'];
442            }
443        }
444
445        val = self.strrev(val) + price_cents;
446
447        return val;
448    };
449
450    this.strrev = function(string) {
451        string = string + '';
452
453        var grapheme_extend = /(.)([\uDC00-\uDFFF\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065E\u0670\u06D6-\u06DC\u06DE-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0901-\u0903\u093C\u093E-\u094D\u0951-\u0954\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C01-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C82\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D02\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F90-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B6-\u17D3\u17DD\u180B-\u180D\u18A9\u1920-\u192B\u1930-\u193B\u19B0-\u19C0\u19C8\u19C9\u1A17-\u1A1B\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAA\u1C24-\u1C37\u1DC0-\u1DE6\u1DFE\u1DFF\u20D0-\u20F0\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA67C\uA67D\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C4\uA926-\uA92D\uA947-\uA953\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uFB1E\uFE00-\uFE0F\uFE20-\uFE26])/g;
454        string = string.replace(grapheme_extend, '$2$1'); // Temporarily reverse
455
456        return string.split('').reverse().join('');
457    };
458
459    /**
460     * Paint booked and closed days on the calendar
461     */
462    this.paintUserBook = function() {
463        for (id in bookingConfigs.usBook) {
464            var pr             = '';
465            var status_b       = bookingConfigs.usBook[id][0];
466            var st_b           = bookingConfigs.usBook[id][1];
467            var en_b           = bookingConfigs.usBook[id][2];
468            let daysInPeriod   = bookingConfigs.Booking_type === 'time_range'
469                ? 1
470                : self.getCountDaysBetween2Timestamps(en_b, st_b);
471
472            var currentDate   = st_b;
473            var req_id         = Number(bookingConfigs.usBook[id]['3']);
474            var current_req_id = Number(bookingConfigs.request_id);
475            var $dayContainer  = null;
476
477            // don't draw days of current request
478            if (!current_req_id || (current_req_id !== req_id)) {
479                for (let i = 0; i <= daysInPeriod; i++) {
480                    if (i !== 0) {
481                        currentDate = self.addDaysToTimestamp(currentDate);
482                    }
483
484                    pr = status_b === 'process' ? 'pr_' : '';
485                    $dayContainer = $(bookingConfigs.day_prefix + currentDate);
486
487                    if (i === 0) {
488                        if (bookingConfigs.Booking_type === 'time_range') {
489                            if ($dayContainer.hasClass('fully-booked')) {
490                                $dayContainer.addClass('booked').removeAttr('title');
491                            } else if (!$dayContainer.hasClass('partially-booked')) {
492                                $dayContainer.addClass('partially-booked');
493                                $dayContainer.append(
494                                    $('<img>').attr({
495                                        src: rlConfig.plugins_url + 'booking/static/hour.png',
496                                        alt: ''
497                                    })
498                                );
499                            }
500
501                            if (bookingConfigs['usBook'][id]['4'] && rlPageInfo.controller !== 'booking_requests') {
502                                $dayContainer.addClass('own-request');
503                            }
504                        } else {
505                            $dayContainer.addClass(pr + 'checkin');
506
507                            if (bookingConfigs['usBook'][id]['4'] && rlPageInfo.controller !== 'booking_requests') {
508                                $dayContainer.addClass(pr + 'checkin_own');
509                            }
510
511                            if ($dayContainer.hasClass('pr_checkout')) {
512                                $dayContainer.addClass('booked').removeAttr('title');
513                            }
514
515                            if ($dayContainer.hasClass(pr + 'checkout') || $dayContainer.hasClass('checkout')) {
516                                $dayContainer.addClass('booked').removeAttr('title');
517                            }
518                        }
519                    } else if (i === daysInPeriod) {
520                        if (bookingConfigs.Booking_type !== 'time_range') {
521                            $dayContainer.addClass(pr + 'checkout');
522
523                            if (bookingConfigs['usBook'][id]['4'] && rlPageInfo.controller !== 'booking_requests') {
524                                $dayContainer.addClass(pr + 'checkout_own');
525                            }
526
527                            if ($dayContainer.hasClass(pr + 'checkin') || $dayContainer.hasClass('checkin')) {
528                                $dayContainer.addClass('booked').removeAttr('title');
529                            }
530                        }
531                    } else {
532                        $dayContainer.addClass('booked').removeAttr('title');
533                    }
534
535                    if (bookingConfigs['usBook'][id]['4']
536                        && rlPageInfo.controller !== 'booking_requests'
537                        && bookingConfigs.Booking_type !== 'time_range'
538                    ) {
539                        $dayContainer.addClass('own-request');
540                    }
541                }
542            } else {
543                // highlight start date of current request
544                var $startDay = $(bookingConfigs['day_prefix'] + st_b);
545                if (st_b && rlPageInfo.controller === 'booking_requests' && $startDay.hasClass('current-request')) {
546                    $startDay.addClass('start');
547                }
548
549                // highlight end date of current request
550                var $endDay = $(bookingConfigs['day_prefix'] + en_b);
551                if (en_b && rlPageInfo.controller === 'booking_requests' && $endDay.hasClass('current-request')) {
552                    $(bookingConfigs['day_prefix'] + en_b).addClass('end');
553                }
554            }
555        }
556
557        // Fill closed days
558        for (let cd in bookingConfigs.closeRange) {
559            let st_close     = bookingConfigs.closeRange[cd][0];
560            let end_close    = bookingConfigs.closeRange[cd][1];
561            let daysInPeriod = self.getCountDaysBetween2Timestamps(end_close, st_close);
562            let currentDate  = st_close;
563
564            for (let j = 0; j <= daysInPeriod; j++) {
565                if (j !== 0) {
566                    currentDate = self.addDaysToTimestamp(currentDate);
567                }
568
569                $(bookingConfigs.day_prefix + currentDate).removeClass().addClass('closed').removeAttr('title');
570            }
571        }
572    };
573
574    /**
575     * Change dates in calendar | First step
576     * @param {string} mode - Next or previous month
577     */
578    this.changeDates = function(mode) {
579        self.booking_mask('set');
580        self.getDates(bookingConfigs['listing_id'], mode);
581    };
582
583    /**
584     * Adding/Removing mask with "loading" to calendar
585     * @param {string} mode - Detect adding or removing
586     */
587    this.booking_mask = function(mode) {
588        if (mode === 'set') {
589            $('#booking_calendar').addClass('loading');
590        } else if (mode === 'reset') {
591            $('#booking_calendar').removeClass('loading');
592        }
593    };
594
595    /**
596     * Accepting or Rejecting booking request by owner of listing
597     * @param  {string} action     - Some of action
598     * @param  {int}    require_id - ID of request
599     */
600    this.bookingRequestHandler = function(action, require_id) {
601        if (action === 'accept') {
602            $('#accept_btn').val(lang['loading']);
603        } else if (action === 'refuse') {
604            $('#refuse_btn').val(lang['loading']);
605        }
606
607        var message = CKEDITOR.instances.textarea_answer.getData();
608
609        $('#accept_btn,#refuse_btn').addClass('disabled').attr('disabled', true);
610        $('.cancel').fadeOut('fast');
611        $('#refuse_btn').after('<span>' + lang['booking_refuse'] + '</span>');
612
613        // get updated checkIn/checkOut time values if owner changed it
614        var checkin_time = bookingConfigs.Booking_type === 'time_range'
615        ? bookingConfigs.escort_type_service
616        : bookingConfigs.check_in;
617
618        var checkout_time = bookingConfigs.Booking_type === 'time_range'
619        ? bookingConfigs.escort_time_service
620        : bookingConfigs.check_out;
621
622        var request = {
623            mode: 'bookingRequestHandler',
624            item: {
625                ID      : require_id,
626                action  : action,
627                message : message ? message : '',
628                data    : {
629                            db_start       : bookingConfigs.db_start,
630                            db_end         : bookingConfigs.Booking_type === 'time_range'
631                                ? bookingConfigs.escort_duration
632                                : bookingConfigs.db_end,
633                            total_cost     : bookingConfigs.total_cost,
634                            checkin_time   : checkin_time,
635                            checkout_time  : checkout_time,
636                            period_updated : bookingConfigs.period_updated
637                        }
638            },
639            lang: rlLang
640        };
641
642        $.post(
643            rlConfig.ajax_url,
644            request,
645            function(response){
646                if (response.status === 'OK') {
647                    $('#owner_actions').fadeOut('fast');
648                    $('#change_period').fadeOut('slow', function(){ $(this).remove() });
649                    $('.current_dates').removeClass('current_dates');
650
651                    if (action === 'accept') {
652                        $('#owRes').html(lang.booking_accepted);
653                    } else if (action === 'refuse') {
654                        $('#owRes').html(lang.booking_refused);
655                    }
656
657                    if (bookingConfigs.Booking_type === 'time_range') {
658                        $('#change_date_button').addClass('hide');
659
660                        if (bookingConfigs['period_updated']) {
661                            $('div.field.type').html($('[name="escort_rates"] option:selected').text());
662                            $('div.field.time').html($('[name="f[booking_check_out]"] option:selected').text());
663
664                            var duration    = '';
665                            var $duraionObj = $('[name="escort_duration"] option:selected');
666
667                            if (parseInt($duraionObj.val()) > 0) {
668                                duration = $duraionObj.text();
669                                $('div.field.duration').html(duration);
670                            } else {
671                                $('div.field.duration').closest('div.table-cell').addClass('hide');
672                            }
673                        }
674                    }
675
676                    // update calendar
677                    bookingConfigs['request_id'] = null;
678                    self.getDates(bookingConfigs.listing_id);
679
680                    printMessage('notice', response.data);
681                } else {
682                    printMessage('error', response.message);
683                }
684            },
685            'json'
686        );
687    };
688
689    /**
690     * Remove request from page
691     * @param {int}    id       - ID of request
692     * @param {string} redirect - Custom redirect to prev page if page have pagination
693     */
694    this.removeRequest = function(id, redirect) {
695        if (!id) {
696            return false;
697        }
698
699        $(this).val(lang['loading']);
700
701        var request = {mode: 'bookingRemoveRequest', item: id, lang: rlLang};
702
703        $.post(rlConfig.ajax_url, request, function(response){
704            $('#modal_block div.close').trigger('click');
705            if (response.status === 'OK') {
706                $('#item_request_' + id).fadeOut('fast', function() {
707                    $('#item_request_' + id).remove();
708
709                    // hide table if table is empty
710                    var parent = false;
711                    if ($('#area_requests').length || $('#area_booking').length) {
712                        if ($('#area_requests').length && $('#area_requests .list-table > .row').length <= 0) {
713                            parent = $('#area_requests');
714                        }
715                        if ($('#area_booking').length && $('#area_booking .list-table > .row').length <= 0) {
716                            parent = $('#area_booking');
717                        }
718                    }
719
720                    if ($('#listings').length) {
721                        if ($('#listings article.item').length <= 0) {
722                            parent = $('#listings');
723                        }
724                    }
725
726                    if (parent) {
727                        if ($(parent).find('.list-table').length > 0) {
728                            $(parent).find('.list-table').fadeOut('fast');
729                        }
730
731                        // reservation page
732                        if ($('#listings').length > 0) {
733                            $(parent).parent().html('<div class="text-notice">' + lang['booking_no_reservations'] + '</div>');
734
735                            // redirect to previos page
736                            if (redirect) {
737                                window.location.href = prev_page;
738                            }
739                        }
740                        // other pages
741                        else {
742                            $(parent).html('<div class="text-notice">' + lang['booking_no_requests'] + '</div>');
743                        }
744                    }
745                });
746
747                setTimeout(function(){ printMessage('notice', response.data) }, 500);
748            } else {
749                setTimeout(function(){ printMessage('error', response.message) }, 500);
750            }
751        }, 'json');
752    };
753
754    /**
755     * Window with confirmation of removing request
756     * @param {string} redirect - Custom redirect if page have pagination
757     */
758    this.requestRemoveHandler = function(redirect) {
759        $('.remove_request').each(function(){
760            $(this).flModal({
761                caption: lang['warning'],
762                content: lang['booking_remove_notice'],
763                prompt : "booking.removeRequest(" + $(this).attr('id') + ", '" + redirect + "')",
764                width  : 'auto',
765                height : 'auto'
766            });
767        });
768    };
769
770    /**
771     * Adding new rate range to list
772     */
773    this.addRateRange = function() {
774        var lCurrency = $('select[name="f[price][currency]"] option:selected').text();
775        var field = '<div class="row tmp" id="add_rate_' + bookingConfigs['current_field'] + '">';
776        field += '<div data-caption="' + lang['from'] + '">';
777        field += '<input class="w120" type="text" name="b[' + bookingConfigs['current_field'] + '][from]" ';
778        field += 'id="brr_from_' + bookingConfigs['current_field'] + '" value="" autocomplete="off" /></div>';
779        field += '<div data-caption="' + lang['to'] + '">';
780        field += '<input class="w120" type="text" name="b[' + bookingConfigs['current_field'] + '][to]" ';
781        field += 'id="brr_to_' + bookingConfigs['current_field'] + '" value="" autocomplete="off" /></div>';
782        field += '<div data-caption="' + lang['price'] + '">';
783        field += bookingConfigs.system_currency_position === 'before' ? lCurrency + ' ' : '';
784        field += '<input type="text" class="numeric w120 price" name="b[' + bookingConfigs['current_field'] + '][price]" ';
785        field += 'id="price_' + bookingConfigs['current_field'] + '" value="" />';
786        field += bookingConfigs.system_currency_position === 'after' ? ' ' + lCurrency : '';
787        field += '<img class="qtip booking_qtip" alt="" title="' + lang['booking_close_rate_range_notice'];
788        field += '" id="rate_desc_' + bookingConfigs['current_field'] + '" src="';
789        field += bookingConfigs['src_delete_img'] + '" />' + '</div>';
790
791        /* description */
792        field += '<div data-caption="' + lang.description + '" class="rate-ranges-description">';
793        field += '<a href="javascript:void(0)" onclick="booking.addDesc(' + bookingConfigs['current_field'] + ')">';
794        field += lang.description + '</a>';
795        field += '<textarea class="hide" id="save_desc_' + bookingConfigs['current_field'] + '" ';
796        field += 'name="b[' + bookingConfigs['current_field'] +'][desc]" cols="30" rows="2"></textarea>';
797        field += '</div>';
798        /* description end */
799
800        field += '<div data-caption="' + lang['actions'] + '" class="rate-ranges-actions">';
801        field += '<img class="remove" onclick="booking.removeRate(' + bookingConfigs['current_field'] + ')" ';
802        field += 'title="' + lang['delete'] + '" alt="' + lang['delete'] + '" src="';
803        field += bookingConfigs['src_delete_img'] + '" /></div>';
804        field += '</div>';
805
806        $('#booking_rate_ranges_list > div.list-table #rrange_regular').before(field);
807
808        self.rateRangeFieldsHandler(bookingConfigs['current_field']);
809        setTimeout(function(){ bookingConfigs['current_field']++ }, 200);
810    };
811
812    /**
813     * Apply handlers to new rate range fields
814     * @param {int} id
815     */
816    this.rateRangeFieldsHandler = function(id) {
817        $('.numeric').numeric();
818
819        self.qtipInit(true);
820
821        var rate_ranges_selector = '';
822
823        if (id) {
824            rate_ranges_selector = '#brr_from_' + id + ',#brr_to_' + id;
825        } else {
826            for (var i = 1; i < parseInt(bookingConfigs['current_field']); i++) {
827                rate_ranges_selector = rate_ranges_selector
828                ? rate_ranges_selector + ', #brr_from_' + i + ', #brr_to_' + i
829                : '#brr_from_' + i + ', #brr_to_' + i;
830            }
831        }
832
833        if (rate_ranges_selector) {
834            setTimeout(function(){
835                var dp_regional = bookingConfigs['rlLang'] === 'en' ? 'en-GB' : bookingConfigs['rlLang'];
836                var dates = $(rate_ranges_selector)
837                    .datepicker('destroy')
838                    .datepicker({
839                        showOn: 'focus',
840                        dateFormat: 'dd-mm-yy',
841                        minDate: self.getMinRateDate(),
842                        onSelect: function(selectedDate) {
843                            if (this.id.indexOf('from') !== -1) {
844                                var instance = $(this).data("datepicker"),
845                                date = $.datepicker.parseDate(instance.settings.dateFormat || $.datepicker._defaults.dateFormat, selectedDate, instance.settings);
846                                var to_date = date.getDate() + parseInt(bookingConfigs['booking_min_book_day']);
847                                date.setDate(to_date);
848                                dates.not(this).datepicker("option", "minDate", date);
849
850                                var mMonth = date.getMonth() + 1;
851                                var mDay = date.getDate();
852                                mMonth = mMonth < 10 ? '0'+ mMonth : mMonth;
853                                mDay = mDay < 10 ? '0'+ mDay : mDay;
854
855                                dates.not(this).val(mDay + '-' + mMonth + '-' + date.getFullYear());
856                            }
857                        }
858                    })
859                    .datepicker($.datepicker.regional[dp_regional]);
860            }, 100);
861        }
862    };
863
864    /**
865     * Get minimum date for new rate range (by exist ranges)
866     */
867    this.getMinRateDate = function() {
868        var default_date = new Date();
869        var targetTimeOffset = bookingConfigs['bookingTimeOffset'] * 60; // desired time zone
870        default_date.setMinutes(default_date.getMinutes() + default_date.getTimezoneOffset() + targetTimeOffset);
871
872        var exist_min_date = '';
873        var previous_field = bookingConfigs['current_field'] - 1;
874        var min_date = bookingConfigs['current_field'] > 1 && !bookingConfigs['post_rate_ranges']
875        ? ($('[name="b[' + previous_field + '][to]"]').length
876            ? $('[name="b[' + previous_field + '][to]"]').val()
877            : $('#rrange_' + previous_field + ' div[data-caption="to"]').text())
878        : default_date;
879
880        /* find value from exist rate ranges (if new created rate is empty) */
881        if (!min_date) {
882            for (var i = previous_field; i > 0; i--) {
883                if ($('#rrange_' + i + ' div[data-caption="to"]').length
884                    && $('#rrange_' + i + ' div[data-caption="to"]').text()
885                ) {
886                    exist_min_date = $('#rrange_' + i + ' div[data-caption="to"]').text();
887                    break;
888                }
889            }
890
891            /* find last filled value in tmp rate ranges */
892            if (!min_date) {
893                for (var i = previous_field; i > 0; i--) {
894                    if ($('[name="b[' + i + '][to]"]').length && $('[name="b[' + i + '][to]"]').val()) {
895                        min_date = $('[name="b[' + i + '][to]"]').val();
896                        break;
897                    }
898                }
899            }
900        }
901
902        if (exist_min_date || min_date) {
903            min_date = bookingConfigs['current_field'] > 1 && !bookingConfigs['post_rate_ranges']
904            ? new Date(exist_min_date
905                ? exist_min_date.replace(/(\d{2})\-(\d{2})\-(\d{4})/,'$3-$2-$1')
906                : min_date.replace(/(\d{2})\-(\d{2})\-(\d{4})/,'$3-$2-$1'))
907            : min_date;
908
909            /* allow to create new rate from next day only */
910            if (bookingConfigs['current_field'] > 1 ) {
911                min_date.setDate(min_date.getDate() + 1);
912            }
913        }
914
915        return min_date ? min_date : default_date;
916    };
917
918    /**
919     * Deleting rate range row from list
920     * @param {int}  id           - ID of row with rate
921     * @param {bool} rate_details - Detect exist rate or temp
922     */
923    this.removeRate = function(id, rate_details) {
924        if (!id) {
925            return false;
926        }
927
928        if (rate_details) {
929            $(this).val(lang['loading']);
930
931            var request = {
932                mode: 'bookingRemoveRateRange',
933                item: {
934                    rate_id: id,
935                    listing_id: bookingConfigs['listing_id']
936                },
937                lang: typeof rlLang !== 'undefined' ? rlLang : bookingConfigs.rlLang
938            };
939
940            $.post(rlConfig.ajax_url, request, function(response){
941                if (bookingConfigs['isAdmin']) {
942                    self.closeApModal();
943                } else {
944                    $('#modal_block div.close').trigger('click');
945                }
946
947                if (response.status === 'OK') {
948                    $('#rrange_' + id).fadeOut('fast', function(){ $(this).remove() });
949                    bookingConfigs['current_field']--;
950
951                    if (!bookingConfigs['isAdmin']) {
952                        setTimeout(function(){ printMessage('notice', response.data) }, 500);
953                    }
954                } else {
955                    setTimeout(function(){ printMessage('error', response.message) }, 500);
956                }
957            }, 'json');
958        } else {
959            $('#add_rate_' + id).fadeOut('fast', function(){ $(this).remove() });
960            bookingConfigs['current_field']--;
961        }
962    };
963
964    /**
965     * Window with confirmation of removing rate
966     */
967    this.confirmRemoveRateHandler = function() {
968        var caption = lang['warning'];
969        var confirmation_phrase = lang['booking_remove_confirm'];
970
971        $('.remove_rate').each(function(){
972            if (bookingConfigs['isAdmin']) {
973                $(this).flModal({
974                    width: 450,
975                    height: 'auto',
976                    caption: caption,
977                    content: confirmation_phrase + '<div class="prompt admin"><input name="remove_confirmation" onclick="booking.removeRate(' + $(this).attr('id') + ', true)" type="button" value="' + lang['booking_ok'] + '"><a class="close" href="javascript:void(0)" onclick="self.closeApModal()">' + lang['cancel'] + '</a></div>'
978                });
979            } else {
980                $(this).flModal({
981                    caption: caption,
982                    content: confirmation_phrase,
983                    prompt : "booking.removeRate(" + $(this).attr('id') + ", true)",
984                    width  : 'auto',
985                    height : 'auto'
986                });
987            }
988        });
989    };
990
991    /**
992     * Close modal window
993     */
994    this.closeApModal = function() {
995        $('.modal-window > div:first > span:last').trigger('click');
996    };
997
998    /**
999     * Show/Hide section field group
1000     */
1001    this.bookingSectionHandler = function() {
1002        if (bookingConfigs.booking_available) {
1003            if (self.$bookingRadioButton.length > 0) {
1004                /* show for all booking type except Escort (time_ranges) */
1005                if (bookingConfigs.booking_type && bookingConfigs.booking_type !== 'time_range') {
1006                    /* check in field */
1007                    var checkin = '<' + self.rateParentTag + ' id="booking_checkin" class="submit-cell clearfix hide">';
1008                    checkin += '<' + self.rateChildTag + ' class="name">' + lang['booking_checkin'] + '</' + self.rateChildTag + '>';
1009                    checkin += '<'+ self.rateChildTag + ' class="field" id="booking_checkin_obj"></' + self.rateChildTag + '>';
1010                    checkin += '</' + self.rateParentTag + '>';
1011
1012                    self.$bookingSection.after(checkin);
1013                    self.$checkInSection.clone().appendTo('#booking_checkin_obj').fadeIn('slow');
1014
1015                    /* check out field */
1016                    var checkout = '<' + self.rateParentTag + ' id="booking_checkout" class="submit-cell clearfix hide">';
1017                    checkout += '<' + self.rateChildTag + ' class="name">' + lang['booking_checkout'] + '</' + self.rateChildTag + '>';
1018                    checkout += '<' + self.rateChildTag + ' class="field" id="booking_checkout_obj"></' + self.rateChildTag + '>';
1019                    checkout += '</' + self.rateParentTag + '>';
1020
1021                    $('#booking_checkin').after(checkout);
1022                    self.$checkOutSection.clone().appendTo('#booking_checkout_obj').fadeIn('slow');
1023
1024                    /* rate ranges fields */
1025                    var rateRanges = '<' + self.rateParentTag + ' id="booking_rate_ranges" class="submit-cell clearfix hide">';
1026                    rateRanges += '<' + self.rateChildTag + ' class="name">' + lang['booking_rate_range'] + '</' + self.rateChildTag + '>';
1027                    rateRanges += '<' + self.rateChildTag + ' class="field" id="booking_rate_ranges_obj"></' + self.rateChildTag + '>';
1028                    rateRanges += '</' + self.rateParentTag + '>';
1029
1030                    if (bookingConfigs.booking_type && bookingConfigs.booking_type !== 'time_range') {
1031                        $('#booking_checkout').after(rateRanges);
1032                    } else {
1033                        self.$bookingSection.after(rateRanges);
1034                    }
1035
1036                    $('#booking_rate_ranges_list').appendTo('#booking_rate_ranges_obj').fadeIn('slow');
1037
1038                    if ($('[name="f[booking_module]"][value="1"]').is(':checked')) {
1039                        $('#booking_rate_ranges, #booking_checkin, #booking_checkout').fadeIn('normal');
1040                    }
1041                } else if (bookingConfigs.booking_type === 'time_range' && !bookingConfigs.is_escort)  {
1042                    var $availability = $('#availability'), $rates = $('#rates');
1043
1044                    $availability.appendTo(self.$bookingSection.parent());
1045                    $rates.appendTo(self.$bookingSection.parent());
1046
1047                    if (self.$bookingRadioButton.filter(':checked').val() === '1') {
1048                        $availability.removeClass('hide');
1049                        $rates.removeClass('hide');
1050                    }
1051                }
1052
1053                self.$bookingRadioButton.off('click').click(function() {
1054                    if ($(this).is('[value="1"]:checked')) {
1055                        $('#booking_checkin').fadeIn('fast');
1056                        $('#booking_checkout').fadeIn('fast');
1057                        $('#booking_rate_ranges').fadeIn('normal', self.loadingRangesHandler);
1058
1059                        if (bookingConfigs.booking_type === 'time_range' && !bookingConfigs.is_escort)  {
1060                            $availability.removeClass('hide');
1061                            $rates.removeClass('hide');
1062                        }
1063                    } else {
1064                        $('#booking_checkin').fadeOut('normal');
1065                        $('#booking_checkout').fadeOut('normal');
1066                        $('#booking_rate_ranges').fadeOut('normal',
1067                            function(){
1068                                $('#booking_rate_ranges_list > div.list-table div.row').remove();
1069                            }
1070                        );
1071
1072                        if (bookingConfigs.booking_type === 'time_range' && !bookingConfigs.is_escort)  {
1073                            $availability.addClass('hide');
1074                            $rates.addClass('hide');
1075                        }
1076                    }
1077                });
1078
1079                self.$bookingRadioButton.closest('form').on('submit', function(event) {
1080                    if (!self.$bookingRadioButton.is('[value="1"]:checked')) {
1081                        return true;
1082                    }
1083
1084                    var submit       = true,
1085                        emptyFields  = [],
1086                        errorMessage = lang.booking_addEditListingErrorEmptyRanges;
1087
1088                    if (bookingConfigs.booking_type === 'time_range' && !bookingConfigs.is_escort)  {
1089                        var emptyAvailabilityFields = 0, emptyAvailabilityIDs = [];
1090                        $availability.find('select').each(function () {
1091                            if ($(this).val() === '0') {
1092                                emptyAvailabilityFields++;
1093                                emptyAvailabilityIDs.push('#' + $(this).attr('id'))
1094                            }
1095                        });
1096
1097                        if (emptyAvailabilityFields === 14) {
1098                            submit      = false;
1099                            emptyFields = emptyAvailabilityIDs;
1100                            errorMessage = lang.booking_emptyHourlyRanges;
1101                        } else if (emptyAvailabilityFields > 0) {
1102                            let fromAvailability, toAvailability, i;
1103                            emptyAvailabilityIDs = [];
1104
1105                            for (i = 1; i <= 7; i++) {
1106                                fromAvailability = $('#hourly-availability-from-' + i).val();
1107                                toAvailability   = $('#hourly-availability-to-' + i).val();
1108
1109                                if (fromAvailability !== '0' && toAvailability === '0') {
1110                                    emptyAvailabilityIDs.push('#hourly-availability-to-' + i);
1111                                } else if (fromAvailability === '0' && toAvailability !== '0') {
1112                                    emptyAvailabilityIDs.push('#hourly-availability-from-' + i);
1113                                }
1114                            }
1115
1116                            if (emptyAvailabilityIDs.length > 0) {
1117                                submit       = false;
1118                                emptyFields  = emptyAvailabilityIDs;
1119                                errorMessage = lang.booking_emptyHourlyRanges;
1120                            }
1121                        }
1122
1123                        let emptyRatesIDs = [];
1124                        $('.rates-field-container .rates').each(function () {
1125                            $(this).find('input').each(function () {
1126                                if ($(this).val() === '') {
1127                                    emptyRatesIDs.push('#' + $(this).attr('id'));
1128                                }
1129                            });
1130
1131                            if (!$(this).find('select.hourly-rates-duration').val()) {
1132                                emptyRatesIDs.push('#' + $(this).find('select.hourly-rates-duration').attr('id'));
1133                            }
1134                        });
1135
1136                        if (emptyRatesIDs.length > 0) {
1137                            submit       = false;
1138                            emptyFields  = emptyFields.concat(emptyRatesIDs);
1139                            errorMessage = lang.booking_emptyHourlyRanges;
1140                        }
1141                    } else {
1142                        $('[id^=add_rate_] input').each(function() {
1143                            if ($(this).val() === '') {
1144                                emptyFields.push('#' + $(this).attr('id'));
1145                                submit = false;
1146                            }
1147                        });
1148                    }
1149
1150                    if (!submit) {
1151                        event.preventDefault();
1152                        let $submitButton = self.$bookingRadioButton.closest('form').find('[type="submit"]');
1153                        if ($submitButton.prop('disabled')) {
1154                            $submitButton.val(lang.add_listing).prop('disabled', false).removeClass('disabled');
1155                        }
1156
1157                        printMessage('error', errorMessage, emptyFields);
1158                        flynax.slideTo(bookingConfigs.isAdmin ? '#system_message' : '#fs_booking_rates');
1159                        self.rateRangeFieldsHandler();
1160
1161                        // Restore phrase in button and removing "disabled" class
1162                        if (typeof manageListing === 'object' && typeof manageListing.enableButton === 'function') {
1163                            manageListing.enableButton();
1164                        }
1165                    }
1166
1167                    return submit;
1168                });
1169            }
1170
1171            self.recountRegularPrice(true);
1172            self.confirmRemoveRateHandler();
1173            self.qtipInit();
1174
1175            $('.add_rate_range').click(function(){
1176                self.addRateRange();
1177            });
1178
1179            if (bookingConfigs['post_rate_ranges']) {
1180                self.rateRangeFieldsHandler();
1181            }
1182        } else {
1183            $('div#fs_booking_rates').remove();
1184        }
1185    };
1186
1187    /**
1188     * Add new row with data of hourly rate
1189     * @since 3.2.0
1190     */
1191    this.addNewHourlyRate = function () {
1192        var $timeSelector = $('<select>', {
1193                name : 'f[booking_rates][' + self.rateIndex + '][time]',
1194                id   : 'hourly-rates-duration-' + self.rateIndex,
1195                class: 'hourly-rates-duration',
1196            }),
1197            timeValue     = '',
1198            hoursValue    = '';
1199
1200        $timeSelector.append($('<option>').val('').text(lang.booking_rate_selector));
1201
1202        for (i = 1; i <= 24; i++) {
1203            if (i > 1) {
1204                hoursValue = i % 2 ? i / 2 : i / 2;
1205                timeValue  = i % 2 ? parseInt(hoursValue) + ':30' : hoursValue + ':00';
1206
1207                if (hoursValue <= 9.5) {
1208                    timeValue = '0' + timeValue;
1209                }
1210            } else {
1211                hoursValue = 0.5;
1212                timeValue = '00:30';
1213            }
1214
1215            $timeSelector.append(
1216                $('<option>').val(hoursValue).text(timeValue)
1217            )
1218        }
1219
1220        var $currency = $('<select>').attr({
1221            name: 'f[booking_rates][' + self.rateIndex + '][currency]',
1222            id  : 'hourly-rate-currency-' + self.rateIndex,
1223        });
1224
1225        bookingConfigs.currencies.forEach(function (currency) {
1226            $currency.append(
1227                $('<option>').val(currency.key).text(currency.value)
1228            )
1229        });
1230
1231        $('.rates-field-container .rates:last').after(
1232            $('<div>', {class: 'rates'}).append(
1233                $('<input>').attr({
1234                    type       : 'text',
1235                    name       : 'f[booking_rates][' + self.rateIndex + '][title]',
1236                    id         : 'hourly-rates-name-' + self.rateIndex,
1237                    placeholder: lang.booking_rate_title
1238                }),
1239                $timeSelector,
1240                $('<input>').attr({
1241                    type       : 'text',
1242                    name       : 'f[booking_rates][' + self.rateIndex + '][price]',
1243                    id         : 'hourly-rates-price-' + self.rateIndex,
1244                    placeholder: lang.price,
1245                    class      : 'numeric w120',
1246                }),
1247                $currency,
1248                (!bookingConfigs.bookingRepeatedly
1249                    ? $('<select>').attr({
1250                        name: 'f[booking_rates][' + self.rateIndex + '][type]',
1251                        id  : 'hourly-rate-type-' + self.rateIndex,
1252                       }).append(
1253                           $('<option>').attr({value: 'single'}).text(lang.booking_single_rate),
1254                            $('<option>').attr({value: 'multi', selected: true}).text(lang.booking_multi_rate),
1255                        )
1256                    : null
1257                ),
1258                $('<img>').addClass('remove remove-hourly-rate').attr({
1259                    'title': lang.delete,
1260                    'alt'  : lang.delete,
1261                    'src'  : bookingConfigs.src_delete_img,
1262                    'id'   : 'hourly-rate-' + self.rateIndex
1263                }).click(function () {
1264                    $(this).closest('.rates').fadeOut('fast', function(){
1265                        $(this).remove();
1266                        self.rateIndex--;
1267                    });
1268                })
1269            )
1270        );
1271
1272        self.rateIndex++;
1273    };
1274
1275    /**
1276     * Load list with rate ranges to page (for ex. after desc editing)
1277     */
1278    this.loadingRangesHandler = function() {
1279        $('#booking_rate_ranges_list > div.list-table div.row').remove();
1280        $('#booking_rate_ranges_list > div.list-table').append('<div id="loading_ranges">' + lang['loading'] + '</div>');
1281
1282        if (bookingConfigs.rlController === 'edit_listing') {
1283            var request = {mode: 'bookingGetRateRanges', item: bookingConfigs['listing_id'], lang: rlLang};
1284
1285            $.post(rlConfig.ajax_url, request, function(response){
1286                if (response.status === 'OK') {
1287                    $('#loading_ranges').remove();
1288
1289                    var rate_ranges = response.data.rate_ranges;
1290                    var regular_price = response.data.regular_price;
1291
1292                    if (rate_ranges.length > 0)  {
1293                        for (var i = 0; i < rate_ranges.length; i++) {
1294                            var item = rate_ranges[i];
1295                            var rate_id = item.ID ? parseInt(item.ID) : 0;
1296
1297                            bookingConfigs['current_field'] = rate_id > parseInt(bookingConfigs['current_field'])
1298                            ? rate_id
1299                            : bookingConfigs['current_field'];
1300
1301                            $('#booking_rate_ranges_list > div.list-table').append(
1302                                self.buildRateFieldHtml(rate_id, item.From, item.To, item.Price, item.Desc)
1303                            );
1304                        }
1305
1306                        self.confirmRemoveRateHandler();
1307                    }
1308
1309                    if (regular_price) {
1310                        var regular_price_desc_html = self.buildRateFieldHtml(
1311                            'regular',
1312                            lang['booking_rate_price_per_day'],
1313                            '',
1314                            regular_price.value,
1315                            regular_price.desc
1316                        );
1317
1318                        $('#booking_rate_ranges_list > div.list-table').append(regular_price_desc_html);
1319                    }
1320
1321                    self.qtipInit();
1322                    self.recountRegularPrice();
1323                    $('#loading_ranges').remove();
1324                    $('#add_ranges_table').fadeIn('slow');
1325                } else {
1326                    printMessage('error', response.message);
1327                }
1328            }, 'json');
1329        } else {
1330            var regular_price_desc_html = self.buildRateFieldHtml(
1331                'regular',
1332                lang['booking_rate_price_per_day'],
1333                '',
1334                '',
1335                '',
1336                true
1337            );
1338            $('#booking_rate_ranges_list > div.list-table').append(regular_price_desc_html);
1339
1340            self.qtipInit();
1341            self.recountRegularPrice();
1342
1343            $('#loading_ranges').remove();
1344            $('#add_ranges_table').fadeIn('normal');
1345        }
1346    };
1347
1348    /**
1349     * Generate html code for row with data of rate range
1350     * @param  {int}    id          - ID of rate range
1351     * @param  {string} from        - From date
1352     * @param  {string} to          - To date
1353     * @param  {int}    price       - Price of rate range
1354     * @param  {string} desc        - Description of rate range
1355     * @param  {bool}   add_listing - Detect add listing page
1356     * @return {string}             - Html code of rate range
1357     */
1358    this.buildRateFieldHtml = function(id, from, to, price, desc, add_listing) {
1359        if (!id) {
1360            return false;
1361        }
1362
1363        var lCurrency = $('select[name="f[price][currency]"] option:selected').text();
1364
1365        var result = '<div class="row" id="rrange_' + id + '">';
1366        result += '<div data-caption="' + lang.from + '">' + from +'</div>';
1367        result += '<div data-caption="' + lang.to + '">' + to + '</div>';
1368        result += '<div data-caption="' + lang.price + '">';
1369
1370        /* price */
1371        result += price && parseFloat(price) !== 0 && price !== lang.not_available
1372        ? (bookingConfigs.system_currency_position === 'before'
1373            ? lCurrency + ' ' + price
1374            : price + ' ' + lCurrency)
1375        : (parseFloat(price) === 0 ? lang['booking_close_days'] : lang['not_available']);
1376        /* price end */
1377
1378        // add qtip with description of missing standard rate
1379        if (id === 'regular' && price === lang.not_available) {
1380            result += ' <img class="qtip booking_qtip" alt="" title="' + lang['booking_empty_regular'] + '" ';
1381            result += 'src="' + bookingConfigs['src_delete_img'] + '" />';
1382        }
1383
1384        result += '</div>';
1385
1386        /* description */
1387        desc = desc ? desc : '';
1388        result += '<div data-caption="' + lang.description + '">';
1389
1390        if (add_listing) {
1391            result += '<a href="javascript:void(0)" onclick="booking.addDesc()">' + lang.description + '</a>';
1392        } else {
1393            var qtip_e = '<br><hr><a class=\'booking_edit_desc\' href=\'javascript:void(0)\' ';
1394            qtip_e += 'onclick=\'booking.editDesc(' + (id !== 'regular' ? id : '') + ')\'>' + lang.edit + '</a>';
1395
1396            result += '<img class="qtip booking_qtip" alt="" title="' + (desc ? desc : lang['not_available']);
1397            result += ' ' + qtip_e + '" id="desc_ico_' + id + '" src="';
1398            result += bookingConfigs['src_delete_img'] + '" />';
1399        }
1400
1401        result += '<textarea class="hide" id="save_desc_' + id + '" name="b[' + id +'][desc] cols="30" rows="2">';
1402        result += desc + '</textarea>';
1403        result += '</div>';
1404        /* description end */
1405
1406        result += '<div data-caption="' + lang['actions'] + '">';
1407
1408        if (id === 'regular') {
1409            result += lang['not_available'];
1410        } else {
1411            result += '<img class="remove remove_rate" id=' + id;
1412            result += ' title="' + lang['delete'] + '" alt="' + lang['delete'] + '"';
1413            result += ' src="' + bookingConfigs['src_delete_img'] + '" />';
1414        }
1415
1416        result += '</div>';
1417        result += '</div>';
1418
1419        return result;
1420    };
1421
1422    /**
1423     * Custom qTip initialization with delay
1424     * @param {bool} second_call - Add qtip's for new fields only
1425     */
1426    this.qtipInit = function(second_call) {
1427        var qtip_style_admin =  {
1428            width: 'auto',
1429            background: '#858585',
1430            color: 'white',
1431            border: {
1432                width: 7,
1433                radius: 5,
1434                color: '#858585'
1435            },
1436            tip: 'bottomLeft'
1437        };
1438
1439        // first call in document ready
1440        if (!second_call) {
1441            setTimeout(
1442                function(){
1443                    $('.booking_qtip').addClass('qtip');
1444                    $('.qtip.booking_qtip').each(function(){
1445                        if ($(this).attr('title')) {
1446                            $(this).qtip({
1447                                content: $(this).attr('title'),
1448                                show: 'mouseover',
1449                                hide: {
1450                                    fixed: true,
1451                                    delay: 500
1452                                },
1453                                position: {
1454                                    corner: {
1455                                        target: 'topRight',
1456                                        tooltip: 'bottomLeft'
1457                                    }
1458                                },
1459                                style: bookingConfigs['isAdmin'] ? qtip_style_admin : qtip_style
1460                            }).attr('title', '');
1461                        }
1462                    });
1463                },
1464                100
1465            );
1466        } else {
1467            if (bookingConfigs['isAdmin']) {
1468                // second call for new images with qtip (in admin)
1469                $('.qtip.booking_qtip').each(function(){
1470                    if ($(this).attr('title')) {
1471                        $(this).qtip({
1472                            width: 'auto',
1473                            content: $(this).attr('title'),
1474                            show: 'mouseover',
1475                            hide: 'mouseout',
1476                            position: {
1477                                corner: {
1478                                    target: 'topRight',
1479                                    tooltip: 'bottomLeft'
1480                                }
1481                            },
1482                            style: {
1483                                width: 'auto',
1484                                background: '#858585',
1485                                color: 'white',
1486                                border: {
1487                                    width: 7,
1488                                    radius: 5,
1489                                    color: '#858585'
1490                                },
1491                                tip: 'bottomLeft'
1492                            }
1493                        }).attr('title', '');
1494                    }
1495                });
1496            } else {
1497                // second call for new images with qtip (in frontend)
1498                $('.qtip.booking_qtip').each(function(){
1499                    var target = 'topRight';
1500                    var tooltip = 'bottomLeft';
1501
1502                    if ($(this).hasClass('middle-bottom')) {
1503                        target = 'topMiddle';
1504                        tooltip = 'bottomMiddle';
1505
1506                        var tmp_style = jQuery.extend({}, qtip_style);
1507                        tmp_style.tip = 'bottomMiddle';
1508                    }
1509
1510                    if ($(this).attr('title')) {
1511                        $(this).qtip({
1512                            content: $(this).attr('title'),
1513                            show: 'mouseover',
1514                            hide: 'mouseout',
1515                            position: {
1516                                corner: {
1517                                    target: target,
1518                                    tooltip: tooltip
1519                                }
1520                            },
1521                            style: $(this).hasClass('middle-bottom') ? tmp_style : qtip_style
1522                        }).attr('title', '');
1523                    }
1524                });
1525            }
1526        }
1527    };
1528
1529    /**
1530     * Adding description for new rate range
1531     * @param {int} id - ID of rate range
1532     */
1533    this.addDesc = function(id) {
1534        if (!id) {
1535            id = 'regular';
1536        }
1537
1538        var caption = lang['manage'];
1539        var html_content = $('#manage_item_dom').html();
1540
1541        if (bookingConfigs['isAdmin']) {
1542            $(document).flModal({
1543                width: 450,
1544                height: 'auto',
1545                caption: caption,
1546                onReady: function(){
1547                    $('#manage_item_dom').html('');
1548                    self.copyDescToTable(id)
1549                },
1550                content: html_content,
1551                onClose: function(){
1552                    $('#manage_item_dom').html(html_content)
1553                }
1554            });
1555        } else {
1556            $(document).flModal({
1557                click: false,
1558                source: '#manage_item_dom',
1559                caption: lang['manage'],
1560                width: 450,
1561                height: 'auto',
1562                ready: function(){
1563                    self.copyDescToTable(id)
1564                }
1565            });
1566        }
1567    };
1568
1569    /**
1570     * Copy description from temp container to ranges table
1571     * @param {int} id - ID of rate range
1572     */
1573    this.copyDescToTable = function(id) {
1574        if (!id) {
1575            return false;
1576        }
1577
1578        var value = $('#save_desc_' + id).val();
1579
1580        if (value != lang['not_available']) {
1581            $('input[name=item-desc]').val(value);
1582        }
1583
1584        $('input[name=item-desc-save]').click(function(){
1585            $('#save_desc_' + id).val($('input[name=item-desc]').val());
1586
1587            if (bookingConfigs['isAdmin']) {
1588                self.closeApModal();
1589            } else {
1590                $('#modal_block div.close').trigger('click');
1591            }
1592        });
1593    };
1594
1595    /**
1596     * Edit description of Rate Range
1597     * @param {string} id - ID of rate range or Regular
1598     */
1599    this.editDesc = function(id) {
1600        var caption = lang['manage'];
1601        var html_content = $('#manage_item_dom').html();
1602
1603        if (bookingConfigs['isAdmin']) {
1604            $(document).flModal({
1605                width: 450,
1606                height: 'auto',
1607                caption: caption,
1608                onReady: function(){
1609                    $('#manage_item_dom').html('');
1610                    self.sendDescContentFromModal(id)
1611                },
1612                content: html_content,
1613                onClose: function(){
1614                    $('#manage_item_dom').html(html_content)
1615                }
1616            });
1617        } else {
1618            $(document).flModal({
1619                click: false,
1620                source: '#manage_item_dom',
1621                caption: caption,
1622                width: 450,
1623                height: 'auto',
1624                ready: function(){
1625                    self.sendDescContentFromModal(id)
1626                }
1627            });
1628        }
1629    };
1630
1631    /**
1632     * Send description of rate via ajax request
1633     * @param {string} id - ID of rate range or Regular
1634     */
1635    this.sendDescContentFromModal = function(id) {
1636        if (!id) {
1637            id = 'regular';
1638        }
1639
1640        var old_desc = $('#save_desc_' + id).val();
1641
1642        $('[name="item-desc"]').val(old_desc !== lang.not_available ? old_desc : '');
1643        $('[name="item-desc-save"]').click(function(){
1644            var new_desc = $('[name=item-desc]').val();
1645
1646            if (new_desc !== lang.not_available && old_desc !== new_desc) {
1647                if (bookingConfigs.rlController === 'edit_listing') {
1648                    $(this).val(lang['loading']).addClass('disabled').attr('disabled', true);
1649
1650                    var request = {
1651                        mode: 'bookingUpdateRateDesc',
1652                        item: {rate_id: id, listing_id: bookingConfigs['listing_id']},
1653                        desc: JSON.stringify(new_desc),
1654                        lang: rlLang
1655                    };
1656
1657                    $.post(rlConfig.ajax_url, request, function(response){
1658                        if (bookingConfigs['isAdmin']) {
1659                            self.closeApModal();
1660                        } else {
1661                            $('#modal_block div.close').trigger('click');
1662                        }
1663
1664                        if (response.status === 'OK') {
1665                            $('#save_desc_' + id).val(new_desc);
1666
1667                            // update rows with rate ranges
1668                            self.loadingRangesHandler();
1669
1670                            if (!bookingConfigs['isAdmin']) {
1671                                setTimeout(function(){ printMessage('notice', response.data) }, 500);
1672                            }
1673                        } else {
1674                            setTimeout(function(){ printMessage('error', response.message) }, 500);
1675                        }
1676                    }, 'json');
1677                } else {
1678                    if (bookingConfigs['isAdmin']) {
1679                        self.closeApModal();
1680                    } else {
1681                        $('#modal_block div.close').trigger('click');
1682                    }
1683                }
1684            } else {
1685                if (bookingConfigs['isAdmin']) {
1686                    self.closeApModal();
1687                } else {
1688                    $('#modal_block div.close').trigger('click');
1689                }
1690            }
1691        });
1692    };
1693
1694    /**
1695     * Get dates for calendar
1696     *
1697     * @param {int}    listing_id - ID of listing
1698     * @param {string} param_mode - Button for next/previous month
1699     * @param {bool}   update     - Update calendar without current request
1700     */
1701    this.getDates = function(listing_id, param_mode, update) {
1702        if (!listing_id) {
1703            return false;
1704        }
1705
1706        self.loadExternalScripts(function() {
1707            flUtil.ajax({
1708                mode: 'bookingGetDates',
1709                item: {
1710                    listing_id: listing_id,
1711                    mode      : param_mode,
1712                    request_id: bookingConfigs.request_id ? bookingConfigs.request_id : null
1713                }
1714            },
1715            function(response) {
1716                bookingConfigs.usBook     = [];
1717                bookingConfigs.usRange    = [];
1718                bookingConfigs.closeRange = [];
1719
1720                if (response.status === 'OK') {
1721                    let weekDays = {
1722                        1: lang.booking_monday,
1723                        2: lang.booking_tuesday,
1724                        3: lang.booking_wednesday,
1725                        4: lang.booking_thursday,
1726                        5: lang.booking_friday,
1727                        6: lang.booking_saturday,
1728                        7: lang.booking_sunday,
1729                    };
1730
1731                    // assign already booked days
1732                    if (response.data.userBook) {
1733                        for (var i = response.data.userBook.length - 1; i >= 0; i--) {
1734                            bookingConfigs.usBook[i] = [];
1735                            bookingConfigs.usBook[i][0] = response.data.userBook[i].Status;
1736                            bookingConfigs.usBook[i][1] = response.data.userBook[i].From;
1737                            bookingConfigs.usBook[i][2] = response.data.userBook[i].To;
1738                            bookingConfigs.usBook[i][3] = response.data.userBook[i].ID;
1739                            bookingConfigs.usBook[i][4] = response.data.userBook[i].Own_request;
1740                            bookingConfigs.usBook[i][5] = response.data.userBook[i].Checkout;
1741
1742                            if (bookingConfigs.Booking_type === 'time_range') {
1743                                bookingConfigs.usBook[i][6] = response.data.userBook[i].Duration;
1744                                bookingConfigs.usBook[i][7] = response.data.userBook[i].Type;
1745                                bookingConfigs.usBook[i][8] = response.data.userBook[i].RateIndex;
1746                            }
1747                        }
1748                    }
1749
1750                    // assign rate ranges
1751                    if (response.data.usRange) {
1752                        for (var i = response.data.usRange.length - 1; i >= 0; i--) {
1753                            bookingConfigs.usRange[i] = [];
1754                            bookingConfigs.usRange[i][0] = response.data.usRange[i]['From'];
1755                            bookingConfigs.usRange[i][1] = response.data.usRange[i]['To'];
1756                            bookingConfigs.usRange[i][2] = response.data.usRange[i]['Price'];
1757                        }
1758                    }
1759
1760                    // assign closed days
1761                    if (response.data.closeRange) {
1762                        for (var i = response.data.closeRange.length - 1; i >= 0; i--) {
1763                            bookingConfigs.closeRange[i] = [];
1764                            bookingConfigs.closeRange[i][0] = response.data.closeRange[i]['From'];
1765                            bookingConfigs.closeRange[i][1] = response.data.closeRange[i]['To'];
1766                        }
1767                    }
1768
1769                    // assign binding days
1770                    if (response.data.bindingDays
1771                        && (response.data.bindingDays.checkin || response.data.bindingDays.checkout)
1772                    ) {
1773                        bookingConfigs['bind_checkin']  = response.data.bindingDays.checkin
1774                        ? response.data.bindingDays.checkin
1775                        : '';
1776                        bookingConfigs['bind_checkout'] = response.data.bindingDays.checkout
1777                        ? response.data.bindingDays.checkout
1778                        : '';
1779
1780                        let additionalNotices = '';
1781                        if (bookingConfigs.Booking_type !== 'time_range'
1782                            && (bookingConfigs.bind_checkin || bookingConfigs.bind_checkout)
1783                        ) {
1784                            if (bookingConfigs.bind_checkin && bookingConfigs.bind_checkin.split(',').length < 7) {
1785                                let checkinLocale = self.getBindDaysByLocale(bookingConfigs.bind_checkin, weekDays);
1786                                bookingConfigs.bindCheckinLocale = checkinLocale;
1787
1788                                additionalNotices += '<li class="active">';
1789                                additionalNotices += lang.check_in_only_text.replace('{days}', checkinLocale);
1790                                additionalNotices += '</li>';
1791                            }
1792
1793                            if (bookingConfigs.bind_checkout && bookingConfigs.bind_checkout.split(',').length < 7) {
1794                                let checkoutLocale = self.getBindDaysByLocale(bookingConfigs.bind_checkout, weekDays);
1795                                bookingConfigs.bindCheckoutLocale = checkoutLocale;
1796
1797                                additionalNotices += '<li class="active">';
1798                                additionalNotices += lang.check_out_only_text.replace('{days}', checkoutLocale);
1799                                additionalNotices += '</li>';
1800                            }
1801
1802                            let $notices = $('.booking_notices ul');
1803                            $notices.find('li:not(.static)').remove();
1804                            $notices.append(additionalNotices);
1805                        }
1806                    }
1807
1808                    // draw calendar
1809                    if (response.data.bookingDays) {
1810                        var month = [];
1811                        var first = false;
1812                        var mass_bind_checkin = bookingConfigs.bind_checkin
1813                        ? bookingConfigs.bind_checkin.split(',')
1814                        : false;
1815                        var mass_bind_checkout = bookingConfigs.bind_checkout
1816                        ? bookingConfigs.bind_checkout.split(',')
1817                        : false;
1818
1819                        var calendar_html = '<div id="calendar_map" class="clearfix row';
1820                        calendar_html += bookingConfigs['is_escort'] ? ' escort' : '';
1821                        calendar_html += '">';
1822
1823                        var narrow_templates = {
1824                            'escort_flatty': 'col-sm-12',
1825                            'escort_velvet_flatty': 'col-sm-12',
1826                            'general_modern': 'col-sm-12',
1827                            'auto_modern': 'col-sm-12',
1828                            'general_simple': 'col-sm-12',
1829                            'escort_sun_cocktail': 'col-sm-12 col-md-12 col-lg-6',
1830                        };
1831
1832                        var col_class = (
1833                            typeof narrow_templates[bookingConfigs['site_template']] == 'undefined'
1834                            || rlPageInfo['controller'] !== 'listing_details'
1835                        )
1836                        ? 'col-sm-6'
1837                        : narrow_templates[bookingConfigs['site_template']];
1838
1839                        for (var item in response.data.bookingDays) {
1840                            month = response.data.bookingDays[item];
1841                            first = !first;
1842
1843                            calendar_html += '<div class="' + col_class + '"><div>';
1844                            calendar_html += '<div class="month-name"><b>' + month.Name + ' ' + month.Year + '</b></div>';
1845                            calendar_html += '<table class="month-table" ';
1846                            calendar_html += '>';
1847                            calendar_html += '<tr class="dayName">';
1848
1849                            let dayTitle;
1850                            for (let i = 1; i <= 7; i++) {
1851                                if (bookingConfigs.first_day === 'sunday') {
1852                                    dayTitle = i === 1 ? weekDays[7] : weekDays[i - 1];
1853                                } else {
1854                                    dayTitle = weekDays[i];
1855                                }
1856
1857                                calendar_html += '<td><div>' + dayTitle + '</div></td>';
1858                            }
1859
1860                            calendar_html += '</tr>';
1861                            calendar_html += '<tr>';
1862
1863                            var index = 0;
1864
1865                            // show other days
1866                            for (var item_day in month.Days) {
1867                                var day = month.Days[item_day];
1868                                var book_color = '';
1869                                index++;
1870
1871                                calendar_html += '<td class="calendar_td">';
1872
1873                                if (!day.Miss) {
1874                                    // disable binding days
1875                                    if (day.Color !== 'U' && (mass_bind_checkin || mass_bind_checkout)) {
1876                                        if (bookingConfigs.Booking_type === 'time_range') {
1877                                            if (bookingConfigs.bind_checkin
1878                                                && !in_array(
1879                                                    self.getDateByTimeZone(parseInt(day.mktime), 'ddd'),
1880                                                    mass_bind_checkin
1881                                                )
1882                                            ) {
1883                                                day.Color = 'U';
1884                                            }
1885                                        }
1886                                    }
1887
1888                                    if (day.Color === 'U') {
1889                                        book_color = 'unavailable';
1890                                    } else if (day.Color === 'R') {
1891                                        book_color = 'restriction';
1892                                    } else if (day.Color === 'T') {
1893                                        book_color = 'today';
1894                                    } else if (day.Color === 'A') {
1895                                        var current = false;
1896
1897                                        if (!update
1898                                            && !param_mode
1899                                            && bookingConfigs['usBook']
1900                                            && bookingConfigs['request_id']
1901                                        ) {
1902                                            for (let id in bookingConfigs['usBook']) {
1903                                                let st_b           = bookingConfigs['usBook'][id][1];
1904                                                let en_b           = bookingConfigs['usBook'][id][2];
1905                                                let daysInPeriod   = self.getCountDaysBetween2Timestamps(en_b, st_b);
1906                                                let currentDate    = st_b;
1907                                                let req_id         = Number(bookingConfigs['usBook'][id]['3']);
1908                                                let current_req_id = Number(bookingConfigs['request_id']);
1909
1910                                                // Highlight current request in calendar
1911                                                if (current_req_id && current_req_id === req_id) {
1912                                                    for (i = 0; i <= daysInPeriod; i++) {
1913                                                        if (i !== 0) {
1914                                                            currentDate = self.addDaysToTimestamp(currentDate);
1915                                                        }
1916
1917                                                        current = day.mktime >= st_b && day.mktime <= en_b;
1918                                                    }
1919                                                }
1920                                            }
1921                                        }
1922
1923                                        book_color = current ? 'current-request' : 'available';
1924                                    }
1925
1926                                    if (bookingConfigs.Booking_type === 'time_range' && day.fullyBooked) {
1927                                        book_color += ' fully-booked';
1928                                    }
1929
1930                                    calendar_html += '<div class="date ' + book_color + '"';
1931
1932                                    if (day.mktime) {
1933                                        calendar_html += 'id="day_' + day.mktime + '" ';
1934                                    }
1935
1936                                    if (day.Color !== 'U' && day.Color !== 'R') {
1937                                        calendar_html += ' title="' + lang.booking_start_booking_title + '" ';
1938                                        calendar_html += 'onclick="booking.xSelect(\'' + day.mktime + '\')"';
1939                                    }
1940
1941                                    calendar_html += '>';
1942                                    calendar_html += '<span>';
1943                                    calendar_html += parseInt(day.Day);
1944                                    calendar_html += '</span>';
1945
1946                                    // add price of day
1947                                    if (book_color !== 'unavailable' && book_color !== 'restriction') {
1948                                        var price = 0;
1949
1950                                        if (bookingConfigs['usRange']) {
1951                                            for (var idR in bookingConfigs['usRange']) {
1952                                                price = 0;
1953
1954                                                if (parseInt(bookingConfigs['usRange'][idR][0]) <= day.mktime
1955                                                    && day.mktime <= parseInt(bookingConfigs['usRange'][idR][1])
1956                                                ) {
1957                                                    price = parseFloat(bookingConfigs['usRange'][idR][2].split('|')[0]);
1958                                                    break;
1959                                                }
1960                                            }
1961                                        }
1962
1963                                        price = price ? price : bookingConfigs['defPrice'];
1964
1965                                        if (price) {
1966                                            price = self.adaptNumbersInPrice(price);
1967                                            price = bookingConfigs['system_currency_position'] === 'before'
1968                                            ? bookingConfigs['defCurrency'] + ' ' + price
1969                                            : price + ' ' + bookingConfigs['defCurrency'];
1970
1971                                            calendar_html += '<span class="day-price">' + price + '</span>';
1972                                        }
1973                                    }
1974
1975                                    calendar_html += '</div>';
1976                                    calendar_html += '</td>';
1977                                }
1978
1979                                if (index  % 7 === 0 && index !== month.Days.length) {
1980                                    calendar_html += '</tr><tr>';
1981                                }
1982                            }
1983
1984                            calendar_html += '</tr>';
1985                            calendar_html += '</table>';
1986                            calendar_html += '</div></div>';
1987                        }
1988
1989                        /* Prev/next month navigation */
1990                        calendar_html += '<div class="horizontal">';
1991                        calendar_html += '<div ';
1992                        calendar_html += 'onclick="booking.changeDates(\'-M\')" id="prevRange" ';
1993                        calendar_html += 'class="prev hide" title="' + lang['booking_prev'] + ' ';
1994                        calendar_html += lang['booking_month'] + '"';
1995                        calendar_html += '></div>';
1996                        calendar_html += '<div ';
1997                        calendar_html += 'onclick="booking.changeDates(\'+M\')" class="next" ';
1998                        calendar_html += 'title="' + lang['booking_next'] + ' ' + lang['booking_month'] + '"';
1999                        calendar_html += '></div>';
2000                        calendar_html += '</div>';
2001                        /* Prev/next month navigation end */
2002
2003                        calendar_html += '</div>';
2004
2005                        $('#booking_calendar').html(calendar_html).fadeIn('slow');
2006
2007                        self.booking_mask("set");
2008                        self.paintUserBook();
2009                        self.booking_mask('reset');
2010                    }
2011
2012                    // show/hide arrow for previous month
2013                    response.data.showPrev ? $('#prevRange').fadeIn('slow') : $('#prevRange').fadeOut('fast');
2014
2015                    // set prebooked days in calendar to continue order (in booking order page only)
2016                    // simulate selection
2017                    if (bookingConfigs['db_start']
2018                        && (bookingConfigs['db_end'] || bookingConfigs.Booking_type === 'time_range')
2019                        && bookingConfigs['start_date_value']
2020                        && (bookingConfigs['end_date_value'] || bookingConfigs.Booking_type === 'time_range')
2021                        && bookingConfigs['total_days']
2022                    ) {
2023                        // update calendar and count total amount
2024                        self.bUpdate(
2025                            bookingConfigs['db_start'],
2026                            false,
2027                            false,
2028                            param_mode || bookingConfigs.in_popup ? true : false
2029                        );
2030
2031                        if (!bookingConfigs['edit_action']) {
2032                            bookingConfigs['cur_id'] = bookingConfigs['s_id'] = bookingConfigs['db_start'];
2033                        }
2034
2035                        bookingConfigs['total_cost'] = 0;
2036                        bookingConfigs['first'] = 1;
2037
2038                        if (bookingConfigs.in_popup) {
2039                            // remove selected day from calendar
2040                            bookingConfigs['s_id'] = 0;
2041                            self.book_color(true);
2042                            bookingConfigs['first'] = 0;
2043                        } else {
2044                            var result = self.bUpdate(
2045                                bookingConfigs['db_end'],
2046                                bookingConfigs['db_start'],
2047                                '+',
2048                                !!param_mode
2049                            );
2050
2051                            if (!param_mode) {
2052                                self.stepsHandler(
2053                                    'details',
2054                                    'show',
2055                                    {
2056                                        start_book : bookingConfigs.start_date_value,
2057                                        end_book   : bookingConfigs.end_date_value,
2058                                        total_days : bookingConfigs.total_days,
2059                                        total_cost : bookingConfigs.total_cost,
2060                                        mode       : !!param_mode,
2061                                    }
2062                                );
2063
2064                                // set default condition for second selection
2065                                bookingConfigs['first'] = 0;
2066                            }
2067                        }
2068                    }
2069
2070                    if (param_mode) {
2071                        self.book_color(false, true, param_mode, true);
2072                    }
2073
2074                    if (typeof $.convertPrice == 'function') {
2075                        $('#area_booking .day-price').convertPrice({showCents: false, shortView: true});
2076                    }
2077
2078                    // disable calendar in request details page in first loading
2079                    if (rlPageInfo.controller === 'booking_requests') {
2080                        if (update) {
2081                            $('#booking_calendar').removeClass('disabled');
2082                            self.booking_mask('reset');
2083                            $('#area_booking .table-cell.current_dates').removeClass('current_dates');
2084
2085                            if (bookingConfigs.Booking_type === 'date_time_range'
2086                                && bookingConfigs.listing_check_in
2087                                && bookingConfigs.listing_check_out
2088                            ) {
2089                                /* build html for check in field */
2090                                checkin_html = '<div class="name"><div><span>' + lang['booking_checkin'];
2091                                checkin_html += '</span></div></div>';
2092                                checkin_html += '<div class="field start_date">';
2093
2094                                // block unavailable time
2095                                $('[name="f[booking_check_in]"] option').each(function(){
2096                                    $(this).attr('disabled', 'disabled');
2097
2098                                    if ($(this).attr('value') === bookingConfigs.listing_check_in) {
2099                                        $(this).removeAttr('disabled').attr('selected', 'selected');
2100
2101                                        return false;
2102                                    }
2103                                });
2104
2105                                checkin_html += '<b>' + bookingConfigs['start_date_value'] + '</b> - ';
2106                                checkin_html += $('#booking_checkin_field').html();
2107                                checkin_html += '</div>';
2108
2109                                $('.table-cell.check-in').html(checkin_html);
2110                                /* end html for check-in field */
2111
2112                                /* build html for check-out field */
2113                                checkout_html = '<div class="name"><div><span>' + lang['booking_checkout'];
2114                                checkout_html += '</span></div></div>';
2115                                checkout_html += '<div class="field end_date">';
2116
2117                                $('[name="f[booking_check_out]"] option[value="0"]').attr('disabled', 'disabled');
2118
2119                                // block unavailable time
2120                                $($('[name="f[booking_check_out]"] option').get().reverse()).each(function(){
2121                                    $(this).attr('disabled', 'disabled');
2122
2123                                    if ($(this).attr('value') === bookingConfigs.listing_check_out) {
2124                                        $(this).removeAttr('disabled').attr('selected', 'selected');
2125
2126                                        return false;
2127                                    }
2128                                });
2129
2130                                checkout_html += '<b>' + bookingConfigs['end_date_value'] + '</b> - ';
2131                                checkout_html += $('#booking_checkout_field').html();
2132                                checkout_html += '</div>';
2133
2134                                $('.table-cell.check-out').html(checkout_html);
2135                                /* end html for check-out field */
2136
2137                                self.dateTimeRangeHandler();
2138                            }
2139                        } else if (!update && !param_mode && bookingConfigs.Booking_type !== 'time_range') {
2140                            $('#booking_calendar').addClass('disabled');
2141                            self.booking_mask('set');
2142                        }
2143                    }
2144                } else {
2145                    printMessage('error', response.message);
2146                }
2147            })
2148        });
2149    };
2150
2151    /**
2152     * Saving selected binding days
2153     */
2154    this.saveBindingDaysHandler = function() {
2155        if (!bookingConfigs['listing_id']) {
2156            return false;
2157        }
2158
2159        $('.save_binding_days').click(function(){
2160            $(this).val(lang['loading']).addClass('disabled').attr('disabled', 'disabled');
2161
2162            var formData = $('#binding_days_form').serializeArray();
2163            var request  = {
2164                mode: 'bookingSaveBindingDays',
2165                item: {
2166                    listing_id: bookingConfigs['listing_id'],
2167                    formData: formData
2168                },
2169                lang: rlLang
2170            };
2171
2172            $.post(rlConfig.ajax_url, request, function(response){
2173                $('.save_binding_days').val(lang['save']).removeClass('disabled').removeAttr('disabled');
2174
2175                if (response.status == 'OK') {
2176                    printMessage('notice', response.data)
2177                } else {
2178                    printMessage('error', response.message);
2179                }
2180            }, 'json');
2181        });
2182    };
2183
2184    /**
2185     * Saving selected configs
2186     */
2187    this.saveConfigsHandler = function() {
2188        if (!bookingConfigs['listing_id']) {
2189            return false;
2190        }
2191
2192        $('.save_configs').click(function(){
2193            $(this).val(lang['loading']).addClass('disabled').attr('disabled', 'disabled');
2194            var min_day_value = $('[name="min_book_day"]').val();
2195            var max_day_value = $('[name="max_book_day"]').val();
2196
2197            if ((min_day_value == '' || parseInt(min_day_value) <= 0) && bookingConfigs['Booking_type'] != 'time_range') {
2198                $('.save_configs').val(lang['save']).removeClass('disabled').removeAttr('disabled');
2199                printMessage('error', lang['booking_min_config_desc'], 'min_book_day');
2200            } else if (max_day_value == ''
2201                || parseInt(max_day_value) < 0
2202                || (parseInt(max_day_value) > 0 && parseInt(max_day_value) <= parseInt(min_day_value))
2203            ) {
2204                $('.save_configs').val(lang['save']).removeClass('disabled').removeAttr('disabled');
2205                printMessage('error', lang['booking_max_config_error'], 'max_book_day');
2206            } else {
2207                var formData = $('#configs_form').serializeArray();
2208                var request  = {
2209                    mode: 'bookingSaveConfigs',
2210                    item: {
2211                        listing_id: bookingConfigs['listing_id'],
2212                        formData: formData
2213                    },
2214                    lang: rlLang
2215                };
2216
2217                $.post(rlConfig.ajax_url, request, function(response){
2218                    $('.save_configs').val(lang['save']).removeClass('disabled').removeAttr('disabled');
2219
2220                    if (response.status == 'OK') {
2221                        printMessage('notice', response.data)
2222                    } else {
2223                        printMessage('error', response.message);
2224                    }
2225                }, 'json');
2226            }
2227        });
2228    };
2229
2230    /**
2231     * Recount of regular price by changed value of "Rent field"
2232     */
2233    this.rentFieldHandler = function() {
2234        if (!bookingConfigs.booking_rent_field || !bookingConfigs.booking_rent_value) {
2235            return false;
2236        }
2237
2238        var $rent = $('[name="f[' + bookingConfigs.booking_rent_field +']"]');
2239
2240        if ($rent.length) {
2241            $rent.change(function() {
2242                if ($('[name="f[' + bookingConfigs['booking_rent_field'] +']"]:checked').val() !== bookingConfigs.booking_rent_value) {
2243                    if (bookingConfigs['booking_time_frame_field']) {
2244                        $('[name="f[' + bookingConfigs['booking_time_frame_field'] + ']"]').removeAttr('checked');
2245                    }
2246
2247                    self.recountRegularPrice();
2248                }
2249            });
2250        }
2251    };
2252
2253    /**
2254     * Recount of regular price by changed price
2255     */
2256    this.priceHandler = function() {
2257        if (!bookingConfigs.booking_price_field) {
2258            return false;
2259        }
2260
2261        var $price = $('[name="f[' + bookingConfigs['booking_price_field'] + '][value]"]');
2262
2263        if ($price.length > 0) {
2264            $price.change(function(){
2265                self.recountRegularPrice();
2266            });
2267        }
2268    };
2269
2270    /**
2271     * Recount of regular price by changed time frame
2272     */
2273    this.rentTimeFrameHandler = function() {
2274        if (!bookingConfigs.booking_time_frame_field) {
2275            return false;
2276        }
2277
2278        var $timeFrame = $('[name="f[' + bookingConfigs['booking_time_frame_field'] + ']"]');
2279
2280        if ($timeFrame.length) {
2281            $timeFrame.change(function(){
2282                self.recountRegularPrice();
2283            });
2284        }
2285    };
2286
2287    /**
2288     * Recount of regular price by changed currency
2289     */
2290    this.currencyHandler = function() {
2291        if (!bookingConfigs.booking_price_field) {
2292            return false;
2293        }
2294
2295        var $currency = $('[name="f[' + bookingConfigs.booking_price_field + '][currency]"]');
2296
2297        if ($currency.length) {
2298            $currency.change(function(){
2299                self.recountRegularPrice();
2300            });
2301        }
2302    };
2303
2304    /**
2305     * Recount and put the regular price to rate ranges table
2306     * @param {bool} enable_handlers - Enable handlers for recount
2307     */
2308    this.recountRegularPrice = function(enable_handlers) {
2309        if (enable_handlers) {
2310            self.priceHandler();
2311            self.rentTimeFrameHandler();
2312            self.currencyHandler();
2313            self.rentFieldHandler();
2314
2315            return;
2316        }
2317
2318        var $time_frame_field_val = $('[name="f[' + bookingConfigs.booking_time_frame_field + ']"]').is('select')
2319            ? $('[name="f[' + bookingConfigs.booking_time_frame_field + ']"] option:selected').val()
2320            : $('[name="f[' + bookingConfigs.booking_time_frame_field + ']"]:checked').val();
2321
2322        if (!bookingConfigs['booking_price_field']
2323            || !bookingConfigs['booking_time_frame']
2324            || $('[name="f[booking_module]"][value="1"]').is(':checked') === false
2325            || ((!bookingConfigs['booking_time_frame_field']
2326                || !$time_frame_field_val)
2327                && (bookingConfigs['booking_rent_field']
2328                    && bookingConfigs['booking_rent_value']
2329                    && $('[name="f[' + bookingConfigs['booking_rent_field'] + ']"]:checked').val() == bookingConfigs['booking_rent_value'])
2330                )
2331        ) {
2332            return;
2333        }
2334
2335        let price, currency, time_frame, price_cel, price_field = bookingConfigs.booking_price_field;
2336
2337        price = parseFloat($('[name="f[' + price_field + '][value]"]').val())
2338        ? parseFloat($('[name="f[' + price_field + '][value]"]').val())
2339        : lang['not_available'];
2340        currency = $('[name="f[' + price_field + '][currency]"] option:selected').text();
2341        time_frame = $time_frame_field_val;
2342
2343        /* count regular price */
2344        if (price > 0 && time_frame) {
2345            for (var time_frame_key in bookingConfigs.booking_time_frame) {
2346                if (!bookingConfigs.booking_time_frame.hasOwnProperty(time_frame_key)) {
2347                    continue;
2348                }
2349
2350                var time_frame_value = bookingConfigs.booking_time_frame[time_frame_key];
2351
2352                if (time_frame === time_frame_value) {
2353                    if (time_frame_key === 'day') {
2354                        price_cel = price;
2355                    } else if (time_frame_key === 'week') {
2356                        price_cel = Math.round((price / 7) * 100) / 100;
2357                    } else if (time_frame_key === 'month') {
2358                        // get count days of current month
2359                        Date.prototype.daysInMonth = function(mth, yr){
2360                            yr = yr || this.getFullYear();
2361                            mth = mth || this.getMonth();
2362
2363                            return new Date(yr, mth + 1, 0).getDate();
2364                        };
2365
2366                        price_cel = Math.round((price / new Date().daysInMonth()) * 100) / 100;
2367                    } else if (time_frame_key === 'year') {
2368                        price_cel = Math.round((price / 365) * 100) / 100;
2369                    } else if (time_frame_key === 'hour' && bookingConfigs.booking_type === 'date_time_range') {
2370                        price_cel = Math.round((price * 24) * 100) / 100;
2371                    } else {
2372                        price_cel = lang.not_available;
2373                    }
2374
2375                    break;
2376                } else {
2377                    price_cel = lang.not_available;
2378                }
2379            }
2380
2381            /* build regular price */
2382            if (price_cel !== lang.not_available) {
2383                price = bookingConfigs.system_currency_position === 'before'
2384                ? currency + ' ' + price_cel
2385                : price_cel + ' ' + currency;
2386            } else {
2387                price = lang.not_available;
2388            }
2389        } else {
2390            price = lang.not_available;
2391        }
2392
2393        /* add value to table with rate ranges */
2394        if ($('#rrange_regular').length > 0) {
2395            var price_val = price;
2396
2397            // add qtip with description of missing standard rate
2398            if (price_val === lang.not_available) {
2399                price += ' <img class="qtip booking_qtip" alt="" title="' + lang['booking_empty_regular'] + '" ';
2400                price += 'src="' + bookingConfigs['src_delete_img'] + '" />';
2401            }
2402
2403            $('#rrange_regular').find('div[data-caption="' + lang['price'] + '"]').html(price);
2404
2405            if (price_val === lang.not_available) {
2406                self.qtipInit(true);
2407            }
2408        }
2409
2410        return price;
2411    };
2412
2413    /**
2414     * Handler for booking steps
2415     *
2416     * @param {string} steps      - Key of step or "all"
2417     * @param {string} action     - Show/hide
2418     * @param {object} parameters - Additional parameters (optional)
2419     */
2420    this.stepsHandler = function(steps, action, parameters) {
2421        if (!steps || !action) {
2422            return;
2423        }
2424
2425        // show/hide booking details section
2426        if (steps === 'details') {
2427            if (action === 'show' && !parameters.mode) {
2428                var html = '';
2429
2430                if (!bookingConfigs.in_popup) {
2431                    if (rlPageInfo.controller === 'booking_order') {
2432                        html = '<div class="table-cell escort-date">';
2433                        html += '<div class="name"><div><span>';
2434
2435                        html += bookingConfigs.Booking_type === 'time_range'
2436                        ? lang.booking_escort_date
2437                        : lang.booking_checkin;
2438
2439                        html += '</span></div></div>';
2440                        html += '<div class="value">';
2441
2442                        // adding check-in (date & time)
2443                        if (parameters && parameters.start_book) {
2444                            if (bookingConfigs.Booking_type === 'date_time_range') {
2445                                if (bookingConfigs.listing_check_in) {
2446                                    $('[name="f[booking_check_in]"] option').each(function(){
2447                                        $(this).attr('disabled', 'disabled');
2448
2449                                        if ($(this).attr('value') === bookingConfigs.listing_check_in) {
2450                                            $(this).removeAttr('disabled').attr('selected', 'selected');
2451
2452                                            return false;
2453                                        }
2454                                    });
2455                                }
2456
2457                                html += '<div class="date-value"><b>' + parameters.start_book + '</b> - </div>' + $('#booking_checkin_field').html();
2458                            } else {
2459                                html += bookingConfigs.check_in && bookingConfigs.Booking_type !== 'time_range'
2460                                ? parameters.start_book + ' - ' + bookingConfigs.check_in
2461                                : parameters.start_book;
2462
2463                                if (bookingConfigs.Booking_type === 'time_range') {
2464                                    html += '<span style="margin: 0 5px;">/</span><a href="javascript:void(0)" ';
2465                                    html += 'onclick="booking.escortChangeDate()">';
2466                                    html += lang.booking_button_select_date + '</a>';
2467                                }
2468                            }
2469                        }
2470
2471                        html += '</div></div>';
2472
2473                        if (bookingConfigs.Booking_type === 'time_range') {
2474                            html += '<div class="table-cell">';
2475                            html += '<div class="name"><div><span>' + lang.booking_escort_type + '</span></div></div>';
2476                            html += '<div class="value">' + $('#escort_rates_obj').html() + '</div>';
2477
2478                            $('#escort_rates_obj').remove();
2479
2480                            html += '</div>';
2481                        }
2482
2483                        html += '<div class="table-cell">';
2484                        html += '<div class="name"><div><span>';
2485
2486                        if (bookingConfigs.Booking_type === 'time_range') {
2487                            let $timeContainer = $('#booking_checkout_field'),
2488                                $durationContainer = $('#escort_duration_obj');
2489
2490                            if (bookingConfigs.Booking_repeatedly) {
2491                                html += lang.booking_escort_time;
2492                                html += '</span></div></div>';
2493                                html += '<div class="value checkout_value_field">';
2494
2495                                self.updateEscortAvailabilityData();
2496
2497                                html += $timeContainer.html();
2498                                html += '</div></div>';
2499                                html += '<div class="table-cell">';
2500                                html += '<div class="name"><div><span>' + lang.booking_escort_duration;
2501                                html += '</span></div></div>';
2502                                html += '<div class="value">' + $durationContainer.html();
2503                            } else {
2504                                html += lang.booking_escort_duration;
2505                                html += '</span></div></div>';
2506                                html += '<div class="value hourly-time"><div class="view">' + lang.not_available + '</div>';
2507                                html += '<div class="origin hide">' + $durationContainer.html() + '</div>';
2508                                html += '</div></div>';
2509                                html += '<div class="table-cell">';
2510                                html += '<div class="name"><div><span>';
2511                                html += lang.booking_escort_time;
2512                                html += '</span></div></div>';
2513                                html += '<div class="value checkout_value_field">';
2514
2515                                self.updateEscortAvailabilityData();
2516
2517                                html += $timeContainer.html();
2518                                html += '</div></div>';
2519                            }
2520
2521                            $timeContainer.remove();
2522                            $durationContainer.remove();
2523                        } else {
2524                            html += lang.booking_checkout;
2525                            html += '</span></div></div>';
2526                            html += '<div class="value checkout_value_field">';
2527
2528                            // adding check-out (date & time)
2529                            if (parameters && parameters.end_book) {
2530                                if (bookingConfigs.Booking_type === 'date_time_range') {
2531                                    if (bookingConfigs.listing_check_out) {
2532                                        $('[name="f[booking_check_out]"] option[value="0"]').attr('disabled', 'disabled');
2533
2534                                        $($('[name="f[booking_check_out]"] option').get().reverse()).each(function(){
2535                                            $(this).attr('disabled', 'disabled');
2536
2537                                            if ($(this).attr('value') === bookingConfigs.listing_check_out) {
2538                                                $(this).removeAttr('disabled').attr('selected', 'selected');
2539
2540                                                return false;
2541                                            }
2542                                        });
2543                                    }
2544
2545                                    html += '<div class="date-value"><b>' + parameters.end_book + '</b> - </div>' + $('#booking_checkout_field').html();
2546                                } else {
2547                                    html += bookingConfigs.check_out
2548                                    ? parameters.end_book + ' - ' + bookingConfigs.check_out
2549                                    : parameters.end_book
2550                                }
2551                            }
2552                        }
2553
2554                        html += '</div></div>';
2555
2556                        if (bookingConfigs.Booking_type !== 'time_range') {
2557                            html += '<div class="table-cell">';
2558                            html += '<div class="name"><div><span>' + lang.booking_nights + '</span></div></div>';
2559                            html += '<div class="value">';
2560                            html += parameters && parameters.total_days ? parameters.total_days : '';
2561                            html += '</div></div>';
2562                        }
2563
2564                        if (bookingConfigs.total_cost || bookingConfigs.Booking_type === 'time_range') {
2565                            html += '<div class="table-cell">';
2566                            html += '<div class="name"><div><span>' + lang.booking_amount + '</span></div></div>';
2567                            html += '<div class="value amount">';
2568
2569                            // adding total price
2570                            var total_cost = bookingConfigs.total_cost ? bookingConfigs.total_cost : lang.not_available;
2571
2572                            if (bookingConfigs.total_cost) {
2573                                html += bookingConfigs.system_currency_position === 'before'
2574                                ? bookingConfigs.defCurrency + ' ' + total_cost
2575                                : total_cost + ' ' + bookingConfigs.defCurrency;
2576                            } else {
2577                                html += total_cost;
2578                            }
2579
2580                            html += '</div></div>';
2581
2582                            // added downpayment data
2583                            if (bookingConfigs.prepayment) {
2584                                var downpayment = self.countPrepayment(bookingConfigs.total_cost);
2585
2586                                html += '<div class="table-cell">';
2587                                html += '<div class="name"><div><span>' + lang.booking_prepayment_step + '</span></div></div>';
2588                                html += '<div class="value downpayment">' + downpayment + '</div></div>';
2589                            }
2590                        }
2591
2592                        html += '</div>';
2593                    }
2594
2595                    // next step button
2596                    html += '<div class="form-buttons">';
2597
2598                    // hidden fields with system values
2599                    html += '<form method="post" action="' + bookingConfigs.booking_order_page + '">';
2600                    html += '<input type="hidden" name="listing_id" value="' + bookingConfigs.listing_id + '">';
2601                    html += '<input type="hidden" name="start_date" value="' + bookingConfigs.db_start + '">';
2602                    html += '<input type="hidden" name="start_date_value" value="';
2603                    html += parameters && parameters.start_book ? parameters.start_book : '';
2604                    html += '">';
2605                    html += '<input type="hidden" name="checkin" value="' + bookingConfigs.check_in + '">';
2606                    html += '<input type="hidden" name="end_date" value="' + bookingConfigs.db_end + '">';
2607                    html += '<input type="hidden" name="end_date_value" value="';
2608                    html += parameters && parameters.end_book ? parameters.end_book : '';
2609                    html += '">';
2610                    html += '<input type="hidden" name="checkout" value="' + bookingConfigs.check_out + '">';
2611                    html += '<input type="hidden" name="total_cost" value="' + bookingConfigs.total_cost + '">';
2612                    html += '<input type="hidden" name="total_days" value="';
2613                    html += parameters && parameters.total_days ? parameters.total_days : '';
2614                    html += '">';
2615                    html += '<input ';
2616                    html += rlPageInfo.controller === 'listing_details'
2617                    ? 'type="submit" value="' + lang.booking_message_step_button + '"'
2618                    : 'type="button" onclick="booking.stepsHandler(\'fields\', \'show\')" ' + 'value="' + lang.next_step + '"';
2619
2620                    if (bookingConfigs.Booking_type === 'time_range'
2621                        && rlPageInfo.controller === 'booking_order'
2622                        && !bookingConfigs.edit_action
2623                    ) {
2624                        html += ' class="disabled" disabled="disabled"';
2625                    }
2626
2627                    html += ' /></form>';
2628                    html += '</div>';
2629
2630                    $('#booking_message').html(html);
2631
2632                    if (typeof $.convertPrice == 'function') {
2633                        $('#booking_message .value.amount, #booking_message .value.downpayment').convertPrice({
2634                            showCents: true,
2635                            shortView: true
2636                        });
2637                    }
2638
2639                    // add copy of button which will be added after all fields
2640                    if (rlPageInfo.controller === 'booking_order') {
2641                        $('.booking_message_clone').remove();
2642
2643                        let $buttonForm = $('#booking_message').find('input[type="button"]');
2644                        let $button = $buttonForm.clone();
2645                        $buttonForm.hide();
2646                        $button.addClass('booking_message_clone');
2647                        $('#booking_message_obj').append(
2648                            $('<div>', {class: 'table-cell'}).append(
2649                                $('<div>', {class: 'name'}),
2650                                $('<div>', {class: 'value'}).append(
2651                                    $button
2652                                )
2653                            )
2654                        );
2655                        $button.show();
2656                    }
2657
2658                    $('#booking_message_obj').fadeIn('slow');
2659
2660                    if (rlPageInfo.controller === 'listing_details') {
2661                        flynax.slideTo('#area_booking');
2662                    }
2663
2664                    if (rlPageInfo.controller === 'booking_order') {
2665                        self.dateTimeRangeHandler();
2666
2667                        if (bookingConfigs.Booking_type === 'time_range') {
2668                            self.escortFieldsValidateHandler();
2669                            self.escortAmountHandler();
2670
2671                            if (bookingConfigs.edit_action) {
2672                                $('[name="escort_duration"]').change();
2673                            }
2674                        }
2675                    }
2676                } else {
2677                    html += '<div class="name"><div><span>';
2678
2679                    html += bookingConfigs.Booking_type === 'time_range'
2680                    ? lang.booking_escort_date
2681                    : lang.booking_checkin;
2682
2683                    html += '</span></div></div>';
2684                    html += '<div class="value">';
2685
2686                    html += bookingConfigs.check_in && bookingConfigs.Booking_type !== 'time_range'
2687                    ? parameters.start_book + ' - ' + bookingConfigs.check_in
2688                    : parameters.start_book;
2689
2690                    html += '<span style="margin: 0 5px;">/</span><a href="javascript:void(0)" ';
2691                    html += 'onclick="booking.escortChangeDate()">';
2692                    html += lang['booking_button_select_date'] + '</a>';
2693
2694                    html += '</div>';
2695
2696                    // update start date
2697                    $('#booking_message').find('form input[name="start_date"]').val(bookingConfigs.db_start);
2698
2699                    // update start date value
2700                    $('#booking_message').find('form input[name="start_date_value"]').val(parameters.start_book);
2701
2702                    // update date for user
2703                    $('.table-cell.escort-date').html(html);
2704                }
2705            }
2706        } else if (steps === 'fields') {
2707            if (action === 'show') {
2708                if (rlPageInfo.controller === 'booking_order') {
2709                    // Saving escort data to form
2710                    if (bookingConfigs.Booking_type === 'time_range') {
2711                        var $bookingForm = $('#booking_message');
2712
2713                        $bookingForm.find('[name="checkin"]').val(bookingConfigs.escort_type_service);
2714                        $bookingForm.find('[name="checkout"]').val(bookingConfigs.escort_time_service);
2715                        $bookingForm.find('[name="end_date"]').val($('[name="escort_duration"] option:selected').val());
2716                        $bookingForm.find('[name="total_cost"]').val(bookingConfigs.total_cost);
2717                    }
2718
2719                    var $booking_form = $('#booking_message form');
2720                    var action_url = $booking_form.attr('action');
2721
2722                    $booking_form.find('[type="button"]')
2723                        .addClass('disabled')
2724                        .attr('disabled', true)
2725                        .val(lang.loading);
2726
2727                    if (action_url) {
2728                        if (rlConfig.mod_rewrite) {
2729                            action_url = action_url.replace('.html', '/') + 'contact-details.html';
2730                            action_url += bookingConfigs.edit_action ? '?edit' : '';
2731                        } else {
2732                            action_url = action_url + '&step=contact-details';
2733                            action_url += bookingConfigs.edit_action ? '&edit' : '';
2734                        }
2735
2736                        $booking_form.attr('action', action_url);
2737                        $booking_form.submit();
2738                    }
2739                } else {
2740                    $('#step_2').fadeIn('slow');
2741                    self.bookingFieldsValidator();
2742                    $('#booking_message [type="button"]').closest('.table-cell').hide();
2743                }
2744            } else if (action === 'hide') {
2745                $('#step_2').fadeOut('fast');
2746            }
2747        } else if (steps === 'all') {
2748            if (action === 'hide') {
2749                $('#step_2, #booking_message_obj').fadeOut('fast');
2750                bookingConfigs.first = 0;
2751                self.book_color(true);
2752            }
2753        } else if (steps === 'update_period') {
2754            if (action === 'update') {
2755                if (bookingConfigs.Booking_type === 'date_time_range'
2756                    && bookingConfigs.listing_check_in
2757                    && bookingConfigs.listing_check_out
2758                ) {
2759                    /* build html for check in field */
2760                    checkin_html = '<div class="name"><div><span>' + lang.booking_checkin;
2761                    checkin_html += '</span></div></div>';
2762                    checkin_html += '<div class="field start_date">';
2763
2764                    // block unavailable time
2765                    $('[name="f[booking_check_in]"] option').each(function(){
2766                        $(this).attr('disabled', 'disabled');
2767
2768                        if ($(this).attr('value') === bookingConfigs.listing_check_in) {
2769                            $(this).removeAttr('disabled').attr('selected', 'selected');
2770
2771                            return false;
2772                        }
2773                    });
2774
2775                    checkin_html += bookingConfigs.start_date_value + ' - ';
2776                    checkin_html += $('#booking_checkin_field').html();
2777                    checkin_html += '</div>';
2778
2779                    $('.table-cell.check-in').html(checkin_html);
2780                    /* end html for check-in field */
2781
2782                    /* build html for check-out field */
2783                    checkout_html = '<div class="name"><div><span>' + lang.booking_checkout;
2784                    checkout_html += '</span></div></div>';
2785                    checkout_html += '<div class="field end_date">';
2786
2787                    $('[name="f[booking_check_out]"] option[value="0"]').attr('disabled', 'disabled');
2788
2789                    // block unavailable time
2790                    $($('[name="f[booking_check_out]"] option').get().reverse()).each(function(){
2791                        $(this).attr('disabled', 'disabled');
2792
2793                        if ($(this).attr('value') === bookingConfigs.listing_check_out) {
2794                            $(this).removeAttr('disabled').attr('selected', 'selected');
2795
2796                            return false;
2797                        }
2798                    });
2799
2800                    checkout_html += '<b>' + bookingConfigs.end_date_value + '</b> - ';
2801                    checkout_html += $('#booking_checkout_field').html();
2802                    checkout_html += '</div>';
2803
2804                    $('.table-cell.check-out').html(checkout_html);
2805                    /* end html for check-out field */
2806
2807                    self.dateTimeRangeHandler();
2808                } else {
2809                    if (bookingConfigs.start_date_value) {
2810                        let start_date = bookingConfigs.check_in
2811                            ? '<b>' + bookingConfigs.start_date_value + '</b> - ' + bookingConfigs.check_in
2812                            : '<b>' + bookingConfigs.start_date_value + '</b>';
2813
2814                        $('#area_booking .field.start_date').html(start_date);
2815                    }
2816
2817                    if (bookingConfigs.end_date_value) {
2818                        let end_date = bookingConfigs.check_out
2819                            ? '<b>' + bookingConfigs.end_date_value + '</b> - ' + bookingConfigs.check_out
2820                            : '<b>' + bookingConfigs.end_date_value + '</b>';
2821
2822                        $('#area_booking .field.end_date').html(end_date);
2823                    }
2824
2825                    if (bookingConfigs.Booking_type === 'date_range' && bookingConfigs.db_start && bookingConfigs.db_end) {
2826                        let nightsCount = self.getCountDaysBetween2Timestamps(
2827                            bookingConfigs.db_start,
2828                            bookingConfigs.db_end
2829                        );
2830
2831                        $('#area_booking .field.nights-count').html(nightsCount);
2832                    }
2833                }
2834
2835                if (bookingConfigs.total_cost) {
2836                    var price = bookingConfigs.system_currency_position === 'before'
2837                    ? bookingConfigs.defCurrency + ' ' + bookingConfigs.total_cost
2838                    : bookingConfigs.total_cost + ' ' + bookingConfigs.defCurrency;
2839
2840                    $('#area_booking .field.total_cost').text(price);
2841
2842                    if (typeof $.convertPrice == 'function') {
2843                        $('#area_booking .field.total_cost').convertPrice({showCents: true, shortView: true});
2844                    }
2845                }
2846
2847                bookingConfigs.period_updated = true;
2848            }
2849        }
2850    };
2851
2852    /**
2853     * Show calendar in modal window for escort package
2854     */
2855    this.escortChangeDate = function() {
2856        $(document).flModal({
2857            caption : lang['booking_button_select_date'],
2858            content : '<div id="booking_calendar" class="in_popup"' + '>' + lang['loading'] + '</div>',
2859            ready   : function() {
2860                bookingConfigs['in_popup'] = true;
2861                bookingConfigs['s_id']     = 0;
2862
2863                self.getDates(bookingConfigs.listing_id, null, true);
2864            },
2865            width   : '400',
2866            height  : 'auto',
2867            click   : false
2868        });
2869    };
2870
2871    /**
2872     * Control filling escort fields (type, time, duration)
2873     */
2874    this.escortFieldsValidateHandler = function() {
2875        var $typeSelector     = $('[name="escort_rates"]'),
2876            $timeSelector     = $('[name="f[booking_check_out]"]'),
2877            $durationSelector = $('[name="escort_duration"]');
2878
2879        $typeSelector.change(function(){
2880            // reset time/duration fields
2881            var type_service  = Number($typeSelector.find('option:selected').val());
2882            var hours_in_type = Number($typeSelector.find('option:selected').data('hours'));
2883
2884            self.selectFirstActiveOption($timeSelector);
2885            self.selectFirstActiveOption($durationSelector);
2886
2887            if (type_service < 0) {
2888                $timeSelector.addClass('disabled').attr('disabled', 'disabled');
2889                $durationSelector.addClass('disabled').attr('disabled', 'disabled');
2890            } else {
2891                bookingConfigs.escort_type_service       = type_service;
2892                bookingConfigs.escort_type_service_title = trim($typeSelector.find('option:selected').html());
2893
2894                // enable all options
2895                $durationSelector.find('option').removeClass('hide').removeAttr('disabled');
2896
2897                if (hours_in_type) {
2898                    let activeWorkOptions = $timeSelector.find('option:not(:disabled)').length;
2899                    let activeWorkHours = (0.5 * activeWorkOptions) - 0.5; // Exclude last option with end of workday
2900
2901                    // enable only options which can be used with selected type of service
2902                    $durationSelector.find('option').each(function(){
2903                        var option_value = parseFloat($(this).attr('value'));
2904
2905                        if ((option_value > 0 && option_value % hours_in_type !== 0)
2906                            || (activeWorkHours > 0 && option_value > activeWorkHours)
2907                        ) {
2908                            $(this).attr('disabled', true).addClass('hide');
2909                        }
2910                    });
2911
2912                    $durationSelector.removeClass('disabled').removeAttr('disabled');
2913                } else {
2914                    bookingConfigs['escort_duration'] = false;
2915                    $durationSelector.addClass('disabled').attr('disabled', 'disabled');
2916                }
2917
2918                if ($timeSelector.hasClass('disabled')) {
2919                    $timeSelector.removeClass('disabled').removeAttr('disabled');
2920
2921                    if (Number($timeSelector.find('option:selected').val()) > 0 && hours_in_type) {
2922                        $durationSelector.removeClass('disabled').removeAttr('disabled');
2923                    }
2924                }
2925
2926                self.selectFirstActiveOption($durationSelector);
2927            }
2928
2929            if (!bookingConfigs.Booking_repeatedly) {
2930                let $hourlyTime = $('.hourly-time .view');
2931
2932                if (type_service >= 0) {
2933                    $hourlyTime.text(trim($durationSelector.find('option:selected').html()));
2934                } else {
2935                    $hourlyTime.text(lang.not_available);
2936                }
2937            }
2938
2939            self.updateEscortAvailabilityData();
2940        });
2941
2942        $timeSelector.change(function(){
2943            var time = parseInt($timeSelector.find('option:selected').val());
2944
2945            if (time > 0) {
2946                bookingConfigs.escort_time_service    = $timeSelector.find('option:selected').val();
2947                bookingConfigs.escortTimeServiceValue = trim($timeSelector.find('option:selected').html());
2948
2949                if (parseInt($typeSelector.find('option:selected').data('hours')) > 1) {
2950                    $durationSelector.removeClass('disabled').removeAttr('disabled');
2951                }
2952
2953                // Enable all options
2954                $durationSelector.find('option').removeClass('hide').removeAttr('disabled');
2955
2956                var hours_in_type = Number($typeSelector.find('option:selected').data('hours'));
2957
2958                if (hours_in_type) {
2959                    let activeWorkHours = 0, currentTime = false;
2960                    $timeSelector.find('option').each(function() {
2961                        if (currentTime && !$(this).is(':disabled')) {
2962                            activeWorkHours += 0.5;
2963                        }
2964
2965                        if ($(this).is(':selected')) {
2966                            currentTime = true;
2967                        }
2968                    });
2969
2970                    // Enable only options which can be used with selected time and rest of time
2971                    if (activeWorkHours === 0) {
2972                        $durationSelector.find('option:not(:first)').attr('disabled', true).addClass('hide');
2973                    } else {
2974                        $($durationSelector.find('option').get().reverse()).each(function(){
2975                            var optionValue = parseFloat($(this).attr('value'));
2976
2977                            if ((optionValue > 0 && optionValue % hours_in_type !== 0)
2978                                || (activeWorkHours > 0 && optionValue > activeWorkHours)
2979                            ) {
2980                                $(this).attr('disabled', true).addClass('hide');
2981                            }
2982                        });
2983                    }
2984                }
2985
2986                self.selectFirstActiveOption($durationSelector);
2987            } else {
2988                $durationSelector.addClass('disabled').attr('disabled', 'disabled');
2989            }
2990        });
2991
2992        $durationSelector.change(function () {
2993            if (rlPageInfo.controller !== 'booking_requests') {
2994                return;
2995            }
2996
2997            bookingConfigs.escort_duration       = $durationSelector.find('option:selected').val();
2998            bookingConfigs.escort_duration_title = trim($durationSelector.find('option:selected').html());
2999        });
3000    };
3001
3002    /**
3003     * Count total booking amount for escort
3004     */
3005    this.escortAmountHandler = function() {
3006        let $nextButton           = $('#booking_message input[type="button"]'),
3007            $amountContainer      = $('#booking_message,#booking_details').find('.value.amount,.field.amount'),
3008            $answerContainer      = $('#owner_actions'),
3009            $downpaymentContainer = $('.table-cell .value.downpayment');
3010
3011        $('[name="escort_rates"],[name="f[booking_check_out]"],[name="escort_duration"]').change(function(){
3012            let $selectedType   = $('[name="escort_rates"] option:selected'),
3013                amount          = 0,
3014                activeWorkHours = 0,
3015                $timeSelector   = $('[name="f[booking_check_out]"]'),
3016                type            = parseInt($selectedType.val()),
3017                time            = parseInt($('[name="f[booking_check_out]"] option:selected').val()),
3018                duration        = parseFloat($('[name="escort_duration"] option:selected').val()),
3019                price           = parseFloat($selectedType.data('price')),
3020                hoursInType     = parseFloat($selectedType.data('hours'));
3021
3022            $timeSelector.find('option').each(function() {
3023                if (!$(this).is(':disabled')) {
3024                    activeWorkHours += 0.5;
3025                }
3026            });
3027
3028            if (type !== -1
3029                && time !== 0
3030                && (duration !== -1 || (duration === -1 && !hoursInType))
3031                && bookingConfigs.db_start
3032                && price
3033                && (activeWorkHours && activeWorkHours >= hoursInType)
3034            ) {
3035                $nextButton.removeClass('disabled').removeAttr('disabled');
3036                $answerContainer.removeClass('hide');
3037
3038                if (rlPageInfo.controller === 'booking_order') {
3039                    $('.booking_message_clone').removeClass('disabled').removeAttr('disabled');
3040                }
3041
3042                // show price without calculation
3043                if (duration === -1 && !hoursInType) {
3044                    amount = price;
3045                } else {
3046                    bookingConfigs.escort_duration = duration;
3047
3048                    if (hoursInType === duration) {
3049                        amount = price;
3050                    } else {
3051                        duration = hoursInType > 0 ? duration / hoursInType : duration;
3052                        amount   = Math.round((price * duration) * 100) / 100;
3053                    }
3054                }
3055
3056                bookingConfigs.total_cost = amount;
3057
3058                amount = bookingConfigs.system_currency_position === 'before'
3059                ? bookingConfigs.defCurrency + ' ' + amount
3060                : amount + ' ' + bookingConfigs.defCurrency;
3061
3062                $amountContainer.text(amount);
3063
3064                if (typeof $.convertPrice == 'function') {
3065                    $amountContainer.convertPrice({showCents: true, shortView: true});
3066                }
3067
3068                // update downpayment if it exist
3069                var downpayment = self.countPrepayment(bookingConfigs.total_cost);
3070                if (bookingConfigs.prepayment) {
3071                    $downpaymentContainer.text(downpayment);
3072
3073                    if (typeof $.convertPrice == 'function') {
3074                        $downpaymentContainer.convertPrice({showCents: true, shortView: true});
3075                    }
3076                }
3077            } else {
3078                $nextButton.addClass('disabled').attr('disabled', 'disabled');
3079                $answerContainer.addClass('hide');
3080                $amountContainer.text(lang.not_available);
3081
3082                if (bookingConfigs.prepayment) {
3083                    $downpaymentContainer.text(lang.not_available);
3084                }
3085
3086                if (rlPageInfo.controller === 'booking_order') {
3087                    $('.booking_message_clone').addClass('disabled').attr('disabled', 'disabled');
3088                }
3089            }
3090        });
3091    };
3092
3093    /**
3094     * Count prepayment by price
3095     * @param  {string} price
3096     * @return {string}
3097     */
3098    this.countPrepayment = function(price) {
3099        var downpayment = lang.not_available;
3100
3101        if (price && bookingConfigs.prepayment) {
3102            downpayment = (parseFloat(price.toString().replace(/[^0-9.]+/g, "")) / 100) * bookingConfigs.prepayment;
3103            downpayment = Math.round((downpayment) * 100) / 100;
3104            downpayment = bookingConfigs.system_currency_position === 'before'
3105            ? bookingConfigs.defCurrency + ' ' + downpayment
3106            : downpayment + ' ' + bookingConfigs.defCurrency;
3107        }
3108
3109        return downpayment;
3110    };
3111
3112    /**
3113     * Get availability of selected day
3114     *
3115     * @since 3.2.0
3116     *
3117     * @param day - Date in timestamp
3118     * @returns {string|boolean}
3119     */
3120    this.getAvailabilityOfDay = function(day) {
3121        day = Number(day);
3122
3123        if (!day) {
3124            return false;
3125        }
3126
3127        var av_mon = bookingConfigs.availability.day_1 ? bookingConfigs.availability.day_1 : '';
3128        var av_tue = bookingConfigs.availability.day_2 ? bookingConfigs.availability.day_2 : '';
3129        var av_wed = bookingConfigs.availability.day_3 ? bookingConfigs.availability.day_3 : '';
3130        var av_thu = bookingConfigs.availability.day_4 ? bookingConfigs.availability.day_4 : '';
3131        var av_fri = bookingConfigs.availability.day_5 ? bookingConfigs.availability.day_5 : '';
3132        var av_sat = bookingConfigs.availability.day_6 ? bookingConfigs.availability.day_6 : '';
3133        var av_sun = bookingConfigs.availability.day_7 ? bookingConfigs.availability.day_7 : '';
3134
3135        var selected_day = self.getDateByTimeZone(day, 'ddd').toLowerCase();
3136        var current_av = '';
3137
3138        // get availability data of selected day
3139        if (selected_day === 'mon' && av_mon) {
3140            current_av = av_mon;
3141        } else if (selected_day === 'tue' && av_tue) {
3142            current_av = av_tue;
3143        } else if (selected_day === 'wed' && av_wed) {
3144            current_av = av_wed;
3145        } else if (selected_day === 'thu' && av_thu) {
3146            current_av = av_thu;
3147        } else if (selected_day === 'fri' && av_fri) {
3148            current_av = av_fri;
3149        } else if (selected_day === 'sat' && av_sat) {
3150            current_av = av_sat;
3151        } else if (selected_day === 'sun' && av_sun) {
3152            current_av = av_sun;
3153        }
3154
3155        return current_av;
3156    };
3157
3158    /**
3159     * Update data in "Time of Service" field
3160     */
3161    this.updateEscortAvailabilityData = function() {
3162        let $timeSelector = $('[name="f[booking_check_out]"]');
3163        let $timeOptions  = $timeSelector.find('option');
3164        let $selectedRate = $('select[name="escort_rates"] option[value="' + bookingConfigs.escort_type_service + '"]');
3165        let rateType      = $selectedRate.length ? $selectedRate.data('type') : null;
3166        let rateIndex     = $selectedRate.length ? $selectedRate.val() : null;
3167
3168        // reset options
3169        $($timeOptions.get()).each(function(){
3170            $(this).removeAttr('disabled');
3171
3172            if (!bookingConfigs.edit_action) {
3173                $(this).removeAttr('selected');
3174            }
3175        });
3176
3177        var current_av = self.getAvailabilityOfDay(bookingConfigs.db_start);
3178
3179        // update by availability data
3180        if (current_av) {
3181            var av_from = '';
3182            if (current_av[0] !== lang.time_midnight && current_av[0] !== lang.time_noon) {
3183                av_from = current_av[0];
3184            } else {
3185                if (current_av[0] === lang.time_midnight) {
3186                    av_from = '12:00 am';
3187                } else if (current_av[0] === lang.time_noon) {
3188                    av_from = '12:00 pm';
3189                }
3190            }
3191
3192            var av_to = '';
3193            if (current_av[1] !== lang.time_midnight && current_av[1] !== lang.time_noon) {
3194                av_to = current_av[1];
3195            } else {
3196                if (current_av[1] === lang.time_midnight) {
3197                    av_to = '12:00 am';
3198                } else if (current_av[1] === lang.time_noon) {
3199                    av_to = '12:00 pm';
3200                }
3201            }
3202
3203            // set "from"
3204            $($timeOptions.get()).each(function(){
3205                $(this).attr('disabled', 'disabled');
3206                var option_value = $(this).attr('value');
3207
3208                if (option_value === av_from) {
3209                    $(this).removeAttr('disabled');
3210
3211                    if (!bookingConfigs.edit_action) {
3212                        $(this).attr('selected', 'selected');
3213                        bookingConfigs.escort_time_service = option_value;
3214                    } else {
3215                        bookingConfigs.escort_time_service = bookingConfigs.check_out;
3216                    }
3217
3218                    return false;
3219                }
3220            });
3221
3222            // set "to"
3223            $($timeOptions.get().reverse()).each(function(){
3224                $(this).attr('disabled', 'disabled');
3225
3226                if ($(this).attr('value').replace(' ' + lang.booking_time_next_day, '') === av_to) {
3227                    // Disable the end of work-day
3228                    $(this).removeAttr('disabled');
3229                    return false;
3230                }
3231            });
3232
3233            // Disable already booked time by other bookings
3234            if (bookingConfigs.usBook) {
3235                for (var i in bookingConfigs.usBook) {
3236                    if (!bookingConfigs.usBook.hasOwnProperty(i)) {
3237                        continue;
3238                    }
3239
3240                    var userBook = bookingConfigs.usBook[i];
3241
3242                    // Skip booking from other days
3243                    if (userBook[1] != bookingConfigs.db_start) {
3244                        continue;
3245                    }
3246
3247                    // Skip own request in requests page
3248                    if (rlPageInfo.controller === 'booking_requests' && userBook[3] == bookingConfigs.request_id) {
3249                        continue;
3250                    }
3251
3252                    // Miss all booked time which using other type of rate
3253                    if (((rateType && rateIndex !== userBook[8]) || !rateType)
3254                        && bookingConfigs.Booking_repeatedly === false
3255                        && bookingConfigs.is_escort === false
3256                    ) {
3257                        continue;
3258                    }
3259
3260                    var checkInTime          = userBook[5];
3261                    var duration             = userBook[6];
3262                    var countDisabledOptions = duration * 2; // 1h => 30m * 2, 2h => 30m * 4 and etc.
3263
3264                    $($timeOptions.get()).each(function(){
3265                        if ($(this).val() === '0' || $(this).val().indexOf(lang.booking_time_next_day) > 0) {
3266                            return true;
3267                        }
3268
3269                        if ($(this).val() === checkInTime) {
3270                            $(this).attr('disabled', 'disabled');
3271                            var $option = $(this);
3272
3273                            for (var i = 1; i <= countDisabledOptions; i++) {
3274                                $option.attr('disabled', 'disabled');
3275                                $option = $option.next();
3276                            }
3277                        }
3278                    });
3279                }
3280            }
3281
3282            self.selectFirstActiveOption($timeSelector);
3283            bookingConfigs.escort_time_service = $timeSelector.find('option:selected').val();
3284        }
3285    };
3286
3287    /**
3288     * Check booking fields before submit form
3289     */
3290    this.bookingFieldsValidator = function() {
3291        $('#checkValid').unbind('click').click(function(){
3292            var error_fields = field_names = cond_fields_names = '';
3293            $(this).closest('form').submit(function(){ return false });
3294
3295            // get all fields from form
3296            $(this).closest('form').find('.submit-cell:not(.buttons)').each(function(){
3297                var field = $(this).find('input,textarea');
3298                var field_name = field.closest('.submit-cell').find('.name').text().replace('* ', '').trim();
3299                var field_key = field.attr('name');
3300                var field_val = field.val();
3301
3302                // check "text", "number" and "textarea" fields
3303                if (field.hasClass('required') && !field.hasClass('bool') && !field_val) {
3304                    // collect field names
3305                    field_names += (field_names ? ',' : '') + '"<b>' + field_name + '</b>"';
3306
3307                    // collect field keys
3308                    if (error_fields.indexOf(field_key) < 0) {
3309                        error_fields = error_fields ? error_fields + ',' + field_key : field_key;
3310                    }
3311                }
3312
3313                // check conditions (isUrl, isDomain, isEmail)
3314                if (field.hasClass('required')
3315                    && ((field.hasClass('isUrl') && !self.checkCondition(field_val, 'url'))
3316                        || (field.hasClass('isDomain') && !self.checkCondition(field_val, 'domain'))
3317                        || (field.hasClass('isEmail') && !self.checkCondition(field_val, 'mail'))
3318                    )
3319                ) {
3320                    // collect field names
3321                    cond_fields_names += (cond_fields_names ? ',' : '') + '"<b>' + field_name + '</b>"';
3322
3323                    // collect field keys & if key not exist in list already
3324                    if (error_fields.indexOf(field_key) < 0) {
3325                        error_fields = error_fields ? error_fields + ',' + field_key : field_key;
3326                    }
3327                }
3328            });
3329
3330            if (error_fields && (field_names || cond_fields_names)) {
3331                // some fields are empty && values incorrect
3332                if (error_fields && field_names && cond_fields_names) {
3333                    var errors_content = '<ul>';
3334                    errors_content += '<li>';
3335                    errors_content += lang['booking_field_error'].replace('error_field', field_names);
3336                    errors_content += '</li>';
3337                    errors_content += '<li>';
3338                    errors_content += lang['booking_inccorect_field_error'].replace('error_field', cond_fields_names);
3339                    errors_content += '</li>';
3340
3341                    printMessage('error', errors_content, error_fields);
3342                }
3343                // some fields are empty
3344                else if (error_fields && field_names) {
3345                    printMessage(
3346                        'error',
3347                        lang['booking_field_error'].replace('error_field', field_names),
3348                        error_fields
3349                    );
3350                }
3351                // values incorrect
3352                else if (error_fields && cond_fields_names) {
3353                    printMessage(
3354                        'error',
3355                        lang['booking_inccorect_field_error'].replace('error_field', cond_fields_names),
3356                        error_fields
3357                    );
3358                }
3359            } else {
3360                $(this).closest('form').unbind('submit').submit(function(){
3361                    $('#checkValid').addClass('disabled').attr('disabled', true).val(lang['loading']);
3362
3363                    if (bookingConfigs['booking_order_page']) {
3364                        var $booking_form = $(this);
3365                        var action_url = bookingConfigs['booking_order_page'];
3366
3367                        if (action_url) {
3368                            if (bookingConfigs.prepayment) {
3369                                if (rlConfig['mod_rewrite']) {
3370                                    action_url = action_url.replace('.html', '/') + 'prepayment.html';
3371                                } else {
3372                                    action_url = action_url + '&step=prepayment';
3373                                }
3374                            }
3375
3376                            $booking_form.attr('action', action_url);
3377                        }
3378                    }
3379
3380                    return true;
3381                });
3382            }
3383        });
3384    };
3385
3386    /**
3387     * Validate string by condition
3388     * @param  {string} string - Incoming string
3389     * @param  {string} mode   - Type of condition
3390     * @return {bool}
3391     */
3392    this.checkCondition = function(string, mode) {
3393        var pattern;
3394
3395        if (mode === 'domain') {
3396            pattern = /\S+\.\S+/;
3397        } else if (mode === 'url') {
3398            pattern = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
3399        } else if (mode === 'mail') {
3400            pattern = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
3401        }
3402
3403        return string && pattern ? pattern.test(string) : false;
3404    };
3405
3406    /**
3407     * Copying data from selectors to output form
3408     */
3409    this.dateTimeRangeHandler = function() {
3410        // check-in time field
3411        $('[name="f[booking_check_in]"]').change(function(){
3412            var checkin_time = $(this).val();
3413            $('[name="checkin"]').val(checkin_time);
3414
3415            // decrease booking amount if user reduce time of check in
3416            if (checkin_time && bookingConfigs['booking_regular_per_hour']) {
3417                var new_checkin_time = checkin_time.split(':');
3418                var old_checkin_time = rlPageInfo.controller === 'booking_requests'
3419                ? bookingConfigs['listing_check_in'].split(':')
3420                : bookingConfigs['check_in'].split(':');
3421
3422                var new_hour = new_checkin_time[0] ? parseInt(new_checkin_time[0]) : 0;
3423                var time_period = new_checkin_time[1] ? new_checkin_time[1].split(' ') : false;
3424                new_hour = time_period['1'] && time_period['1'] === 'pm' && new_hour < 12 ? new_hour + 12 : new_hour;
3425
3426                var old_hour = old_checkin_time[0] ? parseInt(old_checkin_time[0]) : 0;
3427                time_period = old_checkin_time[1] ? old_checkin_time[1].split(' ') : false;
3428                old_hour = time_period['1'] && time_period['1'] === 'pm' && old_hour < 12 ? old_hour + 12 : old_hour;
3429
3430                var new_minutes = new_checkin_time[1] ? parseInt(new_checkin_time[1]) : 0;
3431                var old_minutes = old_checkin_time[1] ? parseInt(old_checkin_time[1]) : 0;
3432
3433                if (new_hour > old_hour || (new_hour === old_hour && new_minutes > old_minutes)) {
3434                    var difference_time = new_hour - old_hour;
3435                    difference_time = new_minutes > old_minutes ? difference_time + 0.5 : difference_time;
3436
3437                    var price_per_hour = bookingConfigs.booking_regular_per_hour;
3438                    var difference_price = difference_time && price_per_hour
3439                    ? difference_time * price_per_hour
3440                    : price_per_hour;
3441
3442                    var current_booking_amount = parseFloat(bookingConfigs.total_cost);
3443
3444                    if (current_booking_amount && difference_price) {
3445                        var new_booking_amount = Math.round((current_booking_amount - difference_price) * 100) / 100;
3446
3447                        $('[name="total_cost"]').val(new_booking_amount);
3448
3449                        $('.amount').text(
3450                            bookingConfigs.system_currency_position === 'before'
3451                            ? bookingConfigs.defCurrency + ' ' + new_booking_amount
3452                            : new_booking_amount + ' ' + bookingConfigs.defCurrency
3453                        );
3454                    }
3455                } else {
3456                    $('[name="total_cost"]').val(bookingConfigs.total_cost);
3457                    $('.amount').text(
3458                        bookingConfigs.system_currency_position === 'before'
3459                        ? bookingConfigs.defCurrency + ' ' + bookingConfigs.total_cost
3460                        : bookingConfigs.total_cost + ' ' + bookingConfigs.defCurrency
3461                    );
3462                }
3463            }
3464        });
3465
3466        // check-out time field
3467        $('[name="f[booking_check_out]"]').change(function(){
3468            var checkout_time = $(this).val();
3469            $('[name="checkout"]').val(checkout_time);
3470
3471            // Decrease booking amount if user reduce time of check out
3472            if (checkout_time && bookingConfigs['booking_regular_per_hour']) {
3473                var new_checkout_time = checkout_time.split(':');
3474                var old_checkout_time = rlPageInfo.controller == 'booking_requests'
3475                ? bookingConfigs['listing_check_out'].split(':')
3476                : bookingConfigs['check_out'].split(':');
3477
3478                var new_hour = new_checkout_time[0] ? parseInt(new_checkout_time[0]) : 0;
3479                var time_period = new_checkout_time[1] ? new_checkout_time[1].split(' ') : false;
3480
3481                if (time_period['1'] && time_period['1'] == 'pm' && new_hour < 12) {
3482                    new_hour +=  12;
3483                } else if (time_period['1'] && time_period['1'] == 'am' && new_hour == 12) {
3484                    new_hour =  0;
3485                }
3486
3487                var old_hour = old_checkout_time[0] ? parseInt(old_checkout_time[0]) : 0;
3488                time_period = old_checkout_time[1] ? old_checkout_time[1].split(' ') : false;
3489                old_hour = time_period['1'] && time_period['1'] == 'pm' && old_hour < 12 ? old_hour + 12 : old_hour;
3490
3491                var new_minutes = new_checkout_time[1] ? parseInt(new_checkout_time[1]) : 0;
3492                var old_minutes = old_checkout_time[1] ? parseInt(old_checkout_time[1]) : 0;
3493
3494                if (new_hour < old_hour || (new_hour == old_hour && new_minutes != old_minutes)) {
3495                    var difference_time = old_hour - new_hour;
3496                    difference_time = new_minutes > old_minutes ? difference_time - 0.5 : difference_time;
3497
3498                    var price_per_hour = bookingConfigs['booking_regular_per_hour'];
3499                    var difference_price = difference_time && price_per_hour
3500                    ? (Math.round((difference_time * price_per_hour) * 100) / 100)
3501                    : price_per_hour;
3502
3503                    var current_booking_amount = parseFloat(bookingConfigs['total_cost']);
3504
3505                    if (current_booking_amount && difference_price) {
3506                        var new_booking_amount = Math.round((current_booking_amount - difference_price) * 100) / 100;
3507
3508                        $('[name="total_cost"]').val(new_booking_amount);
3509
3510                        $('.amount').text(
3511                            bookingConfigs['system_currency_position'] == 'before'
3512                            ? bookingConfigs['defCurrency'] + ' ' + new_booking_amount
3513                            : new_booking_amount + ' ' + bookingConfigs['defCurrency']
3514                        );
3515                    }
3516                } else {
3517                    $('[name="total_cost"]').val(bookingConfigs['total_cost']);
3518                    $('.amount').text(
3519                        bookingConfigs['system_currency_position'] == 'before'
3520                        ? bookingConfigs['defCurrency'] + ' ' + bookingConfigs['total_cost']
3521                        : bookingConfigs['total_cost'] + ' ' + bookingConfigs['defCurrency']
3522                    );
3523                }
3524            }
3525        });
3526    };
3527
3528    /**
3529     * Handler for "Change Period" button
3530     */
3531    this.changePeriodHandler = function() {
3532        // Save request data
3533        tmpBookingConfigs.db_start                  = bookingConfigs.db_start;
3534        tmpBookingConfigs.start_date_value          = bookingConfigs.start_date_value;
3535        tmpBookingConfigs.db_end                    = bookingConfigs.db_end;
3536        tmpBookingConfigs.end_date_value            = bookingConfigs.end_date_value;
3537        tmpBookingConfigs.total_cost                = self.str2money(bookingConfigs.total_cost);
3538        tmpBookingConfigs.check_in                  = bookingConfigs.check_in;
3539        tmpBookingConfigs.check_out                 = bookingConfigs.check_out;
3540        tmpBookingConfigs.escort_duration           = bookingConfigs.escort_duration;
3541        tmpBookingConfigs.escort_duration_title     = bookingConfigs.escort_duration_title;
3542        tmpBookingConfigs.escort_type_service       = bookingConfigs.escort_type_service;
3543        tmpBookingConfigs.escort_type_service_title = bookingConfigs.escort_type_service_title;
3544        tmpBookingConfigs.escort_time_service       = bookingConfigs.escort_time_service;
3545
3546        $('#change_period').click(function() {
3547            if ($(this).hasClass('process')) {
3548                $('#owner_actions').removeClass('hide');
3549                $(this).removeClass('process').text(lang.booking_button_change_period);
3550                $('#cancel_changes').fadeOut();
3551                $('#booking_calendar').addClass('disabled loading');
3552
3553                if (bookingConfigs.Booking_type === 'date_time_range') {
3554                    var $checkInCont  = $('.check-in [name="f[booking_check_in]"]'),
3555                        $checkOutCont = $('.check-out [name="f[booking_check_out]"]');
3556
3557                    let checkin_time = $checkInCont.length && $checkInCont.val() !== '0'
3558                    && $checkInCont.val() !== bookingConfigs.check_in
3559                        ? $checkInCont.val()
3560                        : bookingConfigs.check_in;
3561
3562                    let checkInTimeHuman = $checkInCont.length && $checkInCont.val() !== '0'
3563                        ? $checkInCont.find('option:selected').html()
3564                        : bookingConfigs.check_in
3565
3566                    bookingConfigs.check_in = checkin_time;
3567
3568                    if (bookingConfigs.start_date_value) {
3569                        let start_date = checkInTimeHuman
3570                            ? '<b>' + bookingConfigs.start_date_value + '</b> - ' + checkInTimeHuman
3571                            : '<b>' + bookingConfigs.start_date_value + '</b>';
3572
3573                        $('#area_booking .field.start_date').html(start_date);
3574                    }
3575
3576                    let checkout_time = $checkOutCont.length && $checkOutCont.val() !== '0'
3577                        && $checkOutCont.val() !== bookingConfigs.check_out
3578                            ? $checkOutCont.val()
3579                            : bookingConfigs.check_out;
3580
3581                    let checkOutTimeHuman = $checkOutCont.length && $checkOutCont.val() !== '0'
3582                        ? $checkOutCont.find('option:selected').html()
3583                        : bookingConfigs.check_out
3584
3585                    bookingConfigs.check_out = checkout_time;
3586
3587                    if (bookingConfigs.end_date_value) {
3588                        let end_date = checkOutTimeHuman
3589                            ? '<b>' + bookingConfigs.end_date_value + '</b> - ' + checkOutTimeHuman
3590                            : '<b>' + bookingConfigs.end_date_value + '</b>';
3591
3592                        $('#area_booking .field.end_date').html(end_date);
3593                    }
3594
3595                    if (bookingConfigs.check_in !== bookingConfigs.listing_check_in
3596                        || bookingConfigs.check_out !== bookingConfigs.listing_check_out
3597                    ) {
3598                        bookingConfigs.period_updated = true;
3599                    }
3600                } else if (bookingConfigs.Booking_type === 'time_range') {
3601                    // Move selectors to initial place
3602                    $('[name="escort_rates"]').detach().appendTo($('#escort_rates_obj'));
3603                    $('[name="f[booking_check_out]"]').detach().appendTo($('#booking_checkout_field'));
3604                    $('[name="escort_duration"]').detach().appendTo($('#escort_duration_obj'));
3605
3606                    if (bookingConfigs.start_date_value) {
3607                        $('#area_booking .field.start_date').html('<b>' + bookingConfigs.start_date_value + '</b>');
3608                    }
3609
3610                    if (bookingConfigs.escort_type_service_title) {
3611                        $('#area_booking .field.type').html(bookingConfigs.escort_type_service_title);
3612                    }
3613
3614                    if (bookingConfigs.escortTimeServiceValue) {
3615                        $('#area_booking .field.time').html(bookingConfigs.escortTimeServiceValue);
3616                    }
3617
3618                    if (bookingConfigs.escort_duration_title) {
3619                        $('#area_booking .field.duration').html(bookingConfigs.escort_duration_title);
3620                    }
3621
3622                    $('#area_booking').removeClass('request-details');
3623
3624                    bookingConfigs.period_updated = true;
3625                }
3626            } else {
3627                $('#owner_actions').addClass('hide');
3628                $(this).text(lang.save).addClass('process');
3629                $(this).next().fadeIn('fast');
3630
3631                if (bookingConfigs.Booking_type === 'time_range') {
3632                    var $bookingDetailsContainer = $('#booking_details'),
3633                        $typeContainer           = $('[name="escort_rates"]'),
3634                        $timeContainer           = $('[name="f[booking_check_out]"]'),
3635                        $durationContainer       = $('[name="escort_duration"]');
3636
3637                    // Add padding in details
3638                    $('#area_booking').addClass('request-details');
3639
3640                    // Add "Change date" button to "Date Of Service" field
3641                    var html = '<a id="change_date_button" href="javascript:void(0)">';
3642                    html += lang.booking_button_select_date + '</a>';
3643
3644                    $bookingDetailsContainer.find('div.field.start_date')
3645                        .html(trim($bookingDetailsContainer.find('div.field.start_date').html()) + html);
3646
3647                    $('#change_date_button').click(function() {
3648                        self.escortChangeDate();
3649                    });
3650
3651                    // Add escort rates to "Type Of Service" field
3652                    if (bookingConfigs.check_in) {
3653                        $typeContainer.find('option[value="' + bookingConfigs.check_in + '"]').attr('selected', true);
3654                        $typeContainer = $typeContainer.detach();
3655                        $typeContainer.appendTo($bookingDetailsContainer.find('div.field.type').html(''));
3656                    }
3657
3658                    // Add checkout-time selector to "Time Of Service" field
3659                    if (bookingConfigs.check_out) {
3660                        self.updateEscortAvailabilityData();
3661
3662                        $timeContainer.find('option[value="' + bookingConfigs.check_out + '"]').attr('selected', true);
3663                        $timeContainer.removeClass('disabled').removeAttr('disabled');
3664                        $timeContainer = $timeContainer.detach();
3665                        $timeContainer.appendTo($bookingDetailsContainer.find('div.field.time').html(''));
3666                    }
3667
3668                    // Add hours selector to "Duration (Hours)" field
3669                    if (bookingConfigs.db_end) {
3670                        $durationContainer.find('option[value="' + bookingConfigs.db_end + '"]').attr('selected', true);
3671                        $durationContainer.removeClass('disabled').removeAttr('disabled');
3672                    }
3673                    $durationContainer = $durationContainer.detach();
3674
3675                    if (!bookingConfigs.Booking_repeatedly) {
3676                        $durationContainer = $('<div>', {class: 'hourly-time'}).append(
3677                            $('<div>', {class: 'view'}).text(lang.not_available),
3678                            $('<div>', {class: 'origin hide'}).append($durationContainer)
3679                        )
3680                    }
3681
3682                    $durationContainer.appendTo($bookingDetailsContainer.find('div.field.duration').html(''));
3683
3684                    self.escortFieldsValidateHandler();
3685                    self.escortAmountHandler();
3686
3687                    // Hide unavailable values in "Duration" field
3688                    var hours_in_type = parseInt($typeContainer.find('option:selected').data('hours'));
3689
3690                    $durationContainer.find('option').each(function(){
3691                        var option_value = parseInt($(this).attr('value'));
3692
3693                        if (option_value > 0 && option_value % hours_in_type !== 0) {
3694                            $(this).attr('disabled', true).addClass('hide');
3695                        }
3696                    });
3697
3698                    $('div.field.duration').closest('div.table-cell').removeClass('hide');
3699                    $typeContainer.change();
3700                } else {
3701                    self.getDates(bookingConfigs.listing_id, null, true);
3702                }
3703            }
3704        });
3705    };
3706
3707    /**
3708     * Revert data of request to initial
3709     */
3710    this.revertRequestChanges = function() {
3711        if (tmpBookingConfigs['db_start'] && tmpBookingConfigs['db_end']) {
3712            $('#cancel_changes').click(function(){
3713                bookingConfigs['db_start']                  = tmpBookingConfigs['db_start'];
3714                bookingConfigs['start_date_value']          = tmpBookingConfigs['start_date_value'];
3715                bookingConfigs['db_end']                    = tmpBookingConfigs['db_end'];
3716                bookingConfigs['end_date_value']            = tmpBookingConfigs['end_date_value'];
3717                bookingConfigs['total_cost']                = tmpBookingConfigs['total_cost'];
3718                bookingConfigs['check_in']                  = tmpBookingConfigs['check_in'];
3719                bookingConfigs['check_out']                 = tmpBookingConfigs['check_out']
3720                bookingConfigs['escort_duration']           = tmpBookingConfigs['escort_duration'];
3721                bookingConfigs['escort_duration_title']     = tmpBookingConfigs['escort_duration_title'];
3722                bookingConfigs['escort_type_service']       = tmpBookingConfigs['escort_type_service'];
3723                bookingConfigs['escort_type_service_title'] = tmpBookingConfigs['escort_type_service_title'];
3724                bookingConfigs['escort_time_service']       = tmpBookingConfigs['escort_time_service'];
3725
3726                self.getDates(bookingConfigs['listing_id']);
3727                self.stepsHandler('update_period', 'update');
3728
3729                if (bookingConfigs['Booking_type'] == 'date_time_range') {
3730                    if (bookingConfigs['start_date_value']) {
3731                        var start_date = bookingConfigs['check_in']
3732                        ? '<b>' + bookingConfigs['start_date_value'] + '</b> - ' + bookingConfigs['check_in']
3733                        : '<b>' + bookingConfigs['start_date_value'] + '</b>';
3734
3735                        $('#area_booking .field.start_date').html(start_date);
3736                    }
3737
3738                    if (bookingConfigs['end_date_value']) {
3739                        var end_date = bookingConfigs['check_out']
3740                        ? '<b>' + bookingConfigs['end_date_value'] + '</b> - ' + bookingConfigs['check_out']
3741                        : '<b>' + bookingConfigs['end_date_value'] + '</b>';
3742
3743                        $('#area_booking .field.end_date').html(end_date);
3744                    }
3745                } else if (bookingConfigs['Booking_type'] == 'time_range') {
3746                    // move selectors to inital place
3747                    $('[name="escort_rates"]').detach().appendTo($('#escort_rates_obj'));
3748                    $('[name="f[booking_check_out]"]').detach().appendTo($('#booking_checkout_field'));
3749                    $('[name="escort_duration"]').detach().appendTo($('#escort_duration_obj'));
3750
3751                    if (bookingConfigs['start_date_value']) {
3752                        $('#area_booking .field.start_date').html('<b>' + bookingConfigs['start_date_value'] + '</b>');
3753                    }
3754
3755                    if (bookingConfigs['escort_type_service_title']) {
3756                        $('#area_booking .field.type').html(bookingConfigs['escort_type_service_title']);
3757                    }
3758
3759                    if (bookingConfigs['escort_time_service']) {
3760                        $('#area_booking .field.time').html(bookingConfigs['escort_time_service']);
3761                    }
3762
3763                    if (bookingConfigs['escort_duration_title']) {
3764                        $('#area_booking .field.duration').html(bookingConfigs['escort_duration_title']);
3765                    }
3766
3767                    $('#area_booking').removeClass('request-details');
3768                }
3769
3770                $('#change_period').text(lang['booking_button_change_period']).removeClass('process');
3771                $('#cancel_changes').fadeOut();
3772                $('#owner_actions').removeClass('hide');
3773            });
3774        }
3775    };
3776
3777    /**
3778     * Find first option without "disabled" attribute and select it
3779     * @param {object} $container
3780     */
3781    this.selectFirstActiveOption = function($container) {
3782        if ($container.length < 0) {
3783            return false;
3784        }
3785
3786        // remove selection from active option
3787        $container.find('option:selected').removeAttr('selected').removeProp('selected');
3788
3789        // select first active option
3790        $container.find('option').each(function(){
3791            if (!$(this).prop('disabled') && $(this).val() != '-1' && !$(this).hasClass('hide')) {
3792                $(this).prop('selected', true);
3793                return false;
3794            }
3795        });
3796
3797        $container.trigger('change');
3798    };
3799
3800    /**
3801     * Convert date by selected timezone
3802     * @param  {int}    timestamp
3803     * @param  {string} custom_format
3804     * @return {string}               - Converted date
3805     */
3806    this.getDateByTimeZone = function(timestamp, custom_format) {
3807        if (!timestamp) {
3808            return '';
3809        }
3810
3811        var format = custom_format
3812        ? custom_format
3813        : bookingConfigs['bookingDateFormat'].toUpperCase().replace(/%/g, '').replace('B', 'MMM');
3814
3815        if (timestamp && format) {
3816            return moment.tz(new Date(timestamp * 1000), bookingConfigs.bookingTimeZone).format(format);
3817        }
3818    };
3819
3820    /**
3821     * Remove decimals and convert price to short forms (1k, 1m, 1b)
3822     *
3823     * @since 3.2.0 Rename method from "adaptNumbersinPrice" to "adaptNumbersInPrice"
3824     * @since 3.1.0
3825     *
3826     * @param  {float} price
3827     * @return {int}         - Adapted price
3828     */
3829    this.adaptNumbersInPrice = function(price) {
3830        if (!price) {
3831            return 0;
3832        }
3833
3834        if (typeof $.convertPrice == 'function') {
3835            return price;
3836        }
3837
3838        price         = parseInt(price);
3839        var price_val = price.toString();
3840        var separator = Math.floor(price_val.length/3);
3841
3842        separator -= price_val.length % 3 == 0 ? 1 : 0;
3843
3844        if (separator > 0) {
3845            var sign, short;
3846
3847            if (separator == 1) {
3848                sign = lang['booking_short_price_k'];
3849                short = price / 1000;
3850            } else if (separator == 2) {
3851                sign = lang['booking_short_price_m'];
3852                short = price / 1000000;
3853            } else if (separator > 2) {
3854                sign = lang['booking_short_price_b'];
3855                short = price / 1000000000;
3856            }
3857
3858            if (sign) {
3859                short = Math.round(short * 10)/10;
3860                price = short + sign;
3861            }
3862        }
3863
3864        return price;
3865    };
3866
3867    /**
3868     * Get title of day by current localization
3869     *
3870     * @since 3.2.0
3871     *
3872     * @param bindCheckin
3873     * @param weekDays
3874     * @returns {string}
3875     */
3876    this.getBindDaysByLocale = function (bindCheckin, weekDays) {
3877        bindCheckin = bindCheckin.split(',');
3878
3879        if (!bindCheckin) {
3880            return '';
3881        }
3882
3883        let bindLocale = '';
3884
3885        for (let dayIndex in bindCheckin) {
3886            if (!bindCheckin.hasOwnProperty(dayIndex)) {
3887                continue;
3888            }
3889
3890            switch (bindCheckin[dayIndex]) {
3891                case 'Mon':
3892                    bindLocale += (bindLocale ? ', ' : '') + weekDays[1];
3893                    break;
3894                case 'Tue':
3895                    bindLocale += (bindLocale ? ', ' : '') + weekDays[2];
3896                    break;
3897                case 'Wed':
3898                    bindLocale += (bindLocale ? ', ' : '') + weekDays[3];
3899                    break;
3900                case 'Thu':
3901                    bindLocale += (bindLocale ? ', ' : '') + weekDays[4];
3902                    break;
3903                case 'Fri':
3904                    bindLocale += (bindLocale ? ', ' : '') + weekDays[5];
3905                    break;
3906                case 'Sat':
3907                    bindLocale += (bindLocale ? ', ' : '') + weekDays[6];
3908                    break;
3909                case 'Sun':
3910                    bindLocale += (bindLocale ? ', ' : '') + weekDays[7];
3911                    break;
3912            }
3913        }
3914
3915        return bindLocale;
3916    }
3917
3918    /**
3919     * Load all external JS libraries (momentJS etc.) asynchronously
3920     * @since 3.3.0
3921     * @param {function} callback
3922     */
3923    this.loadExternalScripts = function (callback) {
3924        flUtil.loadScript(rlConfig.plugins_url + 'booking/static/moment.min.js', function () {
3925            flUtil.loadScript(rlConfig.plugins_url + 'booking/static/moment-timezone-with-data.min.js', function () {
3926                if (callback && typeof callback === 'function') {
3927                    callback.call(this);
3928                }
3929            });
3930        });
3931    }
3932
3933    /**
3934     * Get count of days between 2 timestamps in days
3935     * @param {timestamp} date1
3936     * @param {timestamp} date2
3937     * @return {number}
3938     */
3939    this.getCountDaysBetween2Timestamps = function(date1, date2) {
3940        if (!date1 || !date2) {
3941            return 0;
3942        }
3943
3944        date1 = moment.tz(new Date(date1 * 1000), bookingConfigs.bookingTimeZone);
3945        date2 = moment.tz(new Date(date2 * 1000), bookingConfigs.bookingTimeZone);
3946        return Math.abs(date1.diff(date2, 'days'));
3947    }
3948
3949    /**
3950     * Add count of days to date in timestamp format
3951     * @since 3.3.0
3952     * @param {timestamp} timestamp
3953     * @param {number} days
3954     * @return {timestamp}
3955     */
3956    this.addDaysToTimestamp = function(timestamp, days = 1) {
3957        return self.modifyTimestamp(timestamp, days, '+');
3958    }
3959
3960    /**
3961     * Subtract count of days to date in timestamp format
3962     * @since 3.3.0
3963     * @param {timestamp} timestamp
3964     * @param {number} days
3965     * @return {timestamp}
3966     */
3967    this.subtractDaysFromTimestamp = function(timestamp, days = 1) {
3968        return self.modifyTimestamp(timestamp, days, '-');
3969    }
3970
3971    /**
3972     * Add/subtract count of days to date in timestamp format
3973     * @since 3.3.0
3974     * @param {timestamp} timestamp
3975     * @param {number} days
3976     * @param {string} action
3977     * @return {timestamp}
3978     */
3979    this.modifyTimestamp = function(timestamp, days, action = '+') {
3980        if (!timestamp) {
3981            return timestamp;
3982        }
3983
3984        if (action === '+') {
3985            return moment.tz(new Date(timestamp * 1000), bookingConfigs.bookingTimeZone).add(days, 'days').unix();
3986        } else {
3987            return moment.tz(new Date(timestamp * 1000), bookingConfigs.bookingTimeZone).subtract(days, 'days').unix();
3988        }
3989    }
3990
3991    /**
3992     * Initialization process of booking section (add/edit listing)
3993     */
3994    this.init = function() {
3995        self.rateParentTag       = bookingConfigs && bookingConfigs.isAdmin ? 'tr' : 'div';
3996        self.rateChildTag        = bookingConfigs && bookingConfigs.isAdmin ? 'td' : 'div';
3997        self.$checkInSection     = $('#booking_checkin_field');
3998        self.$checkOutSection    = $('#booking_checkout_field');
3999        self.$bookingRadioButton = $('[name="f[booking_module]"]');
4000        self.$bookingSection     = self.$bookingRadioButton.parent().parent().parent();
4001
4002        var $rates     = $('.rates-field-container .rates');
4003        self.rateIndex = $rates.length ? $rates.length : 1;
4004
4005        self.bookingSectionHandler();
4006    }
4007};
4008
4009var booking = new bookingClass();
4010
4011function in_array(needle,haystack,argStrict){var key='',strict=!!argStrict;if(strict){for(key in haystack){if(haystack[key]===needle){return true;}}}else{for(key in haystack){if(haystack[key]==needle){return true;}}}return false;}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.