PluginProbe
WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress / 9.1.3
WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress v9.1.3
9.1.3 9.1.2 9.1.1 9.1.0 9.0.3 9.0.2 9.0.1 9.0.0 8.5.79 8.5.78 8.5.77 8.5.76 8.5.75 8.5.74 8.5.73 8.5.72 8.5.71 8.5.70 8.5.69 8.5.68 8.5.35 8.5.36 8.5.37 8.5.38 8.5.39 All 222 releases
wpvr / legacy / admin / classes / class-wpvr-scene.php

class-wpvr-scene.php in WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress 9.1.3, at legacy/admin/classes/class-wpvr-scene.php

3,771 lines 195.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) exit; // Exit if accessed directly
3 /**
4 * Responsible for managing Scene tab on Setup meta box
5 *
6 * @link http://rextheme.com/
7 * @since 8.0.0
8 *
9 * @package Wpvr
10 * @subpackage Wpvr/admin/classes
11 */
12
13 class WPVR_Scene {
14
15 /**
16 * Instance of WPVR_Hotspot class
17 *
18 * @var object
19 * @since 8.0.0
20 */
21 protected $hotspot;
22
23 /**
24 * Instance of WPVR_Format class
25 *
26 * @var object
27 * @since 8.0.0
28 */
29 protected $format;
30
31 /**
32 * Instance of WPVR_Validator class
33 *
34 * @var object
35 * @since 8.0.0
36 */
37 private $validator;
38
39 /**
40 * Number of scene or hotspot item
41 *
42 * @var integer
43 * @since 8.0.0
44 */
45 protected $data_limit;
46
47 /**
48 * Pro version license status
49 *
50 * @var string
51 * @since 8.0.0
52 */
53 protected $status;
54
55
56 function __construct()
57 {
58 $this->hotspot = new WPVR_Hotspot();
59 $this->format = new WPVR_Format();
60 $this->validator = new WPVR_Validator();
61
62 $this->status = apply_filters( 'check_pro_license_status', $this->status );
63
64 if ($this->status !== false && $this->status == 'valid') {
65 $this->data_limit = 999999999;
66 } else {
67 $this->data_limit = 5;
68 }
69 }
70
71 /**
72 * Render Scene Settings Content
73 *
74 * @param array $postdata
75 *
76 * @return void
77 * @since 8.0.0
78 */
79 public function render_scene($postdata)
80 {
81 ob_start();
82 ?>
83
84 <!-- Scene and Hotspot repeater -->
85 <div class="scene-setup rex-pano-sub-tabs" data-limit="<?php echo esc_attr( $this->data_limit + 1 ); ?>">
86 <?php $this->render_scene_repeater_list($postdata); ?>
87 </div>
88
89 <?php
90 // ob_end_flush();
91 }
92
93
94 /**
95 * Render scene setup data repeater list
96 *
97 * @param array $postdata
98 *
99 * @return void
100 * @since 8.0.0
101 */
102 private function render_scene_repeater_list($postdata)
103 {
104 ob_start();
105 ?>
106 <nav class="rex-pano-tab-nav rex-pano-nav-menu scene-nav">
107 <?php $this->render_nav_menu($postdata); // Will render scene navigation bar ?>
108 </nav>
109
110 <div data-repeater-list="scene-list" class="rex-pano-tab-content">
111
112 <!-- Default empty repeater -->
113 <div data-repeater-item class="single-scene rex-pano-tab" data-title="0" id="scene-0">
114 <?php $this->render_default_repeater_item(); ?>
115 </div>
116 <!-- Empty repeater end -->
117
118 <?php
119 if ( ! empty( $postdata['panodata']["scene-list"] ) && is_array( $postdata['panodata']["scene-list"] ) ) {
120 $s = 1; $firstvalue = reset($postdata['panodata']["scene-list"]);
121 $default_scene = $postdata['defaultscene'] ?? ($firstvalue['scene-id'] ?? '');
122 foreach ($postdata['panodata']["scene-list"] as $pano_scene) {
123 if ( ! is_array( $pano_scene ) ) {
124 continue;
125 }
126 if ( ! isset( $pano_scene['dscene'] ) ) {
127 $pano_scene['dscene'] = ( ! empty( $default_scene ) && ( $pano_scene['scene-id'] ?? '' ) === $default_scene ) ? 'on' : 'off';
128 }
129 $is_active = ( ( $pano_scene['scene-id'] ?? '' ) === ( $firstvalue['scene-id'] ?? '' ) );
130 ?>
131
132 <div data-repeater-item class="single-scene rex-pano-tab <?php if($is_active) { echo esc_attr('active'); }; ?>" data-title="1" id="scene-<?php echo esc_attr( $s ); ?>">
133 <?php $this->render_repeater_item_with_panodata($pano_scene, $s); ?>
134 </div>
135
136 <?php $s++; }
137 } ?>
138 </div>
139 <?php
140 // ob_end_flush();
141 }
142
143
144 /**
145 * Render scene nav menu
146 *
147 * @param array $postdata
148 *
149 * @return void
150 * @since 8.0.0
151 */
152 private function render_nav_menu($postdata)
153 {
154 ob_start();
155 ?>
156 <ul>
157 <?php
158 if ( ! empty( $postdata['panodata']["scene-list"] ) && is_array( $postdata['panodata']["scene-list"] ) ) {
159 $i = 1; $firstvalue = reset($postdata['panodata']["scene-list"]);
160 foreach ($postdata['panodata']["scene-list"] as $pano_scene) {
161 if ( ! is_array( $pano_scene ) ) {
162 continue;
163 }
164 $is_active = ( ( $pano_scene['scene-id'] ?? '' ) === ( $firstvalue['scene-id'] ?? '' ) );
165 ?>
166
167 <li class="<?php if ($is_active) {echo 'active';};?>">
168 <span data-index="<?php echo esc_attr( $i ); ?>" data-href="#scene-<?php echo esc_attr( $i ); ?>">
169 <i class="fa fa-image"></i>
170 </span>
171 </li>
172
173 <?php $i++; }
174 } ?>
175 <li class="add" data-repeater-create><span><i class="fa fa-plus-circle"></i></span></li>
176 </ul>
177 <?php
178 ob_end_flush();
179 }
180
181
182 /**
183 * Render repeater item for default scene
184 *
185 * @param int $data_limit
186 *
187 * @return void
188 * @since 8.0.0
189 */
190 private function render_default_repeater_item()
191 {
192 ob_start();
193 ?>
194 <div class="active_scene_id"><p></p></div>
195 <div class="scene-content">
196 <?php $this->render_default_repeater_item_scene_content(); ?>
197 </div>
198
199 <!-- hotspot setup -->
200 <div class="hotspot-setup rex-pano-sub-tabs" data-limit="<?php echo esc_attr( $this->data_limit ); ?>">
201 <?php $this->hotspot->render_hotspot($s = 0, $h =1)?>
202 </div>
203 <button data-repeater-delete type="button" title="Delete Scene" class="delete-scene"><i class="far fa-trash-alt"></i></button>
204 <?php
205 ob_end_flush();
206 }
207
208
209 /**
210 * Render repeater items while scene has panaromic data
211 *
212 * @param array $pano_scene
213 * @param int $s scene number increment var
214 *
215 * @return void
216 * @since 8.0.0
217 */
218 private function render_repeater_item_with_panodata($pano_scene, $s)
219 {
220 ob_start();
221 ?>
222 <div class="active_scene_id"><p></p></div>
223 <div class="scene-content">
224 <!--
225 - Render repeater item scene content
226 - If scene has panaromic data
227 -->
228 <?php $this->render_repeater_scene_content_with_data($pano_scene); ?>
229 </div>
230 <!--
231 - Render repeater item hotspot content
232 -->
233 <?php $this->render_repeater_item_hotspot_content($pano_scene, $s); ?>
234
235 <button data-repeater-delete type="button" title="Delete Scene" class="delete-scene"><i class="far fa-trash-alt"></i></button>
236 <?php
237 ob_end_flush();
238 }
239
240
241 /**
242 * Render scene content for default repeater item
243 *
244 * @return void
245 * @since 8.0.0
246 */
247 private function render_default_repeater_item_scene_content()
248 {
249 ob_start();
250 ?>
251
252 <h6 class="title"><i class="fa fa-cog"></i> <?php esc_html_e('Scene Settings','wpvr'); ?> </h6>
253
254 <div class="scene-left">
255 <?php WPVR_Meta_Field::render_scene_left_fields_empty_panodata(); ?>
256 </div>
257
258 <div class="scene-right">
259 <?php do_action( 'wpvr_pro_scene_empty_right_fields' ) ?>
260 </div>
261
262 <?php
263 ob_end_flush();
264 }
265
266
267 /**
268 * Render repeater item scene content is scene has panaromic data
269 *
270 * @param mixed $dscene
271 * @param mixed $scene_id
272 * @param mixed $scene_photo
273 *
274 * @return void
275 * @since 8.0.0
276 */
277 private function render_repeater_scene_content_with_data($pano_scene)
278 {
279 ob_start();
280 ?>
281 <h6 class="title"><i class="fa fa-cog"></i> <?php esc_html_e('Scene Settings','wpvr'); ?> </h6>
282
283 <div class="scene-left">
284 <?php WPVR_Meta_Field::render_scene_left_fields_with_panodata($pano_scene) ;?>
285 </div>
286
287 <div class="scene-right">
288 <?php do_action( 'wpvr_pro_scene_right_fields', $pano_scene ) ?>
289 </div>
290
291
292 <?php
293 ob_end_flush();
294 }
295
296
297 /**
298 * Render repeater item hotspot content
299 *
300 * @param array $pano_hotspots
301 * @param int $data_limit
302 * @param int $s
303 *
304 * @return void
305 * @since 8.0.0
306 */
307 private function render_repeater_item_hotspot_content($pano_scene, $s)
308 {
309 if (!empty($pano_scene['hotspot-list'])) { ?>
310 <div class="hotspot-setup rex-pano-sub-tabs" data-limit="<?php echo esc_attr( $this->data_limit ); ?>">
311
312 <?php $this->hotspot->render_hotspot_with_panodata($pano_scene['hotspot-list'], $s); //Render hotspot while scene has hotspot data ?>
313
314 </div>
315 <?php } else { ?>
316 <div class="hotspot-setup rex-pano-sub-tabs" data-limit="<?php echo esc_attr( $this->data_limit ); ?>">
317
318 <?php $this->hotspot->render_hotspot($s, $h = 1); //Render hotspot while scene has no hotspot data ?>
319
320 </div>
321 <?php }
322 }
323
324
325 /**
326 * Update post meta data
327 *
328 * @param integer $postid
329 * @param integer $panoid
330 * @param boolean $is_publish_action
331 *
332 * @return void
333 * @since 8.0.0
334 */
335 public function wpvr_update_meta_box($postid, $panoid, $is_publish_action = false)
336 {
337 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
338 if ( ! wp_verify_nonce( $nonce, 'wpvr' ) ) {
339 wp_die( 'Permission denied.' );
340 }
341 $panodata = $this->format->prepare_panodata(isset($_POST['panodata']) ? wp_unslash( $_POST['panodata'] ) : ''); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
342 $default_scene = $this->format->prepare_default_scene($panodata);
343 $previewtext = isset($_POST['previewtext']) ? $this->validator->preview_text_validation(wp_unslash( $_POST['previewtext'] )) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
344
345 $gzoom = $this->format->set_pro_checkbox_value(@wp_unslash( $_POST['gzoom'] ));
346 $default_global_zoom = '';
347 $max_global_zoom = '';
348 $min_global_zoom = '';
349 if ($gzoom == 'on') {
350 $default_global_zoom = isset($_POST['dzoom']) ? wp_unslash( $_POST['dzoom'] ) : '';
351 $max_global_zoom = isset($_POST['maxzoom']) ? wp_unslash( $_POST['maxzoom'] ) : '';
352 $min_global_zoom = isset($_POST['minzoom']) ? wp_unslash( $_POST['minzoom'] ) : '';
353 }
354
355 $custom_control = isset($_POST['customcontrol']) ? wp_unslash( $_POST['customcontrol'] ) : null;
356
357 $vrgallery = $this->format->set_checkbox_value(@wp_unslash( $_POST['vrgallery'] ));
358 $vrgallery_title = $this->format->set_checkbox_value(@wp_unslash( $_POST['vrgallery_title'] ));
359 $vrgallery_display = $this->format->set_checkbox_value(@wp_unslash( $_POST['vrgallery_display'] ));
360 $vrgallery_icon_size = $this->format->set_checkbox_value(@wp_unslash( $_POST['vrgallery_icon_size'] ));
361
362 $existing_panodata = get_post_meta( $postid, 'panodata', true );
363 $existing_panodata = is_array( $existing_panodata ) ? $existing_panodata : array();
364 $mouseZoom = isset( $_POST['mouseZoom'] )
365 ? $this->format->set_pro_checkbox_value( wp_unslash( $_POST['mouseZoom'] ) )
366 : ( $existing_panodata['mouseZoom'] ?? 'on' );
367 $draggable = isset( $_POST['draggable'] )
368 ? $this->format->set_pro_checkbox_value( wp_unslash( $_POST['draggable'] ) )
369 : ( $existing_panodata['draggable'] ?? 'on' );
370 $diskeyboard = $this->format->set_pro_checkbox_value(@wp_unslash( $_POST['diskeyboard'] ));
371 $keyboardzoom = $this->format->set_checkbox_value(@wp_unslash( $_POST['keyboardzoom'] ));
372 $compass = $this->format->set_checkbox_on_value(@wp_unslash( $_POST['compass'] ));
373 //===Gyroscopre control===//
374 $gyro = $this->format->set_pro_checkbox_value(@wp_unslash( $_POST['gyro'] ));
375 if ($gyro == 'on') {
376 if (!is_ssl()) {
377 wp_send_json_error('<p><span>Warning:</span> Please add SSL to enable Gyroscope for WP VR. </p>');
378 die();
379 }
380 $gyro = true;
381 $deviceorientationcontrol = $this->format->set_checkbox_value(@wp_unslash( $_POST['deviceorientationcontrol'] ));
382 } else {
383 $gyro = false;
384 $deviceorientationcontrol = false;
385 }
386 //===Gyroscopre control===//
387
388 $autoload = $this->format->set_checkbox_value(isset($_POST['autoload']) ? wp_unslash( $_POST['autoload'] ) : '');
389 $control = $this->format->set_checkbox_value(isset($_POST['control']) ? wp_unslash( $_POST['control'] ) : '');
390
391 $scene_fade_duration = isset($_POST['scenefadeduration']) ? sanitize_text_field(wp_unslash( $_POST['scenefadeduration'] )) : '';
392 $preview = isset($_POST['preview']) ? esc_url(wp_unslash( $_POST['preview'] )) : '';
393 $rotation = isset($_POST['rotation']) ? sanitize_text_field(wp_unslash( $_POST['rotation'] )) : '';
394 $autorotation = isset($_POST['autorotation']) ? sanitize_text_field(wp_unslash( $_POST['autorotation'] )) : '';
395
396 $autorotationinactivedelay = isset($_POST['autorotationinactivedelay']) ? sanitize_text_field(wp_unslash( $_POST['autorotationinactivedelay'] )) : '';
397 $autorotationstopdelay = isset($_POST['autorotationstopdelay']) ? sanitize_text_field(wp_unslash( $_POST['autorotationstopdelay'] )) : '';
398
399 //===generic form===//
400 $genericform = sanitize_text_field(isset($_POST['genericform']) ? wp_unslash( $_POST['genericform'] ) : 'off');
401 $genericformshortcode = isset($_POST['genericformshortcode']) ? sanitize_text_field(wp_unslash( $_POST['genericformshortcode'] )) : '' ;
402 $genericformicon = isset($_POST['genericformicon'])
403 ? sanitize_text_field(wp_unslash($_POST['genericformicon']))
404 : ($existing_panodata['genericformicon'] ?? 'fab fa-wpforms');
405 $genericformiconcolor = isset($_POST['genericformiconcolor'])
406 ? sanitize_hex_color(wp_unslash($_POST['genericformiconcolor']))
407 : ($existing_panodata['genericformiconcolor'] ?? '#f7fffb');
408 $genericformiconcolor = $genericformiconcolor ?: '#f7fffb';
409 //===generic form===//
410
411
412 if(isset($_POST['rotation']) && sanitize_text_field(wp_unslash( $_POST['rotation'] )) === 'on' ) {
413 $this->validator->basic_setting_validation($autorotationinactivedelay, $autorotationstopdelay); // Basic setting error control and validation //
414 }
415
416 //===Company Logo===//
417 $cpLogoSwitch = isset($_POST['cpLogoSwitch']) ? wp_unslash( $_POST['cpLogoSwitch'] ) : 'off';
418 $cpLogoImg = isset($_POST['cpLogoImg']) ? wp_unslash( $_POST['cpLogoImg'] ) : '';
419 $cpLogoContent = isset($_POST['cpLogoContent']) ? sanitize_text_field(wp_unslash( $_POST['cpLogoContent'] )) : '';
420 //===Company Logo===//
421
422 //===Explainer video===//
423 $explainerSwitch = isset($_POST['explainerSwitch']) ? wp_unslash( $_POST['explainerSwitch'] ) : 'off';
424 $explainerContent = '';
425 $explainerContent = isset($_POST['explainerContent']) ? wp_unslash( $_POST['explainerContent'] ) : '';
426 //===Explainer video===//
427
428
429 $scene_fade_duration = '';
430 $scene_fade_duration = isset($_POST['scenefadeduration']) ? sanitize_text_field(wp_unslash( $_POST['scenefadeduration'] )) : '';
431
432 // Only validate when publishing, not when saving drafts
433 if ($is_publish_action) {
434 $this->validator->scene_validation($panodata); // Scene content error control and validation //
435
436 $this->validator->empty_scene_validation($panodata); // Empty scene content error control and validation //
437
438 $this->validator->duplicate_hotspot_validation($panodata); // Duplicate error control and validation //
439 }
440
441 $panodata = $this->format->remove_empty_scene_and_hotspot($panodata); // Remove Empty scene and hotspot //
442
443 //===audio===//
444 $bg_music = isset($_POST['bg_music']) ? sanitize_text_field(wp_unslash( $_POST['bg_music'] )) : 'off';
445 $bg_music_url = isset($_POST['bg_music_url']) ? esc_url_raw(wp_unslash( $_POST['bg_music_url'] )) : '';
446 $autoplay_bg_music = isset($_POST['autoplay_bg_music']) ? sanitize_text_field(wp_unslash( $_POST['autoplay_bg_music'] )) : 'off';
447 $loop_bg_music = isset($_POST['loop_bg_music']) ? sanitize_text_field(wp_unslash( $_POST['loop_bg_music'] )) : 'off';
448 if ($bg_music == 'on') {
449 if (empty($bg_music_url)) {
450 wp_send_json_error('<p><span>Warning:</span> Please add an audio file as you enabled audio for this tour </p>');
451 die();
452 }
453 }
454 //===audio===//
455 $advanced_control = array(
456 'keyboardzoom' => $keyboardzoom,
457 'diskeyboard' => $diskeyboard,
458 'draggable' => $draggable,
459 'mouseZoom' => $mouseZoom,
460 'gyro' => $gyro,
461 'deviceorientationcontrol' => $deviceorientationcontrol,
462 'compass' => $compass,
463 'vrgallery' => $vrgallery,
464 'vrgallery_title' => $vrgallery_title,
465 'vrgallery_display' => $vrgallery_display,
466 'vrgallery_icon_size' => $vrgallery_icon_size,
467 'bg_music' => $bg_music,
468 'bg_music_url' => $bg_music_url,
469 'autoplay_bg_music' => $autoplay_bg_music,
470 'loop_bg_music' => $loop_bg_music,
471 'cpLogoSwitch' => $cpLogoSwitch,
472 'cpLogoImg' => $cpLogoImg,
473 'cpLogoContent' => $cpLogoContent,
474 'hfov' => $default_global_zoom,
475 'maxHfov' => $max_global_zoom,
476 'minHfov' => $min_global_zoom,
477 'explainerSwitch' => $explainerSwitch,
478 'explainerContent' => $explainerContent,
479 );
480
481 $pano_array = array();
482 $pano_array = array(
483 "panoid" => $panoid,
484 "autoLoad" => $autoload,
485 "showControls" => $control,
486 "customcontrol" => $custom_control,
487 "autoRotate" => $autorotation,
488 "autoRotateInactivityDelay" => $autorotationinactivedelay,
489 "autoRotateStopDelay" => $autorotationstopdelay,
490 "genericform" => $genericform,
491 "genericformshortcode" => $genericformshortcode,
492 "genericformicon" => $genericformicon,
493 "genericformiconcolor" => $genericformiconcolor,
494 "preview" => $preview,
495 "defaultscene" => $default_scene,
496 "scenefadeduration" => $scene_fade_duration,
497 "panodata" => $panodata,
498 "previewtext" => $previewtext);
499 $pano_array = apply_filters( 'prepare_scene_pano_array_with_pro_version', $pano_array, $_POST, $advanced_control );
500 $pano_array = $this->format->prepare_rotation_wrapper_data($pano_array, $rotation);
501 // Prepare tour rotation wrapper data /
502 update_post_meta($postid, 'panodata', $pano_array);
503 do_action( 'wpvr_hotspot_saved', $postid, $pano_array );
504 $response = array(
505 'success' => true,
506 'data' => array(
507 'post_ID' => $postid,
508 'post_status' => get_post_status($postid)
509 )
510 );
511
512 do_action( 'wpvr_tour_settings_saved', $postid );
513
514
515 /**
516 * Delete orphaned analytics data
517 *
518 * @param integer $postid POST ID
519 *
520 * @return void
521 * @since 8.5.16
522 */
523 do_action('wpvr_pro_delete_orphaned_analytics_data', $postid);
524
525 wp_send_json($response);
526 die();
527
528 }
529
530
531 /**
532 * Allow iframe but disallow script tags in user input
533 * @since 8.5.16
534 */
535 public function wpvr_sanitize_iframe_only( $input ) {
536 if ( function_exists( 'wpvr_sanitize_iframe_only' ) ) {
537 return wpvr_sanitize_iframe_only( $input );
538 }
539
540 // Start with standard allowed post HTML (p, a, strong, etc.)
541 $allowed_tags = wp_kses_allowed_html( 'post' );
542
543 // Explicitly allow <iframe> with specific safe attributes
544 $allowed_tags['iframe'] = array(
545 'src' => true,
546 'width' => true,
547 'height' => true,
548 'title' => true,
549 'frameborder' => true,
550 'allow' => true,
551 'allowfullscreen' => true,
552 'referrerpolicy' => true,
553 );
554
555 // Sanitize input
556 return wp_kses( $input, $allowed_tags );
557 }
558
559
560
561 /**
562 * Responsible for showing Scene Preview
563 *
564 * @param string $panoid
565 * @param string $panovideo
566 *
567 * @return wp_send_json_success
568 * @since 8.0.0
569 */
570 public function wpvr_scene_preview($panoid, $panovideo)
571 {
572 $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
573 if ( ! wp_verify_nonce( $nonce, 'wpvr' ) ) {
574 wp_die( 'Permission denied.' );
575 }
576 $panodata = $this->format->prepare_panodata( isset( $_POST['panodata'] ) ? wp_unslash( $_POST['panodata'] ) : '' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
577
578 $control = $this->format->set_checkbox_value(wp_unslash( $_POST['control'] ));
579 $autoload = $this->format->set_checkbox_value(wp_unslash( $_POST['autoload'] ));
580
581 $compass = $this->format->set_checkbox_on_value(@wp_unslash( $_POST['compass'] ));
582 $mouseZoom = $this->format->set_checkbox_value(@wp_unslash( $_POST['mouseZoom'] ));
583 $draggable = $this->format->set_checkbox_value(wp_unslash( $_POST['draggable'] ) ?? null);
584 $gzoom = $this->format->set_pro_checkbox_value(@wp_unslash( $_POST['gzoom'] ));
585 $diskeyboard = $this->format->set_checkbox_value(@wp_unslash( $_POST['diskeyboard'] ));
586 $keyboardzoom = $this->format->set_checkbox_value(@wp_unslash( $_POST['keyboardzoom'] ));
587
588 $gyro = $this->format->set_checkbox_on_value(@wp_unslash( $_POST['gyro'] ));
589 $deviceorientationcontrol = $this->format->set_checkbox_value(@wp_unslash( $_POST['deviceorientationcontrol'] ));
590
591 $floor_plan_enabler = $this->format->set_pro_checkbox_value(@wp_unslash( $_POST['wpvr_floor_plan_enabler'] ));
592 $floor_plan_image = isset($_POST['wpvr_floor_plan_image']) ? wp_unslash( $_POST['wpvr_floor_plan_image'] ) : '';
593
594 $scene_fade_duration = sanitize_text_field(wp_unslash( $_POST['scenefadeduration'] ));
595 $preview = esc_url(wp_unslash( $_POST['preview'] ));
596
597 $default_scene = '';
598
599 $rotation = sanitize_text_field(wp_unslash( $_POST['rotation'] ));
600 $autorotation = sanitize_text_field(wp_unslash( $_POST['autorotation'] ));
601 $autorotationinactivedelay = sanitize_text_field(wp_unslash( $_POST['autorotationinactivedelay'] ));
602 $autorotationstopdelay = sanitize_text_field(wp_unslash( $_POST['autorotationstopdelay'] ));
603
604 $default_global_zoom = '';
605 $max_global_zoom = '';
606 $min_global_zoom = '';
607 if ($gzoom == 'on') {
608 $default_global_zoom = wp_unslash( $_POST['dzoom'] );
609 $max_global_zoom = wp_unslash( $_POST['maxzoom'] );
610 $min_global_zoom = wp_unslash( $_POST['minzoom'] );
611 }
612
613 $default_scene = $this->format->prepare_default_scene($panodata);
614
615 if(isset($_POST['rotation']) && sanitize_text_field(wp_unslash( $_POST['rotation'] )) === 'on' ) {
616 $this->validator->basic_setting_validation($autorotationinactivedelay, $autorotationstopdelay); // Basic setting error control and validation //
617 }
618 $this->validator->scene_validation($panodata); // Scene content error control and validation //
619
620 $this->validator->empty_scene_validation($panodata); // Empty scene content error control and validation //
621
622 $this->validator->duplicate_hotspot_validation($panodata);
623 if($floor_plan_enabler == 'on'){
624 $this->validator->empty_floor_plan_image_validation($floor_plan_image);
625 }
626
627 $default_data = array();
628 if ($gzoom == 'on') {
629 $default_data = array("firstScene" => $default_scene, "sceneFadeDuration" => $scene_fade_duration, "hfov" => $default_global_zoom, "maxHfov" => $max_global_zoom, "minHfov" => $min_global_zoom);
630 } else {
631 $default_data = array("firstScene" => $default_scene, "sceneFadeDuration" => $scene_fade_duration);
632 }
633
634 $scene_data = $this->format->prepare_scene_data_for_preview($panodata);
635
636
637 $pano_id_array = array();
638 $pano_id_array = array("panoid" => $panoid);
639 $pano_response = array();
640 $pano_response = array("autoLoad" => $autoload, "defaultZoom" => $default_global_zoom, "minZoom" => $min_global_zoom, "maxZoom" => $max_global_zoom, "showControls" => $control, "compass" => $compass, "orientationOnByDefault" => $deviceorientationcontrol, "mouseZoom" => $mouseZoom, "draggable" => $draggable, "disableKeyboardCtrl" => $diskeyboard, 'keyboardZoom' => $keyboardzoom, "preview" => $preview, "autoRotate" => $autorotation, "autoRotateInactivityDelay" => $autorotationinactivedelay, "autoRotateStopDelay" => $autorotationstopdelay, "default" => $default_data, "scenes" => $scene_data);
641
642 $pano_response = $this->format->prepare_rotation_wrapper_data($pano_response, $rotation);
643
644 $is_pro = apply_filters('is_wpvr_pro_active',false);
645 $status = get_option('wpvr_edd_license_status');
646 $pano_floor_plan = array();
647 $call_to_action = array();
648 if ($status !== false && 'valid' == $status && $is_pro) {
649 $pano_floor_plan = array(
650 "floor_plan_tour_enabler" => $floor_plan_enabler,
651 "floor_plan_attachment_url" => $floor_plan_image
652 );
653 if ( 'on' === $floor_plan_enabler ) {
654 do_action( 'wpvr_floor_plan_configured', $postid, $pano_floor_plan );
655 }
656 $call_to_action = array(
657 'button_enable' =>sanitize_text_field(wp_unslash( $_POST['callToAction'] )),
658 'button_text' =>sanitize_text_field(wp_unslash( $_POST['buttontext'] )),
659 'button_url' =>sanitize_text_field(wp_unslash( $_POST['buttonurl'] ))
660 );
661 }
662
663 $response = array();
664 $response = array($pano_id_array, $pano_response, $panovideo,$pano_floor_plan,$call_to_action);
665 wp_send_json_success($response);
666 }
667
668
669 /**
670 * Render shortcode for scene and hotspot post data
671 *
672 * @param array $postdata
673 * @param string $panoid
674 * @param integer $id
675 * @param mixed $radius
676 * @param mixed $width
677 * @param mixed $height
678 *
679 * @return string
680 * @since 8.0.0
681 */
682 public function render_scene_shortcode($postdata, $panoid, $id, $radius, $width, $height, $mobile_height)
683 {
684 if ( function_exists( 'wpvr_enqueue_frontend_scripts' ) ) {
685 wpvr_enqueue_frontend_scripts( 'scene' );
686 }
687 $postdata = is_array( $postdata ) ? wpvr_get_effective_panodata( $postdata ) : [];
688 $show_scene_info = ( $postdata['scene-info-enabled'] ?? 'on' ) !== 'off';
689 $tour_layout = is_array( $postdata['tourLayout'] ?? null )
690 ? ( $postdata['tourLayout']['layout'] ?? 'default' )
691 : ( $postdata['tourLayout'] ?? 'default' );
692 $is_classic_layout = 'layout1' !== $tour_layout;
693
694 if (
695 ( isset( $_GET['bricks'] ) && wp_unslash( $_GET['bricks'] ) === 'run' ) ||
696 ( defined('DOING_AJAX') && DOING_AJAX && isset( $_REQUEST['action'] ) && wp_unslash( $_REQUEST['action'] ) === 'bricks_render_element' ) ||
697 ( defined('REST_REQUEST') && REST_REQUEST && isset($_SERVER['REQUEST_URI']) && strpos( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ), '/wp-json/bricks/v1/render_element') !== false )
698 ) {
699 return esc_html__('Bricks Editor Mode - WPVR Preview is not available.', 'wpvr');
700 }
701
702 do_action('rex_wpvr_embadded_tour', $id);
703
704 $control = false;
705 if (isset($postdata['showControls'])) {
706 $control = $postdata['showControls'];
707 }
708
709 if ($control) {
710 if (isset($postdata['customcontrol']) && is_array($postdata['customcontrol'])) {
711 $custom_control = $postdata['customcontrol'];
712 $gyro_enabled = isset($postdata['gyro']) && in_array($postdata['gyro'], array(true, 1, '1', 'on'), true);
713 $custom_control['gyroSwitch'] = $gyro_enabled ? 'on' : 'off';
714 $gyro_button_enabled = wpvr_isMobileDevice() && $custom_control['gyroSwitch'] == "on" && $custom_control['gyroscopeSwitch'] == "on";
715 if ($custom_control['panupSwitch'] == "on" || $custom_control['panDownSwitch'] == "on" || $custom_control['panLeftSwitch'] == "on" || $custom_control['panRightSwitch'] == "on" || $custom_control['panZoomInSwitch'] == "on" || $custom_control['panZoomOutSwitch'] == "on" || $custom_control['panFullscreenSwitch'] == "on" || $gyro_button_enabled || $custom_control['backToHomeSwitch'] == "on") {
716 $control = false;
717 }
718 }
719 }
720
721 $vrgallery = false;
722 if (isset($postdata['vrgallery'])) {
723 $vrgallery = $postdata['vrgallery'];
724 }
725
726 $vrgallery_title = false;
727 if (isset($postdata['vrgallery_title'])) {
728 $vrgallery_title = $postdata['vrgallery_title'];
729 }
730
731 $vrgallery_display = false;
732 if (isset($postdata['vrgallery_display'])) {
733 $vrgallery_display = $postdata['vrgallery_display'];
734 }
735 $vrgallery_icon_size = false;
736 if (isset($postdata['vrgallery_icon_size'])) {
737 $vrgallery_icon_size = $postdata['vrgallery_icon_size'];
738 }
739 $gyro = false;
740 $gyro_orientation = false;
741 if (isset($postdata['gyro'])) {
742 $gyro = in_array($postdata['gyro'], array(true, 1, '1', 'on'), true);
743 if ($gyro && isset($postdata['deviceorientationcontrol'])) {
744 $gyro_orientation = in_array($postdata['deviceorientationcontrol'], array(true, 1, '1', 'on'), true);
745 }
746 }
747 //== Floor plan handle ==//
748 $floor_plan_enable = 'off';
749 $floor_plan_image = '';
750 if (isset($postdata['floor_plan_tour_enabler']) && $postdata['floor_plan_tour_enabler'] == 'on'){
751 $floor_plan_enable = $postdata['floor_plan_tour_enabler'];
752 if(isset($postdata['floor_plan_attachment_url']) && !empty($postdata['floor_plan_attachment_url'])){
753 $floor_plan_image = $postdata['floor_plan_attachment_url'];
754 }
755 }
756
757 $compass = false;
758 $audio_right = "5px";
759 if (isset($postdata['compass'])) {
760 $compass = $postdata['compass'] == 'on' || $postdata['compass'] != null ? true : false;
761 if ($compass) {
762 $audio_right = "60px";
763 }
764 }
765 $floor_map_right = "25px";
766 if((isset($postdata['compass']) && $postdata['compass'] == 'on') && (isset($postdata['bg_music']) && $postdata['bg_music'] == 'on')){
767 $floor_map_right = "85px";
768 }elseif(isset($postdata['compass']) && $postdata['compass'] == 'on'){
769 $floor_map_right = "55px";
770 }elseif (isset($postdata['bg_music']) && $postdata['bg_music'] == "on") {
771 $floor_map_right = "25px";
772 }
773
774
775 //===explainer handle===//
776
777 $explainer_right = "10px";
778 if ((isset($postdata['compass']) && $postdata['compass'] == 'on') && (isset($postdata['bg_music']) && $postdata['bg_music'] == 'on') && ( $floor_plan_enable == 'on' && !empty($floor_plan_image) ) ) {
779 $explainer_right = "130px";
780 } elseif (isset($postdata['compass']) && $postdata['compass'] == 'on' && ($floor_plan_enable == 'on' && !empty($floor_plan_image) )) {
781 $explainer_right = "100px";
782 } elseif (isset($postdata['bg_music']) && $postdata['bg_music'] == "on" && ($floor_plan_enable == 'on' && !empty($floor_plan_image) )) {
783 $explainer_right = "55px";
784 } elseif((isset($postdata['compass']) && $postdata['compass'] == 'on') && (isset($postdata['bg_music']) && $postdata['bg_music'] == 'on') ) {
785 $explainer_right = "80px";
786 }elseif (isset($postdata['compass']) && $postdata['compass'] == 'on') {
787 $explainer_right = "55px";
788 } elseif (isset($postdata['bg_music']) && $postdata['bg_music'] == "on") {
789 $explainer_right = "30px";
790 } elseif ($floor_plan_enable == 'on' && !empty($floor_plan_image) ) {
791 $explainer_right = "70px";
792 }
793
794
795 $enable_cardboard = '';
796 $is_cardboard = get_option('wpvr_cardboard_disable');
797 if(wpvr_isMobileDevice() && $is_cardboard == 'true' ){
798 $enable_cardboard = 'enable-cardboard';
799 $audio_right = "73px";
800 if (isset($postdata['compass'])) {
801 $compass = $postdata['compass'] == 'on' || $postdata['compass'] != null ? true : false;
802 if ($compass) {
803 $audio_right = "130px";
804 }
805 }
806 //===Floor plan handle===//
807 $floor_map_right = "60px";
808 if((isset($postdata['compass']) && $postdata['compass'] == 'on') && (isset($postdata['bg_music']) && $postdata['bg_music'] == 'on')){
809 $floor_map_right = "150px";
810 }elseif(isset($postdata['compass']) && $postdata['compass'] == 'on'){
811 $floor_map_right = "120px";
812 }elseif (isset($postdata['bg_music']) && $postdata['bg_music'] == "on") {
813 $floor_map_right = "90px";
814 }
815
816 //===explainer handle===//
817 $explainer_right = "65px";
818
819 if ((isset($postdata['compass']) && $postdata['compass'] == true) && (isset($postdata['bg_music']) && $postdata['bg_music'] == 'on')) {
820 $explainer_right = "150px";
821 } elseif((isset($postdata['compass']) && $postdata['compass'] == true) && (isset($postdata['bg_music']) && $postdata['bg_music'] == 'on') && ($floor_plan_enable == 'on' && !empty($floor_plan_image) )) {
822 $explainer_right = "180px";
823 } elseif (isset($postdata['compass']) && $postdata['compass'] == true && ($floor_plan_enable == 'on' && !empty($floor_plan_image) )) {
824 $explainer_right = "150px";
825 } elseif (isset($postdata['bg_music']) && $postdata['bg_music'] == "on" && ($floor_plan_enable == 'on' && !empty($floor_plan_image) )) {
826 $explainer_right = "120px";
827 }elseif (isset($postdata['compass']) && $postdata['compass'] == true) {
828 $explainer_right = "130px";
829 } elseif (isset($postdata['bg_music']) && $postdata['bg_music'] == "on") {
830 $explainer_right = "90px";
831 }elseif ($floor_plan_enable == 'on' && !empty($floor_plan_image) ) {
832 $explainer_right = "90px";
833 }
834 }
835
836 //===explainer handle===//
837
838 $mouseZoom = true;
839 if (isset($postdata['mouseZoom'])) {
840 if($postdata['mouseZoom'] == "off") {
841 $mouseZoom = false;
842 }
843 else {
844 $mouseZoom = true;
845 }
846 }
847
848 $draggable = true;
849 if (isset($postdata['draggable'])) {
850 $draggable = ! in_array( $postdata['draggable'], array( 'off', 'false', false ), true );
851 }
852
853 $diskeyboard = false;
854 if (isset($postdata['diskeyboard'])) {
855 $diskeyboard = ! in_array( $postdata['diskeyboard'], array( 'off', 'false', false ), true );
856 }
857
858 $keyboardzoom = true;
859 if (isset($postdata['keyboardzoom'])) {
860 $keyboardzoom = ! in_array( $postdata['keyboardzoom'], array( 'off', 'false', false ), true );
861 }
862
863 $autoload = false;
864
865 if (isset($postdata['autoLoad'])) {
866 $autoload = in_array( $postdata['autoLoad'], array( true, 1, '1', 'on', 'true' ), true );
867 }
868
869 $default_scene = '';
870 if (isset($postdata['defaultscene'])) {
871 $default_scene = $postdata['defaultscene'];
872 }
873
874 $default_global_zoom = '';
875 if (isset($postdata['hfov'])) {
876 $default_global_zoom = $postdata['hfov'];
877 }
878
879 $max_global_zoom = '';
880 if (isset($postdata['maxHfov'])) {
881 $max_global_zoom = $postdata['maxHfov'];
882 }
883
884 $min_global_zoom = '';
885 if (isset($postdata['minHfov'])) {
886 $min_global_zoom = $postdata['minHfov'];
887 }
888
889 $preview = '';
890 if (isset($postdata['preview'])) {
891 $preview = $postdata['preview'];
892 }
893
894 $autorotation = '';
895 if (isset($postdata["autoRotate"])) {
896 $autorotation = $postdata["autoRotate"];
897 }
898 $autorotationinactivedelay = '';
899 if (isset($postdata["autoRotateInactivityDelay"])) {
900 $autorotationinactivedelay = $postdata["autoRotateInactivityDelay"];
901 }
902 $autorotationstopdelay = '';
903 if (isset($postdata["autoRotateStopDelay"])) {
904 $autorotationstopdelay = $postdata["autoRotateStopDelay"];
905 }
906
907 $scene_fade_duration = '';
908 if (isset($postdata['scenefadeduration'])) {
909 $scene_fade_duration = $postdata['scenefadeduration'];
910 }
911
912 $panodata = array();
913 if (isset($postdata['panodata']) && is_array($postdata['panodata'])) {
914 $panodata = $postdata['panodata'];
915 }
916
917 $hotspoticoncolor = '#00b4ff';
918 $hotspotblink = 'on';
919 $default_data = array();
920 if ($default_global_zoom != '' && $max_global_zoom != '' && $min_global_zoom != '') {
921 $default_data = array("firstScene" => $default_scene, "sceneFadeDuration" => $scene_fade_duration, "hfov" => $default_global_zoom, "maxHfov" => $max_global_zoom, "minHfov" => $min_global_zoom);
922 } else {
923 $default_data = array("firstScene" => $default_scene, "sceneFadeDuration" => $scene_fade_duration);
924 }
925
926 $scene_data = array();
927
928 if (!empty($panodata["scene-list"])) {
929 foreach ($panodata["scene-list"] as $panoscenes) {
930 $scene_ititle = '';
931 if (isset($panoscenes["scene-ititle"])) {
932 $scene_ititle = sanitize_text_field($panoscenes["scene-ititle"]);
933 }
934
935 $scene_author = '';
936 if (isset($panoscenes["scene-author"])) {
937 $scene_author = sanitize_text_field($panoscenes["scene-author"]);
938 }
939
940 $scene_author_url = '';
941 if (isset($panoscenes["scene-author-url"])) {
942 $scene_author_url = esc_url($panoscenes["scene-author-url"]);
943 }
944
945 $scene_vaov = 180;
946 if (isset($panoscenes["scene-vaov"])) {
947 $scene_vaov = (float)$panoscenes["scene-vaov"];
948 }
949
950 $scene_haov = 360;
951 if (isset($panoscenes["scene-haov"])) {
952 $scene_haov = (float)$panoscenes["scene-haov"];
953 }
954
955
956 $scene_vertical_offset = 0;
957 if (isset($panoscenes["scene-vertical-offset"])) {
958 $scene_vertical_offset = (float)$panoscenes["scene-vertical-offset"];
959 }
960
961 $default_scene_pitch = null;
962 if (isset($panoscenes["scene-pitch"])) {
963 $default_scene_pitch = (float)$panoscenes["scene-pitch"];
964 }
965
966 $default_scene_yaw = null;
967 if (isset($panoscenes["scene-yaw"])) {
968 $default_scene_yaw = (float)$panoscenes["scene-yaw"];
969 }
970
971 $scene_max_pitch = '';
972 if (isset($panoscenes["scene-maxpitch"])) {
973 $scene_max_pitch = (float)$panoscenes["scene-maxpitch"];
974 }
975
976
977 $scene_min_pitch = '';
978 if (isset($panoscenes["scene-minpitch"])) {
979 $scene_min_pitch = (float)$panoscenes["scene-minpitch"];
980 }
981
982
983 $scene_max_yaw = '';
984 if (isset($panoscenes["scene-maxyaw"])) {
985 $scene_max_yaw = (float)$panoscenes["scene-maxyaw"];
986 }
987
988
989 $scene_min_yaw = '';
990 if (isset($panoscenes["scene-minyaw"])) {
991 $scene_min_yaw = (float)$panoscenes["scene-minyaw"];
992 }
993
994 $default_zoom = 100;
995 if (isset($panoscenes["scene-zoom"]) && $panoscenes["scene-zoom"] != "") {
996 $default_zoom = $panoscenes["scene-zoom"];
997 } else {
998 if ($default_global_zoom != '') {
999 $default_zoom = (int)$default_global_zoom;
1000 }
1001 }
1002
1003
1004 $max_zoom = 120;
1005 if (isset($panoscenes["scene-maxzoom"]) && $panoscenes["scene-maxzoom"] != '') {
1006 $max_zoom = (int)$panoscenes["scene-maxzoom"];
1007 } else {
1008 if ($max_global_zoom != '') {
1009 $max_zoom = (int)$max_global_zoom;
1010 }
1011 }
1012
1013
1014
1015 $min_zoom = 50;
1016 if (isset($panoscenes["scene-minzoom"]) && $panoscenes["scene-minzoom"] != '') {
1017 $min_zoom = (int)$panoscenes["scene-minzoom"];
1018 } else {
1019 if ($min_global_zoom != '') {
1020 $min_zoom = (int)$min_global_zoom;
1021 }
1022 }
1023
1024
1025 $hotspot_datas = array();
1026 if (isset($panoscenes["hotspot-list"])) {
1027 $hotspot_datas = $panoscenes["hotspot-list"];
1028 }
1029
1030 $hotspots = array();
1031
1032
1033 foreach ($hotspot_datas as $hotspot_data) {
1034 $status = get_option('wpvr_edd_license_status');
1035 if ($status !== false && $status == 'valid') {
1036 if (!empty($hotspot_data["hotspot-customclass-pro"]) && $hotspot_data["hotspot-customclass-pro"] != 'none') {
1037 $hotspot_data['hotspot-customclass'] = $hotspot_data["hotspot-customclass-pro"] . ' custom-' . $id.'-' . $panoscenes['scene-id'] .'-'. sanitize_html_class($hotspot_data['hotspot-title']);
1038 }
1039 if (isset($hotspot_data['hotspot-blink'])) {
1040 $hotspotblink = $hotspot_data['hotspot-blink'];
1041 }
1042 }
1043 $has_hotspot_scene_pitch = isset($hotspot_data["hotspot-scene-pitch"])
1044 && $hotspot_data["hotspot-scene-pitch"] !== '';
1045 $has_hotspot_scene_yaw = isset($hotspot_data["hotspot-scene-yaw"])
1046 && $hotspot_data["hotspot-scene-yaw"] !== '';
1047
1048 $hotspot_type = $hotspot_data["hotspot-type"] !== 'scene' ? 'info' : $hotspot_data["hotspot-type"];
1049 $hotspot_content = '';
1050
1051 ob_start();
1052 do_action('wpvr_hotspot_content', $hotspot_data);
1053 $hotspot_content = ob_get_clean();
1054
1055 $is_fluent_form = isset($hotspot_data["hotspot-type"]) && 'fluent_form' === $hotspot_data["hotspot-type"];
1056
1057 if ($is_fluent_form) {
1058 if (!empty($hotspot_content)) {
1059 $hotspot_content = preg_replace('/<script\b[^>]*>[\s\S]*?<\/script>/i', '', $hotspot_content);
1060 }
1061 } else {
1062 if (!$hotspot_content) {
1063 $hotspot_content = $hotspot_data["hotspot-content"] ?? '';
1064 }
1065 }
1066
1067 $hotspot_url = $this->normalize_hotspot_external_url($hotspot_data["hotspot-url"] ?? '');
1068
1069 if (isset($hotspot_data["hotspot-url-open"])) {
1070 // New UI: "on" means new tab; legacy renderer: "on" means same tab.
1071 $wpvr_url_open = $hotspot_data["hotspot-url-open"] === 'on' ? 'off' : 'on';
1072 } elseif (isset($hotspot_data["wpvr_url_open"][0])) {
1073 $wpvr_url_open = $hotspot_data["wpvr_url_open"][0];
1074 } else {
1075 $wpvr_url_open = "off";
1076 }
1077 $on_hover_content = preg_replace_callback(
1078 '/<p>\s*(<img[^>]*>)\s*<br>\s*<\/p>/i',
1079 function ($matches) {
1080 return $matches[1];
1081 },
1082 $hotspot_data['hotspot-hover'] ?? ''
1083 );
1084
1085
1086 $on_hover_content = $this->sanitize_content_preserve_styles($on_hover_content ?? '', false);
1087 $on_click_content = preg_replace_callback('/<img[^>]*>/', "replace_callback", $hotspot_content ?? '');
1088 $on_click_content = $this->sanitize_content_preserve_styles($on_click_content ?? '', $is_fluent_form);
1089
1090 // Shape is independent of the optional custom icon in the
1091 // new editor, so it must always be included in the public
1092 // hotspot configuration.
1093 $hotspot_shape = isset( $hotspot_data['hotspot-shape'] )
1094 && in_array( $hotspot_data['hotspot-shape'], [ 'round', 'square', 'hexagon' ], true )
1095 ? $hotspot_data['hotspot-shape']
1096 : 'round';
1097 $hotspot_data_for_on_click=[];
1098 if( ('info' === $hotspot_type || 'fluent_form' === $hotspot_data["hotspot-type"]) && !empty($on_click_content)){
1099 $hotspot_data_for_on_click = [
1100 'on_click_content' => $on_click_content,
1101 'tour_id' => $id,
1102 'scene_id' => $panoscenes['scene-id'],
1103 'hotspot_id' => sanitize_html_class($hotspot_data['hotspot-title']),
1104 ];
1105 } elseif('info' === $hotspot_type && !empty($hotspot_url)){
1106 $hotspot_data_for_on_click = [
1107 'on_click_content' => '',
1108 'tour_id' => $id,
1109 'scene_id' => $panoscenes['scene-id'],
1110 'hotspot_id' => sanitize_html_class($hotspot_data['hotspot-title']),
1111 ];
1112 }
1113
1114 $hotspot_info = array(
1115 "text" => esc_html(sanitize_text_field($hotspot_data["hotspot-title"])),
1116 "pitch" => $hotspot_data["hotspot-pitch"],
1117 "yaw" => $hotspot_data["hotspot-yaw"],
1118 "type" => $hotspot_type,
1119 "cssClass" => $hotspot_data["hotspot-customclass"],
1120 "URL" => $hotspot_url,
1121 "wpvr_url_open" => $wpvr_url_open,
1122 "clickHandlerArgs" => $hotspot_data_for_on_click,
1123 'createTooltipArgs' => !empty(trim($on_hover_content)) ? trim($on_hover_content) : '',
1124 "sceneId" => $hotspot_data["hotspot-scene"],
1125 'hotspot_type' => $hotspot_data['hotspot-type'],
1126 'hotspot_target' => 'notBlank',
1127 'hotspot_shape' => $hotspot_shape,
1128 );
1129
1130 // An omitted entry angle lets Pannellum use the target
1131 // scene's own default face. Keep explicit zero values.
1132 if (!empty($hotspot_data["hotspot-scene"])) {
1133 if ($has_hotspot_scene_pitch) {
1134 $hotspot_info['targetPitch'] = (float)$hotspot_data["hotspot-scene-pitch"];
1135 }
1136 if ($has_hotspot_scene_yaw) {
1137 $hotspot_info['targetYaw'] = (float)$hotspot_data["hotspot-scene-yaw"];
1138 }
1139 }
1140 $hotspot_info['URL'] = ($hotspot_data['hotspot-type'] === 'fluent_form' || $hotspot_data['hotspot-type'] === 'wc_product') ? '' : $hotspot_info['URL'];
1141
1142 if ($hotspot_data["hotspot-customclass"] == 'none' || $hotspot_data["hotspot-customclass"] == '') {
1143 unset($hotspot_info["cssClass"]);
1144 }
1145 array_push($hotspots, $hotspot_info);
1146 }
1147
1148 $device_scene = $panoscenes['scene-attachment-url'];
1149 $mobile_media_resize = get_option('mobile_media_resize');
1150 $file_accessible = ini_get('allow_url_fopen');
1151 if ($mobile_media_resize == "true" && $device_scene ) {
1152 if ($file_accessible == "1") {
1153 $image_info = getimagesize($device_scene);
1154 if ( isset($image_info[0]) && $image_info[0] > 4096) {
1155 $src_to_id_for_mobile = '';
1156 $src_to_id_for_desktop = '';
1157 if (wpvr_isMobileDevice()) {
1158 $src_to_id_for_mobile = attachment_url_to_postid($panoscenes['scene-attachment-url']);
1159 if ($src_to_id_for_mobile) {
1160 $mobile_scene = wp_get_attachment_image_src($src_to_id_for_mobile, 'wpvr_mobile');
1161 if ($mobile_scene[3]) {
1162 $device_scene = $mobile_scene[0];
1163 }
1164 }
1165 } else {
1166 $src_to_id_for_desktop = attachment_url_to_postid($panoscenes['scene-attachment-url']);
1167 if ($src_to_id_for_desktop) {
1168 $desktop_scene = wp_get_attachment_image_src($src_to_id_for_mobile, 'full');
1169 if (isset($desktop_scene[0])) {
1170 $device_scene = $desktop_scene[0];
1171 }
1172 }
1173 }
1174 }
1175 }
1176 }
1177
1178 $scene_info = array();
1179
1180 if ($panoscenes["scene-type"] == 'cubemap') {
1181 $pano_attachment = array(
1182 $panoscenes["scene-attachment-url-face0"],
1183 $panoscenes["scene-attachment-url-face1"],
1184 $panoscenes["scene-attachment-url-face2"],
1185 $panoscenes["scene-attachment-url-face3"],
1186 $panoscenes["scene-attachment-url-face4"],
1187 $panoscenes["scene-attachment-url-face5"]
1188 );
1189
1190 $scene_info = array("type" => $panoscenes["scene-type"], "cubeMap" => $pano_attachment, "pitch" => $default_scene_pitch, "maxPitch" => $scene_max_pitch, "minPitch" => $scene_min_pitch, "maxYaw" => $scene_max_yaw, "minYaw" => $scene_min_yaw, "yaw" => $default_scene_yaw, "hfov" => $default_zoom, "maxHfov" => $max_zoom, "minHfov" => $min_zoom, "title" => $scene_ititle, "author" => $scene_author, "authorURL" => $scene_author_url, "vaov" => $scene_vaov, "haov" => $scene_haov, "vOffset" => $scene_vertical_offset, "hotSpots" => $hotspots);
1191 } else {
1192 $scene_info = array("type" => $panoscenes["scene-type"], "panorama" => $device_scene, "pitch" => $default_scene_pitch, "maxPitch" => $scene_max_pitch, "minPitch" => $scene_min_pitch, "maxYaw" => $scene_max_yaw, "minYaw" => $scene_min_yaw, "yaw" => $default_scene_yaw, "hfov" => $default_zoom, "maxHfov" => $max_zoom, "minHfov" => $min_zoom, "title" => $scene_ititle, "author" => $scene_author, "authorURL" => $scene_author_url, "vaov" => $scene_vaov, "haov" => $scene_haov, "vOffset" => $scene_vertical_offset, "hotSpots" => $hotspots);
1193 }
1194
1195
1196 if (isset($panoscenes["ptyscene"])) {
1197 if ($panoscenes["ptyscene"] == "off") {
1198 unset($scene_info['pitch']);
1199 unset($scene_info['yaw']);
1200 }
1201 }
1202
1203 if (!$show_scene_info || empty($panoscenes["scene-ititle"])) {
1204 unset($scene_info['title']);
1205 }
1206 if (!$show_scene_info || empty($panoscenes["scene-author"])) {
1207 unset($scene_info['author']);
1208 }
1209 if (!$show_scene_info || empty($panoscenes["scene-author-url"])) {
1210 unset($scene_info['authorURL']);
1211 }
1212
1213 if (empty($scene_vaov)) {
1214 unset($scene_info['vaov']);
1215 }
1216
1217 if (empty($scene_haov)) {
1218 unset($scene_info['haov']);
1219 }
1220
1221 if (empty($scene_vertical_offset)) {
1222 unset($scene_info['vOffset']);
1223 }
1224
1225 if (isset($panoscenes["cvgscene"])) {
1226 if ($panoscenes["cvgscene"] == "off") {
1227 unset($scene_info['maxPitch']);
1228 unset($scene_info['minPitch']);
1229 }
1230 }
1231 if (empty($panoscenes["scene-maxpitch"])) {
1232 unset($scene_info['maxPitch']);
1233 }
1234
1235 if (empty($panoscenes["scene-minpitch"])) {
1236 unset($scene_info['minPitch']);
1237 }
1238
1239 if (isset($panoscenes["chgscene"])) {
1240 if ($panoscenes["chgscene"] == "off") {
1241 unset($scene_info['maxYaw']);
1242 unset($scene_info['minYaw']);
1243 }
1244 }
1245 if (empty($panoscenes["scene-maxyaw"])) {
1246 unset($scene_info['maxYaw']);
1247 }
1248
1249 if (empty($panoscenes["scene-minyaw"])) {
1250 unset($scene_info['minYaw']);
1251 }
1252
1253 // if (isset($panoscenes["czscene"])) {
1254 // if ($panoscenes["czscene"] == "off") {
1255 // unset($scene_info['hfov']);
1256 // unset($scene_info['maxHfov']);
1257 // unset($scene_info['minHfov']);
1258 // }
1259 // }
1260
1261 $scene_array = array();
1262 $scene_array = array(
1263 $panoscenes["scene-id"] => $scene_info
1264 );
1265 $scene_data[$panoscenes["scene-id"]] = $scene_info;
1266 }
1267 }
1268
1269 $pano_id_array = array();
1270 $pano_id_array = array("panoid" => $panoid);
1271 $pano_response = array();
1272 $pano_response = array("autoLoad" => $autoload, "showControls" => $control, "orientationSupport" => 'false', "compass" => $compass, 'orientationOnByDefault' => $gyro_orientation, "mouseZoom" => $mouseZoom, "draggable" => $draggable, 'disableKeyboardCtrl' => $diskeyboard, 'keyboardZoom' => $keyboardzoom, "preview" => $preview, "autoRotate" => $autorotation, "autoRotateInactivityDelay" => $autorotationinactivedelay, "autoRotateStopDelay" => $autorotationstopdelay, "default" => $default_data, "scenes" => $scene_data);
1273 if (empty($autorotation)) {
1274 unset($pano_response['autoRotate']);
1275 unset($pano_response['autoRotateInactivityDelay']);
1276 unset($pano_response['autoRotateStopDelay']);
1277 }
1278 if (empty($autorotationinactivedelay)) {
1279 unset($pano_response['autoRotateInactivityDelay']);
1280 }
1281 if (empty($autorotationstopdelay)) {
1282 unset($pano_response['autoRotateStopDelay']);
1283 }
1284 $response = array();
1285 $response = array($pano_id_array, $pano_response);
1286 if (!empty($response)) {
1287 $response = json_encode($response);
1288 }
1289
1290
1291 if (empty($width)) {
1292 $width = '600px';
1293 }
1294 if (empty($height)) {
1295 $height = '400px';
1296 }
1297 $foreground_color = '#fff';
1298 $pulse_color = wpvr_hex2rgb($hotspoticoncolor);
1299 $rgb = wpvr_HTMLToRGB($hotspoticoncolor);
1300 $hsl = wpvr_RGBToHSL($rgb);
1301 if ($hsl->lightness > 200) {
1302 $foreground_color = '#000000';
1303 } else {
1304 $foreground_color = '#fff';
1305 }
1306 $html = '';
1307
1308 $html .= '<style>';
1309 if ($width == 'embed') {
1310 $html .= 'body{
1311 overflow: hidden;
1312 }';
1313 }
1314 $status = get_option('wpvr_edd_license_status');
1315 if ($status !== false && $status == 'valid') {
1316 if(isset($postdata['customcss_enable']) && $postdata['customcss_enable'] == 'on'){
1317 $html .= isset($postdata['customcss']) ? $postdata['customcss'] : '';
1318 }
1319 }
1320 $pano_suffix = (strpos($panoid, 'pano') === 0) ? substr($panoid, 4) : (string) $panoid;
1321 $panoid2 = 'pano2' . $pano_suffix;
1322 $master_container_id = ($panoid === 'pano' . $id) ? 'master-container' : ('master-container-' . $pano_suffix);
1323 $status = get_option('wpvr_edd_license_status');
1324 if ($status !== false && $status == 'valid' && ! empty( $panodata['scene-list'] ) && is_array( $panodata['scene-list'] )) {
1325 foreach ($panodata['scene-list'] as $panoscenes){
1326 if ( empty( $panoscenes['hotspot-list'] ) || ! is_array( $panoscenes['hotspot-list'] ) ) {
1327 continue;
1328 }
1329 foreach($panoscenes['hotspot-list'] as $hotspot){
1330 if (isset($hotspot['hotspot-customclass-color-icon-value']) && !empty($hotspot['hotspot-customclass-color-icon-value'])) {
1331 $hotspoticoncolor = $hotspot['hotspot-customclass-color-icon-value'];
1332 } else {
1333 $hotspoticoncolor = "#00b4ff";
1334 }
1335 $hotspot_border = '';
1336 if(isset($hotspot['hotspot-border']) && $hotspot['hotspot-border'] == 'on'){
1337 $border_width = $hotspot['hotspot-border-width'];
1338 $border_color = $hotspot['hotspot-border-color'];
1339 $border_style = $hotspot['hotspot-border-style'];
1340 $hotspot_border = 'border: '.$border_width.'px '.$border_style.' '.$border_color.';';
1341 }
1342 $hotspot_background_color = 'background-color: ' . $hotspoticoncolor . ';';
1343 $hotspot_animation = ' animation: icon-pulse' . $panoid . '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ' 1.5s infinite cubic-bezier(.25, 0, 0, 1);
1344 '. $hotspot_border.'';
1345 $pulse_color = wpvr_hex2rgb($hotspoticoncolor);
1346 if (!empty($hotspot["hotspot-customclass-pro"]) && $hotspot["hotspot-customclass-pro"] != 'none') {
1347 $border_radius = ' border-radius: 100%;';
1348 $hotspot_shape = isset($hotspot["hotspot-shape"]) ? $hotspot["hotspot-shape"] : 'round';
1349 if($hotspot_shape === 'square'){
1350 $border_radius = '';
1351 }
1352 if ($hotspot_shape === 'hexagon') {
1353 $border_radius = '';
1354 $hotspot_background_color = 'background-color: transparent;';
1355 $hotspot_animation = '';
1356 }
1357
1358 if(isset($hotspot['hotspot-custom-icon-color-value']) && !empty($hotspot['hotspot-custom-icon-color-value'])){
1359 $foreground_color = $hotspot['hotspot-custom-icon-color-value'];
1360 }
1361
1362 $html .= '#' . $panoid . ' div.pnlm-hotspot-base.fas.custom-' . $id.'-' . $panoscenes['scene-id'] .'-'. sanitize_html_class($hotspot['hotspot-title']).',
1363 #' . $panoid . ' div.pnlm-hotspot-base.fab.custom-' . $id.'-' . $panoscenes['scene-id'] .'-'. sanitize_html_class($hotspot['hotspot-title']).',
1364 #' . $panoid . ' div.pnlm-hotspot-base.fa-solid.custom-' . $id.'-' . $panoscenes['scene-id'] .'-'. sanitize_html_class($hotspot['hotspot-title']).',
1365 #' . $panoid . ' div.pnlm-hotspot-base.fa.custom-' . $id.'-' . $panoscenes['scene-id'] .'-'. sanitize_html_class($hotspot['hotspot-title']).',
1366 #' . $panoid . ' div.pnlm-hotspot-base.far.custom-' . $id.'-' . $panoscenes['scene-id'] .'-'. sanitize_html_class($hotspot['hotspot-title']).' {
1367 display: block !important;
1368 '.$hotspot_background_color.'
1369 color: ' . $foreground_color . ';
1370 '.$border_radius.'
1371 width: 30px;
1372 height: 30px;
1373 font-size: 16px;
1374 line-height: 30px;
1375 '.$hotspot_animation.'
1376 }';
1377
1378 if($hotspot_shape === 'hexagon'){
1379
1380 $html .= '#' . $panoid . ' .custom-' . $id . '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ' .hexagon-wrapper svg path {
1381 fill: ' . $hotspoticoncolor . ';
1382 }';
1383
1384 $html .= '#' . $panoid . ' .custom-' . $id . '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . '.pnlm-tooltip:after{
1385 content: "";
1386 position: absolute;
1387 left: 50%;
1388 top: 50%;
1389 transform: translate(-50%, -50%);
1390 width: 85%;
1391 height: 85%;
1392 animation: icon-pulse' . $panoid . '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ' 1.5s infinite cubic-bezier(.25, 0, 0, 1);
1393 border-radius: 100%;
1394 z-index: -2;
1395 }';
1396
1397 }
1398
1399 $html .= '#' . $panoid2 . ' div.pnlm-hotspot-base.fas.custom-' . $id.'-' . $panoscenes['scene-id'] .'-'. sanitize_html_class($hotspot['hotspot-title']).',
1400 #' . $panoid2 . ' div.pnlm-hotspot-base.fab.custom-' . $id.'-' . $panoscenes['scene-id'] .'-'. sanitize_html_class($hotspot['hotspot-title']).',
1401 #' . $panoid2 . ' div.pnlm-hotspot-base.fa-solid.custom-' . $id.'-' . $panoscenes['scene-id'] .'-'. sanitize_html_class($hotspot['hotspot-title']).',
1402 #' . $panoid2 . ' div.pnlm-hotspot-base.fa.custom-' . $id.'-' . $panoscenes['scene-id'] .'-'. sanitize_html_class($hotspot['hotspot-title']).',
1403 #' . $panoid2 . ' div.pnlm-hotspot-base.far.custom-' . $id.'-' . $panoscenes['scene-id'] .'-'. sanitize_html_class($hotspot['hotspot-title']).' {
1404 display: block !important;
1405 '.$hotspot_background_color.'
1406 color: ' . $foreground_color . ';
1407 '.$border_radius.'
1408 width: 30px;
1409 height: 30px;
1410 font-size: 16px;
1411 line-height: 30px;
1412 '.$hotspot_animation.'
1413 }';
1414 }
1415 if (isset($hotspot['hotspot-blink'])) {
1416 $hotspotblink = $hotspot['hotspot-blink'];
1417 if ($hotspotblink == 'on') {
1418 $html .= '@-webkit-keyframes icon-pulse' .$panoid .'-'. $panoscenes['scene-id'] .'-'. sanitize_html_class($hotspot['hotspot-title']) .' {
1419 0% {
1420 box-shadow: 0 0 0 0px rgba(' . $pulse_color[0] . ', 1);
1421 }
1422 100% {
1423 box-shadow: 0 0 0 10px rgba(' . $pulse_color[0] . ', 0);
1424 }
1425 }
1426 @keyframes icon-pulse' . $panoid . ' {
1427 0% {
1428 box-shadow: 0 0 0 0px rgba(' . $pulse_color[0] . ', 1);
1429 }
1430 100% {
1431 box-shadow: 0 0 0 10px rgba(' . $pulse_color[0] . ', 0);
1432 }
1433 }';
1434 $html .= '@-webkit-keyframes icon-pulse' .$panoid2 .'-'. $panoscenes['scene-id'] .'-'. sanitize_html_class($hotspot['hotspot-title']) .' {
1435 0% {
1436 box-shadow: 0 0 0 0px rgba(' . $pulse_color[0] . ', 1);
1437 }
1438 100% {
1439 box-shadow: 0 0 0 10px rgba(' . $pulse_color[0] . ', 0);
1440 }
1441 }
1442 @keyframes icon-pulse' . $panoid . ' {
1443 0% {
1444 box-shadow: 0 0 0 0px rgba(' . $pulse_color[0] . ', 1);
1445 }
1446 100% {
1447 box-shadow: 0 0 0 10px rgba(' . $pulse_color[0] . ', 0);
1448 }
1449 }';
1450 }
1451 }
1452
1453 }
1454
1455 }
1456
1457 }
1458
1459 $status = get_option('wpvr_edd_license_status');
1460 if ($status !== false && $status == 'valid') {
1461 if (!$gyro) {
1462 $html .= '#' . $panoid . ' div.pnlm-orientation-button {
1463 display: none;
1464 }';
1465 }
1466 } else {
1467 $html .= '#' . $panoid . ' div.pnlm-orientation-button {
1468 display: none;
1469 }';
1470 }
1471 $floor_plan_custom_color = isset($postdata['floor_plan_custom_color']) ? $postdata['floor_plan_custom_color'] : '#cca92c';
1472 $foreground_color_pointer = '#fff';
1473 if($floor_plan_custom_color != ''){
1474 $pointer_pulse = wpvr_hex2rgb($floor_plan_custom_color);
1475 $floor_rgb = wpvr_HTMLToRGB($floor_plan_custom_color);
1476 $floor_hsl = wpvr_RGBToHSL($floor_rgb);
1477 if ($floor_hsl->lightness > 200) {
1478 $foreground_color_pointer = '#000000';
1479 }
1480 $html .= '
1481 .wpvr-floor-map .floor-plan-pointer.add-pulse:before {
1482 border: 17px solid '.$floor_plan_custom_color.';
1483 }
1484 @-webkit-keyframes pulse {
1485 0% {
1486 -webkit-box-shadow: 0 0 0 0 rgba('.$pointer_pulse[0].', 0.7);
1487 }
1488 70% {
1489 -webkit-box-shadow: 0 0 0 10px rgba('.$pointer_pulse[0].', 0);
1490 }
1491 100% {
1492 -webkit-box-shadow: 0 0 0 0 rgba('.$pointer_pulse[0].', 0);
1493 }
1494 }
1495 @keyframes pulse {
1496 0% {
1497 -moz-box-shadow: 0 0 0 0 rgba('.$pointer_pulse[0].', 0.7);
1498 box-shadow: 0 0 0 0 rgba('.$pointer_pulse[0].', 0.7);
1499 }
1500 70% {
1501 -moz-box-shadow: 0 0 0 10px rgba('.$pointer_pulse[0].', 0);
1502 box-shadow: 0 0 0 10px rgba('.$pointer_pulse[0].', 0);
1503 }
1504 100% {
1505 -moz-box-shadow: 0 0 0 0 rgba('.$pointer_pulse[0].', 0);
1506 box-shadow: 0 0 0 0 rgba('.$pointer_pulse[0].', 0);
1507 }
1508 }';
1509 }
1510
1511 $html .= '</style>';
1512
1513
1514 $scene_animation = isset($postdata['sceneAnimation']) ? $postdata['sceneAnimation'] : 'off';
1515 $scene_animation_enabled = in_array( $scene_animation, array( 'on', '1', 1, true ), true );
1516
1517 if( $scene_animation_enabled && is_plugin_active( 'wpvr-pro/wpvr-pro.php' ) ) {
1518 $animation_type = isset($postdata['sceneAnimationName']) ? $postdata['sceneAnimationName'] : 'none';
1519 $animation_type = $animation_type === 'fade' ? 'fade_in' : $animation_type;
1520 $animationDuration = isset($postdata['sceneAnimationTransitionDuration']) ? $postdata['sceneAnimationTransitionDuration'] : '500ms';
1521 $animationDelay = isset($postdata['sceneAnimationTransitionDelay']) ? $postdata['sceneAnimationTransitionDelay'] : '0ms';
1522 $animation_css = apply_filters('wpvr_tour_scene_animation',$animation_type,$animationDuration,$animationDelay,$postdata,$id);
1523 $html .= $animation_css;
1524 }
1525
1526 $container_width = $width;
1527 $container_height = $height;
1528 $container_mobile_height = $mobile_height;
1529
1530 if ($width == 'fullwidth') {
1531 $container_width = "100%";
1532 } elseif ($width == 'embed') {
1533 $container_width = "100%";
1534 $container_height = "100%";
1535 $container_mobile_height = "100%";
1536 }
1537
1538 if (wpvr_isMobileDevice()) {
1539 $html .= '<div id="' . $master_container_id . '" class="wpvr-master-container wpvr-cardboard '.$enable_cardboard.'" style="max-width:' . $container_width . '; width: 100%; height: ' . $container_mobile_height . '; border-radius:' . $radius . '; direction:ltr; ">';
1540 } else {
1541 $html .= '<div id="' . $master_container_id . '" class="wpvr-master-container wpvr-cardboard '.$enable_cardboard.'" style="max-width:' . $container_width . '; width: 100%; height: ' . $container_height . '; border-radius:' . $radius . '; direction:ltr; ">';
1542 }
1543 $is_pro = apply_filters('is_wpvr_pro_active',false);
1544 $status = get_option('wpvr_edd_license_status');
1545 $is_cardboard = get_option('wpvr_cardboard_disable');
1546 if ($status !== false && 'valid' == $status && $is_pro && wpvr_isMobileDevice() && $is_cardboard == 'true' ) {
1547 $html .= '<button class="fullscreen-button">';
1548 $html .= '<span class="expand">';
1549 $html .= '<i class="fas fa-expand" aria-hidden="true"></i>';
1550 $html .= '</span>';
1551
1552 $html .= '<span class="compress">';
1553 $html .= '<i class="fas fa-minimize" aria-hidden="true"></i>';
1554 $html .= '</span>';
1555 $html .= '</button>';
1556 $embed_mode = '';
1557 if($width == "embed"){
1558 $embed_mode = "vr-embade-mode";
1559 }
1560 $html .= '<label class="wpvr-cardboard-switcher '.$embed_mode.'">
1561 <input type="checkbox" class="vr_mode_change' . $pano_suffix . '" name="vr_mode_change" value="off">
1562 <span class="switcher-box">
1563 <span class="normal-mode-tooltip">Normal VR Mode</span>
1564 <svg width="78" height="60" viewBox="0 0 78 60" fill="none" xmlns="http://www.w3.org/2000/svg">
1565 <path d="M42.25 21.4286C42.25 22.2811 41.9076 23.0986 41.2981 23.7014C40.6886 24.3042 39.862 24.6429 39 24.6429C38.138 24.6429 37.3114 24.3042 36.7019 23.7014C36.0924 23.0986 35.75 22.2811 35.75 21.4286C35.75 20.5761 36.0924 19.7585 36.7019 19.1557C37.3114 18.5529 38.138 18.2143 39 18.2143C39.862 18.2143 40.6886 18.5529 41.2981 19.1557C41.9076 19.7585 42.25 20.5761 42.25 21.4286ZM19.5 30C18.9254 30 18.3743 30.2258 17.9679 30.6276C17.5616 31.0295 17.3333 31.5745 17.3333 32.1429C17.3333 32.7112 17.5616 33.2562 17.9679 33.6581C18.3743 34.06 18.9254 34.2857 19.5 34.2857H28.1667C28.7413 34.2857 29.2924 34.06 29.6987 33.6581C30.1051 33.2562 30.3333 32.7112 30.3333 32.1429C30.3333 31.5745 30.1051 31.0295 29.6987 30.6276C29.2924 30.2258 28.7413 30 28.1667 30H19.5ZM47.6667 32.1429C47.6667 31.5745 47.8949 31.0295 48.3013 30.6276C48.7076 30.2258 49.2587 30 49.8333 30H58.5C59.0746 30 59.6257 30.2258 60.0321 30.6276C60.4384 31.0295 60.6667 31.5745 60.6667 32.1429C60.6667 32.7112 60.4384 33.2562 60.0321 33.6581C59.6257 34.06 59.0746 34.2857 58.5 34.2857H49.8333C49.2587 34.2857 48.7076 34.06 48.3013 33.6581C47.8949 33.2562 47.6667 32.7112 47.6667 32.1429ZM32.5 0C31.9254 0 31.3743 0.225765 30.9679 0.627629C30.5616 1.02949 30.3333 1.57454 30.3333 2.14286V8.57143H18.4167C14.8693 8.57183 11.4528 9.89617 8.84994 12.2798C6.24706 14.6634 4.64954 17.9306 4.37667 21.4286H2.16667C1.59203 21.4286 1.04093 21.6543 0.634602 22.0562C0.228273 22.4581 0 23.0031 0 23.5714V36.4286C0 36.9969 0.228273 37.5419 0.634602 37.9438C1.04093 38.3457 1.59203 38.5714 2.16667 38.5714H4.33333V46.0714C4.33333 49.7655 5.81711 53.3083 8.45825 55.9204C11.0994 58.5325 14.6815 60 18.4167 60H25.3933C29.1269 59.9986 32.7071 58.5311 35.347 55.92L37.921 53.3786C38.0618 53.2393 38.229 53.1288 38.4131 53.0534C38.5971 52.978 38.7943 52.9392 38.9935 52.9392C39.1927 52.9392 39.3899 52.978 39.5739 53.0534C39.758 53.1288 39.9252 53.2393 40.066 53.3786L42.6357 55.92C45.2766 58.5322 48.8586 59.9998 52.5937 60H59.5833C63.3185 60 66.9006 58.5325 69.5418 55.9204C72.1829 53.3083 73.6667 49.7655 73.6667 46.0714V38.5714H75.8333C76.408 38.5714 76.9591 38.3457 77.3654 37.9438C77.7717 37.5419 78 36.9969 78 36.4286V23.5714C78 23.0031 77.7717 22.4581 77.3654 22.0562C76.9591 21.6543 76.408 21.4286 75.8333 21.4286H73.6233C73.3505 17.9306 71.753 14.6634 69.1501 12.2798C66.5472 9.89617 63.1307 8.57183 59.5833 8.57143H47.6667V2.14286C47.6667 1.57454 47.4384 1.02949 47.0321 0.627629C46.6257 0.225765 46.0746 0 45.5 0H32.5ZM69.3333 22.5V46.0714C69.3333 48.6289 68.3061 51.0816 66.4776 52.89C64.6491 54.6983 62.1692 55.7143 59.5833 55.7143H52.5937C50.0093 55.7132 47.5311 54.6973 45.7037 52.89L43.1297 50.3486C42.5864 49.8108 41.9413 49.3842 41.2312 49.0931C40.5211 48.8021 39.76 48.6522 38.9913 48.6522C38.2227 48.6522 37.4616 48.8021 36.7515 49.0931C36.0414 49.3842 35.3963 49.8108 34.853 50.3486L32.2833 52.89C30.4559 54.6973 27.9777 55.7132 25.3933 55.7143H18.4167C15.8308 55.7143 13.3509 54.6983 11.5224 52.89C9.6939 51.0816 8.66667 48.6289 8.66667 46.0714V22.5C8.66667 19.9426 9.6939 17.4899 11.5224 15.6815C13.3509 13.8731 15.8308 12.8571 18.4167 12.8571H59.5833C62.1692 12.8571 64.6491 13.8731 66.4776 15.6815C68.3061 17.4899 69.3333 19.9426 69.3333 22.5Z" fill="#216DF0"/>
1566 </svg>
1567 </span>
1568 </label>';
1569
1570 }
1571
1572 if ($width == 'fullwidth') {
1573 if (wpvr_isMobileDevice()) {
1574 $html .= '<div class="cardboard-vrfullwidth vrfullwidth">';
1575 $html .= '<div id="' . $panoid2 . '" class="pano-wrap pano-left cardboard-half pano2' . $id . '" style="width: 49%!important; border-radius:' . $radius . ' text-align:center; direction:ltr;" ><div id="center-pointer2' . $pano_suffix . '" class="vr-pointer-container"><span class="center-pointer"></span></div></div>';
1576 $html .= '<div id="' . $panoid . '" class="pano-wrap pano-right pano' . $id . '" style="width: 100%; text-align:center; direction:ltr; border-radius:' . $radius . '" >';
1577 } else {
1578 $html .= '<div id="' . $panoid2 . '" class="pano-wrap pano-left pano2' . $id . '" style="width: 49%; border-radius:' . $radius . '"><div id="center-pointer2' . $pano_suffix . '" class="vr-pointer-container"><span class="center-pointer"></span></div></div>';
1579 if ($radius) {
1580 $html .= '<div id="' . $panoid . '" class="pano-wrap vrfullwidth pano' . $id . '" style=" text-align:center; height: ' . $height . '; border-radius:' . $radius . '; direction:ltr;" >';
1581 } else {
1582 $html .= '<div id="' . $panoid . '" class="pano-wrap vrfullwidth pano' . $id . '" style=" text-align:center; height: ' . $height . '; direction:ltr;" >';
1583 }
1584 }
1585 } elseif ($width == 'embed') {
1586 $html .= '<div class="cardboard-vrembed vrembed">';
1587 $html .= '<div id="' . $panoid2 . '" class="pano-wrap pano-left pano2' . $id . '" style=" width: 49%!important; text-align:center; direction:ltr;" ><div id="center-pointer2' . $pano_suffix . '" class="vr-pointer-container"><span class="center-pointer"></span></div></div>';
1588 $html .= '<div id="' . $panoid . '" class="pano-wrap pano-right pano' . $id . '" style="width: 100%; height: 100%; text-align:center; direction:ltr;" >';
1589 } else {
1590 if (wpvr_isMobileDevice()) {
1591 $html .= '<div id="' . $panoid2 . '" class="pano-wrap pano-left cardboard-half pano2' . $id . '" style="width: 49%; border-radius:' . $radius . '"><div id="center-pointer2' . $pano_suffix . '" class="vr-pointer-container"><span class="center-pointer"></span></div></div>';
1592 if ($radius) {
1593 $html .= '<div id="' . $panoid . '" class="pano-wrap pano-right pano' . $id . '" style=" width: 100%; border-radius:' . $radius . ';">';
1594 } else {
1595 $html .= '<div id="' . $panoid . '" class="pano-wrap pano-right pano' . $id . '" style=" width: 100%; ">';
1596 }
1597 } else {
1598 $html .= '<div id="' . $panoid2 . '" class="pano-wrap pano-left pano2' . $id . '" style="width: 49%; border-radius:' . $radius . '"><div id="center-pointer2' . $pano_suffix . '" class="vr-pointer-container"><span class="center-pointer"></span></div></div>';
1599
1600 if ($radius) {
1601 $html .= '<div id="' . $panoid . '" class="pano-wrap pano-right pano' . $id . '" style="width: 100%; border-radius:' . $radius . ';">';
1602 } else {
1603 $html .= '<div id="' . $panoid . '" class="pano-wrap pano-right pano' . $id . '" style="width: 100%;">';
1604 }
1605 }
1606 }
1607 // Vr mode transction scene to scene
1608 if ($status !== false && 'valid' == $status && $is_pro) {
1609 $html .= '<div id="center-pointer' . $pano_suffix . '" class="vr-pointer-container" style="display:none"><span class="center-pointer"></span></div>';
1610 }
1611 $social_logo_top = '';
1612 //===company logo===//
1613 if (isset($postdata['cpLogoSwitch'])) {
1614 $cpLogoImg = $postdata['cpLogoImg'] ?? '';
1615 $cpLogoContent = $postdata['cpLogoContent'] ?? '';
1616 if ($postdata['cpLogoSwitch'] == 'on' && 'valid' == $status && $is_pro) {
1617 $html .= '<div id="cp-logo-controls">';
1618 $html .= '<div class="cp-logo-ctrl" id="cp-logo">';
1619 if ($cpLogoImg) {
1620 $social_logo_top = '50px';
1621 $html .= '<img loading="lazy" src="' . $cpLogoImg . '" alt="Company Logo">';
1622 }
1623
1624 if ($cpLogoContent) {
1625 $html .= '<div class="cp-info">' . esc_attr($cpLogoContent) . '</div>';
1626 }
1627 $html .= '</div>';
1628 $html .= '</div>';
1629 }
1630 }
1631 //===company logo ends===//
1632
1633 //===Generic Form===//
1634 if (isset($postdata["genericform"]) && $postdata["genericform"] === 'on' && 'valid' == $status && $is_pro) {
1635 // Process shortcode with fallback handling
1636 $shortcode_content = '';
1637 if (isset($postdata["genericformshortcode"]) && !empty(trim($postdata["genericformshortcode"]))) {
1638 $shortcode_content = do_shortcode( $postdata["genericformshortcode"] );
1639 } else {
1640 $shortcode_content = '<p class="error-message">No shortcode found.</p>';
1641 }
1642
1643 // Generate the form trigger button
1644 $generic_form_icon = isset($postdata['genericformicon']) ? (string) $postdata['genericformicon'] : 'fab fa-wpforms';
1645 $generic_form_icon = implode(' ', array_filter(array_map('sanitize_html_class', preg_split('/\s+/', $generic_form_icon))));
1646 $generic_form_icon = $generic_form_icon ?: 'fab fa-wpforms';
1647 $generic_form_icon_color = sanitize_hex_color($postdata['genericformiconcolor'] ?? '') ?: '#f7fffb';
1648 $html .= '<div class="generic_form_button" id="generic_form_button_' . esc_attr($pano_suffix) . '">';
1649 $html .= '<div class="generic-form-icon" title ="Generic Form" id="generic_form_target_' . esc_attr($pano_suffix) . '"><i class="' . esc_attr($generic_form_icon) . '" style="color:' . esc_attr($generic_form_icon_color) . ';"></i></div>';
1650 $html .= '</div>';
1651
1652 // Generate the modal form container
1653 $html .= '<div class="wpvr-generic-form" id="wpvr-generic-form' . esc_attr($pano_suffix) . '" style="display: none">';
1654 $html .= '<span class="close-generic-form"><i class="fa fa-times"></i></span>';
1655 $html .= '<div class="generic-form-container">' . $shortcode_content . '</div>';
1656 $html .= '</div>';
1657 }
1658 //===Generic Form ends===//
1659
1660 //===Background Tour===//
1661 if (isset($postdata['bg_tour_enabler'])) {
1662
1663 $bg_tour_enabler = $postdata['bg_tour_enabler'];
1664 if ($bg_tour_enabler == 'on') {
1665 $bg_tour_navmenu = $postdata['bg_tour_navmenu'] ?? 'off';
1666 $bg_tour_title = $postdata['bg_tour_title'] ?? '';
1667 $bg_tour_subtitle = $postdata['bg_tour_subtitle'] ?? '';
1668
1669 if ($bg_tour_navmenu == 'on') {
1670 $menuLocations = get_nav_menu_locations();
1671 if (!empty($menuLocations['primary'])) {
1672 $menuID = $menuLocations['primary'];
1673 $primaryNav = wp_get_nav_menu_items($menuID);
1674 $html .= '<ul class="wpvr-navbar-container">';
1675 foreach ($primaryNav as $primaryNav_key => $primaryNav_value) {
1676 if ($primaryNav_value->menu_item_parent == "0") {
1677 $html .= '<li>';
1678 $html .= '<a href="' . $primaryNav_value->url . '">' . $primaryNav_value->title . '</a>';
1679 $html .= '<ul class="wpvr-navbar-dropdown">';
1680 foreach ($primaryNav as $pm_key => $pm_value) {
1681 if ($pm_value->menu_item_parent == $primaryNav_value->ID) {
1682 $html .= '<li>';
1683 $html .= '<a href="' . $pm_value->url . '">' . $pm_value->title . '</a>';
1684 $html .= '</li>';
1685 }
1686 }
1687 $html .= '</ul>';
1688 $html .= '</li>';
1689 }
1690 }
1691 $html .= '</ul>';
1692 }
1693 }
1694 if($is_pro && 'valid' == $status ){
1695 $html .= '<div class="wpvr-home-content">';
1696 $html .= '<div class="wpvr-home-title">' . $bg_tour_title . '</div>';
1697 $html .= '<div class="wpvr-home-subtitle">' . $bg_tour_subtitle . '</div>';
1698 $html .= '</div>';
1699 }
1700 }
1701 }
1702 //===Background Tour End===//
1703
1704 //===Custom Control===//
1705 if (isset($custom_control)) {
1706 $gyro_button_enabled = wpvr_isMobileDevice() && $custom_control['gyroSwitch'] == "on" && $custom_control['gyroscopeSwitch'] == "on";
1707 if ($custom_control['panZoomInSwitch'] == "on" || $custom_control['panZoomOutSwitch'] == "on" || $gyro_button_enabled || $custom_control['backToHomeSwitch'] == "on") {
1708 $html .= '<div id="zoom-in-out-controls' . $pano_suffix . '" class="zoom-in-out-controls">';
1709
1710 if ($custom_control['backToHomeSwitch'] == "on" && 'valid' == $status && $is_pro) {
1711 $html .= '<div class="ctrl" id="backToHome' . $pano_suffix . '"><i class="' . $custom_control['backToHomeIcon'] . '" style="color:' . $custom_control['backToHomeColor'] . ';"></i></div>';
1712 }
1713
1714 if ($custom_control['panZoomInSwitch'] == "on" && 'valid' == $status && $is_pro) {
1715 $html .= '<div class="ctrl" id="zoom-in' . $pano_suffix . '"><i class="' . $custom_control['panZoomInIcon'] . '" style="color:' . $custom_control['panZoomInColor'] . ';"></i></div>';
1716 }
1717
1718 if ($custom_control['panZoomOutSwitch'] == "on" && 'valid' == $status && $is_pro) {
1719 $html .= '<div class="ctrl" id="zoom-out' . $pano_suffix . '"><i class="' . $custom_control['panZoomOutIcon'] . '" style="color:' . $custom_control['panZoomOutColor'] . ';"></i></div>';
1720 }
1721 if ($gyro_button_enabled && 'valid' == $status && $is_pro) {
1722 $html .= '<div class="ctrl" id="gyroscope' . $pano_suffix . '" ><i class="' . $custom_control['gyroscopeIcon'] . '" style="color:' . $custom_control['gyroscopeColor'] . ';"></i></div>';
1723 }
1724 $html .= '</div>';
1725 }
1726 //===zoom in out Control===//
1727
1728 if (($custom_control['panupSwitch'] == "on" || $custom_control['panDownSwitch'] == "on" || $custom_control['panLeftSwitch'] == "on" || $custom_control['panRightSwitch'] == "on" || $custom_control['panFullscreenSwitch'] == "on" ) && 'valid' == $status && $is_pro) {
1729 //===Custom Control===//
1730 $html .= '<div class="controls" id="controls' . $pano_suffix . '">';
1731
1732 if ($custom_control['panupSwitch'] == "on") {
1733 $html .= '<div class="ctrl pan-up" id="pan-up' . $pano_suffix . '"><i class="' . $custom_control['panupIcon'] . '" style="color:' . $custom_control['panupColor'] . ';"></i></div>';
1734 }
1735
1736 if ($custom_control['panDownSwitch'] == "on") {
1737 $html .= '<div class="ctrl pan-down" id="pan-down' . $pano_suffix . '"><i class="' . $custom_control['panDownIcon'] . '" style="color:' . $custom_control['panDownColor'] . ';"></i></div>';
1738 }
1739
1740 if ($custom_control['panLeftSwitch'] == "on") {
1741 $html .= '<div class="ctrl pan-left" id="pan-left' . $pano_suffix . '"><i class="' . $custom_control['panLeftIcon'] . '" style="color:' . $custom_control['panLeftColor'] . ';"></i></div>';
1742 }
1743
1744 if ($custom_control['panRightSwitch'] == "on") {
1745 $html .= '<div class="ctrl pan-right" id="pan-right' . $pano_suffix . '"><i class="' . $custom_control['panRightIcon'] . '" style="color:' . $custom_control['panRightColor'] . ';"></i></div>';
1746 }
1747
1748 if ($custom_control['panFullscreenSwitch'] == "on") {
1749 $html .= '<div class="ctrl fullscreen" id="fullscreen' . $pano_suffix . '"><i class="' . $custom_control['panFullscreenIcon'] . '" style="color:' . $custom_control['panFullscreenColor'] . ';"></i></div>';
1750 }
1751 $html .= '</div>';
1752 }
1753 }
1754 //===Custom Control===//
1755
1756 //===explainer button===//
1757 $html .= wpvr_render_explainer_button(
1758 isset( $custom_control ) ? $custom_control : null,
1759 $postdata,
1760 $is_pro,
1761 $autoload,
1762 $explainer_right,
1763 $pano_suffix
1764 );
1765 //===explainer button end===//
1766
1767 //===Floor map button===//
1768 $status = get_option('wpvr_edd_license_status');
1769 if ($status !== false && 'valid' == $status && $is_pro){
1770 if ($floor_plan_enable == "on" && !empty($floor_plan_image)) {
1771 $html .= '<div class="floor_map_button" id="floor_map_button_' . $pano_suffix . '" style="right:'.$floor_map_right.'">';
1772 $html .= '<div class="ctrl" id="floor_map_target_' . $pano_suffix . '"><i class="fas fa-map" style="color:#f7fffb;"></i></div>';
1773 $html .= '</div>';
1774 }
1775 }
1776 //===floor map button===//
1777
1778 if ($vrgallery && 'valid' == $status && $is_pro ) {
1779 //===Carousal setup===//
1780 $size = '';
1781 if($vrgallery_icon_size){
1782 $size = 'vrg-icon-size-large';
1783 }
1784 $html .= '<div id="vrgcontrols' . $pano_suffix . '" class="vrgcontrols">';
1785
1786 $html .= '<div class="vrgctrl' . $pano_suffix . ' vrbounce '.$size.'">';
1787 $html .= '</div>';
1788 $html .= '</div>';
1789
1790 $gallery_layout_class = isset($postdata['tourLayout']['layout']) && 'layout1' === $postdata['tourLayout']['layout']
1791 ? 'wpvr-gallery--modern'
1792 : 'wpvr-gallery--classic';
1793 $html .= '<div id="sccontrols' . $pano_suffix . '" class="scene-gallery vrowl-carousel ' . esc_attr($gallery_layout_class) . '">';
1794 if (isset($panodata["scene-list"])) {
1795 foreach ($panodata["scene-list"] as $panoscenes) {
1796 $scene_key = $panoscenes['scene-id'];
1797
1798 if ($vrgallery_title == 'on') {
1799 $scene_key_title = isset($panoscenes['scene-ititle']) ? sanitize_text_field($panoscenes['scene-ititle']) : '';
1800 } else {
1801 $scene_key_title = "";
1802 }
1803
1804 if ($panoscenes['scene-type'] == 'cubemap') {
1805 $img_src_url = $panoscenes['scene-attachment-url-face0'];
1806 } else {
1807 $img_src_url = $panoscenes['scene-attachment-url'];
1808 }
1809
1810 $src_to_id = attachment_url_to_postid($img_src_url);
1811 $thumbnail_array = wp_get_attachment_image_src($src_to_id, 'thumbnail');
1812 if ($thumbnail_array) {
1813 $thumbnail = $thumbnail_array[0];
1814 } else {
1815 $thumbnail = $img_src_url;
1816 }
1817
1818 if( isset($postdata['tourLayout']['layout']) && 'layout1' !== $postdata['tourLayout']['layout']) {
1819 $html .= '<ul><li title="Click to view scene"><span class="scene-title" title="' . esc_attr($scene_key_title) . '">' . $scene_key_title . '</span><img loading="lazy" class="scctrl" id="' . $scene_key . '_gallery_' . $pano_suffix . '" src="' . $thumbnail . '"></li></ul>';
1820 }else {
1821 $html .= '<ul><li title="Click to view scene"><img loading="lazy" class="scctrl" id="' . $scene_key . '_gallery_' . $pano_suffix . '" src="' . $thumbnail . '"><span class="scene-title" title="' . esc_attr($scene_key_title) . '">' . $scene_key_title . '</span></li></ul>';
1822 }
1823 }
1824 }
1825
1826 $html .= '</div>';
1827
1828 $html .= '
1829 <div class="wpvr_slider_nav">
1830 <button type="button" role="presentation" class="wpvr_owl_prev">
1831 <div class="nav-btn prev-slide"><i class="fa fa-angle-left"></i></div>
1832 </button>
1833 <button type="button" role="presentation" class="wpvr_owl_next">
1834 <div class="nav-btn next-slide"><i class="fa fa-angle-right"></i></div>
1835 </button>
1836 </div>
1837 ';
1838
1839 //===Carousal setup end===//
1840 }
1841
1842 //===Call TO action Button===//
1843 $bg_music = isset($postdata['bg_music']) ? $postdata['bg_music'] : 'off';
1844 $bg_music_url = isset($postdata['bg_music_url']) ? $postdata['bg_music_url'] : '';
1845 $autoplay_bg_music = isset($postdata['autoplay_bg_music']) ? $postdata['autoplay_bg_music'] : 'off';
1846 $loop_bg_music = isset($postdata['loop_bg_music']) ? $postdata['loop_bg_music'] : 'off';
1847
1848 $bg_loop = ($loop_bg_music === 'on') ? 'loop' : '';
1849 $autoplay_attr = ($autoplay_bg_music === 'on') ? 'autoplay' : '';
1850 $audio_muted_attr = ($autoplay_bg_music === 'on') ? 'muted' : '';
1851 $audio_icon_class = 'fa-volume-mute'; // Always start with mute icon
1852
1853 if ($bg_music === 'on' && 'valid' == $status && $is_pro) {
1854 $html .= '<div id="adcontrol' . esc_attr( $pano_suffix ) . '" class="adcontrol" style="right:' . esc_attr( $audio_right ) . '">';
1855 $html .= '<audio id="vrAudio' . esc_attr($pano_suffix) . '" class="vrAudioDefault" data-autoplay="' . esc_attr($autoplay_bg_music) . '" onended="audionEnd' . esc_attr($pano_suffix) . '()" ' . $autoplay_attr . ' ' . $audio_muted_attr . ' ' . $bg_loop . '>
1856 <source src="' . esc_url($bg_music_url) . '" type="audio/mpeg">
1857 Your browser does not support the audio element.
1858 </audio>';
1859 $html .= '<button onclick="playPause' . esc_attr($pano_suffix) . '()" class="ctrl audio_control" id="audio_control' . esc_attr($pano_suffix) . '">
1860 <i id="vr-volume' . esc_attr($pano_suffix) . '" class="wpvrvolumeicon' . esc_attr($pano_suffix) . ' fas ' . esc_attr($audio_icon_class) . '" style="color:#fff;"></i>
1861 </button>';
1862 $html .= '</div>';
1863 }
1864 //===Explainer video section===//
1865 $explainerContent = "";
1866 if (isset($postdata['explainerContent'])) {
1867 $explainerContent = $this->wpvr_sanitize_iframe_only($postdata['explainerContent']);
1868 }
1869 $html .= '<div class="explainer" id="explainer' . $pano_suffix . '" style="display: none">';
1870 $html .= '<span class="close-explainer-video"><i class="fa fa-times"></i></span>';
1871 $html .= '' . $explainerContent . '';
1872 $html .= '</div>';
1873 //===Explainer video section End===//
1874
1875 //===Scene navigation Control===//
1876 if (isset($postdata['scene_navigation']) && $postdata['scene_navigation'] === 'on' && 'valid' == $status && $is_pro) {
1877 $scene_navigation_nav_class = 'custom-scene-navigation-nav' . ( $is_classic_layout ? ' wpvr-scene-navigation-nav--classic' : '' );
1878 $html .= '<style>
1879 #et-boc .et-l .pnlm-controls-container,
1880 .pnlm-controls-container{
1881 top: 33px;
1882 }
1883
1884 #et-boc .et-l .zoom-in-out-controls,
1885 .zoom-in-out-controls {
1886 top: 37px;
1887 }
1888 </style>';
1889 $html .= '<div id="custom-scene-navigation' . $pano_suffix . '" class="custom-scene-navigation">
1890 <span class="hamburger-menu"><svg width="16" height="10" fill="none" viewBox="0 0 22 15" xmlns="http://www.w3.org/2000/svg"><rect width="21.177" height="2.647" fill="#f7fffb" rx="1.324"/><rect width="21.177" height="2.647" y="6.177" fill="#f7fffb" rx="1.324"/><rect width="21.177" height="2.647" y="12.352" fill="#f7fffb" rx="1.324"/></svg></span>
1891 </div>
1892
1893 <div id="custom-scene-navigation-nav' . $pano_suffix . '" class="' . esc_attr( $scene_navigation_nav_class ) . '">
1894 <ul></ul>
1895 </div>
1896 ';
1897 }
1898 //===Scene navigation Control===//
1899
1900
1901 if( 'embed' === $width){
1902 if(WPVR_Helper::is_enable_social_share($postdata) === 'on'){
1903 $html .= '<div id="wpvr-social-share-bg-box'.$pano_suffix.'" class="wpvr-social-share-bg-box" style="top:'.$social_logo_top.'">
1904 <span class="share-btn-svg"><svg fill="none" viewBox="0 0 24 24" width="24" height="24" ><path fill="#1F1CF4" d="M18.4 2.4a3.2 3.2 0 00-3.2 3.2 3.2 3.2 0 00.075.67L8.01 9.901A3.2 3.2 0 005.6 8.8a3.2 3.2 0 101.325 6.112 3.2 3.2 0 001.086-.812l7.261 3.632a3.2 3.2 0 101.803-2.242c-.415.189-.786.466-1.085.81l-7.261-3.63A3.2 3.2 0 008.8 12a3.2 3.2 0 00-.075-.667L15.991 7.7a3.2 3.2 0 002.41 1.1 3.2 3.2 0 100-6.4z"/></svg></span>
1905 <nav class="wpvr-share-nav">
1906 '.WPVR_Helper::social_media_share_links_display_in_embed(home_url().'/?embed_page='. $id).'
1907 </nav>
1908 </div>';
1909 }
1910 }
1911 //===Floor plan section===//
1912 $floor_map_image = "";
1913 $floor_map_pointer = array();
1914 $floor_map_scene_id = '';
1915 $floor_plan_custom_color = '#cca92c';
1916 $floor_plan_direction_indicator = isset($postdata['floor_plan_direction_indicator']) ? $postdata['floor_plan_direction_indicator'] : 'on';
1917
1918 if (isset($postdata['floor_plan_attachment_url'])) {
1919 $floor_map_image = $postdata['floor_plan_attachment_url'];
1920 $floor_map_pointer = $postdata['floor_plan_pointer_position'];
1921 $floor_map_scene_id = $postdata['floor_plan_data_list'];
1922 $floor_plan_custom_color = $postdata['floor_plan_custom_color'];
1923 }
1924 $html .= '<div class="wpvr-floor-map" id="wpvr-floor-map' . $pano_suffix . '" style="display: none">';
1925 $html .= '<span class="close-floor-map-plan"><i class="fa fa-times"></i></span>';
1926 $html .= '<img loading="lazy" src="'.$floor_map_image.'">';
1927 foreach($floor_map_pointer as $key=> $pointer_position){
1928 $html .= '<div class="floor-plan-pointer ui-draggable ui-draggable-handle" scene_id = "'.$floor_map_scene_id[$key]->value.'" id="'.$pointer_position->id.'" data-top="'.$pointer_position->data_top.'" data-left="'.$pointer_position->data_left.'" style="'.$pointer_position->style.'">
1929 <svg class="floor-pointer-circle" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
1930 <circle cx="12" cy="12" r="11.5" stroke="'.$floor_plan_custom_color.'"/>
1931 <circle cx="12" cy="12" r="5" fill="'.$foreground_color_pointer.'"/>
1932 </svg>';
1933
1934 // Only add the floor pointer flash SVG if floor_plan_direction_indicator is "on"
1935 if ($floor_plan_direction_indicator === "on") {
1936 $html .= '<svg class="floor-pointer-flash" width="54" height="35" viewBox="0 0 54 35" fill="none" xmlns="http://www.w3.org/2000/svg">
1937 <path d="M0.454054 1.32433L11.7683 34.3243C11.9069 34.7285 12.287 35 12.7143 35H41.2857C41.713 35 42.0931 34.7285 42.2317 34.3243L53.5459 1.32432C53.7685 0.675257 53.2862 0 52.6 0H1.4C0.713843 0 0.231517 0.675258 0.454054 1.32433Z" fill="url(#paint0_linear_1_10)"/>
1938 <defs>
1939 <linearGradient id="paint0_linear_1_10" x1="27" y1="4.59807e-08" x2="26.5" y2="28" gradientUnits="userSpaceOnUse">
1940 <stop stop-color="' . esc_attr( $floor_plan_custom_color ) . '" stop-opacity="0"/>
1941 <stop offset="1" stop-color="' . esc_attr( $floor_plan_custom_color ) . '"/>
1942 </linearGradient>
1943 </defs>
1944 </svg>';
1945 }
1946 $html .= '</div>';
1947 }
1948 $html .= '</div>';
1949 //===Floor plan section===//
1950
1951 $html .= '<div class="wpvr-hotspot-tweak-contents-wrapper" style="display: none">';
1952 $html .= '<i class="fa fa-times cross" data-id="' . $id . '"></i>';
1953 $html .= '<div class="wpvr-hotspot-tweak-contents-flex">';
1954 $html .= '<div class="wpvr-hotspot-tweak-contents">';
1955 ob_start();
1956 do_action('wpvr_hotspot_tweak_contents', $scene_data);
1957 $hotspot_content = ob_get_clean();
1958 $html .= $hotspot_content;
1959 $html .= '</div>';
1960 $html .= '</div>';
1961 $html .= '</div>';
1962
1963 $html .= '<div class="custom-ifram-wrapper" style="display: none;">';
1964 $html .= '<i class="fa fa-times cross" data-id="' . $id . '"></i>';
1965
1966 $html .= '<div class="custom-ifram-flex">';
1967 $html .= '<div class="custom-ifram">';
1968 $html .= '</div>';
1969 $html .= '</div>';
1970 $html .= '</div>';
1971
1972 $html .= '</div>';
1973 $html .= '</div>';
1974 if( "embed" == $width ){
1975 $html .= '</div>';
1976 }
1977
1978
1979 if ($status !== false && 'valid' == $status && $is_pro) {
1980 $call_to_action = isset($postdata['calltoaction']) ? $postdata['calltoaction'] : 'off';
1981 if( 'on' == $call_to_action){
1982 $buttontext = isset($postdata['buttontext']) ? $postdata['buttontext'] : '';
1983 $buttonurl = isset($postdata['buttonurl']) ? $postdata['buttonurl'] : '';
1984 $cta_btn_style = isset($postdata['button_configuration']) ? $postdata['button_configuration'] : array();
1985
1986 $button_open_new_tab = isset($cta_btn_style['button_open_new_tab']) ? $cta_btn_style['button_open_new_tab'] : "off";
1987 $target = '_self';
1988 $button_position = isset($cta_btn_style['button_position']) ? $cta_btn_style['button_position'] : "";
1989 $background_color = isset($cta_btn_style['button_background_color']) ? $cta_btn_style['button_background_color'] : "";
1990 $color = isset($cta_btn_style['button_font_color']) ? $cta_btn_style['button_font_color'] : "";
1991 $font_size = isset($cta_btn_style['button_font_size']) ? $cta_btn_style['button_font_size'] : "";
1992 $font_weight = isset($cta_btn_style['button_font_weight']) ? $cta_btn_style['button_font_weight'] : "";
1993 $text_align = isset($cta_btn_style['button_alignment']) ? $cta_btn_style['button_alignment'] : "";
1994 $text_transform = isset($cta_btn_style['button_transform']) ? $cta_btn_style['button_transform'] : "";
1995 $font_style = isset($cta_btn_style['button_text_style']) ? $cta_btn_style['button_text_style'] : "";
1996 $text_decoration = isset($cta_btn_style['button_text_decoration']) ? $cta_btn_style['button_text_decoration'] : "";
1997 $line_height = isset($cta_btn_style['button_line_height']) ? $cta_btn_style['button_line_height'] : "";
1998 $letter_spacing = isset($cta_btn_style['button_letter_spacing']) ? $cta_btn_style['button_letter_spacing'] : "";
1999 $word_spacing = isset($cta_btn_style['button_word_spacing']) ? $cta_btn_style['button_word_spacing'] : "";
2000
2001 $border_width = isset($cta_btn_style['button_border_width']) ? $cta_btn_style['button_border_width'] : "";
2002 $border_style = isset($cta_btn_style['button_border_style']) ? $cta_btn_style['button_border_style'] : "";
2003 $border_color = isset($cta_btn_style['button_border_color']) ? $cta_btn_style['button_border_color'] : "";
2004 $border_radius = isset($cta_btn_style['button_border_radius']) ? $cta_btn_style['button_border_radius'] : "";
2005
2006 $button_pt = isset($cta_btn_style['button_pt']) ? $cta_btn_style['button_pt'] : "";
2007 $button_pr = isset($cta_btn_style['button_pr']) ? $cta_btn_style['button_pr'] : "";
2008 $button_pb = isset($cta_btn_style['button_pb']) ? $cta_btn_style['button_pb'] : "";
2009 $button_pl = isset($cta_btn_style['button_pl']) ? $cta_btn_style['button_pl'] : "";
2010
2011 if($button_open_new_tab == 'on'){
2012 $target = '_blank';
2013 }
2014 $style = 'background-color: '.$background_color.';
2015 color: '.$color.';
2016 font-size: '.$font_size.'px;
2017 font-weight: '.$font_weight.';
2018 text-align: center;
2019 display: inline-block;
2020 text-transform: '.$text_transform.';
2021 font-style: '.$font_style.';
2022 text-decoration: '.$text_decoration.';
2023 line-height: '.$line_height.';
2024 letter-spacing: '.$letter_spacing.'px;
2025 word-spacing: '.$word_spacing.'px;
2026 border: '.$border_width.'px '.$border_style.' '.$border_color.';
2027 border-radius: '.$border_radius.'px;
2028 padding: '.$button_pt.'px '.$button_pr.'px '.$button_pb.'px '.$button_pl.'px;
2029 ';
2030 $cta_width = ( $width === 'fullwidth' || $width === 'embed' ) ? '100%' : $width;
2031 $html .= '<div class="wpvr-call-to-action-button position-'.$text_align.'" style="max-width:' . $cta_width . '">
2032 <a href="'.$buttonurl.'" style="'.$style.'" target="'.$target.'">'.$buttontext.'</a>
2033 </div>';
2034
2035 }
2036 }
2037
2038
2039 //script started
2040 $html .= '<script>';
2041 if (isset($postdata['bg_music']) && $bg_music == 'on' && 'valid' == $status && $is_pro) {
2042 $html .= '
2043 var x' . $pano_suffix . ' = document.getElementById("vrAudio' . $pano_suffix . '");
2044 var playing' . $pano_suffix . ' = false;
2045 var autoplaySupported' . $pano_suffix . ' = false;
2046 var alertShown' . $pano_suffix . ' = false;
2047 var autoplayChecked' . $pano_suffix . ' = false;
2048
2049 function playPause' . $pano_suffix . '() {
2050 if (playing' . $pano_suffix . ') {
2051 jQuery("#vr-volume' . $pano_suffix . '").removeClass("fas fa-volume-up").addClass("fas fa-volume-mute");
2052 x' . $pano_suffix . '.pause();
2053 jQuery("#audio_control' . $pano_suffix . '").attr("data-play", "off");
2054 playing' . $pano_suffix . ' = false;
2055 } else {
2056 x' . $pano_suffix . '.muted = false;
2057 x' . $pano_suffix . '.play().then(function() {
2058 jQuery("#vr-volume' . $pano_suffix . '").removeClass("fas fa-volume-mute").addClass("fas fa-volume-up");
2059 jQuery("#audio_control' . $pano_suffix . '").attr("data-play", "on");
2060 playing' . $pano_suffix . ' = true;
2061 }).catch(function(e) {
2062 console.log("Play failed:", e);
2063 });
2064 }
2065 }
2066
2067 function audionEnd' . $pano_suffix . '() {
2068 playing' . $pano_suffix . ' = false;
2069 jQuery("#vr-volume' . $pano_suffix . '").removeClass("fas fa-volume-up").addClass("fas fa-volume-mute");
2070 jQuery("#audio_control' . $pano_suffix . '").attr("data-play", "off");
2071 }
2072
2073 x' . $pano_suffix . '.addEventListener("ended", audionEnd' . $pano_suffix . ');';
2074
2075 if ($autoplay_bg_music == 'on') {
2076 $html .= '
2077
2078 x' . $pano_suffix . '.addEventListener("loadeddata", function() {
2079 if (!autoplayChecked' . $pano_suffix . ') {
2080 checkAutoplayStatus' . $pano_suffix . '();
2081 }
2082 });
2083
2084 x' . $pano_suffix . '.addEventListener("canplay", function() {
2085 if (!autoplayChecked' . $pano_suffix . ') {
2086 checkAutoplayStatus' . $pano_suffix . '();
2087 }
2088 });
2089
2090 x' . $pano_suffix . '.addEventListener("canplaythrough", function() {
2091 if (!autoplayChecked' . $pano_suffix . ') {
2092 checkAutoplayStatus' . $pano_suffix . '();
2093 }
2094 });
2095
2096 setTimeout(function() {
2097 if (!autoplayChecked' . $pano_suffix . ') {
2098 checkAutoplayStatus' . $pano_suffix . '();
2099 }
2100 }, 1000);
2101
2102 x' . $pano_suffix . '.addEventListener("play", function() {
2103 jQuery("#vr-volume' . $pano_suffix . '").removeClass("fas fa-volume-mute").addClass("fas fa-volume-up");
2104 jQuery("#audio_control' . $pano_suffix . '").attr("data-play", "on");
2105 playing' . $pano_suffix . ' = true;
2106 });
2107
2108 x' . $pano_suffix . '.addEventListener("pause", function() {
2109 if (!playing' . $pano_suffix . ') {
2110 jQuery("#vr-volume' . $pano_suffix . '").removeClass("fas fa-volume-up").addClass("fas fa-volume-mute");
2111 jQuery("#audio_control' . $pano_suffix . '").attr("data-play", "off");
2112 }
2113 });
2114
2115 function checkAutoplayStatus' . $pano_suffix . '() {
2116 autoplayChecked' . $pano_suffix . ' = true;
2117
2118 x' . $pano_suffix . '.muted = true;
2119 var playPromise = x' . $pano_suffix . '.play();
2120
2121 if (playPromise !== undefined) {
2122 playPromise.then(function () {
2123 if (x' . $pano_suffix . '.muted || x' . $pano_suffix . '.volume === 0) {
2124 handleAutoplayBlocked' . $pano_suffix . '();
2125 } else {
2126 autoplaySupported' . $pano_suffix . ' = true;
2127 jQuery("#vr-volume' . $pano_suffix . '").removeClass("fas fa-volume-mute").addClass("fas fa-volume-up");
2128 jQuery("#audio_control' . $pano_suffix . '").attr("data-play", "on");
2129 playing' . $pano_suffix . ' = true;
2130 }
2131 }).catch(function () {
2132 handleAutoplayBlocked' . $pano_suffix . '();
2133 });
2134 } else {
2135 setTimeout(function () {
2136 if (x' . $pano_suffix . '.paused || x' . $pano_suffix . '.currentTime === 0) {
2137 handleAutoplayBlocked' . $pano_suffix . '();
2138 } else {
2139 autoplaySupported' . $pano_suffix . ' = true;
2140 jQuery("#vr-volume' . $pano_suffix . '").removeClass("fas fa-volume-mute").addClass("fas fa-volume-up");
2141 jQuery("#audio_control' . $pano_suffix . '").attr("data-play", "on");
2142 playing' . $pano_suffix . ' = true;
2143 }
2144 }, 300);
2145 }
2146 }
2147
2148 function handleAutoplayBlocked' . $pano_suffix . '() {
2149 autoplaySupported' . $pano_suffix . ' = false;
2150 if (!alertShown' . $pano_suffix . ') {
2151 alert("Autoplay is not supported in your browser. Please click the audio button to play music.");
2152 alertShown' . $pano_suffix . ' = true;
2153 }
2154
2155 x' . $pano_suffix . '.pause();
2156 x' . $pano_suffix . '.currentTime = 0;
2157 x' . $pano_suffix . '.muted = true;
2158
2159 jQuery("#vr-volume' . $pano_suffix . '").removeClass("fas fa-volume-up").addClass("fas fa-volume-mute");
2160 jQuery("#audio_control' . $pano_suffix . '").attr("data-play", "off");
2161
2162 var musicTriggerElem = document.getElementById("' . $panoid . '");
2163 if (musicTriggerElem) {
2164 musicTriggerElem.addEventListener("click", musicPlay' . $pano_suffix . ');
2165 }
2166 document.addEventListener("touchstart", musicPlay' . $pano_suffix . ', { once: true });
2167 document.addEventListener("click", musicPlay' . $pano_suffix . ', { once: true });
2168 }
2169
2170 function musicPlay' . $pano_suffix . '() {
2171 x' . $pano_suffix . '.muted = false;
2172 var playPromise = x' . $pano_suffix . '.play();
2173
2174 if (playPromise !== undefined) {
2175 playPromise.then(function () {
2176 jQuery("#vr-volume' . $pano_suffix . '").removeClass("fas fa-volume-mute").addClass("fas fa-volume-up");
2177 jQuery("#audio_control' . $pano_suffix . '").attr("data-play", "on");
2178 playing' . $pano_suffix . ' = true;
2179 }).catch(function(e) {
2180 console.log("Play failed:", e);
2181 });
2182 } else {
2183 setTimeout(function () {
2184 if (!x' . $pano_suffix . '.paused) {
2185 jQuery("#vr-volume' . $pano_suffix . '").removeClass("fas fa-volume-mute").addClass("fas fa-volume-up");
2186 jQuery("#audio_control' . $pano_suffix . '").attr("data-play", "on");
2187 playing' . $pano_suffix . ' = true;
2188 }
2189 }, 100);
2190 }
2191
2192 var musicTriggerElem = document.getElementById("' . $panoid . '");
2193 if (musicTriggerElem) {
2194 musicTriggerElem.removeEventListener("click", musicPlay' . $pano_suffix . ');
2195 }
2196 document.removeEventListener("touchstart", musicPlay' . $pano_suffix . ');
2197 document.removeEventListener("click", musicPlay' . $pano_suffix . ');
2198 }
2199 ';
2200 }
2201 }
2202 $html .= '(function ($) {';
2203 $html .= 'jQuery(document).ready(function() {';
2204 $html .= 'var response = ' . $response . ';';
2205 $html .= 'var scenes = response[1];';
2206 $html .= 'if(scenes) {';
2207 $html .= 'var scenedata = scenes.scenes;';
2208 $html .= 'for(var scId in scenedata) {';
2209 $html .= 'if(!scenedata.hasOwnProperty(scId)) continue;';
2210 $html .= 'var scenehotspot = scenedata[scId].hotSpots;';
2211 $html .= 'if(scenehotspot && scenehotspot.length) {';
2212 $html .= 'for(var hIdx = 0; hIdx < scenehotspot.length; hIdx++) {';
2213 $html .= 'if(scenehotspot[hIdx].type === "info") {';
2214 $html .= ' scenehotspot[hIdx]["clickHandlerFunc"] = function(div, args) { if (typeof window.wpvrhotspot === "function") { window.wpvrhotspot(div, args); } };';
2215 $html .= '} else if(scenehotspot[hIdx].type === "scene") {';
2216 $html .= ' scenehotspot[hIdx]["clickHandlerArgs"] = scenehotspot[hIdx]["text"] || "";';
2217 $status = get_option('wpvr_edd_license_status');
2218 if ($status !== false && $status == 'valid') {
2219 $html .='if(typeof wpvr_public !== "undefined") {';
2220 $html .='if(wpvr_public.is_pro_active) {';
2221 $html .= ' scenehotspot[hIdx]["clickHandlerFunc"] = function(div, args) { if (typeof wpvrhotspotscene' . $pano_suffix . ' === "function") { wpvrhotspotscene' . $pano_suffix . '(div, args); } else if (typeof wpvrhotspotscene === "function") { wpvrhotspotscene(div, args); } else if (typeof window.wpvrhotspotscene === "function") { window.wpvrhotspotscene(div, args); } };';
2222 $html .='}';
2223 $html .='}';
2224 }
2225 $html .= '}';
2226
2227 if (wpvr_isMobileDevice() && get_option('dis_on_hover') == "true") {
2228 } else {
2229 $html .= 'if(scenehotspot[hIdx]["createTooltipArgs"] != "") {';
2230 $html .= 'scenehotspot[hIdx]["createTooltipFunc"] = function(div, args) { if (typeof window.wpvrtooltip === "function") { window.wpvrtooltip(div, args); } };';
2231 $html .= '}';
2232 }
2233
2234 $html .= '}';
2235 $html .= '}';
2236 $html .= '}';
2237 $html .= '}';
2238 $html .= 'var panoshow' . $pano_suffix . ';';
2239 $html .= 'var panoshow2' . $pano_suffix . ';';
2240 $html .= 'function initWPVRViewer' . $pano_suffix . '() {';
2241 $html .= 'if (typeof pannellum === "undefined" || typeof jQuery === "undefined") { setTimeout(initWPVRViewer' . $pano_suffix . ', 50); return; }';
2242 $html .= 'panoshow' . $pano_suffix . ' = pannellum.viewer(response[0]["panoid"], scenes);';
2243 $html .= '
2244 window.wpvrViewers = window.wpvrViewers || {};
2245 window.wpvrViewers[response[0]["panoid"]] = panoshow' . $pano_suffix . ';
2246 document.dispatchEvent(new CustomEvent("wpvr:viewer-ready", {
2247 detail: { containerId: response[0]["panoid"], viewer: panoshow' . $pano_suffix . ' }
2248 }));
2249 ';
2250 if ( (float) $scene_fade_duration > 0 ) {
2251 $html .= '
2252 panoshow' . $pano_suffix . '.on("scenechange", function() {
2253 var fadeImage = document.querySelector("#' . $panoid . ' .pnlm-render-container > .pnlm-fade-img");
2254 if (fadeImage) {
2255 fadeImage.style.opacity = "1";
2256 void fadeImage.offsetWidth;
2257 }
2258 });';
2259 }
2260 $html .= '
2261
2262 if(typeof wpvr_public === "undefined" || !wpvr_public.is_pro_active || !wpvr_public.is_license_active) {
2263 panoshow' . $pano_suffix . '.on("load", function() {
2264 jQuery(".pnlm-panorama-info").hide();
2265 jQuery(".pnlm-compass").hide();
2266 });
2267
2268 panoshow' . $pano_suffix . '.on("scenechange", function() {
2269 jQuery(".pnlm-panorama-info").hide();
2270 jQuery(".pnlm-compass").hide();
2271 });
2272 }';
2273 $html .= '}';
2274 $html .= 'initWPVRViewer' . $pano_suffix . '();';
2275 //===Dplicate mode only for vr mode===//
2276 $response2 = json_decode($response);
2277 $response2[1]->compass = false;
2278 $response2[1]->autoRotate = false;
2279 $response = json_encode($response2);
2280 $html .= 'var response_duplicate = ' . $response . ';';
2281 $html .= 'var scenes_duplicate = response_duplicate[1];';
2282
2283 $html .= 'if(scenes_duplicate) {';
2284 $html .= 'var scenedata = scenes_duplicate.scenes;';
2285 $html .= 'for(var scId in scenedata) {';
2286 $html .= 'if(!scenedata.hasOwnProperty(scId)) continue;';
2287 $html .= 'var scenehotspot = scenedata[scId].hotSpots;';
2288 $html .= 'if(scenehotspot && scenehotspot.length) {';
2289 $html .= 'for(var hIdx = 0; hIdx < scenehotspot.length; hIdx++) {';
2290 $html .= 'if(scenehotspot[hIdx]["clickHandlerArgs"] != "") {';
2291 $html .= 'scenehotspot[hIdx]["clickHandlerFunc"] = function(div, args) { if (typeof window.wpvrhotspot === "function") { window.wpvrhotspot(div, args); } };';
2292 $html .= '}';
2293 if (wpvr_isMobileDevice() && get_option('dis_on_hover') == "true") {
2294 } else {
2295 $html .= 'if(scenehotspot[hIdx]["createTooltipArgs"] != "") {';
2296 $html .= 'scenehotspot[hIdx]["createTooltipFunc"] = function(div, args) { if (typeof window.wpvrtooltip === "function") { window.wpvrtooltip(div, args); } };';
2297 $html .= '}';
2298 }
2299 $html .= '}';
2300 $html .= '}';
2301 $html .= '}';
2302 $html .= '}';
2303
2304 $is_pro = apply_filters('is_wpvr_pro_active',false);
2305 $status = get_option('wpvr_edd_license_status');
2306 $html .= 'var vr_mode = "off";';
2307 if ($status !== false && 'valid' == $status && $is_pro) {
2308 $html .= 'function initWPVRViewer2' . $pano_suffix . '() {';
2309 $html .= 'if (typeof pannellum === "undefined" || typeof jQuery === "undefined") { setTimeout(initWPVRViewer2' . $pano_suffix . ', 50); return; }';
2310 $html .= 'panoshow2' . $pano_suffix . ' = pannellum.viewer("' . $panoid2 . '", scenes_duplicate);';
2311 $html .= '
2312 window.wpvrViewers = window.wpvrViewers || {};
2313 window.wpvrViewers["' . $panoid2 . '"] = panoshow2' . $pano_suffix . ';
2314 document.dispatchEvent(new CustomEvent("wpvr:viewer-ready", {
2315 detail: { containerId: "' . $panoid2 . '", viewer: panoshow2' . $pano_suffix . ' }
2316 }));
2317 ';
2318 $html .= '}';
2319 $html .= 'initWPVRViewer2' . $pano_suffix . '();';
2320 // Show Cardboard Mode in Tour
2321 $html .= '
2322 var tim;
2323 var im = 0;
2324 var active_scene = "'.$default_scene.'";
2325 var c_time;
2326 c_time = new Date();
2327 var timer = c_time.getTime() + 2000;
2328 function panoShowCardBoardOnTrigger(data){
2329 if(scenes_duplicate) {
2330 var scenedata = scenes_duplicate.scenes;
2331 for(var i in scenedata) {
2332 if(active_scene === i) {
2333 var scenehotspot = scenedata[i].hotSpots;
2334 if(scenehotspot && scenehotspot.length) {
2335 for(var j = 0; j < scenehotspot.length; j++) {
2336 var plusFiveYaw = Math.round(scenehotspot[j].yaw) + 5;
2337 var minusFiveYaw = Math.round(scenehotspot[j].yaw) - 5;
2338 var plusFivePitch = Math.round(scenehotspot[j].pitch) + 5;
2339 var minusFivePitch = Math.round(scenehotspot[j].pitch) - 5;
2340 if(Math.round(data.pitch) > minusFivePitch) {
2341 if(Math.round(data.pitch) < plusFivePitch) {
2342 if(Math.round(data.yaw) > minusFiveYaw) {
2343 if(Math.round(data.yaw) < plusFiveYaw) {
2344 jQuery(".center-pointer").addClass("wpvr-pluse-effect");
2345 var getScene = scenehotspot[j].sceneId;
2346 if(scenehotspot[j].type == "scene"){
2347 panoshow' . $pano_suffix . '.loadScene(getScene);
2348 panoshow2' . $pano_suffix . '.loadScene(getScene);
2349 }else{
2350 jQuery(".center-pointer").removeClass("wpvr-pluse-effect");
2351 }
2352 } else {
2353 jQuery(".center-pointer").removeClass("wpvr-pluse-effect");
2354 c_time = new Date();
2355 timer = c_time.getTime() + 2000;
2356 }
2357 } else {
2358 jQuery(".center-pointer").removeClass("wpvr-pluse-effect");
2359 c_time = new Date();
2360 timer = c_time.getTime() + 2000;
2361 }
2362 } else {
2363 c_time = new Date();
2364 timer = c_time.getTime() + 2000;
2365 }
2366 } else {
2367 c_time = new Date();
2368 timer = c_time.getTime() + 2000;
2369 }
2370 }
2371 }
2372 }
2373 }
2374 }
2375 };
2376 function vrDeviseOrientation(){
2377 var data = {
2378 pitch: panoshow' . $pano_suffix . '.getPitch(),
2379 yaw: panoshow' . $pano_suffix . '.getYaw(),
2380 };
2381 panoShowCardBoardOnTrigger(data);
2382 }';
2383 $html .= '
2384 function requestFullScreen(){
2385 var elem = document.getElementById("' . $master_container_id . '") || document.getElementById("master-container");
2386 if (elem) {
2387 if (elem.requestFullscreen) {
2388 elem.requestFullscreen();
2389 } else if (elem.webkitRequestFullscreen) { /* Safari */
2390 elem.webkitRequestFullscreen();
2391 } else if (elem.msRequestFullscreen) { /* IE11 */
2392 elem.msRequestFullscreen();
2393 }
2394 }
2395 }
2396 function requestExitFullscreen(){
2397 var elem = document.getElementById("' . $master_container_id . '") || document.getElementById("master-container");
2398 if (document.exitFullscreen) {
2399 document.exitFullscreen();
2400 } else if (document.webkitExitFullscreen) { /* Safari */
2401 document.webkitExitFullscreen();
2402 } else if (document.msExitFullscreen) { /* IE11 */
2403 document.msExitFullscreen();
2404 }
2405 }
2406 jQuery(document).on("click",".fullscreen-button .expand",function() {
2407 jQuery(this).hide()
2408 jQuery(this).parent().find(".compress").show()
2409 requestFullScreen()
2410 });
2411 jQuery(document).on("click",".fullscreen-button .compress",function() {
2412 jQuery(this).hide()
2413 jQuery(this).parent().find(".expand").show()
2414 requestExitFullscreen()
2415 screen.orientation.unlock();
2416 });
2417 let onLoadAnalytics = false;
2418 let sceneLoadAnalytics = false;
2419
2420 function storeAnalyticsData(data) {
2421 if (typeof wpvrAnalyticsObj !== "undefined") {
2422 if (typeof wpvr_public !== "undefined") {
2423 if (wpvr_public.is_pro_active) {
2424 jQuery.ajax({
2425 url: wpvrAnalyticsObj.ajaxUrl,
2426 type: "POST",
2427 data: {
2428 action: "store_scene_hotspot_data",
2429 scene_id: data.scene_id,
2430 tour_id: data.tour_id,
2431 type: data.type,
2432 hotspot_id: data.hotspot_id || "",
2433 user_agent: navigator.userAgent,
2434 device_type: getDeviceType() || "desktop",
2435 nonce: wpvrAnalyticsObj.nonce,
2436 },
2437 success: function (response) {
2438 console.log("Data stored successfully");
2439 },
2440 error: function (error) {
2441 console.log("Error in storing data");
2442 }
2443 });
2444 }
2445 }
2446 } else {
2447 console.warn("Analytics object not available or pro not active");
2448 }
2449 }
2450 function getDeviceType() {
2451 const userAgent = navigator.userAgent.toLowerCase();
2452 if (/mobile|android|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent)) {
2453 return "mobile";
2454 } else if (/tablet|ipad/i.test(userAgent)) {
2455 return "tablet";
2456 } else {
2457 return "desktop";
2458 }
2459 }
2460 panoshow' . $pano_suffix . '.on("scenechange", function(scene) {
2461 onLoadAnalytics = true;
2462 sceneLoadAnalytics = true;
2463 let scene_id = scene;
2464 let tour_id = ' . $id . ';
2465 let type = "scene";
2466 let hotspot_id = "";
2467 let user_agent = navigator.userAgent;
2468 let device_type = getDeviceType() ? getDeviceType() : "desktop";
2469 storeAnalyticsData({
2470 scene_id: scene_id,
2471 tour_id: tour_id,
2472 type: type,
2473 hotspot_id: hotspot_id,
2474 user_agent: user_agent,
2475 device_type: device_type,
2476 });
2477 });
2478 panoshow' . $pano_suffix . '.on("load", function() {
2479 let scene_id = panoshow' . $pano_suffix . '.getScene();
2480 let tour_id = ' . $id . ';
2481 let type = "scene";
2482 let hotspot_id = "";
2483 let user_agent = navigator.userAgent;
2484 let device_type = getDeviceType() ? getDeviceType() : "desktop";
2485 if(!onLoadAnalytics) {
2486 if(!sceneLoadAnalytics) {
2487 storeAnalyticsData({
2488 scene_id: scene_id,
2489 tour_id: tour_id,
2490 type: type,
2491 hotspot_id: hotspot_id,
2492 user_agent: user_agent,
2493 device_type: device_type,
2494 });
2495 }
2496 }
2497 });
2498 function wpvrhotspotscene' . $pano_suffix . '(hotSpotDiv, args) {
2499 onLoadAnalytics = true;
2500 let scene_id = panoshow' . $pano_suffix . '.getScene();
2501 let tour_id = ' . $id . ';
2502 let type = "hotspot";
2503 let hotspot_id = args;
2504 let user_agent = navigator.userAgent;
2505 let device_type = getDeviceType() ? getDeviceType() : "desktop";
2506 storeAnalyticsData({
2507 scene_id: scene_id,
2508 tour_id: tour_id,
2509 type: type,
2510 hotspot_id: hotspot_id,
2511 user_agent: user_agent,
2512 device_type: device_type
2513 });
2514 }
2515 var wpvrhotspotscene = wpvrhotspotscene' . $pano_suffix . ';
2516 ';
2517 $html .= 'panoshow' . $pano_suffix . '.on("scenechange", function (scene){
2518 jQuery(".center-pointer").removeClass("wpvr-pluse-effect")
2519 active_scene = scene;
2520 // if(localStorage.getItem("vr_mode") == "on") {
2521 if(vr_mode == "on") {
2522 jQuery("#' . $panoid2 . ' .pnlm-compass.pnlm-controls.pnlm-control").css("display","none");
2523 jQuery("#' . $panoid . ' .pnlm-compass.pnlm-controls.pnlm-control").css("display","none");
2524 }
2525 });
2526 var compassBlock = "";
2527 var infoBlock = "";
2528 jQuery(document).on("click",".vr_mode_change' . $pano_suffix . '",function (){
2529 jQuery("#' . $panoid2 . ' .pnlm-load-button").trigger("click");
2530 jQuery("#' . $panoid . ' .pnlm-load-button").trigger("click");
2531 var getValue = jQuery(this).val();
2532 var getParent = jQuery(this).parent().parent();
2533 var compass = getParent.find("#' . $panoid2 . ' .pnlm-compass.pnlm-controls.pnlm-control").css("display");
2534 var panoInfo = getParent.find("#' . $panoid . ' .pnlm-panorama-info").css("display");
2535 if(compass == "block"){
2536 compassBlock = "block";
2537 }
2538 if(panoInfo == "block"){
2539 infoBlock = "block";
2540 }
2541 if (getValue == "off") {
2542 requestFullScreen()
2543 screen.orientation.lock("landscape-primary").then(function() {
2544 }).catch(function(error) {
2545 alert("VR Glass Mode not supported in this device");
2546 });
2547 // localStorage.setItem("vr_mode", "on");
2548 vr_mode = "on";
2549 jQuery(".vr-mode-title").show();
2550 jQuery(this).val("on");
2551 getParent.find("#' . $panoid2 . '").css({
2552 "opacity": "1",
2553 "visibility": "visible",
2554 "position": "relative",
2555 });
2556 gyroSwitch = true;
2557 panoshow' . $pano_suffix . '.startOrientation();
2558 panoshow2' . $pano_suffix . '.startOrientation();
2559 panoshow2' . $pano_suffix . '.setPitch(panoshow' . $pano_suffix . '.getPitch(), 0);
2560 panoshow2' . $pano_suffix . '.setYaw(panoshow' . $pano_suffix . '.getYaw(), 0);
2561 getParent.find(".pano-wrap").addClass("wpvr-cardboard-disable-event");
2562 getParent.find("#' . $panoid . ' #zoom-in-out-controls' . $pano_suffix . '").hide();
2563 getParent.find("#' . $panoid . ' #controls' . $pano_suffix . '").hide();
2564 getParent.find("#' . $panoid . ' #explainer_button_' . $pano_suffix . '").hide();
2565 getParent.find("#' . $panoid . ' #generic_form_button_' . $pano_suffix . '").hide();
2566 getParent.find("#' . $panoid . ' #floor_map_button_' . $pano_suffix . '").hide();
2567 getParent.find("#' . $panoid . ' #vrgcontrols' . $pano_suffix . '").hide();
2568 getParent.find("#' . $panoid . ' #sccontrols' . $pano_suffix . '").hide();
2569 getParent.find("#' . $panoid . ' #adcontrol' . $pano_suffix . '").hide();
2570 getParent.find("#' . $panoid . ' .wpvr_slider_nav").hide();
2571 getParent.find("#' . $panoid . ' #cp-logo-controls").hide();
2572 getParent.find("#' . $panoid . ' #wpvr-social-share-bg-box' . $pano_suffix . '").hide();
2573 getParent.find("#' . $panoid2 . ' .pnlm-controls-container").hide();
2574 getParent.find("#' . $panoid . ' .pnlm-controls-container").hide();
2575 getParent.find("#' . $panoid2 . ' .pnlm-compass.pnlm-controls.pnlm-control").hide();
2576 getParent.find("#' . $panoid . ' .pnlm-compass.pnlm-controls.pnlm-control").hide();
2577 getParent.find("#' . $panoid2 . ' .pnlm-panorama-info").hide();
2578 getParent.find("#' . $panoid . ' .pnlm-panorama-info").hide();
2579 getParent.find("#' . $panoid . '").addClass("cardboard-half");
2580 getParent.find("#center-pointer' . $pano_suffix . '").show();
2581 getParent.find(".fullscreen-button").hide();
2582 getParent.find("#' . $panoid . ' #custom-scene-navigation' . $pano_suffix . '").hide();
2583 if (window.DeviceOrientationEvent) {
2584 window.addEventListener("deviceorientation", vrDeviseOrientation);
2585 }
2586 panoshow' . $pano_suffix . '.on("zoomchange", function (data){
2587 panoshow2' . $pano_suffix . '.setHfov(data, 0);
2588 });
2589 panoshow2' . $pano_suffix . '.on("zoomchange", function (data){
2590 panoshow' . $pano_suffix . '.setHfov(data, 0);
2591 });
2592 jQuery(document).on("click","#' . $panoid2 . '",function(event) {
2593 panoshow' . $pano_suffix . '.startOrientation();
2594 panoshow2' . $pano_suffix . '.startOrientation();
2595 });
2596 jQuery(document).on("click","#' . $panoid . '",function(event) {
2597 panoshow' . $pano_suffix . '.startOrientation();
2598 panoshow2' . $pano_suffix . '.startOrientation();
2599 });
2600 panoshow' . $pano_suffix . '.on("mousemove", function (data){
2601 panoshow2' . $pano_suffix . '.setPitch(data.pitch, 0);
2602 panoshow2' . $pano_suffix . '.setYaw(data.yaw, 0);
2603 panoShowCardBoardOnTrigger(data);
2604 });
2605 panoshow2' . $pano_suffix . '.on("mousemove", function (data){
2606 panoshow' . $pano_suffix . '.setPitch(data.pitch, 0);
2607 panoshow' . $pano_suffix . '.setYaw(data.yaw, 0);
2608 panoShowCardBoardOnTrigger(data);
2609 });
2610 panoshow' . $pano_suffix . '.on("touchmove", function (data){
2611 panoshow' . $pano_suffix . '.stopOrientation();
2612 panoshow2' . $pano_suffix . '.stopOrientation();
2613 panoshow2' . $pano_suffix . '.setPitch(data.pitch, 0);
2614 panoshow2' . $pano_suffix . '.setYaw(data.yaw, 0);
2615 panoShowCardBoardOnTrigger(data);
2616 });
2617 panoshow2' . $pano_suffix . '.on("touchmove", function (data){
2618 panoshow' . $pano_suffix . '.stopOrientation();
2619 panoshow2' . $pano_suffix . '.stopOrientation();
2620 panoshow' . $pano_suffix . '.setPitch(data.pitch, 0);
2621 panoshow' . $pano_suffix . '.setYaw(data.yaw, 0);
2622 panoShowCardBoardOnTrigger(data);
2623 });
2624 } else if(getValue == "on") {
2625 screen.orientation.unlock();
2626 requestExitFullscreen();
2627 // localStorage.setItem("vr_mode", "off");
2628 vr_mode = "off";
2629 jQuery(".vr-mode-title").hide();
2630 jQuery(this).val("off");
2631 getParent.find("#' . $panoid2 . '").css({
2632 "opacity": "0",
2633 "visibility": "hidden",
2634 "position": "absolute",
2635 });
2636 getParent.find(".pano-wrap").removeClass("wpvr-cardboard-disable-event");
2637 getParent.find("#' . $panoid . ' #zoom-in-out-controls' . $pano_suffix . '").show();
2638 getParent.find("#' . $panoid . ' #controls' . $pano_suffix . '").show();
2639 getParent.find("#' . $panoid . ' #explainer_button_' . $pano_suffix . '").show();
2640 getParent.find("#' . $panoid . ' #generic_form_button_' . $pano_suffix . '").show();
2641 getParent.find("#' . $panoid . ' #floor_map_button_' . $pano_suffix . '").show();
2642 getParent.find("#' . $panoid2 . ' .pnlm-controls-container").show();
2643 getParent.find("#' . $panoid . ' .pnlm-controls-container").show();
2644 getParent.find("#' . $panoid . ' #vrgcontrols' . $pano_suffix . '").show();
2645 getParent.find("#' . $panoid . ' #sccontrols' . $pano_suffix . '").hide();
2646 getParent.find("#' . $panoid . ' #adcontrol' . $pano_suffix . '").show();
2647 getParent.find("#' . $panoid . ' .wpvr_slider_nav").hide();
2648 getParent.find("#' . $panoid . ' #cp-logo-controls").show();
2649 getParent.find("#' . $panoid . ' #wpvr-social-share-bg-box' . $pano_suffix . '").show();
2650 getParent.find("#' . $panoid . ' #custom-scene-navigation' . $pano_suffix . '").show();
2651 if(compassBlock == "block"){
2652 getParent.find("#' . $panoid2 . ' .pnlm-compass.pnlm-controls.pnlm-control").show();
2653 getParent.find("#' . $panoid . ' .pnlm-compass.pnlm-controls.pnlm-control").show();
2654 }
2655 if(infoBlock == "block"){
2656 getParent.find("#' . $panoid2 . ' .pnlm-panorama-info").show();
2657 getParent.find("#' . $panoid . ' .pnlm-panorama-info").show();
2658 }
2659 getParent.find("#' . $panoid . '").removeClass("cardboard-half");
2660 getParent.find("#center-pointer' . $pano_suffix . '").hide();
2661 getParent.find(".fullscreen-button").hide();
2662 panoshow' . $pano_suffix . '.off("mousemove");
2663 panoshow' . $pano_suffix . '.off("touchmove");
2664 panoshow2' . $pano_suffix . '.off("mousemove");
2665 panoshow2' . $pano_suffix . '.off("touchmove");
2666 if (window.DeviceOrientationEvent) {
2667 window.removeEventListener("deviceorientation", vrDeviseOrientation);
2668 }
2669 }
2670 });';
2671 $html .= 'panoshow2' . $pano_suffix . '.on("load", function (){
2672 // if(localStorage.getItem("vr_mode") == "off") {
2673 if( vr_mode == "off") {
2674 jQuery(".vr-mode-title").hide();
2675 }
2676 else {
2677 jQuery("#' . $panoid2 . ' .pnlm-compass.pnlm-controls.pnlm-control").css("display","none");
2678 jQuery("#' . $panoid . ' .pnlm-compass.pnlm-controls.pnlm-control").css("display","none");
2679 jQuery("#' . $panoid2 . ' .pnlm-panorama-info").hide();
2680 jQuery("#' . $panoid . ' .pnlm-panorama-info").hide();
2681 jQuery(".vr-mode-title").show();
2682 }
2683 });';
2684 }
2685 //=== end Dplicate mode only for vr mode===//
2686 $html .= 'jQuery("#' . $panoid . ' .wpvr-floor-map .floor-plan-pointer").on("click",function(){
2687 var scene_id = jQuery(this).attr("scene_id");
2688 panoshow' . $pano_suffix . '.loadScene(scene_id)
2689 jQuery(".floor-plan-pointer").removeClass("add-pulse")
2690 jQuery(this).addClass("add-pulse")
2691 });';
2692 if ($scene_animation_enabled && is_plugin_active('wpvr-pro/wpvr-pro.php')) {
2693 $animation_type = $postdata['sceneAnimationName'] ?? 'none';
2694 $animation_type = $animation_type === 'fade' ? 'fade_in' : $animation_type;
2695 $animationDuration = $postdata['sceneAnimationTransitionDuration'] ?? '500ms';
2696 $animationDelay = $postdata['sceneAnimationTransitionDelay'] ?? '0ms';
2697 $animation_js = apply_filters('wpvr_scene_animation_js', $id, $animation_type, $animationDuration, $animationDelay);
2698 if (!empty($animation_js)) {
2699 $html .= $animation_js;
2700 $html .= 'panoshow' . $pano_suffix . '.on("load", function (scene){
2701 if (typeof changeScene === "function") {
2702 changeScene();
2703 } else {
2704 console.warn("changeScene function is not defined.");
2705 }
2706 });';
2707 }
2708 }
2709
2710 $html .= 'panoshow' . $pano_suffix . '.on("mousemove", function (data){
2711 jQuery(".add-pulse").css({"transform":"rotate("+data.yaw+"deg)"});
2712 });
2713 ';
2714 $status = get_option('wpvr_edd_license_status');
2715 if ($status !== false && 'valid' == $status && $is_pro){
2716 $html .= 'panoshow' . $pano_suffix . '.on("scenechange", function (scene){
2717 jQuery(".center-pointer").removeClass("wpvr-pluse-effect")
2718 jQuery(".floor-plan-pointer").each(function(index ,element){
2719 var scene_id = jQuery(this).attr("scene_id");
2720 if( active_scene == scene_id ){
2721 jQuery(".floor-plan-pointer").removeClass("add-pulse")
2722 jQuery(this).addClass("add-pulse")
2723 }
2724 });
2725 });';
2726 $html .= 'panoshow' . $pano_suffix . '.on("load", function (){
2727 if(jQuery(".floor-plan-pointer").length > 0){
2728 jQuery(".floor-plan-pointer").each(function(index ,element){
2729 var scene_id = jQuery(this).attr("scene_id");
2730 if( active_scene == scene_id ){
2731 jQuery(".floor-plan-pointer").removeClass("add-pulse")
2732 jQuery(this).addClass("add-pulse")
2733 }
2734 });
2735 }
2736 });';
2737 }
2738 if ($status !== false && 'valid' == $status && $is_pro){
2739 $scene_navigation_content_type = isset($postdata['scene_navigation_content_type']) ? $postdata['scene_navigation_content_type'] : 'scene_id';
2740 $html .= 'jQuery("#' . $panoid . ' .custom-scene-navigation").on("click", function() {
2741 jQuery("#custom-scene-navigation-nav' . $pano_suffix . ' ul").empty();
2742 if (scenes) {
2743 var scene_navigation_content_type = "' . $scene_navigation_content_type . '";
2744 var sceneList = scenes.scenes;
2745 var getScene = panoshow' . $pano_suffix . '.getScene();
2746 for (const key in sceneList) {
2747 let title;
2748 if (scene_navigation_content_type === "scene_title") {
2749 if (sceneList[key].title) {
2750 title = sceneList[key].title;
2751 } else {
2752 if (title == "" || title == undefined) {
2753 if (sceneList[key].panorama) {
2754 title = getImageNameWithoutExtension(sceneList[key].panorama);
2755 }
2756 }
2757 }
2758 } else if (scene_navigation_content_type === "scene_image_name") {
2759 if (sceneList[key].panorama) {
2760 title = getImageNameWithoutExtension(sceneList[key].panorama);
2761 }
2762 } else {
2763 title = key;
2764 }
2765 if (sceneList.hasOwnProperty(key)) {
2766 let ulElement = document.querySelector("#custom-scene-navigation-nav' . $pano_suffix . ' ul");
2767 if (ulElement) {
2768 let liElement = document.createElement("li");
2769 liElement.className = "scene-navigation-list" + (key === getScene ? " active" : "");
2770 liElement.setAttribute("scene_id", key);
2771 liElement.textContent = title;
2772 ulElement.appendChild(liElement);
2773 }
2774 }
2775 }
2776 jQuery("#custom-scene-navigation-nav' . $pano_suffix . '").toggleClass("visible");
2777 }
2778 });';
2779 $html .='function getImageNameWithoutExtension(imageUrl) {
2780 // Split the URL by "/"
2781 var parts = imageUrl.split("/");
2782 // Get the last part which contains the image name
2783 var imageNameWithExtension = parts[parts.length - 1];
2784 // Split the image name by period (.)
2785 var imageNameParts = imageNameWithExtension.split(".");
2786 // Remove the last part (which is the extension) and join the remaining parts
2787 var imageNameWithoutExtension = imageNameParts.slice(0, -1).join(".");
2788 // Return the image name without extension
2789 return imageNameWithoutExtension;
2790 }';
2791 $html .= 'jQuery("#' . $panoid . ' #custom-scene-navigation-nav' . $pano_suffix . ' ul").on("click", "li.scene-navigation-list", function() {
2792 if (scenes) {
2793 jQuery(this).siblings("li").removeClass("active");
2794 jQuery(this).addClass("active");
2795 var scene_key = jQuery(this).attr("scene_id");
2796 panoshow' . $pano_suffix . '.loadScene(scene_key);
2797 }
2798 });';
2799 }
2800 $html .= 'const node = document.querySelector(".add-pulse");
2801 panoshow' . $pano_suffix . '.on("compasschange", function (data){
2802 // const node = document.querySelector(".add-pulse");
2803 // node.style.transform = data;
2804 // jQuery(".add-pulse").css({"transform":data});
2805 });';
2806 $html .= 'panoshow' . $pano_suffix . '.on("load", function (){
2807 // if(localStorage.getItem("vr_mode") == "off") {
2808 if(vr_mode == "off") {
2809 jQuery(".vr-mode-title").hide();
2810 } else {
2811 jQuery("#' . $panoid2 . ' .pnlm-compass.pnlm-controls.pnlm-control").css("display","none");
2812 jQuery("#' . $panoid . ' .pnlm-compass.pnlm-controls.pnlm-control").css("display","none");
2813 jQuery("#' . $panoid2 . ' .pnlm-panorama-info").hide();
2814 jQuery("#' . $panoid . ' .pnlm-panorama-info").hide();
2815 jQuery(".vr-mode-title").show();
2816 }
2817 setTimeout(() => {
2818 window.dispatchEvent(new Event("resize"));
2819 }, 200);
2820 if (jQuery("#' . $panoid . '").children().children(".pnlm-panorama-info:visible").length > 0) {
2821 jQuery("#controls' . $pano_suffix . '").css("bottom", "80px");
2822 }
2823 else {
2824 jQuery("#controls' . $pano_suffix . '").css("bottom", "5px");
2825 }
2826 });';
2827 $html .= 'panoshow' . $pano_suffix . '.on("render", function (){
2828 window.dispatchEvent(new Event("resize"));
2829 });';
2830 $html .= 'if (scenes.autoRotate) {
2831 var wpvrAutoRotateTimer' . $pano_suffix . ' = null;
2832 var wpvrAutoRotateStopDelay' . $pano_suffix . ' = parseInt(scenes.autoRotateStopDelay, 10) || 0;
2833 var wpvrScheduleAutoRotate' . $pano_suffix . ' = function () {
2834 clearTimeout(wpvrAutoRotateTimer' . $pano_suffix . ');
2835 if (wpvrAutoRotateStopDelay' . $pano_suffix . ' > 0) {
2836 wpvrAutoRotateTimer' . $pano_suffix . ' = setTimeout(function () {
2837 panoshow' . $pano_suffix . '.stopAutoRotate();
2838 }, wpvrAutoRotateStopDelay' . $pano_suffix . ');
2839 } else {
2840 wpvrAutoRotateTimer' . $pano_suffix . ' = setTimeout(function () {
2841 panoshow' . $pano_suffix . '.startAutoRotate(scenes.autoRotate, 0);
2842 }, 3000);
2843 }
2844 };
2845 panoshow' . $pano_suffix . '.on("load", wpvrScheduleAutoRotate' . $pano_suffix . ');
2846 panoshow' . $pano_suffix . '.on("scenechange", wpvrScheduleAutoRotate' . $pano_suffix . ');
2847 }';
2848 $html .= 'var touchtime = 0;';
2849 $html .= '
2850 var wpvrHotspotRoot' . $pano_suffix . ' = document.getElementById("' . $panoid . '");
2851 if (wpvrHotspotRoot' . $pano_suffix . ') {
2852 wpvrHotspotRoot' . $pano_suffix . '.addEventListener("click", function (event) {
2853 var hotspot = event.target.closest(".pnlm-hotspot-base");
2854
2855 if (!hotspot) {
2856 return;
2857 }
2858
2859 var hotspotLink = hotspot.closest("a[href]");
2860 if (!hotspotLink) {
2861 hotspotLink = hotspot.querySelector("a[href]");
2862 }
2863
2864 if (!hotspotLink) {
2865 return;
2866 }
2867
2868 event.preventDefault();
2869 event.stopImmediatePropagation();
2870
2871 if ((hotspotLink.getAttribute("target") || "_blank") === "_self") {
2872 window.location.assign(hotspotLink.href);
2873 } else {
2874 window.open(hotspotLink.href, "_blank", "noopener,noreferrer");
2875 }
2876 }, true);
2877 }
2878 ';
2879 if ($vrgallery) {
2880 $gallery_scene_ids = [];
2881 if (isset($panodata["scene-list"])) {
2882 foreach ($panodata["scene-list"] as $panoscenes) {
2883 $scene_key = $panoscenes['scene-id'];
2884 $gallery_scene_ids[] = (string) $scene_key;
2885 $scene_key_gallery = $panoscenes['scene-id'] . '_gallery_' . $pano_suffix;
2886 $html .= 'jQuery(document).on("click","#' . $scene_key_gallery . '",function() {
2887 panoshow' . $pano_suffix . '.loadScene(' . wp_json_encode( (string) $scene_key ) . ');
2888 });';
2889 }
2890 }
2891
2892 $is_modern_gallery = isset($postdata['tourLayout']['layout']) && 'layout1' === $postdata['tourLayout']['layout'];
2893 $gallery_options = $is_modern_gallery
2894 ? '{
2895 loop: false,
2896 nav: false,
2897 dots: false,
2898 margin: 18,
2899 mouseDrag: false,
2900 responsive: {
2901 0: { items: 2 },
2902 320: { items: 3 },
2903 500: { items: 4 },
2904 690: { items: 5 }
2905 }
2906 }'
2907 : '{
2908 autoWidth: true,
2909 loop: false,
2910 nav: false,
2911 dots: false,
2912 margin: 10,
2913 stagePadding: 0
2914 }';
2915
2916 $html .= '
2917 var wpvrGallery' . $pano_suffix . ' = jQuery("#sccontrols' . $pano_suffix . '");
2918 var wpvrGalleryRoot' . $pano_suffix . ' = jQuery("#' . $panoid . '");
2919 var wpvrGalleryScenes' . $pano_suffix . ' = ' . wp_json_encode(array_values($gallery_scene_ids)) . ';
2920 var wpvrGalleryResizeTimer' . $pano_suffix . ';
2921 var wpvrGalleryVisibilityTimer' . $pano_suffix . ';
2922 var wpvrGalleryObserver' . $pano_suffix . ';
2923
2924 function wpvrPrepareSceneNavigation' . $pano_suffix . '() {
2925 var navigationDisabled = wpvrGalleryScenes' . $pano_suffix . '.length < 2;
2926 var previousButtons = wpvrGalleryRoot' . $pano_suffix . '.find(".wpvr_owl_prev");
2927 var nextButtons = wpvrGalleryRoot' . $pano_suffix . '.find(".wpvr_owl_next");
2928
2929 wpvrGallery' . $pano_suffix . '.removeClass("owl-theme");
2930 wpvrGalleryRoot' . $pano_suffix . '.find(".wpvr_slider_nav").removeClass("owl-nav");
2931 previousButtons
2932 .removeClass("owl-prev")
2933 .prop("disabled", navigationDisabled)
2934 .toggleClass("disabled", navigationDisabled);
2935 nextButtons
2936 .removeClass("owl-next")
2937 .prop("disabled", navigationDisabled)
2938 .toggleClass("disabled", navigationDisabled);
2939 }
2940
2941 function wpvrRefreshGallery' . $pano_suffix . '() {
2942 wpvrPrepareSceneNavigation' . $pano_suffix . '();
2943
2944 if (!wpvrGallery' . $pano_suffix . '.length || !wpvrGallery' . $pano_suffix . '.hasClass("owl-loaded") || !wpvrGallery' . $pano_suffix . '.is(":visible")) {
2945 return;
2946 }
2947
2948 if (wpvrGallery' . $pano_suffix . '.css("display") === "flex") {
2949 wpvrGallery' . $pano_suffix . '.css("display", "block");
2950 }
2951 wpvrGallery' . $pano_suffix . '.trigger("refresh.owl.carousel");
2952 wpvrUpdateGalleryNavigation' . $pano_suffix . '(panoshow' . $pano_suffix . '.getScene());
2953 }
2954
2955 function wpvrGallerySceneIndex' . $pano_suffix . '(sceneId) {
2956 return wpvrGalleryScenes' . $pano_suffix . '.indexOf(String(sceneId));
2957 }
2958
2959 function wpvrUpdateGalleryNavigation' . $pano_suffix . '(sceneId) {
2960 var currentIndex = wpvrGallerySceneIndex' . $pano_suffix . '(sceneId);
2961 var activeThumbnailId = String(sceneId) + "_gallery_' . $pano_suffix . '";
2962 var thumbnails = wpvrGallery' . $pano_suffix . '.find("img.scctrl");
2963 var activeThumbnail = thumbnails.filter(function () {
2964 return this.id === activeThumbnailId;
2965 });
2966
2967 wpvrPrepareSceneNavigation' . $pano_suffix . '();
2968 thumbnails.removeClass("wpvr-active-thumbnail");
2969 wpvrGallery' . $pano_suffix . '.find(".owl-item").removeClass("clicked");
2970 activeThumbnail.addClass("wpvr-active-thumbnail");
2971 activeThumbnail.closest(".owl-item").addClass("clicked");
2972
2973 if (currentIndex >= 0) {
2974 if (wpvrGallery' . $pano_suffix . '.hasClass("owl-loaded")) {
2975 wpvrGallery' . $pano_suffix . '.trigger("to.owl.carousel", [currentIndex, 300, true]);
2976 window.setTimeout(wpvrPrepareSceneNavigation' . $pano_suffix . ', 0);
2977 }
2978 }
2979 }
2980
2981 function wpvrLoadAdjacentGalleryScene' . $pano_suffix . '(direction) {
2982 var currentIndex = wpvrGallerySceneIndex' . $pano_suffix . '(panoshow' . $pano_suffix . '.getScene());
2983
2984 if (currentIndex < 0 || wpvrGalleryScenes' . $pano_suffix . '.length < 2) {
2985 return;
2986 }
2987
2988 var targetIndex = (currentIndex + direction + wpvrGalleryScenes' . $pano_suffix . '.length) % wpvrGalleryScenes' . $pano_suffix . '.length;
2989 panoshow' . $pano_suffix . '.loadScene(wpvrGalleryScenes' . $pano_suffix . '[targetIndex]);
2990 }
2991
2992 if (jQuery.fn.owlCarousel) {
2993 if (wpvrGallery' . $pano_suffix . '.length) {
2994 if (!wpvrGallery' . $pano_suffix . '.hasClass("owl-loaded")) {
2995 wpvrGallery' . $pano_suffix . '.owlCarousel(' . $gallery_options . ');
2996 }
2997 }
2998 }
2999
3000 wpvrGalleryRoot' . $pano_suffix . '
3001 .off("click.wpvrGalleryRefresh' . $pano_suffix . '", "#vrgcontrols' . $pano_suffix . '")
3002 .on("click.wpvrGalleryRefresh' . $pano_suffix . '", "#vrgcontrols' . $pano_suffix . '", function () {
3003 wpvrPrepareSceneNavigation' . $pano_suffix . '();
3004 window.setTimeout(wpvrRefreshGallery' . $pano_suffix . ', 450);
3005 });
3006
3007 if (wpvrGalleryRoot' . $pano_suffix . '.length) {
3008 wpvrGalleryRoot' . $pano_suffix . '[0].addEventListener("click", function (event) {
3009 var navigationButton = event.target.closest(".wpvr_owl_prev, .wpvr_owl_next");
3010
3011 if (!navigationButton) {
3012 return;
3013 }
3014
3015 if (!wpvrGalleryRoot' . $pano_suffix . '[0].contains(navigationButton)) {
3016 return;
3017 }
3018
3019 event.preventDefault();
3020 event.stopImmediatePropagation();
3021
3022 if (navigationButton.classList.contains("wpvr_owl_prev")) {
3023 wpvrLoadAdjacentGalleryScene' . $pano_suffix . '(-1);
3024 } else {
3025 wpvrLoadAdjacentGalleryScene' . $pano_suffix . '(1);
3026 }
3027 }, true);
3028 }
3029
3030 jQuery(window)
3031 .off("resize.wpvrGallery' . $pano_suffix . '")
3032 .on("resize.wpvrGallery' . $pano_suffix . '", function () {
3033 window.clearTimeout(wpvrGalleryResizeTimer' . $pano_suffix . ');
3034 wpvrGalleryResizeTimer' . $pano_suffix . ' = window.setTimeout(wpvrRefreshGallery' . $pano_suffix . ', 100);
3035 });
3036
3037 if (window.MutationObserver) {
3038 if (wpvrGallery' . $pano_suffix . '.length) {
3039 wpvrGalleryObserver' . $pano_suffix . ' = new MutationObserver(function () {
3040 window.clearTimeout(wpvrGalleryVisibilityTimer' . $pano_suffix . ');
3041 wpvrGalleryVisibilityTimer' . $pano_suffix . ' = window.setTimeout(wpvrRefreshGallery' . $pano_suffix . ', 50);
3042 });
3043 wpvrGalleryObserver' . $pano_suffix . '.observe(wpvrGallery' . $pano_suffix . '[0], {
3044 attributes: true,
3045 attributeFilter: ["style"]
3046 });
3047 }
3048 }
3049
3050 panoshow' . $pano_suffix . '.on("scenechange", function (sceneId) {
3051 wpvrUpdateGalleryNavigation' . $pano_suffix . '(sceneId);
3052 });
3053
3054 window.setTimeout(wpvrRefreshGallery' . $pano_suffix . ', 0);
3055 window.setTimeout(function () {
3056 wpvrUpdateGalleryNavigation' . $pano_suffix . '(panoshow' . $pano_suffix . '.getScene());
3057 }, 0);
3058 ';
3059 }
3060 //===Custom Control===//
3061 if (isset($custom_control)) {
3062 if ($custom_control['panupSwitch'] == "on" && 'valid' == $status && $is_pro) {
3063 $html .= 'document.getElementById("pan-up' . $pano_suffix . '").addEventListener("click", function(e) {';
3064 $html .= 'panoshow' . $pano_suffix . '.setPitch(panoshow' . $pano_suffix . '.getPitch() + 10);';
3065 $html .= '});';
3066 }
3067 if ($custom_control['panDownSwitch'] == "on" && 'valid' == $status && $is_pro) {
3068 $html .= 'document.getElementById("pan-down' . $pano_suffix . '").addEventListener("click", function(e) {';
3069 $html .= 'panoshow' . $pano_suffix . '.setPitch(panoshow' . $pano_suffix . '.getPitch() - 10);';
3070 $html .= '});';
3071 }
3072 if ($custom_control['panLeftSwitch'] == "on" && 'valid' == $status && $is_pro) {
3073 $html .= 'document.getElementById("pan-left' . $pano_suffix . '").addEventListener("click", function(e) {';
3074 $html .= 'panoshow' . $pano_suffix . '.setYaw(panoshow' . $pano_suffix . '.getYaw() - 10);';
3075 $html .= '});';
3076 }
3077 if ($custom_control['panRightSwitch'] == "on" && 'valid' == $status && $is_pro) {
3078 $html .= 'document.getElementById("pan-right' . $pano_suffix . '").addEventListener("click", function(e) {';
3079 $html .= 'panoshow' . $pano_suffix . '.setYaw(panoshow' . $pano_suffix . '.getYaw() + 10);';
3080 $html .= '});';
3081 }
3082 if ($custom_control['panZoomInSwitch'] == "on") {
3083 $html .= 'document.getElementById("zoom-in' . $pano_suffix . '").addEventListener("click", function(e) {';
3084 $html .= 'panoshow' . $pano_suffix . '.setHfov(panoshow' . $pano_suffix . '.getHfov() - 10);';
3085 $html .= '});';
3086 }
3087 if ($custom_control['panZoomOutSwitch'] == "on") {
3088 $html .= 'document.getElementById("zoom-out' . $pano_suffix . '").addEventListener("click", function(e) {';
3089 $html .= 'panoshow' . $pano_suffix . '.setHfov(panoshow' . $pano_suffix . '.getHfov() + 10);';
3090 $html .= '});';
3091 }
3092 if ($custom_control['panFullscreenSwitch'] == "on" && 'valid' == $status && $is_pro) {
3093 $html .= 'document.getElementById("fullscreen' . $pano_suffix . '").addEventListener("click", function(e) {';
3094 $html .= 'panoshow' . $pano_suffix . '.toggleFullscreen();';
3095 $html .= '});';
3096 $html .= '(function() {';
3097 $html .= 'function wpvrFsChange' . $pano_suffix . '() {';
3098 $html .= 'var fsIcon = document.querySelector("#fullscreen' . $pano_suffix . ' i");';
3099 $html .= 'if (!fsIcon) return;';
3100 $html .= 'if (document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement) {';
3101 $html .= 'fsIcon.classList.remove("fa-expand"); fsIcon.classList.add("fa-minimize");';
3102 $html .= '} else {';
3103 $html .= 'fsIcon.classList.remove("fa-minimize"); fsIcon.classList.add("fa-expand");';
3104 $html .= '}';
3105 $html .= '}';
3106 $html .= 'document.addEventListener("fullscreenchange", wpvrFsChange' . $pano_suffix . ');';
3107 $html .= 'document.addEventListener("webkitfullscreenchange", wpvrFsChange' . $pano_suffix . ');';
3108 $html .= 'document.addEventListener("mozfullscreenchange", wpvrFsChange' . $pano_suffix . ');';
3109 $html .= 'document.addEventListener("MSFullscreenChange", wpvrFsChange' . $pano_suffix . ');';
3110 $html .= '})();';
3111 }
3112 if ($custom_control['backToHomeSwitch'] == "on" && 'valid' == $status && $is_pro) {
3113 $html .= 'document.getElementById("backToHome' . $pano_suffix . '").addEventListener("click", function(e) {';
3114 $html .= 'panoshow' . $pano_suffix . '.loadScene(' . wp_json_encode( (string) $default_scene ) . ');';
3115 $html .= '});';
3116 }
3117 if ($gyro_button_enabled && 'valid' == $status && $is_pro) {
3118 $html .= '
3119 (function() {
3120 var gyroButton = document.getElementById("gyroscope' . $pano_suffix . '");
3121 var gyroIcon = gyroButton ? gyroButton.querySelector("i") : null;
3122 var activeColor = ' . wp_json_encode($custom_control['gyroscopeColor']) . ';
3123 var permissionGranted = false;
3124
3125 if (!gyroButton || !gyroIcon) {
3126 return;
3127 }
3128
3129 function updateGyroscopeState() {
3130 gyroIcon.style.color = panoshow' . $pano_suffix . '.isOrientationActive()
3131 ? activeColor
3132 : "red";
3133 }
3134
3135 function startGyroscope() {
3136 if (!panoshow' . $pano_suffix . '.isOrientationSupported()) {
3137 updateGyroscopeState();
3138 return;
3139 }
3140
3141 panoshow' . $pano_suffix . '.startOrientation();
3142 window.setTimeout(updateGyroscopeState, 0);
3143 }
3144
3145 function requestGyroscopePermission() {
3146 var orientationEvent = window.DeviceOrientationEvent;
3147 if (orientationEvent) {
3148 if (typeof orientationEvent.requestPermission === "function") {
3149 if (!permissionGranted) {
3150 orientationEvent.requestPermission()
3151 .then(function(state) {
3152 permissionGranted = state === "granted";
3153 if (permissionGranted) {
3154 startGyroscope();
3155 } else {
3156 updateGyroscopeState();
3157 }
3158 })
3159 .catch(updateGyroscopeState);
3160 return;
3161 }
3162 }
3163 }
3164
3165 startGyroscope();
3166 }
3167
3168 panoshow' . $pano_suffix . '.on("load", updateGyroscopeState);
3169 panoshow' . $pano_suffix . '.on("scenechange", updateGyroscopeState);
3170
3171 gyroButton.addEventListener("click", function() {
3172 if (panoshow' . $pano_suffix . '.isOrientationActive()) {
3173 panoshow' . $pano_suffix . '.stopOrientation();
3174 updateGyroscopeState();
3175 return;
3176 }
3177
3178 requestGyroscopePermission();
3179 });
3180 })();';
3181 }
3182 }
3183 $angle_up = '<i class="fa fa-angle-up"></i>';
3184 $angle_down = '<i class="fa fa-angle-down"></i>';
3185 $sin_qout = "'";
3186
3187 //===Explainer Script===//
3188
3189 if ($autoplay_bg_music == 'on') {
3190 $html .= 'jQuery(document).on("click","#explainer_button_' . $pano_suffix . '",function() {
3191 jQuery("#explainer' . $pano_suffix . '").slideToggle();
3192 playing' . $pano_suffix . ' = false;
3193 var x' . $pano_suffix . ' = document.getElementById("vrAudio' . $pano_suffix . '");
3194 jQuery("#vr-volume' . $pano_suffix . '").removeClass("fas fa-volume-up");
3195 jQuery("#vr-volume' . $pano_suffix . '").addClass("fas fa-volume-mute");
3196 x' . $pano_suffix . '.pause();
3197 });
3198 jQuery(document).on("click",".close-explainer-video",function() {
3199 jQuery(this).parent(".explainer").hide();
3200 var el_src = jQuery(".vr-iframe").attr("src");
3201 jQuery(".vr-iframe").attr("src", el_src);
3202 });';
3203 } else {
3204 $html .= 'jQuery(document).on("click","#explainer_button_' . $pano_suffix . '",function() {
3205 jQuery("#explainer' . $pano_suffix . '").slideToggle(function(){
3206 var $explainerVideoId = jQuery("#explainer' . $pano_suffix . '");
3207 var $explainerVideoIframe = $explainerVideoId.find("iframe");
3208 var explainerVideoIframSrc = $explainerVideoIframe.attr("src");
3209 $explainerVideoIframe.attr("src", "");
3210 $explainerVideoIframe.attr("src", explainerVideoIframSrc);
3211 });
3212 });
3213 jQuery(document).on("click", ".close-explainer-video", function() {
3214 var $explainer = jQuery(this).parent(".explainer");
3215 var $iframe = $explainer.find("iframe");
3216 var el_src = $iframe.attr("src");
3217 $iframe.attr("src", "");
3218 $explainer.hide();
3219 $iframe.attr("src", el_src);
3220 });';
3221 }
3222
3223 $html .= '
3224 jQuery(document).on("click","#' . $panoid . '",function(event) {
3225 var isCross = event.target.closest(".cross");
3226 var isActiveModal = event.target.closest(".custom-ifram-wrapper");
3227 var isForm = event.target.closest(".wpvr-hotspot-tweak-contents-wrapper");
3228 var isHotspot = event.target.closest(".pnlm-hotspot-base");
3229 if(isCross != null){
3230 return;
3231 }
3232 if(isForm != null){
3233 jQuery(this).addClass("show-modal");
3234 }else if(isActiveModal == null){
3235 if(isHotspot == null){
3236 jQuery(".custom-ifram-wrapper .custom-ifram").empty();
3237 jQuery(".custom-ifram-wrapper").hide();
3238 jQuery(this).removeClass("show-modal");
3239 jQuery(".wpvr-hotspot-tweak-contents-wrapper").hide();
3240 }
3241 }
3242 });';
3243
3244 //===Explainer Script End===//
3245
3246 //===generic form script===//
3247 if (isset($postdata["genericform"]) && $postdata["genericform"] === 'on') {
3248 $html .= '
3249 jQuery(document).on("click","#generic_form_button_' . $pano_suffix . '",function() {
3250 jQuery("#wpvr-generic-form' . $pano_suffix . '").fadeToggle();
3251 });
3252
3253 jQuery(document).on("click",".close-generic-form",function() {
3254 jQuery(this).parent(".wpvr-generic-form").fadeOut()
3255 });
3256 ';
3257 }
3258 //===generic from script===//
3259
3260 //===Floor map Script===//
3261 $html .= 'jQuery(document).on("click","#floor_map_button_' . $pano_suffix . '",function() {
3262 jQuery("#wpvr-floor-map' . $pano_suffix . '").toggle().removeClass("fullwindow");
3263 });
3264 jQuery(document).on("dblclick","#wpvr-floor-map' . $pano_suffix . '",function(){
3265 jQuery(this).addClass("fullwindow");
3266 jQuery(this).parents(".pano-wrap").addClass("show-modal");
3267 });
3268 jQuery(document).on("click",".close-floor-map-plan",function() {
3269 jQuery(this).parent(".wpvr-floor-map").hide();
3270 jQuery(this).parent(".wpvr-floor-map").removeClass("fullwindow");
3271 jQuery(this).parents(".pano-wrap").removeClass("show-modal");
3272 });';
3273 //===Floor map Script End===//
3274
3275 if ($vrgallery_display) {
3276
3277 if (!$autoload) {
3278 $html .= 'jQuery(document).ready(function($){
3279 jQuery("#sccontrols' . $pano_suffix . '").hide();
3280 jQuery(".vrgctrl' . $pano_suffix . '").html(' . $sin_qout . $angle_up . $sin_qout . ');
3281 jQuery("#sccontrols' . $pano_suffix . '").hide();
3282 jQuery("#' . $panoid . ' .wpvr_slider_nav").hide();
3283 });';
3284 $html .= 'var slide' . $pano_suffix . ' = "down";
3285 jQuery(document).on("click","#vrgcontrols' . $pano_suffix . '",function() {
3286 if (slide' . $pano_suffix . ' == "up") {
3287 jQuery(".vrgctrl' . $pano_suffix . '").empty();
3288 jQuery(".vrgctrl' . $pano_suffix . '").html(' . $sin_qout . $angle_up . $sin_qout . ');
3289 slide' . $pano_suffix . ' = "down";
3290 jQuery("#' . $panoid . ' .wpvr_slider_nav").slideToggle();
3291 jQuery("#sccontrols' . $pano_suffix . '").slideToggle(function(){
3292 if (jQuery(".elementor-edit-mode .elementor-widget-container").length) {
3293 if (jQuery(this).is(":visible")) {
3294 jQuery(".elementor-edit-mode .elementor-widget-container .wpvr-cardboard .pnlm-container #sccontrols' . $pano_suffix . '").css({
3295 "display": "flex",
3296 "justify-content": "center",
3297 "align-items": "center",
3298 "gap": "15px"
3299 });
3300 }
3301 }
3302 if (jQuery(".bricks-is-frontend").length) {
3303 if (jQuery(this).is(":visible")) {
3304 jQuery(".bricks-is-frontend .wpvr-cardboard .pnlm-container #sccontrols' . $pano_suffix . '").css({
3305 "display": "flex",
3306 "justify-content": "center",
3307 "align-items": "center"
3308 });
3309 }
3310 }
3311 });
3312 }
3313 else {
3314 jQuery(".vrgctrl' . $pano_suffix . '").empty();
3315 jQuery(".vrgctrl' . $pano_suffix . '").html(' . $sin_qout . $angle_down . $sin_qout . ');
3316 slide' . $pano_suffix . ' = "up";
3317 jQuery("#' . $panoid . ' .wpvr_slider_nav").slideToggle();
3318 jQuery("#sccontrols' . $pano_suffix . '").slideToggle(function(){
3319 if (jQuery(".elementor-edit-mode .elementor-widget-container").length) {
3320 if (jQuery(this).is(":visible")) {
3321 jQuery(".elementor-edit-mode .elementor-widget-container .wpvr-cardboard .pnlm-container #sccontrols' . $pano_suffix . '").css({
3322 "display": "flex",
3323 "justify-content": "center",
3324 "align-items": "center",
3325 "gap": "15px"
3326 });
3327 }
3328 }
3329 if (jQuery(".bricks-is-frontend").length) {
3330 if (jQuery(this).is(":visible")) {
3331 jQuery(".bricks-is-frontend .wpvr-cardboard .pnlm-container #sccontrols' . $pano_suffix . '").css({
3332 "display": "flex",
3333 "justify-content": "center",
3334 "align-items": "center"
3335 });
3336 }
3337 }
3338 });
3339 }
3340 });';
3341 } else {
3342 $html .= 'jQuery(document).ready(function($){
3343 jQuery("#sccontrols' . $pano_suffix . '").show();
3344 jQuery(".vrgctrl' . $pano_suffix . '").html(' . $sin_qout . $angle_down . $sin_qout . ');
3345 jQuery("#' . $panoid . ' .wpvr_slider_nav").show();
3346 });';
3347 $html .= 'var slide' . $pano_suffix . ' = "down";
3348 jQuery(document).on("click","#vrgcontrols' . $pano_suffix . '",function() {
3349 if (slide' . $pano_suffix . ' == "up") {
3350 jQuery(".vrgctrl' . $pano_suffix . '").empty();
3351 jQuery(".vrgctrl' . $pano_suffix . '").html(' . $sin_qout . $angle_down . $sin_qout . ');
3352 slide' . $pano_suffix . ' = "down";
3353 } else {
3354 jQuery(".vrgctrl' . $pano_suffix . '").empty();
3355 jQuery(".vrgctrl' . $pano_suffix . '").html(' . $sin_qout . $angle_up . $sin_qout . ');
3356 slide' . $pano_suffix . ' = "up";
3357 }
3358 jQuery("#' . $panoid . ' .wpvr_slider_nav").slideToggle();
3359 jQuery("#sccontrols' . $pano_suffix . '").slideToggle(function(){
3360 if (jQuery(".elementor-edit-mode .elementor-widget-container").length) {
3361 if (jQuery(this).is(":visible")) {
3362 jQuery(".elementor-edit-mode .elementor-widget-container .wpvr-cardboard .pnlm-container #sccontrols' . $pano_suffix . '").css({
3363 "display": "flex",
3364 "justify-content": "center",
3365 "align-items": "center",
3366 "gap": "15px"
3367 });
3368 }
3369 }
3370 if (jQuery(".bricks-is-frontend").length) {
3371 if (jQuery(this).is(":visible")) {
3372 jQuery(".bricks-is-frontend .wpvr-cardboard .pnlm-container #sccontrols' . $pano_suffix . '").css({
3373 "display": "flex",
3374 "justify-content": "center",
3375 "align-items": "center"
3376 });
3377 }
3378 }
3379 });
3380 });';
3381 }
3382 } else {
3383 $html .= 'jQuery(document).ready(function($){
3384 jQuery("#sccontrols' . $pano_suffix . '").hide();
3385 jQuery("#' . $panoid . ' .wpvr_slider_nav").hide();
3386 jQuery(".vrgctrl' . $pano_suffix . '").html(' . $sin_qout . $angle_up . $sin_qout . ');
3387 });';
3388 $html .= 'var slide' . $pano_suffix . ' = "down";
3389 jQuery(document).on("click","#vrgcontrols' . $pano_suffix . '",function() {
3390 if (slide' . $pano_suffix . ' == "up") {
3391 jQuery(".vrgctrl' . $pano_suffix . '").empty();
3392 jQuery(".vrgctrl' . $pano_suffix . '").html(' . $sin_qout . $angle_up . $sin_qout . ');
3393 slide' . $pano_suffix . ' = "down";
3394 }
3395 else {
3396 jQuery(".vrgctrl' . $pano_suffix . '").empty();
3397 jQuery(".vrgctrl' . $pano_suffix . '").html(' . $sin_qout . $angle_down . $sin_qout . ');
3398 slide' . $pano_suffix . ' = "up";
3399 }
3400 jQuery("#' . $panoid . ' .wpvr_slider_nav").slideToggle();
3401 jQuery("#sccontrols' . $pano_suffix . '").slideToggle(function(){
3402 if (jQuery(".elementor-edit-mode .elementor-widget-container").length) {
3403 if (jQuery(this).is(":visible")) {
3404 jQuery(".elementor-edit-mode .elementor-widget-container .wpvr-cardboard .pnlm-container #sccontrols' . $pano_suffix . '").css({
3405 "display": "flex",
3406 "justify-content": "center",
3407 "align-items": "center",
3408 "gap": "15px"
3409 });
3410 }
3411 }
3412 if (jQuery(".bricks-is-frontend").length) {
3413 if (jQuery(this).is(":visible")) {
3414 jQuery(".bricks-is-frontend .wpvr-cardboard .pnlm-container #sccontrols' . $pano_suffix . '").css({
3415 "display": "flex",
3416 "justify-content": "center",
3417 "align-items": "center"
3418 });
3419 }
3420 }
3421 });
3422 });';
3423 }
3424 if (!$autoload) {
3425 $html .= 'jQuery(document).ready(function(){
3426 jQuery("#controls' . $pano_suffix . '").hide();
3427 jQuery("#zoom-in-out-controls' . $pano_suffix . '").hide();
3428 jQuery("#adcontrol' . $pano_suffix . '").hide();
3429 jQuery("#explainer_button_' . $pano_suffix . '").hide();
3430 jQuery("#generic_form_button_' . $pano_suffix . '").hide();
3431 jQuery("#floor_map_button_' . $pano_suffix . '").hide();
3432 jQuery("#vrgcontrols' . $pano_suffix . '").hide();
3433 jQuery("#cp-logo-controls").hide();
3434 jQuery(".custom-scene-navigation").hide();
3435 jQuery("#' . $panoid . '").find(".pnlm-panorama-info").hide();
3436 });';
3437 if ($vrgallery_display) {
3438 $html .= 'var load_once' . $pano_suffix . ' = "true";';
3439 $html .= 'panoshow' . $pano_suffix . '.on("load", function (){
3440 if (load_once' . $pano_suffix . ' == "true") {
3441 load_once' . $pano_suffix . ' = "false";
3442 jQuery("#sccontrols' . $pano_suffix . '").slideToggle(function(){
3443 if (jQuery(".elementor-edit-mode .elementor-widget-container").length) {
3444 if (jQuery(this).is(":visible")) {
3445 jQuery(".elementor-edit-mode .elementor-widget-container .wpvr-cardboard .pnlm-container #sccontrols' . $pano_suffix . '").css("display", "flex");
3446 }
3447 }
3448 if (jQuery(".bricks-is-frontend").length) {
3449 if (jQuery(this).is(":visible")) {
3450 jQuery(".bricks-is-frontend .wpvr-cardboard .pnlm-container #sccontrols' . $pano_suffix . '").css({
3451 "display": "flex",
3452 "justify-content": "center",
3453 "align-items": "center"
3454 });
3455 }
3456 }
3457 });
3458 jQuery("#' . $panoid . ' .wpvr_slider_nav").slideToggle();
3459 }
3460 });';
3461 }
3462 $html .= 'panoshow' . $pano_suffix . '.on("load", function (){
3463 jQuery("#controls' . $pano_suffix . '").show();
3464 jQuery("#zoom-in-out-controls' . $pano_suffix . '").show();
3465 jQuery("#adcontrol' . $pano_suffix . '").show();
3466 jQuery("#explainer_button_' . $pano_suffix . '").show();
3467 jQuery("#generic_form_button_' . $pano_suffix . '").show();
3468 jQuery("#floor_map_button_' . $pano_suffix . '").show();
3469 jQuery("#vrgcontrols' . $pano_suffix . '").show();
3470 jQuery("#cp-logo-controls").show();
3471 jQuery(".custom-scene-navigation").show();
3472 jQuery("#' . $panoid . '").find(".pnlm-panorama-info").show();
3473 });';
3474 }
3475
3476 //==Old code working properly==//
3477
3478 $previeword = "Click to Load Panorama";
3479 if (isset($postdata['previewtext']) && $postdata['previewtext'] != '') {
3480 $previeword = $postdata['previewtext'];
3481 }
3482 $html .= 'jQuery(".elementor-tab-title").click(function(){
3483 var element_id;
3484 var pano_id;
3485 var element_id = this.id;
3486 element_id = element_id.split("-");
3487 element_id = element_id[3];
3488 jQuery("#elementor-tab-content-"+element_id).find("#' . $master_container_id . '").children("div").eq(1).addClass("awwww");
3489 var pano_id = jQuery(".awwww").attr("id");
3490 jQuery("#elementor-tab-content-"+element_id).find("#' . $master_container_id . '").children("div").eq(1).removeClass("awwww");;
3491 if (pano_id != undefined) {
3492 if (pano_id == "' . $panoid . '") {
3493 jQuery("#' . $panoid . '").children(".pnlm-render-container").remove();
3494 jQuery("#' . $panoid . '").children(".pnlm-ui").remove();
3495 panoshow' . $pano_suffix . ' = pannellum.viewer(response[0]["panoid"], scenes);
3496 window.wpvrViewers = window.wpvrViewers || {};
3497 window.wpvrViewers[response[0]["panoid"]] = panoshow' . $pano_suffix . ';
3498 document.dispatchEvent(new CustomEvent("wpvr:viewer-ready", {
3499 detail: { containerId: response[0]["panoid"], viewer: panoshow' . $pano_suffix . ' }
3500 }));
3501 jQuery("#' . $panoid . '").children(".pnlm-ui").find(".pnlm-load-button p").text(' . wp_json_encode( (string) $previeword ) . ')
3502 setTimeout(function() {
3503 // panoshow' . $pano_suffix . '.loadScene("' . $default_scene . '");
3504 window.dispatchEvent(new Event("resize"));
3505 if (jQuery("#' . $panoid . '").children().children(".pnlm-panorama-info:visible").length > 0) {
3506 jQuery("#controls' . $pano_suffix . '").css("bottom", "55px");
3507 } else {
3508 jQuery("#controls' . $pano_suffix . '").css("bottom", "5px");
3509 }
3510 }, 200);
3511 }
3512 }
3513 });';
3514 $html .= 'jQuery(".geodir-tab-head dd, #vr-tour-tab").click(function(){
3515 jQuery("#' . $panoid . '").children(".pnlm-render-container").remove();
3516 jQuery("#' . $panoid . '").children(".pnlm-ui").remove();
3517 panoshow' . $pano_suffix . ' = pannellum.viewer(response[0]["panoid"], scenes);
3518 window.wpvrViewers = window.wpvrViewers || {};
3519 window.wpvrViewers[response[0]["panoid"]] = panoshow' . $pano_suffix . ';
3520 document.dispatchEvent(new CustomEvent("wpvr:viewer-ready", {
3521 detail: { containerId: response[0]["panoid"], viewer: panoshow' . $pano_suffix . ' }
3522 }));
3523 setTimeout(function() {
3524 panoshow' . $pano_suffix . '.loadScene(' . wp_json_encode( (string) $default_scene ) . ');
3525 window.dispatchEvent(new Event("resize"));
3526 if (jQuery("#' . $panoid . '").children().children(".pnlm-panorama-info:visible").length > 0) {
3527 jQuery("#controls' . $pano_suffix . '").css("bottom", "55px");
3528 }
3529 else {
3530 jQuery("#controls' . $pano_suffix . '").css("bottom", "5px");
3531 }
3532 }, 200);
3533 });';
3534 if (isset($previeword) && $previeword != '') {
3535 $html .= '
3536 jQuery("#' . $panoid . '").children(".pnlm-ui").find(".pnlm-load-button p").text(' . wp_json_encode( (string) $previeword ) . ')
3537 ';
3538 }
3539 if ($default_global_zoom != '' || $max_global_zoom != '' || $min_global_zoom != '') {
3540 $html .= 'jQuery(".globalzoom").val("on").change();';
3541 }
3542
3543 $html .= 'jQuery("#' . $panoid . ' .pnlm-title-box").on("mouseenter", function(){
3544 jQuery(this).attr("title", jQuery(this).text());
3545 });
3546 jQuery("#' . $panoid . ' .pnlm-title-box").on("mouseleave", function(){
3547 jQuery(this).removeAttr("title");
3548 });';
3549 $html .= '});';
3550 $html .= '})(jQuery);';
3551 $html .= '</script>';
3552 $tour_data = [];
3553 if(defined("WPVR_PRO_VERSION")){
3554 $tour_data = array(
3555 'explainerControlSwitch' => (
3556 isset( $postdata['explainerSwitch'] )
3557 && in_array( $postdata['explainerSwitch'], array( true, 1, '1', 'on' ), true )
3558 ) ? 'on' : 'off',
3559 'floor_plan_enable' => $floor_plan_enable ?? '',
3560 'floor_plan_image' => $floor_plan_image ?? '',
3561 'custom_control' => $custom_control ?? '',
3562 );
3563 }
3564 ob_start();
3565 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
3566 echo $html;
3567 $output = ob_get_clean();
3568 return apply_filters('wpvr_generate_tour_layout_html', $output ,$postdata ,$id, $tour_data);
3569 }
3570
3571 function replace_callback($matches){
3572 foreach ($matches as $match){
3573 return str_replace('<img','<img decoding="async"',$match);
3574 }
3575
3576 }
3577
3578 private function sanitize_content_preserve_styles($content, $allow_forms = false) {
3579 // Decode HTML entities first (in case content was encoded in database)
3580 $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8');
3581
3582 // Escape or strip <script> blocks
3583 if ($allow_forms) {
3584 $content = preg_replace('/<script\b[^>]*>[\s\S]*?<\/script>/i', '', $content);
3585 } else {
3586 $content = preg_replace_callback('/<script\b[^>]*>(.*?)<\/script>/si', function($matches) {
3587 return esc_html($matches[0]); // Convert to plain text
3588 }, $content);
3589 }
3590
3591 // Strip dangerous URL-based attributes
3592 $content = preg_replace('/(href|action|formaction)\s*=\s*["\']?\s*(javascript|vbscript|data|about):/i', '$1=""', $content);
3593
3594 // Escape inline event handlers (onclick, onhover, etc.) to display as text
3595 $content = preg_replace_callback('/\s*(on\w+)\s*=\s*(["\'])([^"\']*)\2/i', function($matches) {
3596 // Escape the attribute value but keep the attribute name visible as text
3597 return ' ' . esc_html($matches[1]) . '=' . $matches[2] . esc_html($matches[3]) . $matches[2];
3598 }, $content);
3599 $content = preg_replace_callback('/\s*(on\w+)\s*=\s*([^>\s]+)/i', function($matches) {
3600 // Handle unquoted event handlers
3601 return ' ' . esc_html($matches[1]) . '=' . esc_html($matches[2]);
3602 }, $content);
3603
3604 // Remove unsafe embedded/interactive elements
3605 if ($allow_forms) {
3606 $content = preg_replace('/<(object|embed|applet|frame|frameset|meta|link|base)\b[^>]*>/i', '', $content);
3607 $content = preg_replace('/<\/(object|embed|applet|frame|frameset|meta|link|base)>/i', '', $content);
3608 } else {
3609 $content = preg_replace('/<(object|embed|applet|frame|frameset|meta|link|base|form|input|button|textarea|select|option)\b[^>]*>/i', '', $content);
3610 $content = preg_replace('/<\/(object|embed|applet|frame|frameset|meta|link|base|form|input|button|textarea|select|option)>/i', '', $content);
3611 }
3612
3613 // Clean style attributes safely
3614 $content = preg_replace_callback('/style\s*=\s*["\']([^"\']*)["\']/', function($matches) {
3615 $style = $matches[1];
3616 $style = preg_replace('/expression\s*\(/i', '', $style);
3617 $style = preg_replace('/(javascript|vbscript|data|about)\s*:/i', '', $style);
3618 $style = preg_replace('/url\s*\(\s*["\']?\s*(javascript|vbscript|data):/i', '', $style);
3619 $style = preg_replace('/behavior\s*:/i', '', $style);
3620 $style = preg_replace('/-moz-binding\s*:/i', '', $style);
3621 return 'style="' . esc_attr($style) . '"';
3622 }, $content);
3623
3624 // Sanitize <style> blocks
3625 $content = preg_replace_callback('/<style\b[^>]*>(.*?)<\/style>/si', function($matches) {
3626 $css = $matches[1];
3627 $css = preg_replace('/(expression|javascript|vbscript|data|about)\s*:/i', '', $css);
3628 $css = preg_replace('/url\s*\(\s*["\']?\s*(javascript|vbscript|data):/i', '', $css);
3629 $css = preg_replace('/behavior\s*:/i', '', $css);
3630 $css = preg_replace('/-moz-binding\s*:/i', '', $css);
3631 return '<style>' . esc_html($css) . '</style>';
3632 }, $content);
3633
3634 // Allow iframes and styles from safe sources only
3635 $allowed_tags = wp_kses_allowed_html('post');
3636 $allowed_tags['style'] = [
3637 'type' => true,
3638 'id' => true,
3639 'class' => true,
3640 'media' => true,
3641 ];
3642 $allowed_tags['iframe'] = [
3643 'src' => true,
3644 'width' => true,
3645 'height' => true,
3646 'frameborder' => true,
3647 'allowfullscreen' => true,
3648 'class' => true,
3649 'style' => true,
3650 'title' => true,
3651 'allow' => true,
3652 'name' => true,
3653 'referrerpolicy' => true,
3654 'loading' => true,
3655 'sandbox' => true,
3656 ];
3657 $allowed_tags['img'] = [
3658 'src' => true,
3659 'alt' => true,
3660 'title' => true,
3661 'width' => true,
3662 'height' => true,
3663 'class' => true,
3664 'id' => true,
3665 'style' => true,
3666 'loading' => true,
3667 'srcset' => true,
3668 'sizes' => true,
3669 ];
3670
3671 if ($allow_forms) {
3672 $form_attributes = [
3673 'id' => true,
3674 'class' => true,
3675 'style' => true,
3676 'name' => true,
3677 'value' => true,
3678 'type' => true,
3679 'placeholder' => true,
3680 'action' => true,
3681 'method' => true,
3682 'target' => true,
3683 'enctype' => true,
3684 'disabled' => true,
3685 'readonly' => true,
3686 'required' => true,
3687 'checked' => true,
3688 'selected' => true,
3689 'multiple' => true,
3690 'size' => true,
3691 'rows' => true,
3692 'cols' => true,
3693 'maxlength' => true,
3694 'minlength' => true,
3695 'min' => true,
3696 'max' => true,
3697 'step' => true,
3698 'pattern' => true,
3699 'autocomplete' => true,
3700 'autofocus' => true,
3701 'for' => true,
3702 'data-*' => true,
3703 'data-form_id' => true,
3704 'data-form_instance' => true,
3705 'data-name' => true,
3706 'data-type' => true,
3707 'aria-invalid' => true,
3708 'aria-required' => true,
3709 'aria-label' => true,
3710 'aria-describedby' => true,
3711 'aria-labelledby' => true,
3712 ];
3713 $allowed_tags['form'] = $form_attributes;
3714 $allowed_tags['input'] = $form_attributes;
3715 $allowed_tags['button'] = $form_attributes;
3716 $allowed_tags['textarea'] = $form_attributes;
3717 $allowed_tags['select'] = $form_attributes;
3718 $allowed_tags['option'] = $form_attributes;
3719 $allowed_tags['optgroup'] = $form_attributes;
3720 $allowed_tags['label'] = $form_attributes;
3721 $allowed_tags['fieldset'] = $form_attributes;
3722 $allowed_tags['legend'] = $form_attributes;
3723 if (!isset($allowed_tags['div'])) {
3724 $allowed_tags['div'] = [];
3725 }
3726 $allowed_tags['div']['data-*'] = true;
3727 $allowed_tags['div']['data-form_id'] = true;
3728 $allowed_tags['div']['data-form_instance'] = true;
3729 if (!isset($allowed_tags['span'])) {
3730 $allowed_tags['span'] = [];
3731 }
3732 $allowed_tags['span']['data-*'] = true;
3733 }
3734
3735 // Apply wp_kses() to keep only allowed tags/attributes
3736 $content = wp_kses($content, $allowed_tags);
3737
3738 // Finally, validate iframe src for security (allow https only, block javascript: etc.)
3739 $content = preg_replace_callback('/<iframe[^>]+src=["\']([^"\']+)["\'][^>]*>(?:<\/iframe>)?/i', function($matches) {
3740 $src = $matches[1];
3741 // Allow any https:// or http:// URL, but block javascript:, data:, vbscript:, etc.
3742 if (preg_match('/^(https?:)?\/\//i', $src) && !preg_match('/^(javascript|data|vbscript|about):/i', $src)) {
3743 return $matches[0]; // keep safe iframe
3744 }
3745 // Strip unsafe iframe
3746 return '';
3747 }, $content);
3748
3749 return $content;
3750 }
3751
3752 private function normalize_hotspot_external_url($url) {
3753 $url = trim((string) $url);
3754
3755 if (
3756 $url === '' ||
3757 preg_match('/^[a-z][a-z0-9+.-]*:/i', $url) ||
3758 preg_match('/^[\/#?]/', $url)
3759 ) {
3760 return $url;
3761 }
3762
3763 if (preg_match('/^[^\s\/]+\.[^\s\/]+(?:[\/?#]|$)/i', $url)) {
3764 return 'https://' . $url;
3765 }
3766
3767 return $url;
3768 }
3769
3770 }
3771