PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
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 1.7.3 All 36 releases
vikbooking / admin / views / tableaux / tmpl / default.php

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

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