PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.7.2
VikBooking Hotel Booking Engine & PMS v1.7.2
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 / overv / tmpl / default.php

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

3,414 lines 136.5 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 $rows = $this->rows;
14 $arrbusy = $this->arrbusy;
15 $wmonthsel = $this->wmonthsel;
16 $tsstart = $this->tsstart;
17 $lim0 = $this->lim0;
18 $navbut = $this->navbut;
19
20 $vbo_auth_pricing = JFactory::getUser()->authorise('core.vbo.pricing', 'com_vikbooking');
21 $vbo_app = VikBooking::getVboApplication();
22 JHtml::fetch('script', VBO_SITE_URI.'resources/jquery-ui.min.js');
23 JHtml::fetch('script', VBO_ADMIN_URI.'resources/js_upload/jquery.stickytableheaders.min.js');
24
25 $file_base = VBOPlatformDetection::isWordPress() ? 'admin.php' : 'index.php';
26
27 // JS lang defs
28 JText::script('VBSAVE');
29 JText::script('VBPVIEWORDERSTHREE');
30 JText::script('VBEDITORDERTHREE');
31 JText::script('VBDAYS');
32 JText::script('VBDAY');
33 JText::script('VBMAILADULTS');
34 JText::script('VBMAILADULT');
35 JText::script('VBMAILCHILDREN');
36 JText::script('VBMAILCHILD');
37 JText::script('VBO_MISSING_SUBUNIT');
38 JText::script('VBO_SHOW_CANCELLATIONS');
39 JText::script('VBO_DRAG_SUBUNITS_SAMEDATE');
40 JText::script('VBPVIEWORDERCHANNEL');
41 JText::script('VBANNULLA');
42 JText::script('VIKLOADING');
43 JText::script('VBOVWDNDERRNOTENCELLS');
44 JText::script('VBOVWDNDMOVINGBID');
45 JText::script('VBOVWDNDMOVINGROOM');
46 JText::script('VBOVWDNDMOVINGDATES');
47 JText::script('VBEDITORDERROOMSNUM');
48 JText::script('VBEDITORDERADULTS');
49 JText::script('VBPICKUPAT');
50 JText::script('VBRELEASEAT');
51 JText::script('VBEDITORDERCHILDREN');
52 JText::script('VBEDITORDERNINE');
53 JText::script('VBPEDITBUSYTOTPAID');
54 JText::script('VBOFEATASSIGNUNIT');
55 JText::script('VBO_SPLIT_STAY');
56 JText::script('VBO_CONF_SWAP_RNUMB');
57 JText::script('VBO_ERR_SUBUN_MOVE_SUBUN');
58 JText::script('VBO_BTYPE_OVERBOOKING');
59
60 $days_labels = [
61 JText::translate('VBSUN'),
62 JText::translate('VBMON'),
63 JText::translate('VBTUE'),
64 JText::translate('VBWED'),
65 JText::translate('VBTHU'),
66 JText::translate('VBFRI'),
67 JText::translate('VBSAT')
68 ];
69
70 $nowdf = VikBooking::getDateFormat();
71 if ($nowdf == "%d/%m/%Y") {
72 $df = 'd/m/Y';
73 } elseif ($nowdf == "%m/%d/%Y") {
74 $df = 'm/d/Y';
75 } else {
76 $df = 'Y/m/d';
77 }
78 $currencysymb = VikBooking::getCurrencySymb(true);
79
80 $pdebug = VikRequest::getInt('e4j_debug', '', 'request');
81 $session = JFactory::getSession();
82 $show_type = $session->get('vbUnitsShowType', '');
83 $mnum = $session->get('vbOvwMnum', '1');
84 $mnum = intval($mnum);
85 $pcategory_id = $session->get('vbOvwCatid', 0);
86 $cookie = JFactory::getApplication()->input->cookie;
87 $cookie_uleft = $cookie->get('vboAovwUleft', '', 'string');
88 $cookie_sticky_heads = $cookie->get('vboAovwStheads', 'off', 'string');
89 $colortags = VikBooking::loadBookingsColorTags();
90
91 // View mode - Classic or Tags
92 $pbmode = $session->get('vbTagsMode', 'classic');
93 $tags_view_supported = false;
94
95 // Room Units Distinctive Features
96 $rooms_features_map = [];
97 $rooms_features_bookings = [];
98 $rooms_bids_pools = [];
99 $room_bookings_pool = [];
100 $bids_checkins = [];
101 $index_loop = 0;
102 foreach ($rows as $kr => $room) {
103 if ($room['units'] <= 1) {
104 $tags_view_supported = true;
105 } else {
106 if (!empty($room['params']) && $room['units'] <= 150) {
107 //sub-room units only if room type has 150 units at most
108 $room_params = json_decode($room['params'], true);
109 if (is_array($room_params) && isset($room_params['features']) && is_array($room_params['features'])) {
110 $rooms_features_map[$room['id']] = [];
111 foreach ($room_params['features'] as $rind => $rfeatures) {
112 foreach ($rfeatures as $fname => $fval) {
113 if (strlen($fval)) {
114 // $rooms_features_map[$room['id']][$rind] = '#'.$rind.' - '.JText::translate($fname).': '.$fval;
115 $rooms_features_map[$room['id']][$rind] = "#{$rind} - {$fval}";
116 break;
117 }
118 }
119 }
120 if (!$rooms_features_map[$room['id']]) {
121 unset($rooms_features_map[$room['id']]);
122 } else {
123 foreach ($rooms_features_map[$room['id']] as $rind => $indexdata) {
124 $clone_room = $room;
125 $clone_room['unit_index'] = (int)$rind;
126 $clone_room['unit_index_str'] = $indexdata;
127 array_splice($rows, ($kr + 1 + $index_loop), 0, [$clone_room]);
128 $index_loop++;
129 }
130 }
131 }
132 }
133 }
134 }
135
136 if (!$tags_view_supported) {
137 $pbmode = 'classic';
138 }
139
140 // locked (stand-by) records
141 $arrlocked = [];
142 if (array_key_exists('tmplock', $arrbusy)) {
143 if (count($arrbusy['tmplock']) > 0) {
144 $arrlocked = $arrbusy['tmplock'];
145 }
146 unset($arrbusy['tmplock']);
147 }
148
149 /**
150 * Global closing dates.
151 *
152 * @since 1.17.1 (J) - 1.7.1 (WP)
153 */
154 $globally_closed = VikBooking::getClosingDates();
155
156 ?>
157 <script type="text/Javascript">
158 function vboUnitsLeftOrBooked() {
159 var set_to = jQuery('#uleftorbooked').val();
160 jQuery('.vbo-overv-avcell[data-units-booked]').each(function() {
161 var counter_el = jQuery(this).find('a');
162 if (!counter_el.length) {
163 return;
164 }
165 counter_el.first().text(jQuery(this).attr('data-'+set_to));
166 });
167 var nd = new Date();
168 nd.setTime(nd.getTime() + (365*24*60*60*1000));
169 document.cookie = "vboAovwUleft="+set_to+"; expires=" + nd.toUTCString() + "; path=/; SameSite=Lax";
170 }
171 if (jQuery.isFunction(jQuery.fn.tooltip)) {
172 jQuery(".hasTooltip").tooltip();
173 } else {
174 jQuery.fn.tooltip = function(){};
175 }
176 var vboFests = <?php echo json_encode($this->festivities); ?>;
177 var vboRdayNotes = <?php echo json_encode($this->rdaynotes); ?>;
178 </script>
179 <form action="index.php?option=com_vikbooking&amp;task=overv" method="post" name="vboverview" class="vbo-avov-form">
180 <div class="btn-toolbar vbo-avov-toolbar" id="filter-bar" style="width: 100%; display: inline-block;">
181 <div class="btn-group pull-left">
182 <?php echo $wmonthsel; ?>
183 </div>
184 <div class="btn-group pull-left">
185 <select name="mnum" onchange="document.vboverview.submit();">
186 <?php
187 for($i = 1; $i <= 18; $i++) {
188 ?>
189 <option value="<?php echo $i; ?>"<?php echo $i == $mnum ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBOVWNUMMONTHS').' '.$i; ?></option>
190 <?php
191 }
192 ?>
193 </select>
194 </div>
195 <?php
196 if (count($this->categories)) {
197 ?>
198 <div class="btn-group pull-left">
199 <select name="category_id" onchange="document.vboverview.submit();">
200 <option value="0">- <?php echo JText::translate('VBPVIEWROOMTWO'); ?> -</option>
201 <?php
202 foreach ($this->categories as $catid => $catname) {
203 ?>
204 <option value="<?php echo $catid; ?>"<?php echo $catid == $pcategory_id ? ' selected="selected"' : ''; ?>><?php echo $catname; ?></option>
205 <?php
206 }
207 ?>
208 </select>
209 </div>
210 <?php
211 }
212
213 $stickyheaders_cmd_on = '';
214 $stickyheaders_cmd_off = '';
215 if ((count($rows) * $mnum) > 10) {
216 if (VBOPlatformDetection::isJoomla()) {
217 /**
218 * @joomlaonly fixed offset selector is ".navbar"
219 */
220 $stickyheaders_cmd_on = "jQuery('table.vboverviewtable').stickyTableHeaders({cacheHeaderHeight: true, fixedOffset: (jQuery('.navbar').length ? jQuery('.navbar') : jQuery('#subhead-container'))});";
221 } else {
222 /**
223 * @wponly fixed offset selector is "#wpadminbar"
224 */
225 $stickyheaders_cmd_on = "jQuery('table.vboverviewtable').stickyTableHeaders({cacheHeaderHeight: true, fixedOffset: jQuery('#wpadminbar')});";
226 }
227 $stickyheaders_cmd_off = "jQuery('table.vboverviewtable').stickyTableHeaders('destroy'); jQuery('th.bluedays').attr('style', '');";
228 }
229 ?>
230 <script type="text/javascript">
231 function vboToggleStickyTableHeaders(val) {
232 var sticky_heads_cval = 'off';
233 if (val > 0) {
234 sticky_heads_cval = 'on';
235 jQuery('table.vboverviewtable').addClass('vbo-overv-sticky-table-head-on');
236 jQuery('table.vboverviewtable').removeClass('vbo-overv-sticky-table-head-off');
237 <?php echo $stickyheaders_cmd_on; ?>
238 // stop listening to the document scroll event to handle the fixed table heads
239 document.removeEventListener('scroll', vboOvervHandleVScrollTableHeads);
240 // scan all tables for listening to their horizontal scrolling event
241 let tables = document.querySelectorAll('table.vboverviewtable.vbo-overv-sticky-table-head-off');
242 tables.forEach((table) => {
243 // stop listening to the table horizontal scroll events
244 table.parentNode.removeEventListener('scroll', vboOvervHandleHScrollTableHead);
245 });
246 } else {
247 jQuery('table.vboverviewtable').addClass('vbo-overv-sticky-table-head-off');
248 jQuery('table.vboverviewtable').removeClass('vbo-overv-sticky-table-head-on');
249 <?php echo $stickyheaders_cmd_off; ?>
250 // listen to the document scroll event to handle the fixed table heads
251 document.addEventListener('scroll', vboOvervHandleVScrollTableHeads);
252 // scan all tables for listening to their horizontal scrolling event
253 let tables = document.querySelectorAll('table.vboverviewtable.vbo-overv-sticky-table-head-off');
254 tables.forEach((table) => {
255 table.parentNode.addEventListener('scroll', vboOvervHandleHScrollTableHead);
256 });
257 }
258 var nd = new Date();
259 nd.setTime(nd.getTime() + (365*24*60*60*1000));
260 document.cookie = "vboAovwStheads=" + sticky_heads_cval + "; expires=" + nd.toUTCString() + "; path=/; SameSite=Lax";
261 }
262 </script>
263 <?php
264 if ((count($rows) * $mnum) > 10) {
265 ?>
266 <div class="btn-group pull-left">
267 <select name="stickytheads" onchange="vboToggleStickyTableHeaders(this.value);">
268 <optgroup label="<?php echo JHtml::fetch('esc_attr', JText::translate('VBO_LAYOUT')); ?>">
269 <option value="1"<?php echo empty($cookie_sticky_heads) || $cookie_sticky_heads == 'on' ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBO_TABLE'); ?></option>
270 <option value="0"<?php echo $cookie_sticky_heads == 'off' ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBO_SCROLL'); ?></option>
271 </optgroup>
272 </select>
273 </div>
274 <?php
275 }
276
277 if ($tags_view_supported === true) {
278 ?>
279 <div class="btn-group pull-left">
280 <div class="vbo-overview-tagsorclassic">
281 <select name="bmode" onchange="document.vboverview.submit();">
282 <option value="classic"><?php echo JText::translate('VBOAVOVWBMODECLASSIC'); ?></option>
283 <option value="tags"<?php echo $pbmode == 'tags' ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBOAVOVWBMODETAGS'); ?></option>
284 </select>
285 </div>
286 <?php
287 if ($pbmode == 'tags' && count($colortags) > 0) {
288 ?>
289 <div class="vbo-overview-tagslegend">
290 <span class="vbo-overview-tagslegend-lbl"><?php echo JText::translate('VBOAVOVWBMODETAGSLBL'); ?></span>
291 <?php
292 foreach ($colortags as $ctagk => $ctagv) {
293 ?>
294 <div class="vbo-overview-legend-tag">
295 <div class="vbo-colortag-circle hasTooltip" style="background-color: <?php echo $ctagv['color']; ?>;" title="<?php echo JText::translate($ctagv['name']); ?>"></div>
296 </div>
297 <?php
298 }
299 ?>
300 </div>
301 <?php
302 }
303 ?>
304 </div>
305 <?php
306 }
307 ?>
308 <div class="btn-group pull-right">
309 <select name="units_show_type" id="uleftorbooked" onchange="vboUnitsLeftOrBooked();"><option value="units-booked"<?php echo (!empty($cookie_uleft) && $cookie_uleft == 'units-booked' ? ' selected="selected"' : ''); ?>><?php echo JText::translate('VBOVERVIEWUBOOKEDFILT'); ?></option><option value="units-left"<?php echo $show_type == 'units-left' || (!empty($cookie_uleft) && $cookie_uleft == 'units-left') ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBOVERVIEWULEFTFILT'); ?></option></select>
310 </div>
311 <div class="btn-group pull-right vbo-avov-legend">
312 <span class="vbo-overview-legend-init"><?php echo JText::translate('VBOVERVIEWLEGEND'); ?></span>
313 <div class="vbo-overview-legend-red">
314 <span class="vbo-overview-legend-box">&nbsp;</span>
315 <span class="vbo-overview-legend-title"><?php echo JText::translate('VBOVERVIEWLEGRED'); ?></span>
316 </div>
317 <div class="vbo-overview-legend-yellow">
318 <span class="vbo-overview-legend-box">&nbsp;</span>
319 <span class="vbo-overview-legend-title"><?php echo JText::translate('VBOVERVIEWLEGYELLOW'); ?></span>
320 </div>
321 <div class="vbo-overview-legend-green">
322 <span class="vbo-overview-legend-box">&nbsp;</span>
323 <span class="vbo-overview-legend-title"><?php echo JText::translate('VBOVERVIEWLEGGREEN'); ?></span>
324 </div>
325 <div class="vbo-overview-legend-green vbo-overview-legend-dnd">
326 <span class="vbo-overview-legend-box"><i class="vboicn-enlarge" style="margin: 1px; display: block; text-align: center;"></i></span>
327 <span class="vbo-overview-legend-title"><?php echo JText::translate('VBOVERVIEWLEGDND'); ?></span>
328 </div>
329 </div>
330 </div>
331 </form>
332
333 <?php
334 $todayymd = date('Y-m-d');
335 $nowts = getdate($tsstart);
336 $curts = $nowts;
337 for ($mind = 1; $mind <= $mnum; $mind++) {
338 $monthname = VikBooking::sayMonth($curts['mon']);
339 ?>
340 <div class="vbo-overv-montable-wrap">
341 <div class="vbo-table-responsive">
342 <table class="vboverviewtable vbo-roverview-table vbo-table <?php echo $cookie_sticky_heads == 'off' ? 'vbo-overv-sticky-table-head-off' : 'vbo-overv-sticky-table-head-on'; ?>" data-table-index="<?php echo ($mind - 1); ?>">
343 <thead>
344 <tr class="vboverviewtablerowone">
345 <th class="bluedays skip-bluedays-click vbo-overview-month"><?php echo $monthname . " " . $curts['year']; ?></th>
346 <?php
347 $moncurts = $curts;
348 $mon = $moncurts['mon'];
349 while ($moncurts['mon'] == $mon) {
350 $curdayymd = date('Y-m-d', $moncurts[0]);
351 $read_day = $days_labels[$moncurts['wday']] . ' ' . $moncurts['mday'] . ' ' . $monthname . ' ' . $curts['year'];
352 ?>
353 <th class="bluedays<?php echo ($todayymd == $curdayymd ? ' vbo-overv-todaycell' : '') . (isset($this->festivities[$curdayymd]) ? ' vbo-overv-festcell' : ''); ?>" data-ymd="<?php echo $curdayymd; ?>" data-readymd="<?php echo $read_day; ?>">
354 <span class="vbo-overw-tablewday"><?php echo $days_labels[$moncurts['wday']]; ?></span>
355 <span class="vbo-overw-tablemday"><?php echo $moncurts['mday']; ?></span>
356 </th>
357 <?php
358 // parse the next day
359 $moncurts = getdate(mktime(0, 0, 0, $moncurts['mon'], ($moncurts['mday'] + 1), $moncurts['year']));
360 }
361 ?>
362 </tr>
363 </thead>
364 <tbody>
365 <?php
366 foreach ($rows as $room) {
367 $moncurts = $curts;
368 $mon = $moncurts['mon'];
369 $room_tags_view = $pbmode == 'tags' && $tags_view_supported === true && $room['units'] <= 1 ? true : false;
370 $is_subunit = array_key_exists('unit_index', $room);
371
372 ?>
373 <tr class="vboverviewtablerow<?php echo $is_subunit ? ' vboverviewtablerow-subunit' : ''; ?><?php echo ($is_subunit || $room['units'] == 1) && $cookie_sticky_heads == 'off' ? ' vbo-overv-row-snake' : ''; ?>"<?php echo $is_subunit ? ' data-subroomid="' . $room['id'] . '-' . $room['unit_index'] . '"' : ''; ?>>
374 <?php
375 if ($is_subunit) {
376 ?>
377 <td class="roomname subroomname" data-roomid="<?php echo '-' . $room['id']; ?>">
378 <span class="vbo-overview-subroomunits"><?php VikBookingIcons::e('bed'); ?></span>
379 <span class="vbo-overview-subroomname"><?php echo $room['unit_index_str']; ?></span>
380 </td>
381 <?php
382 } else {
383 ?>
384 <td class="roomname" data-roomid="<?php echo $room['id']; ?>">
385 <span class="vbo-overview-room-info">
386 <span class="vbo-overview-roomunits"><?php echo $room['units']; ?></span>
387 <span class="vbo-overview-roomname"><?php echo $room['name']; ?></span>
388 <?php
389 if (isset($rooms_features_map[$room['id']])) {
390 ?>
391 <span class="vbo-overview-subroom-toggle"><i class="<?php echo VikBookingIcons::i('chevron-down', 'hasTooltip'); ?>" style="margin: 0;" title="<?php echo JHtml::fetch('esc_attr', JText::translate('VBOVERVIEWTOGGLESUBROOM')); ?>"></i></span>
392 <?php
393 }
394 ?>
395 </span>
396 </td>
397 <?php
398 }
399
400 // loop through every day of the month
401 $room_bids_pool = [];
402 $room_bookings = [];
403 while ($moncurts['mon'] == $mon) {
404 // init cell values
405 $dclass = 'vbo-grid-avcell vbo-overv-avcell ' . (!$is_subunit ? "notbusy" : "subnotbusy");
406 $is_checkin = false;
407 $is_sharedcal = false;
408 $is_closure = false;
409 $lastbidcheckout = null;
410 $dalt = "";
411 $bid = "";
412 $bids_pool = [];
413 $totfound = 0;
414 $prev_day_key = date('Y-m-d', strtotime('-1 day', $moncurts[0]));
415 $cur_day_key = date('Y-m-d', $moncurts[0]);
416
417 // check for global closing dates
418 foreach ($globally_closed as $glob_closed) {
419 if ($moncurts[0] >= $glob_closed['from'] && $moncurts[0] <= $glob_closed['to']) {
420 $dclass .= ' vbo-overv-globally-closed';
421 }
422 }
423
424 // check availability
425 if (!empty($arrbusy[$room['id']]) && !$is_subunit) {
426 foreach ($arrbusy[$room['id']] as $b) {
427 $tmpone = getdate($b['checkin']);
428 $ritts = mktime(0, 0, 0, $tmpone['mon'], $tmpone['mday'], $tmpone['year']);
429 $tmptwo = getdate($b['checkout']);
430 $conts = mktime(0, 0, 0, $tmptwo['mon'], $tmptwo['mday'], $tmptwo['year']);
431 if (!($moncurts[0] >= $ritts && $moncurts[0] < $conts)) {
432 // booking does not involve the current day
433 continue;
434 }
435 $dclass = "vbo-grid-avcell vbo-overv-avcell busy";
436 $bid = $b['idorder'];
437 $is_sharedcal = !empty($b['sharedcal']) ? true : $is_sharedcal;
438 $is_closure = !empty($b['closure']) ? true : $is_closure;
439 $bid_str = '-' . $bid . '-';
440 if (!in_array($bid_str, $bids_pool)) {
441 $bids_pool[] = $bid_str;
442 }
443 if (isset($rooms_features_map[$room['id']])) {
444 // multi-unit room with distinctive features defined
445 if (!isset($room_bids_pool[$cur_day_key])) {
446 $room_bids_pool[$cur_day_key] = [];
447 $room_bookings[$cur_day_key] = [];
448 }
449 $room_bids_pool[$cur_day_key][] = (int)$bid;
450 $room_bookings[$cur_day_key][] = $b;
451 } elseif ($room['units'] == 1) {
452 // single-unit room
453 if (!isset($room_bookings[$cur_day_key])) {
454 $room_bookings[$cur_day_key] = [];
455 }
456 $room_bookings[$cur_day_key][] = $b;
457 }
458 if ($moncurts[0] == $ritts) {
459 $dalt = JText::translate('VBPICKUPAT')." ".date('H:i', $b['checkin']);
460 $is_checkin = true;
461 $lastbidcheckout = $b['checkout'];
462 $bids_checkins[$bid] = $cur_day_key;
463 } elseif ($moncurts[0] == $conts) {
464 $dalt = JText::translate('VBRELEASEAT')." ".date('H:i', $b['checkout']);
465 }
466 $totfound++;
467 }
468 }
469
470 // locked (stand-by) records
471 if ($room_tags_view === true && isset($arrlocked[$room['id']]) && $arrlocked[$room['id']] && !$is_subunit) {
472 foreach ($arrlocked[$room['id']] as $l) {
473 $tmpone = getdate($l['checkin']);
474 $ritts = mktime(0, 0, 0, $tmpone['mon'], $tmpone['mday'], $tmpone['year']);
475 $tmptwo = getdate($l['checkout']);
476 $conts = mktime(0, 0, 0, $tmptwo['mon'], $tmptwo['mday'], $tmptwo['year']);
477 if ($moncurts[0] >= $ritts && $moncurts[0] < $conts) {
478 $dclass = strpos($dclass, "notbusy") !== false ? "busytmplock" : "busy busytmplock";
479 $bid = $l['idorder'];
480 if (!in_array($bid, $bids_pool)) {
481 if (count($bids_pool) > 0) {
482 array_unshift($bids_pool, '-'.$bid.'-');
483 } else {
484 $bids_pool[] = '-'.$bid.'-';
485 }
486 }
487 if ($moncurts[0] == $ritts) {
488 $dalt = JText::translate('VBPICKUPAT')." ".date('H:i', $l['checkin']);
489 } elseif ($moncurts[0] == $conts) {
490 $dalt = JText::translate('VBRELEASEAT')." ".date('H:i', $l['checkout']);
491 }
492 $totfound++;
493 }
494 }
495 }
496
497 // handle single-unit room reservations
498 if ($room['units'] == 1 && !isset($rooms_features_map[$room['id']]) && isset($room_bookings[$cur_day_key]) && $room_bookings[$cur_day_key]) {
499 // set the first (and only) booking
500 $day_booking = [];
501 foreach ($room_bookings[$cur_day_key] as $day_res) {
502 if (!$day_res['closure'] && !$day_res['sharedcal']) {
503 $day_booking = $day_res;
504 break;
505 }
506 }
507 if ($day_booking) {
508 if (!isset($room_bookings_pool[$room['id']])) {
509 $room_bookings_pool[$room['id']] = [];
510 }
511 if (!isset($room_bookings_pool[$room['id']][$cur_day_key])) {
512 $room_bookings_pool[$room['id']][$cur_day_key] = [];
513 }
514 $room_bookings_pool[$room['id']][$cur_day_key][0] = $day_booking;
515 }
516 }
517
518 $useday = $moncurts['mday'] < 10 ? "0{$moncurts['mday']}" : $moncurts['mday'];
519 $dclass .= $totfound < $room['units'] && $totfound > 0 ? ' vbo-partially' : '';
520 $dclass .= $is_sharedcal ? ' busy-sharedcalendar' : '';
521 $dclass .= $is_closure ? ' busy-closure' : '';
522 $dstyle = '';
523 $astyle = '';
524 if ($room_tags_view === true && $totfound > 0) {
525 $last_bid = intval(str_replace('-', '', $bids_pool[(count($bids_pool) - 1)]));
526 $binfo = VikBooking::getBookingInfoFromID($last_bid);
527 if (count($binfo) > 0) {
528 $bcolortag = VikBooking::applyBookingColorTag($binfo);
529 if (count($bcolortag) > 0) {
530 $bcolortag['name'] = JText::translate($bcolortag['name']);
531 $dstyle = " style=\"background-color: ".$bcolortag['color']."; color: ".(array_key_exists('fontcolor', $bcolortag) ? $bcolortag['fontcolor'] : '#ffffff').";\" data-lastbid=\"".$last_bid."\"";
532 $astyle = " style=\"color: ".(array_key_exists('fontcolor', $bcolortag) ? $bcolortag['fontcolor'] : '#ffffff').";\"";
533 $dclass .= ' vbo-hascolortag';
534 }
535 }
536 }
537 if ($is_subunit && isset($rooms_features_bookings[$room['id']]) && isset($rooms_bids_pools[$room['id']][$cur_day_key]) && isset($rooms_features_bookings[$room['id']][$room['unit_index']])) {
538 foreach ($rooms_bids_pools[$room['id']][$cur_day_key] as $bid) {
539 $bid = intval(str_replace('-', '', $bid));
540 if (in_array($bid, $rooms_features_bookings[$room['id']][$room['unit_index']])) {
541 $room['units'] = 1;
542 $totfound = 1;
543 $dclass = "vbo-grid-avcell vbo-overv-avcell subroom-busy";
544 $is_checkin = isset($bids_checkins[$bid]) && $bids_checkins[$bid] == $cur_day_key ? true : $is_checkin;
545 break;
546 }
547 }
548 }
549 $write_units = $show_type == 'units-left' || (!empty($cookie_uleft) && $cookie_uleft == 'units-left') ? ($room['units'] - $totfound) : $totfound;
550 // check today's date
551 $curdayymd = date('Y-m-d', $moncurts[0]);
552 if ($todayymd == $curdayymd) {
553 $dclass .= ' vbo-overv-todaycell';
554 }
555 if (isset($this->festivities[$curdayymd])) {
556 $dclass .= ' vbo-overv-festcell';
557 }
558
559 /**
560 * Critical dates defined at room-day level.
561 *
562 * @since 1.13.5 (J) - 1.3.5 (WP)
563 */
564 $rdaynote_keyid = $cur_day_key . '_' . $room['id'] . '_' . (isset($room['unit_index']) ? $room['unit_index'] : '0');
565 if (isset($this->rdaynotes[$rdaynote_keyid])) {
566 // note exists for this combination of date, room ID and subunit
567 $dclass .= ' vbo-roomdaynote-full';
568 $rdaynote_icn = 'sticky-note';
569 } else {
570 // no notes for this cell
571 $dclass .= ' vbo-roomdaynote-empty';
572 $rdaynote_icn = 'far fa-sticky-note';
573 }
574 $critical_note = '<span class="vbo-roomdaynote-trigger" data-roomday="' . $rdaynote_keyid . '"><i class="' . VikBookingIcons::i($rdaynote_icn, 'vbo-roomdaynote-display') . '"></i></span>';
575
576 /**
577 * Closures overlapping one real reservation for a single-unit room should still
578 * allow the display of the booking snake if it's just one real reservation.
579 */
580 $closure_on_real_res = false;
581 if ($totfound === 2 && !$is_subunit && $room['units'] == 1 && $cookie_sticky_heads == 'off' && strpos($dclass, "busy-closure") !== false) {
582 if (isset($room_bookings_pool[$room['id']]) && isset($room_bookings_pool[$room['id']][$cur_day_key]) && $room_bookings_pool[$room['id']][$cur_day_key]) {
583 if (isset($room_bookings_pool[$room['id']][$cur_day_key][0]) && !$room_bookings_pool[$room['id']][$cur_day_key][0]['closure']) {
584 // found a real booking on a date with at least one closure
585 $closure_on_real_res = true;
586 }
587 }
588 }
589
590 // prepare cell content
591 if ($totfound === 1 || $closure_on_real_res) {
592 $day_booking_snake = '';
593 $day_booking_data = [];
594 if (isset($room_bookings_pool[$room['id']]) && isset($room_bookings_pool[$room['id']][$cur_day_key]) && $room_bookings_pool[$room['id']][$cur_day_key]) {
595 if ($is_subunit && isset($room_bookings_pool[$room['id']][$cur_day_key][$room['unit_index']])) {
596 $day_booking_data = $room_bookings_pool[$room['id']][$cur_day_key][$room['unit_index']];
597 } elseif (!$is_subunit && isset($room_bookings_pool[$room['id']][$cur_day_key][0])) {
598 $day_booking_data = $room_bookings_pool[$room['id']][$cur_day_key][0];
599 }
600 }
601 if (isset($room_bookings_pool[$room['id']]) && isset($room_bookings_pool[$room['id']][$prev_day_key]) && $room_bookings_pool[$room['id']][$prev_day_key]) {
602 if ($is_subunit && isset($room_bookings_pool[$room['id']][$prev_day_key][$room['unit_index']])) {
603 if (date('Y-m-d', $room_bookings_pool[$room['id']][$prev_day_key][$room['unit_index']]['checkout']) == $cur_day_key) {
604 // prepend checkout snake
605 $day_booking_snake .= '<div class="vbo-tableaux-booking vbo-tableaux-booking-singleunit vbo-tableaux-booking-checkout"><span>&nbsp;</span></div>';
606 }
607 } elseif (!$is_subunit && $room['units'] == 1 && isset($room_bookings_pool[$room['id']][$prev_day_key][0])) {
608 if (date('Y-m-d', $room_bookings_pool[$room['id']][$prev_day_key][0]['checkout']) == $cur_day_key) {
609 // prepend checkout snake
610 $day_booking_snake .= '<div class="vbo-tableaux-booking vbo-tableaux-booking-singleunit vbo-tableaux-booking-checkout"><span>&nbsp;</span></div>';
611 }
612 }
613 }
614 if ($room['units'] == 1 && $day_booking_data && !$day_booking_data['closure']) {
615 // build tableaux-style snake container for guest
616 $customer_descr = '';
617 if ($is_checkin) {
618 // customer details
619 if (!empty($day_booking_data['first_name']) || !empty($day_booking_data['last_name'])) {
620 // check if we need to display a profile picture or a channel logo
621 $booking_avatar_src = null;
622 $booking_avatar_alt = null;
623 if (!empty($day_booking_data['pic'])) {
624 // customer profile picture
625 $booking_avatar_src = strpos($day_booking_data['pic'], 'http') === 0 ? $day_booking_data['pic'] : VBO_SITE_URI . 'resources/uploads/' . $day_booking_data['pic'];
626 $booking_avatar_alt = basename($booking_avatar_src);
627 } elseif (!empty($day_booking_data['idorderota']) && !empty($day_booking_data['channel'])) {
628 // channel logo
629 $logo_helper = VikBooking::getVcmChannelsLogo($day_booking_data['channel'], $get_istance = true);
630 if ($logo_helper !== false) {
631 $booking_avatar_src = $logo_helper->getSmallLogoURL();
632 $booking_avatar_alt = $logo_helper->provenience;
633 }
634 }
635
636 if (!empty($booking_avatar_src)) {
637 // make sure the alt attribute is not too long in case of broken images
638 $booking_avatar_alt = !empty($booking_avatar_alt) && strlen($booking_avatar_alt) > 15 ? '...' . substr($booking_avatar_alt, -12) : $booking_avatar_alt;
639 // append booking avatar image
640 $customer_descr .= '<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>';
641 }
642
643 // customer name
644 $customer_fullname = trim($day_booking_data['first_name'] . ' ' . $day_booking_data['last_name']);
645 if (strlen($customer_fullname) > 26) {
646 if (function_exists('mb_substr')) {
647 $customer_fullname = trim(mb_substr($customer_fullname, 0, 26, 'UTF-8')) . '..';
648 } else {
649 $customer_fullname = trim(substr($customer_fullname, 0, 26)) . '..';
650 }
651 }
652 $customer_descr .= '<span class="vbo-tableaux-guest-name">' . $customer_fullname . '</span>';
653 } else {
654 // parse the customer data string
655 $custdata_parts = explode("\n", $day_booking_data['custdata']);
656 $enoughinfo = false;
657 if (count($custdata_parts) > 2 && strpos($custdata_parts[0], ':') !== false && strpos($custdata_parts[1], ':') !== false) {
658 // get the first two fields
659 $custvalues = array();
660 foreach ($custdata_parts as $custdet) {
661 if (strlen($custdet) < 1) {
662 continue;
663 }
664 $custdet_parts = explode(':', $custdet);
665 if (count($custdet_parts) >= 2) {
666 unset($custdet_parts[0]);
667 array_push($custvalues, trim(implode(':', $custdet_parts)));
668 }
669 if (count($custvalues) > 1) {
670 break;
671 }
672 }
673 if (count($custvalues) > 1) {
674 $enoughinfo = true;
675 $customer_nominative = trim(implode(' ', $custvalues));
676 if (strlen($customer_nominative) > 26) {
677 if (function_exists('mb_substr')) {
678 $customer_nominative = trim(mb_substr($customer_nominative, 0, 26, 'UTF-8')) . '..';
679 } else {
680 $customer_nominative = trim(substr($customer_nominative, 0, 26)) . '..';
681 }
682 }
683 if (!empty($day_booking_data['idorderota']) && !empty($day_booking_data['channel'])) {
684 // add support for the channel logo for the imported OTA reservations with no customer record
685 $logo_helper = VikBooking::getVcmChannelsLogo($day_booking_data['channel'], $get_istance = true);
686 if ($logo_helper !== false) {
687 $booking_avatar_src = $logo_helper->getSmallLogoURL();
688 $booking_avatar_alt = $logo_helper->provenience;
689 // make sure the alt attribute is not too long in case of broken images
690 $booking_avatar_alt = !empty($booking_avatar_alt) && strlen($booking_avatar_alt) > 15 ? '...' . substr($booking_avatar_alt, -12) : $booking_avatar_alt;
691 // append booking avatar image
692 $customer_descr .= '<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>';
693 }
694 }
695 // set customer nominative built
696 $customer_descr .= '<span class="vbo-tableaux-guest-name">' . $customer_nominative . '</span>';
697 }
698 }
699 if (!$enoughinfo) {
700 $customer_descr .= '<span class="vbo-tableaux-guest-name">#' . $day_booking_data['idorder'] . '</span>';
701 }
702 }
703 }
704 // set value
705 $day_booking_snake .= '<div class="vbo-tableaux-booking vbo-tableaux-booking-singleunit ' . ($is_checkin ? 'vbo-tableaux-booking-checkin' : 'vbo-tableaux-booking-stay') . '"' . ($is_checkin ? ' data-nights="' . $day_booking_data['days'] . '" draggable="true"' : '') . '><span>' . ($is_checkin ? $customer_descr : '&nbsp;') . '</span></div>';
706 }
707
708 if ($cookie_sticky_heads != 'off') {
709 // not supported with the sticky table headers layout
710 $day_booking_snake = '';
711 }
712
713 $write_units = strpos($dclass, "subroom-busy") !== false ? '' : $write_units;
714 $stopdrag = ($mind == $mnum && !is_null($lastbidcheckout) && (int)date('n', $lastbidcheckout) != (int)$mon);
715 $dclass .= $is_checkin === true ? ' vbo-checkinday' : '';
716
717 // build proper link
718 if ($totfound > 1) {
719 // there must be a closure overlapping a real booking
720 $cell_link = 'index.php?option=com_vikbooking&task=choosebusy&idroom=' . $room['id'] . '&ts=' . $moncurts[0] . '&goto=overv';
721 } else {
722 // regular situation with one booking only
723 $cell_link = 'index.php?option=com_vikbooking&task=editbusy&cid[]=' . $bid . '&goto=overv';
724 }
725 ?>
726 <td align="center" class="<?php echo $dclass; ?>"<?php echo $dstyle; ?> data-day="<?php echo $cur_day_key; ?>" data-units-booked="<?php echo $totfound; ?>" data-units-left="<?php echo ($room['units'] - $totfound); ?>" data-bids="<?php echo strpos($dclass, "subroom-busy") !== false ? "-{$bid}-" : implode(',', $bids_pool); ?>">
727 <?php
728 if (!$day_booking_snake) {
729 if ($is_checkin === true && !$stopdrag) {
730 ?>
731 <span class="vbo-draggable-sp" draggable="true">
732 <a href="<?php echo $cell_link; ?>" class="<?php echo strpos($dclass, "subroom-busy") === false ? 'vbo-overview-redday' : 'vbo-overview-subredday'; ?>"<?php echo $astyle . (!empty($dalt) ? ' title="' . JHtml::fetch('esc_attr', $dalt) . '"' : ''); ?>><?php echo $write_units; ?></a>
733 </span>
734 <?php
735 } else {
736 ?>
737 <a href="<?php echo $cell_link; ?>" class="<?php echo strpos($dclass, "subroom-busy") === false ? 'vbo-overview-redday' : 'vbo-overview-subredday'; ?>"<?php echo $astyle . (!empty($dalt) ? ' title="' . JHtml::fetch('esc_attr', $dalt) . '"' : ''); ?>><?php echo $write_units; ?></a>
738 <?php
739 }
740 }
741 echo $day_booking_snake . $critical_note;
742 ?>
743 </td>
744 <?php
745 } elseif ($totfound > 1) {
746 ?>
747 <td align="center" class="<?php echo $dclass; ?>"<?php echo $dstyle; ?> data-day="<?php echo $cur_day_key; ?>" data-units-booked="<?php echo $totfound; ?>" data-units-left="<?php echo ($room['units'] - $totfound); ?>" data-bids="<?php echo implode(',', $bids_pool); ?>">
748 <a href="index.php?option=com_vikbooking&task=choosebusy&idroom=<?php echo $room['id']; ?>&ts=<?php echo $moncurts[0]; ?>&goto=overv" class="vbo-overview-redday"<?php echo $astyle; ?>><?php echo $write_units; ?></a>
749 <?php echo $critical_note; ?>
750 </td>
751 <?php
752 } else {
753 // no booked records
754 ?>
755 <td align="center" class="<?php echo $dclass; ?>" data-day="<?php echo $cur_day_key; ?>" data-bids="">
756 <?php
757 if ($cookie_sticky_heads == 'off' && isset($room_bookings_pool[$room['id']]) && isset($room_bookings_pool[$room['id']][$prev_day_key]) && $room_bookings_pool[$room['id']][$prev_day_key]) {
758 if ($is_subunit && isset($room_bookings_pool[$room['id']][$prev_day_key][$room['unit_index']])) {
759 if (date('Y-m-d', $room_bookings_pool[$room['id']][$prev_day_key][$room['unit_index']]['checkout']) == $cur_day_key) {
760 // prepend checkout snake
761 echo '<div class="vbo-tableaux-booking vbo-tableaux-booking-singleunit vbo-tableaux-booking-checkout"><span>&nbsp;</span></div>';
762 }
763 } elseif (!$is_subunit && $room['units'] == 1 && isset($room_bookings_pool[$room['id']][$prev_day_key][0])) {
764 if (date('Y-m-d', $room_bookings_pool[$room['id']][$prev_day_key][0]['checkout']) == $cur_day_key) {
765 // prepend checkout snake
766 echo '<div class="vbo-tableaux-booking vbo-tableaux-booking-singleunit vbo-tableaux-booking-checkout"><span>&nbsp;</span></div>';
767 }
768 }
769 }
770 echo $critical_note;
771 ?>
772 </td>
773 <?php
774 }
775
776 // iterate to next day
777 $moncurts = getdate(mktime(0, 0, 0, $moncurts['mon'], ($moncurts['mday'] + 1), $moncurts['year']));
778 }
779
780 // room row parsed, check if we have to parse a sub-unit next
781 if (!$is_subunit && $room_bids_pool && isset($rooms_features_map[$room['id']])) {
782 // load bookings for distinctive features when parsing the parent $room array
783 $room_indexes_bids = VikBooking::loadRoomIndexesBookings($room['id'], $room_bids_pool);
784 if ($room_indexes_bids) {
785 $rooms_features_bookings[$room['id']] = $room_indexes_bids;
786 $rooms_bids_pools[$room['id']] = $room_bids_pool;
787 // map sub-unit room bookings
788 foreach ($room_indexes_bids as $rindex => $rindex_bids) {
789 if (!is_array($rindex_bids) || !$rindex_bids) {
790 continue;
791 }
792 foreach ($rindex_bids as $seek_bid) {
793 foreach ($room_bookings as $day_key => $day_ress) {
794 foreach ($day_ress as $day_res) {
795 if ($day_res['idorder'] != $seek_bid) {
796 continue;
797 }
798 if (!isset($room_bookings_pool[$room['id']])) {
799 $room_bookings_pool[$room['id']] = [];
800 }
801 if (!isset($room_bookings_pool[$room['id']][$day_key])) {
802 $room_bookings_pool[$room['id']][$day_key] = [];
803 }
804 $room_bookings_pool[$room['id']][$day_key][$rindex] = $day_res;
805 }
806 }
807 }
808 }
809 }
810 }
811
812 // close row
813 ?>
814 </tr>
815 <?php
816 }
817 ?>
818 </tbody>
819 </table>
820 </div>
821 </div>
822 <?php
823 // iterate to next month
824 $curts = getdate(mktime(0, 0, 0, ($nowts['mon'] + $mind), $nowts['mday'], $nowts['year']));
825 }
826
827 //Prepare modal
828 ?>
829 <script type="text/javascript">
830 var hasNewBooking = false;
831 var last_room_click = '',
832 last_date_click = '',
833 next_date_click = '';
834 function vboJModalHideCallback() {
835 if (hasNewBooking === true) {
836 location.reload();
837 }
838 }
839 function vboCallOpenJModal(identif, baseurl) {
840 if (last_room_click && last_room_click.length) {
841 baseurl += '&cid[]='+last_room_click;
842 last_room_click = '';
843 }
844 if (last_date_click && last_date_click.length) {
845 baseurl += '&checkin='+last_date_click;
846 last_date_click = '';
847 }
848 if (next_date_click && next_date_click.length) {
849 baseurl += '&checkout='+next_date_click;
850 next_date_click = '';
851 }
852 vboOpenJModal(identif, baseurl);
853 }
854 </script>
855 <?php
856 echo $vbo_app->getJmodalScript('', 'vboJModalHideCallback();', '');
857 echo $vbo_app->getJmodalHtml('vbo-new-res', JText::translate('VBOSHOWQUICKRES'));
858 //end Prepare modal
859 ?>
860 <div class="vbo-ovrv-flt-butn" onclick="vboCallOpenJModal('vbo-new-res', 'index.php?option=com_vikbooking&task=calendar&overv=1&tmpl=component');"><span><i class="vboicn-user-plus"></i> <?php echo JText::translate('VBOSHOWQUICKRES'); ?></span></div>
861 <div class="vbo-info-overlay-block">
862 <div class="vbo-info-overlay-loading-dnd">
863 <span class="vbo-loading-dnd-head"></span>
864 <span class="vbo-loading-dnd-body"></span>
865 <span class="vbo-loading-dnd-footer"><?php echo JText::translate('VIKLOADING'); ?></span>
866 <span id="vbo-dnd-response" class="vbo-loading-dnd-response"></span>
867 <canvas id="vbo-dnd-canvas-success" height="250"></canvas>
868 </div>
869 </div>
870
871 <form action="index.php?option=com_vikbooking" method="post" name="adminForm" id="adminForm">
872 <input type="hidden" name="option" value="com_vikbooking" />
873 <input type="hidden" name="task" value="overv" />
874 <input type="hidden" name="month" value="<?php echo $tsstart; ?>" />
875 <input type="hidden" name="mnum" value="<?php echo $mnum; ?>" />
876 <input type="hidden" name="category_id" value="<?php echo $pcategory_id; ?>" />
877 <?php echo '<br/>'.$navbut; ?>
878 </form>
879
880 <a class="vbo-basenavuri-details" href="index.php?option=com_vikbooking&task=editorder&goto=overv&cid[]=%d" style="display: none;"></a>
881 <a class="vbo-basenavuri-edit" href="index.php?option=com_vikbooking&task=editbusy&goto=overv&cid[]=%d" style="display: none;"></a>
882
883 <script type="text/Javascript">
884 var hovtimer;
885 var hovtip = false;
886 var vbodialogorph_on = false;
887 var isdragging = false;
888 var vboMessages = {
889 currencySymb: "<?php echo $currencysymb; ?>"
890 };
891 var debug_mode = '<?php echo $pdebug; ?>';
892 var bctags_count = <?php echo count($colortags); ?>;
893 var bctags_pool = <?php echo json_encode($colortags); ?>;
894 <?php
895 if ($colortags) {
896 $bctags_tip = '<div class=\"vbo-overview-tip-bctag-subtip-inner\">';
897 foreach ($colortags as $ctagk => $ctagv) {
898 $bctags_tip .= '<div class=\"vbo-overview-tip-bctag-subtip-circle hasTooltip\" data-ctagkey=\"'.$ctagk.'\" data-ctagcolor=\"'.$ctagv['color'].'\" title=\"'.addslashes(JText::translate($ctagv['name'])).'\"><div class=\"vbo-overview-tip-bctag-subtip-circlecont\" style=\"background-color: '.$ctagv['color'].';\"></div></div>';
899 }
900 $bctags_tip .= '</div>';
901 ?>
902 var bctags_tip = "<?php echo $bctags_tip; ?>";
903 <?php
904 }
905 ?>
906 </script>
907
908 <script type="text/Javascript">
909 /**
910 * Render the units view mode
911 */
912 vboUnitsLeftOrBooked();
913
914 /**
915 * Orphans dialog
916 */
917 function hideVboDialogOverv(action) {
918 if (vbodialogorph_on === true) {
919 jQuery(".vbo-orphans-overlay-block").fadeOut(400, function () {
920 jQuery(".vbo-info-overlay-content").show();
921 });
922 vbodialogorph_on = false;
923 }
924 // check action
925 if (action < 0) {
926 // stop reminding, set cookie
927 var nd = new Date();
928 nd.setTime(nd.getTime() + (365*24*60*60*1000));
929 document.cookie = "vboHideOrphans=1; expires=" + nd.toUTCString() + "; path=/; SameSite=Lax";
930 }
931 }
932
933 /* DnD global vars */
934 var cellspool = [];
935 var newcellspool = [];
936
937 /* DnD Count the number of consecutive cells for this booking */
938 function countBookingCells(cellobj, bidstart, roomid) {
939 var totnights = 1;
940 var loop = true;
941 var cellelem = cellobj;
942 cellspool.push(cellelem);
943 while (loop === true) {
944 var next = cellelem.next('td');
945 if (next === undefined || !next.length) {
946 //attempt to go to the month after
947 var partable = cellelem.closest('tr').closest('table').nextAll('table.vboverviewtable').first();
948 var nextmonth = false;
949 if (partable !== undefined && partable.length) {
950 partable.find('tr.vboverviewtablerow').each(function() {
951 var roomexists = jQuery(this).find('td').first();
952 if (roomexists !== undefined && roomexists.length) {
953 if (roomexists.attr('data-roomid') == roomid) {
954 nextmonth = true;
955 next = roomexists.next('td');
956 return true;
957 }
958 }
959 });
960 }
961 if (nextmonth === false) {
962 //nothing was found in the month after
963 loop = false;
964 break;
965 }
966 }
967 cellelem = next;
968 var nextbids = cellelem.attr('data-bids');
969 if (nextbids.length && nextbids.indexOf(bidstart) >= 0) {
970 cellspool.push(cellelem);
971 totnights++;
972 } else {
973 loop = false;
974 break;
975 }
976 }
977 return totnights;
978 }
979
980 /* DnD Count the number of consecutive free date-cells for moving the booking onto the landing cell selected for drop */
981 function countCellFreeNights(landobj, roomid, totnights, moving_bids) {
982 var freenights = 1;
983 var loop = true;
984 var cellelem = landobj;
985
986 // populate cells pool
987 newcellspool.push(cellelem);
988
989 while (loop === true) {
990 var next = cellelem.next('td');
991 if (next === undefined || !next.length) {
992 // attempt to go to the month after
993 var partable = cellelem.closest('tr').closest('table').nextAll('table.vboverviewtable').first();
994 var nextmonth = false;
995 if (partable !== undefined && partable.length) {
996 partable.find('tr.vboverviewtablerow').each(function() {
997 var roomexists = jQuery(this).find('td').first();
998 if (roomexists !== undefined && roomexists.length) {
999 if (roomexists.attr('data-roomid') == roomid) {
1000 nextmonth = true;
1001 next = roomexists.next('td');
1002 return true;
1003 }
1004 }
1005 });
1006 }
1007 if (nextmonth === false) {
1008 // nothing was found in the month after
1009 loop = false;
1010 break;
1011 }
1012 }
1013 cellelem = next;
1014 var cell_bids = cellelem.attr('data-bids');
1015 if (!cellelem.hasClass('busy') || (cellelem.hasClass('busy') && cellelem.hasClass('vbo-partially'))) {
1016 // bookings of 1 night can stop here because there is availability for one day
1017 if (parseInt(totnights) === 1) {
1018 loop = false;
1019 break;
1020 }
1021 // push free cell
1022 newcellspool.push(cellelem);
1023 freenights++;
1024 if (freenights >= totnights) {
1025 loop = false;
1026 break;
1027 }
1028 } else {
1029 // cell is occupied, check if it's occupied by the booking we are moving or not
1030 if (!cell_bids || cell_bids != moving_bids) {
1031 loop = false;
1032 break;
1033 }
1034 // cell is occupied by the same booking we are moving, so this cell counts as free
1035 newcellspool.push(cellelem);
1036 freenights++;
1037 if (freenights >= totnights) {
1038 loop = false;
1039 break;
1040 }
1041 }
1042 }
1043
1044 return freenights;
1045 }
1046
1047 /* DnD function to perform the ajax request of the booking modification */
1048 function doAlterBooking(bid, roomid, landrid) {
1049 var nowdatefrom = jQuery(newcellspool[0]).attr('data-day');
1050 var nowdateto = jQuery(newcellspool[(newcellspool.length -1)]).attr('data-day');
1051
1052 // perform the request
1053 VBOCore.doAjax(
1054 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=alterbooking'); ?>",
1055 {
1056 tmpl: "component",
1057 idorder: bid,
1058 oldidroom: roomid,
1059 idroom: landrid,
1060 fromdate: nowdatefrom,
1061 todate: nowdateto,
1062 e4j_debug: debug_mode
1063 },
1064 (res) => {
1065 if (res.indexOf('e4j.error') >= 0 ) {
1066 // console.log('doAlterBooking-- Booking ID: '+bid+' - Old Room ID: '+roomid+' - New Room ID: '+landrid+' - New Date From: '+nowdatefrom+' - New Date To: '+nowdateto);
1067 // console.log(res);
1068 alert(res.replace("e4j.error.", ""));
1069 //restore the old cells
1070 jQuery('td.vbo-dragging-cells-tmp').removeClass('vbo-dragging-cells-tmp');
1071 jQuery('td.vbo-dragged-cells-tmp').removeClass('vbo-dragged-cells-tmp');
1072 //
1073 jQuery(".vbo-info-overlay-block").hide();
1074 } else {
1075 //move to the new cells and if there are already bookings on those dates, reload the page without moving the blocks
1076 var obj_res = typeof res === 'string' ? JSON.parse(res) : res;
1077 var mustReload = obj_res.esit < 1 ? true : false;
1078 //always force reload if booking was made for more than one room
1079 var samebidcells = jQuery("td.vbo-checkinday[data-bids='"+bid+"']");
1080 if (samebidcells.length > 1) {
1081 mustReload = true;
1082 }
1083 //
1084 jQuery(newcellspool).each(function(k, v) {
1085 var cur_units_booked = parseInt(jQuery(cellspool[k]).find('a').parent('td').attr('data-units-booked'));
1086 if ((v.hasClass('busy') && v.attr('data-bids') != jQuery(cellspool[k]).attr('data-bids')) || isNaN(cur_units_booked) || cur_units_booked > 1) {
1087 mustReload = true;
1088 return false;
1089 }
1090 });
1091 if (mustReload !== true) {
1092 /* switch cells */
1093 var switchmap = [];
1094 jQuery(cellspool).each(function(k, v) {
1095 var switchcell = {
1096 hcont: v.html(),
1097 cl: v.attr('class'),
1098 bids: v.attr('data-bids'),
1099 tit: v.attr('title')
1100 };
1101 switchmap[k] = switchcell;
1102 v.html("").attr('class', 'notbusy').attr('data-bids', '').attr('title', '');
1103 });
1104 jQuery(switchmap).each(function(k, v) {
1105 jQuery(newcellspool[k]).html(v.hcont).attr('class', v.cl).attr('data-bids', v.bids).attr('title', v.tit);
1106 // re-bind hover event
1107 jQuery(newcellspool[k]).hover(function() {
1108 registerHoveringTooltip(this);
1109 }, unregisterHoveringTooltip);
1110 //
1111 });
1112 jQuery('td.vbo-dragging-cells-tmp').removeClass('vbo-dragging-cells-tmp');
1113 jQuery('td.vbo-dragged-cells-tmp').removeClass('vbo-dragged-cells-tmp');
1114 }
1115 if (obj_res.esit < 1) {
1116 //some errors occurred after executing certain functions
1117 if (obj_res.message.length) {
1118 alert(obj_res.message);
1119 }
1120 document.location.href='<?php echo $file_base; ?>?option=com_vikbooking&task=overv';
1121 } else {
1122 finalizeDndUpdate(mustReload, obj_res);
1123 }
1124 }
1125 },
1126 (err) => {
1127 alert("Request Failed");
1128 // restore the old cells
1129 jQuery('td.vbo-dragging-cells-tmp').removeClass('vbo-dragging-cells-tmp');
1130 jQuery('td.vbo-dragged-cells-tmp').removeClass('vbo-dragged-cells-tmp');
1131 //
1132 jQuery(".vbo-info-overlay-block").hide();
1133 }
1134 );
1135 }
1136
1137 /* DnD function to animate a success checkmark. The function may refresh the page once complete as this could be launched when there are multiple units for the rooms */
1138 function finalizeDndUpdate(mustReload, obj_res) {
1139 jQuery('#vbo-dnd-response').html(obj_res.message+' '+obj_res.vcm);
1140 jQuery('.vbo-loading-dnd-footer').hide();
1141 var start = 100;
1142 var mid = 145;
1143 var end = 250;
1144 var width = 22;
1145 var leftX = start;
1146 var leftY = start;
1147 var rightX = mid - (width / 2.7);
1148 var rightY = mid + (width / 2.7);
1149 var animationSpeed = 20;
1150 var closingdelay = 700;
1151 var ctx = document.getElementById('vbo-dnd-canvas-success').getContext('2d');
1152 ctx.lineWidth = width;
1153 ctx.strokeStyle = 'rgba(0, 150, 0, 1)';
1154 for (var i = start; i < mid; i++) {
1155 var drawLeft = window.setTimeout(function () {
1156 ctx.beginPath();
1157 ctx.moveTo(start, start);
1158 ctx.lineTo(leftX, leftY);
1159 ctx.stroke();
1160 leftX++;
1161 leftY++;
1162 }, 1 + (i * animationSpeed) / 3);
1163 }
1164 for (var i = mid; i < end; i++) {
1165 var drawRight = window.setTimeout(function () {
1166 ctx.beginPath();
1167 ctx.moveTo(leftX, leftY);
1168 ctx.lineTo(rightX, rightY);
1169 ctx.stroke();
1170 rightX++;
1171 rightY--;
1172 }, 1 + (i * animationSpeed) / 3);
1173 }
1174 //hide modal window
1175 window.setTimeout(function () {
1176 if (obj_res.vcm.length) {
1177 var vcmbtn = '<br clear="all"/><button type="button" class="btn btn-danger" onclick="'+(mustReload === true ? 'document.location.href=\'<?php echo $file_base; ?>?option=com_vikbooking&task=overv\'' : 'closeEsitDialog();')+'">' + Joomla.JText._('VBANNULLA') + '</button>';
1178 jQuery('#vbo-dnd-response').append(vcmbtn);
1179 } else {
1180 if (mustReload === true) {
1181 document.location.href='<?php echo $file_base; ?>?option=com_vikbooking&task=overv';
1182 } else {
1183 jQuery('.vbo-info-overlay-block').fadeOut(400, function(){
1184 //clear/reset canvas in case of previous drawing and response text
1185 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
1186 jQuery('#vbo-dnd-response').html("");
1187 //
1188 });
1189 }
1190 }
1191 }, closingdelay + (i * animationSpeed) / 3);
1192 //
1193 }
1194
1195 /* DnD function that can be called by those with VCM that have disabled the automated updates. Simply closes the modal window */
1196 function closeEsitDialog() {
1197 jQuery('.vbo-info-overlay-block').fadeOut(400, function(){
1198 //clear/reset canvas in case of previous drawing and response text
1199 var ctx = document.getElementById('vbo-dnd-canvas-success').getContext('2d');
1200 ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
1201 jQuery('#vbo-dnd-response').html("");
1202 //
1203 });
1204 }
1205
1206 /* Hover Tooltip functions */
1207 function registerHoveringTooltip(that) {
1208 if (hovtip) {
1209 return false;
1210 }
1211 if (hovtimer) {
1212 clearTimeout(hovtimer);
1213 hovtimer = null;
1214 }
1215 var elem = jQuery(that);
1216 var cellheight = elem.outerHeight();
1217 var celldata = new Array();
1218 if (elem.hasClass('subroom-busy')) {
1219 celldata.push(elem.parent('tr').attr('data-subroomid'));
1220 celldata.push(elem.attr('data-day'));
1221 }
1222 hovtimer = setTimeout(() => {
1223 if (isdragging) {
1224 // prevent tooltip from popping up if dragging
1225 unregisterHoveringTooltip();
1226
1227 return;
1228 }
1229
1230 // turn flag on
1231 hovtip = true;
1232
1233 // calculate cell-element position
1234 let pos_top = elem.offset().top;
1235 let pos_left = elem.offset().left;
1236 let elem_height = elem.outerHeight();
1237 let screen_width = window?.screen?.width || 0;
1238
1239 // build tooltip block element
1240 let tooltip_block = jQuery('<div></div>');
1241 tooltip_block.addClass('vbo-overview-tipblock');
1242 tooltip_block.append("<div class=\"vbo-overview-tipinner\"><span class=\"vbo-overview-tiploading\">" + Joomla.JText._('VIKLOADING') + "</span></div>");
1243 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>");
1244
1245 // calculate block position
1246 tooltip_block.css('top', (pos_top + elem_height - 16) + 'px');
1247 if (screen_width > 600 && (pos_left + 400) > screen_width) {
1248 // place the tooltip starting from right
1249 tooltip_block.css('left', (pos_left - (400 - elem.outerWidth())) + 'px');
1250 } else {
1251 // regular placing starting from left
1252 tooltip_block.css('left', (pos_left - 6) + 'px');
1253 }
1254
1255 // append block to body
1256 tooltip_block.appendTo(jQuery('body'));
1257
1258 // load tooltip bookings
1259 loadTooltipBookings(elem.attr('data-bids'), celldata);
1260 }, 1500);
1261 }
1262
1263 function unregisterHoveringTooltip() {
1264 clearTimeout(hovtimer);
1265 hovtimer = null;
1266 }
1267
1268 /**
1269 * @deprecated 1.16.10 (J) - 1.6.10 (WP)
1270 */
1271 function adjustHoveringTooltip() {
1272 setTimeout(() => {
1273 var ver_difflim = 35;
1274 var hor_difflim = 20;
1275 var tip_block = jQuery('.vbo-overview-tipblock');
1276 if (!tip_block || !tip_block.length) {
1277 return;
1278 }
1279 var table_wrap = tip_block.closest('.vbo-overv-montable-wrap');
1280
1281 if (tip_block.outerHeight() > table_wrap.outerHeight()) {
1282 // tooltip is too tall to fit in the table, render the modal instead
1283 tip_block.hide().closest('td.busy').trigger('click');
1284 return;
1285 }
1286
1287 // vertical positioning
1288 var otop = tip_block.offset().top;
1289 if (otop > 0 && otop < ver_difflim) {
1290 // adjust tooltip position
1291 tip_block.css('bottom', '-=' + (ver_difflim - otop));
1292 } else if (otop < table_wrap.offset().top) {
1293 // tooltip exceeds wrapping table, move it underneath the cell
1294 var extra_padding_top = 0;
1295 var tip_inner_padtop = tip_block.find('.vbo-overview-tipinner').css('padding-top');
1296 if (tip_inner_padtop) {
1297 // get only numbers
1298 extra_padding_top = tip_inner_padtop.replace(/[^0-9]/g, '');
1299 extra_padding_top = !isNaN(extra_padding_top) ? parseInt(extra_padding_top) : 0;
1300 }
1301 tip_block.css('bottom', 'auto').css('top', (tip_block.parent('td').outerHeight() - extra_padding_top));
1302 }
1303
1304 // horizontal positioning
1305 if (tip_block.offset().left < table_wrap.offset().left) {
1306 var left_diff = table_wrap.offset().left - tip_block.offset().left;
1307 if (left_diff > hor_difflim) {
1308 // tooltip exceeds table on the left, move it to the right of the cell (i.e 1st day of month)
1309 tip_block.css('right', tip_block.css('left'));
1310 tip_block.css('left', 'unset');
1311 }
1312 }
1313 }, 60);
1314 }
1315
1316 function hideVboTooltip() {
1317 jQuery('.vbo-overview-tipblock').remove();
1318 hovtip = false;
1319 }
1320
1321 function loadTooltipBookings(bids, celldata) {
1322 if (!bids || bids === undefined || !bids.length) {
1323 hideVboTooltip();
1324 return false;
1325 }
1326
1327 var subroomdata = celldata.length ? celldata[0] : '';
1328
1329 // perform the request
1330 VBOCore.doAjax(
1331 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=getbookingsinfo'); ?>",
1332 {
1333 tmpl: "component",
1334 idorders: bids,
1335 subroom: subroomdata
1336 },
1337 (res) => {
1338 try {
1339 var obj_res = typeof res === 'string' ? JSON.parse(res) : res;
1340 jQuery('.vbo-overview-tiploading').remove();
1341 var container = jQuery('.vbo-overview-tipinner');
1342 jQuery(obj_res).each(function(k, v) {
1343 // get base navigation URIs
1344 var base_uri_details = jQuery('.vbo-basenavuri-details').attr('href');
1345 var base_uri_edit = jQuery('.vbo-basenavuri-edit').attr('href');
1346
1347 // build content
1348 var bcont = "<div class=\"vbo-overview-tip-bookingcont\">";
1349 bcont += "<div class=\"vbo-overview-tip-bookingcont-left\">";
1350 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) + "\"><i class=\"<?php echo VikBookingIcons::i('edit'); ?>\"></i></a></span></span><span class=\"vbo-overview-tip-cnt\">"+v.id+"</span></div>";
1351 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>";
1352 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) + "\">"+v.ts+"</a></span></div>";
1353 if (bctags_count > 0) {
1354 var bctag_title = '';
1355 var bctag_color = '#ffffff';
1356 if (v.colortag.hasOwnProperty('color')) {
1357 bctag_color = v.colortag.color;
1358 bctag_title = v.colortag.name;
1359 }
1360 bcont += "<div class=\"vbo-overview-tip-bctag-wrap\"><div class=\"vbo-overview-tip-bctag\" data-bid=\""+v.id+"\" data-ctagcolor=\""+bctag_color+"\" style=\"background-color: "+bctag_color+"; color: "+v.colortag.fontcolor+";\"><i class=\"vboicn-price-tags\"></i></div><span class=\"vbo-overview-tip-bctag-name\">" + bctag_title + "</span></div>";
1361 }
1362 bcont += "</div>";
1363 bcont += "<div class=\"vbo-overview-tip-bookingcont-right\">";
1364 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>";
1365 if (v.roomsnum > 1) {
1366 bcont += "<div class=\"vbo-overview-tip-brooms\"><span class=\"vbo-overview-tip-lbl\">" + Joomla.JText._('VBEDITORDERROOMSNUM') + (parseInt(v.roomsnum) > 1 ? " (" + v.roomsnum + ")" : "") + "</span><span class=\"vbo-overview-tip-cnt\">" + v.room_names + "</span></div>";
1367 }
1368 bcont += "<div class=\"vbo-overview-tip-bguests\"><span class=\"vbo-overview-tip-lbl\">" + Joomla.JText._('VBDAYS') + (v.split_stay > 0 ? ' (' + Joomla.JText._('VBO_SPLIT_STAY') + ')' : '') + "</span><span class=\"vbo-overview-tip-cnt hasTooltip\" title=\"" + Joomla.JText._('VBPICKUPAT') + " " + v.checkin + " - " + Joomla.JText._('VBRELEASEAT') + " " + v.checkout + "\">" + v.days + ", " + Joomla.JText._('VBEDITORDERADULTS') + ": " + v.tot_adults + (v.tot_children > 0 ? ", " + Joomla.JText._('VBEDITORDERCHILDREN') + ": " + v.tot_children : "") + "</span></div>";
1369 if (v.hasOwnProperty('rindexes')) {
1370 for (var rindexk in v.rindexes) {
1371 if (v.rindexes.hasOwnProperty(rindexk)) {
1372 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>";
1373 }
1374 }
1375 }
1376 if (v.hasOwnProperty('channelimg')) {
1377 bcont += "<div class=\"vbo-overview-tip-bprovenience\"><span class=\"vbo-overview-tip-lbl\">" + Joomla.JText._('VBPVIEWORDERCHANNEL') + "</span><span class=\"vbo-overview-tip-cnt\">" + v.channelimg + "</span></div>";
1378 }
1379 if (v.hasOwnProperty('optindexes') && celldata.length) {
1380 var subroomids = celldata[0].split('-');
1381 bcont += "<div class=\"vbo-overview-tip-optindexes\"><span class=\"vbo-overview-tip-lbl\"> </span><span class=\"vbo-overview-tip-cnt\"><select onchange=\"vboMoveSubunit('" + v.id + "', '" + subroomids[0] + "', '" + subroomids[1] + "', this.value, '" + celldata[1] + "');\">" + v.optindexes + "</select></span></div>";
1382 }
1383 bcont += "<div class=\"vbo-overview-tip-bookingcont-total\">";
1384 bcont += "<div class=\"vbo-overview-tip-btot\"><span class=\"vbo-overview-tip-lbl\">" + Joomla.JText._('VBEDITORDERNINE') + "</span><span class=\"vbo-overview-tip-cnt\">" + vboMessages.currencySymb + " " + v.format_tot + "</span></div>";
1385 if (v.totpaid > 0.00) {
1386 bcont += "<div class=\"vbo-overview-tip-btot vbo-overview-tip-btotpaid\"><span class=\"vbo-overview-tip-lbl\">" + Joomla.JText._('VBPEDITBUSYTOTPAID') + "</span><span class=\"vbo-overview-tip-cnt\">" + vboMessages.currencySymb + " " + v.format_totpaid + "</span></div>";
1387 }
1388 var getnotes = v.adminnotes;
1389 if (getnotes !== null && getnotes.length) {
1390 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>";
1391 }
1392 bcont += "</div>";
1393 bcont += "</div>";
1394 bcont += "</div>";
1395 container.append(bcont);
1396 jQuery('.vbo-overview-tipexpander').show();
1397 });
1398
1399 /**
1400 * @deprecated 1.16.10 (J) - 1.6.10 (WP)
1401 */
1402 // adjust the position so that it won't go under other contents
1403 // adjustHoveringTooltip();
1404
1405 jQuery(".hasTooltip").tooltip();
1406 } catch(err) {
1407 // restore
1408 hideVboTooltip();
1409 // display error
1410 console.error('could not parse JSON response', err, res);
1411 alert('Could not parse JSON response');
1412 }
1413 },
1414 (err) => {
1415 // restore
1416 hideVboTooltip();
1417 // display error
1418 console.error(err);
1419 alert(err.responseText);
1420 }
1421 );
1422 }
1423
1424 /**
1425 * Set a sub-unit to a room booking. Triggered by the room-day bookings modal
1426 */
1427 function vboSetSubunit(bid, rid, orkey, rindex) {
1428 if (!(rindex + '').length) {
1429 return false;
1430 }
1431
1432 if (!confirm(Joomla.JText._('VBOFEATASSIGNUNIT') + rindex + '?')) {
1433 return false;
1434 }
1435
1436 // show loading
1437 VBOCore.emitEvent('vbo-loading-modal-overv-rdaybookings');
1438
1439 // make the request to set the room sub-unit
1440 VBOCore.doAjax(
1441 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=bookings.set_room_booking_subunit'); ?>",
1442 {
1443 bid: bid,
1444 rid: rid,
1445 orkey: orkey,
1446 rindex: rindex,
1447 tmpl: 'component'
1448 },
1449 (res) => {
1450 // dismiss the modal (no need to stop the loading)
1451 VBOCore.emitEvent('vbo-dismiss-modal-overv-rdaybookings');
1452
1453 try {
1454 var obj_res = typeof res === 'string' ? JSON.parse(res) : res;
1455 if (!obj_res.hasOwnProperty('nights') || !Array.isArray(obj_res['nights']) || !obj_res['nights'].length) {
1456 // invalid response
1457 throw new Error('Invalid response');
1458 }
1459
1460 // check if cells can be occupied
1461 var sub_rows = jQuery('tr.vboverviewtablerow-subunit[data-subroomid="' + rid + '-' + rindex + '"]');
1462 if (!sub_rows || !sub_rows.length) {
1463 console.error('Could not find any room sub-unit row');
1464 return false;
1465 }
1466
1467 // loop through all sub-unit rows to match the cells for the nights affected
1468 sub_rows.each(function() {
1469 var sub_row = jQuery(this);
1470 obj_res['nights'].forEach((ymd, kindex) => {
1471 let sub_row_cell = sub_row.find('td[data-day="' + ymd + '"]');
1472 if (sub_row_cell.length) {
1473 sub_row_cell.removeClass('subnotbusy').addClass('subroom-busy');
1474 if (kindex == 0) {
1475 sub_row_cell.addClass('vbo-checkinday');
1476 }
1477 sub_row_cell.attr('data-bids', '-' + obj_res['bid'] + '-');
1478 if (!sub_row_cell.find('span.vbo-draggable-sp').length) {
1479 sub_row_cell.prepend('<span class="vbo-draggable-sp"><a class="vbo-overview-subredday">&bull;</a></span>');
1480 }
1481 }
1482 });
1483 });
1484 } catch(err) {
1485 console.error('could not parse JSON response', err, res);
1486 alert('Could not parse JSON response');
1487 }
1488 },
1489 (err) => {
1490 // stop loading
1491 VBOCore.emitEvent('vbo-loading-modal-overv-rdaybookings');
1492
1493 // log and display the error
1494 console.error(err);
1495 alert(err.responseText);
1496 }
1497 );
1498 }
1499
1500 /**
1501 * Move a subunit group of cells to another subroom row. Triggered by the Hover Tooltip, by the room-day bookings modal, or by DnD.
1502 */
1503 function vboMoveSubunit(bid, rid, old_rindex, new_rindex, dday) {
1504 if (!confirm(Joomla.JText._('VBOFEATASSIGNUNIT') + new_rindex.replace('#', '') + '?')) {
1505 return false;
1506 }
1507 // check if movement can be made
1508 var cur_tr = jQuery('tr.vboverviewtablerow-subunit[data-subroomid="' + rid + '-' + old_rindex + '"]');
1509 if (!cur_tr || !cur_tr.length) {
1510 console.error('could not find the parent row of the subunit cells');
1511 return false;
1512 }
1513 var maincell = cur_tr.find('td.subroom-busy[data-day="' + dday + '"]');
1514 if (!maincell || !maincell.length) {
1515 console.error('could not find the main cell of the subunit to move');
1516 return false;
1517 }
1518 var dest_tr = jQuery('tr.vboverviewtablerow-subunit[data-subroomid="' + rid + '-' + new_rindex + '"]');
1519 if (!dest_tr || !dest_tr.length) {
1520 console.error('could not find the destination row for the subunit cells');
1521 return false;
1522 }
1523 var targetbids = maincell.attr('data-bids');
1524 if (targetbids.indexOf('-' + bid + '-') < 0) {
1525 console.error('given bid does not match with cell bids', bid, targetbids);
1526 return false;
1527 }
1528 var firstcell = maincell;
1529 var loop = true;
1530 while (loop === true) {
1531 var prevsib = firstcell.prev();
1532 if (prevsib && prevsib.length && prevsib.attr('data-bids') && prevsib.attr('data-bids').indexOf('-' + bid + '-') >= 0) {
1533 firstcell = prevsib;
1534 } else {
1535 loop = false;
1536 }
1537 }
1538 // make sure to get the check-in day (first cell)
1539 dday = firstcell.attr('data-day');
1540 var destcell = dest_tr.find('td.subnotbusy[data-day="' + dday + '"]');
1541 if (!destcell || !destcell.length) {
1542 // first destination cell is occupied, check if swap is allowed
1543 if (dest_tr.find('td.subroom-busy[data-day="' + dday + '"]').length) {
1544 var landbids = dest_tr.find('td.subroom-busy[data-day="' + dday + '"]').attr('data-bids');
1545 return vboSwapRoomSubunits(bid, landbids.replaceAll('-', ''), rid, old_rindex, new_rindex, dday);
1546 }
1547 console.error('could not find the first free destination cell');
1548 return false;
1549 }
1550 // check if all dates are free before making movements. Redundant, but useful.
1551 var freedates = true;
1552 var copyfirstcell = firstcell;
1553 var copydday = dday;
1554 loop = true;
1555 while (loop === true) {
1556 var nextsib = copyfirstcell.next();
1557 if (nextsib && nextsib.length && nextsib.attr('data-bids') && nextsib.attr('data-bids').indexOf('-' + bid + '-') >= 0) {
1558 copyfirstcell = nextsib;
1559 copydday = copyfirstcell.attr('data-day');
1560 var copydestcell = dest_tr.find('td.subnotbusy[data-day="' + copydday + '"]');
1561 if (!copydestcell || !copydestcell.length) {
1562 console.error('could not find the next free destination cell for ' + copydday);
1563 freedates = false;
1564 loop = false;
1565 }
1566 } else {
1567 loop = false;
1568 }
1569 }
1570 if (freedates === false) {
1571 return false;
1572 }
1573
1574 // loading opacity
1575 jQuery('.vbo-overview-tipblock').css('opacity', '0.6');
1576
1577 // perform the request
1578 VBOCore.doAjax(
1579 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=switchRoomIndex'); ?>",
1580 {
1581 tmpl: "component",
1582 bid: bid,
1583 rid: rid,
1584 old_rindex: old_rindex,
1585 new_rindex: new_rindex
1586 },
1587 (res) => {
1588 if (res.indexOf('e4j.error') >= 0 ) {
1589 // console.log(res);
1590 alert(res.replace("e4j.error.", ""));
1591
1592 // restore loading opacity in container
1593 jQuery('.vbo-overview-tipblock').css('opacity', '1');
1594
1595 // abort
1596 return;
1597 }
1598
1599 // hide tooltip
1600 hideVboTooltip();
1601
1602 // loop for moving cells
1603 loop = true;
1604 while (loop === true) {
1605 // populate destination cell attributes, classes and content
1606 destcell.removeClass('subnotbusy').addClass('subroom-busy').attr('data-bids', firstcell.attr('data-bids'));
1607 if (firstcell.hasClass('vbo-checkinday')) {
1608 firstcell.removeClass('vbo-checkinday');
1609 destcell.addClass('vbo-checkinday');
1610 }
1611 firstcell.children().not('.vbo-tableaux-booking-checkout').not('.vbo-roomdaynote-trigger').appendTo(destcell);
1612
1613 // re-bind hover event for tooltip
1614 jQuery(destcell).hover(function() {
1615 registerHoveringTooltip(this);
1616 }, unregisterHoveringTooltip);
1617
1618 // clean up the previous cell from which we moved data
1619 firstcell.removeClass('subroom-busy').addClass('subnotbusy').attr('data-bids', '');
1620
1621 // check if we have a next iteration to perform
1622 var nextsib = firstcell.next();
1623 if (nextsib && nextsib.length && nextsib.attr('data-bids') && nextsib.attr('data-bids').indexOf('-' + bid + '-') >= 0) {
1624 // prepare data for the next iteration
1625 firstcell = nextsib;
1626 dday = firstcell.attr('data-day');
1627 destcell = dest_tr.find('td.subnotbusy[data-day="' + dday + '"]');
1628 if (!destcell || !destcell.length) {
1629 console.error('could not find the next free destination cell');
1630 loop = false;
1631 }
1632 } else {
1633 // all cells were moved, break the loop
1634 loop = false;
1635 // check if we need to move the checkout snake as well
1636 if (nextsib && nextsib.find('.vbo-tableaux-booking-checkout').length) {
1637 dday = nextsib.attr('data-day');
1638 destcell = dest_tr.find('td.subnotbusy[data-day="' + dday + '"]');
1639 if (!destcell || !destcell.length) {
1640 // the landing checkout snake may be on an occupied date
1641 destcell = dest_tr.find('td.subroom-busy[data-day="' + dday + '"]');
1642 }
1643 if (destcell && destcell.length) {
1644 // prepend the checkout snake to the next cell
1645 nextsib.find('.vbo-tableaux-booking-checkout').prependTo(destcell);
1646 }
1647 }
1648 }
1649 }
1650 },
1651 (err) => {
1652 alert("Request Failed");
1653 // restore loading opacity in container
1654 jQuery('.vbo-overview-tipblock').css('opacity', '1');
1655 }
1656 );
1657
1658 return true;
1659 }
1660
1661 /**
1662 * Swap room sub-unit with another sub-unit on an occupied date. Triggered by DnD.
1663 */
1664 function vboSwapRoomSubunits(bid, bidtwo, rid, old_rindex, new_rindex, dday) {
1665 if (!confirm(Joomla.JText._('VBO_CONF_SWAP_RNUMB').replace('%s', old_rindex).replace('%s', new_rindex))) {
1666 return false;
1667 }
1668 if (!bid || !bidtwo || !rid || !old_rindex || !new_rindex || !dday) {
1669 console.error('Missing required arguments', bid, bidtwo, rid, old_rindex, new_rindex, dday);
1670 return false;
1671 }
1672 // check if movement can be made
1673 var cur_tr = jQuery('tr.vboverviewtablerow-subunit[data-subroomid="' + rid + '-' + old_rindex + '"]');
1674 if (!cur_tr || !cur_tr.length) {
1675 console.error('could not find the parent row of the subunit cells');
1676 return false;
1677 }
1678 var maincell = cur_tr.find('td.subroom-busy[data-day="' + dday + '"]');
1679 if (!maincell || !maincell.length) {
1680 console.error('could not find the main cell of the subunit to move');
1681 return false;
1682 }
1683 var dest_tr = jQuery('tr.vboverviewtablerow-subunit[data-subroomid="' + rid + '-' + new_rindex + '"]');
1684 if (!dest_tr || !dest_tr.length) {
1685 console.error('could not find the destination row for the subunit cells');
1686 return false;
1687 }
1688 var destcell = dest_tr.find('td.subroom-busy[data-day="' + dday + '"]');
1689 if (!destcell || !destcell.length) {
1690 console.error('could not find the destination cell of the subunit to move');
1691 return false;
1692 }
1693
1694 // gather the cells for the swop
1695 var swap_cells_one = [];
1696 var swap_cells_two = [];
1697 var swap_dates_one = [];
1698 var swap_dates_two = [];
1699 var swap_cell_from = maincell;
1700 var swap_cell_to = destcell;
1701
1702 // register the elements to swap of the first sub-unit
1703 while (true) {
1704 if (!swap_cell_from || !swap_cell_from.length) {
1705 // next cell not found
1706 break;
1707 }
1708 var swap_bids = swap_cell_from.attr('data-bids');
1709 if (!swap_bids || swap_bids.indexOf('-' + bid + '-') < 0) {
1710 // last occupied cell reached
1711 break;
1712 }
1713 // push cloned cell data to move
1714 swap_cells_one.push(swap_cell_from.children().not('.vbo-tableaux-booking-checkout').not('.vbo-roomdaynote-trigger').clone(true));
1715 swap_dates_one.push(swap_cell_from.attr('data-day'));
1716 // set next iteration cell
1717 swap_cell_from = swap_cell_from.next();
1718 }
1719
1720 // register the elements to swap of the second sub-unit
1721 while (true) {
1722 if (!swap_cell_to || !swap_cell_to.length) {
1723 // next cell not found
1724 break;
1725 }
1726 var swap_bids = swap_cell_to.attr('data-bids');
1727 if (!swap_bids || swap_bids.indexOf('-' + bidtwo + '-') < 0) {
1728 // last occupied cell reached
1729 break;
1730 }
1731 // make sure we are swapping with the check-in day, or if it's a stay-date we need to unshift until the check-in
1732 if (!swap_cells_two.length && !swap_cell_to.hasClass('vbo-checkinday')) {
1733 // prepend first the cells until the check-in or until not found
1734 var prev_cell = swap_cell_to;
1735 while (true) {
1736 prev_cell = prev_cell.prev();
1737 if (!prev_cell || !prev_cell.length) {
1738 break;
1739 }
1740 var prev_cell_snake = prev_cell.find('.vbo-tableaux-booking').not('.vbo-tableaux-booking-checkout');
1741 if (prev_cell_snake && prev_cell_snake.length && (prev_cell_snake.hasClass('vbo-tableaux-booking-checkin') || prev_cell_snake.hasClass('vbo-tableaux-booking-stay'))) {
1742 // prepend cloned cell data to move
1743 swap_cells_two.unshift(prev_cell.children().not('.vbo-tableaux-booking-checkout').not('.vbo-roomdaynote-trigger').clone(true));
1744 swap_dates_two.unshift(prev_cell.attr('data-day'));
1745 } else {
1746 // no more previous cells
1747 break;
1748 }
1749 }
1750 }
1751 // push cloned cell data to move
1752 swap_cells_two.push(swap_cell_to.children().not('.vbo-tableaux-booking-checkout').not('.vbo-roomdaynote-trigger').clone(true));
1753 swap_dates_two.push(swap_cell_to.attr('data-day'));
1754 // set next iteration cell
1755 swap_cell_to = swap_cell_to.next();
1756 }
1757
1758 if (!swap_cells_one.length || !swap_cells_two.length) {
1759 console.error('could not gather the cells to swap', swap_cells_one, swap_cells_two);
1760 return false;
1761 }
1762
1763 // walk over the cells of the first room to swap and check the corresponding destination
1764 var check_destcell = destcell;
1765 for (let i = 0; i < swap_cells_one.length; i++) {
1766 if (!check_destcell || !check_destcell.length) {
1767 // this cell does not exist, abort
1768 console.error('destination cell to #' + (i + 1) + ' was not found');
1769 return false;
1770 }
1771 var dest_bids = check_destcell.attr('data-bids');
1772 if (dest_bids && dest_bids.length && dest_bids.indexOf('-' + bidtwo + '-') < 0) {
1773 // this cell is already occupied, abort
1774 console.error('destination cell to #' + (i + 1) + ' is occupied by another reservation');
1775 return false;
1776 }
1777 // set next destination cell
1778 check_destcell = check_destcell.next();
1779 }
1780
1781 // walk over the cells of the second room to swap and check the corresponding destination
1782 check_destcell = maincell;
1783 for (let i = 0; i < swap_cells_two.length; i++) {
1784 if (!check_destcell || !check_destcell.length) {
1785 // this cell does not exist, abort
1786 console.error('destination cell from #' + (i + 1) + ' was not found');
1787 return false;
1788 }
1789 var dest_bids = check_destcell.attr('data-bids');
1790 if (dest_bids && dest_bids.length && dest_bids.indexOf('-' + bid + '-') < 0) {
1791 // this cell is already occupied, abort
1792 console.error('destination cell from #' + (i + 1) + ' is occupied by another reservation');
1793 return false;
1794 }
1795 // set next destination cell
1796 check_destcell = check_destcell.next();
1797 }
1798
1799 // make the AJAX request and swap cells only in case of success
1800 VBOCore.doAjax(
1801 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=bookings.swap_room_subunits'); ?>",
1802 {
1803 bid_one: bid,
1804 bid_two: bidtwo,
1805 rid: rid,
1806 index_one: old_rindex,
1807 index_two: new_rindex,
1808 checkin: dday,
1809 tmpl: 'component'
1810 },
1811 (res) => {
1812 // move the cells of the first sub-unit onto the cells of the second sub-unit
1813 var target_cell = destcell;
1814 var current_cell = maincell;
1815 var swap_diff_dates = false;
1816 for (let counter = 0; counter < swap_cells_one.length; counter++) {
1817 // remove elements from target
1818 target_cell.children().not('.vbo-tableaux-booking-checkout').not('.vbo-roomdaynote-trigger').remove();
1819 // swap cell elements
1820 swap_cells_one[counter].appendTo(target_cell);
1821 // set proper bids attribute
1822 target_cell.attr('data-bids', '-' + bid + '-');
1823 // check if we are swapping with a non check-in day sub-unit occupied cell
1824 if (counter === 0 && !target_cell.hasClass('vbo-checkinday')) {
1825 target_cell.addClass('vbo-checkinday');
1826 swap_diff_dates = true;
1827 } else if (counter > 0 && !target_cell.hasClass('subroom-busy')) {
1828 target_cell.removeClass('subnotbusy').addClass('subroom-busy');
1829 }
1830 // check if the reservations have different nights of stay
1831 var cell_will_occupy = true;
1832 if (counter > 0 && swap_diff_dates && !swap_dates_two.includes(swap_dates_one[counter])) {
1833 // the swapped unit will not occupy this cell, so clean it
1834 cell_will_occupy = false;
1835 } else if ((counter + 1) > swap_cells_two.length) {
1836 // this reservation is longer than the other
1837 cell_will_occupy = false;
1838 }
1839 if (!cell_will_occupy) {
1840 // remove the children elements from this longer reservation
1841 current_cell.children().not('.vbo-tableaux-booking-checkout').not('.vbo-roomdaynote-trigger').remove();
1842 // remove the occupied class as well
1843 current_cell.removeClass('subroom-busy').addClass('subnotbusy').attr('data-bids', '');
1844 }
1845 // set next target cell
1846 target_cell = target_cell.next();
1847 // update current cell
1848 current_cell = current_cell.next();
1849 }
1850
1851 // move the checkout snake of the first sub-unit
1852 if (target_cell && target_cell.length && current_cell && current_cell.length && current_cell.find('.vbo-tableaux-booking-checkout').length) {
1853 current_cell.find('.vbo-tableaux-booking-checkout').prependTo(target_cell);
1854 }
1855
1856 // move the cells of the second sub-unit onto the cells of the first sub-unit
1857 target_cell = maincell;
1858 current_cell = destcell;
1859 // check if we are swapping with a non check-in day sub-unit occupied cell
1860 if (swap_diff_dates) {
1861 var prev_land_cell = current_cell;
1862 while (true) {
1863 prev_land_cell = prev_land_cell.prev();
1864 if (!prev_land_cell || !prev_land_cell.length) {
1865 break;
1866 }
1867 if (prev_land_cell.hasClass('subroom-busy')) {
1868 // overwrite first cell to move and to land
1869 current_cell = prev_land_cell;
1870 target_cell = target_cell.prev();
1871 // empty this cell because no one will occupy it
1872 current_cell.children().not('.vbo-tableaux-booking-checkout').not('.vbo-roomdaynote-trigger').remove();
1873 // make the cell free
1874 current_cell.removeClass('subroom-busy').addClass('subnotbusy').attr('data-bids', '');
1875 }
1876 if (prev_land_cell.hasClass('vbo-checkinday')) {
1877 // we have reached the first cell of this booking
1878 break;
1879 }
1880 }
1881 }
1882 for (let counter = 0; counter < swap_cells_two.length; counter++) {
1883 // remove elements from target
1884 target_cell.children().not('.vbo-tableaux-booking-checkout').not('.vbo-roomdaynote-trigger').remove();
1885 // swap cell elements
1886 swap_cells_two[counter].appendTo(target_cell);
1887 // set proper bids attribute
1888 target_cell.attr('data-bids', '-' + bidtwo + '-');
1889 // check if we are swapping with a non check-in day sub-unit occupied cell
1890 if (counter === 0 && !target_cell.hasClass('vbo-checkinday')) {
1891 target_cell.addClass('vbo-checkinday');
1892 if (current_cell.hasClass('vbo-checkinday') && !swap_dates_one.includes(swap_dates_two[counter])) {
1893 current_cell.removeClass('vbo-checkinday');
1894 }
1895 } else if (counter > 0 && target_cell.hasClass('vbo-checkinday')) {
1896 target_cell.removeClass('vbo-checkinday');
1897 }
1898 if (!target_cell.hasClass('subroom-busy')) {
1899 target_cell.removeClass('subnotbusy').addClass('subroom-busy');
1900 }
1901 // check if the reservations have different nights of stay
1902 if ((counter + 1) > swap_cells_one.length) {
1903 // remove the children elements from this longer reservation
1904 current_cell.children().not('.vbo-tableaux-booking-checkout').not('.vbo-roomdaynote-trigger').remove();
1905 // remove the occupied class as well
1906 current_cell.removeClass('subroom-busy').addClass('subnotbusy').attr('data-bids', '');
1907 // set the occupied class onto the target
1908 target_cell.removeClass('subnotbusy').addClass('subroom-busy');
1909 }
1910 // set next target cell
1911 target_cell = target_cell.next();
1912 // update current cell
1913 current_cell = current_cell.next();
1914 }
1915
1916 // move the checkout snake of the second sub-unit
1917 if (target_cell && target_cell.length && current_cell && current_cell.length && current_cell.find('.vbo-tableaux-booking-checkout').length) {
1918 // make sure to move just the first check-out snake, because snakes with equal duration may have moved the check-out here already
1919 current_cell.find('.vbo-tableaux-booking-checkout').first().prependTo(target_cell);
1920 }
1921 },
1922 (err) => {
1923 alert(err.responseText);
1924 console.error(err.responseText);
1925 }
1926 );
1927
1928 return true;
1929 }
1930
1931 /**
1932 * Open a booking in a new tab.
1933 */
1934 function vboOvervOpenBooking(bid) {
1935 var open_url = jQuery('.vbo-basenavuri-details').attr('href');
1936 open_url = open_url.replace('%d', bid);
1937 // navigate in a new tab
1938 window.open(open_url, '_blank');
1939 }
1940
1941 /**
1942 * DOM ready state
1943 */
1944 jQuery(function() {
1945 // register to the event emitted when a new booking is created through an admin widget
1946 document.addEventListener('vbo_new_booking_created', (e) => {
1947 if (!e || !e.detail || !e.detail.hasOwnProperty('bid') || !e.detail['bid']) {
1948 // do nothing
1949 return;
1950 }
1951 // reload the page to display the new booking just created
1952 location.reload();
1953 });
1954
1955 /* calculate padding to increase draggable area and avoid "display: table" for the parent TD and "display: table-cell" for the draggable SPAN with 100% width and height, middle aligns */
1956 jQuery("td.vbo-checkinday span.vbo-draggable-sp").each(function(k, v) {
1957 var parentheight = jQuery(this).closest('td').height();
1958 var spheight = jQuery(this).height();
1959 var padsp = Math.floor((parentheight - spheight) / 2);
1960 jQuery(this).css({"padding": padsp + "px 0"});
1961 });
1962
1963 /* Expand/Collapse tooltip */
1964 jQuery(document.body).on("click", ".vbo-overview-expandtoggle", function() {
1965 jQuery(this).closest('.vbo-overview-tipblock').toggleClass('vbo-overview-tipblock-expanded');
1966 });
1967
1968 /* DnD Start */
1969
1970 /* DnD Event dragstart */
1971 jQuery(document.body).on("dragstart", "td.vbo-checkinday span, td.vbo-checkinday .vbo-tableaux-booking-checkin", function(e) {
1972 // start dragging and prevent tooltip from popping up
1973 isdragging = true;
1974 if (hovtip === true) {
1975 hideVboTooltip();
1976 }
1977
1978 var parentcell = jQuery(this).closest('td');
1979 if (parentcell.hasClass('busytmplock') || parentcell.hasClass('busy-sharedcalendar')) {
1980 return false;
1981 }
1982
1983 // reset pool of booked date cells
1984 cellspool = [];
1985 var dt = e.originalEvent.dataTransfer;
1986 var bidstart = parentcell.attr('data-bids');
1987 var checkind = parentcell.attr('data-day');
1988 var roomid = parentcell.parent('tr').find('td').first().attr('data-roomid');
1989 if (!bidstart.length || !checkind.length || !roomid.length) {
1990 return false;
1991 }
1992
1993 // count booking nights and set booked date cells
1994 var totnights = countBookingCells(parentcell, bidstart, roomid);
1995
1996 // add dragging class
1997 if (jQuery(this).find('.vbo-tableaux-guest-name').length) {
1998 jQuery(this).find('.vbo-tableaux-guest-name').addClass('vbo-dragging-sp');
1999 } else {
2000 if (!jQuery(this).hasClass('vbo-tableaux-booking-avatar')) {
2001 jQuery(this).addClass('vbo-dragging-sp');
2002 }
2003 }
2004
2005 // populate dataTransfer data
2006 var parent_rid = '';
2007 var subunit_id = '';
2008 if (parentcell.hasClass('subroom-busy')) {
2009 var subdata = parentcell.parent('tr').attr('data-subroomid').split('-');
2010 parent_rid = subdata[0];
2011 subunit_id = subdata[1];
2012 }
2013 dt.setData('Checkin', checkind);
2014 dt.setData('Roomid', roomid);
2015 dt.setData('Bid', bidstart);
2016 dt.setData('Nights', totnights);
2017 dt.setData('Parentrid', parent_rid);
2018 dt.setData('Subunit', subunit_id);
2019 });
2020
2021 /* DnD Events dragenter dragover drop */
2022 jQuery(document.body).on("dragenter dragover drop", "td.notbusy, td.busy, td.subnotbusy, td.subroom-busy", function(e) {
2023 // always prevent default
2024 e.preventDefault();
2025
2026 // gather dragging cell data
2027 var dt = e.originalEvent.dataTransfer;
2028 var checkind = dt.getData('Checkin');
2029 var roomid = dt.getData('Roomid');
2030 var bid = dt.getData('Bid');
2031 var totnights = dt.getData('Nights');
2032 var parent_rid = dt.getData('Parentrid');
2033 var subunit_id = dt.getData('Subunit');
2034
2035 // check if we are hovering something that requires styling
2036 if (e.type === 'dragover') {
2037 var hover_cell = jQuery(this);
2038 if (hover_cell.hasClass('subroom-busy') && hover_cell.hasClass('vbo-checkinday')) {
2039 if (hover_cell.attr('data-day') != checkind && totnights > 1) {
2040 hover_cell.find('.vbo-tableaux-booking-checkin').addClass('vbo-droppable-cell');
2041 }
2042 }
2043 // do not proceed any further
2044 return;
2045 }
2046
2047 // only look for drop
2048 if (e.type !== 'drop') {
2049 // ignore if we are not dropping the element
2050 return;
2051 }
2052
2053 // always reset pool of cells
2054 newcellspool = [];
2055
2056 /* check if drop is allowed */
2057 var landrow = jQuery(this).closest('tr');
2058 var landrid = landrow.find('td').first().attr('data-roomid');
2059 var landbids = jQuery(this).attr('data-bids');
2060 if (landrid === undefined || !landrid.length) {
2061 return false;
2062 }
2063
2064 if (jQuery(this).hasClass('busy') && !jQuery(this).hasClass('vbo-partially')) {
2065 if (bid != landbids) {
2066 // landed on an occupied date but not for the same booking ID so drop is not allowed on this day
2067 return false;
2068 }
2069 }
2070
2071 // build pool of cells
2072 var freenights = countCellFreeNights(jQuery(this), landrid, totnights, bid);
2073 if (freenights < totnights) {
2074 alert(Joomla.JText._('VBOVWDNDERRNOTENCELLS').replace('%s', freenights).replace('%d', totnights));
2075 return false;
2076 }
2077
2078 // collect the new dates
2079 var nowdatefrom = jQuery(newcellspool[0]).attr('data-day');
2080 var nowdateto = jQuery(newcellspool[(newcellspool.length -1)]).attr('data-day');
2081
2082 // check if we are just moving a sub-unit onto another for the same dates
2083 if (jQuery(this).hasClass('subnotbusy') || jQuery(this).hasClass('subroom-busy')) {
2084 // dropping on a sub-unit is only allowed onto the same room ID, for the same dates (switch room index only)
2085 var subdata = landrow.attr('data-subroomid').split('-');
2086 if (!subdata || !parent_rid || !subunit_id || parent_rid != subdata[0] || !subdata[1] || subunit_id == subdata[1]) {
2087 // display proper error
2088 if (subdata[1] && subunit_id == subdata[1]) {
2089 // must use the same dates for sub-units
2090 alert(Joomla.JText._('VBO_DRAG_SUBUNITS_SAMEDATE'));
2091 } else {
2092 // only sub-units can be moved onto rows for sub-units
2093 alert(Joomla.JText._('VBO_ERR_SUBUN_MOVE_SUBUN'));
2094 }
2095 return false;
2096 }
2097
2098 // sub-unit drag&drop is only allowed on the same date to change sub-unit index
2099 if (nowdatefrom != checkind) {
2100 alert(Joomla.JText._('VBO_DRAG_SUBUNITS_SAMEDATE'));
2101 return false;
2102 }
2103
2104 // attempt to perform the switch-room-index request
2105 if (jQuery(this).hasClass('subnotbusy')) {
2106 // move sub-unit to a different index
2107 if (!vboMoveSubunit(bid.replaceAll('-', ''), parent_rid, subunit_id, subdata[1], checkind)) {
2108 return false;
2109 }
2110 } else {
2111 // dropped on an occupied sub-unit, so ask for swap confirmation
2112 if (!vboSwapRoomSubunits(bid.replaceAll('-', ''), landbids.replaceAll('-', ''), parent_rid, subunit_id, subdata[1], checkind)) {
2113 return false;
2114 }
2115 }
2116
2117 // do not proceed
2118 return;
2119 }
2120
2121 /* populate temporary class for the new cells */
2122 jQuery(cellspool).each(function(k, v) {
2123 v.addClass('vbo-dragging-cells-tmp');
2124 });
2125
2126 // bookings for multiple rooms should add the same dragging class also to the booking for the other rooms
2127 var samebidcells = jQuery("td.vbo-checkinday[data-bids='" + bid + "']");
2128 if (samebidcells.length > 1) {
2129 jQuery("td[data-bids='" + bid + "']").each(function(k, v) {
2130 if (!jQuery(v).hasClass('vbo-dragging-cells-tmp')) {
2131 jQuery(v).addClass('vbo-dragging-cells-tmp');
2132 }
2133 });
2134 }
2135 jQuery(newcellspool).each(function(k, v) {
2136 v.addClass('vbo-dragged-cells-tmp');
2137 });
2138
2139 /* populate and showing loading message */
2140 jQuery('.vbo-loading-dnd-head').text(Joomla.JText._('VBOVWDNDMOVINGBID').replace('%d', bid));
2141 var movingmess = '';
2142 if (roomid != landrid) {
2143 movingmess += Joomla.JText._('VBOVWDNDMOVINGROOM').replace('%s', jQuery("td[data-roomid='"+landrid+"']").first().find('span.vbo-overview-roomname').text() );
2144 }
2145 if (nowdatefrom != jQuery(cellspool[0]).attr('data-day') || nowdateto != jQuery(cellspool[(cellspool.length -1)]).attr('data-day')) {
2146 if (roomid != landrid) {
2147 movingmess += ', ';
2148 }
2149 movingmess += Joomla.JText._('VBOVWDNDMOVINGDATES').replace('%s', nowdatefrom + ' - ' + nowdateto);
2150 }
2151
2152 // hide tooltip, if any
2153 hideVboTooltip();
2154
2155 // display moving message
2156 jQuery('.vbo-loading-dnd-body').text(movingmess);
2157 jQuery('.vbo-info-overlay-block').fadeIn();
2158 jQuery('.vbo-loading-dnd-footer').show();
2159
2160 /* fire the Ajax request after 1.5 seconds just for giving visibility to the loading message */
2161 setTimeout(function() {
2162 doAlterBooking(bid, roomid, landrid);
2163 }, 1500);
2164 });
2165
2166 /* DnD Event dragend: remove class, attribute and dataTransfer Data if drag ends on the same position as dragstart or onto an invalid date */
2167 jQuery(document.body).on("dragend", "td.vbo-checkinday span, td.vbo-checkinday .vbo-tableaux-booking-checkin", function(e) {
2168 // stop dragging to restore tooltip functions
2169 isdragging = false;
2170 // we could also access the originally dragged cell
2171 var dt = e.originalEvent.dataTransfer;
2172 // restore temporary classes
2173 jQuery('.vbo-dragging-sp').removeClass('vbo-dragging-sp');
2174 jQuery('.vbo-droppable-cell').removeClass('vbo-droppable-cell');
2175 });
2176 /* DnD End */
2177
2178 /* Show New Reservation Form & Modal with bookings - Start */
2179 jQuery('td.busy, td.notbusy').dblclick(function() {
2180 var curday = jQuery(this).attr('data-day');
2181 var roomid = jQuery(this).parent('tr').find('td.roomname').attr('data-roomid');
2182 roomid = roomid && roomid.length ? roomid : '';
2183 last_room_click = '';
2184 last_date_click = '';
2185 next_date_click = '';
2186 vboCallOpenJModal('vbo-new-res', 'index.php?option=com_vikbooking&task=calendar&cid[]='+roomid+'&checkin='+curday+'&overv=1&tmpl=component');
2187 });
2188
2189 jQuery(document.body).on('click', 'td.busy, td.notbusy, td.busytmplock, td.subroom-busy', function(e) {
2190 if (jQuery(this).hasClass('vbo-widget-booskcal-cell-mday')) {
2191 return;
2192 }
2193
2194 if (jQuery(this).hasClass('busy') || jQuery(this).hasClass('busytmplock') || jQuery(this).hasClass('subroom-busy')) {
2195 // make sure the clicked target is not inside the tooltip
2196 if (e && e.target) {
2197 if (jQuery(e.target).closest('.vbo-overview-tipblock').length) {
2198 // abort when click originated from the tooltip
2199 return;
2200 }
2201 if (jQuery(e.target).is('a')) {
2202 // abort when click is made on a link (View choosebusy)
2203 return;
2204 }
2205 if (jQuery(e.target).hasClass('vbo-roomdaynote-display') || jQuery(e.target).hasClass('vbo-roomdaynote-trigger')) {
2206 // abort when click is made on a room-day note
2207 return;
2208 }
2209 }
2210
2211 // trigger mouseleave event to prevent tooltip from showing
2212 jQuery(this).trigger('mouseleave');
2213
2214 // get room name, date and day-bids
2215 var room_name = '';
2216 var sub_room_data = '';
2217 var room_id = 0;
2218 if (jQuery(this).hasClass('subroom-busy')) {
2219 sub_room_data = jQuery(this).parent('tr').attr('data-subroomid');
2220 room_id = sub_room_data.split('-')[0];
2221 room_name = jQuery('td.roomname[data-roomid="' + room_id + '"]').not('.subroomname').first().find('.vbo-overview-roomname').text();
2222 } else {
2223 var main_room_cell = jQuery(this).parent('tr').find('td.roomname');
2224 room_id = main_room_cell.attr('data-roomid');
2225 room_name = main_room_cell.find('.vbo-overview-roomname').text();
2226 }
2227 var date_ymd = jQuery(this).attr('data-day');
2228 var date_read = jQuery('.bluedays[data-ymd="' + date_ymd + '"]').attr('data-readymd');
2229 var date_bids = jQuery(this).attr('data-bids');
2230 var def_bicon = '<?php VikBookingIcons::e('user', 'vbo-dashboard-guest-activity-avatar-icon'); ?>';
2231 var closure_i = '<?php VikBookingIcons::e('ban', 'vbo-dashboard-guest-activity-avatar-icon'); ?>';
2232
2233 // display modal with booking details
2234 var rday_bookings_modal_body = VBOCore.displayModal({
2235 suffix: 'overv-rdaybookings',
2236 extra_class: 'vbo-modal-rounded vbo-modal-tall vbo-modal-nofooter',
2237 title: room_name + (sub_room_data ? ' #' + sub_room_data.split('-')[1] : '') + ' - ' + date_read,
2238 draggable: true,
2239 dismiss_event: 'vbo-dismiss-modal-overv-rdaybookings',
2240 loading_event: 'vbo-loading-modal-overv-rdaybookings',
2241 loading_body: '<?php VikBookingIcons::e('circle-notch', 'fa-spin fa-fw'); ?>',
2242 });
2243
2244 // show loading
2245 VBOCore.emitEvent('vbo-loading-modal-overv-rdaybookings');
2246
2247 // make the request to get the bookings information
2248 VBOCore.doAjax(
2249 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=getbookingsinfo'); ?>",
2250 {
2251 status: 'any',
2252 idroom: room_id,
2253 stay_date: date_ymd,
2254 idorders: date_bids,
2255 subroom: sub_room_data,
2256 sharedcal: (jQuery(this).hasClass('busy-sharedcalendar') ? 1 : 0),
2257 tmpl: 'component'
2258 },
2259 (res) => {
2260 // stop loading
2261 VBOCore.emitEvent('vbo-loading-modal-overv-rdaybookings');
2262 try {
2263 var obj_res = typeof res === 'string' ? JSON.parse(res) : res;
2264
2265 // build the HTML response nodes
2266 var rday_bookings_wrap = jQuery('<div></div>').addClass('vbo-dashboard-guests-latest');
2267 var rday_bookings_list = jQuery('<div></div>').addClass('vbo-dashboard-guest-messages-list');
2268
2269 // flag to indicate if the button to toggle the cancelled reservations was displayed
2270 var show_canc_res_flag = false;
2271
2272 // loop through all bookings
2273 for (var b in obj_res) {
2274 if (!obj_res.hasOwnProperty(b)) {
2275 continue;
2276 }
2277 // nights and guests
2278 var nights_guests = [
2279 obj_res[b]['roomsnum'] + ' ' + Joomla.JText._((obj_res[b]['roomsnum'] > 1 ? 'VBPVIEWORDERSTHREE' : 'VBEDITORDERTHREE')),
2280 obj_res[b]['days'] + ' ' + Joomla.JText._((obj_res[b]['days'] > 1 ? 'VBDAYS' : 'VBDAY')),
2281 obj_res[b]['tot_adults'] + ' ' + Joomla.JText._((obj_res[b]['tot_adults'] > 1 ? 'VBMAILADULTS' : 'VBMAILADULT'))
2282 ];
2283 if (obj_res[b]['tot_children'] > 0) {
2284 nights_guests.push(obj_res[b]['tot_children'] + ' ' + Joomla.JText._((obj_res[b]['tot_children'] > 1 ? 'VBMAILCHILDREN' : 'VBMAILCHILD')));
2285 }
2286
2287 // OTA booking ID
2288 var ota_bid_info = '';
2289 if (obj_res[b].hasOwnProperty('idorderota') && obj_res[b].hasOwnProperty('channel') && obj_res[b]['idorderota'] && obj_res[b]['channel']) {
2290 if (obj_res[b]['idorderota'].length <= 16) {
2291 // try to display the information only for API channels
2292 ota_bid_info = '<span class="label label-info">' + obj_res[b]['idorderota'] + '</span> ';
2293 }
2294 }
2295
2296 // the separator between confirmed and cancelled reservations
2297 var res_type_separator = null;
2298
2299 // booking status and badge
2300 var badge_type = 'warning';
2301 if (obj_res[b]['status'] == 'confirmed') {
2302 badge_type = 'success';
2303 } else if (obj_res[b]['status'] == 'cancelled') {
2304 badge_type = 'danger';
2305 if (!show_canc_res_flag) {
2306 // build the separator between confirmed and cancelled reservations to toggle the latter ones
2307 res_type_separator = jQuery('<div></div>').addClass('vbo-bookings-status-separator');
2308 var btn_separator = jQuery('<button></button>').attr('type', 'button').addClass('btn btn-small btn-secondary').text(Joomla.JText._('VBO_SHOW_CANCELLATIONS'));
2309 btn_separator.on('click', function() {
2310 jQuery(this).closest('.vbo-dashboard-guest-messages-list').find('[data-type="cancelled"]').toggle();
2311 });
2312 res_type_separator.append(btn_separator);
2313 // turn flag on at the very first cancelled reservation found
2314 show_canc_res_flag = true;
2315 }
2316 }
2317
2318 // build main booking node
2319 var rday_booking = jQuery('<div></div>').addClass('vbo-dashboard-guest-activity vbo-w-guestmessages-message');
2320 if (badge_type == 'danger') {
2321 // hide cancelled booking by default and set proper attribute
2322 rday_booking.attr('data-type', 'cancelled').css('display', 'none');
2323 }
2324 rday_booking.attr('data-idorder', obj_res[b]['id']);
2325 rday_booking.on('click', function(e) {
2326 var click_target = jQuery(e.target);
2327 if (click_target.is('a') || click_target.is('select') || click_target.is('option')) {
2328 return false;
2329 }
2330 if ((click_target.is('span') && click_target.hasClass('label')) || click_target.is('img') || click_target.is('i')) {
2331 // open booking details within the apposite admin-widget
2332 e.stopPropagation();
2333 VBOCore.handleDisplayWidgetNotification({
2334 widget_id: 'booking_details',
2335 }, {
2336 booking_id: jQuery(this).attr('data-idorder'),
2337 });
2338 } else {
2339 // open booking details on a new tab
2340 vboOvervOpenBooking(jQuery(this).attr('data-idorder'));
2341 }
2342 });
2343
2344 // build booking structure
2345 var rday_booking_html = '';
2346 rday_booking_html += '<div class="vbo-dashboard-guest-activity-avatar">' + "\n";
2347 if (obj_res[b]['avatar_src']) {
2348 rday_booking_html += '<img class="vbo-dashboard-guest-activity-avatar-profile" src="' + obj_res[b]['avatar_src'] + '" alt="' + obj_res[b]['avatar_alt'] + '" />' + "\n";
2349 } else if (obj_res[b]['closure']) {
2350 rday_booking_html += closure_i + "\n";
2351 } else {
2352 rday_booking_html += def_bicon + "\n";
2353 }
2354 rday_booking_html += '</div>' + "\n";
2355 rday_booking_html += '<div class="vbo-dashboard-guest-activity-content">' + "\n";
2356 rday_booking_html += ' <div class="vbo-dashboard-guest-activity-content-head">' + "\n";
2357 rday_booking_html += ' <div class="vbo-dashboard-guest-activity-content-info-details">' + "\n";
2358 rday_booking_html += ' <h4 class="vbo-w-guestmessages-message-gtitle">' + (!obj_res[b]['closure'] ? obj_res[b]['cinfo'] : obj_res[b]['closure_txt']) + '</h4>' + "\n";
2359 rday_booking_html += ' <div class="vbo-dashboard-guest-activity-content-info-icon">' + "\n";
2360 rday_booking_html += ' <span class="badge badge-info">' + obj_res[b]['id'] + '</span> ' + "\n";
2361 if (obj_res[b]['type'] && obj_res[b]['type'] == 'overbooking') {
2362 rday_booking_html += ' <span class="label label-error vbo-label-overbooking">' + Joomla.JText._('VBO_BTYPE_OVERBOOKING') + '</span>' + "\n";
2363 }
2364 rday_booking_html += ' <span class="badge badge-' + badge_type + '">' + obj_res[b]['status_lbl'] + '</span>' + "\n";
2365 rday_booking_html += ' <span class="vbo-w-guestmessages-message-staydates">' + "\n";
2366 rday_booking_html += ' <?php VikBookingIcons::e('calendar-alt'); ?>' + "\n";
2367 rday_booking_html += ' <span class="vbo-w-guestmessages-message-staydates-in">' + obj_res[b]['checkin_short'] + '</span>' + "\n";
2368 rday_booking_html += ' <span class="vbo-w-guestmessages-message-staydates-sep">-</span>' + "\n";
2369 rday_booking_html += ' <span class="vbo-w-guestmessages-message-staydates-out">' + obj_res[b]['checkout_short'] + '</span>' + "\n";
2370 rday_booking_html += ' </span>' + "\n";
2371 rday_booking_html += ' </div>' + "\n";
2372 rday_booking_html += ' </div>' + "\n";
2373 rday_booking_html += ' <div class="vbo-dashboard-guest-activity-content-info-date">' + "\n";
2374 rday_booking_html += ' <span>' + obj_res[b]['book_date'] + '</span>' + "\n";
2375 rday_booking_html += ' <span>' + obj_res[b]['book_time'] + '</span>' + "\n";
2376 if (obj_res[b].hasOwnProperty('meals_included')) {
2377 rday_booking_html += ' <div class="vbo-wider-badges-wrap">' + "\n";
2378 obj_res[b]['meals_included'].forEach((meal_enum) => {
2379 rday_booking_html += ' <div class="badge badge-info">' + meal_enum + '</div>' + "\n";
2380 });
2381 rday_booking_html += ' </div>' + "\n";
2382 }
2383 rday_booking_html += ' </div>' + "\n";
2384 rday_booking_html += ' </div>' + "\n";
2385 rday_booking_html += ' <div class="vbo-dashboard-guest-activity-content-info-msg">' + "\n";
2386 rday_booking_html += ' <div>' + ota_bid_info + '<?php VikBookingIcons::e('bed'); ?> ' + nights_guests.join(', ') + '</div>' + "\n";
2387 if (obj_res[b].hasOwnProperty('sub_units_data')) {
2388 rday_booking_html += ' <div class="vbo-rdaybooking-subunits">' + "\n";
2389 for (let sub_rname in obj_res[b]['sub_units_data']) {
2390 if (!obj_res[b]['sub_units_data'].hasOwnProperty(sub_rname)) {
2391 continue;
2392 }
2393 rday_booking_html += '<span class="label label-success">' + (obj_res[b]['roomsnum'] > 1 ? sub_rname + ' ' : '') + '#' + obj_res[b]['sub_units_data'][sub_rname] + '</span>' + "\n";
2394 }
2395 rday_booking_html += ' </div>' + "\n";
2396 } else if (obj_res[b].hasOwnProperty('missing_index') && obj_res[b]['missing_index']) {
2397 rday_booking_html += ' <div class="vbo-rdaybooking-subunits">' + "\n";
2398 rday_booking_html += ' <span class="label label-warning">' + Joomla.JText._('VBO_MISSING_SUBUNIT') + '</span>' + "\n";
2399 if (obj_res[b].hasOwnProperty('av_room_indexes')) {
2400 // build drop downs to set the sub-unit to each room with no data
2401 rday_booking_html += ' <div class="vbo-rdaybooking-subunits-list">' + "\n";
2402 for (var av_rindex in obj_res[b]['av_room_indexes']) {
2403 if (!obj_res[b]['av_room_indexes'].hasOwnProperty(av_rindex) || !obj_res[b]['av_room_indexes'][av_rindex].hasOwnProperty('name') || !obj_res[b]['av_room_indexes'][av_rindex].hasOwnProperty('list')) {
2404 continue;
2405 }
2406 var set_subunit_node = '<select onchange="vboSetSubunit(\'' + obj_res[b]['id'] + '\', \'' + obj_res[b]['av_room_indexes'][av_rindex]['rid'] + '\', \'' + av_rindex + '\', this.value);">' + "\n";
2407 set_subunit_node += '<option value="">' + obj_res[b]['av_room_indexes'][av_rindex]['name'] + '</option>' + "\n";
2408 for (var rindex_k in obj_res[b]['av_room_indexes'][av_rindex]['list']) {
2409 if (!obj_res[b]['av_room_indexes'][av_rindex]['list'].hasOwnProperty(rindex_k)) {
2410 continue;
2411 }
2412 set_subunit_node += '<option value="' + rindex_k + '">' + obj_res[b]['av_room_indexes'][av_rindex]['list'][rindex_k] + '</option>' + "\n";
2413 }
2414 set_subunit_node += '</select>' + "\n";
2415 // append drop down
2416 rday_booking_html += set_subunit_node;
2417 }
2418 rday_booking_html += ' </div>' + "\n";
2419 }
2420 rday_booking_html += ' </div>' + "\n";
2421 }
2422 rday_booking_html += ' </div>' + "\n";
2423 rday_booking_html += '</div>' + "\n";
2424
2425 // set booking HTML to node
2426 rday_booking.append(rday_booking_html);
2427
2428 if (res_type_separator !== null) {
2429 // append the separator to toggle the cancelled reservations
2430 rday_bookings_list.append(res_type_separator);
2431 }
2432
2433 // append booking node to list
2434 rday_bookings_list.append(rday_booking);
2435 }
2436
2437 // finalize response nodes
2438 rday_bookings_wrap.append(rday_bookings_list);
2439
2440 // append the response
2441 rday_bookings_modal_body.append(rday_bookings_wrap);
2442 } catch(err) {
2443 console.error('could not parse JSON response', err, res);
2444 alert('Could not parse JSON response');
2445 }
2446 },
2447 (err) => {
2448 // stop loading and display alert message
2449 VBOCore.emitEvent('vbo-loading-modal-overv-rdaybookings');
2450 console.error(err);
2451 alert(err.responseText);
2452 }
2453 );
2454 }
2455
2456 if (jQuery(this).hasClass('busy') && !jQuery(this).hasClass('vbo-partially')) {
2457 // abort when fully booked date
2458 return;
2459 }
2460
2461 if (jQuery(this).hasClass('busytmplock') || jQuery(this).hasClass('subroom-busy')) {
2462 // abort when pending record or sub-room
2463 return;
2464 }
2465
2466 // update clicked dates info
2467 var curday = jQuery(this).attr('data-day');
2468 var roomid = jQuery(this).parent('tr').find('td.roomname').attr('data-roomid');
2469 roomid = roomid && roomid.length ? roomid : '';
2470 last_room_click = roomid;
2471 if (!last_date_click) {
2472 last_date_click = curday;
2473 } else if (last_date_click && next_date_click) {
2474 last_date_click = curday;
2475 next_date_click = '';
2476 } else {
2477 var from_info = new Date(last_date_click);
2478 var to_info = new Date(curday);
2479 if (from_info.getTime() < to_info.getTime()) {
2480 next_date_click = curday;
2481 } else {
2482 last_date_click = curday;
2483 next_date_click = '';
2484 }
2485 }
2486
2487 // button shake effect
2488 jQuery('.vbo-ovrv-flt-butn').addClass('vbo-ovrv-flt-butn-shake');
2489 setTimeout(function() {
2490 jQuery('.vbo-ovrv-flt-butn').removeClass('vbo-ovrv-flt-butn-shake');
2491 }, 1000);
2492 });
2493 /* Show New Reservation Form & Modal with bookings - End */
2494
2495 /* Hover Tooltip Start */
2496 jQuery('td.busy, td.busytmplock, td.subroom-busy').not('.vbo-widget-booskcal-cell-mday').hover(function() {
2497 registerHoveringTooltip(this);
2498 }, unregisterHoveringTooltip);
2499
2500 jQuery(document.body).on('click', '.vbo-overview-tip-bctag', function() {
2501 if (!jQuery(this).parent().find(".vbo-overview-tip-bctag-subtip").length) {
2502 jQuery(".vbo-overview-tip-bctag-subtip").remove();
2503 var cur_color = jQuery(this).attr("data-ctagcolor");
2504 var cur_bid = jQuery(this).attr("data-bid");
2505 jQuery(this).after("<div class=\"vbo-overview-tip-bctag-subtip\">"+bctags_tip+"</div>");
2506 jQuery(this).parent().find(".vbo-overview-tip-bctag-subtip").find(".vbo-overview-tip-bctag-subtip-circle[data-ctagcolor='"+cur_color+"']").addClass("vbo-overview-tip-bctag-activecircle").css('border-color', cur_color);
2507 jQuery(this).parent().find(".vbo-overview-tip-bctag-subtip").find(".vbo-overview-tip-bctag-subtip-circle").attr('data-bid', cur_bid);
2508 jQuery(".vbo-overview-tip-bctag-subtip .hasTooltip").tooltip();
2509 } else {
2510 jQuery(".vbo-overview-tip-bctag-subtip").remove();
2511 }
2512 });
2513
2514 var applying_tag = false;
2515
2516 jQuery(document.body).on('click', '.vbo-overview-tip-bctag-subtip-circle', function() {
2517 if (applying_tag === true) {
2518 return false;
2519 }
2520 applying_tag = true;
2521 var clickelem = jQuery(this);
2522 var ctagkey = clickelem.attr('data-ctagkey');
2523 var bid = clickelem.attr('data-bid');
2524 // set opacity to circles as loading
2525 jQuery('.vbo-overview-tip-bctag-subtip-circle').css('opacity', '0.6');
2526
2527 // perform the request
2528 VBOCore.doAjax(
2529 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=setbookingtag'); ?>",
2530 {
2531 tmpl: "component",
2532 idorder: bid,
2533 tagkey: ctagkey
2534 },
2535 (res) => {
2536 applying_tag = false;
2537 if (res.indexOf('e4j.error') >= 0 ) {
2538 // console.log(res);
2539 alert(res.replace("e4j.error.", ""));
2540 //restore loading opacity in circles
2541 jQuery('.vbo-overview-tip-bctag-subtip-circle').css('opacity', '1');
2542 } else {
2543 var obj_res = typeof res === 'string' ? JSON.parse(res) : res;
2544 var last_bid = jQuery(clickelem).closest(".vbo-hascolortag").attr('data-lastbid');
2545 <?php if ($pbmode == 'tags') { echo 'jQuery(".vbo-hascolortag[data-lastbid=\'"+last_bid+"\']").each(function(k, v) { jQuery(this).css("background-color", obj_res.color).find("a").first().css("color", obj_res.fontcolor); });'; } ?>
2546 hideVboTooltip();
2547 }
2548 },
2549 (err) => {
2550 applying_tag = false;
2551 alert("Request Failed");
2552 // restore loading opacity in circles
2553 jQuery('.vbo-overview-tip-bctag-subtip-circle').css('opacity', '1');
2554 }
2555 );
2556 });
2557
2558 jQuery(document).keydown(function(e) {
2559 if ( e.keyCode == 27 ) {
2560 if (hovtip === true) {
2561 hideVboTooltip();
2562 }
2563 if (vbodialogorph_on === true) {
2564 hideVboDialogOverv(1);
2565 }
2566 }
2567 });
2568
2569 jQuery(document).mouseup(function(e) {
2570 if (!hovtip && !vbodialogorph_on) {
2571 return false;
2572 }
2573 if (hovtip) {
2574 var vbo_overlay_cont = jQuery(".vbo-overview-tipblock");
2575 if (!vbo_overlay_cont.is(e.target) && vbo_overlay_cont.has(e.target).length === 0) {
2576 hideVboTooltip();
2577 return true;
2578 }
2579 if (jQuery(".vbo-overview-tip-bctag-subtip").length) {
2580 var vbo_overlay_subtip_cont = jQuery(".vbo-overview-tip-bctag-wrap");
2581 if (!vbo_overlay_subtip_cont.is(e.target) && vbo_overlay_subtip_cont.has(e.target).length === 0) {
2582 jQuery(".vbo-overview-tip-bctag-subtip").remove();
2583 return true;
2584 }
2585 }
2586 }
2587 if (vbodialogorph_on) {
2588 var vbo_overlay_cont = jQuery(".vbo-info-overlay-content");
2589 if (!vbo_overlay_cont.is(e.target) && vbo_overlay_cont.has(e.target).length === 0) {
2590 hideVboDialogOverv(1);
2591 }
2592 }
2593 });
2594 /* Hover Tooltip End */
2595
2596 /* Toggle Sub-units */
2597 jQuery('.vbo-overview-subroom-toggle').click(function() {
2598 var roomid = jQuery(this).closest('td').attr('data-roomid');
2599 if (jQuery(this).hasClass('vbo-overview-subroom-toggle-active')) {
2600 jQuery("td.roomname[data-roomid='" + roomid + "']").find('span.vbo-overview-subroom-toggle').removeClass('vbo-overview-subroom-toggle-active').find('i').removeClass('fa-chevron-up').addClass('fa-chevron-down');
2601 // do not use .hide() or "display: none" may not work due to forced "display: table-row"
2602 jQuery("td.subroomname[data-roomid='-" + roomid + "']").parent('tr').css('display', 'none');
2603 // update storage object
2604 var storage_subunits_status = VBOCore.storageGetItem('vbo_overv_subunits_status');
2605 try {
2606 storage_subunits_status = JSON.parse(storage_subunits_status);
2607 storage_subunits_status = storage_subunits_status || {};
2608 } catch(e) {
2609 storage_subunits_status = {};
2610 }
2611 storage_subunits_status[roomid] = 0;
2612 VBOCore.storageSetItem('vbo_overv_subunits_status', storage_subunits_status);
2613 } else {
2614 jQuery("td.roomname[data-roomid='" + roomid + "']").find('span.vbo-overview-subroom-toggle').addClass('vbo-overview-subroom-toggle-active').find('i').removeClass('fa-chevron-down').addClass('fa-chevron-up');
2615 // do not use .show() or "display: block" may be added rather than "display: table-row"
2616 jQuery("td.subroomname[data-roomid='-" + roomid + "']").parent('tr').css('display', 'table-row');
2617 // update storage object
2618 var storage_subunits_status = VBOCore.storageGetItem('vbo_overv_subunits_status');
2619 try {
2620 storage_subunits_status = JSON.parse(storage_subunits_status);
2621 storage_subunits_status = storage_subunits_status || {};
2622 } catch(e) {
2623 storage_subunits_status = {};
2624 }
2625 storage_subunits_status[roomid] = 1;
2626 VBOCore.storageSetItem('vbo_overv_subunits_status', storage_subunits_status);
2627 }
2628 });
2629
2630 /* Display sub-units based on storage status */
2631 var storage_subunits_defstatus = VBOCore.storageGetItem('vbo_overv_subunits_status');
2632 try {
2633 storage_subunits_defstatus = JSON.parse(storage_subunits_defstatus);
2634 storage_subunits_defstatus = storage_subunits_defstatus || {};
2635 for (let rid in storage_subunits_defstatus) {
2636 if (!storage_subunits_defstatus.hasOwnProperty(rid)) {
2637 continue;
2638 }
2639 if (storage_subunits_defstatus[rid]) {
2640 // trigger click event to display the sub-units
2641 jQuery('td.roomname[data-roomid="' + rid + '"]').find('.vbo-overview-subroom-toggle').trigger('click');
2642 }
2643 }
2644 } catch(e) {
2645 // do nothing
2646 }
2647
2648 /* Default state for sticky table heads */
2649 vboToggleStickyTableHeaders(<?php echo (count($rows) * $mnum) > 10 && $cookie_sticky_heads != 'off' ? 1 : 0; ?>);
2650
2651 // check orphans (only if not disabled with the cookie)
2652 var hideorphans = false;
2653 var buiscuits = document.cookie;
2654 if (buiscuits.length) {
2655 var hideorphansck = "vboHideOrphans=1";
2656 if (buiscuits.indexOf(hideorphansck) >= 0) {
2657 hideorphans = true;
2658 }
2659 }
2660 if (!hideorphans && <?php echo $vbo_auth_pricing ? 'true' : 'false'; ?>) {
2661 // make the request
2662
2663 VBOCore.doAjax(
2664 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=orphanscount'); ?>",
2665 {
2666 from: '<?php echo date($df, $tsstart); ?>',
2667 months: <?php echo $mnum; ?>,
2668 tmpl: "component"
2669 },
2670 (res) => {
2671 var obj_res = typeof res === 'string' ? JSON.parse(res) : res;
2672 var orphans_list = '';
2673 for (var rid in obj_res) {
2674 if (!obj_res.hasOwnProperty(rid)) {
2675 continue;
2676 }
2677 orphans_list += '<div class="vbo-orphans-info-room">';
2678 orphans_list += ' <h4 class="vbo-orphans-roomname">'+obj_res[rid]['name']+'</h4>';
2679 orphans_list += ' <div class="vbo-orphans-info-dates">';
2680 for (var dind in obj_res[rid]['rdates']) {
2681 if (!obj_res[rid]['rdates'].hasOwnProperty(dind)) {
2682 continue;
2683 }
2684 orphans_list += ' <div class="vbo-orphans-info-date">'+obj_res[rid]['rdates'][dind]+'</div>';
2685 }
2686 orphans_list += ' </div>';
2687 orphans_list += ' <div class="vbo-orphans-info-btn">';
2688 orphans_list += ' <a href="<?php echo $file_base; ?>?option=com_vikbooking&task=ratesoverv&cid[]='+rid+'&startdate='+obj_res[rid]['linkd']+'" class="btn btn-primary" target="_blank"><?php echo addslashes(JText::translate('VBORPHANSCHECKBTN')); ?></a>';
2689 orphans_list += ' </div>';
2690 orphans_list += '</div>';
2691 }
2692 if (orphans_list.length) {
2693 // show the modal
2694 jQuery('.vbo-orphans-info-list').html(orphans_list);
2695 jQuery('.vbo-orphans-overlay-block').fadeIn();
2696 vbodialogorph_on = true;
2697 }
2698 },
2699 (err) => {
2700 console.log("orphanscount Request Failed");
2701 }
2702 );
2703 }
2704 //
2705
2706 // fests
2707 jQuery(document.body).on("click", "th.bluedays", function() {
2708 if (jQuery(this).hasClass('skip-bluedays-click')) {
2709 return;
2710 }
2711 var ymd = jQuery(this).attr('data-ymd');
2712 var daytitle = jQuery(this).attr('data-readymd');
2713 if (jQuery(this).hasClass('vbo-overv-festcell')) {
2714 // cell has fests
2715 if (!vboFests.hasOwnProperty(ymd)) {
2716 return;
2717 }
2718 vboRenderFests(ymd, daytitle);
2719 } else {
2720 // let the admin create a new fest
2721
2722 // update ymd key for the selected date, useful for adding new fests
2723 jQuery('.vbo-overlay-fests-addnew').attr('data-ymd', ymd);
2724
2725 // unset content and display modal for just adding a new fest
2726 jQuery('.vbo-overlay-fests-list').html('');
2727 var fests_modal = VBOCore.displayModal({
2728 suffix: 'overv-mng-fests',
2729 extra_class: 'vbo-modal-rounded vbo-modal-tall',
2730 title: '<?php VikBookingIcons::e('star'); ?> <span>' + daytitle + '</span>',
2731 footer_right: '<button type="button" class="btn btn-success" onclick="vboAddFest();">' + Joomla.JText._('VBSAVE') + '</button>',
2732 dismiss_event: 'vbo-dismiss-modal-overv-mng-fests',
2733 onDismiss: () => {
2734 jQuery('.vbo-overv-mngfest-wrap').appendTo('.vbo-overv-mngfest-block');
2735 },
2736 });
2737
2738 // set modal body
2739 jQuery('.vbo-overv-mngfest-wrap').appendTo(fests_modal);
2740 }
2741 });
2742 //
2743
2744 // room-day notes
2745 jQuery(document.body).on("click", ".vbo-roomdaynote-display", function() {
2746 if (!jQuery(this).closest('.vbo-roomdaynote-trigger').length) {
2747 return;
2748 }
2749 var daytitle = new Array;
2750 var roomday_info = jQuery(this).closest('.vbo-roomdaynote-trigger').attr('data-roomday').split('_');
2751 // readable day
2752 var readymd = roomday_info[0];
2753 if (jQuery('.bluedays[data-ymd="' + roomday_info[0] + '"]').length) {
2754 readymd = jQuery('.bluedays[data-ymd="' + roomday_info[0] + '"]').attr('data-readymd');
2755 }
2756 daytitle.push(readymd);
2757 // room name
2758 if (jQuery('.roomname[data-roomid="' + roomday_info[1] + '"]').length) {
2759 daytitle.push(jQuery('.roomname[data-roomid="' + roomday_info[1] + '"]').first().find('.vbo-overview-roomname').text());
2760 }
2761 // sub-unit
2762 if (parseInt(roomday_info[2]) > 0 && jQuery('.subroomname[data-roomid="-' + roomday_info[1] + '"]').length) {
2763 daytitle.push(jQuery('.subroomname[data-roomid="-' + roomday_info[1] + '"]').find('.vbo-overview-subroomname').eq((parseInt(roomday_info[2]) - 1)).text());
2764 }
2765
2766 // display modal
2767 var rdaynotes_modal = VBOCore.displayModal({
2768 suffix: 'overv-mng-rdaynotes',
2769 extra_class: 'vbo-modal-rounded vbo-modal-tall',
2770 title: '<?php VikBookingIcons::e('comment'); ?> <span>' + daytitle.join(', ') + '</span>',
2771 footer_right: '<button type="button" class="btn btn-success" onclick="vboAddRoomDayNote();">' + Joomla.JText._('VBSAVE') + '</button>',
2772 dismiss_event: 'vbo-dismiss-modal-overv-mng-rdaynotes',
2773 onDismiss: () => {
2774 jQuery('.vbo-overv-mngroomdaynotes-wrap').appendTo('.vbo-overv-mngroomdaynotes-block');
2775 },
2776 });
2777
2778 // set modal body
2779 jQuery('.vbo-overv-mngroomdaynotes-wrap').appendTo(rdaynotes_modal);
2780
2781 // populate current room day notes
2782 vboRenderRdayNotes(roomday_info[0], roomday_info[1], roomday_info[2], readymd);
2783 });
2784 //
2785
2786 <?php
2787 /**
2788 * Handle fixed table heads when on snake-layout (scroll), which is completely
2789 * different than the table-layout sticky table headers (handled through jQuery).
2790 *
2791 * @since 1.16.9 (J) - 1.6.9 (WP)
2792 */
2793 if ($cookie_sticky_heads == 'off') {
2794 ?>
2795 // listen to the document scroll event to handle the fixed table heads
2796 document.addEventListener('scroll', vboOvervHandleVScrollTableHeads);
2797
2798 // scan all tables for listening to their horizontal scrolling event
2799 let tables = document.querySelectorAll('table.vboverviewtable.vbo-overv-sticky-table-head-off');
2800 tables.forEach((table) => {
2801 table.parentNode.addEventListener('scroll', vboOvervHandleHScrollTableHead);
2802 });
2803 <?php
2804 }
2805 ?>
2806 });
2807
2808 /**
2809 * Document vertical scroll event handler for the fixed table heads.
2810 *
2811 * @param object e the document scroll event object.
2812 *
2813 * @return void
2814 *
2815 * @since 1.16.9 (J) - 1.6.9 (WP)
2816 */
2817 function vboOvervHandleVScrollTableHeads(e) {
2818 // register throttling callback
2819 VBOCore.throttleTimer(() => {
2820 // get all eligible tables
2821 let tables = document.querySelectorAll('table.vboverviewtable.vbo-overv-sticky-table-head-off');
2822 if (!tables.length) {
2823 // un-register event
2824 document.removeEventListener('scroll', vboOvervHandleVScrollTableHeads);
2825 return;
2826 }
2827
2828 // pool of fixed table heads
2829 let fixedTableHeadNodes = vboOvervBuildFixedTableHeads(tables);
2830
2831 if (fixedTableHeadNodes.length) {
2832 // append to body just the last table head node for the bottom farthest table
2833 document.body.appendChild(fixedTableHeadNodes[fixedTableHeadNodes.length - 1]);
2834 }
2835 }, 400);
2836 }
2837
2838 /**
2839 * Table-element horizontal scroll event handler for updating the fixed table heads.
2840 *
2841 * @param object e the table-element scroll event object.
2842 *
2843 * @return void
2844 *
2845 * @since 1.16.9 (J) - 1.6.9 (WP)
2846 */
2847 function vboOvervHandleHScrollTableHead(e) {
2848 // register throttling callback
2849 VBOCore.throttleTimer(() => {
2850 // horizontal scrolling happens on the parent DIV node
2851 let tables = e.target.querySelectorAll('table.vboverviewtable.vbo-overv-sticky-table-head-off');
2852 if (tables.length !== 1) {
2853 // un-register event for missing table
2854 e.target.removeEventListener('scroll', vboOvervHandleHScrollTableHead);
2855 return;
2856 }
2857
2858 // pool of fixed table heads
2859 let fixedTableHeadNodes = vboOvervBuildFixedTableHeads(tables);
2860
2861 if (fixedTableHeadNodes.length) {
2862 // append to body the first and only table head node expected
2863 document.body.appendChild(fixedTableHeadNodes[0]);
2864 }
2865 }, 200);
2866 }
2867
2868 /**
2869 * Builds the list of element nodes for the fixed table heads.
2870 *
2871 * @param object tables The table elements to parse.
2872 *
2873 * @return object[]
2874 *
2875 * @since 1.16.9 (J) - 1.6.9 (WP)
2876 */
2877 function vboOvervBuildFixedTableHeads(tables) {
2878 // pool of fixed table heads
2879 let fixedTableHeadNodes = [];
2880
2881 // get document body scroll top value
2882 let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
2883
2884 // platform fixed offset top
2885 let platformAdminBar = <?php echo VBOPlatformDetection::isWordPress() ? 'document.querySelector("#wpadminbar")' : '(document.querySelector(".navbar") || document.querySelector("#subhead-container"))'; ?>;
2886 let fixedOffsetTop = platformAdminBar ? platformAdminBar.offsetHeight : 0;
2887 if (fixedOffsetTop) {
2888 // ensure the responsive mode has got a fixed/absolute positioning
2889 let adminBarPosition = platformAdminBar.style.position || '';
2890 if (adminBarPosition != 'fixed' && adminBarPosition != 'absolute') {
2891 // no fixed admin bar
2892 fixedOffsetTop = 0;
2893 }
2894 }
2895
2896 // scan all table elements
2897 (tables || []).forEach((table, index) => {
2898 // always check for existing fixed table head element
2899 let scrollingHeadElement = document.querySelector('.vbo-overv-sticky-scroll-month[data-table-index="' + index + '"]');
2900 if (scrollingHeadElement) {
2901 // remove fixed table head element from DOM, will be reconstructed if needed
2902 scrollingHeadElement.remove();
2903 }
2904
2905 // get table head element node
2906 let tableHeadNode = table.querySelector('tr.vboverviewtablerowone');
2907
2908 // get position of current table head
2909 let tableHeadHeight = tableHeadNode.offsetHeight;
2910
2911 // make sure the table head is NOT visible, hence requires a fixed head
2912 if (scrollTop > (table.offsetTop + tableHeadHeight - (fixedOffsetTop / 2))) {
2913 if (scrollTop > (table.offsetTop + table.offsetHeight)) {
2914 // scrolling went even below the table
2915 return;
2916 }
2917
2918 // build main element node
2919 let fixedTableHead = document.createElement('div');
2920 fixedTableHead.className = 'vbo-overv-sticky-scroll-month';
2921 fixedTableHead.style.top = fixedOffsetTop + 'px';
2922 fixedTableHead.style.left = table.parentNode.getBoundingClientRect().x + 'px';
2923 fixedTableHead.setAttribute('data-table-index', index);
2924
2925 // inner element node
2926 let fixedTableHeadWrap = document.createElement('div');
2927 fixedTableHeadWrap.className = 'vbo-overv-sticky-scroll-month-wrap';
2928
2929 // month-name element node
2930 let tableHeadMonth = tableHeadNode.querySelector('.vbo-overview-month');
2931 let fixedTableHeadMonth = document.createElement('div');
2932 fixedTableHeadMonth.className = 'vbo-overv-sticky-scroll-month-m';
2933 fixedTableHeadMonth.style.width = tableHeadMonth.offsetWidth + 'px';
2934 fixedTableHeadMonth.appendChild(
2935 document.createTextNode(tableHeadMonth.innerText)
2936 );
2937 fixedTableHeadWrap.appendChild(fixedTableHeadMonth);
2938
2939 // obtain the list of visible month-day cells
2940 vboOvervVisibleTableHeadNodes(table, index).forEach((mday) => {
2941 // month-day cell element node
2942 let fixedTableHeadMday = document.createElement('div');
2943 fixedTableHeadMday.className = 'vbo-overv-sticky-scroll-month-d';
2944 fixedTableHeadMday.style.width = mday['vwidth'] + 'px';
2945 fixedTableHeadMday.addEventListener('click', () => {
2946 try {
2947 // use jQuery to trigger the click event, or plain JS won't work
2948 /*
2949 document
2950 .querySelector('table.vboverviewtable.vbo-overv-sticky-table-head-off[data-table-index="' + index + '"]')
2951 .querySelector('.bluedays[data-ymd="' + mday['ymd'] + '"]')
2952 .dispatchEvent(new Event('click'));
2953 */
2954 jQuery('table.vboverviewtable.vbo-overv-sticky-table-head-off[data-table-index="' + index + '"]')
2955 .find('.bluedays[data-ymd="' + mday['ymd'] + '"]')
2956 .trigger('click');
2957 } catch(e) {
2958 // do nothing
2959 }
2960 });
2961
2962 // week-day element
2963 let wdayEl = document.createElement('span');
2964 wdayEl.className = 'vbo-overv-sticky-scroll-month-day';
2965 wdayEl.appendChild(
2966 document.createTextNode(mday['wday'])
2967 );
2968 fixedTableHeadMday.appendChild(wdayEl);
2969
2970 // month-day element
2971 let mdayEl = document.createElement('span');
2972 mdayEl.className = 'vbo-overv-sticky-scroll-month-day-num';
2973 mdayEl.appendChild(
2974 document.createTextNode(mday['mday'])
2975 );
2976 fixedTableHeadMday.appendChild(mdayEl);
2977
2978 // append month-day cell element node
2979 fixedTableHeadWrap.appendChild(fixedTableHeadMday);
2980 });
2981
2982 // append wrapper
2983 fixedTableHead.appendChild(fixedTableHeadWrap);
2984
2985 // push the final element node to the pool
2986 fixedTableHeadNodes.push(fixedTableHead);
2987 }
2988 });
2989
2990 return fixedTableHeadNodes;
2991 }
2992
2993 /**
2994 * Builds a list of visible month-day cells in a table head.
2995 *
2996 * @param object table the NodeList element representing a table.
2997 * @param number index the NodeList element index we are parsing.
2998 *
2999 * @return object[]
3000 *
3001 * @since 1.16.9 (J) - 1.6.9 (WP)
3002 */
3003 function vboOvervVisibleTableHeadNodes(table, index) {
3004 // get the main table head element
3005 let tableHeadElement = table.querySelector('tr.vboverviewtablerowone');
3006
3007 // get month element of the given table
3008 let monthElement = tableHeadElement.querySelector('.vbo-overview-month');
3009 let monthName = monthElement.innerText;
3010 let monthLeft = monthElement.offsetLeft - 2;
3011 let monthWidth = monthElement.offsetWidth;
3012
3013 // get horizontal scroll left and table width from parent node
3014 let tableScrollLeft = table.parentNode.scrollLeft;
3015 let tableViewWidth = table.parentNode.offsetWidth;
3016
3017 // pool of visible month-day cells
3018 let visibleMonthDays = [];
3019
3020 // scan all visible month-day elements
3021 tableHeadElement.querySelectorAll('.bluedays:not(.vbo-overview-month)').forEach((cell) => {
3022 let monthDayLeft = cell.offsetLeft;
3023 let monthDayWidth = cell.offsetWidth;
3024
3025 // ensure cell is visible from left scroll
3026 if ((monthDayLeft + monthDayWidth) >= (monthLeft + monthWidth)) {
3027 // ensure cell is also visible from right scroll
3028 if ((monthLeft + tableViewWidth) > monthDayLeft) {
3029 // calculate cell exact visible width
3030 let cellViewWidth = monthDayWidth;
3031 if ((monthDayLeft + monthDayWidth) > (monthLeft + tableViewWidth)) {
3032 // cell is cut from right scroll, count visible width
3033 cellViewWidth = monthDayWidth - ((monthDayLeft + monthDayWidth) - (tableViewWidth + monthLeft));
3034 } else if ((monthWidth + tableScrollLeft) > monthDayLeft) {
3035 // cell is cut from left scroll, count visible width
3036 cellViewWidth = monthDayWidth - ((monthWidth + tableScrollLeft) - monthDayLeft);
3037 }
3038 cellViewWidth = cellViewWidth > monthDayWidth || cellViewWidth < 1 ? 1 : cellViewWidth;
3039
3040 // push cell details
3041 visibleMonthDays.push({
3042 wday: cell.querySelector('.vbo-overw-tablewday').innerText,
3043 mday: cell.querySelector('.vbo-overw-tablemday').innerText,
3044 ymd: cell.getAttribute('data-ymd'),
3045 vwidth: cellViewWidth,
3046 });
3047 }
3048 }
3049 });
3050
3051 return visibleMonthDays;
3052 }
3053
3054 /**
3055 * Fests
3056 */
3057 function vboRenderFests(day, daytitle) {
3058 // compose fests information
3059 var fests_html = '';
3060 if (vboFests[day] && vboFests[day]['festinfo'] && vboFests[day]['festinfo'].length) {
3061 for (var i = 0; i < vboFests[day]['festinfo'].length; i++) {
3062 var fest = vboFests[day]['festinfo'][i];
3063 fests_html += '<div class="vbo-overlay-fest-details">';
3064 fests_html += ' <div class="vbo-fest-info">';
3065 fests_html += ' <div class="vbo-fest-name">' + fest['trans_name'] + '</div>';
3066 fests_html += ' <div class="vbo-fest-desc">' + fest['descr'].replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + '<br />' + '$2') + '</div>';
3067 fests_html += ' </div>';
3068 fests_html += ' <div class="vbo-fest-cmds">';
3069 fests_html += ' <button type="button" class="btn btn-danger" onclick="vboRemoveFest(\'' + day + '\', \'' + i + '\', \'' + fest['type'] + '\', this);"><?php VikBookingIcons::e('trash-alt', 'no-margin'); ?></button>';
3070 fests_html += ' </div>';
3071 fests_html += '</div>';
3072 }
3073 }
3074
3075 // update ymd key for the selected date, useful for adding new fests
3076 jQuery('.vbo-overlay-fests-addnew').attr('data-ymd', day);
3077
3078 // set content and display modal
3079 jQuery('.vbo-overlay-fests-list').html(fests_html);
3080
3081 if (typeof daytitle !== 'undefined') {
3082 // display modal only when we are not deleting a fest
3083 var fests_modal = VBOCore.displayModal({
3084 suffix: 'overv-mng-fests',
3085 extra_class: 'vbo-modal-rounded vbo-modal-tall',
3086 title: '<?php VikBookingIcons::e('star'); ?> <span>' + daytitle + '</span>',
3087 footer_right: '<button type="button" class="btn btn-success" onclick="vboAddFest();">' + Joomla.JText._('VBSAVE') + '</button>',
3088 dismiss_event: 'vbo-dismiss-modal-overv-mng-fests',
3089 onDismiss: () => {
3090 jQuery('.vbo-overv-mngfest-wrap').appendTo('.vbo-overv-mngfest-block');
3091 },
3092 });
3093
3094 // set modal body
3095 jQuery('.vbo-overv-mngfest-wrap').appendTo(fests_modal);
3096 }
3097 }
3098
3099 function vboRemoveFest(day, index, fest_type, that) {
3100 if (!confirm('<?php echo addslashes(JText::translate('VBDELCONFIRM')); ?>')) {
3101 return false;
3102 }
3103 var elem = jQuery(that);
3104 // make the AJAX request to the controller to remove this fest from the DB
3105 VBOCore.doAjax(
3106 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=remove_fest'); ?>",
3107 {
3108 tmpl: "component",
3109 dt: day,
3110 ind: index,
3111 type: fest_type
3112 },
3113 (res) => {
3114 if (res.indexOf('e4j.ok') >= 0) {
3115 // delete fest also from the json-decode array of objects
3116 if (vboFests[day] && vboFests[day]['festinfo']) {
3117 // use splice to remove the desired index from array, or delete would not make the length of the array change
3118 vboFests[day]['festinfo'].splice(index, 1);
3119 // re-build indexes of delete buttons, fundamental for removing the right index at next click
3120 vboRenderFests(day);
3121 if (!vboFests[day]['festinfo'].length) {
3122 // delete also this date object from fests
3123 delete vboFests[day];
3124 // no more fests, remove the class for this date from all cells
3125 jQuery('th.bluedays[data-ymd="'+day+'"]').removeClass('vbo-overv-festcell');
3126 jQuery('td.notbusy[data-day="'+day+'"]').removeClass('vbo-overv-festcell');
3127 jQuery('td.subnotbusy[data-day="'+day+'"]').removeClass('vbo-overv-festcell');
3128 jQuery('td.busy[data-day="'+day+'"]').removeClass('vbo-overv-festcell');
3129 jQuery('td.subroom-busy[data-day="'+day+'"]').removeClass('vbo-overv-festcell');
3130 }
3131 }
3132 elem.closest('.vbo-overlay-fest-details').remove();
3133 } else {
3134 // console.log(res);
3135 alert('Invalid response');
3136 }
3137 },
3138 (err) => {
3139 alert('Request failed');
3140 }
3141 );
3142 }
3143
3144 function vboAddFest() {
3145 var ymd = jQuery('.vbo-overlay-fests-addnew').attr('data-ymd');
3146 var fest_name = jQuery('#vbo-newfest-name').val();
3147 var fest_descr = jQuery('#vbo-newfest-descr').val();
3148 if (!fest_name || !fest_name.length) {
3149 return false;
3150 }
3151 // make the AJAX request to the controller to add this fest to the DB
3152 VBOCore.doAjax(
3153 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=add_fest'); ?>",
3154 {
3155 tmpl: "component",
3156 dt: ymd,
3157 type: "custom",
3158 name: fest_name,
3159 descr: fest_descr
3160 },
3161 (res) => {
3162 // parse the JSON response that contains the fest object for the passed date
3163 try {
3164 var stored_fest = typeof res === 'string' ? JSON.parse(res) : res;
3165 if (!vboFests.hasOwnProperty(stored_fest['dt'])) {
3166 // we need to add the proper class to all cells to show that there is a fest
3167 jQuery('th.bluedays[data-ymd="'+stored_fest['dt']+'"]').addClass('vbo-overv-festcell');
3168 jQuery('td.notbusy[data-day="'+stored_fest['dt']+'"]').addClass('vbo-overv-festcell');
3169 jQuery('td.subnotbusy[data-day="'+stored_fest['dt']+'"]').addClass('vbo-overv-festcell');
3170 jQuery('td.busy[data-day="'+stored_fest['dt']+'"]').addClass('vbo-overv-festcell');
3171 jQuery('td.subroom-busy[data-day="'+stored_fest['dt']+'"]').addClass('vbo-overv-festcell');
3172 }
3173 vboFests[stored_fest['dt']] = stored_fest;
3174 // hide modal
3175 VBOCore.emitEvent('vbo-dismiss-modal-overv-mng-fests');
3176 // reset input fields
3177 jQuery('#vbo-newfest-name').val('');
3178 jQuery('#vbo-newfest-descr').val('');
3179 } catch (e) {
3180 // console.log(res);
3181 alert('Invalid response');
3182 return false;
3183 }
3184 },
3185 (err) => {
3186 alert('Request failed');
3187 }
3188 );
3189 }
3190
3191 /**
3192 * Room-day notes
3193 */
3194 var rdaynote_icn_full = '<?php echo VikBookingIcons::i('sticky-note', 'vbo-roomdaynote-display'); ?>';
3195 var rdaynote_icn_empty = '<?php echo VikBookingIcons::i('far fa-sticky-note', 'vbo-roomdaynote-display'); ?>';
3196 function vboRenderRdayNotes(day, idroom, subunit, readymd) {
3197 // compose fests information
3198 var notes_html = '';
3199 var keyid = day + '_' + idroom + '_' + subunit;
3200 if (vboRdayNotes.hasOwnProperty(keyid) && vboRdayNotes[keyid]['info'] && vboRdayNotes[keyid]['info'].length) {
3201 for (var i = 0; i < vboRdayNotes[keyid]['info'].length; i++) {
3202 var note_data = vboRdayNotes[keyid]['info'][i];
3203 notes_html += '<div class="vbo-overlay-fest-details vbo-modal-roomdaynotes-note-details">';
3204 notes_html += ' <div class="vbo-fest-info vbo-modal-roomdaynotes-note-info">';
3205 notes_html += ' <div class="vbo-fest-name vbo-modal-roomdaynotes-note-name">' + note_data['name'] + '</div>';
3206 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>';
3207 notes_html += ' </div>';
3208 notes_html += ' <div class="vbo-fest-cmds vbo-modal-roomdaynotes-note-cmds">';
3209 notes_html += ' <button type="button" class="btn btn-danger" onclick="vboRemoveRdayNote(\'' + i + '\', \'' + day + '\', \'' + idroom + '\', \'' + subunit + '\', \'' + note_data['type'] + '\', this);"><?php VikBookingIcons::e('trash-alt', 'no-margin'); ?></button>';
3210 notes_html += ' </div>';
3211 notes_html += '</div>';
3212 }
3213 }
3214 // update attributes keys for the selected date, useful for adding new notes
3215 jQuery('.vbo-modal-roomdaynotes-addnew').attr('data-ymd', day).attr('data-roomid', idroom).attr('data-subroomid', subunit);
3216 if (readymd !== null) {
3217 jQuery('.vbo-modal-roomdaynotes-addnew').attr('data-readymd', readymd);
3218 jQuery('.vbo-newrdnote-dayto-val').text(readymd);
3219 }
3220 // set content and display modal
3221 jQuery('.vbo-modal-roomdaynotes-list').html(notes_html);
3222 }
3223
3224 function vboAddRoomDayNote(that) {
3225 var mainelem = jQuery('.vbo-modal-roomdaynotes-addnew');
3226 var ymd = mainelem.attr('data-ymd');
3227 var roomid = mainelem.attr('data-roomid');
3228 var subroomid = mainelem.attr('data-subroomid');
3229 var note_name = jQuery('#vbo-newrdnote-name').val();
3230 var note_descr = jQuery('#vbo-newrdnote-descr').val();
3231 var note_cdays = jQuery('#vbo-newrdnote-cdays').val();
3232 if (!note_name.length && !note_descr.length) {
3233 alert('Missing required fields');
3234 return false;
3235 }
3236 // make the AJAX request to the controller to add this note to the DB
3237 VBOCore.doAjax(
3238 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=add_roomdaynote'); ?>",
3239 {
3240 tmpl: "component",
3241 dt: ymd,
3242 idroom: roomid,
3243 subunit: subroomid,
3244 type: "custom",
3245 name: note_name,
3246 descr: note_descr,
3247 cdays: note_cdays
3248 },
3249 (res) => {
3250 // parse the JSON response that contains the note object for the passed date
3251 try {
3252 var stored_notes = typeof res === 'string' ? JSON.parse(res) : res;
3253 for (var keyid in stored_notes) {
3254 if (!stored_notes.hasOwnProperty(keyid)) {
3255 continue;
3256 }
3257 if (!vboRdayNotes.hasOwnProperty(keyid) && jQuery('.vbo-roomdaynote-trigger[data-roomday="' + keyid + '"]').length) {
3258 // we need to add the proper class to the cell for this note (if it's visible)
3259 jQuery('.vbo-roomdaynote-trigger[data-roomday="' + keyid + '"]').parent('td').removeClass('vbo-roomdaynote-empty').addClass('vbo-roomdaynote-full').find('i').attr('class', rdaynote_icn_full);
3260 }
3261 // update global object with the new notes in any case
3262 vboRdayNotes[keyid] = stored_notes[keyid];
3263 }
3264 // hide modal
3265 VBOCore.emitEvent('vbo-dismiss-modal-overv-mng-rdaynotes');
3266 // reset input fields
3267 jQuery('#vbo-newrdnote-name').val('');
3268 jQuery('#vbo-newrdnote-descr').val('');
3269 jQuery('#vbo-newrdnote-cdays').val('0').trigger('change');
3270 } catch (e) {
3271 // console.log(res);
3272 alert('Invalid response');
3273 return false;
3274 }
3275 },
3276 (err) => {
3277 alert('Request failed');
3278 }
3279 );
3280 }
3281
3282 function vboRemoveRdayNote(index, day, idroom, subunit, note_type, that) {
3283 if (!confirm('<?php echo addslashes(JText::translate('VBDELCONFIRM')); ?>')) {
3284 return false;
3285 }
3286 var elem = jQuery(that);
3287 // make the AJAX request to the controller to remove this note from the DB
3288 VBOCore.doAjax(
3289 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=remove_roomdaynote'); ?>",
3290 {
3291 tmpl: "component",
3292 dt: day,
3293 idroom: idroom,
3294 subunit: subunit,
3295 ind: index,
3296 type: note_type
3297 },
3298 (res) => {
3299 if (res.indexOf('e4j.ok') >= 0) {
3300 var keyid = day + '_' + idroom + '_' + subunit;
3301 // delete note also from the json-decode array of objects
3302 if (vboRdayNotes[keyid] && vboRdayNotes[keyid]['info']) {
3303 // use splice to remove the desired index from array, or delete would not make the length of the array change
3304 vboRdayNotes[keyid]['info'].splice(index, 1);
3305 // re-build indexes of delete buttons, fundamental for removing the right index at next click
3306 vboRenderRdayNotes(day, idroom, subunit, null);
3307 if (!vboRdayNotes[keyid]['info'].length) {
3308 // delete also this date object from notes
3309 delete vboRdayNotes[keyid];
3310 // no more notes, update the proper class attribute for this cell (should be visible)
3311 if (jQuery('.vbo-roomdaynote-trigger[data-roomday="' + keyid + '"]').length) {
3312 jQuery('.vbo-roomdaynote-trigger[data-roomday="' + keyid + '"]').parent('td').removeClass('vbo-roomdaynote-full').addClass('vbo-roomdaynote-empty').find('i').attr('class', rdaynote_icn_empty);
3313 }
3314 }
3315 }
3316 elem.closest('.vbo-modal-roomdaynotes-note-details').remove();
3317 } else {
3318 // console.log(res);
3319 alert('Invalid response');
3320 }
3321 },
3322 (err) => {
3323 alert('Request failed');
3324 }
3325 );
3326 }
3327
3328 function vboRdayNoteCdaysCount() {
3329 var cdays = parseInt(jQuery('#vbo-newrdnote-cdays').val());
3330 var defymd = jQuery('.vbo-modal-roomdaynotes-addnew').attr('data-ymd');
3331 var defreadymd = jQuery('.vbo-modal-roomdaynotes-addnew').attr('data-readymd');
3332 defreadymd = !defreadymd || !defreadymd.length ? defymd : defreadymd;
3333 if (isNaN(cdays) || cdays < 1) {
3334 jQuery('.vbo-newrdnote-dayto-val').text(defreadymd);
3335 return;
3336 }
3337 // calculate target (until) date
3338 var targetdate = new Date(defymd);
3339 targetdate.setDate(targetdate.getDate() + cdays);
3340 var target_y = targetdate.getFullYear();
3341 var target_m = targetdate.getMonth() + 1;
3342 target_m = target_m < 10 ? '0' + target_m : target_m;
3343 var target_d = targetdate.getDate();
3344 target_d = target_d < 10 ? '0' + target_d : target_d;
3345 // display target date
3346 var display_target = target_y + '-' + target_m + '-' + target_d;
3347 // check if we can get the "read ymd property"
3348 if (jQuery('.bluedays[data-ymd="' + display_target + '"]').length) {
3349 display_target = jQuery('.bluedays[data-ymd="' + display_target + '"]').attr('data-readymd');
3350 }
3351 jQuery('.vbo-newrdnote-dayto-val').text(display_target);
3352 }
3353 </script>
3354
3355 <div class="vbo-orphans-overlay-block">
3356 <a class="vbo-info-overlay-close" href="javascript: void(0);"></a>
3357 <div class="vbo-info-overlay-content vbo-info-overlay-content-orphans">
3358 <h3><?php echo $vbo_app->createPopover(array('title' => JText::translate('VBORPHANSFOUND'), 'content' => JText::translate('VBORPHANSFOUNDSHELP'), 'icon_class' => VikBookingIcons::i('exclamation-triangle'))); ?> <?php echo JText::translate('VBORPHANSFOUND'); ?></h3>
3359 <div class="vbo-info-overlay-scroll-content">
3360 <div class="vbo-orphans-info-list"></div>
3361 </div>
3362 <div class="vbo-orphans-info-cmds">
3363 <div class="vbo-orphans-info-cmd">
3364 <button type="button" class="btn btn-success" onclick="javascript: hideVboDialogOverv(1);"><?php echo JText::translate('VBOBTNKEEPREMIND'); ?></button>
3365 </div>
3366 <div class="vbo-orphans-info-cmd">
3367 <button type="button" class="btn btn-danger" onclick="javascript: hideVboDialogOverv(-1);"><?php echo JText::translate('VBOBTNDONTREMIND'); ?></button>
3368 </div>
3369 </div>
3370 </div>
3371 </div>
3372
3373 <div class="vbo-overv-mngfest-block" style="display: none;">
3374 <div class="vbo-overv-mngfest-wrap">
3375 <div class="vbo-overlay-fests-list"></div>
3376 <div class="vbo-overlay-fests-addnew" data-ymd="">
3377 <h4><?php echo JText::translate('VBOADDCUSTOMFESTTODAY'); ?></h4>
3378 <div class="vbo-overlay-fests-addnew-elem">
3379 <label for="vbo-newfest-name"><?php echo JText::translate('VBPVIEWPLACESONE'); ?></label>
3380 <input type="text" id="vbo-newfest-name" value="" />
3381 </div>
3382 <div class="vbo-overlay-fests-addnew-elem">
3383 <label for="vbo-newfest-descr"><?php echo JText::translate('VBPLACEDESCR'); ?></label>
3384 <textarea id="vbo-newfest-descr"></textarea>
3385 </div>
3386 </div>
3387 </div>
3388 </div>
3389
3390 <div class="vbo-overv-mngroomdaynotes-block" style="display: none;">
3391 <div class="vbo-overv-mngroomdaynotes-wrap">
3392 <div class="vbo-modal-roomdaynotes-list"></div>
3393 <div class="vbo-modal-roomdaynotes-addnew" data-readymd="" data-ymd="" data-roomid="" data-subroomid="">
3394 <h4><?php echo JText::translate('VBOADDCUSTOMFESTTODAY'); ?></h4>
3395 <div class="vbo-modal-roomdaynotes-addnew-elem">
3396 <label for="vbo-newrdnote-name"><?php echo JText::translate('VBPVIEWPLACESONE'); ?></label>
3397 <input type="text" id="vbo-newrdnote-name" value="" />
3398 </div>
3399 <div class="vbo-modal-roomdaynotes-addnew-elem">
3400 <label for="vbo-newrdnote-descr"><?php echo JText::translate('VBPLACEDESCR'); ?></label>
3401 <textarea id="vbo-newrdnote-descr"></textarea>
3402 </div>
3403 <div class="vbo-modal-roomdaynotes-addnew-elem">
3404 <label for="vbo-newrdnote-cdays"><?php echo JText::translate('VBOCONSECUTIVEDAYS'); ?></label>
3405 <input type="number" id="vbo-newrdnote-cdays" min="0" max="365" value="0" onchange="vboRdayNoteCdaysCount();" onkeyup="vboRdayNoteCdaysCount();" />
3406 <span class="vbo-newrdnote-dayto">
3407 <span class="vbo-newrdnote-dayto-lbl"><?php echo JText::translate('VBOUNTIL'); ?></span>
3408 <span class="vbo-newrdnote-dayto-val"></span>
3409 </span>
3410 </div>
3411 </div>
3412 </div>
3413 </div>
3414