PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.6.9
VikBooking Hotel Booking Engine & PMS v1.6.9
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.6.9, at admin/views/tableaux/tmpl/default.php

1,463 lines 59.3 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 if (hovtimer) {
720 clearTimeout(hovtimer);
721 hovtimer = null;
722 }
723 var elem = jQuery(that);
724 var cellheight = elem.outerHeight();
725 hovtimer = setTimeout(function() {
726 hovtip = true;
727 jQuery(
728 "<div class=\"vbo-overview-tipblock\">"+
729 "<div class=\"vbo-overview-tipinner\"><span class=\"vbo-overview-tiploading\">"+vboMessages.loadingTip+"</span></div>"+
730 "<div class=\"vbo-overview-tipexpander\" style=\"display: none;\"><div class=\"vbo-overview-expandtoggle\"><i class=\"<?php echo VikBookingIcons::i('expand'); ?>\"></i></div></div>"+
731 "</div>"
732 ).appendTo(elem);
733 jQuery(".vbo-overview-tipblock").css("bottom", "+="+cellheight);
734 loadTooltipBookings(elem.attr('data-bid'));
735 }, 900);
736 }
737
738 function unregisterHoveringTooltip() {
739 clearTimeout(hovtimer);
740 hovtimer = null;
741 }
742
743 function adjustHoveringTooltip() {
744 setTimeout(function() {
745 var difflim = 35;
746 var otop = jQuery(".vbo-overview-tipblock").offset().top;
747 if (otop < difflim) {
748 jQuery(".vbo-overview-tipblock").css("bottom", "-="+(difflim - otop));
749 } else {
750 // check top position from table
751 var tipheight = jQuery(".vbo-overview-tipblock").outerHeight();
752 var blocktop = jQuery(".vbo-overview-tipblock").parent().offset().top;
753 var tabletop = jQuery(".vbo-tableaux-table").offset().top;
754 if ((blocktop - tabletop) < tipheight) {
755 // tooltip is going under the table, move it down the container
756 jQuery(".vbo-overview-tipblock").css("bottom", "-"+tipheight+'px');
757 }
758 // check left position from second cell
759 var tipwidth = jQuery(".vbo-overview-tipblock").outerWidth();
760 var cellscndleft = jQuery('.vbo-tableaux-table').find('tr td').first().next().offset().left;
761 var blockleft = jQuery('.vbo-overview-tipblock').parent().offset().left;
762 if (tipwidth > (blockleft - cellscndleft)) {
763 // tooltip should start from left: 0 going to the right
764 jQuery(".vbo-overview-tipblock").css("left", "0px");
765 }
766 }
767 }, 100);
768 }
769
770 function hideVboTooltip() {
771 jQuery('.vbo-overview-tipblock').remove();
772 hovtip = false;
773 }
774
775 function loadTooltipBookings(bid) {
776 if (!bid || bid === undefined || !bid.length) {
777 hideVboTooltip();
778 return false;
779 }
780 // ajax request
781 var jqxhr = jQuery.ajax({
782 type: "POST",
783 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=getbookingsinfo'); ?>",
784 data: {
785 tmpl: "component",
786 idorders: bid
787 }
788 }).done(function(res) {
789 try {
790 var obj_res = typeof res === 'string' ? JSON.parse(res) : res;
791 jQuery('.vbo-overview-tiploading').remove();
792 var container = jQuery('.vbo-overview-tipinner');
793 jQuery(obj_res).each(function(k, v) {
794 // get base navigation URIs
795 var base_uri_details = jQuery('.vbo-basenavuri-details').attr('href');
796 var base_uri_edit = jQuery('.vbo-basenavuri-edit').attr('href');
797
798 // build content
799 var bcont = "<div class=\"vbo-overview-tip-bookingcont\">";
800 bcont += "<div class=\"vbo-overview-tip-bookingcont-left\">";
801 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>";
802 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=\"label "+(v.status == 'confirmed' ? 'label-success' : 'label-warning')+"\">"+v.status_lbl+"</div></span></div>";
803 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>";
804 bcont += "</div>";
805 bcont += "<div class=\"vbo-overview-tip-bookingcont-right\">";
806 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>";
807 if (v.roomsnum > 1) {
808 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>";
809 }
810 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>";
811 if (v.hasOwnProperty('rindexes')) {
812 for (var rindexk in v.rindexes) {
813 if (v.rindexes.hasOwnProperty(rindexk)) {
814 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>";
815 }
816 }
817 }
818 if (v.hasOwnProperty('channelimg')) {
819 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>";
820 }
821 bcont += "<div class=\"vbo-overview-tip-bookingcont-total\">";
822 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>";
823 if (v.totpaid > 0.00) {
824 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>";
825 }
826 var getnotes = v.adminnotes;
827 if (getnotes !== null && getnotes.length) {
828 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>";
829 }
830 bcont += "</div>";
831 bcont += "</div>";
832 bcont += "</div>";
833 container.append(bcont);
834 jQuery('.vbo-overview-tipexpander').show();
835 });
836 // adjust the position so that it won't go under other contents
837 adjustHoveringTooltip()
838 //
839 } catch(err) {
840 // restore
841 hideVboTooltip();
842 // display error
843 console.error('could not parse JSON response', err, res);
844 alert('Could not parse JSON response');
845 }
846 }).fail(function(err) {
847 // restore
848 hideVboTooltip();
849 // display error
850 console.error(err);
851 alert(err.responseText);
852 });
853 }
854
855 function vboSendPrintTableaux() {
856 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();
857 setTimeout(function() {
858 jQuery('#adminForm').attr('target', '_self');
859 jQuery('.vbo-tableaux-rmafterprint').remove();
860 }, 500);
861 }
862
863 /**
864 * Fests
865 */
866 function vboRenderFests(day, daytitle) {
867 // set day title
868 if (daytitle) {
869 jQuery('.vbo-modal-overlay-content-fests').find('h3').find('span').text(daytitle);
870 }
871 // compose fests information
872 var fests_html = '';
873 if (vboFests[day] && vboFests[day]['festinfo'] && vboFests[day]['festinfo'].length) {
874 for (var i = 0; i < vboFests[day]['festinfo'].length; i++) {
875 var fest = vboFests[day]['festinfo'][i];
876 fests_html += '<div class="vbo-overlay-fest-details">';
877 fests_html += ' <div class="vbo-fest-info">';
878 fests_html += ' <div class="vbo-fest-name">' + fest['trans_name'] + '</div>';
879 fests_html += ' <div class="vbo-fest-desc">' + fest['descr'].replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + '<br />' + '$2') + '</div>';
880 fests_html += ' </div>';
881 fests_html += ' <div class="vbo-fest-cmds">';
882 fests_html += ' <button type="button" class="btn btn-danger" onclick="vboRemoveFest(\'' + day + '\', \'' + i + '\', \'' + fest['type'] + '\', this);"><?php VikBookingIcons::e('trash-alt'); ?></button>';
883 fests_html += ' </div>';
884 fests_html += '</div>';
885 }
886 }
887 // update ymd key for the selected date, useful for adding new fests
888 jQuery('.vbo-overlay-fests-addnew').attr('data-ymd', day);
889 // set content and display modal
890 jQuery('.vbo-overlay-fests-list').html(fests_html);
891 jQuery('.vbo-modal-overlay-block-fests').fadeIn();
892 vbodialogfests_on = true;
893 }
894
895 function vboRemoveFest(day, index, fest_type, that) {
896 if (!confirm('<?php echo addslashes(JText::translate('VBDELCONFIRM')); ?>')) {
897 return false;
898 }
899 var elem = jQuery(that);
900 // make the AJAX request to the controller to remove this fest from the DB
901 var jqxhr = jQuery.ajax({
902 type: "POST",
903 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=remove_fest'); ?>",
904 data: {
905 tmpl: "component",
906 dt: day,
907 ind: index,
908 type: fest_type
909 }
910 }).done(function(res) {
911 if (res.indexOf('e4j.ok') >= 0) {
912 // delete fest also from the json-decode array of objects
913 if (vboFests[day] && vboFests[day]['festinfo']) {
914 // use splice to remove the desired index from array, or delete would not make the length of the array change
915 vboFests[day]['festinfo'].splice(index, 1);
916 // re-build indexes of delete buttons, fundamental for removing the right index at next click
917 vboRenderFests(day);
918 if (!vboFests[day]['festinfo'].length) {
919 // delete also this date object from fests
920 delete vboFests[day];
921 // no more fests, remove the class for this date from all cells
922 jQuery('td.vbo-tableaux-daycell[data-ymd="'+day+'"]').removeClass('vbo-tableaux-festcell');
923 }
924 }
925 elem.closest('.vbo-overlay-fest-details').remove();
926 } else {
927 console.log(res);
928 alert('Invalid response');
929 }
930 }).fail(function() {
931 alert('Request failed');
932 });
933 }
934
935 function vboAddFest(that) {
936 var elem = jQuery(that);
937 var ymd = elem.closest('.vbo-overlay-fests-addnew').attr('data-ymd');
938 var fest_name = jQuery('#vbo-newfest-name').val();
939 var fest_descr = jQuery('#vbo-newfest-descr').val();
940 if (!fest_name.length) {
941 return false;
942 }
943 // make the AJAX request to the controller to add this fest to the DB
944 var jqxhr = jQuery.ajax({
945 type: "POST",
946 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=add_fest'); ?>",
947 data: {
948 tmpl: "component",
949 dt: ymd,
950 type: "custom",
951 name: fest_name,
952 descr: fest_descr
953 }
954 }).done(function(res) {
955 // parse the JSON response that contains the fest object for the passed date
956 try {
957 var stored_fest = JSON.parse(res);
958 if (!vboFests.hasOwnProperty(stored_fest['dt'])) {
959 // we need to add the proper class to all cells to show that there is a fest
960 jQuery('td.vbo-tableaux-daycell[data-ymd="'+stored_fest['dt']+'"]').addClass('vbo-tableaux-festcell');
961 }
962 vboFests[stored_fest['dt']] = stored_fest;
963 hideVboDialogFests();
964 // reset input fields
965 jQuery('#vbo-newfest-name').val('');
966 jQuery('#vbo-newfest-descr').val('');
967 } catch (e) {
968 console.log(res);
969 alert('Invalid response');
970 return false;
971 }
972 }).fail(function() {
973 alert('Request failed');
974 });
975 }
976 //
977
978 /**
979 * Room-day notes
980 */
981 var rdaynote_icn_full = '<?php echo VikBookingIcons::i('sticky-note', 'vbo-roomdaynote-display'); ?>';
982 var rdaynote_icn_empty = '<?php echo VikBookingIcons::i('far fa-sticky-note', 'vbo-roomdaynote-display'); ?>';
983 var rooms_features_map = JSON.parse('<?php echo isset($rooms_features_map) && count($rooms_features_map) ? json_encode($rooms_features_map) : json_encode(array()); ?>');
984
985 function vboRenderRdayNotes(day, idroom, subunit, readymd) {
986 // compose room-day notes information (subunit always = 0 for room-day level)
987 var notes_html = '';
988 var keyids = new Array;
989 // always push 0 as the room-level day note key
990 keyids.push(0);
991 // grab all the other note keys at sub-unit level
992 if (jQuery('.vbo-tableaux-roombooks[data-dayrid="' + day + idroom + '"]').length) {
993 var all_nkeys = jQuery('.vbo-tableaux-roombooks[data-dayrid="' + day + idroom + '"]').attr('data-rdnkeys');
994 if (all_nkeys && all_nkeys.length) {
995 var nkeys = all_nkeys.split(',');
996 for (var k in nkeys) {
997 var int_index = parseInt(nkeys[k]);
998 if (!nkeys.hasOwnProperty(k) || isNaN(int_index) || int_index < 1) {
999 continue;
1000 }
1001 // push sub-unit key for lookup
1002 keyids.push(nkeys[k]);
1003 }
1004 }
1005 }
1006 // parse all room keys with notes
1007 for (var z = 0; z < keyids.length; z++) {
1008 // this is the real sub-unit we are parsing
1009 var subunit_index = keyids[z];
1010 var keyid = day + '_' + idroom + '_' + subunit_index;
1011 if (vboRdayNotes.hasOwnProperty(keyid) && vboRdayNotes[keyid]['info'] && vboRdayNotes[keyid]['info'].length) {
1012 for (var i = 0; i < vboRdayNotes[keyid]['info'].length; i++) {
1013 var note_data = vboRdayNotes[keyid]['info'][i];
1014 var subunit_name = '';
1015 if (parseInt(subunit_index) > 0) {
1016 subunit_name = '(' + subunit_index + ') ';
1017 if (rooms_features_map.hasOwnProperty(idroom) && rooms_features_map[idroom].hasOwnProperty(subunit_index)) {
1018 subunit_name = rooms_features_map[idroom][subunit_index] + ' - ';
1019 }
1020 }
1021 notes_html += '<div class="vbo-overlay-fest-details vbo-modal-roomdaynotes-note-details">';
1022 notes_html += ' <div class="vbo-fest-info vbo-modal-roomdaynotes-note-info">';
1023 notes_html += ' <div class="vbo-fest-name vbo-modal-roomdaynotes-note-name">' + subunit_name + note_data['name'] + '</div>';
1024 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>';
1025 notes_html += ' </div>';
1026 notes_html += ' <div class="vbo-fest-cmds vbo-modal-roomdaynotes-note-cmds">';
1027 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>';
1028 notes_html += ' </div>';
1029 notes_html += '</div>';
1030 }
1031 }
1032 }
1033 // update attributes keys for the selected date, useful for adding new notes (always at room-level)
1034 jQuery('.vbo-modal-roomdaynotes-addnew').attr('data-ymd', day).attr('data-roomid', idroom).attr('data-subroomid', '0');
1035 if (readymd !== null) {
1036 jQuery('.vbo-modal-roomdaynotes-addnew').attr('data-readymd', readymd);
1037 jQuery('.vbo-newrdnote-dayto-val').text(readymd);
1038 }
1039 // set content and display modal
1040 jQuery('.vbo-modal-roomdaynotes-list').html(notes_html);
1041 }
1042
1043 function vboAddRoomDayNote(that) {
1044 var mainelem = jQuery(that).closest('.vbo-modal-roomdaynotes-addnew');
1045 var ymd = mainelem.attr('data-ymd');
1046 var roomid = mainelem.attr('data-roomid');
1047 var subroomid = mainelem.attr('data-subroomid');
1048 var note_name = jQuery('#vbo-newrdnote-name').val();
1049 var note_descr = jQuery('#vbo-newrdnote-descr').val();
1050 var note_cdays = jQuery('#vbo-newrdnote-cdays').val();
1051 if (!note_name.length && !note_descr.length) {
1052 alert('Missing required fields');
1053 return false;
1054 }
1055 // make the AJAX request to the controller to add this note to the DB
1056 var jqxhr = jQuery.ajax({
1057 type: "POST",
1058 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=add_roomdaynote'); ?>",
1059 data: {
1060 tmpl: "component",
1061 dt: ymd,
1062 idroom: roomid,
1063 subunit: subroomid,
1064 type: "custom",
1065 name: note_name,
1066 descr: note_descr,
1067 cdays: note_cdays
1068 }
1069 }).done(function(res) {
1070 // parse the JSON response that contains the note object for the passed date
1071 try {
1072 var stored_notes = JSON.parse(res);
1073 var counter = 0;
1074 for (var keyid in stored_notes) {
1075 if (!stored_notes.hasOwnProperty(keyid)) {
1076 continue;
1077 }
1078 var is_date_visible = (jQuery('.vbo-roomdaynote-trigger[data-roomday="' + keyid + '"]').length > 0);
1079 if (!vboRdayNotes.hasOwnProperty(keyid) && is_date_visible) {
1080 // we need to add the proper class to the cell for this note (if it's visible)
1081 jQuery('.vbo-roomdaynote-trigger[data-roomday="' + keyid + '"]').parent('td').removeClass('vbo-roomdaynote-empty').addClass('vbo-roomdaynote-full').find('i').attr('class', rdaynote_icn_full);
1082 }
1083 // update global object with the new notes in any case
1084 vboRdayNotes[keyid] = stored_notes[keyid];
1085 // update readable notes in custom data attribute
1086 if (counter < 1 && is_date_visible && note_name.length) {
1087 // we push just this name
1088 var current_cell_notes = jQuery('.vbo-roomdaynote-trigger[data-roomday="' + keyid + '"]').parent('td').attr('data-notes');
1089 var all_cell_notes = current_cell_notes && current_cell_notes.length ? current_cell_notes.split(', ') : new Array;
1090 all_cell_notes.push(note_name);
1091 jQuery('.vbo-roomdaynote-trigger[data-roomday="' + keyid + '"]').parent('td').attr('data-notes', all_cell_notes.join(', '));
1092 }
1093 // increase counter
1094 counter++;
1095 }
1096 // close modal
1097 hideVboDialogRdaynotes();
1098 // reset input fields
1099 jQuery('#vbo-newrdnote-name').val('');
1100 jQuery('#vbo-newrdnote-descr').val('');
1101 jQuery('#vbo-newrdnote-cdays').val('0').trigger('change');
1102 } catch (e) {
1103 console.log(res);
1104 alert('Invalid response');
1105 return false;
1106 }
1107 }).fail(function() {
1108 alert('Request failed');
1109 });
1110 }
1111
1112 function vboRdayNotesAvailable(day, idroom) {
1113 /**
1114 * We check whether a room has notes at parent or subunit level for the given date.
1115 */
1116 var base_key = day + '_' + idroom + '_';
1117 var notes_available = false;
1118 for (var k in vboRdayNotes) {
1119 if (!vboRdayNotes.hasOwnProperty(k) || k.indexOf(base_key) < 0) {
1120 continue;
1121 }
1122 // notes found for this room, either parent or sub-unit
1123 if (vboRdayNotes[k]['info'] && vboRdayNotes[k]['info'].length) {
1124 notes_available = true;
1125 break;
1126 }
1127 }
1128
1129 return notes_available;
1130 }
1131
1132 function vboRemoveRdayNote(index, day, idroom, subunit, note_type, that) {
1133 if (!confirm('<?php echo addslashes(JText::translate('VBDELCONFIRM')); ?>')) {
1134 return false;
1135 }
1136 var elem = jQuery(that);
1137 // make the AJAX request to the controller to remove this note from the DB
1138 var jqxhr = jQuery.ajax({
1139 type: "POST",
1140 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=remove_roomdaynote'); ?>",
1141 data: {
1142 tmpl: "component",
1143 dt: day,
1144 idroom: idroom,
1145 subunit: subunit,
1146 ind: index,
1147 type: note_type
1148 }
1149 }).done(function(res) {
1150 if (res.indexOf('e4j.ok') >= 0) {
1151 var keyid = day + '_' + idroom + '_' + subunit;
1152 var glob_keyid = day + '_' + idroom + '_0';
1153 // delete note also from the json-decode array of objects
1154 if (vboRdayNotes[keyid] && vboRdayNotes[keyid]['info']) {
1155 // get the original note name
1156 var old_name = vboRdayNotes[keyid]['info'][index]['name'];
1157 // use splice to remove the desired index from array, or delete would not make the length of the array change
1158 vboRdayNotes[keyid]['info'].splice(index, 1);
1159 // re-build indexes of delete buttons, fundamental for removing the right index at next click (reload always at room-level)
1160 vboRenderRdayNotes(day, idroom, '0', null);
1161 if (!vboRdayNotes[keyid]['info'].length && !vboRdayNotesAvailable(day, idroom)) {
1162 // delete also this date object from notes
1163 delete vboRdayNotes[keyid];
1164 // no more notes, update the proper class attribute for this cell (should be visible)
1165 if (jQuery('.vbo-roomdaynote-trigger[data-roomday="' + glob_keyid + '"]').length) {
1166 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);
1167 }
1168 } else if (old_name && old_name.length) {
1169 // try to adjust the readable notes in custom data attribute
1170 var current_cell_notes = jQuery('.vbo-roomdaynote-trigger[data-roomday="' + glob_keyid + '"]').parent('td').attr('data-notes');
1171 var all_cell_notes = current_cell_notes && current_cell_notes.length ? current_cell_notes.split(', ') : new Array;
1172 var new_cell_notes = new Array;
1173 for (var i = 0; i < all_cell_notes.length; i++) {
1174 if (all_cell_notes[i].indexOf(old_name) < 0) {
1175 new_cell_notes.push(all_cell_notes[i]);
1176 }
1177 }
1178 jQuery('.vbo-roomdaynote-trigger[data-roomday="' + glob_keyid + '"]').parent('td').attr('data-notes', new_cell_notes.join(', '));
1179 }
1180 }
1181 elem.closest('.vbo-modal-roomdaynotes-note-details').remove();
1182 } else {
1183 console.log(res);
1184 alert('Invalid response');
1185 }
1186 }).fail(function() {
1187 alert('Request failed');
1188 });
1189 }
1190
1191 function vboRdayNoteCdaysCount() {
1192 var cdays = parseInt(jQuery('#vbo-newrdnote-cdays').val());
1193 var defymd = jQuery('.vbo-modal-roomdaynotes-addnew').attr('data-ymd');
1194 var defreadymd = jQuery('.vbo-modal-roomdaynotes-addnew').attr('data-readymd');
1195 defreadymd = !defreadymd || !defreadymd.length ? defymd : defreadymd;
1196 if (isNaN(cdays) || cdays < 1) {
1197 jQuery('.vbo-newrdnote-dayto-val').text(defreadymd);
1198 return;
1199 }
1200 // calculate target (until) date
1201 var targetdate = new Date(defymd);
1202 targetdate.setDate(targetdate.getDate() + cdays);
1203 var target_y = targetdate.getFullYear();
1204 var target_m = targetdate.getMonth() + 1;
1205 target_m = target_m < 10 ? '0' + target_m : target_m;
1206 var target_d = targetdate.getDate();
1207 target_d = target_d < 10 ? '0' + target_d : target_d;
1208 // display target date
1209 var display_target = target_y + '-' + target_m + '-' + target_d;
1210 // check if we can get the "read ymd property"
1211 if (jQuery('.vbo-tableaux-daycell[data-ymd="' + display_target + '"]').length) {
1212 display_target = jQuery('.vbo-tableaux-daycell[data-ymd="' + display_target + '"]').attr('data-readymd');
1213 }
1214 jQuery('.vbo-newrdnote-dayto-val').text(display_target);
1215 }
1216 //
1217
1218 /**
1219 * Tells whether an element can fit entirely in the viewport.
1220 * Example: if a table is too high to fit in the screen.
1221 * If false, it means that scrolling will always be needed.
1222 */
1223 function vboFitsInViewport(selector) {
1224 var elemHeight = jQuery(selector).outerHeight();
1225 var maxHeight = jQuery(window).height();
1226
1227 return elemHeight < maxHeight;
1228 }
1229
1230 // tableaux scrolling
1231 var fittingTableaux = null;
1232 var tableauxStickySet = false;
1233
1234 /**
1235 * Throttle guarantees a constant flow of events at a given time interval,
1236 * but it runs immediately when the event takes place. We rather need a
1237 * debounce technique to group a flurry of events into one single event.
1238 * This is useful for listening to the scrolling event of the table, actually
1239 * the DIV that wraps it with overflow, and setting the sticky headers.
1240 */
1241 function vboDebounceScroll(func, wait, immediate) {
1242 var timeout;
1243 return function() {
1244 var context = this, args = arguments;
1245 var later = function() {
1246 timeout = null;
1247 if (!immediate) func.apply(context, args);
1248 };
1249 var callNow = immediate && !timeout;
1250 clearTimeout(timeout);
1251 timeout = setTimeout(later, wait);
1252 if (callNow) {
1253 func.apply(context, args);
1254 }
1255 }
1256 }
1257 /**
1258 * We do not need a throttle technique, but this method would
1259 * throttle the scroll event rather than debouncing it.
1260 */
1261 function vboThrottleScroll(method, delay) {
1262 var time = Date.now();
1263 return function() {
1264 if ((time + delay - Date.now()) < 0) {
1265 method();
1266 time = Date.now();
1267 }
1268 }
1269 }
1270
1271 /**
1272 * This will fire during the throttle of the scrolling event.
1273 */
1274 function vboHandleScroll() {
1275 if (fittingTableaux === null) {
1276 fittingTableaux = vboFitsInViewport('.vbo-tableaux-table');
1277 }
1278 if (fittingTableaux) {
1279 // no sticky table headers needed, remove the listener as it is not needed
1280 document.getElementById('vbo-tableaux-table-scroller').removeEventListener('scroll', vboDebounceScroll(vboHandleScroll, 1000));
1281 return;
1282 }
1283 // we have scrolled (vertically or horizontally), update the position of the sticky table head
1284 if (tableauxStickySet) {
1285 // destroy sticky table head
1286 jQuery('table.vbo-tableaux-table').stickyTableHeaders('destroy');
1287 }
1288 // re-build sticky head
1289 jQuery('table.vbo-tableaux-table').stickyTableHeaders({cacheHeaderHeight: true, fixedOffset: jQuery('.navbar')});
1290 tableauxStickySet = true;
1291 }
1292
1293 jQuery(function() {
1294 // add listener to scroll event on the main table
1295 document.getElementById('vbo-tableaux-table-scroller').addEventListener('scroll', vboDebounceScroll(vboHandleScroll, 1000));
1296
1297 jQuery("#roomsel, .vbo-perm-roomsel").select2({placeholder: '<?php echo addslashes(JText::translate('VBCOUPONALLVEHICLES')); ?>', width: "300px", allowClear: true});
1298
1299 jQuery('#vbo-date-from').datepicker({
1300 showOn: 'focus',
1301 dateFormat: '<?php echo $juidf; ?>',
1302 <?php echo ($this->mindate > 0 ? 'minDate: "'.date(str_replace('%', '', $nowdf), $this->mindate).'", ' : '').($this->maxdate > 0 ? 'maxDate: "'.date(str_replace('%', '', $nowdf), $this->maxdate).'", ' : ''); ?>
1303 onSelect: function( selectedDate ) {
1304 jQuery('#vbo-date-to').datepicker('option', 'minDate', selectedDate);
1305 jQuery('#vbo-month').val('');
1306 }
1307 });
1308
1309 jQuery('#vbo-date-to').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-from').datepicker('option', 'maxDate', selectedDate);
1315 jQuery('#vbo-month').val('');
1316 }
1317 });
1318
1319 jQuery('#vbo-date-from-trig, #vbo-date-to-trig').click(function() {
1320 var jdp = jQuery(this).prev('input.hasDatepicker');
1321 if (jdp.length) {
1322 jdp.focus();
1323 }
1324 });
1325
1326 jQuery('.vbo-tableaux-selmonth').click(function() {
1327 var xts = jQuery(this).attr('data-ts');
1328 var xfrom = jQuery(this).attr('data-from');
1329 var xto = jQuery(this).attr('data-to');
1330 jQuery('#vbo-month').val(xts);
1331 jQuery('#vbo-date-from').val(xfrom);
1332 jQuery('#vbo-date-to').val(xto);
1333 jQuery('#adminForm').submit();
1334 });
1335
1336 /* Expand/Collapse tooltip */
1337 jQuery(document.body).on("click", ".vbo-overview-expandtoggle", function() {
1338 jQuery(this).closest('.vbo-overview-tipblock').toggleClass('vbo-overview-tipblock-expanded');
1339 });
1340 /* ----------------------- */
1341
1342 /* Hover Tooltip */
1343 jQuery('.vbo-tableaux-booking-checkin, .vbo-tableaux-booking-checkout, .vbo-tableaux-booking-stay').hover(function() {
1344 registerHoveringTooltip(this);
1345 }, unregisterHoveringTooltip);
1346
1347 jQuery(document).keydown(function(e) {
1348 if (e.keyCode == 27) {
1349 if (hovtip === true) {
1350 hideVboTooltip();
1351 }
1352 if (vbodialogfests_on === true) {
1353 hideVboDialogFests();
1354 }
1355 if (vbodialogrdaynotes_on === true) {
1356 hideVboDialogRdaynotes();
1357 }
1358 }
1359 });
1360
1361 jQuery(document).mouseup(function(e) {
1362 if (!hovtip && !vbodialogfests_on && !vbodialogrdaynotes_on) {
1363 return false;
1364 }
1365 if (hovtip) {
1366 var vbo_overlay_cont = jQuery(".vbo-overview-tipblock");
1367 if (!vbo_overlay_cont.is(e.target) && vbo_overlay_cont.has(e.target).length === 0) {
1368 hideVboTooltip();
1369 return true;
1370 }
1371 }
1372 if (vbodialogfests_on) {
1373 var vbo_overlay_cont = jQuery(".vbo-modal-overlay-content-fests");
1374 if (!vbo_overlay_cont.is(e.target) && vbo_overlay_cont.has(e.target).length === 0) {
1375 hideVboDialogFests();
1376 }
1377 }
1378 if (vbodialogrdaynotes_on) {
1379 var vbo_overlay_cont = jQuery(".vbo-modal-overlay-content-roomdaynotes");
1380 if (!vbo_overlay_cont.is(e.target) && vbo_overlay_cont.has(e.target).length === 0) {
1381 hideVboDialogRdaynotes();
1382 }
1383 }
1384 });
1385
1386 // fests
1387 jQuery(document.body).on("click", "td.vbo-tableaux-daycell", function() {
1388 if (jQuery(this).hasClass('skip-tableaux-daycell-click')) {
1389 return;
1390 }
1391 var ymd = jQuery(this).attr('data-ymd');
1392 var daytitle = jQuery(this).attr('data-readymd');
1393 if (jQuery(this).hasClass('vbo-tableaux-festcell')) {
1394 // cell has fests
1395 if (!vboFests.hasOwnProperty(ymd)) {
1396 return;
1397 }
1398 vboRenderFests(ymd, daytitle);
1399 } else {
1400 // let the admin create a new fest
1401 // set day title
1402 jQuery('.vbo-modal-overlay-content-fests').find('h3').find('span').text(daytitle);
1403 // update ymd key for the selected date, useful for adding new fests
1404 jQuery('.vbo-overlay-fests-addnew').attr('data-ymd', ymd);
1405 // unset content and display modal for just adding a new fest
1406 jQuery('.vbo-overlay-fests-list').html('');
1407 jQuery('.vbo-modal-overlay-block-fests').fadeIn();
1408 vbodialogfests_on = true;
1409 }
1410 });
1411 //
1412
1413 // room-day notes
1414 jQuery(document.body).on("click", ".vbo-roomdaynote-display", function() {
1415 if (!jQuery(this).closest('.vbo-roomdaynote-trigger').length) {
1416 return;
1417 }
1418 var daytitle = new Array;
1419 var roomday_info = jQuery(this).closest('.vbo-roomdaynote-trigger').attr('data-roomday').split('_');
1420 // readable day
1421 var readymd = roomday_info[0];
1422 if (jQuery('.vbo-tableaux-daycell[data-ymd="' + roomday_info[0] + '"]').length) {
1423 readymd = jQuery('.vbo-tableaux-daycell[data-ymd="' + roomday_info[0] + '"]').attr('data-readymd');
1424 }
1425 daytitle.push(readymd);
1426 // room name
1427 if (jQuery('.vbo-tableaux-roomname[data-roomid="' + roomday_info[1] + '"]').length) {
1428 daytitle.push(jQuery('.vbo-tableaux-roomname[data-roomid="' + roomday_info[1] + '"]').text());
1429 }
1430 // set day title
1431 jQuery('.vbo-modal-overlay-content-head-roomdaynotes').find('h3').find('span').text(daytitle.join(', '));
1432 // populate current room day notes
1433 vboRenderRdayNotes(roomday_info[0], roomday_info[1], roomday_info[2], readymd);
1434 // display modal
1435 jQuery('.vbo-modal-overlay-block-roomdaynotes').fadeIn();
1436 vbodialogrdaynotes_on = true;
1437 //
1438 });
1439 //
1440
1441 // use sticky headers only if the table is longer than the max viewport height
1442 fittingTableaux = vboFitsInViewport('.vbo-tableaux-table');
1443 if (!fittingTableaux) {
1444 // display table head
1445 jQuery('.vbo-tableaux-table-head').show();
1446 // set sticky head for the vertical scrolling
1447 jQuery('table.vbo-tableaux-table').stickyTableHeaders({cacheHeaderHeight: true, fixedOffset: jQuery('.navbar')});
1448 tableauxStickySet = true;
1449 } else {
1450 // hide table head
1451 jQuery('.vbo-tableaux-table-head').hide();
1452 }
1453
1454 <?php
1455 if ($printtableaux > 0) {
1456 ?>
1457 window.print();
1458 <?php
1459 }
1460 ?>
1461 });
1462 </script>
1463