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

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