| 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 |
$geo = VikBooking::getGeocodingInstance(); |
| 14 |
|
| 15 |
$currencysymb = VikBooking::getCurrencySymb(); |
| 16 |
$vat_included = VikBooking::ivaInclusa(); |
| 17 |
$tax_summary = (!$vat_included && VikBooking::showTaxOnSummaryOnly()); |
| 18 |
|
| 19 |
$geo_info_complete = true; |
| 20 |
$global_geo_params = null; |
| 21 |
$rooms_geo_params = []; |
| 22 |
$rooms_features = []; |
| 23 |
$rooms_galleries = []; |
| 24 |
$rooms_units_booked = []; |
| 25 |
$rooms_units_pos = new stdClass; |
| 26 |
$markers_parties = new stdClass; |
| 27 |
|
| 28 |
// lang vars for JS |
| 29 |
JText::script('VBSEARCHROOMNUM'); |
| 30 |
JText::script('VBSEARCHRESADULT'); |
| 31 |
JText::script('VBSEARCHRESADULTS'); |
| 32 |
JText::script('VBSEARCHRESCHILD'); |
| 33 |
JText::script('VBSEARCHRESCHILDREN'); |
| 34 |
JText::script('VBSELECTR'); |
| 35 |
JText::script('VBO_CANCEL_SELECTION'); |
| 36 |
|
| 37 |
// we build a fake booking array to count the units booked for the various rooms |
| 38 |
$booking_data = [ |
| 39 |
'id' => 0, |
| 40 |
'checkin' => $this->checkin, |
| 41 |
'checkout' => $this->checkout, |
| 42 |
]; |
| 43 |
|
| 44 |
foreach ($this->res as $party_num => $party_rooms) { |
| 45 |
foreach ($party_rooms as $party_room) { |
| 46 |
foreach ($party_room as $room_rplan) { |
| 47 |
if (empty($room_rplan['idroom']) || empty($room_rplan['params'])) { |
| 48 |
// unable to proceed |
| 49 |
$geo_info_complete = false; |
| 50 |
break 3; |
| 51 |
} |
| 52 |
if (isset($rooms_geo_params[$room_rplan['idroom']])) { |
| 53 |
// room already parsed |
| 54 |
continue; |
| 55 |
} |
| 56 |
$rparams = json_decode($room_rplan['params']); |
| 57 |
$geo_params = $geo->getRoomGeoParams($rparams); |
| 58 |
if (is_object($geo_params) && isset($geo_params->enabled) && $geo_params->enabled) { |
| 59 |
if (!isset($global_geo_params)) { |
| 60 |
// we use the global geo params of the first room returned |
| 61 |
$global_geo_params = $geo_params; |
| 62 |
} |
| 63 |
// push room geo params |
| 64 |
$rooms_geo_params[$room_rplan['idroom']] = $geo_params; |
| 65 |
// check if distinctive features are available |
| 66 |
$rooms_features[$room_rplan['idroom']] = []; |
| 67 |
$room_features = VikBooking::getRoomParam('features', $room_rplan['params']); |
| 68 |
$room_features = !is_array($room_features) ? [] : $room_features; |
| 69 |
foreach ($room_features as $rindex => $rfeatures) { |
| 70 |
if (!is_array($rfeatures) || !$rfeatures) { |
| 71 |
continue; |
| 72 |
} |
| 73 |
foreach ($rfeatures as $featname => $featval) { |
| 74 |
if (empty($featval)) { |
| 75 |
continue; |
| 76 |
} |
| 77 |
// use the first distinctive feature |
| 78 |
$tn_featname = JText::translate($featname); |
| 79 |
if ($tn_featname == $featname) { |
| 80 |
// no translation was applied |
| 81 |
if (VBOPlatformDetection::isWordPress()) { |
| 82 |
// try to apply a translation through Gettext even if we have to pass a variable |
| 83 |
$tn_featname = __($featname); |
| 84 |
} else { |
| 85 |
// convert the string to a hypothetical INI constant |
| 86 |
$ini_constant = str_replace(' ', '_', strtoupper($featname)); |
| 87 |
$tn_featname = JText::translate($ini_constant); |
| 88 |
$tn_featname = $tn_featname == $ini_constant ? $featname : $tn_featname; |
| 89 |
} |
| 90 |
} |
| 91 |
$rooms_features[$room_rplan['idroom']][$rindex] = $tn_featname . ' ' . $featval; |
| 92 |
break; |
| 93 |
} |
| 94 |
} |
| 95 |
// build rooms gallery |
| 96 |
$rooms_galleries[$room_rplan['idroom']] = []; |
| 97 |
if (!empty($room_rplan['moreimgs'])) { |
| 98 |
$moreimages = explode(';;', $room_rplan['moreimgs']); |
| 99 |
foreach ($moreimages as $mimg) { |
| 100 |
if (empty($mimg)) { |
| 101 |
continue; |
| 102 |
} |
| 103 |
// push thumb URL |
| 104 |
array_push($rooms_galleries[$room_rplan['idroom']], VBO_SITE_URI . 'resources/uploads/thumb_' . $mimg); |
| 105 |
} |
| 106 |
} elseif (!empty($room_rplan['img'])) { |
| 107 |
// push main image URL |
| 108 |
array_push($rooms_galleries[$room_rplan['idroom']], VBO_SITE_URI . 'resources/uploads/' . $room_rplan['img']); |
| 109 |
} |
| 110 |
// count room units booked |
| 111 |
$rooms_units_booked[$room_rplan['idroom']] = VikBooking::getRoomUnitNumsUnavailable($booking_data, $room_rplan['idroom']); |
| 112 |
} else { |
| 113 |
// unable to proceed |
| 114 |
$geo_info_complete = false; |
| 115 |
break 3; |
| 116 |
} |
| 117 |
// we parse just the first rate plan |
| 118 |
break; |
| 119 |
} |
| 120 |
} |
| 121 |
} |
| 122 |
|
| 123 |
// compose markers positions |
| 124 |
foreach ($rooms_geo_params as $idroom => $geo_params) { |
| 125 |
if (isset($geo_params->units_pos) && is_object($geo_params->units_pos) && count(get_object_vars($geo_params->units_pos))) { |
| 126 |
$rooms_units_pos->{$idroom} = $geo_params->units_pos; |
| 127 |
} |
| 128 |
} |
| 129 |
|
| 130 |
if ($geo->isSupported() && $geo_info_complete === true && count($rooms_geo_params) && count(get_object_vars($rooms_units_pos))) { |
| 131 |
// load assets |
| 132 |
$geo->loadAssets([ |
| 133 |
'callback' => 'vbo_gm_ready', |
| 134 |
'libraries' => 'marker', |
| 135 |
'loading' => 'async', |
| 136 |
]); |
| 137 |
|
| 138 |
// check if ground overlay is implemented by the first room that returns the global geo params |
| 139 |
$current_goverlay = null; |
| 140 |
if ((int)$global_geo_params->goverlay > 0 && !empty($global_geo_params->overlay_img) && !empty($global_geo_params->overlay_south)) { |
| 141 |
// ground overlay is available |
| 142 |
$current_goverlay = new stdClass; |
| 143 |
$current_goverlay->url = $global_geo_params->overlay_img; |
| 144 |
$current_goverlay->south = (float)$global_geo_params->overlay_south; |
| 145 |
$current_goverlay->west = (float)$global_geo_params->overlay_west; |
| 146 |
$current_goverlay->north = (float)$global_geo_params->overlay_north; |
| 147 |
$current_goverlay->east = (float)$global_geo_params->overlay_east; |
| 148 |
} |
| 149 |
|
| 150 |
// compose markers parties |
| 151 |
foreach ($this->res as $party_num => $party_rooms) { |
| 152 |
$markers_parties->{$party_num} = []; |
| 153 |
$rooms_in_party = []; |
| 154 |
foreach ($party_rooms as $party_room) { |
| 155 |
foreach ($party_room as $room_rplan) { |
| 156 |
if (empty($room_rplan['idroom']) || !isset($rooms_geo_params[$room_rplan['idroom']])) { |
| 157 |
continue; |
| 158 |
} |
| 159 |
if (!in_array($room_rplan['idroom'], $rooms_in_party)) { |
| 160 |
$raw_roomcost = $tax_summary ? $room_rplan['cost'] : VikBooking::sayCostPlusIva($room_rplan['cost'], $room_rplan['idprice']); |
| 161 |
// build room info object |
| 162 |
$room_info = new stdClass; |
| 163 |
$room_info->id = $room_rplan['idroom']; |
| 164 |
$room_info->name = $room_rplan['name']; |
| 165 |
$room_info->cost = VikBooking::numberFormat($raw_roomcost); |
| 166 |
$room_info->units = isset($room_rplan['unitsavail']) ? (int)$room_rplan['unitsavail'] : (int)$room_rplan['units']; |
| 167 |
$room_info->tot_markers = 1; |
| 168 |
if ($rooms_geo_params[$room_rplan['idroom']]->markers_multi > 0 && $room_rplan['units'] > 1) { |
| 169 |
$room_info->tot_markers = (int)$room_rplan['units']; |
| 170 |
} |
| 171 |
// check if a promo was applied |
| 172 |
if (isset($room_rplan['promotion']) && isset($room_rplan['promotion']['discount'])) { |
| 173 |
if ($room_rplan['promotion']['discount']['pcent']) { |
| 174 |
$prev_amount = $raw_roomcost * 100 / (100 - $room_rplan['promotion']['discount']['amount']); |
| 175 |
} else { |
| 176 |
$prev_amount = $raw_roomcost + $room_rplan['promotion']['discount']['amount']; |
| 177 |
} |
| 178 |
if ($prev_amount > 0) { |
| 179 |
$room_info->prev_cost = VikBooking::numberFormat($prev_amount); |
| 180 |
} |
| 181 |
} |
| 182 |
// push room info |
| 183 |
array_push($markers_parties->{$party_num}, $room_info); |
| 184 |
$rooms_in_party[] = $room_rplan['idroom']; |
| 185 |
} |
| 186 |
// we parse just the first rate plan |
| 187 |
break; |
| 188 |
} |
| 189 |
} |
| 190 |
} |
| 191 |
?> |
| 192 |
<div class="vbo-searchresults-geo-wrapper"> |
| 193 |
<div class="vbo-geo-wrapper"> |
| 194 |
<?php |
| 195 |
if ($this->roomsnum > 1) { |
| 196 |
?> |
| 197 |
<div class="vbo-geomap-uicontrol"> |
| 198 |
<div class="vbo-geomap-uicontrol-inner"> |
| 199 |
<span class="vbo-geomap-uicontrol-partynum"><?php echo JText::translate('VBSEARCHROOMNUM'); ?> 1</span> |
| 200 |
<span class="vbo-geomap-uicontrol-partyguests"> |
| 201 |
<?php VikBookingIcons::e('users', 'vbo-pref-color-text'); ?> |
| 202 |
<?php echo $this->arrpeople[1]['adults']; ?> |
| 203 |
<?php echo ($this->arrpeople[1]['adults'] == 1 ? JText::translate('VBSEARCHRESADULT') : JText::translate('VBSEARCHRESADULTS')); ?> |
| 204 |
<?php echo ($this->showchildren && $this->arrpeople[1]['children'] > 0 ? ", ".$this->arrpeople[1]['children']." ".($this->arrpeople[1]['children'] == 1 ? JText::translate('VBSEARCHRESCHILD') : JText::translate('VBSEARCHRESCHILDREN')) : ""); ?> |
| 205 |
</span> |
| 206 |
</div> |
| 207 |
</div> |
| 208 |
<?php |
| 209 |
} |
| 210 |
?> |
| 211 |
<div class="vbo-results-geo-map-container"> |
| 212 |
<?php |
| 213 |
if ($this->roomsnum > 1) { |
| 214 |
?> |
| 215 |
<div class="vbo-geomap-minicart" style="display: none;"> |
| 216 |
<div class="vbo-geomap-minicart-inner"> |
| 217 |
<div class="vbo-geomap-minicart-head"><?php VikBookingIcons::e('shopping-cart'); ?></div> |
| 218 |
<div class="vbo-geomap-minicart-body"></div> |
| 219 |
</div> |
| 220 |
</div> |
| 221 |
<?php |
| 222 |
} |
| 223 |
?> |
| 224 |
<div id="vbo-results-geo-map" data-height="<?php echo $geo->getRoomGeoParams($global_geo_params, 'height', 300); ?>"></div> |
| 225 |
</div> |
| 226 |
</div> |
| 227 |
</div> |
| 228 |
|
| 229 |
<script type="text/javascript"> |
| 230 |
/** |
| 231 |
* Define global scope vars |
| 232 |
*/ |
| 233 |
var vbo_geomap = null, |
| 234 |
vbo_geo_currency = '<?php echo $currencysymb; ?>'; |
| 235 |
vbo_geo_currency_pos = '<?php echo VikBooking::getCurrencyPosition(); ?>'; |
| 236 |
vbo_geo_showchildren = <?php echo (int)$this->showchildren; ?>, |
| 237 |
vbo_geo_rooms_galleries = <?php echo json_encode($rooms_galleries); ?>, |
| 238 |
vbo_geo_guestparties = <?php echo json_encode($this->arrpeople); ?>, |
| 239 |
vbo_geomarker_parties = <?php echo json_encode($markers_parties); ?>, |
| 240 |
vbo_rooms_units_booked = <?php echo json_encode($rooms_units_booked); ?>, |
| 241 |
vbo_rooms_units_selected = {}, |
| 242 |
vbo_geomarker_units = {}, |
| 243 |
vbo_geomarker_units_pos = <?php echo json_encode($rooms_units_pos); ?>, |
| 244 |
vbo_info_markers = {}, |
| 245 |
vbo_info_markers_helper = <?php echo json_encode($rooms_features); ?>, |
| 246 |
vbo_ground_overlay = null, |
| 247 |
vbo_dbground_overlay = <?php echo is_object($current_goverlay) ? json_encode($current_goverlay) : 'null'; ?>, |
| 248 |
vbo_geomap_booking_step = 1 |
| 249 |
vbo_geomap_total_steps = <?php echo $this->roomsnum; ?>; |
| 250 |
|
| 251 |
// turn off the use of the modal/dialog window in case of multi rooms booking |
| 252 |
var vbo_multirooms_dialog = false; |
| 253 |
|
| 254 |
/** |
| 255 |
* Returns the room information. |
| 256 |
*/ |
| 257 |
function vboGetGeoRoomPartyInformation(idroom) { |
| 258 |
if (!vbo_geomarker_parties.hasOwnProperty(vbo_geomap_booking_step)) { |
| 259 |
return null; |
| 260 |
} |
| 261 |
for (var room in vbo_geomarker_parties[vbo_geomap_booking_step]) { |
| 262 |
if (!vbo_geomarker_parties[vbo_geomap_booking_step].hasOwnProperty(room)) { |
| 263 |
continue; |
| 264 |
} |
| 265 |
if (vbo_geomarker_parties[vbo_geomap_booking_step][room]['id'] == idroom) { |
| 266 |
return vbo_geomarker_parties[vbo_geomap_booking_step][room]; |
| 267 |
} |
| 268 |
} |
| 269 |
return null; |
| 270 |
} |
| 271 |
|
| 272 |
/** |
| 273 |
* Generates the HTML content for the units marker infowindow. |
| 274 |
*/ |
| 275 |
function vboGeoInfoMarkerContent(idroom, index, marker_title, rcost, rprevcost) { |
| 276 |
var rdomselector = '#vbcontainer' + vbo_geomap_booking_step + '_' + idroom; |
| 277 |
var infowin_cont = ''; |
| 278 |
infowin_cont += '<div class="vbo-geomarker-infowin-wrap">'; |
| 279 |
// check if gallery is available |
| 280 |
if (vbo_geo_rooms_galleries.hasOwnProperty(idroom) && vbo_geo_rooms_galleries[idroom].length) { |
| 281 |
infowin_cont += ' <div class="vbo-geomarker-infowin-room-gallery" data-idroom="' + idroom + '"></div>'; |
| 282 |
} |
| 283 |
infowin_cont += ' <div class="vbo-geomarker-room-title">'; |
| 284 |
if (jQuery(rdomselector).find('.vbmodalframe').length) { |
| 285 |
infowin_cont += ' <span class="vbo-geomarker-rstay-info" onclick="jQuery(\'' + rdomselector + '\').find(\'.vbmodalframe\').first().trigger(\'click\');"><?php VikBookingIcons::e('info-circle'); ?></span>'; |
| 286 |
} |
| 287 |
infowin_cont += ' <span class="vbo-geomarker-room-title-cont">' + marker_title + '</span>'; |
| 288 |
infowin_cont += ' </div>'; |
| 289 |
infowin_cont += ' <div class="vbo-geomarker-priceinfo">'; |
| 290 |
if (rprevcost != null) { |
| 291 |
infowin_cont += ' <span class="vbo-geomarker-priceinfo-cost-beforedisc">' + (vbo_geo_currency_pos === 'after' ? '<span class="vbo_price">' + rprevcost + '</span> <span class="vbo_currency">' + vbo_geo_currency + '</span>' : '<span class="vbo_currency">' + vbo_geo_currency + '</span> <span class="vbo_price">' + rprevcost + '</span>') + '</span>'; |
| 292 |
} |
| 293 |
infowin_cont += ' <span class="vbo-geomarker-priceinfo-cost">' + (vbo_geo_currency_pos === 'after' ? '<span class="vbo_price">' + rcost + '</span> <span class="vbo_currency">' + vbo_geo_currency + '</span>' : '<span class="vbo_currency">' + vbo_geo_currency + '</span> <span class="vbo_price">' + rcost + '</span>') + '</span>'; |
| 294 |
infowin_cont += ' </div>'; |
| 295 |
infowin_cont += ' <div class="vbo-geomarker-bookroom">'; |
| 296 |
infowin_cont += ' <button type="button" class="btn vbo-pref-color-btn" onclick="vboInteractiveGeoMapSelectRoom(' + idroom + ', ' + index + ');">' + Joomla.JText._('VBSELECTR') + '</button>'; |
| 297 |
infowin_cont += ' </div>'; |
| 298 |
infowin_cont += '</div>'; |
| 299 |
|
| 300 |
return infowin_cont; |
| 301 |
} |
| 302 |
|
| 303 |
/** |
| 304 |
* Selects one room and index for booking the current party. |
| 305 |
*/ |
| 306 |
function vboInteractiveGeoMapSelectRoom(idroom, index) { |
| 307 |
var current_step = vbo_geomap_booking_step; |
| 308 |
var room_info = vboGetGeoRoomPartyInformation(idroom); |
| 309 |
var tot_markers = room_info !== null ? room_info['tot_markers'] : 1; |
| 310 |
// register room index hidden field |
| 311 |
if (jQuery('#vbo-geo-roomindex-' + current_step).length) { |
| 312 |
jQuery('#vbo-geo-roomindex-' + current_step).remove(); |
| 313 |
} |
| 314 |
// if room does not support sub-units or has markers for sub-units, index value is 0 so that we can skip it later on in the booking process |
| 315 |
var room_index_val = tot_markers === 1 ? 0 : index; |
| 316 |
jQuery('#vbselectroomform').append('<input type="hidden" id="vbo-geo-roomindex-' + current_step + '" name="roomindex[]" value="' + room_index_val + '" />'); |
| 317 |
// trigger the main function to select the room |
| 318 |
var res = vbSelectRoom(current_step, idroom); |
| 319 |
if (res === false) { |
| 320 |
jQuery('#vbo-geo-roomindex-' + current_step).remove(); |
| 321 |
return; |
| 322 |
} |
| 323 |
// check if the next step should be displayed |
| 324 |
if (vbo_geomap_total_steps > 1 && vbo_geomap_total_steps > current_step) { |
| 325 |
// register the room index selected |
| 326 |
vbo_rooms_units_selected[current_step] = { |
| 327 |
idroom: idroom, |
| 328 |
index: index |
| 329 |
}; |
| 330 |
// populate mini-cart |
| 331 |
if (jQuery('.vbo-geomap-minicart').length) { |
| 332 |
var rcost = room_info !== null ? room_info['cost'] : ''; |
| 333 |
var rname = room_info !== null ? room_info['name'] : ''; |
| 334 |
var room_title = rname + ' #' + (index + ''); |
| 335 |
if (tot_markers === 1) { |
| 336 |
room_title = rname; |
| 337 |
} else if (vbo_info_markers_helper.hasOwnProperty(idroom) && vbo_info_markers_helper[idroom].hasOwnProperty(index)) { |
| 338 |
room_title = rname + ' - ' + vbo_info_markers_helper[idroom][index]; |
| 339 |
} |
| 340 |
// get party info |
| 341 |
var vbo_uicontrol_guests = ''; |
| 342 |
vbo_uicontrol_guests += '<?php VikBookingIcons::e('users', 'vbo-pref-color-text'); ?> '; |
| 343 |
vbo_uicontrol_guests += vbo_geo_guestparties[current_step]['adults'] + ' '; |
| 344 |
vbo_uicontrol_guests += (vbo_geo_guestparties[current_step]['adults'] == 1 ? Joomla.JText._('VBSEARCHRESADULT') : Joomla.JText._('VBSEARCHRESADULTS')) + ' '; |
| 345 |
vbo_uicontrol_guests += (vbo_geo_showchildren > 0 && vbo_geo_guestparties[current_step]['children'] > 1 ? (', ' + vbo_geo_guestparties[current_step]['children'] + ' ' + (vbo_geo_guestparties[current_step]['children'] == 1 ? Joomla.JText._('VBSEARCHRESCHILD') : Joomla.JText._('VBSEARCHRESCHILDREN'))) : ''); |
| 346 |
var minicart_row = ''; |
| 347 |
minicart_row += '<div class="vbo-geomap-minicart-row" data-minicartrow="' + current_step + '">'; |
| 348 |
minicart_row += ' <div class="vbo-geomap-minicart-party">'; |
| 349 |
minicart_row += ' <span class="vbo-geomap-minicart-party-num">' + Joomla.JText._('VBSEARCHROOMNUM') + ' ' + current_step + '</span>'; |
| 350 |
minicart_row += ' <span class="vbo-geomap-minicart-party-guests">' + vbo_uicontrol_guests + '</span>'; |
| 351 |
minicart_row += ' </div>'; |
| 352 |
minicart_row += ' <div class="vbo-geomap-minicart-room">'; |
| 353 |
minicart_row += ' <span class="vbo-geomap-minicart-room-name">' + room_title + '</span>'; |
| 354 |
minicart_row += ' <span class="vbo-geomap-minicart-room-price">' + (vbo_geo_currency_pos === 'after' ? '<span class="vbo_price">' + rcost + '</span> <span class="vbo_currency">' + vbo_geo_currency + '</span>' : '<span class="vbo_currency">' + vbo_geo_currency + '</span> <span class="vbo_price">' + rcost + '</span>') + '</span>'; |
| 355 |
minicart_row += ' <span class="vbo-geomap-minicart-room-trash" onclick="vboInteractiveGeoMapDeselectRoom(' + current_step + ', ' + idroom + ');"><?php VikBookingIcons::e('trash'); ?></span>'; |
| 356 |
minicart_row += ' </div>'; |
| 357 |
minicart_row += '</div>'; |
| 358 |
jQuery('.vbo-geomap-minicart-body').append(minicart_row).removeClass('vbo-geomap-minicart-body-hid').addClass('vbo-geomap-minicart-body-enter'); |
| 359 |
jQuery('.vbo-geomap-minicart').show(); |
| 360 |
// register closing event for the minicart body |
| 361 |
setTimeout(function() { |
| 362 |
jQuery('.vbo-geomap-minicart-body').removeClass('vbo-geomap-minicart-body-enter').addClass('vbo-geomap-minicart-body-hid'); |
| 363 |
}, 1500); |
| 364 |
} |
| 365 |
// increase step for next selection |
| 366 |
vbo_geomap_booking_step++; |
| 367 |
// reload map for the next booking party |
| 368 |
vboInitInteractiveGeoMap(); |
| 369 |
return; |
| 370 |
} |
| 371 |
if (res === true) { |
| 372 |
// form is being submitted |
| 373 |
return; |
| 374 |
} |
| 375 |
if (vbo_geomap_booking_step == vbo_geomap_total_steps) { |
| 376 |
// we can submit the form |
| 377 |
document.getElementById('vbselectroomform').submit(); |
| 378 |
return true; |
| 379 |
} |
| 380 |
} |
| 381 |
|
| 382 |
/** |
| 383 |
* Deselects the room from the given party step, as well as for any step after. |
| 384 |
*/ |
| 385 |
function vboInteractiveGeoMapDeselectRoom(step, idroom) { |
| 386 |
if (confirm(Joomla.JText._('VBO_CANCEL_SELECTION'))) { |
| 387 |
if (step > vbo_geomap_total_steps) { |
| 388 |
console.error('invalid step given', step, vbo_geomap_total_steps); |
| 389 |
return false; |
| 390 |
} |
| 391 |
// loop through all room parties starting from the last one until reaching the selected one |
| 392 |
for (var now_step = vbo_geomap_total_steps; now_step >= step; now_step--) { |
| 393 |
// remove cart row |
| 394 |
if (jQuery('.vbo-geomap-minicart-row[data-minicartrow="' + now_step + '"]').length) { |
| 395 |
jQuery('.vbo-geomap-minicart-row[data-minicartrow="' + now_step + '"]').remove(); |
| 396 |
} |
| 397 |
// hide cart if no more rows |
| 398 |
if (!jQuery('.vbo-geomap-minicart-row').length) { |
| 399 |
jQuery('.vbo-geomap-minicart').hide(); |
| 400 |
} |
| 401 |
// remove hidden field for room index |
| 402 |
if (jQuery('#vbo-geo-roomindex-' + now_step).length) { |
| 403 |
jQuery('#vbo-geo-roomindex-' + now_step).remove(); |
| 404 |
} |
| 405 |
// delete the room index previously selected |
| 406 |
if (vbo_rooms_units_selected.hasOwnProperty(now_step)) { |
| 407 |
delete vbo_rooms_units_selected[now_step]; |
| 408 |
} |
| 409 |
// deselect room from classic template |
| 410 |
// remove selected class |
| 411 |
if (jQuery('#vbcontainer' + now_step + '_' + idroom).length) { |
| 412 |
jQuery('#vbcontainer' + now_step + '_' + idroom).removeClass('room_selected'); |
| 413 |
} |
| 414 |
// change selector label |
| 415 |
if (jQuery('#vbselector' + now_step + '_' + idroom).length) { |
| 416 |
jQuery('#vbselector' + now_step + '_' + idroom).text(Joomla.JText._('VBSELECTR')); |
| 417 |
} |
| 418 |
// empty hidden input field |
| 419 |
if (jQuery('#roomopt' + now_step).length) { |
| 420 |
jQuery('#roomopt' + now_step).val(''); |
| 421 |
} |
| 422 |
// update global r_counter object from classic template |
| 423 |
if (typeof r_counter !== 'undefined' && r_counter.hasOwnProperty(idroom)) { |
| 424 |
// decrease units used for this room |
| 425 |
if (r_counter[idroom].hasOwnProperty('used') && r_counter[idroom]['used'] > 0) { |
| 426 |
r_counter[idroom]['used']--; |
| 427 |
} |
| 428 |
} |
| 429 |
} |
| 430 |
// update global booking step |
| 431 |
vbo_geomap_booking_step = step; |
| 432 |
// reload map for the current booking party |
| 433 |
vboInitInteractiveGeoMap(); |
| 434 |
} |
| 435 |
} |
| 436 |
|
| 437 |
/** |
| 438 |
* Given the markers of the rooms for the current booking party, calculates and sets the map bounds |
| 439 |
*/ |
| 440 |
function vboInteractiveGeoMapCenterBounds() { |
| 441 |
if (vbo_geomap === null || !vbo_geomarker_parties.hasOwnProperty(vbo_geomap_booking_step)) { |
| 442 |
console.error('map is null or booking step not found'); |
| 443 |
return false; |
| 444 |
} |
| 445 |
// set map center and zoom automatically |
| 446 |
var latlngbounds = new google.maps.LatLngBounds(); |
| 447 |
|
| 448 |
// get the position of all markers for the current room party |
| 449 |
var party_rooms = vbo_geomarker_parties[vbo_geomap_booking_step]; |
| 450 |
var tot_coords = 0; |
| 451 |
for (var room in party_rooms) { |
| 452 |
if (!party_rooms.hasOwnProperty(room)) { |
| 453 |
continue; |
| 454 |
} |
| 455 |
var idroom = party_rooms[room]['id']; |
| 456 |
if (!vbo_geomarker_units_pos.hasOwnProperty(idroom)) { |
| 457 |
continue; |
| 458 |
} |
| 459 |
for (var m in vbo_geomarker_units_pos[idroom]) { |
| 460 |
if (!vbo_geomarker_units_pos[idroom].hasOwnProperty(m)) { |
| 461 |
continue; |
| 462 |
} |
| 463 |
latlngbounds.extend({ |
| 464 |
lat: parseFloat(vbo_geomarker_units_pos[idroom][m]['lat']), |
| 465 |
lng: parseFloat(vbo_geomarker_units_pos[idroom][m]['lng']) |
| 466 |
}); |
| 467 |
tot_coords++; |
| 468 |
} |
| 469 |
} |
| 470 |
|
| 471 |
// apply calculated center and bounds |
| 472 |
if (tot_coords > 0) { |
| 473 |
vbo_geomap.setCenter(latlngbounds.getCenter()); |
| 474 |
vbo_geomap.fitBounds(latlngbounds); |
| 475 |
} |
| 476 |
} |
| 477 |
|
| 478 |
/** |
| 479 |
* Given the the current booking party, adds the markers to the map |
| 480 |
*/ |
| 481 |
function vboInteractiveGeoMapSetPartyMarkers() { |
| 482 |
if (vbo_geomap === null || !vbo_geomarker_parties.hasOwnProperty(vbo_geomap_booking_step)) { |
| 483 |
console.error('map is null or booking step not found'); |
| 484 |
return false; |
| 485 |
} |
| 486 |
|
| 487 |
// update UI control for this room party |
| 488 |
if (jQuery('.vbo-geomap-uicontrol').length) { |
| 489 |
jQuery('.vbo-geomap-uicontrol-partynum').text(Joomla.JText._('VBSEARCHROOMNUM') + ' ' + vbo_geomap_booking_step); |
| 490 |
var vbo_uicontrol_guests = ''; |
| 491 |
vbo_uicontrol_guests += '<?php VikBookingIcons::e('users', 'vbo-pref-color-text'); ?> '; |
| 492 |
vbo_uicontrol_guests += vbo_geo_guestparties[vbo_geomap_booking_step]['adults'] + ' '; |
| 493 |
vbo_uicontrol_guests += (vbo_geo_guestparties[vbo_geomap_booking_step]['adults'] == 1 ? Joomla.JText._('VBSEARCHRESADULT') : Joomla.JText._('VBSEARCHRESADULTS')) + ' '; |
| 494 |
vbo_uicontrol_guests += (vbo_geo_showchildren > 0 && vbo_geo_guestparties[vbo_geomap_booking_step]['children'] > 1 ? (', ' + vbo_geo_guestparties[vbo_geomap_booking_step]['children'] + ' ' + (vbo_geo_guestparties[vbo_geomap_booking_step]['children'] == 1 ? Joomla.JText._('VBSEARCHRESCHILD') : Joomla.JText._('VBSEARCHRESCHILDREN'))) : ''); |
| 495 |
jQuery('.vbo-geomap-uicontrol-partyguests').html(vbo_uicontrol_guests); |
| 496 |
} |
| 497 |
|
| 498 |
// unset any previously defined marker, if any |
| 499 |
for (var i in vbo_geomarker_units) { |
| 500 |
if (vbo_geomarker_units.hasOwnProperty(i)) { |
| 501 |
vbo_geomarker_units[i].setMap(null); |
| 502 |
} |
| 503 |
} |
| 504 |
vbo_geomarker_units = {}; |
| 505 |
|
| 506 |
// iterate through rooms of current party |
| 507 |
var party_rooms = vbo_geomarker_parties[vbo_geomap_booking_step]; |
| 508 |
for (var room in party_rooms) { |
| 509 |
if (!party_rooms.hasOwnProperty(room)) { |
| 510 |
continue; |
| 511 |
} |
| 512 |
var idroom = party_rooms[room]['id']; |
| 513 |
var rname = party_rooms[room]['name']; |
| 514 |
var runits = party_rooms[room]['units']; |
| 515 |
var rcost = party_rooms[room]['cost']; |
| 516 |
var rprevcost = party_rooms[room].hasOwnProperty('prev_cost') ? party_rooms[room]['prev_cost'] : null; |
| 517 |
var tot_markers = party_rooms[room]['tot_markers']; |
| 518 |
if (!vbo_geomarker_units_pos.hasOwnProperty(idroom)) { |
| 519 |
continue; |
| 520 |
} |
| 521 |
// iterate through all markers for this room |
| 522 |
for (var m = 1; m <= tot_markers; m++) { |
| 523 |
if (!vbo_geomarker_units_pos[idroom].hasOwnProperty(m)) { |
| 524 |
continue; |
| 525 |
} |
| 526 |
// make sure this room index is available on these dates |
| 527 |
if (vbo_rooms_units_booked.hasOwnProperty(idroom)) { |
| 528 |
var room_index_booked = false; |
| 529 |
for (var res_id in vbo_rooms_units_booked[idroom]) { |
| 530 |
if (!vbo_rooms_units_booked[idroom].hasOwnProperty(res_id)) { |
| 531 |
continue; |
| 532 |
} |
| 533 |
if (vbo_rooms_units_booked[idroom][res_id] == m) { |
| 534 |
room_index_booked = true; |
| 535 |
break; |
| 536 |
} |
| 537 |
} |
| 538 |
if (room_index_booked === true) { |
| 539 |
// this marker (room unit) index is booked |
| 540 |
continue; |
| 541 |
} |
| 542 |
} |
| 543 |
// make sure this room is still selectable |
| 544 |
var selected_units = 0; |
| 545 |
for (var step in vbo_rooms_units_selected) { |
| 546 |
if (!vbo_rooms_units_selected.hasOwnProperty(step)) { |
| 547 |
continue; |
| 548 |
} |
| 549 |
if (vbo_rooms_units_selected[step]['idroom'] == idroom) { |
| 550 |
// increase units selected for this room |
| 551 |
selected_units++; |
| 552 |
if (tot_markers > 1 && vbo_rooms_units_selected[step]['index'] == m) { |
| 553 |
// this exact index was selected, and the room has got multiple markers, so skip it |
| 554 |
selected_units = runits; |
| 555 |
break; |
| 556 |
} |
| 557 |
} |
| 558 |
} |
| 559 |
if (selected_units >= runits) { |
| 560 |
// this room has no more units available or this multi-marker was selected already, so skip the marker |
| 561 |
continue; |
| 562 |
} |
| 563 |
|
| 564 |
// prepare marker object for the current room unit |
| 565 |
var marker_title = rname + ' #' + (m + ''); |
| 566 |
if (tot_markers === 1) { |
| 567 |
marker_title = rname; |
| 568 |
} else if (vbo_info_markers_helper.hasOwnProperty(idroom) && vbo_info_markers_helper[idroom].hasOwnProperty(m)) { |
| 569 |
marker_title = rname + ' - ' + vbo_info_markers_helper[idroom][m]; |
| 570 |
} |
| 571 |
|
| 572 |
// build map marker options (no "content" by default, hence the classic Google Maps red marker) |
| 573 |
var marker_options = { |
| 574 |
map: vbo_geomap, |
| 575 |
position: { |
| 576 |
lat: parseFloat(vbo_geomarker_units_pos[idroom][m]['lat']), |
| 577 |
lng: parseFloat(vbo_geomarker_units_pos[idroom][m]['lng']) |
| 578 |
}, |
| 579 |
title: marker_title, |
| 580 |
}; |
| 581 |
|
| 582 |
if (rcost) { |
| 583 |
// define the marker "content" with a span node showing the listing cost |
| 584 |
let marker_cost_el = document.createElement('span'); |
| 585 |
marker_cost_el.classList.add('vbo-map-listing-cost'); |
| 586 |
marker_cost_el.setAttribute('data-room-id', idroom); |
| 587 |
if (vbo_geo_currency_pos === 'after') { |
| 588 |
marker_cost_el.innerHTML = '<span class="vbo_price">' + rcost + '</span> <span class="vbo_currency">' + vbo_geo_currency + '</span>'; |
| 589 |
} else { |
| 590 |
marker_cost_el.innerHTML = '<span class="vbo_currency">' + vbo_geo_currency + '</span> <span class="vbo_price">' + rcost + '</span>'; |
| 591 |
} |
| 592 |
// set marker content |
| 593 |
marker_options['content'] = marker_cost_el; |
| 594 |
} |
| 595 |
|
| 596 |
// check if we know a custom icon for this marker |
| 597 |
if (vbo_geomarker_units_pos[idroom][m].hasOwnProperty('icon')) { |
| 598 |
// custom icon defined, identify the type to build a node |
| 599 |
if (vbo_geomarker_units_pos[idroom][m]['icon']?.url) { |
| 600 |
// we've got an image |
| 601 |
let marker_img = document.createElement('img'); |
| 602 |
marker_img.src = vbo_geomarker_units_pos[idroom][m]['icon'].url; |
| 603 |
if (vbo_geomarker_units_pos[idroom][m]['icon']?.scaledSize?.width) { |
| 604 |
marker_img.style.width = vbo_geomarker_units_pos[idroom][m]['icon'].scaledSize.width + 'px'; |
| 605 |
} |
| 606 |
if (vbo_geomarker_units_pos[idroom][m]['icon']?.scaledSize?.height) { |
| 607 |
marker_img.style.height = vbo_geomarker_units_pos[idroom][m]['icon'].scaledSize.height + 'px'; |
| 608 |
} |
| 609 |
// center the image |
| 610 |
marker_img.style.transform = 'translate(0, 50%)'; |
| 611 |
// set marker content |
| 612 |
marker_options['content'] = marker_img; |
| 613 |
} else if (vbo_geomarker_units_pos[idroom][m]['icon']?.path) { |
| 614 |
// we've got an SVG path symbol |
| 615 |
let svgns = 'http://www.w3.org/2000/svg'; |
| 616 |
let svg = document.createElementNS(svgns, 'svg'); |
| 617 |
svg.setAttribute('xmlns', svgns); |
| 618 |
svg.setAttribute('viewBox', '0 0 90 90'); |
| 619 |
svg.setAttribute('width', 90); |
| 620 |
svg.setAttribute('height', 90); |
| 621 |
let path = document.createElementNS(svgns, 'path'); |
| 622 |
path.setAttribute('d', vbo_geomarker_units_pos[idroom][m]['icon'].path); |
| 623 |
path.setAttribute('fill', vbo_geomarker_units_pos[idroom][m]['icon']?.fillColor || '#eeeeee'); |
| 624 |
path.setAttribute('fill-opacity', vbo_geomarker_units_pos[idroom][m]['icon']?.fillOpacity || 0.9); |
| 625 |
path.setAttribute('stroke-width', vbo_geomarker_units_pos[idroom][m]['icon']?.strokeWeight || 0); |
| 626 |
if (vbo_geomarker_units_pos[idroom][m]['icon']?.scale && vbo_geomarker_units_pos[idroom][m]['icon']?.anchor?.x && vbo_geomarker_units_pos[idroom][m]['icon']?.anchor?.y) { |
| 627 |
path.setAttribute( |
| 628 |
'transform', |
| 629 |
'translate(' + vbo_geomarker_units_pos[idroom][m]['icon'].anchor.x + ', ' + vbo_geomarker_units_pos[idroom][m]['icon'].anchor.y + ') scale(' + vbo_geomarker_units_pos[idroom][m]['icon'].scale + ')' |
| 630 |
); |
| 631 |
} |
| 632 |
svg.appendChild(path); |
| 633 |
// set marker content |
| 634 |
marker_options['content'] = svg; |
| 635 |
} |
| 636 |
} |
| 637 |
|
| 638 |
// set custom properties |
| 639 |
let marker_room_data = {}; |
| 640 |
marker_room_data['room_id'] = idroom; |
| 641 |
marker_room_data['room_name'] = rname; |
| 642 |
marker_room_data['room_units'] = runits; |
| 643 |
marker_room_data['room_cost'] = rcost; |
| 644 |
marker_room_data['room_prev_cost'] = rprevcost; |
| 645 |
marker_room_data['room_index'] = m; |
| 646 |
marker_room_data['room_gallery'] = (vbo_geo_rooms_galleries.hasOwnProperty(idroom) && vbo_geo_rooms_galleries[idroom].length ? 1 : 0); |
| 647 |
|
| 648 |
// create marker infowindow |
| 649 |
var vbo_info_marker_cont = vboGeoInfoMarkerContent(idroom, m, marker_title, rcost, rprevcost); |
| 650 |
var vbo_info_marker = new google.maps.InfoWindow({ |
| 651 |
content: vbo_info_marker_cont, |
| 652 |
}); |
| 653 |
|
| 654 |
// add unit marker to map |
| 655 |
var vbo_geomarker_runit = new google.maps.marker.AdvancedMarkerElement(marker_options); |
| 656 |
|
| 657 |
// add listener to marker's click event |
| 658 |
vbo_geomarker_runit.addListener('click', function() { |
| 659 |
// close any other open infowindow first |
| 660 |
for (var infom in vbo_info_markers) { |
| 661 |
if (!vbo_info_markers.hasOwnProperty(infom)) { |
| 662 |
continue; |
| 663 |
} |
| 664 |
vbo_info_markers[infom].close(); |
| 665 |
} |
| 666 |
// display its infowindow only |
| 667 |
if (marker_room_data['room_id'] && marker_room_data['room_index']) { |
| 668 |
var identifier = marker_room_data['room_id'] + '_' + marker_room_data['room_index']; |
| 669 |
if (vbo_info_markers.hasOwnProperty(identifier)) { |
| 670 |
vbo_info_markers[identifier].open(vbo_geomap, this); |
| 671 |
if (marker_room_data['room_gallery'] > 0) { |
| 672 |
var gallery_params = { |
| 673 |
images: vbo_geo_rooms_galleries[marker_room_data['room_id']], |
| 674 |
navButPrevContent: '<?php VikBookingIcons::e('chevron-left'); ?>', |
| 675 |
navButNextContent: '<?php VikBookingIcons::e('chevron-right'); ?>', |
| 676 |
containerHeight: '195px', |
| 677 |
}; |
| 678 |
var gallery_selector = '.vbo-geomarker-infowin-room-gallery[data-idroom="' + marker_room_data['room_id'] + '"]'; |
| 679 |
if (jQuery(gallery_selector).length) { |
| 680 |
// render gallery immediately |
| 681 |
vboGeoMapRenderInfowinGallery(gallery_selector, gallery_params); |
| 682 |
} else { |
| 683 |
// infowindows may take some seconds to be displayed, so we delay the rendering |
| 684 |
window.setTimeout(vboGeoMapRenderInfowinGallery.bind(null, gallery_selector, gallery_params), 500); |
| 685 |
} |
| 686 |
} |
| 687 |
} else { |
| 688 |
console.error('info marker identifier not found', this, identifier); |
| 689 |
} |
| 690 |
} else { |
| 691 |
console.error('info marker properties not found', this); |
| 692 |
} |
| 693 |
}); |
| 694 |
// register marker to pool |
| 695 |
var identifier = idroom + '_' + m; |
| 696 |
vbo_geomarker_units[identifier] = vbo_geomarker_runit; |
| 697 |
// register info window |
| 698 |
vbo_info_markers[identifier] = vbo_info_marker; |
| 699 |
} |
| 700 |
} |
| 701 |
} |
| 702 |
|
| 703 |
/** |
| 704 |
* Renders the vikDotsSlider gallery for an infowindow. |
| 705 |
* We wrap the use of this plugin inside a function so that we can delay the execution. |
| 706 |
*/ |
| 707 |
function vboGeoMapRenderInfowinGallery(selector, params) { |
| 708 |
jQuery(selector).vikDotsSlider(params); |
| 709 |
} |
| 710 |
|
| 711 |
/** |
| 712 |
* Initializes the interactive geo map through Google Maps for the current booking step. |
| 713 |
*/ |
| 714 |
function vboInitInteractiveGeoMap() { |
| 715 |
// default map options |
| 716 |
var def_map_options = { |
| 717 |
center: new google.maps.LatLng(<?php echo $global_geo_params->latitude; ?>, <?php echo $global_geo_params->longitude; ?>), |
| 718 |
zoom: <?php echo (int)$global_geo_params->zoom; ?>, |
| 719 |
mapId: 'vbo_map', |
| 720 |
mapTypeId: '<?php echo $global_geo_params->mtype; ?>', |
| 721 |
mapTypeControl: false |
| 722 |
}; |
| 723 |
// initialize Map |
| 724 |
vbo_geomap = new google.maps.Map(document.getElementById('vbo-results-geo-map'), def_map_options); |
| 725 |
// add listeners to map |
| 726 |
vbo_geomap.addListener('click', function(e) { |
| 727 |
// close all infowindows |
| 728 |
for (var infom in vbo_info_markers) { |
| 729 |
if (!vbo_info_markers.hasOwnProperty(infom)) { |
| 730 |
continue; |
| 731 |
} |
| 732 |
vbo_info_markers[infom].close(); |
| 733 |
} |
| 734 |
}); |
| 735 |
// add markers and set UI control for the current rooms party |
| 736 |
vboInteractiveGeoMapSetPartyMarkers(); |
| 737 |
// center bounds |
| 738 |
vboInteractiveGeoMapCenterBounds(); |
| 739 |
// populate ground overlay image, if set |
| 740 |
if (vbo_dbground_overlay !== null) { |
| 741 |
// compose LatLngBounds object |
| 742 |
var overlay_bounds = new google.maps.LatLngBounds( |
| 743 |
new google.maps.LatLng(parseFloat(vbo_dbground_overlay.south), parseFloat(vbo_dbground_overlay.west)), |
| 744 |
new google.maps.LatLng(parseFloat(vbo_dbground_overlay.north), parseFloat(vbo_dbground_overlay.east)) |
| 745 |
); |
| 746 |
// update ground overlay object |
| 747 |
vbo_ground_overlay = new google.maps.GroundOverlay(vbo_dbground_overlay.url, overlay_bounds); |
| 748 |
// set the overlay to the map |
| 749 |
vbo_ground_overlay.setMap(vbo_geomap); |
| 750 |
} |
| 751 |
} |
| 752 |
|
| 753 |
jQuery(function() { |
| 754 |
|
| 755 |
// register hovering events for minicart body |
| 756 |
if (jQuery('.vbo-geomap-minicart').length) { |
| 757 |
jQuery('.vbo-geomap-minicart').hover(function() { |
| 758 |
jQuery('.vbo-geomap-minicart-body').removeClass('vbo-geomap-minicart-body-hid').addClass('vbo-geomap-minicart-body-enter'); |
| 759 |
}, function() { |
| 760 |
jQuery('.vbo-geomap-minicart-body').removeClass('vbo-geomap-minicart-body-enter').addClass('vbo-geomap-minicart-body-hid'); |
| 761 |
}); |
| 762 |
} |
| 763 |
|
| 764 |
// preload image gallery |
| 765 |
jQuery(vbo_geo_rooms_galleries).vikDotsSlider('preloadImages'); |
| 766 |
|
| 767 |
// register class in search results container to tell the geomap is enabled |
| 768 |
document.querySelectorAll('.vbo-results-content').forEach((el) => { |
| 769 |
el.classList.add('vbo-results-with-geomap'); |
| 770 |
}); |
| 771 |
|
| 772 |
/** |
| 773 |
* Register promise for Google Maps async loading completion. |
| 774 |
*/ |
| 775 |
VBOCore.onInstanceReady(() => { |
| 776 |
// constantly check when Google Maps async loading is completed |
| 777 |
return VBOGMapsUtils.isReady; |
| 778 |
}).then(() => { |
| 779 |
// init interactive geo map with markers |
| 780 |
vboInitInteractiveGeoMap(); |
| 781 |
}); |
| 782 |
}); |
| 783 |
|
| 784 |
</script> |
| 785 |
<?php |
| 786 |
} else { |
| 787 |
// register class in search results container to tell the geomap is disabled |
| 788 |
JFactory::getDocument()->addScriptDeclaration( |
| 789 |
<<<JAVASCRIPT |
| 790 |
VBOCore.DOMLoaded(() => { |
| 791 |
document.querySelectorAll('.vbo-results-content').forEach((el) => { |
| 792 |
el.classList.add('vbo-results-without-geomap'); |
| 793 |
}); |
| 794 |
}); |
| 795 |
JAVASCRIPT |
| 796 |
); |
| 797 |
} |
| 798 |
|