PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.16
VikBooking Hotel Booking Engine & PMS v1.8.16
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 / manageroom / tmpl / default_geocoding_info.php

default_geocoding_info.php in VikBooking Hotel Booking Engine & PMS 1.8.16, at admin/views/manageroom/tmpl/default_geocoding_info.php

1,789 lines 70.0 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 $vbo_app = VikBooking::getVboApplication();
14 $geo = VikBooking::getGeocodingInstance()->loadAssets([
15 'callback' => 'vbo_gm_ready',
16 'libraries' => 'marker',
17 'loading' => 'async',
18 ]);
19 $geo_supported = $geo->isSupported();
20 $rooms_params = count($this->row) ? json_decode($this->row['params'], true) : array();
21 $geo_import_from = $geo->getImportableConfigRooms((count($this->row) ? $this->row['id'] : 0));
22
23 // load colorpicker assets
24 JFactory::getDocument()->addStyleSheet(VBO_ADMIN_URI.'resources/js_upload/colorpicker.css');
25 JHtml::fetch('script', VBO_ADMIN_URI.'resources/js_upload/colorpicker.js');
26
27 // load lang vars for JS
28 JText::script('VBO_GEO_MARKERS_MULTI_ERRUNIT');
29 JText::script('VBODISTFEATURERUNIT');
30 JText::script('VBO_GEO_ADDRESS');
31 JText::script('VBO_GEO_CUSTOMIZE_MARKER');
32 JText::script('VBMAINPAYMENTSDEL');
33 JText::script('VBO_GEO_RMCONF_MARKER');
34 JText::script('VBO_WIDGETS_CONFRMELEM');
35 JText::script('VBO_GEO_HIDE_FROM_MAP');
36 JText::script('VBO_GEO_MAP_GOVERLAY');
37 JText::script('VBO_PLEASE_SELECT');
38 JText::script('VBO_GEO_IMPORT_FROM');
39 JText::script('VBO_GEO_IMPORT_CONF');
40 JText::script('VBO_GEO_FAILED_REASON');
41 JText::script('VBO_PLEASE_FILL_FIELDS');
42 ?>
43 <fieldset class="adminform">
44 <div class="vbo-params-wrap">
45 <legend class="adminlegend">
46 <span class="vbo-geo-legend-title"><?php VikBookingIcons::e('map-marked-alt'); ?> <?php echo JText::translate('VBO_GEO_INFO'); ?></span>
47 <?php
48 if ($geo_supported && count($geo_import_from)) {
49 ?>
50 <span class="vbo-geo-legend-import">
51 <select id="vbo-geo-importfrom" onchange="vboImportGeoConfig(this.value);">
52 <option></option>
53 <?php
54 foreach ($geo_import_from as $impfrom) {
55 ?>
56 <option value="<?php echo $impfrom['id']; ?>"><?php echo $impfrom['name']; ?></option>
57 <?php
58 }
59 ?>
60 </select>
61 </span>
62 <?php
63 }
64 ?>
65 </legend>
66 <div class="vbo-params-container">
67 <div class="vbo-param-container">
68 <div class="vbo-param-label"><?php echo JText::translate('VBO_ENABLE_GEOCODING'); ?></div>
69 <div class="vbo-param-setting">
70 <?php
71 $geo_enabled = (int)$geo->getRoomGeoParams($rooms_params, 'enabled', 0);
72 echo $vbo_app->printYesNoButtons('geo_enabled', JText::translate('VBYES'), JText::translate('VBNO'), $geo_enabled, 1, 0, 'vboToggleGeoParams();');
73 ?>
74 </div>
75 </div>
76 <?php
77 if (!$geo_supported) {
78 ?>
79 <div class="vbo-param-container vbo-geoinfo-param" style="<?php echo !$geo_enabled ? 'display: none;' : ''; ?>">
80 <div class="vbo-param-setting">
81 <p class="warn notice-noicon"><?php VikBookingIcons::e('exclamation-triangle'); ?> <?php echo JText::translate('VBO_GEO_UNSUPPORTED'); ?></p>
82 </div>
83 </div>
84 <?php
85 } else {
86 ?>
87 <div class="vbo-param-container vbo-geoinfo-param" style="display: none;">
88 <div class="vbo-param-label"><?php echo JText::translate('VBO_GEO_ADDRESS'); ?></div>
89 <div class="vbo-param-setting">
90 <input type="text" id="geo_address" value="<?php echo $geo->getRoomGeoParams($rooms_params, 'address', ''); ?>" />
91 <span class="vbo-param-setting-comment" id="geo_address_formatted"></span>
92 </div>
93 </div>
94 <div class="vbo-param-container vbo-geoinfo-param" style="display: none;">
95 <div class="vbo-param-label"><?php echo JText::translate('VBO_GEO_COORDS'); ?></div>
96 <div class="vbo-param-setting">
97 <span><?php echo JText::translate('VBO_GEO_COORDS_LAT'); ?></span>
98 <input type="number" id="geo_latitude" class="vbo-large-input-number" min="-90" max="90" step="any" value="<?php echo $geo->getRoomGeoParams($rooms_params, 'latitude', '43.7734385'); ?>" />
99 <span><?php echo JText::translate('VBO_GEO_COORDS_LNG'); ?></span>
100 <input type="number" id="geo_longitude" class="vbo-large-input-number" min="-180" max="180" step="any" value="<?php echo $geo->getRoomGeoParams($rooms_params, 'longitude', '11.2565501'); ?>" />
101 </div>
102 </div>
103 <div class="vbo-param-container vbo-geoinfo-param" style="display: none;">
104 <div class="vbo-param-label"><?php echo JText::translate('VBO_GEO_ZOOM'); ?></div>
105 <div class="vbo-param-setting">
106 <input type="number" id="geo_zoom" min="0" step="any" value="<?php echo (int)$geo->getRoomGeoParams($rooms_params, 'zoom', 3); ?>" />
107 </div>
108 </div>
109 <div class="vbo-param-container vbo-geoinfo-param" style="display: none;">
110 <div class="vbo-param-label"><?php echo JText::translate('VBO_GEO_MTYPE'); ?></div>
111 <div class="vbo-param-setting">
112 <?php
113 $geo_mtype = $geo->getRoomGeoParams($rooms_params, 'mtype', 'roadmap');
114 ?>
115 <select id="geo_mtype">
116 <option value="roadmap"<?php echo $geo_mtype == 'roadmap' ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBO_GEO_MTYPE_ROADMAP'); ?></option>
117 <option value="satellite"<?php echo $geo_mtype == 'satellite' ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBO_GEO_MTYPE_SATELLITE'); ?></option>
118 <option value="hybrid"<?php echo $geo_mtype == 'hybrid' ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBO_GEO_MTYPE_HYBRID'); ?></option>
119 <option value="terrain"<?php echo $geo_mtype == 'terrain' ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBO_GEO_MTYPE_TERRAIN'); ?></option>
120 </select>
121 </div>
122 </div>
123 <div class="vbo-param-container vbo-geoinfo-param" style="display: none;">
124 <div class="vbo-param-label"><?php echo JText::translate('VBO_GEO_MAP_HEIGHT'); ?></div>
125 <div class="vbo-param-setting">
126 <input type="number" id="geo_height" min="50" max="3500" step="5" value="<?php echo $geo->getRoomGeoParams($rooms_params, 'height', 300); ?>" /> px
127 </div>
128 </div>
129 <div class="vbo-param-container vbo-geoinfo-param" style="display: none;">
130 <div class="vbo-param-setting">
131 <div id="vbo-geo-map" style="width: 100%; height: <?php echo $geo->getRoomGeoParams($rooms_params, 'height', 300); ?>px;"></div>
132 <?php
133 // main room marker position
134 $main_marker_lat = $geo->getRoomGeoParams($rooms_params, 'marker_lat', '');
135 $main_marker_lng = $geo->getRoomGeoParams($rooms_params, 'marker_lng', '');
136 $main_marker_hide = $geo->getRoomGeoParams($rooms_params, 'marker_hide', '');
137 $main_marker_pos = null;
138 if (empty($main_marker_hide) && !empty($main_marker_lat) && !empty($main_marker_lng)) {
139 // prepare main marker (for base address) object for js
140 $main_marker_pos = new stdClass;
141 $main_marker_pos->lat = (float)$main_marker_lat;
142 $main_marker_pos->lng = (float)$main_marker_lng;
143 }
144 ?>
145 <input type="hidden" id="geo_marker_lat" value="<?php echo $main_marker_lat; ?>" />
146 <input type="hidden" id="geo_marker_lng" value="<?php echo $main_marker_lng; ?>" />
147 <input type="hidden" id="geo_marker_hide" value="<?php echo $main_marker_hide; ?>" />
148 </div>
149 </div>
150 <div class="vbo-param-container vbo-geoinfo-param" style="display: none;">
151 <div class="vbo-param-label">
152 <?php
153 echo $vbo_app->createPopover(array('title' => JText::translate('VBO_GEO_MARKERS_ONEMULTI'), 'content' => JText::translate('VBO_GEO_MARKERS_ONEMULTI_HELP'), 'icon_class' => VikBookingIcons::i('map-marker-alt')));
154 echo ' ' . JText::translate('VBO_GEO_MARKERS_ONEMULTI');
155 ?>
156 </div>
157 <div class="vbo-param-setting">
158 <?php
159 $geo_markers_multi = $geo->getRoomGeoParams($rooms_params, 'markers_multi', 0);
160 ?>
161 <select id="geo_markers_multi">
162 <option value="0"<?php echo $geo_markers_multi == 0 ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBO_GEO_MARKERS_ONE'); ?></option>
163 <option value="1"<?php echo $geo_markers_multi == 1 ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBO_GEO_MARKERS_MULTI'); ?></option>
164 </select>
165 </div>
166 </div>
167 <div class="vbo-param-container vbo-geoinfo-param" style="display: none;">
168 <div class="vbo-param-setting vbo-geoinfo-markers-wrap"></div>
169 </div>
170 <div class="vbo-param-container vbo-geoinfo-param" style="display: none;">
171 <div class="vbo-param-label">
172 <?php
173 echo $vbo_app->createPopover(array('title' => JText::translate('VBO_GEO_MAP_GOVERLAY'), 'content' => JText::translate('VBO_GEO_MAP_GOVERLAY_HELP'), 'icon_class' => VikBookingIcons::i('images')));
174 echo ' ' . JText::translate('VBO_GEO_MAP_GOVERLAY');
175 ?>
176 </div>
177 <div class="vbo-param-setting">
178 <?php
179 $goverlay_enabled = (int)$geo->getRoomGeoParams($rooms_params, 'goverlay', 0);
180 echo $vbo_app->printYesNoButtons('geo_goverlay', JText::translate('VBYES'), JText::translate('VBNO'), $goverlay_enabled, 1, 0, 'vboToggleGeoGroundOverlay();');
181 ?>
182 <button type="button" id="vbo-goverlay-edit" class="btn vbo-config-btn" style="<?php echo !$goverlay_enabled ? 'display: none;' : ''; ?>" onclick="vboToggleGeoGroundOverlay();"><?php VikBookingIcons::e('images'); ?> <?php echo JText::translate('VBMAINPAYMENTSEDIT'); ?></button>
183 </div>
184 </div>
185 <?php
186 }
187 ?>
188 </div>
189 </div>
190 </fieldset>
191
192 <script type="text/javascript">
193 function vboToggleGeoParams() {
194 if (jQuery('input[name="geo_enabled"]').is(':checked')) {
195 jQuery('.vbo-geoinfo-param').show();
196
197 // make a silent AJAX request only once to prepare the temporary record used to store the room geo information
198 var room_id = <?php echo count($this->row) ? $this->row['id'] : '0'; ?>;
199 if (room_id < 1) {
200 var call_method = 'initRoomGeoTransient';
201 jQuery.ajax({
202 type: "POST",
203 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=geocoding_endpoint'); ?>",
204 data: {
205 tmpl: "component",
206 callback: call_method,
207 room_id: room_id
208 }
209 }).done(function(response) {
210 try {
211 var obj_res = typeof response === 'string' ? JSON.parse(response) : response;
212 if (!obj_res.hasOwnProperty(call_method)) {
213 console.error('initRoomGeoTransient unexpected JSON response', obj_res);
214 } else {
215 // console.log(obj_res[call_method]);
216 }
217 } catch(err) {
218 console.error('initRoomGeoTransient could not parse JSON response', err, response);
219 }
220 }).fail(function(err) {
221 console.error('initRoomGeoTransient request failed', err);
222 });
223 }
224 //
225 } else {
226 jQuery('.vbo-geoinfo-param').hide();
227 }
228 }
229 </script>
230 <?php
231 if ($geo_supported) {
232 $current_units_pos = $geo->getRoomGeoParams($rooms_params, 'units_pos');
233 $current_units_pos = is_object($current_units_pos) ? $current_units_pos : (new stdClass);
234 $current_goverlay = null;
235 if ((int)$geo->getRoomGeoParams($rooms_params, 'goverlay', 0)) {
236 $current_overlay_img = $geo->getRoomGeoParams($rooms_params, 'overlay_img', '');
237 $current_overlay_south = $geo->getRoomGeoParams($rooms_params, 'overlay_south');
238 $current_overlay_west = $geo->getRoomGeoParams($rooms_params, 'overlay_west');
239 $current_overlay_north = $geo->getRoomGeoParams($rooms_params, 'overlay_north');
240 $current_overlay_east = $geo->getRoomGeoParams($rooms_params, 'overlay_east');
241 if (!empty($current_overlay_img) && !empty($current_overlay_south)) {
242 // ground overlay is available
243 $current_goverlay = new stdClass;
244 $current_goverlay->url = $current_overlay_img;
245 $current_goverlay->south = (float)$current_overlay_south;
246 $current_goverlay->west = (float)$current_overlay_west;
247 $current_goverlay->north = (float)$current_overlay_north;
248 $current_goverlay->east = (float)$current_overlay_east;
249 }
250 }
251 ?>
252 <script type="text/javascript">
253 /**
254 * Define global scope vars
255 */
256 var vbo_geomap = null,
257 vbo_geomap_saving = false,
258 vbo_geocoder = null,
259 vbo_geomarker_room = null,
260 vbo_geomarker_room_pos = <?php echo is_object($main_marker_pos) ? json_encode($main_marker_pos) : 'null'; ?>,
261 vbo_info_marker_room = null,
262 vbo_geomarker_units = {},
263 vbo_geomarker_units_pos = <?php echo json_encode($current_units_pos); ?>,
264 vbo_info_markers = {},
265 vbo_current_marker_index = null,
266 vbo_icn_marker_in = '<?php echo VikBookingIcons::i('map-marked-alt', 'vbo-geo-marker-icn-in'); ?>',
267 vbo_icn_marker_out = '<?php echo VikBookingIcons::i('map', 'vbo-geo-marker-icn-out'); ?>',
268 vbo_marker_base_uri = '<?php echo JUri::root(); ?>',
269 vbo_doing_ground_overlay = false,
270 vbo_ground_overlay = null,
271 vbo_dbground_overlay = <?php echo is_object($current_goverlay) ? json_encode($current_goverlay) : 'null'; ?>;
272
273 var vbo_modal_geomap_on = false;
274
275 /**
276 * Delay saving button if saving the geomap
277 */
278 Joomla.submitbutton = function(task) {
279 if (vbo_geomap_saving && (task.indexOf('updateroom') >= 0 || task.indexOf('createroom') >= 0)) {
280 console.log('Delay saving to allow geomap saving');
281 setTimeout(function() {
282 Joomla.submitform(task, document.adminForm);
283 }, 750);
284 } else {
285 Joomla.submitform(task, document.adminForm);
286 }
287 }
288
289 /**
290 * Shows the modal window
291 */
292 function vboOpenModalGeoMap() {
293 jQuery('.vbo-modal-overlay-block-geomap').show();
294 vbo_modal_geomap_on = true;
295 }
296
297 /**
298 * Hides the modal window
299 */
300 function hideVboModalGeoMap() {
301 if (vbo_modal_geomap_on === true) {
302 jQuery(".vbo-modal-overlay-block-geomap").fadeOut(400, function () {
303 jQuery(".vbo-modal-overlay-content-geomap").show();
304 });
305 // turn flag off
306 vbo_modal_geomap_on = false;
307 }
308 if (vbo_doing_ground_overlay === true) {
309 if (vbo_ground_overlay === null) {
310 // no overlay set, make sure to deselect the toggle
311 jQuery('input[name="geo_goverlay"]').prop('checked', false);
312 }
313 // turn flag off
314 vbo_doing_ground_overlay = false;
315 }
316 }
317
318 /**
319 * Converts an RGB (or RGBA) color to hexadecimal
320 */
321 function vboRgb2Hex(rgb) {
322 var rgb_match = rgb.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([0-9.]+))?\)$/);
323 if (!rgb_match) {
324 return rgb;
325 }
326 return "#" + vboHex(rgb_match[1]) + vboHex(rgb_match[2]) + vboHex(rgb_match[3]);
327 }
328
329 /**
330 * Composes RGB (or RGBA) color values to hexadecimal
331 */
332 function vboHex(x) {
333 var vboHexDigits = new Array ("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f");
334 return isNaN(x) ? "00" : vboHexDigits[(x - x % 16) / 16] + vboHexDigits[x % 16];
335 }
336
337 /**
338 * Debounce technique to group a flurry of events into one single event.
339 * This is useful for listening to the key up event of the address.
340 */
341 function vboDebounceEvent(func, wait, immediate) {
342 var timeout;
343 return function() {
344 var context = this, args = arguments;
345 var later = function() {
346 timeout = null;
347 if (!immediate) func.apply(context, args);
348 };
349 var callNow = immediate && !timeout;
350 clearTimeout(timeout);
351 timeout = setTimeout(later, wait);
352 if (callNow) {
353 func.apply(context, args);
354 }
355 }
356 }
357
358 /**
359 * Fires when the document is ready. Renders the entire map.
360 */
361 function vboInitGeoMap() {
362 // default map options
363 var def_map_options = {
364 center: new google.maps.LatLng(jQuery('#geo_latitude').val(), jQuery('#geo_longitude').val()),
365 zoom: parseFloat(jQuery('#geo_zoom').val()),
366 mapId: 'vbo_map',
367 mapTypeId: jQuery('#geo_mtype').val()
368 };
369 // initialize Map
370 vbo_geomap = new google.maps.Map(document.getElementById('vbo-geo-map'), def_map_options);
371 // add listeners to map
372 vbo_geomap.addListener('bounds_changed', function() {
373 // update zoom
374 jQuery('#geo_zoom').val(vbo_geomap.getZoom());
375 // update center coordinates
376 var center_bounds = vbo_geomap.getBounds().getCenter();
377 jQuery('#geo_latitude').val(center_bounds.lat());
378 jQuery('#geo_longitude').val(center_bounds.lng());
379 // trigger the geo-saving event
380 document.dispatchEvent(new Event('vbo-room-geosaving'));
381 });
382 vbo_geomap.addListener('maptypeid_changed', function() {
383 // this will return the constant of the mapTypeId
384 var maptypeid = vbo_geomap.getMapTypeId();
385 jQuery('#geo_mtype').val((maptypeid + '').toLowerCase());
386 // trigger the geo-saving event
387 document.dispatchEvent(new Event('vbo-room-geosaving'));
388 });
389 vbo_geomap.addListener('click', function(e) {
390 // handle the map click by passing the position
391 vboGeoMapClickedPos(e.latLng);
392 });
393 // initialize Geocoder
394 vbo_geocoder = new google.maps.Geocoder();
395 // set current default marker for main room
396 if (vbo_geomarker_room_pos !== null) {
397 // create infowindow
398 vbo_info_marker_room = new google.maps.InfoWindow({
399 content: vboGenerateMainInfoMarkerContent(),
400 disableAutoPan: true
401 });
402 // add map marker for base room-type
403 vbo_geomarker_room = new google.maps.marker.AdvancedMarkerElement({
404 map: vbo_geomap,
405 position: {
406 lat: parseFloat(vbo_geomarker_room_pos.lat),
407 lng: parseFloat(vbo_geomarker_room_pos.lng)
408 },
409 title: jQuery('input[name="cname"]').val()
410 });
411 // add listener to marker
412 vbo_geomarker_room.addListener('dragend', function() {
413 // update lat and lng of main room's marker
414 var current_lat = vbo_geomarker_room.getPosition().lat();
415 var current_lng = vbo_geomarker_room.getPosition().lng();
416 jQuery('#geo_marker_lat').val(current_lat);
417 jQuery('#geo_marker_lng').val(current_lng);
418 // update the same information in the marker position
419 if (vbo_geomarker_room_pos !== null) {
420 vbo_geomarker_room_pos['lat'] = current_lat;
421 vbo_geomarker_room_pos['lng'] = current_lng;
422 }
423 // trigger the geo-saving event
424 document.dispatchEvent(new Event('vbo-room-geosaving'));
425 });
426 vbo_geomarker_room.addListener('click', function() {
427 // close any other open infowindow first
428 for (var m in vbo_info_markers) {
429 if (!vbo_info_markers.hasOwnProperty(m)) {
430 continue;
431 }
432 vbo_info_markers[m].close();
433 }
434 vbo_info_marker_room.open(vbo_geomap, vbo_geomarker_room);
435 });
436
437 }
438 // populate current markers, if any
439 vboPopulateMapMarkers();
440 // populate ground overlay image, if set
441 if (vbo_dbground_overlay !== null) {
442 // compose LatLngBounds object
443 var overlay_bounds = new google.maps.LatLngBounds(
444 new google.maps.LatLng(parseFloat(vbo_dbground_overlay.south), parseFloat(vbo_dbground_overlay.west)),
445 new google.maps.LatLng(parseFloat(vbo_dbground_overlay.north), parseFloat(vbo_dbground_overlay.east))
446 );
447 // update ground overlay object
448 vbo_ground_overlay = new google.maps.GroundOverlay(vbo_dbground_overlay.url, overlay_bounds);
449 // set the overlay to the map
450 vbo_ground_overlay.setMap(vbo_geomap);
451 }
452 }
453
454 /**
455 * Simple way for centering the map, but this won't fit all marker bounds.
456 */
457 function vboSetGeoMapCenter(lat, lng) {
458 if (vbo_geomap === null) {
459 console.error('map is null');
460 return false;
461 }
462 if (isNaN(lat) || isNaN(lng)) {
463 console.error('latitude and longitude must be numbers', lat, lng);
464 return false;
465 }
466 vbo_geomap.setCenter(new google.maps.LatLng(lat, lng));
467 }
468
469 /**
470 * Gets the position of all markers to extend the bounds,
471 * and then sets the zoom and bounds to fit them all.
472 */
473 function vboGeoMapCenterBounds() {
474 if (vbo_geomap === null) {
475 console.error('map is null');
476 return false;
477 }
478 // set map center and zoom automatically
479 var latlngbounds = new google.maps.LatLngBounds();
480 // check if main address marker is in the map
481 if (vbo_geomarker_room !== null) {
482 latlngbounds.extend(vbo_geomarker_room.getPosition());
483 }
484 // parse all room-units markers
485 for (var i in vbo_geomarker_units) {
486 if (!vbo_geomarker_units.hasOwnProperty(i)) {
487 continue;
488 }
489 latlngbounds.extend(vbo_geomarker_units[i].getPosition());
490 }
491 // apply calculated center and bounds
492 vbo_geomap.setCenter(latlngbounds.getCenter());
493 vbo_geomap.fitBounds(latlngbounds);
494 }
495
496 /**
497 * Fires when typing an address to geocode it within the map.
498 */
499 function vboAddressGeocoding() {
500 // reset formatted address
501 jQuery('#geo_address_formatted').text('');
502
503 var address = jQuery('#geo_address').val();
504 if (!address.length) {
505 return;
506 }
507 vbo_geocoder.geocode({'address': address}, function(results, status) {
508 if (status == 'OK') {
509 if (results.length > 1) {
510 console.log('multiple address results returned', results);
511 }
512 // populate formatted address
513 if (results[0].hasOwnProperty('formatted_address')) {
514 jQuery('#geo_address_formatted').text(results[0].formatted_address);
515 }
516 // update coordinates
517 jQuery('#geo_latitude').val(results[0].geometry.location.lat());
518 jQuery('#geo_longitude').val(results[0].geometry.location.lng());
519 // remove any previously added marker for main room
520 if (vbo_geomarker_room !== null) {
521 vbo_geomarker_room.setMap(null);
522 }
523 // create infowindow
524 vbo_info_marker_room = new google.maps.InfoWindow({
525 content: vboGenerateMainInfoMarkerContent(),
526 disableAutoPan: true
527 });
528 // add map marker for base room-type
529 vbo_geomarker_room = new google.maps.marker.AdvancedMarkerElement({
530 map: vbo_geomap,
531 position: results[0].geometry.location,
532 title: jQuery('input[name="cname"]').val()
533 });
534 // set new coords for marker
535 jQuery('#geo_marker_lat').val(results[0].geometry.location.lat());
536 jQuery('#geo_marker_lng').val(results[0].geometry.location.lng());
537 jQuery('#geo_marker_hide').val('');
538 // add listener to marker
539 vbo_geomarker_room.addListener('dragend', function() {
540 // update lat and lng of main room's marker
541 var current_lat = vbo_geomarker_room.getPosition().lat();
542 var current_lng = vbo_geomarker_room.getPosition().lng();
543 jQuery('#geo_marker_lat').val(current_lat);
544 jQuery('#geo_marker_lng').val(current_lng);
545 // update the same information in the marker position
546 if (vbo_geomarker_room_pos === null) {
547 vbo_geomarker_room_pos = {};
548 }
549 vbo_geomarker_room_pos['lat'] = current_lat;
550 vbo_geomarker_room_pos['lng'] = current_lng;
551 // trigger the geo-saving event
552 document.dispatchEvent(new Event('vbo-room-geosaving'));
553 });
554 vbo_geomarker_room.addListener('click', function() {
555 // close any other open infowindow first
556 for (var m in vbo_info_markers) {
557 if (!vbo_info_markers.hasOwnProperty(m)) {
558 continue;
559 }
560 vbo_info_markers[m].close();
561 }
562 vbo_info_marker_room.open(vbo_geomap, vbo_geomarker_room);
563 });
564 // set map center and zoom automatically
565 vboGeoMapCenterBounds();
566 } else {
567 alert(Joomla.JText._('VBO_GEO_FAILED_REASON') + ' ' + status);
568 }
569 });
570 }
571
572 /**
573 * Given all the current positions, adds the current markers to the map and register the events.
574 */
575 function vboPopulateMapMarkers() {
576 // always reset markers pool and remove them from map
577 for (var i in vbo_geomarker_units) {
578 if (!vbo_geomarker_units.hasOwnProperty(i)) {
579 continue;
580 }
581 // remove current marker from map
582 vbo_geomarker_units[i].setMap(null);
583 }
584 // reset vars
585 vbo_geomarker_units = {};
586 vbo_info_markers = {};
587 // empty markers wrap
588 jQuery('.vbo-geoinfo-markers-wrap').html('');
589 // calculate limits
590 var multi_markers = jQuery('#geo_markers_multi').val();
591 var room_units = jQuery('#room_units').val();
592 var tot_markers = multi_markers > 0 && room_units > 1 ? room_units : 1;
593 tot_markers = parseInt(tot_markers);
594 // iterate through markers to add and display
595 for (var i = 1; i <= tot_markers; i++) {
596 var marker_options = null;
597 var marker_title = Joomla.JText._('VBODISTFEATURERUNIT') + (i + '');
598 if (tot_markers === 1 && jQuery('input[name="cname"]').val().length) {
599 marker_title = jQuery('input[name="cname"]').val();
600 }
601 if (vbo_geomarker_units_pos.hasOwnProperty(i)) {
602 // marker index saved
603 marker_options = {
604 gmpDraggable: true,
605 map: vbo_geomap,
606 position: {
607 lat: parseFloat(vbo_geomarker_units_pos[i].lat),
608 lng: parseFloat(vbo_geomarker_units_pos[i].lng)
609 },
610 title: marker_title
611 };
612 // register custom unit property
613 let marker_vbo_unit = i;
614 // check if we know a custom icon for this marker
615 if (vbo_geomarker_units_pos[i].hasOwnProperty('icon')) {
616 // custom icon defined, identify the type to build a node
617 if (vbo_geomarker_units_pos[i]['icon']?.url) {
618 // we've got an image
619 let marker_img = document.createElement('img');
620 marker_img.src = vbo_geomarker_units_pos[i]['icon'].url;
621 if (vbo_geomarker_units_pos[i]['icon']?.scaledSize?.width) {
622 marker_img.style.width = vbo_geomarker_units_pos[i]['icon'].scaledSize.width + 'px';
623 }
624 if (vbo_geomarker_units_pos[i]['icon']?.scaledSize?.height) {
625 marker_img.style.height = vbo_geomarker_units_pos[i]['icon'].scaledSize.height + 'px';
626 }
627 // center the image
628 marker_img.style.transform = 'translate(0, 50%)';
629 // set marker content
630 marker_options['content'] = marker_img;
631 } else if (vbo_geomarker_units_pos[i]['icon']?.path) {
632 // we've got an SVG path symbol
633 let svgns = 'http://www.w3.org/2000/svg';
634 let svg = document.createElementNS(svgns, 'svg');
635 svg.setAttribute('xmlns', svgns);
636 svg.setAttribute('viewBox', '0 0 90 90');
637 svg.setAttribute('width', 90);
638 svg.setAttribute('height', 90);
639 let path = document.createElementNS(svgns, 'path');
640 path.setAttribute('d', vbo_geomarker_units_pos[i]['icon'].path);
641 path.setAttribute('fill', vbo_geomarker_units_pos[i]['icon']?.fillColor || '#eeeeee');
642 path.setAttribute('fill-opacity', vbo_geomarker_units_pos[i]['icon']?.fillOpacity || 0.9);
643 path.setAttribute('stroke-width', vbo_geomarker_units_pos[i]['icon']?.strokeWeight || 0);
644 if (vbo_geomarker_units_pos[i]['icon']?.scale && vbo_geomarker_units_pos[i]['icon']?.anchor?.x && vbo_geomarker_units_pos[i]['icon']?.anchor?.y) {
645 path.setAttribute(
646 'transform',
647 'translate(' + vbo_geomarker_units_pos[i]['icon'].anchor.x + ', ' + vbo_geomarker_units_pos[i]['icon'].anchor.y + ') scale(' + vbo_geomarker_units_pos[i]['icon'].scale + ')'
648 );
649 }
650 svg.setAttribute('data-vbo-icon-id', vbo_geomarker_units_pos[i]['icon']?.vbo_icon_id);
651 svg.appendChild(path);
652 // set marker content
653 marker_options['content'] = svg;
654 }
655 }
656 // create marker infowindow
657 var vbo_info_marker_cont = vboGenerateInfoMarkerContent(i, marker_title);
658 var vbo_info_marker = new google.maps.InfoWindow({
659 content: vbo_info_marker_cont,
660 disableAutoPan: true
661 });
662 // add unit marker to map
663 var vbo_geomarker_runit = new google.maps.marker.AdvancedMarkerElement(marker_options);
664 // add listener to marker
665 vbo_geomarker_runit.addListener('dragend', function(e) {
666 // update lat and lng of room unit marker
667 var current_lat = e.latLng.lat();
668 var current_lng = e.latLng.lng();
669 if (vbo_geomarker_units_pos.hasOwnProperty(marker_vbo_unit)) {
670 vbo_geomarker_units_pos[marker_vbo_unit]['lat'] = current_lat;
671 vbo_geomarker_units_pos[marker_vbo_unit]['lng'] = current_lng;
672 }
673 // trigger the geo-saving event
674 document.dispatchEvent(new Event('vbo-room-geosaving'));
675 });
676 vbo_geomarker_runit.addListener('click', function() {
677 if (marker_vbo_unit && vbo_info_markers.hasOwnProperty(marker_vbo_unit)) {
678 // close any other open infowindow first
679 for (var m in vbo_info_markers) {
680 if (!vbo_info_markers.hasOwnProperty(m) || m == marker_vbo_unit) {
681 continue;
682 }
683 vbo_info_markers[m].close();
684 }
685 if (vbo_geomarker_room !== null && vbo_info_marker_room !== null) {
686 // close address marker infowindow
687 vbo_info_marker_room.close();
688 }
689 vbo_info_markers[marker_vbo_unit].open(vbo_geomap, this);
690 } else {
691 console.error('info marker not found', this);
692 }
693 });
694 // register marker to pool
695 vbo_geomarker_units[i] = vbo_geomarker_runit;
696 // register info window
697 vbo_info_markers[i] = vbo_info_marker;
698 }
699 // build marker settings GUI
700 var marker_setting_wrap = '';
701 marker_setting_wrap += '<div class="vbo-geoinfo-marker-cont ' + (marker_options === null ? 'vbo-geoinfo-marker-out' : 'vbo-geoinfo-marker-in') + '" data-markerindex="' + i + '">';
702 marker_setting_wrap += ' <div class="vbo-geoinfo-marker-label">' + marker_title + '</div>';
703 marker_setting_wrap += ' <div class="vbo-geoinfo-marker-cmd">';
704 var marker_btn_icn = '';
705 if (marker_options === null) {
706 marker_btn_icn = '<i class="' + vbo_icn_marker_out + '"></i>';
707 } else {
708 marker_btn_icn = '<i class="' + vbo_icn_marker_in + '"></i>';
709 }
710 marker_setting_wrap += ' <button type="button" class="btn btn-small btn-secondary vbo-geomarker-status" onclick="vboManageRoomMarker(' + i + ');">' + marker_btn_icn + '</button>';
711 marker_setting_wrap += ' </div>';
712 marker_setting_wrap += '</div>';
713 // append marker settings
714 jQuery('.vbo-geoinfo-markers-wrap').append(marker_setting_wrap);
715 }
716 }
717
718 /**
719 * Adds a new marker to the map, or opens its info window if the marker index exists already.
720 */
721 function vboManageRoomMarker(index, position) {
722 if (!index || isNaN(index)) {
723 console.error('vboManageRoomMarker invalid argument', index);
724 return false;
725 }
726 if (vbo_geomap === null) {
727 console.error('map is empty');
728 return false;
729 }
730 if (vbo_geomarker_units.hasOwnProperty(index)) {
731 // close any other open infowindow first
732 for (var m in vbo_info_markers) {
733 if (!vbo_info_markers.hasOwnProperty(m) || m == index) {
734 continue;
735 }
736 vbo_info_markers[m].close();
737 }
738 if (vbo_geomarker_room !== null && vbo_info_marker_room !== null) {
739 // close address marker infowindow
740 vbo_info_marker_room.close();
741 }
742 // marker exists for this room-unit, open its infowindow
743 vbo_info_markers[index].open(vbo_geomap, vbo_geomarker_units[index]);
744 } else {
745 // marker does not exist, add it to map
746 var map_center = vbo_geomap.getCenter();
747 if (!map_center) {
748 console.error('Map center or bounds have not been set');
749 return false;
750 }
751 if (!position) {
752 // calculate new random position from current center and zoom level
753 var center_lat = map_center.lat();
754 var decims_lat = center_lat - parseInt(center_lat);
755 var center_lng = map_center.lng();
756 var decims_lng = center_lng - parseInt(center_lng);
757 var current_zoom = vbo_geomap.getZoom();
758 var increase = ((current_zoom + 1) * 2) / 100000;
759 decims_lat += increase;
760 decims_lng += increase;
761 position = {
762 lat: (parseInt(center_lat) + decims_lat),
763 lng: (parseInt(center_lng) + decims_lng)
764 }
765 }
766 // create marker
767 var marker_options = {
768 gmpDraggable: true,
769 map: vbo_geomap,
770 position: position,
771 title: Joomla.JText._('VBODISTFEATURERUNIT') + (index + '')
772 };
773 // register custom unit property
774 let marker_vbo_unit = index;
775 // create marker infowindow
776 var vbo_info_marker_cont = vboGenerateInfoMarkerContent(index, null);
777 var vbo_info_marker = new google.maps.InfoWindow({
778 content: vbo_info_marker_cont,
779 disableAutoPan: true
780 });
781 // add unit marker to map
782 var vbo_geomarker_runit = new google.maps.marker.AdvancedMarkerElement(marker_options);
783 // add listener to marker
784 vbo_geomarker_runit.addListener('dragend', function(e) {
785 // update lat and lng of room unit marker
786 var current_lat = e.latLng.lat();
787 var current_lng = e.latLng.lng();
788 if (vbo_geomarker_units_pos.hasOwnProperty(marker_vbo_unit)) {
789 vbo_geomarker_units_pos[marker_vbo_unit]['lat'] = current_lat;
790 vbo_geomarker_units_pos[marker_vbo_unit]['lng'] = current_lng;
791 }
792 // trigger the geo-saving event
793 document.dispatchEvent(new Event('vbo-room-geosaving'));
794 });
795 vbo_geomarker_runit.addListener('click', function() {
796 if (marker_vbo_unit && vbo_info_markers.hasOwnProperty(marker_vbo_unit)) {
797 // close any other open infowindow first
798 for (var m in vbo_info_markers) {
799 if (!vbo_info_markers.hasOwnProperty(m) || m == marker_vbo_unit) {
800 continue;
801 }
802 vbo_info_markers[m].close();
803 }
804 if (vbo_geomarker_room !== null && vbo_info_marker_room !== null) {
805 // close address marker infowindow
806 vbo_info_marker_room.close();
807 }
808 vbo_info_markers[marker_vbo_unit].open(vbo_geomap, this);
809 } else {
810 console.error('info marker not found', this);
811 }
812 });
813 // register marker to pool
814 vbo_geomarker_units[index] = vbo_geomarker_runit;
815 // register marker position
816 if (typeof position.lat == 'function') {
817 // position may be a LatLng object, convert it to number properties
818 var prop_position = {
819 lat: position.lat(),
820 lng: position.lng()
821 };
822 position = prop_position;
823 }
824 vbo_geomarker_units_pos[index] = position;
825 // register info window
826 vbo_info_markers[index] = vbo_info_marker;
827 // center map to fit all markers
828 vboGeoMapCenterBounds();
829 // update marker settings status
830 var settings_btn = jQuery('.vbo-geoinfo-marker-cont[data-markerindex="' + index + '"]').find('.vbo-geoinfo-marker-cmd').find('.vbo-geomarker-status');
831 if (settings_btn && settings_btn.length) {
832 settings_btn.find('i').removeClass().addClass(vbo_icn_marker_in);
833 jQuery('.vbo-geoinfo-marker-cont[data-markerindex="' + index + '"]').removeClass('vbo-geoinfo-marker-out').addClass('vbo-geoinfo-marker-in');
834 }
835 // trigger the geo-saving event
836 document.dispatchEvent(new Event('vbo-room-geosaving'));
837 }
838 }
839
840 /**
841 * Prompts the confirmation for removing a room-unit marker from the map
842 */
843 function vboRemoveMarker(index) {
844 if (confirm(Joomla.JText._('VBO_GEO_RMCONF_MARKER'))) {
845 // remove marker from map
846 vbo_geomarker_units[index].setMap(null);
847 // remove object properties
848 delete vbo_geomarker_units[index];
849 delete vbo_geomarker_units_pos[index];
850 delete vbo_info_markers[index];
851 // update marker settings status
852 var settings_btn = jQuery('.vbo-geoinfo-marker-cont[data-markerindex="' + index + '"]').find('.vbo-geoinfo-marker-cmd').find('.vbo-geomarker-status');
853 if (settings_btn && settings_btn.length) {
854 settings_btn.find('i').removeClass().addClass(vbo_icn_marker_out);
855 jQuery('.vbo-geoinfo-marker-cont[data-markerindex="' + index + '"]').removeClass('vbo-geoinfo-marker-in').addClass('vbo-geoinfo-marker-out');
856 }
857 // trigger the geo-saving event
858 document.dispatchEvent(new Event('vbo-room-geosaving'));
859 }
860 }
861
862 /**
863 * Prompts the confirmation for removing the address marker from the map
864 */
865 function vboRemoveAddressMarker() {
866 if (vbo_geomarker_room === null) {
867 return false;
868 }
869 if (confirm(Joomla.JText._('VBO_GEO_RMCONF_MARKER'))) {
870 // remove marker from map
871 vbo_geomarker_room.setMap(null);
872 // unset object properties
873 vbo_geomarker_room = null;
874 vbo_geomarker_room_pos = null;
875 // set main marker for hiding
876 jQuery('#geo_marker_hide').val('1');
877 // trigger the geo-saving event
878 document.dispatchEvent(new Event('vbo-room-geosaving'));
879 }
880 }
881
882 /**
883 * Opens the modal to customize the currently selected map marker
884 */
885 function vboCustomizeMarker(index) {
886 if (!vbo_geomarker_units.hasOwnProperty(index)) {
887 console.error('marker not found from given index', index, vbo_geomarker_units);
888 return false;
889 }
890 // update current marker index
891 vbo_current_marker_index = index;
892 // set proper modal title
893 jQuery('#vbo-modal-geomap-title').html('<?php VikBookingIcons::e('map-marker-alt'); ?> ' + Joomla.JText._('VBO_GEO_CUSTOMIZE_MARKER'));
894 // open modal
895 jQuery('.vbo-cust-geo-overlay-wrapper').hide();
896 jQuery('.vbo-cust-geo-marker-wrapper').show();
897 vboOpenModalGeoMap();
898 // populate current values of the modal depending on the current marker icon type
899 var current_icon = vbo_geomarker_units[index]?.content;
900 if (!current_icon) {
901 // default icon of Google Maps (null/undefined content)
902 jQuery('#vbo-geo-marker-icntype').val('google').trigger('change');
903 } else if (current_icon.matches('svg') && current_icon.querySelector('path')) {
904 // SVG path symbol
905 jQuery('#vbo-geo-marker-icntype').val('symbol').trigger('change');
906 // deselect all SVG icons
907 jQuery('.vbo-cust-geo-marker-svg').attr('data-markerapply', '0');
908 // get path node
909 let svg_path = current_icon.querySelector('path');
910 let svg_icon_id = current_icon.getAttribute('data-vbo-icon-id');
911 // select current SVG icon by using the custom property "vbo_icon_id" (changed to a custom data-attribute)
912 if (svg_icon_id) {
913 jQuery('.vbo-cust-geo-marker-svg[data-markerid="' + svg_icon_id + '"]').trigger('click');
914 }
915 // access the fill and opacity values
916 let svg_path_fill = svg_path.getAttribute('fill');
917 let svg_path_fill_opacity = svg_path.getAttribute('fill-opacity');
918 // populate additional default values
919 if (svg_path_fill) {
920 jQuery('#vbo-geo-marker-fillcolor').val(svg_path_fill);
921 jQuery('.vbo-inspector-colorpicker').css('backgroundColor', svg_path_fill);
922 }
923 if (svg_path_fill_opacity && svg_path_fill_opacity >= 0) {
924 jQuery('#vbo-geo-marker-opacity').val(svg_path_fill_opacity);
925 }
926 } else if (current_icon.matches('img')) {
927 // icon URL (img)
928 jQuery('#vbo-geo-marker-icntype').val('icon').trigger('change');
929 // set width and height, if any
930 let icon_width = current_icon.width;
931 let icon_height = current_icon.height;
932 let icon_url = current_icon?.src || current_icon.getAttribute('src');
933 if (icon_width && icon_height) {
934 jQuery('#vbo-geo-marker-icon-width').val(icon_width);
935 jQuery('#vbo-geo-marker-icon-height').val(icon_height);
936 }
937 // try to populate the input value of the media manager
938 jQuery('input[name="marker_icon_img"]').val(icon_url.replace(vbo_marker_base_uri, ''));
939 // try to set the current image, if any (this changes from J to WP)
940 if (jQuery('.image-preview-wrapper').length) {
941 jQuery('.vbo-cust-geo-marker-wrapper').find('.image-preview-wrapper').html('<img src="' + icon_url + '" />').show();
942 }
943 }
944 }
945
946 /**
947 * Triggers when changing the type of icon for the marker
948 */
949 function vboCustomizeMarkerChangeIconType(type) {
950 // always hide all conditional fields
951 jQuery('.vbo-cust-geo-marker-bottom').hide();
952 if (jQuery('.vbo-cust-geo-marker-bottom[data-geomarker="' + type + '"]').length) {
953 // show requested type
954 jQuery('.vbo-cust-geo-marker-bottom[data-geomarker="' + type + '"]').show();
955 if (type == 'icon' || type == 'symbol') {
956 // lower the z-index of our modal to allow the media-manager modal or color-picker to be displayed on top
957 jQuery('.vbo-modal-overlay-block-geomap').addClass('vbo-modal-overlay-block-geomap-lowerzindex');
958 } else {
959 // restore the original z-index of our modal
960 jQuery('.vbo-modal-overlay-block-geomap').removeClass('vbo-modal-overlay-block-geomap-lowerzindex');
961 }
962 }
963 }
964
965 /**
966 * Fires when clicking on an SVG path symbol
967 */
968 function vboCustomizeMarkerSelectSymbol(elem) {
969 elem = jQuery(elem);
970 jQuery('.vbo-cust-geo-marker-svg').attr('data-markerapply', '0');
971 elem.attr('data-markerapply', '1');
972 if (elem.hasClass('vbo-cust-geo-marker-svg-addnew')) {
973 elem.hide();
974 jQuery('.vbo-cust-geo-marker-svg-newfield').show();
975 // hide all regular customization fields
976 jQuery('.vbo-cust-geo-marker-bottom[data-geomarker="symbol"]').find('.vbo-geo-marker-param-container').not('.vbo-cust-geo-marker-svg-newfield').hide();
977 } else {
978 jQuery('.vbo-cust-geo-marker-svg-addnew').show();
979 jQuery('.vbo-cust-geo-marker-svg-newfield').hide();
980 // restore all regular customization fields
981 jQuery('.vbo-cust-geo-marker-bottom[data-geomarker="symbol"]').find('.vbo-geo-marker-param-container').not('.vbo-cust-geo-marker-svg-newfield').show();
982 // populate default styling from selected symbol
983 var current_fill = elem.find('svg').css('fill');
984 var current_opacity = elem.find('svg').css('opacity');
985 if (current_fill && current_fill.length) {
986 jQuery('.vbo-inspector-colorpicker').css('backgroundColor', current_fill);
987 if (current_fill.indexOf('rgb') >= 0) {
988 current_fill = vboRgb2Hex(current_fill);
989 }
990 jQuery('#vbo-geo-marker-fillcolor').val(current_fill);
991 }
992 if (current_opacity && current_opacity > 0) {
993 jQuery('#vbo-geo-marker-opacity').val(current_opacity);
994 }
995 }
996 jQuery('.vbo-cust-geo-marker-svg-adjust').show();
997 }
998
999 /**
1000 * Fires on the keyup event of the textarea for the SVG path
1001 */
1002 function vboCustomizeMarkerSanitizeCustomSymbol() {
1003 var cur_path = jQuery('#vbo-newsvg-path').val();
1004 if (!cur_path.length) {
1005 return;
1006 }
1007 // check if we got a full path tag with the "d" attribute
1008 if ((/[^i]d="(.+)"/i).test(cur_path)) {
1009 // grab only what's inside the path attribute
1010 var matches = cur_path.match(/[^i]d="(.+)"/i);
1011 // replace the value with just the content of the path attribute
1012 jQuery('#vbo-newsvg-path').val(matches[1]);
1013 return;
1014 }
1015 // always replace invalid characters that may break the content of the SVG
1016 jQuery('#vbo-newsvg-path').val(cur_path.replace(/<|=|"|\/|>|\?/gi, ''));
1017 return;
1018 }
1019
1020 /**
1021 * Fires when adding a new custom SVG symbol path
1022 */
1023 function vboCustomizeMarkerAddCustomSymbol() {
1024 var cur_name = jQuery('#vbo-newsvg-name').val();
1025 var cur_path = jQuery('#vbo-newsvg-path').val();
1026 if (!cur_name.length || !cur_path.length) {
1027 alert(Joomla.JText._('VBO_PLEASE_FILL_FIELDS'));
1028 return false;
1029 }
1030 // compose ID for custom SVG
1031 var now = new Date;
1032 var svgid = now.getTime();
1033 // compose new SVG element
1034 var svgelem = '';
1035 svgelem += '<div class="vbo-cust-geo-marker-svg" data-markerapply="0" data-markerid="' + svgid + '" data-markergroup="custom" onclick="vboCustomizeMarkerSelectSymbol(this);">';
1036 svgelem += ' <div class="vbo-cust-geo-marker-svg-name">';
1037 svgelem += ' <span>' + cur_name + '</span>';
1038 svgelem += ' </div>';
1039 svgelem += ' <div class="vbo-cust-geo-marker-svg-icon">';
1040 svgelem += ' <svg>';
1041 svgelem += ' <path d="' + cur_path + '" />';
1042 svgelem += ' </svg>';
1043 svgelem += ' </div>';
1044 svgelem += '</div>';
1045 // deselect all icons
1046 jQuery('.vbo-cust-geo-marker-svg').attr('data-markerapply', '0');
1047 // unset and hide fields for adding new custom SVG
1048 jQuery('.vbo-cust-geo-marker-svg-newfield, .vbo-cust-geo-marker-svg-adjust').hide();
1049 jQuery('#vbo-newsvg-name, #vbo-newsvg-path').val('');
1050 // append new SVG element
1051 jQuery(svgelem).insertBefore('.vbo-cust-geo-marker-svg-addnew');
1052 // display again the add new button, but only one icon can be saved
1053 jQuery('.vbo-cust-geo-marker-svg-addnew').show();
1054 }
1055
1056 /**
1057 * Cancels the action of creating a custom symbol
1058 */
1059 function vboCustomizeMarkerCancelCustomSymbol() {
1060 // unset and hide fields for adding new custom SVG
1061 jQuery('.vbo-cust-geo-marker-svg-newfield, .vbo-cust-geo-marker-svg-adjust').hide();
1062 jQuery('#vbo-newsvg-name, #vbo-newsvg-path').val('');
1063 // restore add new button and deselect all choices
1064 jQuery('.vbo-cust-geo-marker-svg-addnew').show();
1065 jQuery('.vbo-cust-geo-marker-svg').attr('data-markerapply', '0');
1066 }
1067
1068 /**
1069 * Applies the selected changes to the current map marker
1070 */
1071 function vboCustomizeMarkerApply() {
1072 if (!vbo_current_marker_index || !vbo_geomarker_units.hasOwnProperty(vbo_current_marker_index)) {
1073 console.error('no active map marker found', vbo_current_marker_index, vbo_geomarker_units);
1074 return false;
1075 }
1076 // get icon type
1077 var icntype = jQuery('#vbo-geo-marker-icntype').val();
1078 if (!icntype.length || icntype == 'google') {
1079 // set default icon of Google Maps (by passing null)
1080 vbo_geomarker_units[vbo_current_marker_index].content = null;
1081 // update icon in global object
1082 if (vbo_geomarker_units_pos !== null && vbo_geomarker_units_pos.hasOwnProperty(vbo_current_marker_index)) {
1083 delete vbo_geomarker_units_pos[vbo_current_marker_index]['icon'];
1084 }
1085 } else if (icntype == 'symbol') {
1086 // SVG path symbol
1087 var current_svg = jQuery('.vbo-cust-geo-marker-svg[data-markerapply="1"]');
1088 if (!current_svg.length || !current_svg.first().find('svg').length) {
1089 console.error('no SVG path symbol selected');
1090 alert(Joomla.JText._('VBO_PLEASE_SELECT'));
1091 return false;
1092 }
1093 current_svg = current_svg.first();
1094 // compose selected icon object
1095 var icon_obj = {
1096 id: current_svg.attr('data-markerid'),
1097 name: current_svg.find('.vbo-cust-geo-marker-svg-name').find('span').text(),
1098 group: current_svg.attr('data-markergroup'),
1099 fill: jQuery('#vbo-geo-marker-fillcolor').val(),
1100 opacity: parseFloat(jQuery('#vbo-geo-marker-opacity').val()),
1101 path: current_svg.find('.vbo-cust-geo-marker-svg-icon').find('svg').find('path').attr('d')
1102 };
1103 // check if width and height can be used to set anchor point with X/Y coordinates
1104 var coord_x = current_svg.attr('data-markerpointx');
1105 var coord_y = current_svg.attr('data-markerpointy');
1106 if (coord_x && coord_y) {
1107 icon_obj['anchor'] = {
1108 x: parseFloat(coord_x),
1109 y: parseFloat(coord_y)
1110 };
1111 }
1112
1113 // make a silent AJAX request to update the symbol in the DB or to create it if that's a new icon
1114 var call_method = 'storeSvgSymbol';
1115 jQuery.ajax({
1116 type: "POST",
1117 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=geocoding_endpoint'); ?>",
1118 data: {
1119 tmpl: "component",
1120 callback: call_method,
1121 symbol: icon_obj
1122 }
1123 }).done(function(response) {
1124 try {
1125 var obj_res = JSON.parse(response);
1126 if (!obj_res.hasOwnProperty(call_method)) {
1127 console.error('storeSvgSymbol unexpected JSON response', obj_res);
1128 } else {
1129 // console.log(obj_res[call_method]);
1130 // trigger the geo-saving event
1131 document.dispatchEvent(new Event('vbo-room-geosaving'));
1132 }
1133 } catch(err) {
1134 console.error('storeSvgSymbol could not parse JSON response', err, response);
1135 }
1136 }).fail(function(err) {
1137 console.error('storeSvgSymbol request failed', err);
1138 });
1139
1140 // compose icon symbol object
1141 var symbol_obj = {
1142 path: icon_obj.path,
1143 fillColor: icon_obj.fill,
1144 fillOpacity: icon_obj.opacity,
1145 strokeWeight: 0,
1146 scale: 1,
1147 vbo_icon_id: icon_obj.id
1148 };
1149 // check if anchor point is defined
1150 if (icon_obj.hasOwnProperty('anchor')) {
1151 symbol_obj['anchor'] = icon_obj['anchor'];
1152 }
1153 // build svg element
1154 let svgns = 'http://www.w3.org/2000/svg';
1155 let svg = document.createElementNS(svgns, 'svg');
1156 svg.setAttribute('xmlns', svgns);
1157 svg.setAttribute('viewBox', '0 0 90 90');
1158 svg.setAttribute('width', 90);
1159 svg.setAttribute('height', 90);
1160 let path = document.createElementNS(svgns, 'path');
1161 path.setAttribute('d', symbol_obj.path);
1162 path.setAttribute('fill', symbol_obj.fillColor || '#eeeeee');
1163 path.setAttribute('fill-opacity', symbol_obj.fillOpacity || 0.9);
1164 path.setAttribute('stroke-width', symbol_obj.strokeWeight);
1165 if (symbol_obj?.anchor?.x && symbol_obj?.anchor?.y) {
1166 path.setAttribute(
1167 'transform',
1168 'translate(' + symbol_obj.anchor.x + ', ' + symbol_obj.anchor.y + ') scale(' + symbol_obj.scale + ')'
1169 );
1170 }
1171 svg.appendChild(path);
1172 // register custom data attribute to identify the icon id
1173 svg.setAttribute('data-vbo-icon-id', symbol_obj.vbo_icon_id);
1174 // update marker icon on map
1175 vbo_geomarker_units[vbo_current_marker_index].content = svg;
1176 // update icon in global object
1177 if (vbo_geomarker_units_pos !== null && vbo_geomarker_units_pos.hasOwnProperty(vbo_current_marker_index)) {
1178 vbo_geomarker_units_pos[vbo_current_marker_index]['icon'] = symbol_obj;
1179 }
1180 } else if (icntype == 'icon') {
1181 // icon URL (Icon interface)
1182 var custom_icon_url = jQuery('input[name="marker_icon_img"]').val();
1183 if (!custom_icon_url.length) {
1184 console.error('no image selected');
1185 alert(Joomla.JText._('VBO_PLEASE_SELECT'));
1186 return false;
1187 }
1188 if (custom_icon_url.substr(0, 1) == '/') {
1189 custom_icon_url = custom_icon_url.substr(1);
1190 }
1191 var icon_url = vbo_marker_base_uri + custom_icon_url;
1192 var icon_obj = {
1193 url: icon_url
1194 };
1195 let marker_img = document.createElement('img');
1196 marker_img.src = icon_url;
1197 marker_img.style.transform = 'translate(0, 50%)';
1198 var icon_width = jQuery('#vbo-geo-marker-icon-width').val();
1199 var icon_height = jQuery('#vbo-geo-marker-icon-height').val();
1200 if (icon_width && icon_width > 0 && icon_height > 0) {
1201 var scaledSize = {
1202 width: parseInt(icon_width),
1203 height: parseInt(icon_height)
1204 }
1205 icon_obj['scaledSize'] = scaledSize;
1206 marker_img.style.width = icon_width + 'px';
1207 marker_img.style.height = icon_height + 'px';
1208 }
1209 // update marker icon on map
1210 vbo_geomarker_units[vbo_current_marker_index].content = marker_img;
1211 // update icon in global object
1212 if (vbo_geomarker_units_pos !== null && vbo_geomarker_units_pos.hasOwnProperty(vbo_current_marker_index)) {
1213 vbo_geomarker_units_pos[vbo_current_marker_index]['icon'] = icon_obj;
1214 }
1215 }
1216
1217 // hide modal
1218 hideVboModalGeoMap();
1219
1220 // trigger the geo-saving event
1221 document.dispatchEvent(new Event('vbo-room-geosaving'));
1222 }
1223
1224 /**
1225 * Removes an SVG symbol from DOM and DB
1226 */
1227 function vboCustomizeMarkerDeleteSymbol(elem) {
1228 if (confirm(Joomla.JText._('VBO_WIDGETS_CONFRMELEM'))) {
1229 var symbol_id = jQuery(elem).closest('.vbo-cust-geo-marker-svg').attr('data-markerid');
1230 if (!symbol_id || !symbol_id.length) {
1231 console.error('symbol id is empty', elem);
1232 return false;
1233 }
1234 // remove the element from the DOM
1235 jQuery(elem).closest('.vbo-cust-geo-marker-svg').remove();
1236 // make a silent AJAX request to remove the symbol from the DB
1237 var call_method = 'deleteSvgSymbol';
1238 jQuery.ajax({
1239 type: "POST",
1240 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=geocoding_endpoint'); ?>",
1241 data: {
1242 tmpl: "component",
1243 callback: call_method,
1244 symbol_id: symbol_id
1245 }
1246 }).done(function(response) {
1247 try {
1248 var obj_res = JSON.parse(response);
1249 if (!obj_res.hasOwnProperty(call_method)) {
1250 console.error('deleteSvgSymbol unexpected JSON response', obj_res);
1251 } else {
1252 // console.log(obj_res[call_method]);
1253 }
1254 } catch(err) {
1255 console.error('deleteSvgSymbol could not parse JSON response', err, response);
1256 }
1257 }).fail(function(err) {
1258 console.error('deleteSvgSymbol request failed', err);
1259 });
1260 }
1261 }
1262
1263 /**
1264 * Toggles the use of a ground overlay image for the map.
1265 */
1266 function vboToggleGeoGroundOverlay() {
1267 if (vbo_geomap === null && vbo_ground_overlay === null) {
1268 console.error('both map and ground overlay are null');
1269 return false;
1270 }
1271 if (jQuery('input[name="geo_goverlay"]').is(':checked')) {
1272 // populate bounds coords, and current image if empty
1273 var south_west = null,
1274 north_east = null;
1275 if (vbo_ground_overlay !== null) {
1276 // get current values from ground overlay
1277 var bounds = vbo_ground_overlay.getBounds();
1278 south_west = bounds.getSouthWest();
1279 north_east = bounds.getNorthEast();
1280 // check if current image is set
1281 if (!jQuery('input[name="map_overlay_img"]').val().length) {
1282 var overlay_url = vbo_ground_overlay.getUrl();
1283 if (overlay_url) {
1284 // set input value
1285 jQuery('input[name="map_overlay_img"]').val(overlay_url.replace(vbo_marker_base_uri, ''));
1286 // try to set the image source (this changes from J to WP)
1287 if (jQuery('.image-preview-wrapper').length) {
1288 jQuery('.vbo-cust-geo-overlay-wrapper').find('.image-preview-wrapper').html('<img src="' + overlay_url + '" />').show();
1289 }
1290 }
1291 }
1292 // display button to copy the current map coordinates because the user may need to adjust the overlay position
1293 jQuery('#vbo-overlay-copy-bounds').show();
1294 } else if (vbo_geomap !== null) {
1295 // get current values from geo map
1296 var bounds = vbo_geomap.getBounds();
1297 south_west = bounds.getSouthWest();
1298 north_east = bounds.getNorthEast();
1299 }
1300 if (south_west !== null && north_east !== null) {
1301 jQuery('#vbo-geo-map-overlay-south').val(south_west.lat());
1302 jQuery('#vbo-geo-map-overlay-west').val(south_west.lng());
1303 jQuery('#vbo-geo-map-overlay-north').val(north_east.lat());
1304 jQuery('#vbo-geo-map-overlay-east').val(north_east.lng());
1305 }
1306 // turn on flag for editing ground overlay
1307 vbo_doing_ground_overlay = true;
1308 // set proper modal title
1309 jQuery('#vbo-modal-geomap-title').html('<?php VikBookingIcons::e('images'); ?> ' + Joomla.JText._('VBO_GEO_MAP_GOVERLAY'));
1310 // open modal
1311 jQuery('.vbo-cust-geo-marker-wrapper').hide();
1312 jQuery('.vbo-cust-geo-overlay-wrapper').show();
1313 vboOpenModalGeoMap();
1314 // lower the z-index of our modal to allow the media-manager modal to be displayed on top
1315 jQuery('.vbo-modal-overlay-block-geomap').addClass('vbo-modal-overlay-block-geomap-lowerzindex');
1316 } else {
1317 // unset the ground overlay, if any
1318 if (vbo_ground_overlay !== null) {
1319 vbo_ground_overlay.setMap(null);
1320 vbo_ground_overlay = null;
1321 }
1322 // hide edit button
1323 jQuery('#vbo-goverlay-edit').hide();
1324 // restore the original z-index of our modal
1325 jQuery('.vbo-modal-overlay-block-geomap').removeClass('vbo-modal-overlay-block-geomap-lowerzindex');
1326 // close modal even though it should be closed already
1327 hideVboModalGeoMap();
1328 // trigger the geo-saving event
1329 document.dispatchEvent(new Event('vbo-room-geosaving'));
1330 }
1331 }
1332
1333 /**
1334 * Applies a ground overlay image to the map.
1335 */
1336 function vboGroundOverlayApply() {
1337 if (vbo_geomap === null) {
1338 console.error('map is null');
1339 return false;
1340 }
1341 var overlay_url = jQuery('input[name="map_overlay_img"]').val();
1342 if (!overlay_url || !overlay_url.length) {
1343 alert(Joomla.JText._('VBO_PLEASE_SELECT'));
1344 return false;
1345 }
1346 var overlay_s = jQuery('#vbo-geo-map-overlay-south').val();
1347 var overlay_w = jQuery('#vbo-geo-map-overlay-west').val();
1348 var overlay_n = jQuery('#vbo-geo-map-overlay-north').val();
1349 var overlay_e = jQuery('#vbo-geo-map-overlay-east').val();
1350 if (!overlay_s.length || !overlay_w.length || !overlay_n.length || !overlay_e.length) {
1351 alert(Joomla.JText._('VBO_PLEASE_SELECT'));
1352 return false;
1353 }
1354 var use_overlay_url = overlay_url;
1355 if (use_overlay_url.indexOf(vbo_marker_base_uri) < 0) {
1356 use_overlay_url = vbo_marker_base_uri + overlay_url;
1357 }
1358 // compose LatLngBounds object
1359 var overlay_bounds = new google.maps.LatLngBounds(
1360 new google.maps.LatLng(parseFloat(overlay_s), parseFloat(overlay_w)),
1361 new google.maps.LatLng(parseFloat(overlay_n), parseFloat(overlay_e))
1362 );
1363 if (vbo_ground_overlay !== null) {
1364 // remove it from the map first
1365 vbo_ground_overlay.setMap(null);
1366 }
1367 // update ground overlay object
1368 vbo_ground_overlay = new google.maps.GroundOverlay(use_overlay_url, overlay_bounds);
1369 // set the overlay to the map
1370 vbo_ground_overlay.setMap(vbo_geomap);
1371 // turn off flag for editing ground overlay
1372 vbo_doing_ground_overlay = false;
1373 // show edit button
1374 jQuery('#vbo-goverlay-edit').show();
1375 // close the modal
1376 hideVboModalGeoMap();
1377 // trigger the geo-saving event
1378 document.dispatchEvent(new Event('vbo-room-geosaving'));
1379 }
1380
1381 /**
1382 * Copies the current map bounds and updates the input fields for the ground overlay coordinates.
1383 */
1384 function vboOverlayCopyBounds() {
1385 if (vbo_geomap === null) {
1386 console.error('map is null');
1387 return false;
1388 }
1389 // get current values from geo map
1390 var bounds = vbo_geomap.getBounds();
1391 var south_west = bounds.getSouthWest();
1392 var north_east = bounds.getNorthEast();
1393 // set values in input fields
1394 jQuery('#vbo-geo-map-overlay-south').val(south_west.lat());
1395 jQuery('#vbo-geo-map-overlay-west').val(south_west.lng());
1396 jQuery('#vbo-geo-map-overlay-north').val(north_east.lat());
1397 jQuery('#vbo-geo-map-overlay-east').val(north_east.lng());
1398 var overlay_url = jQuery('input[name="map_overlay_img"]').val();
1399 if (overlay_url && overlay_url.length) {
1400 // we auto-apply the ground overlay image
1401 vboGroundOverlayApply();
1402 }
1403 }
1404
1405 /**
1406 * Prompts for confirmation for importing geo config from the selected room id.
1407 */
1408 function vboImportGeoConfig(room_id) {
1409 if (!room_id) {
1410 return false;
1411 }
1412 if (isNaN(room_id)) {
1413 console.error('invalid room_id provided', room_id);
1414 return false;
1415 }
1416 if (confirm(Joomla.JText._('VBO_GEO_IMPORT_CONF'))) {
1417 var call_method = 'importRoomGeoConfig';
1418 jQuery.ajax({
1419 type: "POST",
1420 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=geocoding_endpoint'); ?>",
1421 data: {
1422 tmpl: "component",
1423 callback: call_method,
1424 room_id: room_id
1425 }
1426 }).done(function(response) {
1427 try {
1428 var obj_res = JSON.parse(response);
1429 if (!obj_res.hasOwnProperty(call_method)) {
1430 console.error('importRoomGeoConfig unexpected JSON response', obj_res);
1431 } else {
1432 var geo_params = obj_res[call_method];
1433 if (parseInt(geo_params['enabled']) < 1) {
1434 // should never occur, but it would break the entire code
1435 alert('The room selected does not have any geographical information');
1436 return false;
1437 }
1438 // populate internal variables
1439 if (!jQuery('input[name="geo_enabled"]').is(':checked')) {
1440 // enable geo-coding params
1441 jQuery('input[name="geo_enabled"]').trigger('click');
1442 }
1443 // set address
1444 jQuery('#geo_address').val(geo_params['address']);
1445 // set center coordinates
1446 jQuery('#geo_latitude').val(geo_params['latitude']);
1447 jQuery('#geo_longitude').val(geo_params['longitude']);
1448 // set zoom
1449 jQuery('#geo_zoom').val(geo_params['zoom']);
1450 // set map type
1451 jQuery('#geo_mtype').val(geo_params['mtype']);
1452 // set height
1453 jQuery('#geo_height').val(geo_params['height']);
1454 jQuery('#vbo-geo-map').css('height', geo_params['height'] + 'px');
1455 // map markers type
1456 jQuery('#geo_markers_multi').val(geo_params['markers_multi']);
1457 // ground overlay
1458 if (parseInt(geo_params['goverlay']) > 0) {
1459 jQuery('input[name="geo_goverlay"]').prop('checked', true);
1460 jQuery('#vbo-goverlay-edit').show();
1461 } else {
1462 jQuery('input[name="geo_goverlay"]').prop('checked', false);
1463 jQuery('#vbo-goverlay-edit').hide();
1464 }
1465 // main room marker hidden data
1466 jQuery('#geo_marker_lat').val(geo_params['marker_lat']);
1467 jQuery('#geo_marker_lng').val(geo_params['marker_lng']);
1468 jQuery('#geo_marker_hide').val(geo_params['marker_hide']);
1469 // main room marker position object
1470 vbo_geomarker_room_pos = null;
1471 if (parseInt(geo_params['marker_hide']) < 1 && geo_params['marker_lat'].length && geo_params['marker_lng'].length) {
1472 vbo_geomarker_room_pos = {
1473 lat: parseFloat(geo_params['marker_lat']),
1474 lng: parseFloat(geo_params['marker_lng'])
1475 };
1476 }
1477 // room units markers positions
1478 if (geo_params.hasOwnProperty('units_pos') && geo_params['units_pos'].hasOwnProperty('1')) {
1479 vbo_geomarker_units_pos = geo_params['units_pos'];
1480 } else {
1481 vbo_geomarker_units_pos = {};
1482 }
1483 // ground overlay
1484 if (parseInt(geo_params['goverlay']) > 0 && geo_params['overlay_img'].length && (geo_params['overlay_south'] + '').length) {
1485 vbo_dbground_overlay = {
1486 url: geo_params['overlay_img'],
1487 south: geo_params['overlay_south'],
1488 west: geo_params['overlay_west'],
1489 north: geo_params['overlay_north'],
1490 east: geo_params['overlay_east']
1491 };
1492 } else {
1493 vbo_dbground_overlay = null;
1494 }
1495 // let the main function build up the whole map to complete the process
1496 vboInitGeoMap();
1497 }
1498 } catch(err) {
1499 console.error('importRoomGeoConfig could not parse JSON response', err, response);
1500 }
1501 }).fail(function(err) {
1502 console.error('importRoomGeoConfig request failed', err);
1503 });
1504 }
1505 }
1506
1507 /**
1508 * Geo map click handler.
1509 */
1510 function vboGeoMapClickedPos(position) {
1511 if (vbo_geomap === null) {
1512 console.error('map is empty');
1513 return false;
1514 }
1515 if (!position) {
1516 console.error('position is empty');
1517 return false;
1518 }
1519 // calculate limits
1520 var multi_markers = jQuery('#geo_markers_multi').val();
1521 var room_units = jQuery('#room_units').val();
1522 var tot_markers = multi_markers > 0 && room_units > 1 ? room_units : 1;
1523 tot_markers = parseInt(tot_markers);
1524 // iterate through markers
1525 for (var i = 1; i <= tot_markers; i++) {
1526 if (!vbo_geomarker_units_pos.hasOwnProperty(i)) {
1527 // marker for this index is missing, add it to given position
1528 vboManageRoomMarker(i, position);
1529 break;
1530 }
1531 }
1532 // if we reach this point it means all room units have got a marker
1533 return true;
1534 }
1535
1536 /**
1537 * Generates the HTML content for the units marker infowindow.
1538 */
1539 function vboGenerateInfoMarkerContent(index, marker_title) {
1540 marker_title = marker_title ? marker_title : Joomla.JText._('VBODISTFEATURERUNIT') + (index + '');
1541 var infowin_cont = '';
1542 infowin_cont += '<div class="vbo-geomarker-infowin-wrap">';
1543 infowin_cont += ' <h4>' + marker_title + '</h4>';
1544 infowin_cont += ' <div class="vbo-geomarker-infowin-inner">';
1545 infowin_cont += ' <button type="button" class="btn btn-primary" onclick="vboCustomizeMarker(' + index + ');"><?php VikBookingIcons::e('palette'); ?> ' + Joomla.JText._('VBO_GEO_CUSTOMIZE_MARKER') + '</button>';
1546 infowin_cont += ' <button type="button" class="btn btn-danger" onclick="vboRemoveMarker(' + index + ');"><?php VikBookingIcons::e('trash'); ?> ' + Joomla.JText._('VBMAINPAYMENTSDEL') + '</button>';
1547 infowin_cont += ' </div>';
1548 infowin_cont += '</div>';
1549
1550 return infowin_cont;
1551 }
1552
1553 /**
1554 * Generates the HTML content for the main room (base address) marker infowindow.
1555 */
1556 function vboGenerateMainInfoMarkerContent() {
1557 var infowin_cont = '';
1558 infowin_cont += '<div class="vbo-geomarker-infowin-wrap vbo-geomarker-address-infowin-wrap">';
1559 infowin_cont += ' <h4>' + Joomla.JText._('VBO_GEO_ADDRESS') + '</h4>';
1560 infowin_cont += ' <div class="vbo-geomarker-infowin-inner">';
1561 infowin_cont += ' <button type="button" class="btn btn-secondary" onclick="vboRemoveAddressMarker();"><?php VikBookingIcons::e('trash'); ?> ' + Joomla.JText._('VBO_GEO_HIDE_FROM_MAP') + '</button>';
1562 infowin_cont += ' </div>';
1563 infowin_cont += '</div>';
1564
1565 return infowin_cont;
1566 }
1567
1568 /**
1569 * Saves the geo information for this room in a transient-like record.
1570 * The controller will then resume this transient to store the geo params.
1571 */
1572 function vboHandleGeoSaving() {
1573 if (vbo_geomap === null) {
1574 console.error('saving is useless as the map is null');
1575 return false;
1576 }
1577 // turn on flag for saving
1578 vbo_geomap_saving = true;
1579 // make a silent AJAX request to store the current room geo information
1580 var call_method = 'updateRoomGeoTransient';
1581 jQuery.ajax({
1582 type: "POST",
1583 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=geocoding_endpoint'); ?>",
1584 data: {
1585 tmpl: "component",
1586 callback: call_method,
1587 room_id: <?php echo count($this->row) ? $this->row['id'] : '0'; ?>,
1588 geo_enabled: (jQuery('input[name="geo_enabled"]').is(':checked') ? 1 : 0),
1589 geo_address: jQuery('#geo_address').val(),
1590 geo_latitude: jQuery('#geo_latitude').val(),
1591 geo_longitude: jQuery('#geo_longitude').val(),
1592 geo_zoom: jQuery('#geo_zoom').val(),
1593 geo_mtype: jQuery('#geo_mtype').val(),
1594 geo_height: jQuery('#geo_height').val(),
1595 geo_markers_multi: jQuery('#geo_markers_multi').val(),
1596 geo_marker_lat: jQuery('#geo_marker_lat').val(),
1597 geo_marker_lng: jQuery('#geo_marker_lng').val(),
1598 geo_marker_hide: jQuery('#geo_marker_hide').val(),
1599 geo_units_pos: vbo_geomarker_units_pos,
1600 geo_goverlay: (jQuery('input[name="geo_goverlay"]').is(':checked') ? 1 : 0),
1601 geo_overlay_img: (jQuery('input[name="map_overlay_img"]').val().length ? (vbo_marker_base_uri + jQuery('input[name="map_overlay_img"]').val()) : ''),
1602 geo_overlay_south: jQuery('#vbo-geo-map-overlay-south').val(),
1603 geo_overlay_west: jQuery('#vbo-geo-map-overlay-west').val(),
1604 geo_overlay_north: jQuery('#vbo-geo-map-overlay-north').val(),
1605 geo_overlay_east: jQuery('#vbo-geo-map-overlay-east').val()
1606 }
1607 }).done(function(response) {
1608 try {
1609 var obj_res = JSON.parse(response);
1610 if (!obj_res.hasOwnProperty(call_method)) {
1611 console.error('updateRoomGeoTransient unexpected JSON response', obj_res);
1612 } else {
1613 // console.log(obj_res[call_method]);
1614 }
1615 } catch(err) {
1616 console.error('updateRoomGeoTransient could not parse JSON response', err, response);
1617 }
1618 // turn off flag for saving
1619 vbo_geomap_saving = false;
1620 }).fail(function(err) {
1621 console.error('updateRoomGeoTransient request failed', err);
1622 });
1623 //
1624 }
1625
1626 jQuery(function() {
1627
1628 // display geocoding params, if necessary
1629 vboToggleGeoParams();
1630
1631 // listener to the geocode-address change event with debounce handler.
1632 document.addEventListener('vbo-geocode-address', vboDebounceEvent(vboAddressGeocoding, 500));
1633
1634 // listener to the change-address event
1635 jQuery('#geo_address').keyup(function(e) {
1636 if (e.keyCode && e.keyCode == 13) {
1637 // if enter is pressed, launch the function immediately
1638 vboAddressGeocoding();
1639 return;
1640 }
1641 // trigger the geocode-address change event debounced
1642 document.dispatchEvent(new Event('vbo-geocode-address'));
1643 });
1644
1645 // listener to the change-center event
1646 jQuery('#geo_latitude, #geo_longitude').change(function() {
1647 var lat = jQuery('#geo_latitude').val(),
1648 lng = jQuery('#geo_longitude').val();
1649 vboSetGeoMapCenter(lat, lng);
1650 // trigger the geo-saving event
1651 document.dispatchEvent(new Event('vbo-room-geosaving'));
1652 });
1653
1654 // listener to the change-zoom event
1655 jQuery('#geo_zoom').change(function() {
1656 var zoom = parseFloat(jQuery(this).val());
1657 if (vbo_geomap === null || isNaN(zoom)) {
1658 return;
1659 }
1660 vbo_geomap.setZoom(zoom);
1661 // trigger the geo-saving event
1662 document.dispatchEvent(new Event('vbo-room-geosaving'));
1663 });
1664
1665 // listener to the change-map-type event
1666 jQuery('#geo_mtype').change(function() {
1667 var mtype = jQuery(this).val();
1668 if (vbo_geomap === null) {
1669 return;
1670 }
1671 vbo_geomap.setMapTypeId(mtype);
1672 // trigger the geo-saving event
1673 document.dispatchEvent(new Event('vbo-room-geosaving'));
1674 });
1675
1676 // listener to address suggestion
1677 jQuery('#geo_address_formatted').click(function() {
1678 var address_format = jQuery(this).text();
1679 if (address_format.length < 2) {
1680 return;
1681 }
1682 jQuery('#geo_address').val(address_format);
1683 jQuery(this).text('');
1684 // trigger the geo-saving event
1685 document.dispatchEvent(new Event('vbo-room-geosaving'));
1686 });
1687
1688 // listener to map height change
1689 jQuery('#geo_height').change(function() {
1690 jQuery('#vbo-geo-map').css('height', jQuery(this).val() + 'px');
1691 // trigger the geo-saving event
1692 document.dispatchEvent(new Event('vbo-room-geosaving'));
1693 });
1694
1695 // listener to room main name to update the marker's title
1696 jQuery('input[name="cname"]').change(function() {
1697 if (vbo_geomarker_room === null) {
1698 return;
1699 }
1700 vbo_geomarker_room.setTitle(jQuery(this).val());
1701 });
1702
1703 // listener to map markers quantity change
1704 jQuery('#geo_markers_multi').change(function() {
1705 var mmtype = jQuery(this).val();
1706 if (parseInt(mmtype) == 1) {
1707 // one for each sub-unit, make sure the room has got enough units
1708 var now_units = jQuery('#room_units').val();
1709 if (now_units < 2) {
1710 alert(Joomla.JText._('VBO_GEO_MARKERS_MULTI_ERRUNIT'));
1711 jQuery('#geo_markers_multi').val(0);
1712 }
1713 }
1714 // update map markers
1715 vboPopulateMapMarkers();
1716 // trigger the geo-saving event
1717 document.dispatchEvent(new Event('vbo-room-geosaving'));
1718 });
1719
1720 // register colorpicker
1721 jQuery('.vbo-inspector-colorpicker').ColorPicker({
1722 color: '#ffffff',
1723 onShow: function (colpkr, el) {
1724 if (!jQuery('.vbo-cust-geo-marker-svg[data-markerapply="1"]').length) {
1725 return false;
1726 }
1727 var cur_color = jQuery(el).css('backgroundColor');
1728 jQuery(el).ColorPickerSetColor(vboRgb2Hex(cur_color));
1729 jQuery(colpkr).show();
1730 return false;
1731 },
1732 onChange: function (hsb, hex, rgb, el) {
1733 var element = jQuery(el);
1734 element.css('backgroundColor', '#'+hex);
1735 if (jQuery('.vbo-cust-geo-marker-svg[data-markerapply="1"]').length) {
1736 jQuery('.vbo-cust-geo-marker-svg[data-markerapply="1"]').first().find('svg').css('fill', '#'+hex);
1737 }
1738 jQuery('#vbo-geo-marker-fillcolor').val('#'+hex);
1739 },
1740 onSubmit: function(hsb, hex, rgb, el) {
1741 var element = jQuery(el);
1742 element.css('backgroundColor', '#'+hex);
1743 if (jQuery('.vbo-cust-geo-marker-svg[data-markerapply="1"]').length) {
1744 jQuery('.vbo-cust-geo-marker-svg[data-markerapply="1"]').first().find('svg').css('fill', '#'+hex);
1745 }
1746 jQuery('#vbo-geo-marker-fillcolor').val('#'+hex);
1747 element.ColorPickerHide();
1748 }
1749 });
1750
1751 // register opacity change
1752 jQuery('#vbo-geo-marker-opacity').change(function() {
1753 var opacity = jQuery(this).val();
1754 if (!jQuery('.vbo-cust-geo-marker-svg[data-markerapply="1"]').length) {
1755 return;
1756 }
1757 jQuery('.vbo-cust-geo-marker-svg[data-markerapply="1"]').first().find('svg').css('opacity', opacity);
1758 });
1759
1760 // register select2 for importing geo configuration from another room
1761 if (jQuery('#vbo-geo-importfrom').length) {
1762 jQuery('#vbo-geo-importfrom').select2({
1763 placeholder: Joomla.JText._('VBO_GEO_IMPORT_FROM'),
1764 width: '190px',
1765 allowClear: true
1766 });
1767 }
1768
1769 /**
1770 * Add event listener to the geo-saving event with debounce handler.
1771 */
1772 document.addEventListener('vbo-room-geosaving', vboDebounceEvent(vboHandleGeoSaving, 500));
1773
1774 /**
1775 * Register promise for Google Maps async loading completion.
1776 */
1777 VBOCore.onInstanceReady(() => {
1778 // constantly check when Google Maps async loading is completed
1779 return VBOGMapsUtils.isReady;
1780 }).then(() => {
1781 // init geo map with current markers, if any
1782 vboInitGeoMap();
1783 });
1784
1785 });
1786 </script>
1787 <?php
1788 }
1789