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

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