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

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