PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.7.1
VikBooking Hotel Booking Engine & PMS v1.7.1
1.8.16 1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 All 37 releases
vikbooking / admin / views / tableaux / tmpl / default.php

default.php in VikBooking Hotel Booking Engine & PMS 1.7.1, at admin/views/tableaux/tmpl/default.php

1,495 lines 60.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikBooking
4 * @subpackage com_vikbooking
5 * @author Alessio Gaggii - e4j - Extensionsforjoomla.com
6 * @copyright Copyright (C) 2018 e4j - Extensionsforjoomla.com. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 * @link https://vikwp.com
9 */
10
11 defined('ABSPATH') or die('No script kiddies please!');
12
13 $roomids = $this->roomids;
14 $rooms = $this->rooms;
15 $rooms_busy = $this->rooms_busy;
16 $months = $this->months;
17 $fromts = $this->fromts;
18 $tots = $this->tots;
19
20 $vbo_app = VikBooking::getVboApplication();
21 $vbo_app->loadSelect2();
22 $vbo_app->loadDatePicker();
23
24 JHtml::fetch('script', VBO_ADMIN_URI . 'resources/js_upload/jquery.stickytableheaders.min.js');
25
26 $nowdf = VikBooking::getDateFormat(true);
27 if ($nowdf == "%d/%m/%Y") {
28 $df = 'd/m/Y';
29 } elseif ($nowdf == "%m/%d/%Y") {
30 $df = 'm/d/Y';
31 } else {
32 $df = 'Y/m/d';
33 }
34 $datesep = VikBooking::getDateSeparator(true);
35 $juidf = $nowdf == "%d/%m/%Y" ? 'dd/mm/yy' : ($nowdf == "%m/%d/%Y" ? 'mm/dd/yy' : 'yy/mm/dd');
36 $currencysymb = VikBooking::getCurrencySymb(true);
37
38 $month = VikRequest::getInt('month', 0, 'request');
39 $printtableaux = VikRequest::getInt('printtableaux', 0, 'request');
40
41 $colortags = VikBooking::loadBookingsColorTags();
42
43 $days_labels = array(
44 JText::translate('VBSUN'),
45 JText::translate('VBMON'),
46 JText::translate('VBTUE'),
47 JText::translate('VBWED'),
48 JText::translate('VBTHU'),
49 JText::translate('VBFRI'),
50 JText::translate('VBSAT')
51 );
52 $months_labels = array(
53 JText::translate('VBMONTHONE'),
54 JText::translate('VBMONTHTWO'),
55 JText::translate('VBMONTHTHREE'),
56 JText::translate('VBMONTHFOUR'),
57 JText::translate('VBMONTHFIVE'),
58 JText::translate('VBMONTHSIX'),
59 JText::translate('VBMONTHSEVEN'),
60 JText::translate('VBMONTHEIGHT'),
61 JText::translate('VBMONTHNINE'),
62 JText::translate('VBMONTHTEN'),
63 JText::translate('VBMONTHELEVEN'),
64 JText::translate('VBMONTHTWELVE')
65 );
66 $long_months_labels = $months_labels;
67 foreach ($months_labels as $i => $v) {
68 $months_labels[$i] = function_exists('mb_substr') ? mb_substr($v, 0, 3, 'UTF-8') : substr($v, 0, 3);
69 }
70
71 ?>
72 <div class="vbo-tableaux-container">
73 <form name="adminForm" action="index.php?option=com_vikbooking&task=tableaux" method="post" id="adminForm">
74 <div class="vbo-tableaux-months-wrap">
75 <?php
76 foreach ($months as $mdata) {
77 ?>
78 <div class="vbo-tableaux-month-cont">
79 <div class="vbo-tableaux-month-link<?php echo $month == $mdata['from'] ? ' vbo-tableaux-month-link-active' : ''; ?>">
80 <span class="vbo-tableaux-selmonth" data-ts="<?php echo $mdata['from']; ?>" data-from="<?php echo date($df, $mdata['from']); ?>" data-to="<?php echo date($df, $mdata['to']); ?>"><?php echo $mdata['name']; ?></span>
81 </div>
82 </div>
83 <?php
84 }
85 ?>
86 </div>
87 <div id="filter-bar" class="btn-toolbar vbo-btn-toolbar vbo-tableaux-filters" style="width: 100%; display: inline-block;">
88 <div class="btn-group pull-left" style="font-size: 12px;">
89 <select id="roomsel" name="roomids[]" multiple="multiple">
90 <?php
91 foreach ($this->allrooms as $rid => $rdata) {
92 ?>
93 <option value="<?php echo $rid; ?>"<?php echo in_array($rid, $roomids) ? ' selected="selected"' : ''; ?>><?php echo $rdata['name']; ?></option>
94 <?php
95 }
96
97 /**
98 * Use the optgroup to separate rooms from categories in the
99 * drop down menu, only if allrooms > 1 e categories > 1.
100 *
101 * @since 1.13.5
102 */
103 if (count($this->allrooms) > 1 && count($this->categories) > 1) {
104 ?>
105 <optgroup label="<?php echo addslashes(JText::translate('VBOCATEGORYFILTER')); ?>">
106 <?php
107 foreach ($this->categories as $cat) {
108 // we use negative values for the IDs of the categories
109 $cat_id = ($cat['id'] - ($cat['id'] * 2));
110 ?>
111 <option value="<?php echo $cat_id; ?>"><?php echo (in_array($cat['id'], $this->reqcats) ? '- ' : '') . $cat['name']; ?></option>
112 <?php
113 }
114 ?>
115 </optgroup>
116 <?php
117 }
118 //
119 ?>
120 </select>
121 </div>
122 <div class="btn-group pull-left">
123 <span style="font-size: 15px;">&nbsp;</span>
124 </div>
125 <div class="btn-group pull-left input-append">
126 <input type="text" id="vbo-date-from" placeholder="<?php echo JText::translate('VBNEWSEASONONE'); ?>" value="<?php echo date($df, $fromts); ?>" size="14" name="tbxfromdate" onfocus="this.blur();" />
127 <button type="button" class="btn" id="vbo-date-from-trig"><i class="icon-calendar"></i></button>
128 </div>
129 <div class="btn-group pull-left input-append">
130 <input type="text" id="vbo-date-to" placeholder="<?php echo JText::translate('VBNEWSEASONTWO'); ?>" value="<?php echo date($df, $tots); ?>" size="14" name="tbxtodate" onfocus="this.blur();" />
131 <button type="button" class="btn" id="vbo-date-to-trig"><i class="icon-calendar"></i></button>
132 </div>
133 <div class="btn-group pull-left">
134 <span style="font-size: 15px;">&nbsp;</span>
135 </div>
136 <div class="btn-group pull-left">
137 <button type="submit" class="btn"><i class="icon-search"></i> <?php echo JText::translate('VBAPPLY'); ?></button>
138 </div>
139 <div class="btn-group pull-right">
140 <a href="javascript: void(0);" onclick="VBOCore.emitEvent('vbo-tool-permissions-modal-toggle');" class="vbo-perms-operators"><i class="vboicn-user-plus icn-nomargin"></i> <span><?php echo JText::translate('VBOPERMSOPERATORS'); ?></span></a>
141 </div>
142 <div class="btn-group pull-right">
143 <a href="javascript: void(0);" onclick="vboSendPrintTableaux();" class="vbcsvexport"><?php VikBookingIcons::e('print'); ?> <span><?php echo JText::translate('VBOPRINT'); ?></span></a>
144 </div>
145 </div>
146 <input type="hidden" name="month" id="vbo-month" value="<?php echo $month; ?>" />
147 <input type="hidden" name="task" value="tableaux" />
148 <input type="hidden" name="option" value="com_vikbooking" />
149 </form>
150
151 <?php
152 /**
153 * Render the permissions layout to manage the permissions of
154 * a specific tool, tableaux, for all the operators.
155 *
156 * @since 1.16.9 (J) - 1.6.9 (WP)
157 */
158 $layout_data = [
159 'tool' => 'tableaux',
160 ];
161 // render the permissions layout
162 echo JLayoutHelper::render('operators.permissions', $layout_data, null, [
163 'component' => 'com_vikbooking',
164 'client' => 'administrator',
165 ]);
166
167 // count the cells per month
168 $thead_info = array();
169 $months_days = array();
170 $nowinfo = getdate($fromts);
171 while ($nowinfo[0] <= $tots) {
172 $mkey = $nowinfo['mon'] . '_' . $nowinfo['year'];
173 if (!isset($months_days[$mkey])) {
174 $months_days[$mkey] = 0;
175 }
176 $months_days[$mkey]++;
177 // push information for the table head
178 array_push($thead_info, array(
179 'mon' => $long_months_labels[($nowinfo['mon'] - 1)],
180 'mday' => $nowinfo['mday'],
181 'wday' => $days_labels[$nowinfo['wday']],
182 'year' => $nowinfo['year'],
183 'ts' => $nowinfo[0],
184 ));
185 // next loop
186 $nowinfo = getdate(mktime(0, 0, 0, $nowinfo['mon'], ($nowinfo['mday'] + 1), $nowinfo['year']));
187 }
188
189 // today
190 $todaydt = date('Y-m-d');
191 ?>
192
193 <div class="vbo-tableaux-tbl-container">
194 <div class="vbo-table-responsive vbo-tableaux-tbl-wrap" id="vbo-tableaux-table-scroller">
195 <table class="vbo-table vbo-tableaux-table">
196 <thead class="vbo-tableaux-table-head" style="display: none;">
197 <tr class="vbo-tableaux-table-head-row">
198 <th class="vbo-tableaux-table-head-cell vbo-tableaux-table-head-cell-empty">&nbsp;</th>
199 <?php
200 foreach ($thead_info as $head_day) {
201 ?>
202 <th class="vbo-tableaux-table-head-cell">
203 <div class="vbo-tableaux-table-head-cell-monyear">
204 <span><?php echo $head_day['mon'] . ' ' . $head_day['year']; ?></span>
205 </div>
206 <div class="vbo-tableaux-table-head-cell-wmday">
207 <span><?php echo $head_day['wday'] . ' ' . $head_day['mday']; ?></span>
208 </div>
209 </th>
210 <?php
211 }
212 ?>
213 </tr>
214 </thead>
215 <tbody>
216 <tr class="vbo-tableaux-monthsrow">
217 <td class="vbo-tableaux-emptycell">&nbsp;</td>
218 <?php
219 foreach ($months_days as $mkey => $totdays) {
220 $mkeyparts = explode('_', $mkey);
221 ?>
222 <td class="vbo-tableaux-monthcell" colspan="<?php echo $totdays; ?>"><?php echo $long_months_labels[($mkeyparts[0] - 1)] . ' ' . $mkeyparts[1]; ?></td>
223 <?php
224 }
225 ?>
226 </tr>
227 <tr class="vbo-tableaux-firstrow">
228 <td class="vbo-tableaux-emptycell">&nbsp;</td>
229 <?php
230 $newmonth = 0;
231 $nowinfo = getdate($fromts);
232 while ($nowinfo[0] <= $tots) {
233 $nowinfo_ymd = date('Y-m-d', $nowinfo[0]);
234 $monthclass = !empty($newmonth) && $nowinfo['mon'] != $newmonth ? ' vbo-tableaux-newmonthcell' : '';
235 $monthclass = $nowinfo_ymd == $todaydt ? ' vbo-tableaux-todaycell' : $monthclass;
236 $newmonth = $nowinfo['mon'];
237 $read_day = $days_labels[$nowinfo['wday']] . ' ' . $nowinfo['mday'] . ' ' . $months_labels[($nowinfo['mon'] - 1)] . ' ' . $nowinfo['year'];
238 $fest_class = isset($this->festivities[$nowinfo_ymd]) ? ' vbo-tableaux-festcell' : '';
239 ?>
240 <td class="vbo-tableaux-daycell<?php echo $monthclass . $fest_class; ?>"<?php echo $printtableaux > 0 ? ' width="100' : ''; ?> data-ymd="<?php echo $nowinfo_ymd; ?>" data-readymd="<?php echo $read_day; ?>">
241 <div class="vbo-tableaux-day-cont">
242 <span class="vbo-tableaux-day-wday"><?php echo $days_labels[$nowinfo['wday']]; ?></span>
243 <span class="vbo-tableaux-day-mday"><?php echo $nowinfo['mday']; ?></span>
244 </div>
245 </td>
246 <?php
247 // next loop
248 $nowinfo = getdate(mktime(0, 0, 0, $nowinfo['mon'], ($nowinfo['mday'] + 1), $nowinfo['year']));
249 }
250 ?>
251 </tr>
252 <?php
253 $rooms_features_map = array();
254 $rooms_features_bookings = array();
255 $colortags_map = array();
256 foreach ($rooms as $rid => $rdata) {
257 // distinctive features
258 $room_params = !empty($rdata['params']) ? json_decode($rdata['params'], true) : array();
259 if (is_array($room_params) && array_key_exists('features', $room_params) && count($room_params['features']) > 0) {
260 $rooms_features_map[$rid] = array();
261 foreach ($room_params['features'] as $rind => $rfeatures) {
262 foreach ($rfeatures as $fname => $fval) {
263 if (strlen($fval)) {
264 $rooms_features_map[$rid][$rind] = '#'.$fval;
265 break;
266 }
267 }
268 }
269 if (!(count($rooms_features_map[$rid]) > 0)) {
270 unset($rooms_features_map[$rid]);
271 }
272 }
273 // extra class for single-unit rooms
274 $room_extra_class = $rdata['units'] == 1 ? 'vbo-tableaux-booking-singleunit ' : '';
275 ?>
276 <tr class="vbo-tableaux-roomrow">
277 <td class="vbo-tableaux-roomname" data-roomid="<?php echo $rdata['id']; ?>"><?php echo $rdata['name']; ?></td>
278 <?php
279 $nowinfo = getdate($fromts);
280 $bookbuffer = array();
281 $prevbuffer = array();
282 $positions = array();
283 $newmonth = 0;
284 while ($nowinfo[0] <= $tots) {
285 $nowinfo_ymd = date('Y-m-d', $nowinfo[0]);
286 $monthclass = !empty($newmonth) && $nowinfo['mon'] != $newmonth ? ' vbo-tableaux-newmonthcell' : '';
287 $monthclass = $nowinfo_ymd == $todaydt ? ' vbo-tableaux-todaycell' : $monthclass;
288 $newmonth = $nowinfo['mon'];
289
290 /**
291 * Critical dates defined at room-day level, or for any sub-unit.
292 *
293 * @since 1.13.5
294 */
295 $rdaynote_class = ' vbo-roomdaynote-empty';
296 $rdaynote_icn = 'far fa-sticky-note';
297 $cell_rdnotes = '';
298 $rdnkeys_lookup = range(0, $rdata['units']);
299 $rdaynote_keyids = array();
300 $glob_rdn_keyid = $nowinfo_ymd . '_' . $rdata['id'] . '_0';
301 // find room-day note keys with some notes (room-day level or subroom-day level)
302 foreach ($rdnkeys_lookup as $lookup_index) {
303 $rdaynote_keyid = $nowinfo_ymd . '_' . $rdata['id'] . '_' . $lookup_index;
304 if (isset($this->rdaynotes[$rdaynote_keyid])) {
305 // push associative index with notes
306 $rdaynote_keyids[$lookup_index] = $rdaynote_keyid;
307 }
308 }
309 if (count($rdaynote_keyids)) {
310 // some notes exist for this combination of date, room ID and subunit
311 $rdaynote_class = ' vbo-roomdaynote-full';
312 $rdaynote_icn = 'sticky-note';
313 /**
314 * Try to populate the notes for this room-day cell
315 * only if the previous day does not have the same note.
316 * Just the first readable room-day cell should have notes.
317 */
318 $notes_titles = array();
319 $yesterday_ts = mktime(0, 0, 0, $nowinfo['mon'], ($nowinfo['mday'] - 1), $nowinfo['year']);
320 $yesterday_ymd = date('Y-m-d', $yesterday_ts);
321 // loop through all the keys with notes
322 foreach ($rdaynote_keyids as $lookup_index => $rdaynote_keyid) {
323 $yesterday_keyid = $yesterday_ymd . '_' . $rdata['id'] . '_' . $lookup_index;
324 foreach ($this->rdaynotes[$rdaynote_keyid]['info'] as $today_note) {
325 // only manual (custom) room-day notes will be displayed
326 $display_note = ($today_note->type == 'custom');
327 if (isset($this->rdaynotes[$yesterday_keyid])) {
328 // make sure the same note is not present for yesterday
329 foreach ($this->rdaynotes[$yesterday_keyid]['info'] as $yesterday_note) {
330 if ($today_note->type == $yesterday_note->type && $today_note->name == $yesterday_note->name) {
331 // same note available also for yesterday
332 $display_note = false;
333 break;
334 }
335 }
336 }
337 if ($display_note) {
338 // push just the name of the note
339 array_push($notes_titles, $today_note->name);
340 }
341 }
342 }
343 // separate all notes (if any) with comma
344 $cell_rdnotes = implode(', ', $notes_titles);
345 //
346 }
347 //
348
349 ?>
350 <td class="vbo-tableaux-roombooks<?php echo $monthclass . $rdaynote_class; ?>" data-day="<?php echo $nowinfo_ymd; ?>" data-dayrid="<?php echo $nowinfo_ymd . $rdata['id']; ?>" data-rdnkeys="<?php echo implode(',', array_keys($rdaynote_keyids)); ?>" data-notes="<?php echo $this->escape($cell_rdnotes); ?>">
351 <?php
352 $room_bookings = array();
353 if (isset($rooms_busy[$rid])) {
354 foreach ($rooms_busy[$rid] as $b) {
355 $tmpone = getdate($b['checkin']);
356 $ritts = mktime(0, 0, 0, $tmpone['mon'], $tmpone['mday'], $tmpone['year']);
357 $tmptwo = getdate($b['checkout']);
358 $conts = mktime(0, 0, 0, $tmptwo['mon'], $tmptwo['mday'], $tmptwo['year']);
359 $b['basefrom'] = $ritts;
360 $b['baseto'] = $conts;
361 if ($nowinfo[0] >= $ritts && $nowinfo[0] <= $conts) {
362 array_push($room_bookings, $b);
363 }
364 }
365 /**
366 * By default, we attempt to sort the bookings for this day by room index.
367 *
368 * @since 1.13.5 (J) - 1.3.5 (WP)
369 */
370 if (count($room_bookings)) {
371 $rindexes_map = array();
372 foreach ($room_bookings as $rbk => $rbval) {
373 $rindexes_map[$rbk] = isset($rbval['indexes']) ? $rbval['indexes'] : '';
374 }
375 asort($rindexes_map);
376 $room_bookings_copy = array();
377 foreach ($rindexes_map as $rbk => $rbval) {
378 array_push($room_bookings_copy, $room_bookings[$rbk]);
379 }
380 $room_bookings = $room_bookings_copy;
381 }
382 //
383 }
384 if (count($room_bookings) && count($bookbuffer)) {
385 // sort bookings according to the previous map
386 $newbookings = array();
387 foreach ($bookbuffer as $oid) {
388 foreach ($room_bookings as $kb => $rbook) {
389 if ($oid == $rbook['idorder']) {
390 // get this key first
391 $newbookings[$kb] = $rbook;
392 // do not break the loop as there could be multiple idorder, just continue
393 continue;
394 }
395 }
396 }
397 if (count($newbookings)) {
398 // merge array by keys by unsetting double keys from second array
399 $room_bookings = $newbookings + $room_bookings;
400 }
401 // copy buffer before reset to see whether this is the first cell displayed for the booking
402 $prevbuffer = $bookbuffer;
403 // reset buffer to fill the current day bookings
404 $bookbuffer = array();
405 }
406 $indexpos = 0;
407 foreach ($room_bookings as $k => $rbook) {
408 $contclass = 'stay';
409 if ($rbook['basefrom'] == $nowinfo[0]) {
410 $contclass = 'checkin';
411 } elseif ($rbook['baseto'] == $nowinfo[0]) {
412 $contclass = 'checkout';
413 }
414 $shortstaycls = '';
415 if (ceil(($rbook['baseto'] - $rbook['basefrom']) / 86400) < 2) {
416 $shortstaycls = ' vbo-tableaux-booking-short';
417 }
418 //check position
419 $pos = $indexpos;
420 if (isset($positions[$rbook['idorder']]) && $indexpos < $positions[$rbook['idorder']]) {
421 // print empty blocks to give the right position to this booking
422 $pos = $indexpos.'-'.$positions[$rbook['idorder']];
423 $looplim = ($positions[$rbook['idorder']] - $indexpos);
424 for ($i = 0; $i < $looplim; $i++) {
425 $indexpos++;
426 ?>
427 <div class="<?php echo $room_extra_class; ?>vbo-tableaux-booking vbo-tableaux-booking-empty">
428 <span>&nbsp;</span>
429 </div>
430 <?php
431 }
432 $pos .= '-'.$indexpos;
433 }
434 // push position
435 if (!isset($positions[$rbook['idorder']])) {
436 $positions[$rbook['idorder']] = $indexpos;
437 }
438 // push booking to the buffer for the ordering in the next loop
439 array_push($bookbuffer, $rbook['idorder']);
440
441 // cell content
442 $cellcont = '&nbsp;';
443
444 // booking color tag (if enabled from overv View through cookie or session)
445 if ($this->pbmode == 'tags' && !isset($colortags_map[$rbook['idorder']])) {
446 $colortags_map[$rbook['idorder']] = VikBooking::applyBookingColorTag($rbook);
447 }
448
449 if (!in_array($rbook['idorder'], $prevbuffer)) {
450 // first time we print the details for this booking - compose the content of the element
451 $cellcont = '';
452
453 // customer details
454 if (!empty($rbook['first_name']) || !empty($rbook['last_name'])) {
455 /**
456 * Check if we need to display a profile picture or a channel logo.
457 *
458 * @since 1.16.0 (J) - 1.6.0 (WP)
459 */
460 $booking_avatar_src = null;
461 $booking_avatar_alt = null;
462 if (!empty($rbook['pic'])) {
463 // customer profile picture
464 $booking_avatar_src = strpos($rbook['pic'], 'http') === 0 ? $rbook['pic'] : VBO_SITE_URI . 'resources/uploads/' . $rbook['pic'];
465 $booking_avatar_alt = basename($booking_avatar_src);
466 } elseif (!empty($rbook['channel'])) {
467 // channel logo
468 $logo_helper = VikBooking::getVcmChannelsLogo($rbook['channel'], $get_istance = true);
469 if ($logo_helper !== false) {
470 $booking_avatar_src = $logo_helper->getSmallLogoURL();
471 $booking_avatar_alt = $logo_helper->provenience;
472 }
473 }
474
475 if (!empty($booking_avatar_src)) {
476 // make sure the alt attribute is not too long in case of broken images
477 $booking_avatar_alt = !empty($booking_avatar_alt) && strlen($booking_avatar_alt) > 15 ? '...' . substr($booking_avatar_alt, -12) : $booking_avatar_alt;
478 // append booking avatar image
479 $cellcont .= '<span class="vbo-tableaux-booking-avatar"><img src="' . $booking_avatar_src . '" class="vbo-tableaux-booking-avatar-img" ' . (!empty($booking_avatar_alt) ? 'alt="' . htmlspecialchars($booking_avatar_alt) . '" ' : '') . '/></span>';
480 }
481
482 // customer record
483 $cellcont .= '<span class="vbo-tableaux-guest-name">' . $rbook['first_name'] . ' ' . $rbook['last_name'] . '</span>';
484 } else {
485 // parse the customer data string
486 $custdata_parts = explode("\n", $rbook['custdata']);
487 $enoughinfo = false;
488 if (count($custdata_parts) > 2 && strpos($custdata_parts[0], ':') !== false && strpos($custdata_parts[1], ':') !== false) {
489 // get the first two fields
490 $custvalues = array();
491 foreach ($custdata_parts as $custdet) {
492 if (strlen($custdet) < 1) {
493 continue;
494 }
495 $custdet_parts = explode(':', $custdet);
496 if (count($custdet_parts) >= 2) {
497 unset($custdet_parts[0]);
498 array_push($custvalues, trim(implode(':', $custdet_parts)));
499 }
500 if (count($custvalues) > 1) {
501 break;
502 }
503 }
504 if (count($custvalues) > 1) {
505 $enoughinfo = true;
506 $cellcont .= '<span class="vbo-tableaux-guest-name">' . implode(' ', $custvalues) . '</span>';
507 }
508 }
509 if (!$enoughinfo) {
510 $cellcont .= '<span class="vbo-tableaux-guest-name">' . $rbook['idorder'] . '</span>';
511 }
512 }
513
514 // distinctive features
515 if (!empty($rbook['indexes']) && isset($rooms_features_map[$rid])) {
516 $bookindexes = explode(';', $rbook['indexes']);
517 $roomindexes = explode(';', $rbook['roomids']);
518 if (!isset($rooms_features_bookings[$rid.'_'.$rbook['idorder']])) {
519 // the index to read of the feature depending on how many times this booking was printed (in case of multiple same rooms in one booking)
520 $rooms_features_bookings[$rid.'_'.$rbook['idorder']] = 0;
521 } else {
522 // increment index for this room booking
523 $rooms_features_bookings[$rid.'_'.$rbook['idorder']]++;
524 }
525 // seek for the index occurrence of this room in the list of rooms booked
526 $count_pos = -1;
527 $room_pos = null;
528 foreach ($roomindexes as $rk => $rv) {
529 if ((int)$rv == (int)$rid) {
530 $count_pos++;
531 if ($count_pos == $rooms_features_bookings[$rid.'_'.$rbook['idorder']]) {
532 $room_pos = $rk;
533 break;
534 }
535 }
536 }
537 $nowfeatindex = isset($bookindexes[$room_pos]) ? $room_pos : null;
538 if (!is_null($room_pos) && isset($bookindexes[$nowfeatindex]) && isset($rooms_features_map[$rid][$bookindexes[$nowfeatindex]])) {
539 // get this room feature
540 $cellcont .= '<span class="vbo-tableaux-roomindex">' . $rooms_features_map[$rid][$bookindexes[$nowfeatindex]] . '</span> ';
541 }
542 }
543
544 // add invoice/receipt number if available (taken from booking color tag)
545 if (isset($colortags_map[$rbook['idorder']]) && count($colortags_map[$rbook['idorder']])) {
546 if (isset($colortags_map[$rbook['idorder']]['invoice_number'])) {
547 // invoice number
548 $invstyle = isset($colortags_map[$rbook['idorder']]['fontcolor']) ? ' style="color: '.$colortags_map[$rbook['idorder']]['fontcolor'].';"' : '';
549 $cellcont .= ' <a class="vbo-tableaux-invlink"'.$invstyle.' href="'.VBO_SITE_URI.'helpers/invoices/generated/'.$colortags_map[$rbook['idorder']]['invoice_file_name'].'" target="_blank"><i class="'.VikBookingIcons::i('file-text').'"></i> '.$colortags_map[$rbook['idorder']]['invoice_number'].'</a>';
550 } elseif (isset($colortags_map[$rbook['idorder']]['receipt_number'])) {
551 // receipt number
552 $cellcont .= ' <i class="'.VikBookingIcons::i('file-text-o').'" title="'.JText::translate('VBOFISCRECEIPT').' #'.$colortags_map[$rbook['idorder']]['receipt_number'].'"></i>';
553 }
554 }
555 }
556
557 // color tag styles
558 $cont_styles = array();
559 $span_styles = array();
560 if (isset($colortags_map[$rbook['idorder']]) && count($colortags_map[$rbook['idorder']])) {
561 $cont_styles[] = 'background-color: '.$colortags_map[$rbook['idorder']]['color'].';';
562 if (isset($colortags_map[$rbook['idorder']]['fontcolor'])) {
563 $cont_styles[] = 'color: '.$colortags_map[$rbook['idorder']]['fontcolor'].';';
564 $span_styles[] = 'color: '.$colortags_map[$rbook['idorder']]['fontcolor'].';';
565 }
566 }
567
568 // try to guess if this will be the last element of the day (single-unit rooms, check-out day with wider space because of no check-ins on the same day)
569 $last_checkout_class = '';
570 if ($rdata['units'] == 1 && strpos($contclass, 'checkout') !== false && !isset($room_bookings[($k + 1)])) {
571 $last_checkout_class = 'vbo-tableaux-booking-checkout-last ';
572 }
573 ?>
574 <div class="<?php echo $room_extra_class . $last_checkout_class; ?>vbo-tableaux-booking vbo-tableaux-booking-<?php echo $contclass . $shortstaycls . ' vbo-' . $pos; ?>"<?php echo count($cont_styles) ? ' style="'.implode('', $cont_styles).'"' : ''; ?> data-bid="<?php echo $rbook['idorder']; ?>">
575 <span<?php echo count($span_styles) ? ' style="'.implode('', $span_styles).'"' : ''; ?>><?php echo $cellcont; ?></span>
576 </div>
577 <?php
578 // increase the positioning index
579 $indexpos++;
580 }
581 ?>
582 <span class="vbo-roomdaynote-trigger" data-roomday="<?php echo $glob_rdn_keyid; ?>"><?php VikBookingIcons::e($rdaynote_icn, 'vbo-roomdaynote-display'); ?></span>
583 </td>
584 <?php
585 // next loop
586 $nowinfo = getdate(mktime(0, 0, 0, $nowinfo['mon'], ($nowinfo['mday'] + 1), $nowinfo['year']));
587 }
588 ?>
589 </tr>
590 <?php
591 }
592 ?>
593 </tbody>
594 </table>
595 </div>
596 </div>
597
598 </div>
599
600 <div class="vbo-modal-overlay-block vbo-modal-overlay-block-fests">
601 <a class="vbo-modal-overlay-close" href="javascript: void(0);"></a>
602 <div class="vbo-modal-overlay-content vbo-modal-overlay-content-fests">
603 <div class="vbo-modal-overlay-content-head vbo-modal-overlay-content-head-fests">
604 <h3><?php VikBookingIcons::e('star'); ?> <span></span></h3>
605 </div>
606 <div class="vbo-modal-overlay-content-body">
607 <div class="vbo-overlay-fests-list"></div>
608 <div class="vbo-overlay-fests-addnew" data-ymd="">
609 <h4><?php echo JText::translate('VBOADDCUSTOMFESTTODAY'); ?></h4>
610 <div class="vbo-overlay-fests-addnew-elem">
611 <label for="vbo-newfest-name"><?php echo JText::translate('VBPVIEWPLACESONE'); ?></label>
612 <input type="text" id="vbo-newfest-name" value="" />
613 </div>
614 <div class="vbo-overlay-fests-addnew-elem">
615 <label for="vbo-newfest-descr"><?php echo JText::translate('VBPLACEDESCR'); ?></label>
616 <textarea id="vbo-newfest-descr"></textarea>
617 </div>
618 <div class="vbo-overlay-fests-addnew-save">
619 <button type="button" class="btn btn-success" onclick="vboAddFest(this);"><?php echo JText::translate('VBSAVE'); ?></button>
620 </div>
621 </div>
622 </div>
623 </div>
624 </div>
625
626 <div class="vbo-modal-overlay-block vbo-modal-overlay-block-roomdaynotes">
627 <a class="vbo-modal-overlay-close" href="javascript: void(0);"></a>
628 <div class="vbo-modal-overlay-content vbo-modal-overlay-content-roomdaynotes">
629 <div class="vbo-modal-overlay-content-head vbo-modal-overlay-content-head-roomdaynotes">
630 <h3><?php VikBookingIcons::e('exclamation-circle'); ?> <span></span></h3>
631 </div>
632 <div class="vbo-modal-overlay-content-body">
633 <div class="vbo-modal-roomdaynotes-list"></div>
634 <div class="vbo-modal-roomdaynotes-addnew" data-readymd="" data-ymd="" data-roomid="" data-subroomid="">
635 <h4><?php echo JText::translate('VBOADDCUSTOMFESTTODAY'); ?></h4>
636 <div class="vbo-modal-roomdaynotes-addnew-elem">
637 <label for="vbo-newrdnote-name"><?php echo JText::translate('VBPVIEWPLACESONE'); ?></label>
638 <input type="text" id="vbo-newrdnote-name" value="" />
639 </div>
640 <div class="vbo-modal-roomdaynotes-addnew-elem">
641 <label for="vbo-newrdnote-descr"><?php echo JText::translate('VBPLACEDESCR'); ?></label>
642 <textarea id="vbo-newrdnote-descr"></textarea>
643 </div>
644 <div class="vbo-modal-roomdaynotes-addnew-elem">
645 <label for="vbo-newrdnote-cdays"><?php echo JText::translate('VBOCONSECUTIVEDAYS'); ?></label>
646 <input type="number" id="vbo-newrdnote-cdays" min="0" max="365" value="0" onchange="vboRdayNoteCdaysCount();" onkeyup="vboRdayNoteCdaysCount();" />
647 <span class="vbo-newrdnote-dayto">
648 <span class="vbo-newrdnote-dayto-lbl"><?php echo JText::translate('VBOUNTIL'); ?></span>
649 <span class="vbo-newrdnote-dayto-val"></span>
650 </span>
651 </div>
652 <div class="vbo-modal-roomdaynotes-addnew-save">
653 <button type="button" class="btn btn-success" onclick="vboAddRoomDayNote(this);"><?php echo JText::translate('VBSAVE'); ?></button>
654 </div>
655 </div>
656 </div>
657 </div>
658 </div>
659
660 <a class="vbo-basenavuri-details" href="index.php?option=com_vikbooking&task=editorder&goto=tableaux&cid[]=%d" style="display: none;"></a>
661 <a class="vbo-basenavuri-edit" href="index.php?option=com_vikbooking&task=editbusy&goto=tableaux&cid[]=%d" style="display: none;"></a>
662
663 <script type="text/javascript">
664 var vboFests = <?php echo json_encode($this->festivities); ?>;
665 var vboRdayNotes = <?php echo json_encode($this->rdaynotes); ?>;
666 var vbodialog_on = false;
667 var vbodialogfests_on = false;
668 var vbodialogrdaynotes_on = false;
669
670 /**
671 * Fests dialog
672 */
673 function hideVboDialogFests() {
674 if (vbodialogfests_on === true) {
675 jQuery(".vbo-modal-overlay-block-fests").fadeOut(400, function () {
676 jQuery(".vbo-modal-overlay-content-fests").show();
677 });
678 vbodialogfests_on = false;
679 }
680 }
681
682 /**
683 * Room-day-notes dialog
684 */
685 function hideVboDialogRdaynotes() {
686 if (vbodialogrdaynotes_on === true) {
687 jQuery(".vbo-modal-overlay-block-roomdaynotes").fadeOut(400, function () {
688 jQuery(".vbo-modal-overlay-content-roomdaynotes").show();
689 });
690 // reset values
691 jQuery('#vbo-newrdnote-name').val('');
692 jQuery('#vbo-newrdnote-descr').val('');
693 jQuery('#vbo-newrdnote-cdays').val('0').trigger('change');
694 // turn flag off
695 vbodialogrdaynotes_on = false;
696 }
697 }
698
699 /* Hover Tooltip */
700 var hovtimer;
701 var hovtip = false;
702 var vboMessages = {
703 loadingTip: "<?php echo addslashes(JText::translate('VIKLOADING')); ?>",
704 numRooms: "<?php echo addslashes(JText::translate('VBEDITORDERROOMSNUM')); ?>",
705 numAdults: "<?php echo addslashes(JText::translate('VBEDITORDERADULTS')); ?>",
706 numNights: "<?php echo addslashes(JText::translate('VBDAYS')); ?>",
707 checkinLbl: "<?php echo addslashes(JText::translate('VBPICKUPAT')); ?>",
708 checkoutLbl: "<?php echo addslashes(JText::translate('VBRELEASEAT')); ?>",
709 numChildren: "<?php echo addslashes(JText::translate('VBEDITORDERCHILDREN')); ?>",
710 totalAmount: "<?php echo addslashes(JText::translate('VBEDITORDERNINE')); ?>",
711 totalPaid: "<?php echo addslashes(JText::translate('VBPEDITBUSYTOTPAID')); ?>",
712 currencySymb: "<?php echo $currencysymb; ?>"
713 };
714
715 function registerHoveringTooltip(that) {
716 if (hovtip) {
717 return false;
718 }
719
720 if (hovtimer) {
721 clearTimeout(hovtimer);
722 hovtimer = null;
723 }
724
725 var elem = jQuery(that);
726 var cellheight = elem.outerHeight();
727
728 hovtimer = setTimeout(function() {
729 // turn flag on
730 hovtip = true;
731
732 // calculate cell-element position
733 let pos_top = elem.offset().top;
734 let pos_left = elem.offset().left;
735 let elem_height = elem.outerHeight();
736 let screen_width = window?.screen?.width || 0;
737
738 // build tooltip block element
739 let tooltip_block = jQuery('<div></div>');
740 tooltip_block.addClass('vbo-overview-tipblock');
741 tooltip_block.append("<div class=\"vbo-overview-tipinner\"><span class=\"vbo-overview-tiploading\">" + vboMessages.loadingTip + "</span></div>");
742 tooltip_block.append("<div class=\"vbo-overview-tipexpander\" style=\"display: none;\"><div class=\"vbo-overview-expandtoggle\"><i class=\"<?php echo VikBookingIcons::i('expand'); ?>\"></i></div></div>");
743
744 // calculate block position
745 tooltip_block.css('top', (pos_top + elem_height - 16) + 'px');
746 // tooltip_block.css('left', (pos_left - 6) + 'px');
747 if (screen_width > 600 && (pos_left + 400) > screen_width) {
748 // place the tooltip starting from right
749 tooltip_block.css('left', (pos_left - (400 - elem.outerWidth())) + 'px');
750 } else {
751 // regular placing starting from left
752 tooltip_block.css('left', (pos_left - 6) + 'px');
753 }
754
755 // append block to body
756 tooltip_block.appendTo(jQuery('body'));
757
758 // load tooltip bookings
759 loadTooltipBookings(elem.attr('data-bid'));
760 }, 900);
761 }
762
763 function unregisterHoveringTooltip() {
764 clearTimeout(hovtimer);
765 hovtimer = null;
766 }
767
768 /**
769 * @deprecated 1.16.10 (J) - 1.6.10 (WP)
770 */
771 function adjustHoveringTooltip() {
772 setTimeout(function() {
773 var difflim = 35;
774 var otop = jQuery(".vbo-overview-tipblock").offset().top;
775 if (otop < difflim) {
776 jQuery(".vbo-overview-tipblock").css("bottom", "-="+(difflim - otop));
777 } else {
778 // check top position from table
779 var tipheight = jQuery(".vbo-overview-tipblock").outerHeight();
780 var blocktop = jQuery(".vbo-overview-tipblock").parent().offset().top;
781 var tabletop = jQuery(".vbo-tableaux-table").offset().top;
782 if ((blocktop - tabletop) < tipheight) {
783 // tooltip is going under the table, move it down the container
784 jQuery(".vbo-overview-tipblock").css("bottom", "-"+tipheight+'px');
785 }
786 // check left position from second cell
787 var tipwidth = jQuery(".vbo-overview-tipblock").outerWidth();
788 var cellscndleft = jQuery('.vbo-tableaux-table').find('tr td').first().next().offset().left;
789 var blockleft = jQuery('.vbo-overview-tipblock').parent().offset().left;
790 if (tipwidth > (blockleft - cellscndleft)) {
791 // tooltip should start from left: 0 going to the right
792 jQuery(".vbo-overview-tipblock").css("left", "0px");
793 }
794 }
795 }, 100);
796 }
797
798 function hideVboTooltip() {
799 jQuery('.vbo-overview-tipblock').remove();
800 hovtip = false;
801 }
802
803 function loadTooltipBookings(bid) {
804 if (!bid || bid === undefined || !bid.length) {
805 hideVboTooltip();
806 return false;
807 }
808 // ajax request
809 var jqxhr = jQuery.ajax({
810 type: "POST",
811 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=getbookingsinfo'); ?>",
812 data: {
813 tmpl: "component",
814 idorders: bid
815 }
816 }).done(function(res) {
817 try {
818 var obj_res = typeof res === 'string' ? JSON.parse(res) : res;
819 jQuery('.vbo-overview-tiploading').remove();
820 var container = jQuery('.vbo-overview-tipinner');
821 jQuery(obj_res).each(function(k, v) {
822 // get base navigation URIs
823 var base_uri_details = jQuery('.vbo-basenavuri-details').attr('href');
824 var base_uri_edit = jQuery('.vbo-basenavuri-edit').attr('href');
825
826 // build content
827 var bcont = "<div class=\"vbo-overview-tip-bookingcont\">";
828 bcont += "<div class=\"vbo-overview-tip-bookingcont-left\">";
829 bcont += "<div class=\"vbo-overview-tip-bid\"><span class=\"vbo-overview-tip-lbl\">ID <span class=\"vbo-overview-tip-lbl-innerleft\"><a href=\"" + base_uri_edit.replace('%d', v.id) + "\" target=\"_blank\"><i class=\"<?php echo VikBookingIcons::i('edit'); ?>\"></i></a></span></span><span class=\"vbo-overview-tip-cnt\">"+v.id+"</span></div>";
830 bcont += "<div class=\"vbo-overview-tip-bstatus\"><span class=\"vbo-overview-tip-lbl\"><?php echo addslashes(JText::translate('VBPVIEWORDERSEIGHT')); ?></span><span class=\"vbo-overview-tip-cnt\"><div class=\"badge "+(v.status == 'confirmed' ? 'badge-success' : 'badge-warning')+"\">"+v.status_lbl+"</div></span></div>";
831 bcont += "<div class=\"vbo-overview-tip-bdate\"><span class=\"vbo-overview-tip-lbl\"><?php echo addslashes(JText::translate('VBPVIEWORDERSONE')); ?></span><span class=\"vbo-overview-tip-cnt\"><a href=\"" + base_uri_details.replace('%d', v.id) + "\" target=\"_blank\">"+v.ts+"</a></span></div>";
832 bcont += "</div>";
833 bcont += "<div class=\"vbo-overview-tip-bookingcont-right\">";
834 bcont += "<div class=\"vbo-overview-tip-bcustomer\"><span class=\"vbo-overview-tip-lbl\"><?php echo addslashes(JText::translate('VBOCUSTOMER')); ?></span><span class=\"vbo-overview-tip-cnt\">"+v.cinfo+"</span></div>";
835 if (v.roomsnum > 1) {
836 bcont += "<div class=\"vbo-overview-tip-brooms\"><span class=\"vbo-overview-tip-lbl\">"+vboMessages.numRooms+"</span><span class=\"vbo-overview-tip-cnt\">"+v.room_names+"</span></div>";
837 }
838 bcont += "<div class=\"vbo-overview-tip-bguests\"><span class=\"vbo-overview-tip-lbl\">"+vboMessages.numNights+"</span><span class=\"vbo-overview-tip-cnt hasTooltip\" title=\""+vboMessages.checkinLbl+" "+v.checkin+" - "+vboMessages.checkoutLbl+" "+v.checkout+"\">"+v.days+", "+vboMessages.numAdults+": "+v.tot_adults+(v.tot_children > 0 ? ", "+vboMessages.numChildren+": "+v.tot_children : "")+"</span></div>";
839 if (v.hasOwnProperty('rindexes')) {
840 for (var rindexk in v.rindexes) {
841 if (v.rindexes.hasOwnProperty(rindexk)) {
842 bcont += "<div class=\"vbo-overview-tip-brindexes\"><span class=\"vbo-overview-tip-lbl\">"+rindexk+"</span><span class=\"vbo-overview-tip-cnt\">"+v.rindexes[rindexk]+"</span></div>";
843 }
844 }
845 }
846 if (v.hasOwnProperty('channelimg')) {
847 bcont += "<div class=\"vbo-overview-tip-bprovenience\"><span class=\"vbo-overview-tip-lbl\"><?php echo addslashes(JText::translate('VBPVIEWORDERCHANNEL')); ?></span><span class=\"vbo-overview-tip-cnt\">"+v.channelimg+"</span></div>";
848 }
849 bcont += "<div class=\"vbo-overview-tip-bookingcont-total\">";
850 bcont += "<div class=\"vbo-overview-tip-btot\"><span class=\"vbo-overview-tip-lbl\">"+vboMessages.totalAmount+"</span><span class=\"vbo-overview-tip-cnt\">"+vboMessages.currencySymb+" "+v.format_tot+"</span></div>";
851 if (v.totpaid > 0.00) {
852 bcont += "<div class=\"vbo-overview-tip-btot\"><span class=\"vbo-overview-tip-lbl\">"+vboMessages.totalPaid+"</span><span class=\"vbo-overview-tip-cnt\">"+vboMessages.currencySymb+" "+v.format_totpaid+"</span></div>";
853 }
854 var getnotes = v.adminnotes;
855 if (getnotes !== null && getnotes.length) {
856 bcont += "<div class=\"vbo-overview-tip-notes\"><span class=\"vbo-overview-tip-lbl\"><span class=\"vbo-overview-tip-notes-inner\"><i class=\"vboicn-info hasTooltip\" title=\""+getnotes+"\"></i></span></span></div>";
857 }
858 bcont += "</div>";
859 bcont += "</div>";
860 bcont += "</div>";
861 container.append(bcont);
862 jQuery('.vbo-overview-tipexpander').show();
863 });
864
865 /**
866 * @deprecated 1.16.10 (J) - 1.6.10 (WP)
867 */
868 // adjust the position so that it won't go under other contents
869 // adjustHoveringTooltip();
870
871 } catch(err) {
872 // restore
873 hideVboTooltip();
874 // display error
875 console.error('could not parse JSON response', err, res);
876 alert('Could not parse JSON response');
877 }
878 }).fail(function(err) {
879 // restore
880 hideVboTooltip();
881 // display error
882 console.error(err);
883 alert(err.responseText);
884 });
885 }
886
887 function vboSendPrintTableaux() {
888 jQuery('#adminForm').attr('target', '_blank').append('<input type="hidden" class="vbo-tableaux-rmafterprint" name="tmpl" value="component" /><input type="hidden" class="vbo-tableaux-rmafterprint" name="printtableaux" value="1" />').submit();
889 setTimeout(function() {
890 jQuery('#adminForm').attr('target', '_self');
891 jQuery('.vbo-tableaux-rmafterprint').remove();
892 }, 500);
893 }
894
895 /**
896 * Fests
897 */
898 function vboRenderFests(day, daytitle) {
899 // set day title
900 if (daytitle) {
901 jQuery('.vbo-modal-overlay-content-fests').find('h3').find('span').text(daytitle);
902 }
903 // compose fests information
904 var fests_html = '';
905 if (vboFests[day] && vboFests[day]['festinfo'] && vboFests[day]['festinfo'].length) {
906 for (var i = 0; i < vboFests[day]['festinfo'].length; i++) {
907 var fest = vboFests[day]['festinfo'][i];
908 fests_html += '<div class="vbo-overlay-fest-details">';
909 fests_html += ' <div class="vbo-fest-info">';
910 fests_html += ' <div class="vbo-fest-name">' + fest['trans_name'] + '</div>';
911 fests_html += ' <div class="vbo-fest-desc">' + fest['descr'].replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + '<br />' + '$2') + '</div>';
912 fests_html += ' </div>';
913 fests_html += ' <div class="vbo-fest-cmds">';
914 fests_html += ' <button type="button" class="btn btn-danger" onclick="vboRemoveFest(\'' + day + '\', \'' + i + '\', \'' + fest['type'] + '\', this);"><?php VikBookingIcons::e('trash-alt'); ?></button>';
915 fests_html += ' </div>';
916 fests_html += '</div>';
917 }
918 }
919 // update ymd key for the selected date, useful for adding new fests
920 jQuery('.vbo-overlay-fests-addnew').attr('data-ymd', day);
921 // set content and display modal
922 jQuery('.vbo-overlay-fests-list').html(fests_html);
923 jQuery('.vbo-modal-overlay-block-fests').fadeIn();
924 vbodialogfests_on = true;
925 }
926
927 function vboRemoveFest(day, index, fest_type, that) {
928 if (!confirm('<?php echo addslashes(JText::translate('VBDELCONFIRM')); ?>')) {
929 return false;
930 }
931 var elem = jQuery(that);
932 // make the AJAX request to the controller to remove this fest from the DB
933 var jqxhr = jQuery.ajax({
934 type: "POST",
935 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=remove_fest'); ?>",
936 data: {
937 tmpl: "component",
938 dt: day,
939 ind: index,
940 type: fest_type
941 }
942 }).done(function(res) {
943 if (res.indexOf('e4j.ok') >= 0) {
944 // delete fest also from the json-decode array of objects
945 if (vboFests[day] && vboFests[day]['festinfo']) {
946 // use splice to remove the desired index from array, or delete would not make the length of the array change
947 vboFests[day]['festinfo'].splice(index, 1);
948 // re-build indexes of delete buttons, fundamental for removing the right index at next click
949 vboRenderFests(day);
950 if (!vboFests[day]['festinfo'].length) {
951 // delete also this date object from fests
952 delete vboFests[day];
953 // no more fests, remove the class for this date from all cells
954 jQuery('td.vbo-tableaux-daycell[data-ymd="'+day+'"]').removeClass('vbo-tableaux-festcell');
955 }
956 }
957 elem.closest('.vbo-overlay-fest-details').remove();
958 } else {
959 console.log(res);
960 alert('Invalid response');
961 }
962 }).fail(function() {
963 alert('Request failed');
964 });
965 }
966
967 function vboAddFest(that) {
968 var elem = jQuery(that);
969 var ymd = elem.closest('.vbo-overlay-fests-addnew').attr('data-ymd');
970 var fest_name = jQuery('#vbo-newfest-name').val();
971 var fest_descr = jQuery('#vbo-newfest-descr').val();
972 if (!fest_name.length) {
973 return false;
974 }
975 // make the AJAX request to the controller to add this fest to the DB
976 var jqxhr = jQuery.ajax({
977 type: "POST",
978 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=add_fest'); ?>",
979 data: {
980 tmpl: "component",
981 dt: ymd,
982 type: "custom",
983 name: fest_name,
984 descr: fest_descr
985 }
986 }).done(function(res) {
987 // parse the JSON response that contains the fest object for the passed date
988 try {
989 var stored_fest = JSON.parse(res);
990 if (!vboFests.hasOwnProperty(stored_fest['dt'])) {
991 // we need to add the proper class to all cells to show that there is a fest
992 jQuery('td.vbo-tableaux-daycell[data-ymd="'+stored_fest['dt']+'"]').addClass('vbo-tableaux-festcell');
993 }
994 vboFests[stored_fest['dt']] = stored_fest;
995 hideVboDialogFests();
996 // reset input fields
997 jQuery('#vbo-newfest-name').val('');
998 jQuery('#vbo-newfest-descr').val('');
999 } catch (e) {
1000 console.log(res);
1001 alert('Invalid response');
1002 return false;
1003 }
1004 }).fail(function() {
1005 alert('Request failed');
1006 });
1007 }
1008 //
1009
1010 /**
1011 * Room-day notes
1012 */
1013 var rdaynote_icn_full = '<?php echo VikBookingIcons::i('sticky-note', 'vbo-roomdaynote-display'); ?>';
1014 var rdaynote_icn_empty = '<?php echo VikBookingIcons::i('far fa-sticky-note', 'vbo-roomdaynote-display'); ?>';
1015 var rooms_features_map = JSON.parse('<?php echo isset($rooms_features_map) && count($rooms_features_map) ? json_encode($rooms_features_map) : json_encode(array()); ?>');
1016
1017 function vboRenderRdayNotes(day, idroom, subunit, readymd) {
1018 // compose room-day notes information (subunit always = 0 for room-day level)
1019 var notes_html = '';
1020 var keyids = new Array;
1021 // always push 0 as the room-level day note key
1022 keyids.push(0);
1023 // grab all the other note keys at sub-unit level
1024 if (jQuery('.vbo-tableaux-roombooks[data-dayrid="' + day + idroom + '"]').length) {
1025 var all_nkeys = jQuery('.vbo-tableaux-roombooks[data-dayrid="' + day + idroom + '"]').attr('data-rdnkeys');
1026 if (all_nkeys && all_nkeys.length) {
1027 var nkeys = all_nkeys.split(',');
1028 for (var k in nkeys) {
1029 var int_index = parseInt(nkeys[k]);
1030 if (!nkeys.hasOwnProperty(k) || isNaN(int_index) || int_index < 1) {
1031 continue;
1032 }
1033 // push sub-unit key for lookup
1034 keyids.push(nkeys[k]);
1035 }
1036 }
1037 }
1038 // parse all room keys with notes
1039 for (var z = 0; z < keyids.length; z++) {
1040 // this is the real sub-unit we are parsing
1041 var subunit_index = keyids[z];
1042 var keyid = day + '_' + idroom + '_' + subunit_index;
1043 if (vboRdayNotes.hasOwnProperty(keyid) && vboRdayNotes[keyid]['info'] && vboRdayNotes[keyid]['info'].length) {
1044 for (var i = 0; i < vboRdayNotes[keyid]['info'].length; i++) {
1045 var note_data = vboRdayNotes[keyid]['info'][i];
1046 var subunit_name = '';
1047 if (parseInt(subunit_index) > 0) {
1048 subunit_name = '(' + subunit_index + ') ';
1049 if (rooms_features_map.hasOwnProperty(idroom) && rooms_features_map[idroom].hasOwnProperty(subunit_index)) {
1050 subunit_name = rooms_features_map[idroom][subunit_index] + ' - ';
1051 }
1052 }
1053 notes_html += '<div class="vbo-overlay-fest-details vbo-modal-roomdaynotes-note-details">';
1054 notes_html += ' <div class="vbo-fest-info vbo-modal-roomdaynotes-note-info">';
1055 notes_html += ' <div class="vbo-fest-name vbo-modal-roomdaynotes-note-name">' + subunit_name + note_data['name'] + '</div>';
1056 notes_html += ' <div class="vbo-fest-desc vbo-modal-roomdaynotes-note-desc">' + note_data['descr'].replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + '<br />' + '$2') + '</div>';
1057 notes_html += ' </div>';
1058 notes_html += ' <div class="vbo-fest-cmds vbo-modal-roomdaynotes-note-cmds">';
1059 notes_html += ' <button type="button" class="btn btn-danger" onclick="vboRemoveRdayNote(\'' + i + '\', \'' + day + '\', \'' + idroom + '\', \'' + subunit_index + '\', \'' + note_data['type'] + '\', this);"><?php VikBookingIcons::e('trash-alt'); ?></button>';
1060 notes_html += ' </div>';
1061 notes_html += '</div>';
1062 }
1063 }
1064 }
1065 // update attributes keys for the selected date, useful for adding new notes (always at room-level)
1066 jQuery('.vbo-modal-roomdaynotes-addnew').attr('data-ymd', day).attr('data-roomid', idroom).attr('data-subroomid', '0');
1067 if (readymd !== null) {
1068 jQuery('.vbo-modal-roomdaynotes-addnew').attr('data-readymd', readymd);
1069 jQuery('.vbo-newrdnote-dayto-val').text(readymd);
1070 }
1071 // set content and display modal
1072 jQuery('.vbo-modal-roomdaynotes-list').html(notes_html);
1073 }
1074
1075 function vboAddRoomDayNote(that) {
1076 var mainelem = jQuery(that).closest('.vbo-modal-roomdaynotes-addnew');
1077 var ymd = mainelem.attr('data-ymd');
1078 var roomid = mainelem.attr('data-roomid');
1079 var subroomid = mainelem.attr('data-subroomid');
1080 var note_name = jQuery('#vbo-newrdnote-name').val();
1081 var note_descr = jQuery('#vbo-newrdnote-descr').val();
1082 var note_cdays = jQuery('#vbo-newrdnote-cdays').val();
1083 if (!note_name.length && !note_descr.length) {
1084 alert('Missing required fields');
1085 return false;
1086 }
1087 // make the AJAX request to the controller to add this note to the DB
1088 var jqxhr = jQuery.ajax({
1089 type: "POST",
1090 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=add_roomdaynote'); ?>",
1091 data: {
1092 tmpl: "component",
1093 dt: ymd,
1094 idroom: roomid,
1095 subunit: subroomid,
1096 type: "custom",
1097 name: note_name,
1098 descr: note_descr,
1099 cdays: note_cdays
1100 }
1101 }).done(function(res) {
1102 // parse the JSON response that contains the note object for the passed date
1103 try {
1104 var stored_notes = JSON.parse(res);
1105 var counter = 0;
1106 for (var keyid in stored_notes) {
1107 if (!stored_notes.hasOwnProperty(keyid)) {
1108 continue;
1109 }
1110 var is_date_visible = (jQuery('.vbo-roomdaynote-trigger[data-roomday="' + keyid + '"]').length > 0);
1111 if (!vboRdayNotes.hasOwnProperty(keyid) && is_date_visible) {
1112 // we need to add the proper class to the cell for this note (if it's visible)
1113 jQuery('.vbo-roomdaynote-trigger[data-roomday="' + keyid + '"]').parent('td').removeClass('vbo-roomdaynote-empty').addClass('vbo-roomdaynote-full').find('i').attr('class', rdaynote_icn_full);
1114 }
1115 // update global object with the new notes in any case
1116 vboRdayNotes[keyid] = stored_notes[keyid];
1117 // update readable notes in custom data attribute
1118 if (counter < 1 && is_date_visible && note_name.length) {
1119 // we push just this name
1120 var current_cell_notes = jQuery('.vbo-roomdaynote-trigger[data-roomday="' + keyid + '"]').parent('td').attr('data-notes');
1121 var all_cell_notes = current_cell_notes && current_cell_notes.length ? current_cell_notes.split(', ') : new Array;
1122 all_cell_notes.push(note_name);
1123 jQuery('.vbo-roomdaynote-trigger[data-roomday="' + keyid + '"]').parent('td').attr('data-notes', all_cell_notes.join(', '));
1124 }
1125 // increase counter
1126 counter++;
1127 }
1128 // close modal
1129 hideVboDialogRdaynotes();
1130 // reset input fields
1131 jQuery('#vbo-newrdnote-name').val('');
1132 jQuery('#vbo-newrdnote-descr').val('');
1133 jQuery('#vbo-newrdnote-cdays').val('0').trigger('change');
1134 } catch (e) {
1135 console.log(res);
1136 alert('Invalid response');
1137 return false;
1138 }
1139 }).fail(function() {
1140 alert('Request failed');
1141 });
1142 }
1143
1144 function vboRdayNotesAvailable(day, idroom) {
1145 /**
1146 * We check whether a room has notes at parent or subunit level for the given date.
1147 */
1148 var base_key = day + '_' + idroom + '_';
1149 var notes_available = false;
1150 for (var k in vboRdayNotes) {
1151 if (!vboRdayNotes.hasOwnProperty(k) || k.indexOf(base_key) < 0) {
1152 continue;
1153 }
1154 // notes found for this room, either parent or sub-unit
1155 if (vboRdayNotes[k]['info'] && vboRdayNotes[k]['info'].length) {
1156 notes_available = true;
1157 break;
1158 }
1159 }
1160
1161 return notes_available;
1162 }
1163
1164 function vboRemoveRdayNote(index, day, idroom, subunit, note_type, that) {
1165 if (!confirm('<?php echo addslashes(JText::translate('VBDELCONFIRM')); ?>')) {
1166 return false;
1167 }
1168 var elem = jQuery(that);
1169 // make the AJAX request to the controller to remove this note from the DB
1170 var jqxhr = jQuery.ajax({
1171 type: "POST",
1172 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=remove_roomdaynote'); ?>",
1173 data: {
1174 tmpl: "component",
1175 dt: day,
1176 idroom: idroom,
1177 subunit: subunit,
1178 ind: index,
1179 type: note_type
1180 }
1181 }).done(function(res) {
1182 if (res.indexOf('e4j.ok') >= 0) {
1183 var keyid = day + '_' + idroom + '_' + subunit;
1184 var glob_keyid = day + '_' + idroom + '_0';
1185 // delete note also from the json-decode array of objects
1186 if (vboRdayNotes[keyid] && vboRdayNotes[keyid]['info']) {
1187 // get the original note name
1188 var old_name = vboRdayNotes[keyid]['info'][index]['name'];
1189 // use splice to remove the desired index from array, or delete would not make the length of the array change
1190 vboRdayNotes[keyid]['info'].splice(index, 1);
1191 // re-build indexes of delete buttons, fundamental for removing the right index at next click (reload always at room-level)
1192 vboRenderRdayNotes(day, idroom, '0', null);
1193 if (!vboRdayNotes[keyid]['info'].length && !vboRdayNotesAvailable(day, idroom)) {
1194 // delete also this date object from notes
1195 delete vboRdayNotes[keyid];
1196 // no more notes, update the proper class attribute for this cell (should be visible)
1197 if (jQuery('.vbo-roomdaynote-trigger[data-roomday="' + glob_keyid + '"]').length) {
1198 jQuery('.vbo-roomdaynote-trigger[data-roomday="' + glob_keyid + '"]').parent('td').removeClass('vbo-roomdaynote-full').addClass('vbo-roomdaynote-empty').attr('data-notes', '').find('i').attr('class', rdaynote_icn_empty);
1199 }
1200 } else if (old_name && old_name.length) {
1201 // try to adjust the readable notes in custom data attribute
1202 var current_cell_notes = jQuery('.vbo-roomdaynote-trigger[data-roomday="' + glob_keyid + '"]').parent('td').attr('data-notes');
1203 var all_cell_notes = current_cell_notes && current_cell_notes.length ? current_cell_notes.split(', ') : new Array;
1204 var new_cell_notes = new Array;
1205 for (var i = 0; i < all_cell_notes.length; i++) {
1206 if (all_cell_notes[i].indexOf(old_name) < 0) {
1207 new_cell_notes.push(all_cell_notes[i]);
1208 }
1209 }
1210 jQuery('.vbo-roomdaynote-trigger[data-roomday="' + glob_keyid + '"]').parent('td').attr('data-notes', new_cell_notes.join(', '));
1211 }
1212 }
1213 elem.closest('.vbo-modal-roomdaynotes-note-details').remove();
1214 } else {
1215 console.log(res);
1216 alert('Invalid response');
1217 }
1218 }).fail(function() {
1219 alert('Request failed');
1220 });
1221 }
1222
1223 function vboRdayNoteCdaysCount() {
1224 var cdays = parseInt(jQuery('#vbo-newrdnote-cdays').val());
1225 var defymd = jQuery('.vbo-modal-roomdaynotes-addnew').attr('data-ymd');
1226 var defreadymd = jQuery('.vbo-modal-roomdaynotes-addnew').attr('data-readymd');
1227 defreadymd = !defreadymd || !defreadymd.length ? defymd : defreadymd;
1228 if (isNaN(cdays) || cdays < 1) {
1229 jQuery('.vbo-newrdnote-dayto-val').text(defreadymd);
1230 return;
1231 }
1232 // calculate target (until) date
1233 var targetdate = new Date(defymd);
1234 targetdate.setDate(targetdate.getDate() + cdays);
1235 var target_y = targetdate.getFullYear();
1236 var target_m = targetdate.getMonth() + 1;
1237 target_m = target_m < 10 ? '0' + target_m : target_m;
1238 var target_d = targetdate.getDate();
1239 target_d = target_d < 10 ? '0' + target_d : target_d;
1240 // display target date
1241 var display_target = target_y + '-' + target_m + '-' + target_d;
1242 // check if we can get the "read ymd property"
1243 if (jQuery('.vbo-tableaux-daycell[data-ymd="' + display_target + '"]').length) {
1244 display_target = jQuery('.vbo-tableaux-daycell[data-ymd="' + display_target + '"]').attr('data-readymd');
1245 }
1246 jQuery('.vbo-newrdnote-dayto-val').text(display_target);
1247 }
1248 //
1249
1250 /**
1251 * Tells whether an element can fit entirely in the viewport.
1252 * Example: if a table is too high to fit in the screen.
1253 * If false, it means that scrolling will always be needed.
1254 */
1255 function vboFitsInViewport(selector) {
1256 var elemHeight = jQuery(selector).outerHeight();
1257 var maxHeight = jQuery(window).height();
1258
1259 return elemHeight < maxHeight;
1260 }
1261
1262 // tableaux scrolling
1263 var fittingTableaux = null;
1264 var tableauxStickySet = false;
1265
1266 /**
1267 * Throttle guarantees a constant flow of events at a given time interval,
1268 * but it runs immediately when the event takes place. We rather need a
1269 * debounce technique to group a flurry of events into one single event.
1270 * This is useful for listening to the scrolling event of the table, actually
1271 * the DIV that wraps it with overflow, and setting the sticky headers.
1272 */
1273 function vboDebounceScroll(func, wait, immediate) {
1274 var timeout;
1275 return function() {
1276 var context = this, args = arguments;
1277 var later = function() {
1278 timeout = null;
1279 if (!immediate) func.apply(context, args);
1280 };
1281 var callNow = immediate && !timeout;
1282 clearTimeout(timeout);
1283 timeout = setTimeout(later, wait);
1284 if (callNow) {
1285 func.apply(context, args);
1286 }
1287 }
1288 }
1289 /**
1290 * We do not need a throttle technique, but this method would
1291 * throttle the scroll event rather than debouncing it.
1292 */
1293 function vboThrottleScroll(method, delay) {
1294 var time = Date.now();
1295 return function() {
1296 if ((time + delay - Date.now()) < 0) {
1297 method();
1298 time = Date.now();
1299 }
1300 }
1301 }
1302
1303 /**
1304 * This will fire during the throttle of the scrolling event.
1305 */
1306 function vboHandleScroll() {
1307 if (fittingTableaux === null) {
1308 fittingTableaux = vboFitsInViewport('.vbo-tableaux-table');
1309 }
1310 if (fittingTableaux) {
1311 // no sticky table headers needed, remove the listener as it is not needed
1312 document.getElementById('vbo-tableaux-table-scroller').removeEventListener('scroll', vboDebounceScroll(vboHandleScroll, 1000));
1313 return;
1314 }
1315 // we have scrolled (vertically or horizontally), update the position of the sticky table head
1316 if (tableauxStickySet) {
1317 // destroy sticky table head
1318 jQuery('table.vbo-tableaux-table').stickyTableHeaders('destroy');
1319 }
1320 // re-build sticky head
1321 jQuery('table.vbo-tableaux-table').stickyTableHeaders({cacheHeaderHeight: true, fixedOffset: jQuery('.navbar')});
1322 tableauxStickySet = true;
1323 }
1324
1325 jQuery(function() {
1326 // add listener to scroll event on the main table
1327 document.getElementById('vbo-tableaux-table-scroller').addEventListener('scroll', vboDebounceScroll(vboHandleScroll, 1000));
1328
1329 jQuery("#roomsel, .vbo-perm-roomsel").select2({placeholder: '<?php echo addslashes(JText::translate('VBCOUPONALLVEHICLES')); ?>', width: "300px", allowClear: true});
1330
1331 jQuery('#vbo-date-from').datepicker({
1332 showOn: 'focus',
1333 dateFormat: '<?php echo $juidf; ?>',
1334 <?php echo ($this->mindate > 0 ? 'minDate: "'.date(str_replace('%', '', $nowdf), $this->mindate).'", ' : '').($this->maxdate > 0 ? 'maxDate: "'.date(str_replace('%', '', $nowdf), $this->maxdate).'", ' : ''); ?>
1335 onSelect: function( selectedDate ) {
1336 jQuery('#vbo-date-to').datepicker('option', 'minDate', selectedDate);
1337 jQuery('#vbo-month').val('');
1338 }
1339 });
1340
1341 jQuery('#vbo-date-to').datepicker({
1342 showOn: 'focus',
1343 dateFormat: '<?php echo $juidf; ?>',
1344 <?php echo ($this->mindate > 0 ? 'minDate: "'.date(str_replace('%', '', $nowdf), $this->mindate).'", ' : '').($this->maxdate > 0 ? 'maxDate: "'.date(str_replace('%', '', $nowdf), $this->maxdate).'", ' : ''); ?>
1345 onSelect: function( selectedDate ) {
1346 jQuery('#vbo-date-from').datepicker('option', 'maxDate', selectedDate);
1347 jQuery('#vbo-month').val('');
1348 }
1349 });
1350
1351 jQuery('#vbo-date-from-trig, #vbo-date-to-trig').click(function() {
1352 var jdp = jQuery(this).prev('input.hasDatepicker');
1353 if (jdp.length) {
1354 jdp.focus();
1355 }
1356 });
1357
1358 jQuery('.vbo-tableaux-selmonth').click(function() {
1359 var xts = jQuery(this).attr('data-ts');
1360 var xfrom = jQuery(this).attr('data-from');
1361 var xto = jQuery(this).attr('data-to');
1362 jQuery('#vbo-month').val(xts);
1363 jQuery('#vbo-date-from').val(xfrom);
1364 jQuery('#vbo-date-to').val(xto);
1365 jQuery('#adminForm').submit();
1366 });
1367
1368 /* Expand/Collapse tooltip */
1369 jQuery(document.body).on("click", ".vbo-overview-expandtoggle", function() {
1370 jQuery(this).closest('.vbo-overview-tipblock').toggleClass('vbo-overview-tipblock-expanded');
1371 });
1372 /* ----------------------- */
1373
1374 /* Hover Tooltip */
1375 jQuery('.vbo-tableaux-booking-checkin, .vbo-tableaux-booking-checkout, .vbo-tableaux-booking-stay').hover(function() {
1376 registerHoveringTooltip(this);
1377 }, unregisterHoveringTooltip);
1378
1379 jQuery(document).keydown(function(e) {
1380 if (e.keyCode == 27) {
1381 if (hovtip === true) {
1382 hideVboTooltip();
1383 }
1384 if (vbodialogfests_on === true) {
1385 hideVboDialogFests();
1386 }
1387 if (vbodialogrdaynotes_on === true) {
1388 hideVboDialogRdaynotes();
1389 }
1390 }
1391 });
1392
1393 jQuery(document).mouseup(function(e) {
1394 if (!hovtip && !vbodialogfests_on && !vbodialogrdaynotes_on) {
1395 return false;
1396 }
1397 if (hovtip) {
1398 var vbo_overlay_cont = jQuery(".vbo-overview-tipblock");
1399 if (!vbo_overlay_cont.is(e.target) && vbo_overlay_cont.has(e.target).length === 0) {
1400 hideVboTooltip();
1401 return true;
1402 }
1403 }
1404 if (vbodialogfests_on) {
1405 var vbo_overlay_cont = jQuery(".vbo-modal-overlay-content-fests");
1406 if (!vbo_overlay_cont.is(e.target) && vbo_overlay_cont.has(e.target).length === 0) {
1407 hideVboDialogFests();
1408 }
1409 }
1410 if (vbodialogrdaynotes_on) {
1411 var vbo_overlay_cont = jQuery(".vbo-modal-overlay-content-roomdaynotes");
1412 if (!vbo_overlay_cont.is(e.target) && vbo_overlay_cont.has(e.target).length === 0) {
1413 hideVboDialogRdaynotes();
1414 }
1415 }
1416 });
1417
1418 // fests
1419 jQuery(document.body).on("click", "td.vbo-tableaux-daycell", function() {
1420 if (jQuery(this).hasClass('skip-tableaux-daycell-click')) {
1421 return;
1422 }
1423 var ymd = jQuery(this).attr('data-ymd');
1424 var daytitle = jQuery(this).attr('data-readymd');
1425 if (jQuery(this).hasClass('vbo-tableaux-festcell')) {
1426 // cell has fests
1427 if (!vboFests.hasOwnProperty(ymd)) {
1428 return;
1429 }
1430 vboRenderFests(ymd, daytitle);
1431 } else {
1432 // let the admin create a new fest
1433 // set day title
1434 jQuery('.vbo-modal-overlay-content-fests').find('h3').find('span').text(daytitle);
1435 // update ymd key for the selected date, useful for adding new fests
1436 jQuery('.vbo-overlay-fests-addnew').attr('data-ymd', ymd);
1437 // unset content and display modal for just adding a new fest
1438 jQuery('.vbo-overlay-fests-list').html('');
1439 jQuery('.vbo-modal-overlay-block-fests').fadeIn();
1440 vbodialogfests_on = true;
1441 }
1442 });
1443 //
1444
1445 // room-day notes
1446 jQuery(document.body).on("click", ".vbo-roomdaynote-display", function() {
1447 if (!jQuery(this).closest('.vbo-roomdaynote-trigger').length) {
1448 return;
1449 }
1450 var daytitle = new Array;
1451 var roomday_info = jQuery(this).closest('.vbo-roomdaynote-trigger').attr('data-roomday').split('_');
1452 // readable day
1453 var readymd = roomday_info[0];
1454 if (jQuery('.vbo-tableaux-daycell[data-ymd="' + roomday_info[0] + '"]').length) {
1455 readymd = jQuery('.vbo-tableaux-daycell[data-ymd="' + roomday_info[0] + '"]').attr('data-readymd');
1456 }
1457 daytitle.push(readymd);
1458 // room name
1459 if (jQuery('.vbo-tableaux-roomname[data-roomid="' + roomday_info[1] + '"]').length) {
1460 daytitle.push(jQuery('.vbo-tableaux-roomname[data-roomid="' + roomday_info[1] + '"]').text());
1461 }
1462 // set day title
1463 jQuery('.vbo-modal-overlay-content-head-roomdaynotes').find('h3').find('span').text(daytitle.join(', '));
1464 // populate current room day notes
1465 vboRenderRdayNotes(roomday_info[0], roomday_info[1], roomday_info[2], readymd);
1466 // display modal
1467 jQuery('.vbo-modal-overlay-block-roomdaynotes').fadeIn();
1468 vbodialogrdaynotes_on = true;
1469 //
1470 });
1471 //
1472
1473 // use sticky headers only if the table is longer than the max viewport height
1474 fittingTableaux = vboFitsInViewport('.vbo-tableaux-table');
1475 if (!fittingTableaux) {
1476 // display table head
1477 jQuery('.vbo-tableaux-table-head').show();
1478 // set sticky head for the vertical scrolling
1479 jQuery('table.vbo-tableaux-table').stickyTableHeaders({cacheHeaderHeight: true, fixedOffset: jQuery('.navbar')});
1480 tableauxStickySet = true;
1481 } else {
1482 // hide table head
1483 jQuery('.vbo-tableaux-table-head').hide();
1484 }
1485
1486 <?php
1487 if ($printtableaux > 0) {
1488 ?>
1489 window.print();
1490 <?php
1491 }
1492 ?>
1493 });
1494 </script>
1495