PluginProbe
WP VR – 360 Panorama and Virtual Tour Builder / 8.5.78
WP VR – 360 Panorama and Virtual Tour Builder v8.5.78
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 8.5.4 8.5.40 All 220 releases
wpvr / wpvr.php

wpvr.php in WP VR – 360 Panorama and Virtual Tour Builder 8.5.78, at wpvr.php

4,417 lines 209.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * The plugin bootstrap file
5 *
6 * This file is read by WordPress to generate the plugin information in the plugin
7 * admin area. This file also includes all of the dependencies used by the plugin,
8 * registers the activation and deactivation functions, and defines a function
9 * that starts the plugin.
10 *
11 * @link http://rextheme.com/
12 * @since 7.3.6
13 * @package Wpvr
14 *
15 * @wordpress-plugin
16 * Plugin Name: WP VR - 360 Panorama and Virtual Tour Builder
17 * Plugin URI: https://rextheme.com/wpvr/
18 * Description: WP VR - 360 Panorama and virtual tour creator is a customized panaroma & virtual builder tool for your website.
19 * Version: 8.5.78
20 * Tested up to: 7.0
21 * Author: Rextheme
22 * Author URI: http://rextheme.com/
23 * License: GPL-2.0+
24 * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
25 * Text Domain: wpvr
26 * Domain Path: /languages
27 */
28
29 // If this file is called directly, abort.
30 if (!defined('WPINC')) {
31 die;
32 }
33
34 require plugin_dir_path(__FILE__) . 'elementor/elementor.php';
35 require_once __DIR__ . '/vendor/autoload.php';
36
37 // if ( wp_get_theme('bricks')->exists() && 'bricks' === get_template()) {
38 require_once plugin_dir_path(__FILE__) . 'bricks/bricks.php';
39 // }
40
41 /**
42 * Currently plugin version.
43 * Start at version 1.0.0 and use SemVer - https://semver.org
44 * Rename this for your plugin and update it as you release new versions.
45 */
46 define('WPVR_VERSION', '8.5.78');
47 define('WPVR_FILE', __FILE__);
48 define("WPVR_PLUGIN_DIR_URL", plugin_dir_url(__FILE__));
49 define("WPVR_PLUGIN_DIR_PATH", plugin_dir_path(__FILE__));
50 define("WPVR_PLUGIN_PUBLIC_DIR_URL", plugin_dir_url(__FILE__) . 'public/');
51 define('WPVR_BASE', plugin_basename(WPVR_FILE));
52 define('WPVR_DEV_MODE', false);
53 define('WPVR_JS_PATH', plugin_dir_url(__FILE__) . 'admin/js/');
54 define('WPVR_CSS_PATH', plugin_dir_url(__FILE__) . 'admin/css/');
55 define('WPVR_ASSET_PATH', plugin_dir_url(__FILE__) . 'admin/');
56 define( 'WPVR_WEBHOOK_URL', sanitize_url( 'https://rextheme.com/?mailmint=1&route=webhook&topic=contact&hash=bbd19901-6d42-4ae5-a7a8-01eb1013c553' ) );
57 define('WPVR_TELEMETRY_API_KEY', 'phc_amk3VQz1P5ZqZOMRRoWM1B41QMFpf3hu8pg7yRfzSXW');
58 define('WPVR_TELEMETRY_API_SECRET', 'sec_dfb2ab8e84391ae7a0f9');
59 define('WPVR_TELEMETRY_HOST', 'https://eu.i.posthog.com');
60
61 /**
62 * The code that runs during plugin activation.
63 * This action is documented in includes/class-wpvr-activator.php
64 */
65 function activate_wpvr()
66 {
67 require_once plugin_dir_path(__FILE__) . 'includes/class-wpvr-activator.php';
68 Wpvr_Activator::activate();
69
70 // Trigger plugin activation tracking
71 do_action( 'wpvr_plugin_activated' );
72 }
73
74 /**
75 * The code that runs during plugin deactivation.
76 * This action is documented in includes/class-wpvr-deactivator.php
77 */
78 function deactivate_wpvr()
79 {
80 require_once plugin_dir_path(__FILE__) . 'includes/class-wpvr-deactivator.php';
81 Wpvr_Deactivator::deactivate();
82
83 // Trigger plugin deactivation tracking
84 do_action( 'wpvr_plugin_deactivated' );
85 }
86
87 register_activation_hook(__FILE__, 'activate_wpvr');
88 register_deactivation_hook(__FILE__, 'deactivate_wpvr');
89
90 /**
91 * The core plugin class that is used to define internationalization,
92 * admin-specific hooks, and public-facing site hooks.
93 */
94 require plugin_dir_path(__FILE__) . 'includes/class-wpvr.php';
95
96 // Include banner classes
97 require_once plugin_dir_path(__FILE__) . 'admin/classes/class-wpvr-occasion-banner.php';
98 require_once plugin_dir_path(__FILE__) . 'admin/classes/class-wpvr-sells-notification-bar.php';
99 require_once plugin_dir_path(__FILE__) . 'admin/classes/class-wpvr-first-tour-banner.php';
100 require_once plugin_dir_path(__FILE__) . 'admin/classes/class-wpvr-onboarding-notice.php';
101 require_once plugin_dir_path(__FILE__) . 'admin/classes/class-wpvr-new-user-tour.php';
102
103 // Include telemetry class
104 require_once plugin_dir_path(__FILE__) . 'includes/class-wpvr-linno-telemetry.php';
105 if ( defined( 'WPB_VC_VERSION' ) ) {
106 require_once plugin_dir_path( __FILE__ ) . 'builders/wpbakery/wpvr-loader.php';
107 if ( class_exists( 'Vc_Manager' ) ) {
108 require_once plugin_dir_path( __FILE__ ) . 'builders/wpbakery/wpvr-element.php';
109 }
110 }
111
112
113 /**
114 * Begins execution of the plugin.
115 *
116 * Since everything within the plugin is registered via hooks,
117 * then kicking off the plugin from this point in the file does
118 * not affect the page life cycle.
119 *
120 * @since 7.3.6
121 */
122 function run_wpvr()
123 {
124
125 $plugin = new Wpvr();
126 $plugin->run();
127 // new Tracker();
128
129 // black friday banner class initialization
130 // new WPVR_Special_Occasion_Banner(
131 // 'halloween_deal_2025',
132 // '2025-10-01 00:00:01',
133 // '2025-11-05 23:59:59'
134 // );
135
136 if (!defined('WPVR_PRO_VERSION') && 'no' === get_option('wpvr_sell_eid_ul_fitr_2026_notification_bar', 'no')) {
137 new WPVR_Notification_Bar(
138 'Eid_Ul_Fitr_2026',
139 '2026-03-16 00:00:00',
140 '2026-03-24 23:59:59'
141 );
142 }
143
144 // Initialize first tour banner
145 new WPVR_First_Tour_Banner();
146
147 // Initialize listing-page onboarding notice ("Remind me later" flow)
148 new WPVR_Onboarding_Notice();
149
150 // Auto-launch guided tour for truly-new users on the Add New Tour page.
151 new WPVR_New_User_Tour();
152
153 // Pro preview banner (shown via JS when free user toggles Advanced Controls)
154 if (!defined('WPVR_PRO_VERSION')) {
155 add_action('admin_notices', 'wpvr_render_pro_preview_banner', 11);
156 }
157
158 }
159 run_wpvr();
160
161 /**
162 * Render Pro preview banner as admin notice.
163 * Hidden by default; shown via JS when free user toggles Advanced Controls.
164 *
165 * @since 8.5.44
166 */
167 function wpvr_render_pro_preview_banner() {
168 $screen = get_current_screen();
169 if (!$screen || $screen->id !== 'wpvr_item') {
170 return;
171 }
172 ?>
173 <div class="wpvr-pro-preview-banner" id="wpvr-pro-preview-banner" style="display:none;">
174 <div class="wpvr-pro-preview-banner__left">
175 <span class="wpvr-pro-preview-banner__icon">
176 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zm0 12.5c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>
177 </span>
178 <span class="wpvr-pro-preview-banner__text"><?php esc_html_e('You are previewing Pro features in action.', 'wpvr'); ?></span>
179 </div>
180 <div class="wpvr-pro-preview-banner__pricing">
181 <div class="wpvr-pro-preview-banner__pricing-top">
182 <span class="wpvr-pro-preview-banner__old-price"><?php esc_html_e('Normally $99.99/year', 'wpvr'); ?></span>
183 <span class="wpvr-pro-preview-banner__badge"><?php esc_html_e('SAVE 20%', 'wpvr'); ?></span>
184 </div>
185 <span class="wpvr-pro-preview-banner__new-price"><?php esc_html_e('Starting at $79.99/year', 'wpvr'); ?></span>
186 </div>
187 <a href="<?php echo esc_url('https://rextheme.com/wpvr/wpvr-pricing/?utm_source=plugin&utm_medium=pro-preview-banner&utm_campaign=advanced-controls'); ?>" class="wpvr-pro-preview-banner__btn" target="_blank" rel="noopener noreferrer">
188 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M5 16L3 5l5.5 5L12 4l3.5 6L21 5l-2 11H5zm0 2h14v1c0 .55-.45 1-1 1H6c-.55 0-1-.45-1-1v-1z"/></svg>
189 <?php esc_html_e('Upgrade to save changes', 'wpvr'); ?>
190 </a>
191 </div>
192 <?php
193 }
194
195
196 /**
197 * Array information checker
198 *
199 * @param mixed $needle
200 * @param mixed $haystack
201 * @param bool $strict
202 *
203 * @return bool
204 * @since 7.3.6
205 */
206 function wpvr_in_array_r($needle, $haystack, $strict = false)
207 {
208 foreach ($haystack as $item) {
209 if ((($strict ? $item === $needle : $item == $needle)) || is_array($item) && wpvr_in_array_r($needle, $item, $strict)) {
210 return true;
211 }
212 }
213 return false;
214 }
215
216 if ( ! function_exists( 'wpvr_render_explainer_button' ) ) {
217 /**
218 * Render the WPVR explainer button independently.
219 *
220 * @param array|null $custom_control
221 * @param array $postdata
222 * @param bool $is_pro
223 * @param bool $autoload
224 * @param string $explainer_right
225 * @param int|string $id
226 *
227 * @return string
228 * @since 8.5.63
229 */
230 function wpvr_render_explainer_button( $custom_control, $postdata, $is_pro, $autoload, $explainer_right, $id ) {
231 $html = '';
232 $explainer_enabled = false;
233 $explainer_icon = 'fa fa-video';
234 $explainer_color = '#f7fffb';
235
236 if ( isset( $custom_control ) ) {
237 $explainer_enabled = ( isset( $custom_control['explainerSwitch'] ) && $custom_control['explainerSwitch'] === 'on' );
238 $explainer_icon = isset( $custom_control['explainerIcon'] ) ? $custom_control['explainerIcon'] : $explainer_icon;
239 $explainer_color = isset( $custom_control['explainerColor'] ) ? $custom_control['explainerColor'] : $explainer_color;
240 } elseif ( isset( $postdata['customcontrol'] ) ) {
241 $raw_control = $postdata['customcontrol'];
242 $explainer_enabled = ( isset( $raw_control['explainerSwitch'] ) && $raw_control['explainerSwitch'] === 'on' );
243 $explainer_icon = isset( $raw_control['explainerIcon'] ) ? $raw_control['explainerIcon'] : $explainer_icon;
244 $explainer_color = isset( $raw_control['explainerColor'] ) ? $raw_control['explainerColor'] : $explainer_color;
245 }
246
247 $explainer_feature_enabled = isset( $postdata['explainerSwitch'] )
248 ? 'on' === $postdata['explainerSwitch']
249 : ! empty( $postdata['explainerContent'] );
250
251 $pro_license_status = get_option( 'wpvr_edd_license_status' );
252 if ( $explainer_enabled && $explainer_feature_enabled && $pro_license_status === 'valid' && $is_pro ) {
253 $explainer_style = empty( $postdata['explainerContent'] )
254 ? 'pointer-events: none; opacity: 0.5;'
255 : '';
256
257 // Initial display:none to prevent flash for non-autoload tours.
258 $initial_display = ! $autoload ? 'display:none; ' : '';
259
260 $html .= '<div class="explainer_button" id="explainer_button_' . esc_attr( $id ) . '" style="' . $initial_display . 'right:' . esc_attr( $explainer_right ) . '; ' . esc_attr( $explainer_style ) . '">';
261 $html .= '<div class="ctrl" id="explainer_target_' . esc_attr( $id ) . '"><i class="' . esc_attr( $explainer_icon ) . '" style="color:' . esc_attr( $explainer_color ) . ';"></i></div>';
262 $html .= '</div>';
263 }
264
265 return $html;
266 }
267 }
268
269 // Linno telemetry integration
270 function wpvr_block()
271 {
272 wp_register_script(
273 'wpvr-block',
274 plugins_url('build/index.build.js', __FILE__),
275 array('wp-blocks', 'wp-element', 'wp-components', 'wp-editor')
276 );
277
278 if (is_admin()) {
279 wp_enqueue_style(
280 'gutyblocks/guty-block',
281 plugins_url('src/view.css', __FILE__),
282 array()
283 );
284 }
285
286
287 if (function_exists('register_block_type')) {
288 register_block_type('wpvr/wpvr-block', array(
289 'attributes' => array(
290 'id' => array(
291 'type' => 'string',
292 'default' => '0',
293 ),
294 'width' => array(
295 'type' => 'string',
296 'default' => '600',
297 ),
298 'width_unit' => array(
299 'type' => 'string',
300 'default' => 'px',
301 ),
302 'height' => array(
303 'type' => 'string',
304 'default' => '400',
305 ),
306 'height_unit' => array(
307 'type' => 'string',
308 'default' => 'px',
309 ),
310 'mobile_height' => array(
311 'type' => 'string',
312 'default' => '300',
313 ),
314 'mobile_height_unit' => array(
315 'type' => 'string',
316 'default' => 'px',
317 ),
318 'radius' => array(
319 'type' => 'string',
320 'default' => '0',
321 ),
322 'border_width' => array(
323 'type' => 'string',
324 'default' => '0px',
325 ),
326 'border_style' => array(
327 'type' => 'string',
328 'default' => 'none',
329 ),
330 'border_color' => array(
331 'type' => 'string',
332 'default' => 'none',
333 ),
334 'radius_unit' => array(
335 'type' => 'string',
336 'default' => 'px',
337 ),
338 'content' => array(
339 'type' => 'string',
340 'source' => 'html',
341 'default' => '<script> </script>'
342 ),
343 ),
344 'editor_script' => 'wpvr-block',
345 'render_callback' => 'wpvr_block_render',
346 ));
347 }
348 }
349
350 add_action('init', 'wpvr_block');
351
352 /**
353 * Enqueue frontend scripts for a given tour type from a render callback.
354 * Called during the_content — footer scripts still fire after, so $in_footer=true works.
355 * CSS is loaded globally by enqueue_styles(); only JS is gated here.
356 *
357 * @param string $tour_type 'scene' | 'video' | 'streetview'
358 */
359 function wpvr_enqueue_frontend_scripts( $tour_type ) {
360 static $localized = false;
361
362 $pub = WPVR_PLUGIN_PUBLIC_DIR_URL;
363 $ver = WPVR_VERSION;
364 $pannellum_version = filemtime( WPVR_PLUGIN_DIR_PATH . 'public/lib/pannellum/src/js/pannellum.js' );
365
366 if ( 'video' === $tour_type ) {
367 wp_enqueue_script( 'videojs-js', $pub . 'js/video.js', array(), $ver, true );
368 wp_enqueue_script( 'videojsvr-js', $pub . 'lib/videojs-vr/videojs-vr.js', array( 'videojs-js' ), $ver, true );
369 wp_enqueue_script( 'panellium-js', $pub . 'lib/pannellum/src/js/pannellum.js', array(), $pannellum_version, true );
370 wp_enqueue_script( 'panelliumlib-js', $pub . 'lib/pannellum/src/js/libpannellum.js', array( 'panellium-js' ), $ver, true );
371 wp_enqueue_script( 'panelliumvid-js', $pub . 'lib/pannellum/src/js/videojs-pannellum-plugin.js', array( 'videojs-js', 'videojsvr-js', 'panellium-js', 'panelliumlib-js' ), $ver, true );
372 } elseif ( 'scene' === $tour_type ) {
373 wp_enqueue_script( 'panellium-js', $pub . 'lib/pannellum/src/js/pannellum.js', array(), $pannellum_version, true );
374 wp_enqueue_script( 'panelliumlib-js', $pub . 'lib/pannellum/src/js/libpannellum.js', array( 'panellium-js' ), $ver, true );
375 wp_enqueue_script( 'owl-js', $pub . 'js/owl.carousel.js', array( 'jquery' ), $ver, true );
376 }
377 // 'streetview' is an iframe only — no extra scripts needed
378
379 wp_enqueue_script( 'jquery_cookie', $pub . 'js/jquery.cookie.js', array( 'jquery' ), $ver, true );
380 wp_enqueue_script( 'wpvr', $pub . 'js/wpvr-public.js', array( 'jquery', 'jquery_cookie' ), $ver, true );
381
382 if ( ! $localized ) {
383 $localized = true;
384 $wpvr_frontend_notice = get_option( 'wpvr_frontend_notice' );
385 wp_localize_script( 'wpvr', 'wpvr_public', array(
386 'notice_active' => $wpvr_frontend_notice,
387 'notice' => $wpvr_frontend_notice ? sanitize_text_field( get_option( 'wpvr_frontend_notice_area' ) ) : '',
388 'is_pro_active' => is_plugin_active( 'wpvr-pro/wpvr-pro.php' ),
389 'is_license_active' => get_option( 'wpvr_edd_license_status' ) === 'valid',
390 'dis_on_hover' => get_option( 'dis_on_hover' ) === 'true',
391 'mobile_hotspot_tip' => get_option( 'wpvr_mobile_hotspot_tip' ) === 'true',
392 ) );
393 }
394
395 do_action( 'wpvr_enqueue_frontend_scripts', $tour_type );
396 }
397
398 function wpvr_block_render($attributes)
399 {
400 if (isset($attributes['id'])) {
401 $id = $attributes['id'];
402 } else {
403 $id = 0;
404 }
405
406 do_action('rex_wpvr_embadded_tour', $id);
407
408 $memberpress_active = defined('MEPR_VERSION');
409 $rcp_active = is_plugin_active( 'restrict-content-pro/restrict-content-pro.php' );
410
411 if (($memberpress_active || $rcp_active ) && apply_filters('is_wpvr_pro_active', false)) {
412
413 if (!is_user_logged_in()) {
414 return esc_html__('You need to log in to access this content.', 'wpvr');
415 }
416
417 $user_id = get_current_user_id();
418 $allowed_access = false;
419
420 // Get saved membership levels from post meta
421 $allowed_membership_levels = get_post_meta($id, '_wpvr_allowed_roles_levels', true);
422 if ( isset($allowed_membership_levels) && 'none' !== $allowed_membership_levels ) {
423 if (!is_array($allowed_membership_levels)) {
424 $allowed_membership_levels = array($allowed_membership_levels);
425 }
426
427 // Check MemberPress Access
428 if ($memberpress_active) {
429 $user = new MeprUser($user_id);
430 $active_memberships = $user->active_product_subscriptions();
431 if (array_intersect($allowed_membership_levels, $active_memberships) ) {
432 $allowed_access = true;
433 }
434 }
435
436 // Check Restrict Content Pro Access
437 if ($rcp_active) {
438 $user_rcp_memberships = rcp_get_customer_memberships($user_id);
439 foreach ($user_rcp_memberships as $membership) {
440 $rcp_access_level = array(
441 $membership->get_object_id()
442 );
443 if (array_intersect($allowed_membership_levels, $rcp_access_level) ) {
444 $allowed_access = true;
445 break;
446 }
447 }
448 }
449 } else {
450 // If no membership restriction is set, allow access by default
451 $allowed_access = true;
452 }
453
454 if (!$allowed_access) {
455 return esc_html__('You do not have access to this content.', 'wpvr');
456 }
457 }
458
459
460 if (isset($attributes['width'])) {
461 $width = $attributes['width'];
462 }
463 if (isset($attributes['width_unit'])) {
464 $width_unit = $attributes['width_unit'];
465 }
466 if (isset($attributes['height'])) {
467 $height = $attributes['height'];
468 }
469 if (isset($attributes['height_unit'])) {
470 $height_unit = $attributes['height_unit'];
471 }
472 if (isset($attributes['mobile_height'])) {
473 $mobile_height = $attributes['mobile_height'];
474 }
475 if (isset($attributes['mobile_height_unit'])) {
476 $mobile_height_unit = $attributes['mobile_height_unit'];
477 }
478 if (isset($attributes['radius']) && isset($attributes['radius_unit'])) {
479 $radius = $attributes['radius'] . $attributes['radius_unit'];
480 }
481 $border_style = '';
482 if (isset($attributes['border_width'], $attributes['border_style'], $attributes['border_color'])) {
483 $border_style = $attributes['border_width'] . 'px ' . $attributes['border_style'] . ' ' . $attributes['border_color'];
484 }
485
486 if (isset($attributes['className'])) {
487 $className = $attributes['className'];
488 } else {
489 $className = '';
490 }
491 $get_post = get_post_status($id);
492 if ($get_post !== 'publish') {
493 return esc_html__('Oops! It seems like this post isn\'t published yet. Stay tuned for updates!', 'wpvr');
494 }
495 if (post_password_required($id)) {
496 return get_the_password_form();
497 }
498 $postdata = get_post_meta($id, 'panodata', true);
499 $panoid = 'pano' . $id;
500 $panoid2 = 'pano2' . $id;
501 if (!isset($postdata) || empty($postdata)) {
502 return wp_kses(
503 __('Oops! It seems that you have not selected any tour yet. Please select a tour from the dropdown of WPVR block.<br>', 'wpvr'),
504 ['br' => []]
505 );
506 }
507 if (isset($postdata['streetviewdata'])) {
508 if (empty($width)) {
509 $width = '600px';
510 }
511 if (empty($height)) {
512 $height = '400px';
513 }
514 $streetviewurl = $postdata['streetviewurl'];
515 $html = '';
516 $html .= '<div class="vr-streetview ' . esc_attr( $className ) . '" style="text-align: center; max-width:100%; width:' . esc_attr( $width ) . esc_attr( $width_unit ) . '; height:' . esc_attr( $height ) . esc_attr( $height_unit ) . '; margin: 0 auto;">';
517 $html .= '<iframe src="' . esc_url( $streetviewurl ) . '" frameborder="0" style="border:0; width:100px; height:100%;" allowfullscreen=""></iframe>';
518 $html .= '</div>';
519
520 wpvr_enqueue_frontend_scripts( 'streetview' );
521 return $html;
522 }
523 $is_pro = apply_filters('is_wpvr_pro_active', false);
524
525 if (isset($postdata['vidid'])) {
526 if (empty($width)) {
527 $width = '600';
528 }
529 if (empty($height)) {
530 $height = '400';
531 }
532 $videourl = $postdata['vidurl'];
533
534 $videourl = $postdata['vidurl'];
535 $autoplay = 'off';
536 if (isset($postdata['autoplay'])) {
537 $autoplay = $postdata['autoplay'];
538 }
539 $loop = 'off';
540 if (isset($postdata['loop'])) {
541 $loop = $postdata['loop'];
542 }
543
544 if (strpos($videourl, 'youtube') > 0 || strpos($videourl, 'youtu') > 0) {
545 $explodeid = '';
546 $explodeid = explode("=", $videourl);
547 $foundid = '';
548 $muted = '&mute=1';
549
550 if ($autoplay == 'on') {
551 $autoplay = '&autoplay=1';
552 } else {
553 $autoplay = '';
554 }
555
556 if ($loop == 'on') {
557 $loop = '&loop=1';
558 } else {
559 $loop = '';
560 }
561
562 if (strpos($videourl, 'youtu') > 0) {
563 $explodeid = explode("/", $videourl);
564 $foundid = $explodeid[3] . '?' . $autoplay . $loop;
565 $expdata = $explodeid[3];
566 } else {
567 $foundid = $explodeid[1] . '?' . $autoplay . $loop;
568 $expdata = $explodeid[1];
569 }
570
571 $playlist = '&playlist=' . $expdata;
572 $playlist = str_replace("?feature=shared", "", $playlist);
573 $origin = '&origin=' . rawurlencode( home_url() );
574
575
576 $html = '';
577 $html .= '<div class="' . esc_attr($className) . '" style="text-align:center; max-width:100%; width:' . esc_attr($width) . esc_attr($width_unit) . '; height:' . esc_attr($height) . esc_attr($height_unit) . '; border-radius: ' . esc_attr($radius) . '; margin: 0 auto;">';
578
579 // Compatibility check script
580 $html .= '<script>
581 function wpvr_check_360_support() {
582 var support = {
583 supported: false,
584 fullySupported: false,
585 webgl: false,
586 orientation: false,
587 gyroscope: false,
588 touch: false,
589 browser: "unknown",
590 isMobile: false,
591 isIPhone: false,
592 isAndroid: false,
593 details: []
594 };
595
596 support.isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
597 support.isIPhone = /iPhone/i.test(navigator.userAgent);
598 support.isAndroid = /Android/i.test(navigator.userAgent);
599 if (support.isMobile) support.details.push("Mobile device detected");
600 if (support.isIPhone) support.details.push("iPhone detected");
601 if (support.isAndroid) support.details.push("Android device detected");
602
603 var ua = navigator.userAgent;
604 if (/^((?!chrome|android).)*safari/i.test(ua)) {
605 support.browser = "safari";
606 support.details.push("Safari browser detected");
607 } else if (ua.indexOf("Chrome") > -1) {
608 support.browser = "chrome";
609 support.details.push("Chrome browser detected");
610 } else if (ua.indexOf("Firefox") > -1) {
611 support.browser = "firefox";
612 support.details.push("Firefox browser detected");
613 } else if (ua.indexOf("MSIE") > -1 || ua.indexOf("Trident") > -1) {
614 support.browser = "ie";
615 support.details.push("Internet Explorer detected");
616 } else if (ua.indexOf("Edge") > -1 || ua.indexOf("Edg") > -1) {
617 support.browser = "edge";
618 support.details.push("Edge browser detected");
619 } else if (ua.indexOf("Opera") > -1 || ua.indexOf("OPR") > -1) {
620 support.browser = "opera";
621 support.details.push("Opera browser detected");
622 }
623
624 try {
625 var canvas = document.createElement("canvas");
626 support.webgl = !!(window.WebGLRenderingContext &&
627 (canvas.getContext("webgl") || canvas.getContext("experimental-webgl")));
628 support.details.push(support.webgl ? "WebGL supported" : "WebGL not supported");
629 } catch (e) {
630 support.webgl = false;
631 support.details.push("WebGL detection error: " + e.message);
632 }
633
634 support.orientation = !!(window.DeviceOrientationEvent);
635 support.details.push(support.orientation ? "Device Orientation API supported" : "Device Orientation API not supported");
636
637 support.gyroscope = !!window.Gyroscope;
638 support.details.push(support.gyroscope ? "Gyroscope supported" : "Gyroscope not supported");
639
640 support.touch = "ontouchstart" in window || navigator.maxTouchPoints > 0;
641 support.details.push(support.touch ? "Touch supported" : "Touch not supported");
642
643 support.supported = support.webgl;
644 support.fullySupported = support.webgl && ((support.isMobile && support.orientation) || !support.isMobile);
645
646 if (support.browser === "ie") {
647 support.browserWarning = "Internet Explorer has limited support for 360 videos.";
648 support.fullySupported = false;
649 } else if (!support.supported) {
650 support.browserWarning = "Your browser does not support 360° videos. Use Chrome or Firefox with WebGL.";
651 }
652
653 return support;
654 }
655 </script>';
656
657 $random_id = 'video_container_' . wp_rand(10000, 99999);
658 $html .= '<div id="' . $random_id . '-container" style="position:relative; width:100%; height:100%;">';
659
660 // Compatibility check screen (hidden by default and non-interactive unless actively shown)
661 $html .= '<div id="' . $random_id . '-compatibility-check" style="position:absolute; top:0; left:0; width:100%; height:100%; display:none; pointer-events:none; flex-direction:column; justify-content:center; align-items:center; background-color:#f9f9f9; border-radius:' . esc_attr( $radius ) . ';">
662 <div style="margin-bottom:20px; text-align:center;">
663 <div class="wpvr-loading-spinner" style="border:5px solid #f3f3f3; border-top:5px solid #3498db; border-radius:50%; width:50px; height:50px; margin:0 auto 15px; animation:wpvr-spin 1s linear infinite;"></div>
664 <p style="margin:0;">Checking browser compatibility...</p>
665 </div>
666 </div>';
667
668 // Spinner animation
669 $html .= '<style>@keyframes wpvr-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }</style>';
670
671 // Iframe (initial display set to block)
672 $html .= '<div id="' . $random_id . '-frame" class="' . esc_attr($className) . '" style="display:block; max-width:100%; width:' . esc_attr($width) . esc_attr($width_unit) . '; height:' . esc_attr($height) . esc_attr($height_unit) . '; border-radius: ' . esc_attr($radius) . '; margin: 0 auto;">';
673 $html .= '<iframe id="' . $random_id . '-iframe" src="https://www.youtube.com/embed/' . rawurlencode(sanitize_text_field($expdata)) . '?rel=0&modestbranding=1' . esc_attr($loop) . '&autohide=1' . esc_attr($muted) . '&showinfo=0&controls=0' . esc_attr($autoplay) . esc_attr($playlist) . '&enablejsapi=1&playsinline=1&html5=1' . $origin . '" width="100%" height="100%" style="border-radius: ' . esc_attr($radius) . ';" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; xr-spatial-tracking"></iframe>';
674 $html .= '</div>';
675 $html .= '<div id="' . $random_id . '-drag-surface" style="position:absolute; top:0; left:0; right:0; bottom:48px; display:none; pointer-events:none; cursor:grab; z-index:3; background:transparent;"></div>';
676
677 // Custom control bar
678 $html .= '<div id="' . $random_id . '-controls" style="display:none; position:absolute; bottom:0; left:0; right:0; height:48px; z-index:5; pointer-events:auto; background:linear-gradient(transparent, rgba(0,0,0,0.7)); align-items:center; padding:0 8px; gap:6px; box-sizing:border-box;">';
679
680 // Play/Pause button
681 $html .= '<button type="button" id="' . $random_id . '-play-btn" style="width:36px; height:36px; border:none; background:transparent; cursor:pointer; padding:0; display:flex; align-items:center; justify-content:center; flex-shrink:0;" title="Play">';
682 $html .= '<svg id="' . $random_id . '-play-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="white"><path d="M8 5v14l11-7z"/></svg>';
683 $html .= '</button>';
684
685 // Time current
686 $html .= '<span id="' . $random_id . '-time-current" style="color:#fff; font-size:12px; font-family:Arial,sans-serif; min-width:36px; text-align:center; user-select:none;">0:00</span>';
687
688 // Progress bar
689 $html .= '<div id="' . $random_id . '-progress-wrap" style="flex:1; height:4px; background:rgba(255,255,255,0.3); border-radius:2px; cursor:pointer; position:relative;">';
690 $html .= '<div id="' . $random_id . '-progress-buffered" style="position:absolute; top:0; left:0; height:100%; background:rgba(255,255,255,0.4); border-radius:2px; pointer-events:none;"></div>';
691 $html .= '<div id="' . $random_id . '-progress-bar" style="position:absolute; top:0; left:0; height:100%; background:#ff0000; border-radius:2px; pointer-events:none;"></div>';
692 $html .= '<div id="' . $random_id . '-progress-thumb" style="position:absolute; top:50%; left:0; width:12px; height:12px; background:#ff0000; border-radius:50%; transform:translate(-50%,-50%); pointer-events:none; display:none;"></div>';
693 $html .= '</div>';
694
695 // Time duration
696 $html .= '<span id="' . $random_id . '-time-duration" style="color:#fff; font-size:12px; font-family:Arial,sans-serif; min-width:36px; text-align:center; user-select:none;">0:00</span>';
697
698 // Mute button
699 $html .= '<button id="' . $random_id . '-mute-btn" style="width:36px; height:36px; border:none; background:transparent; cursor:pointer; padding:0; display:flex; align-items:center; justify-content:center; flex-shrink:0; position:relative;" title="Mute">';
700 $html .= '<svg id="' . $random_id . '-vol-icon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="white"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3A4.5 4.5 0 0014 8.14v7.72c1.48-.73 2.5-2.25 2.5-3.86zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/></svg>';
701 $html .= '</button>';
702
703 // Volume slider (popup on mute hover)
704 $html .= '<style>#' . $random_id . '-vol-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:14px;height:14px;border-radius:50%;background:#fff;cursor:pointer;margin-top:-5px;}#' . $random_id . '-vol-slider::-moz-range-thumb{width:14px;height:14px;border-radius:50%;background:#fff;border:none;cursor:pointer;}#' . $random_id . '-vol-slider::-webkit-slider-runnable-track{height:4px;border-radius:4px;}#' . $random_id . '-vol-slider::-moz-range-track{height:4px;border-radius:4px;background:rgba(255,255,255,0.3);}</style>';
705 $html .= '<div id="' . $random_id . '-vol-slider-wrap" style="display:none; align-items:center; height:40px; padding:0px 4px;">';
706 $html .= '<input id="' . $random_id . '-vol-slider" type="range" min="0" max="100" value="100" style="width:80px; height:4px; cursor:pointer; -webkit-appearance:none; appearance:none; background:linear-gradient(to right,#fff 100%,rgba(255,255,255,0.3) 100%); border-radius:4px; outline:none; padding:0;" />';
707 $html .= '</div>';
708
709 // Fullscreen button
710 $html .= '<button id="' . $random_id . '-fs-btn" style="width:36px; height:36px; border:none; background:transparent; cursor:pointer; padding:0; display:flex; align-items:center; justify-content:center; flex-shrink:0;" title="Toggle fullscreen">';
711 $html .= '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="white"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>';
712 $html .= '</button>';
713
714 $html .= '</div>'; // Close controls
715
716 // Browser warning
717 $html .= '<div id="' . $random_id . '-browser-warning" style="position:absolute; top:0; left:0; width:100%; height:100%; display:none; pointer-events:none; flex-direction:column; justify-content:center; align-items:center; background-color:rgba(0,0,0,0.7); color:white; text-align:center; border-radius:' . esc_attr( $radius ) . ';">';
718 $html .= '<p id="' . $random_id . '-warning-text" style="font-size:16px; margin:0 20px 5px;"></p>';
719 $html .= '<p style="font-size:14px; margin:5px 20px 15px;">For the best experience, use a browser with WebGL support.</p>';
720 $html .= '<button id="' . $random_id . '-browser-continue" style="padding:10px 15px; background-color:#0085ba; color:#fff; border:none; border-radius:4px; cursor:pointer;">Continue Anyway</button>';
721 $html .= '</div>';
722
723 $html .= '<div id="' . $random_id . '-iphone-fallback" style="position:absolute; top:0; left:0; width:100%; height:100%; display:none; pointer-events:none; flex-direction:column; justify-content:center; align-items:center; background-color:rgba(0,0,0,0.75); color:white; text-align:center; border-radius:' . esc_attr( $radius ) . ';">';
724 $html .= '<p id="' . $random_id . '-mobile-fallback-title" style="font-size:16px; margin:0 20px 8px;">YouTube 360 is limited in mobile browser embeds.</p>';
725 $html .= '<p id="' . $random_id . '-mobile-fallback-description" style="font-size:14px; margin:0 20px 15px;">For proper 360 playback, open this video in the YouTube app.</p>';
726 $html .= '<a id="' . $random_id . '-iphone-open" href="https://www.youtube.com/watch?v=' . rawurlencode( sanitize_text_field( $expdata ) ) . '" target="_blank" rel="noopener noreferrer" style="color:rgba(255,255,255,0.85); text-decoration:underline; font-size:12px; margin-bottom:8px;">Open in YouTube</a>';
727 $html .= '<a id="' . $random_id . '-iphone-continue" href="#" style="color:rgba(255,255,255,0.75); text-decoration:underline; font-size:12px;">Show Here Anyway</a>';
728 $html .= '</div>';
729
730 // Main script — output via wp_footer to avoid wptexturize mangling && etc.
731 $wpvr_yt_script = '<script>
732 (function() {
733 function wpvrInitPlayer_' . $random_id . '() {
734
735 var playerInitialised = false;
736 var ytPlayer = null;
737 var dragSurface = document.getElementById("' . $random_id . '-drag-surface");
738 var isDragging = false;
739 var dragStartX = 0;
740 var dragStartY = 0;
741 var dragStartView = null;
742
743 function clamp(value, min, max) {
744 return Math.min(Math.max(value, min), max);
745 }
746
747 function getSphericalView() {
748 if (!ytPlayer || typeof ytPlayer.getSphericalProperties !== "function") {
749 return null;
750 }
751
752 var view = ytPlayer.getSphericalProperties();
753 return view && Object.keys(view).length ? view : null;
754 }
755
756 function setDragSurfaceEnabled(enabled) {
757 if (!dragSurface) {
758 return;
759 }
760
761 dragSurface.style.display = enabled ? "block" : "none";
762 dragSurface.style.pointerEvents = enabled ? "auto" : "none";
763 }
764
765 function getEventPoint(event) {
766 if (event.touches && event.touches.length) {
767 return event.touches[0];
768 }
769
770 if (event.changedTouches && event.changedTouches.length) {
771 return event.changedTouches[0];
772 }
773
774 return event;
775 }
776
777 function beginDrag(event) {
778 var point = getEventPoint(event);
779 var sphericalView = getSphericalView();
780
781 if (!sphericalView || !point) {
782 return;
783 }
784
785 event.preventDefault();
786 dragStartX = point.clientX;
787 dragStartY = point.clientY;
788 dragStartView = sphericalView;
789 dragSurface.style.cursor = "grabbing";
790 }
791
792 function updateDrag(event) {
793 var deltaX;
794 var deltaY;
795 var nextView;
796 var point = getEventPoint(event);
797
798 if (!point) {
799 return;
800 }
801
802 if (!isDragging && dragStartView) {
803 deltaX = point.clientX - dragStartX;
804 deltaY = point.clientY - dragStartY;
805 if (Math.sqrt(deltaX * deltaX + deltaY * deltaY) > 5) {
806 isDragging = true;
807 }
808 }
809
810 if (!isDragging || !dragStartView || !ytPlayer || typeof ytPlayer.setSphericalProperties !== "function") {
811 return;
812 }
813
814 event.preventDefault();
815 deltaX = point.clientX - dragStartX;
816 deltaY = point.clientY - dragStartY;
817
818 nextView = {
819 yaw: (dragStartView.yaw || 0) + (deltaX * 0.2),
820 pitch: clamp((dragStartView.pitch || 0) + (deltaY * 0.2), -85, 85),
821 roll: dragStartView.roll || 0,
822 fov: dragStartView.fov || 100
823 };
824
825 ytPlayer.setSphericalProperties(nextView);
826 }
827
828 function endDrag() {
829 if (!isDragging && dragStartView && ytPlayer) {
830 var playerState = ytPlayer.getPlayerState();
831 if (playerState === 1) {
832 ytPlayer.pauseVideo();
833 } else {
834 ytPlayer.playVideo();
835 }
836 }
837 isDragging = false;
838 dragStartView = null;
839 if (dragSurface) {
840 dragSurface.style.cursor = "grab";
841 }
842 }
843
844 function update360DragAvailability() {
845 var sphericalView = getSphericalView();
846 setDragSurfaceEnabled(!!sphericalView);
847 }
848
849 function schedule360AvailabilityChecks() {
850 update360DragAvailability();
851 window.setTimeout(update360DragAvailability, 500);
852 window.setTimeout(update360DragAvailability, 1500);
853 window.setTimeout(update360DragAvailability, 3000);
854 }
855
856 if (dragSurface) {
857 dragSurface.addEventListener("mousedown", function(event) {
858 beginDrag(event);
859 });
860
861 dragSurface.addEventListener("touchstart", function(event) {
862 beginDrag(event);
863 }, { passive: false });
864
865 dragSurface.addEventListener("dragstart", function(event) {
866 event.preventDefault();
867 });
868
869 window.addEventListener("mousemove", function(event) {
870 updateDrag(event);
871 });
872
873 window.addEventListener("touchmove", function(event) {
874 updateDrag(event);
875 }, { passive: false });
876
877 window.addEventListener("mouseup", function() {
878 endDrag();
879 });
880
881 window.addEventListener("touchend", function() {
882 endDrag();
883 });
884
885 window.addEventListener("touchcancel", function() {
886 endDrag();
887 });
888 }
889
890 function wpvrLoadYouTubeApi(callback) {
891 window.wpvrYoutubeApiCallbacks = window.wpvrYoutubeApiCallbacks || [];
892
893 if (window.YT && window.YT.Player) {
894 callback();
895 return;
896 }
897
898 window.wpvrYoutubeApiCallbacks.push(callback);
899
900 if (window.wpvrYoutubeApiLoading) {
901 return;
902 }
903
904 window.wpvrYoutubeApiLoading = true;
905
906 var previousReady = window.onYouTubeIframeAPIReady;
907 window.onYouTubeIframeAPIReady = function() {
908 if (typeof previousReady === "function") {
909 previousReady();
910 }
911
912 var callbacks = window.wpvrYoutubeApiCallbacks || [];
913 while (callbacks.length) {
914 var queuedCallback = callbacks.shift();
915 if (typeof queuedCallback === "function") {
916 queuedCallback();
917 }
918 }
919 };
920
921 var tag = document.createElement("script");
922 tag.src = "https://www.youtube.com/iframe_api";
923 var firstScriptTag = document.getElementsByTagName("script")[0];
924 firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
925 }
926
927 function initializeYouTubePlayer() {
928 if (playerInitialised) {
929 return;
930 }
931
932 wpvrLoadYouTubeApi(function() {
933 if (playerInitialised || !(window.YT && window.YT.Player)) {
934 return;
935 }
936
937 ytPlayer = new YT.Player("' . esc_js( $random_id . '-iframe' ) . '", {
938 events: {
939 onReady: function() {
940 schedule360AvailabilityChecks();
941 startProgressLoop();
942 scheduleIPhoneFallbackCheck();
943 },
944 onStateChange: function() {
945 schedule360AvailabilityChecks();
946 startProgressLoop();
947 scheduleIPhoneFallbackCheck();
948 }
949 }
950 });
951 playerInitialised = true;
952 });
953 }
954
955 var supportInfo = wpvr_check_360_support();
956 var compatibilityCheck = document.getElementById("' . $random_id . '-compatibility-check");
957 var frameContainer = document.getElementById("' . $random_id . '-frame");
958 var warning = document.getElementById("' . $random_id . '-browser-warning");
959 var warningText = document.getElementById("' . $random_id . '-warning-text");
960 var continueBtn = document.getElementById("' . $random_id . '-browser-continue");
961 var iphoneFallback = document.getElementById("' . $random_id . '-iphone-fallback");
962 var iphoneContinue = document.getElementById("' . $random_id . '-iphone-continue");
963 var mobileFallbackTitle = document.getElementById("' . $random_id . '-mobile-fallback-title");
964 var mobileFallbackDescription = document.getElementById("' . $random_id . '-mobile-fallback-description");
965
966 function updateMobileFallbackContent() {
967 if (!mobileFallbackTitle || !mobileFallbackDescription) {
968 return;
969 }
970
971 if (supportInfo.isIPhone) {
972 mobileFallbackTitle.textContent = "YouTube 360 is limited in iPhone Safari embeds.";
973 mobileFallbackDescription.textContent = "For proper 360 playback, open this video in the YouTube app.";
974 } else if (supportInfo.isAndroid) {
975 mobileFallbackTitle.textContent = "YouTube 360 is limited in Android browser embeds.";
976 mobileFallbackDescription.textContent = "For reliable 360 playback, open this video in the YouTube app.";
977 } else {
978 mobileFallbackTitle.textContent = "YouTube 360 is limited in mobile browser embeds.";
979 mobileFallbackDescription.textContent = "For proper 360 playback, open this video in the YouTube app.";
980 }
981 }
982
983 function hideIPhoneFallback() {
984 if (!iphoneFallback) {
985 return;
986 }
987
988 iphoneFallback.style.display = "none";
989 iphoneFallback.style.pointerEvents = "none";
990 }
991
992 function showIPhoneFallback() {
993 if (!iphoneFallback) {
994 return;
995 }
996
997 frameContainer.style.display = "none";
998 iphoneFallback.style.display = "flex";
999 iphoneFallback.style.pointerEvents = "auto";
1000 }
1001
1002 function scheduleIPhoneFallbackCheck() {
1003 if (!supportInfo.isIPhone && !supportInfo.isAndroid) {
1004 return;
1005 }
1006
1007 updateMobileFallbackContent();
1008
1009 window.setTimeout(function() {
1010 if (!getSphericalView()) {
1011 showIPhoneFallback();
1012 }
1013 }, 1800);
1014 }
1015
1016 if (iphoneContinue) {
1017 iphoneContinue.addEventListener("click", function(event) {
1018 event.preventDefault();
1019 hideIPhoneFallback();
1020 showVideo();
1021 });
1022 }
1023
1024 setTimeout(function() {
1025 compatibilityCheck.style.display = "none";
1026 compatibilityCheck.style.pointerEvents = "none";
1027
1028 if (!supportInfo.supported || supportInfo.browserWarning) {
1029 frameContainer.style.display = "none";
1030 warningText.textContent = supportInfo.browserWarning || "Your browser may not fully support 360° videos.";
1031 warning.style.display = "flex";
1032 warning.style.pointerEvents = "auto";
1033
1034 continueBtn.addEventListener("click", function() {
1035 warning.style.display = "none";
1036 warning.style.pointerEvents = "none";
1037 showVideo();
1038 });
1039 } else {
1040 initializeYouTubePlayer();
1041 }
1042 }, 1000);
1043
1044 function showVideo() {
1045 hideIPhoneFallback();
1046 frameContainer.style.display = "block";
1047 initializeYouTubePlayer();
1048 }
1049
1050 // === Custom control bar logic ===
1051 var outerContainer = document.getElementById("' . $random_id . '-container");
1052 var controlBar = document.getElementById("' . $random_id . '-controls");
1053 var playBtn = document.getElementById("' . $random_id . '-play-btn");
1054 var playIcon = document.getElementById("' . $random_id . '-play-icon");
1055 var timeCurrent = document.getElementById("' . $random_id . '-time-current");
1056 var timeDuration = document.getElementById("' . $random_id . '-time-duration");
1057 var progressWrap = document.getElementById("' . $random_id . '-progress-wrap");
1058 var progressBar = document.getElementById("' . $random_id . '-progress-bar");
1059 var progressBuffered = document.getElementById("' . $random_id . '-progress-buffered");
1060 var progressThumb = document.getElementById("' . $random_id . '-progress-thumb");
1061 var fsBtn = document.getElementById("' . $random_id . '-fs-btn");
1062 var muteBtn = document.getElementById("' . $random_id . '-mute-btn");
1063 var volSlider = document.getElementById("' . $random_id . '-vol-slider");
1064 var volSliderWrap = document.getElementById("' . $random_id . '-vol-slider-wrap");
1065 var volIcon = document.getElementById("' . $random_id . '-vol-icon");
1066
1067 var playPath = "M8 5v14l11-7z";
1068 var pausePath = "M6 19h4V5H6v14zm8-14v14h4V5h-4z";
1069 var volIconMuted = "M16.5 12A4.5 4.5 0 0014 8.14v2.07l2.45 2.45c.03-.21.05-.43.05-.66zm2.5 0c0 .93-.21 1.82-.58 2.61l1.47 1.47A8.94 8.94 0 0021 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06a8.99 8.99 0 003.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z";
1070 var volIconLow = "M7 9v6h4l5 5V4L7 9z";
1071 var volIconMed = "M3 9v6h4l5 5V4L7 9H3zm13.5 3A4.5 4.5 0 0014 8.14v7.72c1.48-.73 2.5-2.25 2.5-3.86z";
1072 var volIconHigh = "M3 9v6h4l5 5V4L7 9H3zm13.5 3A4.5 4.5 0 0014 8.14v7.72c1.48-.73 2.5-2.25 2.5-3.86zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z";
1073
1074 function formatTime(sec) {
1075 if (!sec || isNaN(sec)) return "0:00";
1076 sec = Math.floor(sec);
1077 var m = Math.floor(sec / 60);
1078 var s = sec % 60;
1079 return m + ":" + (s < 10 ? "0" : "") + s;
1080 }
1081
1082 // Show control bar once player exists
1083 if (controlBar) controlBar.style.display = "flex";
1084
1085 // --- Play / Pause ---
1086 if (playBtn) {
1087 playBtn.addEventListener("click", function() {
1088 if (!ytPlayer) return;
1089 var state = ytPlayer.getPlayerState();
1090 if (state === 1) { // playing
1091 ytPlayer.pauseVideo();
1092 } else {
1093 ytPlayer.playVideo();
1094 }
1095 });
1096 }
1097
1098 function updatePlayIcon() {
1099 if (!ytPlayer || !playIcon) return;
1100 var state = ytPlayer.getPlayerState();
1101 playIcon.querySelector("path").setAttribute("d", state === 1 ? pausePath : playPath);
1102 }
1103
1104 // --- Progress bar ---
1105 var progressAnimId = null;
1106 var isSeeking = false;
1107 var progressLoopStarted = false;
1108
1109 function startProgressLoop() {
1110 if (progressLoopStarted) return;
1111 progressLoopStarted = true;
1112 progressAnimId = requestAnimationFrame(updateProgress);
1113 }
1114
1115 function updateProgress() {
1116 if (!ytPlayer || isSeeking) { progressAnimId = requestAnimationFrame(updateProgress); return; }
1117 try {
1118 var current = typeof ytPlayer.getCurrentTime === "function" ? ytPlayer.getCurrentTime() : 0;
1119 var duration = typeof ytPlayer.getDuration === "function" ? ytPlayer.getDuration() : 0;
1120 if (duration > 0) {
1121 var pct = (current / duration) * 100;
1122 progressBar.style.width = pct + "%";
1123 progressThumb.style.left = pct + "%";
1124 timeCurrent.textContent = formatTime(current);
1125 timeDuration.textContent = formatTime(duration);
1126 }
1127 // Buffered
1128 if (typeof ytPlayer.getVideoLoadedFraction === "function") {
1129 progressBuffered.style.width = (ytPlayer.getVideoLoadedFraction() * 100) + "%";
1130 }
1131 updatePlayIcon();
1132 } catch (e) {
1133 // Player API not ready yet, retry on next frame
1134 }
1135 progressAnimId = requestAnimationFrame(updateProgress);
1136 }
1137
1138 // Seek on click / drag
1139 if (progressWrap) {
1140 function seekFromEvent(e) {
1141 var rect = progressWrap.getBoundingClientRect();
1142 var x = Math.max(0, Math.min(e.clientX - rect.left, rect.width));
1143 var pct = x / rect.width;
1144 var duration = typeof ytPlayer.getDuration === "function" ? ytPlayer.getDuration() : 0;
1145 if (duration > 0 && ytPlayer) {
1146 ytPlayer.seekTo(pct * duration, true);
1147 progressBar.style.width = (pct * 100) + "%";
1148 progressThumb.style.left = (pct * 100) + "%";
1149 timeCurrent.textContent = formatTime(pct * duration);
1150 }
1151 }
1152
1153 progressWrap.addEventListener("mousedown", function(e) {
1154 isSeeking = true;
1155 seekFromEvent(e);
1156 function onMove(e) { seekFromEvent(e); }
1157 function onUp() {
1158 isSeeking = false;
1159 document.removeEventListener("mousemove", onMove);
1160 document.removeEventListener("mouseup", onUp);
1161 }
1162 document.addEventListener("mousemove", onMove);
1163 document.addEventListener("mouseup", onUp);
1164 });
1165
1166 progressWrap.addEventListener("mouseenter", function() {
1167 progressThumb.style.display = "block";
1168 });
1169 progressWrap.addEventListener("mouseleave", function() {
1170 if (!isSeeking) progressThumb.style.display = "none";
1171 });
1172 }
1173
1174 // --- Volume ---
1175 var volHideTimer = null;
1176 var localVol = 100;
1177 var localMuted = false;
1178
1179 function updateVolIconFromLocal() {
1180 if (!volIcon) return;
1181 var path;
1182 if (localMuted || localVol === 0) { path = volIconMuted; }
1183 else if (localVol < 40) { path = volIconLow; }
1184 else if (localVol < 75) { path = volIconMed; }
1185 else { path = volIconHigh; }
1186 volIcon.querySelector("path").setAttribute("d", path);
1187 }
1188
1189 // Called from schedule360AvailabilityChecks — only syncs icon, never touches slider
1190 function updateVolIcon() {
1191 updateVolIconFromLocal();
1192 }
1193
1194 function showVolSlider() {
1195 if (volHideTimer) { clearTimeout(volHideTimer); volHideTimer = null; }
1196 if (volSliderWrap) volSliderWrap.style.display = "flex";
1197 }
1198 function hideVolSlider() {
1199 volHideTimer = setTimeout(function() { if (volSliderWrap) volSliderWrap.style.display = "none"; }, 400);
1200 }
1201
1202 if (muteBtn) {
1203 muteBtn.addEventListener("click", function() {
1204 if (!ytPlayer) return;
1205 if (localMuted) {
1206 ytPlayer.unMute();
1207 localMuted = false;
1208 if (localVol === 0) { localVol = 100; ytPlayer.setVolume(100); }
1209 if (volSlider) volSlider.value = localVol;
1210 } else {
1211 ytPlayer.mute();
1212 localMuted = true;
1213 if (volSlider) volSlider.value = 0;
1214 }
1215 updateVolIconFromLocal();
1216 updateVolSliderFill();
1217 });
1218 muteBtn.addEventListener("mouseenter", showVolSlider);
1219 muteBtn.addEventListener("mouseleave", hideVolSlider);
1220 }
1221 if (volSliderWrap) {
1222 volSliderWrap.addEventListener("mouseenter", showVolSlider);
1223 volSliderWrap.addEventListener("mouseleave", hideVolSlider);
1224 }
1225 function updateVolSliderFill() {
1226 if (!volSlider) return;
1227 var v = parseInt(volSlider.value, 10);
1228 volSlider.style.background = "linear-gradient(to right,#fff " + v + "%,rgba(255,255,255,0.3) " + v + "%)";
1229 }
1230 if (volSlider) {
1231 volSlider.addEventListener("input", function() {
1232 if (!ytPlayer) return;
1233 var val = parseInt(volSlider.value, 10);
1234 localVol = val;
1235 ytPlayer.setVolume(val);
1236 if (val === 0) {
1237 ytPlayer.mute();
1238 localMuted = true;
1239 } else if (localMuted) {
1240 ytPlayer.unMute();
1241 localMuted = false;
1242 }
1243 updateVolIconFromLocal();
1244 updateVolSliderFill();
1245 });
1246 updateVolSliderFill();
1247 }
1248
1249 // --- Fullscreen ---
1250 if (fsBtn && outerContainer) {
1251 fsBtn.addEventListener("click", function() {
1252 var fsEl = document.fullscreenElement || document.webkitFullscreenElement;
1253 if (fsEl) {
1254 (document.exitFullscreen || document.webkitExitFullscreen).call(document);
1255 } else {
1256 (outerContainer.requestFullscreen || outerContainer.webkitRequestFullscreen).call(outerContainer);
1257 }
1258 });
1259
1260 // Save original styles
1261 var origFrameStyles = {
1262 width: frameContainer.style.width,
1263 height: frameContainer.style.height,
1264 maxWidth: frameContainer.style.maxWidth
1265 };
1266 var origOuterBg = outerContainer.style.background;
1267
1268 function onFsChange() {
1269 var fsEl = document.fullscreenElement || document.webkitFullscreenElement;
1270 var isFs = (fsEl === outerContainer);
1271 if (isFs) {
1272 frameContainer.style.width = "100%";
1273 frameContainer.style.height = "100%";
1274 frameContainer.style.maxWidth = "100%";
1275 outerContainer.style.background = "#000";
1276 } else {
1277 frameContainer.style.width = origFrameStyles.width;
1278 frameContainer.style.height = origFrameStyles.height;
1279 frameContainer.style.maxWidth = origFrameStyles.maxWidth;
1280 outerContainer.style.background = origOuterBg;
1281 }
1282 fsBtn.querySelector("svg path").setAttribute("d", isFs
1283 ? "M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"
1284 : "M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"
1285 );
1286 schedule360AvailabilityChecks();
1287 }
1288 document.addEventListener("fullscreenchange", onFsChange);
1289 document.addEventListener("webkitfullscreenchange", onFsChange);
1290 }
1291
1292 // Sync controls after player ready
1293 var origSchedule = schedule360AvailabilityChecks;
1294 schedule360AvailabilityChecks = function() {
1295 origSchedule();
1296 updateVolIcon();
1297 updatePlayIcon();
1298 };
1299 }
1300 if (document.readyState === "loading") {
1301 document.addEventListener("DOMContentLoaded", wpvrInitPlayer_' . $random_id . ');
1302 } else {
1303 wpvrInitPlayer_' . $random_id . '();
1304 }
1305 })();
1306 </script>';
1307 add_action( 'wp_footer', static function () use ( $wpvr_yt_script ) {
1308 echo $wpvr_yt_script; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1309 }, 99 );
1310
1311 $html .= '</div>'; // Close container
1312 $html .= '</div>'; // Close outer wrapper
1313 } elseif (strpos($videourl, 'vimeo') !== false) {
1314 $format = new WPVR_Format();
1315 $full_width = $width . ( isset( $width_unit ) ? $width_unit : 'px' );
1316 $full_height = $height . ( isset( $height_unit ) ? $height_unit : 'px' );
1317 $html = $format->prepare_vimeo_video_shortcode_data( $postdata, $full_width, $full_height, $autoplay, $loop, isset( $radius ) ? $radius : '' );
1318 } else {
1319 $html = '';
1320 $html .= '<div id="pano' . esc_attr($id) . '" class="pano-wrap ' . esc_attr($className) . '" style="max-width:100%; width:' . esc_attr($width) . esc_attr($width_unit) . '; height: ' . esc_attr($height) . esc_attr($height_unit) . '; border-radius:' . esc_attr($radius) . '; margin: 0 auto;">';
1321 $html .= '<div style="width:100%; height:100%; border-radius: ' . esc_attr($radius) . ';">' . wp_kses(
1322 $postdata['panoviddata'],
1323 array(
1324 'video' => array(
1325 'id' => array(),
1326 'class' => array(),
1327 'src' => array(),
1328 'controls' => array(),
1329 'autoplay' => array(),
1330 'loop' => array(),
1331 'muted' => array(),
1332 'poster' => array(),
1333 'preload' => array(),
1334 'width' => array(),
1335 'height' => array(),
1336 'style' => array(),
1337 'playsinline' => array(),
1338 'crossorigin' => array(),
1339 'data-*' => true,
1340 ),
1341 'source' => array(
1342 'src' => array(),
1343 'type' => array(),
1344 ),
1345 'track' => array(
1346 'kind' => array(),
1347 'src' => array(),
1348 'srclang' => array(),
1349 'label' => array(),
1350 'default' => array(),
1351 ),
1352 'div' => array(
1353 'id' => array(),
1354 'class' => array(),
1355 'style' => array(),
1356 'data-*' => true,
1357 ),
1358 'span' => array(
1359 'id' => array(),
1360 'class' => array(),
1361 'style' => array(),
1362 ),
1363 'button' => array(
1364 'id' => array(),
1365 'class' => array(),
1366 'style' => array(),
1367 'type' => array(),
1368 'title' => array(),
1369 ),
1370 )
1371 ) . '</div>';
1372
1373 $html .= '
1374 <style>
1375 .video-js {
1376 border-radius:' . esc_attr($radius) . ';
1377 }
1378 .video-js canvas{
1379 border-radius:' . esc_attr($radius) . ';
1380 }
1381 #pano' . esc_attr($id) . ' .vjs-poster {
1382 border-radius: ' . esc_attr($radius) . ';
1383 }
1384 </style>
1385
1386 ';
1387
1388 // $html .= '<script>';
1389 // $html .= 'videojs(' . $postdata['vidid'] . ', {';
1390 // $html .= 'plugins: {';
1391 // $html .= 'pannellum: {}';
1392 // $html .= '}';
1393 // $html .= '});';
1394 // $html .= '</script>';
1395 $html .= '</div>';
1396
1397 //video js vr setup //
1398 $html .= '<script>';
1399 $html .= '
1400 (function (window, videojs) {
1401 var player = window.player = videojs("' . $postdata['vidid'] . '");
1402 player.mediainfo = player.mediainfo || {};
1403 player.mediainfo.projection = "equirectangular";
1404
1405 // AUTO is the default and looks at mediainfo
1406 var vr = window.vr = player.vr({ projection: "AUTO", debug: true, forceCardboard: false, antialias: false });
1407 }(window, window.videojs));
1408
1409 ';
1410 $html .= '</script>';
1411 //video js vr end //
1412 }
1413 wpvr_enqueue_frontend_scripts( 'video' );
1414 return $html;
1415 }
1416
1417 wpvr_enqueue_frontend_scripts( 'scene' );
1418
1419 $control = false;
1420 if (isset($postdata['showControls'])) {
1421 $control = $postdata['showControls'];
1422 }
1423
1424 if ($control) {
1425 if (isset($postdata['customcontrol'])) {
1426 $custom_control = $postdata['customcontrol'];
1427 $custom_control['gyroSwitch'] = isset($postdata['gyro']) && $postdata['gyro'] ? 'on' : 'off';
1428 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" || $custom_control['gyroscopeSwitch'] == "on" || $custom_control['backToHomeSwitch'] == "on") {
1429 $control = false;
1430 }
1431 }
1432 }
1433
1434 $floor_plan_enable = 'off';
1435 $floor_plan_image = '';
1436 if (isset($postdata['floor_plan_tour_enabler']) && $postdata['floor_plan_tour_enabler'] == 'on') {
1437 $floor_plan_enable = $postdata['floor_plan_tour_enabler'];
1438 if (isset($postdata['floor_plan_attachment_url']) && !empty($postdata['floor_plan_attachment_url'])) {
1439 $floor_plan_image = $postdata['floor_plan_attachment_url'];
1440 }
1441 }
1442
1443 $vrgallery = false;
1444 if (isset($postdata['vrgallery'])) {
1445 $vrgallery = $postdata['vrgallery'];
1446 }
1447
1448 $vrgallery_title = false;
1449 if (isset($postdata['vrgallery_title'])) {
1450 $vrgallery_title = $postdata['vrgallery_title'];
1451 }
1452
1453 $vrgallery_display = false;
1454 if (isset($postdata['vrgallery_display'])) {
1455 $vrgallery_display = $postdata['vrgallery_display'];
1456 }
1457 $vrgallery_icon_size = false;
1458 if (isset($postdata['vrgallery_icon_size'])) {
1459 $vrgallery_icon_size = $postdata['vrgallery_icon_size'];
1460 }
1461
1462 $gyro = false;
1463 $gyro_orientation = false;
1464 if (isset($postdata['gyro'])) {
1465 $gyro = $postdata['gyro'];
1466 if (isset($postdata['deviceorientationcontrol'])) {
1467 $gyro_orientation = $postdata['deviceorientationcontrol'];
1468 }
1469 }
1470
1471 $compass = false;
1472 $audio_right = "5px";
1473 if (isset($postdata['compass'])) {
1474 $compass = $postdata['compass'] == 'on' || $postdata['compass'] != null ? true : false;
1475 if ($compass) {
1476 $audio_right = "60px";
1477 }
1478 }
1479 $floor_map_right = "25px";
1480 if ((isset($postdata['compass']) && $postdata['compass'] == 'on') && (isset($postdata['bg_music']) && $postdata['bg_music'] == 'on')) {
1481 $floor_map_right = "85px";
1482 } elseif (isset($postdata['compass']) && $postdata['compass'] == 'on') {
1483 $floor_map_right = "55px";
1484 } elseif (isset($postdata['bg_music']) && $postdata['bg_music'] == "on") {
1485 $floor_map_right = "25px";
1486 }
1487
1488 //===explainer handle===//
1489
1490 //===explainer handle===//
1491 $explainer_right = "10px";
1492 if ((isset($postdata['compass']) && $postdata['compass'] == 'on') && (isset($postdata['bg_music']) && $postdata['bg_music'] == 'on') && ($floor_plan_enable == 'on' && !empty($floor_plan_image))) {
1493 $explainer_right = "130px";
1494 } elseif (isset($postdata['compass']) && $postdata['compass'] == 'on' && ($floor_plan_enable == 'on' && !empty($floor_plan_image))) {
1495 $explainer_right = "100px";
1496 } elseif (isset($postdata['bg_music']) && $postdata['bg_music'] == "on" && ($floor_plan_enable == 'on' && !empty($floor_plan_image))) {
1497 $explainer_right = "55px";
1498 } elseif ((isset($postdata['compass']) && $postdata['compass'] == 'on') && (isset($postdata['bg_music']) && $postdata['bg_music'] == 'on')) {
1499 $explainer_right = "80px";
1500 } elseif (isset($postdata['compass']) && $postdata['compass'] == 'on') {
1501 $explainer_right = "55px";
1502 } elseif (isset($postdata['bg_music']) && $postdata['bg_music'] == "on") {
1503 $explainer_right = "30px";
1504 } elseif ($floor_plan_enable == 'on' && !empty($floor_plan_image)) {
1505 $explainer_right = "70px";
1506 }
1507 $enable_cardboard = '';
1508 $is_cardboard = get_option('wpvr_cardboard_disable');
1509 if (wpvr_isMobileDevice() && $is_cardboard == 'true') {
1510 $enable_cardboard = 'enable-cardboard';
1511 $audio_right = "73px";
1512 if (isset($postdata['compass'])) {
1513 $compass = $postdata['compass'] == 'on' || $postdata['compass'] != null ? true : false;
1514 if ($compass) {
1515 $audio_right = "130px";
1516 }
1517 }
1518
1519 $floor_map_right = "60px";
1520 if ((isset($postdata['compass']) && $postdata['compass'] == 'on') && (isset($postdata['bg_music']) && $postdata['bg_music'] == 'on')) {
1521 $floor_map_right = "150px";
1522 } elseif (isset($postdata['compass']) && $postdata['compass'] == 'on') {
1523 $floor_map_right = "120px";
1524 } elseif (isset($postdata['bg_music']) && $postdata['bg_music'] == "on") {
1525 $floor_map_right = "90px";
1526 }
1527
1528 //===explainer handle===//
1529
1530 $explainer_right = "65px";
1531
1532 if ((isset($postdata['compass']) && $postdata['compass'] == true) && (isset($postdata['bg_music']) && $postdata['bg_music'] == 'on')) {
1533 $explainer_right = "150px";
1534 } elseif ((isset($postdata['compass']) && $postdata['compass'] == true) && (isset($postdata['bg_music']) && $postdata['bg_music'] == 'on') && ($floor_plan_enable == 'on' && !empty($floor_plan_image))) {
1535 $explainer_right = "180px";
1536 } elseif (isset($postdata['compass']) && $postdata['compass'] == true && ($floor_plan_enable == 'on' && !empty($floor_plan_image))) {
1537 $explainer_right = "150px";
1538 } elseif (isset($postdata['bg_music']) && $postdata['bg_music'] == "on" && ($floor_plan_enable == 'on' && !empty($floor_plan_image))) {
1539 $explainer_right = "120px";
1540 } elseif (isset($postdata['compass']) && $postdata['compass'] == true) {
1541 $explainer_right = "130px";
1542 } elseif (isset($postdata['bg_music']) && $postdata['bg_music'] == "on") {
1543 $explainer_right = "90px";
1544 } elseif ($floor_plan_enable == 'on' && !empty($floor_plan_image)) {
1545 $explainer_right = "90px";
1546 }
1547 }
1548
1549 //===explainer handle===//
1550
1551 $mouseZoom = true;
1552 if (isset($postdata['mouseZoom'])) {
1553 if ($postdata['mouseZoom'] == "off") {
1554 $mouseZoom = false;
1555 } else {
1556 $mouseZoom = true;
1557 }
1558 }
1559
1560 $draggable = true;
1561 if (isset($postdata['draggable'])) {
1562 $draggable = $postdata['draggable'] == 'on' || $postdata['draggable'] != null ? true : false;
1563 if ($postdata['draggable'] === 'off') {
1564 $draggable = false;
1565 }
1566 }
1567 $diskeyboard = false;
1568 if (isset($postdata['diskeyboard'])) {
1569 $diskeyboard = $postdata['diskeyboard'] == 'off' || $postdata['diskeyboard'] == null ? false : true;
1570 }
1571
1572 $keyboardzoom = true;
1573 if (isset($postdata['keyboardzoom'])) {
1574 $keyboardzoom = $postdata['keyboardzoom'];
1575 }
1576
1577 $autoload = false;
1578
1579 if (isset($postdata['autoLoad'])) {
1580 $autoload = $postdata['autoLoad'];
1581 }
1582
1583 $default_scene = '';
1584 if (isset($postdata['defaultscene'])) {
1585 $default_scene = $postdata['defaultscene'];
1586 }
1587
1588 $preview = '';
1589 if (isset($postdata['preview'])) {
1590 $preview = $postdata['preview'];
1591 }
1592
1593 $autorotation = '';
1594 if (isset($postdata["autoRotate"])) {
1595 $autorotation = $postdata["autoRotate"];
1596 }
1597 $autorotationinactivedelay = '';
1598 if (isset($postdata["autoRotateInactivityDelay"])) {
1599 $autorotationinactivedelay = $postdata["autoRotateInactivityDelay"];
1600 }
1601 $autorotationstopdelay = '';
1602 if (isset($postdata["autoRotateStopDelay"])) {
1603 $autorotationstopdelay = $postdata["autoRotateStopDelay"];
1604 }
1605
1606 $scene_fade_duration = '';
1607 if (isset($postdata['scenefadeduration'])) {
1608 $scene_fade_duration = $postdata['scenefadeduration'];
1609 }
1610
1611 $panodata = array();
1612 if (isset($postdata['panodata'])) {
1613 $panodata = $postdata['panodata'];
1614 }
1615
1616 $default_zoom_global = 100;
1617 if (isset($postdata['hfov']) && $postdata['hfov'] != '') {
1618 $default_zoom_global = $postdata['hfov'];
1619 }
1620
1621 $min_zoom_global = 50;
1622 if (isset($postdata['minHfov']) && $postdata['minHfov'] != '') {
1623 $min_zoom_global = $postdata['minHfov'];
1624 }
1625
1626 $max_zoom_global = 120;
1627 if (isset($postdata['maxHfov']) && $postdata['maxHfov'] != '') {
1628 $max_zoom_global = $postdata['maxHfov'];
1629 }
1630
1631 $hotspoticoncolor = '#00b4ff';
1632 $hotspotblink = 'on';
1633 $default_data = array();
1634 $default_data = array('firstScene' => $default_scene, 'sceneFadeDuration' => $scene_fade_duration, 'hfov' => $default_zoom_global, 'maxHfov' => $max_zoom_global, 'minHfov' => $min_zoom_global);
1635 $scene_data = array();
1636 if (is_array($panodata) && isset($panodata['scene-list'])) {
1637 if (!empty($panodata['scene-list'])) {
1638 foreach ($panodata['scene-list'] as $panoscenes) {
1639 $scene_ititle = '';
1640 if (isset($panoscenes["scene-ititle"])) {
1641 $scene_ititle = esc_html($panoscenes["scene-ititle"]);
1642 }
1643
1644 $scene_author = '';
1645 if (isset($panoscenes["scene-author"])) {
1646 $scene_author = esc_html($panoscenes["scene-author"]);
1647 }
1648
1649 $scene_author_url = '';
1650 if (isset($panoscenes["scene-author-url"])) {
1651 $scene_author_url = sanitize_text_field($panoscenes["scene-author-url"]);
1652 }
1653
1654 $scene_vaov = 180;
1655 if (isset($panoscenes["scene-vaov"])) {
1656 $scene_vaov = (float)$panoscenes["scene-vaov"];
1657 }
1658
1659 $scene_haov = 360;
1660 if (isset($panoscenes["scene-haov"])) {
1661 $scene_haov = (float)$panoscenes["scene-haov"];
1662 }
1663
1664 $scene_vertical_offset = 0;
1665 if (isset($panoscenes["scene-vertical-offset"])) {
1666 $scene_vertical_offset = (float)$panoscenes["scene-vertical-offset"];
1667 }
1668
1669 $default_scene_pitch = null;
1670 if (isset($panoscenes["scene-pitch"])) {
1671 $default_scene_pitch = (float)$panoscenes["scene-pitch"];
1672 }
1673
1674 $default_scene_yaw = null;
1675 if (isset($panoscenes["scene-yaw"])) {
1676 $default_scene_yaw = (float)$panoscenes["scene-yaw"];
1677 }
1678
1679 $scene_max_pitch = '';
1680 if (isset($panoscenes["scene-maxpitch"])) {
1681 $scene_max_pitch = (float)$panoscenes["scene-maxpitch"];
1682 }
1683
1684
1685 $scene_min_pitch = '';
1686 if (isset($panoscenes["scene-minpitch"])) {
1687 $scene_min_pitch = (float)$panoscenes["scene-minpitch"];
1688 }
1689
1690
1691 $scene_max_yaw = '';
1692 if (isset($panoscenes["scene-maxyaw"])) {
1693 $scene_max_yaw = (float)$panoscenes["scene-maxyaw"];
1694 }
1695
1696
1697 $scene_min_yaw = '';
1698 if (isset($panoscenes["scene-minyaw"])) {
1699 $scene_min_yaw = (float)$panoscenes["scene-minyaw"];
1700 }
1701
1702 $default_zoom = 100;
1703 if (isset($panoscenes["scene-zoom"]) && $panoscenes["scene-zoom"] != '') {
1704 $default_zoom = (int)$panoscenes["scene-zoom"];
1705 } else {
1706 if ($default_zoom_global != '') {
1707 $default_zoom = (int)$default_zoom_global;
1708 }
1709 }
1710
1711
1712 $max_zoom = 120;
1713 if (isset($panoscenes["scene-maxzoom"]) && $panoscenes["scene-maxzoom"] != '') {
1714 $max_zoom = (int)$panoscenes["scene-maxzoom"];
1715 } else {
1716 if ($max_zoom_global != '') {
1717 $max_zoom = (int)$max_zoom_global;
1718 }
1719 }
1720
1721 $min_zoom = 120;
1722 if (isset($panoscenes["scene-minzoom"]) && $panoscenes["scene-minzoom"] != '') {
1723 $min_zoom = (int)$panoscenes["scene-minzoom"];
1724 } else {
1725 if ($min_zoom_global != '') {
1726 $min_zoom = (int)$min_zoom_global;
1727 }
1728 }
1729
1730 $hotspot_datas = array();
1731 if (isset($panoscenes['hotspot-list'])) {
1732 $hotspot_datas = $panoscenes['hotspot-list'];
1733 }
1734
1735 $hotspots = array();
1736
1737 foreach ($hotspot_datas as $hotspot_data) {
1738 $status = get_option('wpvr_edd_license_status');
1739 if ($status !== false && $status == 'valid') {
1740
1741 if (isset($hotspot_data["hotspot-customclass-pro"]) && $hotspot_data["hotspot-customclass-pro"] != 'none') {
1742 $hotspot_data['hotspot-customclass'] = $hotspot_data["hotspot-customclass-pro"] . ' custom-' . $id . '-' . $panoscenes['scene-id'] . '-' . $hotspot_data['hotspot-title'];
1743 }
1744 if (isset($hotspot_data['hotspot-blink'])) {
1745 $hotspotblink = $hotspot_data['hotspot-blink'];
1746 }
1747 }
1748 $hotspot_scene_pitch = '';
1749 if (isset($hotspot_data["hotspot-scene-pitch"])) {
1750 $hotspot_scene_pitch = $hotspot_data["hotspot-scene-pitch"];
1751 }
1752 $hotspot_scene_yaw = '';
1753 if (isset($hotspot_data["hotspot-scene-yaw"])) {
1754 $hotspot_scene_yaw = $hotspot_data["hotspot-scene-yaw"];
1755 }
1756
1757 $hotspot_type = $hotspot_data["hotspot-type"] !== 'scene' ? 'info' : $hotspot_data["hotspot-type"];
1758 $hotspot_content = '';
1759
1760 ob_start();
1761 do_action('wpvr_hotspot_content', $hotspot_data);
1762 $hotspot_content = ob_get_clean();
1763
1764 if (!$hotspot_content) {
1765 $hotspot_content = $hotspot_data["hotspot-content"];
1766 }
1767
1768 if (isset($hotspot_data["wpvr_url_open"][0])) {
1769 $wpvr_url_open = $hotspot_data["wpvr_url_open"][0];
1770 } else {
1771 $wpvr_url_open = "off";
1772 }
1773 $on_hover_content = preg_replace_callback(
1774 '/<p>\s*(<img[^>]*>)\s*<br>\s*<\/p>/i',
1775 function ($matches) {
1776 return $matches[1];
1777 },
1778 $hotspot_data['hotspot-hover'] ?? ''
1779 );
1780
1781 if('fluent_form' !== $hotspot_data["hotspot-type"]){
1782 $on_hover_content = sanitize_content_preserve_styles($on_hover_content ?? '');
1783 $on_click_content = preg_replace_callback('/<img[^>]*>/', "replace_callback", $hotspot_content ?? '');
1784 $on_click_content = sanitize_content_preserve_styles($on_click_content ?? '');
1785 }else{
1786 $on_hover_content = $on_hover_content ?? '';
1787 $on_click_content = $hotspot_content ?? '';
1788 }
1789
1790
1791 $hotspot_shape = 'round';
1792 if (isset($hotspot_data["hotspot-customclass-pro"]) && $hotspot_data["hotspot-customclass-pro"] != 'none') {
1793 $hotspot_shape = isset($hotspot_data["hotspot-shape"]) ? $hotspot_data["hotspot-shape"] : 'round';
1794 }
1795
1796 $hotspot_data_for_on_click=[];
1797 if( ('info' === $hotspot_type || 'fluent_form' === $hotspot_data["hotspot-type"] ) && !empty($on_click_content)){
1798 $hotspot_data_for_on_click = [
1799 'on_click_content' => $on_click_content,
1800 'tour_id' => $id,
1801 'scene_id' => $panoscenes['scene-id'],
1802 'hotspot_id' => sanitize_html_class($hotspot_data['hotspot-title']),
1803 ];
1804 } elseif('info' === $hotspot_type && !empty($hotspot_data["hotspot-url"])){
1805 $hotspot_data_for_on_click = [
1806 'on_click_content' => '',
1807 'tour_id' => $id,
1808 'scene_id' => $panoscenes['scene-id'],
1809 'hotspot_id' => sanitize_html_class($hotspot_data['hotspot-title']),
1810 ];
1811 }
1812
1813 $hotspot_info = array(
1814 'text' => esc_html(sanitize_text_field($hotspot_data['hotspot-title'])),
1815 'pitch' => $hotspot_data['hotspot-pitch'],
1816 'yaw' => $hotspot_data['hotspot-yaw'],
1817 'type' => $hotspot_type,
1818 'cssClass' => $hotspot_data['hotspot-customclass'],
1819 'URL' => $hotspot_data['hotspot-url'],
1820 "wpvr_url_open" => $wpvr_url_open,
1821 "clickHandlerArgs" => $hotspot_data_for_on_click,
1822 'createTooltipArgs' => !empty(trim($on_hover_content)) ? trim($on_hover_content) : '',
1823 "sceneId" => $hotspot_data["hotspot-scene"],
1824 "targetPitch" => (float)$hotspot_scene_pitch,
1825 "targetYaw" => (float)$hotspot_scene_yaw,
1826 'hotspot_type' => $hotspot_data['hotspot-type'],
1827 'hotspot_shape' => $hotspot_shape,
1828 );
1829
1830 $hotspot_info['URL'] = ($hotspot_data['hotspot-type'] === 'fluent_form' || $hotspot_data['hotspot-type'] === 'wc_product') ? '' : $hotspot_info['URL'];
1831
1832 if ($hotspot_data["hotspot-customclass"] == 'none' || $hotspot_data["hotspot-customclass"] == '') {
1833 unset($hotspot_info["cssClass"]);
1834 }
1835 if (empty($hotspot_data["hotspot-scene"])) {
1836 unset($hotspot_info['targetPitch']);
1837 unset($hotspot_info['targetYaw']);
1838 }
1839 array_push($hotspots, $hotspot_info);
1840 }
1841
1842 $device_scene = $panoscenes['scene-attachment-url'];
1843 $mobile_media_resize = get_option('mobile_media_resize');
1844 $file_accessible = ini_get('allow_url_fopen');
1845 if ($mobile_media_resize == "true" && $device_scene) {
1846 if ($file_accessible == "1") {
1847 $image_info = getimagesize($device_scene);
1848 if ($image_info && $image_info[0] > 4096) {
1849 $src_to_id_for_mobile = '';
1850 $src_to_id_for_desktop = '';
1851 if (wpvr_isMobileDevice()) {
1852 $src_to_id_for_mobile = attachment_url_to_postid($panoscenes['scene-attachment-url']);
1853 if ($src_to_id_for_mobile) {
1854 $mobile_scene = wp_get_attachment_image_src($src_to_id_for_mobile, 'wpvr_mobile');
1855 if ($mobile_scene[3]) {
1856 $device_scene = $mobile_scene[0];
1857 }
1858 }
1859 } else {
1860 $src_to_id_for_desktop = attachment_url_to_postid($panoscenes['scene-attachment-url']);
1861 if ($src_to_id_for_desktop) {
1862 $desktop_scene = wp_get_attachment_image_src($src_to_id_for_mobile, 'full');
1863 if ($desktop_scene && $desktop_scene[0]) {
1864 $device_scene = $desktop_scene[0];
1865 }
1866 }
1867 }
1868 }
1869 }
1870 }
1871
1872 $scene_info = array();
1873
1874 if ($panoscenes["scene-type"] == 'cubemap') {
1875 $pano_type = 'cubemap';
1876 $pano_attachment = array(
1877 $panoscenes["scene-attachment-url-face0"],
1878 $panoscenes["scene-attachment-url-face1"],
1879 $panoscenes["scene-attachment-url-face2"],
1880 $panoscenes["scene-attachment-url-face3"],
1881 $panoscenes["scene-attachment-url-face4"],
1882 $panoscenes["scene-attachment-url-face5"]
1883 );
1884
1885 $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);
1886 } else {
1887 $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);
1888 }
1889
1890
1891
1892 if (isset($panoscenes["ptyscene"])) {
1893 if ($panoscenes["ptyscene"] == "off") {
1894 unset($scene_info['pitch']);
1895 unset($scene_info['yaw']);
1896 }
1897 }
1898
1899 if (empty($panoscenes["scene-ititle"])) {
1900 unset($scene_info['title']);
1901 }
1902 if (empty($panoscenes["scene-author"])) {
1903 unset($scene_info['author']);
1904 }
1905 if (empty($panoscenes["scene-author-url"])) {
1906 unset($scene_info['authorURL']);
1907 }
1908
1909 if (empty($scene_vaov)) {
1910 unset($scene_info['vaov']);
1911 }
1912
1913 if (empty($scene_haov)) {
1914 unset($scene_info['haov']);
1915 }
1916
1917 if (empty($scene_vertical_offset)) {
1918 unset($scene_info['vOffset']);
1919 }
1920
1921 if (isset($panoscenes["cvgscene"])) {
1922 if ($panoscenes["cvgscene"] == "off") {
1923 unset($scene_info['maxPitch']);
1924 unset($scene_info['minPitch']);
1925 }
1926 }
1927 if (empty($panoscenes["scene-maxpitch"])) {
1928 unset($scene_info['maxPitch']);
1929 }
1930
1931 if (empty($panoscenes["scene-minpitch"])) {
1932 unset($scene_info['minPitch']);
1933 }
1934
1935 if (isset($panoscenes["chgscene"])) {
1936 if ($panoscenes["chgscene"] == "off") {
1937 unset($scene_info['maxYaw']);
1938 unset($scene_info['minYaw']);
1939 }
1940 }
1941 if (empty($panoscenes["scene-maxyaw"])) {
1942 unset($scene_info['maxYaw']);
1943 }
1944
1945 if (empty($panoscenes["scene-minyaw"])) {
1946 unset($scene_info['minYaw']);
1947 }
1948
1949 // if (isset($panoscenes["czscene"])) {
1950 // if ($panoscenes["czscene"] == "off") {
1951 // unset($scene_info['hfov']);
1952 // unset($scene_info['maxHfov']);
1953 // unset($scene_info['minHfov']);
1954 // }
1955 // }
1956
1957 $scene_array = array();
1958 $scene_array = array(
1959 $panoscenes['scene-id'] => $scene_info
1960 );
1961
1962 $scene_data[$panoscenes['scene-id']] = $scene_info;
1963 }
1964 }
1965 }
1966 $pano_id_array = array();
1967 $pano_id_array = array('panoid' => $panoid);
1968 $pano_response = array();
1969 $pano_response = array('autoLoad' => $autoload, 'showControls' => $control, '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);
1970 if (empty($autorotation)) {
1971 unset($pano_response['autoRotate']);
1972 unset($pano_response['autoRotateInactivityDelay']);
1973 unset($pano_response['autoRotateStopDelay']);
1974 }
1975 if (empty($autorotationinactivedelay)) {
1976 unset($pano_response['autoRotateInactivityDelay']);
1977 }
1978 if (empty($autorotationstopdelay)) {
1979 unset($pano_response['autoRotateStopDelay']);
1980 }
1981
1982 $response = array();
1983 $response = array($pano_id_array, $pano_response);
1984 if (!empty($response)) {
1985 $response = json_encode($response);
1986 }
1987 if (empty($width)) {
1988 $width = '600';
1989 }
1990 if (empty($height)) {
1991 $height = '400';
1992 }
1993 if ('fullwidth' == $width) {
1994 $width = "100%";
1995 }
1996
1997 $foreground_color = '#fff';
1998 $pulse_color = wpvr_hex2rgb($hotspoticoncolor);
1999 $rgb = wpvr_HTMLToRGB($hotspoticoncolor);
2000 $hsl = wpvr_RGBToHSL($rgb);
2001 if ($hsl->lightness > 200) {
2002 $foreground_color = '#000000';
2003 } else {
2004 $foreground_color = '#fff';
2005 }
2006
2007 $class = 'myclass';
2008 $html = 'test';
2009 $html = '';
2010 $html .= '<style>';
2011 if ($width == 'embed') {
2012 $html .= 'body{
2013 overflow: hidden;
2014 }';
2015 }
2016 $status = get_option('wpvr_edd_license_status');
2017 if ($status !== false && $status == 'valid') {
2018 if (isset($postdata['customcss_enable']) && $postdata['customcss_enable'] == 'on') {
2019 $html .= isset($postdata['customcss']) ? $postdata['customcss'] : '';
2020 }
2021 }
2022 if ($status !== false && $status == 'valid') {
2023 if (is_array($panodata) && isset($panodata['scene-list'])) {
2024 foreach ($panodata['scene-list'] as $panoscenes) {
2025
2026 foreach ($panoscenes['hotspot-list'] as $hotspot) {
2027 if (isset($hotspot['hotspot-customclass-color-icon-value']) && !empty($hotspot['hotspot-customclass-color-icon-value'])) {
2028 $hotspoticoncolor = $hotspot['hotspot-customclass-color-icon-value'];
2029 } else {
2030 $hotspoticoncolor = "#00b4ff";
2031 }
2032 $hotspot_border = '';
2033 if(isset($hotspot['hotspot-border']) && $hotspot['hotspot-border'] == 'on'){
2034 $border_width = $hotspot['hotspot-border-width'];
2035 $border_color = $hotspot['hotspot-border-color'];
2036 $border_style = $hotspot['hotspot-border-style'];
2037
2038 $hotspot_border = 'border: '.$border_width.'px '.$border_style.' '.$border_color.';';
2039 }
2040 $hotspot_background_color = 'background-color: ' . $hotspoticoncolor . ';';
2041 $hotspot_animation = ' animation: icon-pulse' . $panoid . '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ' 1.5s infinite cubic-bezier(.25, 0, 0, 1);
2042 '. $hotspot_border.'';
2043
2044
2045 $pulse_color = wpvr_hex2rgb($hotspoticoncolor);
2046 if (isset($hotspot["hotspot-customclass-pro"]) && $hotspot["hotspot-customclass-pro"] != 'none') {
2047 $border_radius = ' border-radius: 100%;';
2048 $hotspot_shape = isset($hotspot["hotspot-shape"]) ? $hotspot["hotspot-shape"] : 'round';
2049 if($hotspot_shape === 'square'){
2050 $border_radius = '';
2051 }
2052 if ($hotspot_shape === 'hexagon') {
2053 $border_radius = '';
2054 $hotspot_background_color = 'background-color: transparent;';
2055 $hotspot_animation = '';
2056 }
2057
2058 if(isset($hotspot['hotspot-custom-icon-color-value']) && !empty($hotspot['hotspot-custom-icon-color-value'])){
2059 $foreground_color = $hotspot['hotspot-custom-icon-color-value'];
2060 }
2061 $html .= '#' . esc_attr( $panoid ). ' div.pnlm-hotspot-base.fas.custom-' . esc_attr( $id ). '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ',
2062 #' . esc_attr( $panoid ). ' div.pnlm-hotspot-base.fab.custom-' . esc_attr( $id ). '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ',
2063 #' . esc_attr( $panoid ). ' div.pnlm-hotspot-base.fa.custom-' . esc_attr( $id ). '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ',
2064 #' . esc_attr( $panoid ). ' div.pnlm-hotspot-base.fa-solid.custom-' . esc_attr( $id ). '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ',
2065 #' . esc_attr( $panoid ). ' div.pnlm-hotspot-base.far.custom-' . esc_attr( $id ). '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ' {
2066 display: block !important;
2067 '.$hotspot_background_color.'
2068 color: ' . $foreground_color . ';
2069 '.$border_radius.'
2070 width: 30px;
2071 height: 30px;
2072 font-size: 16px;
2073 line-height: 30px;
2074 '.$hotspot_animation.'
2075 }';
2076 if($hotspot_shape === 'hexagon'){
2077
2078 $html .= '#' . esc_attr( $panoid ) . ' .custom-' . esc_attr( $id ) . '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ' .hexagon-wrapper svg path {
2079 fill: ' . $hotspoticoncolor . ';
2080 }';
2081
2082 $html .= '#' . esc_attr( $panoid ) . ' .custom-' . esc_attr( $id ) . '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . '.pnlm-tooltip:after{
2083 content: "";
2084 position: absolute;
2085 left: 50%;
2086 top: 50%;
2087 transform: translate(-50%, -50%);
2088 width: 85%;
2089 height: 85%;
2090 animation: icon-pulse' . esc_attr( $panoid ) . '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ' 1.5s infinite cubic-bezier(.25, 0, 0, 1);
2091 border-radius: 100%;
2092 z-index: -2;
2093 }';
2094
2095 }
2096 $html .= '#' . esc_attr( $panoid2 ). ' div.pnlm-hotspot-base.fas.custom-' . esc_attr( $id ). '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ',
2097 #' . esc_attr( $panoid2 ). ' div.pnlm-hotspot-base.fab.custom-' . esc_attr( $id ). '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ',
2098 #' . esc_attr( $panoid2 ). ' div.pnlm-hotspot-base.fa-solid.custom-' . esc_attr( $id ). '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ',
2099 #' . esc_attr( $panoid2) . ' div.pnlm-hotspot-base.fa.custom-' . esc_attr( $id ). '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ',
2100 #' . esc_attr( $panoid2 ). ' div.pnlm-hotspot-base.far.custom-' . esc_attr( $id ). '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ' {
2101 display: block !important;
2102 '.esc_attr( $hotspot_background_color).'
2103 color: ' . esc_attr( $foreground_color) . ';
2104 '.esc_attr( $border_radius).'
2105 width: 30px;
2106 height: 30px;
2107 font-size: 16px;
2108 line-height: 30px;
2109 '.esc_attr( $hotspot_animation).'
2110 }';
2111 }
2112 if (isset($hotspot['hotspot-blink'])) {
2113 $hotspotblink = $hotspot['hotspot-blink'];
2114 if ($hotspotblink == 'on') {
2115 $html .= '@-webkit-keyframes icon-pulse' . esc_attr( $panoid) . '-' . $panoscenes['scene-id'] . '-' . sanitize_html_class($hotspot['hotspot-title']) . ' {
2116 0% {
2117 box-shadow: 0 0 0 0px rgba(' . esc_attr( $pulse_color[0]) . ', 1);
2118 }
2119 100% {
2120 box-shadow: 0 0 0 10px rgba(' . esc_attr( $pulse_color[0]) . ', 0);
2121 }
2122 }
2123 @keyframes icon-pulse' . esc_attr( $panoid) . ' {
2124 0% {
2125 box-shadow: 0 0 0 0px rgba(' . esc_attr( $pulse_color[0]) . ', 1);)
2126 }
2127 100% {
2128 box-shadow: 0 0 0 10px rgba(' . esc_attr( $pulse_color[0] ). ', 0);
2129 }
2130 }';
2131 }
2132 }
2133 }
2134 }
2135 }
2136 }
2137
2138 $status = get_option('wpvr_edd_license_status');
2139 if ($status !== false && $status == 'valid') {
2140 if (!$gyro) {
2141 $html .= '#' . esc_attr( $panoid ). ' div.pnlm-orientation-button {
2142 display: none;
2143 }';
2144 }
2145 } else {
2146 $html .= '#' . esc_attr( $panoid ). ' div.pnlm-orientation-button {
2147 display: none;
2148 }';
2149 }
2150 $floor_plan_custom_color = isset($postdata['floor_plan_custom_color']) ? $postdata['floor_plan_custom_color'] : '#cca92c';
2151 $foreground_color_pointer = '#fff';
2152 if ($floor_plan_custom_color != '') {
2153 $pointer_pulse = wpvr_hex2rgb($floor_plan_custom_color);
2154 $floor_rgb = wpvr_HTMLToRGB($floor_plan_custom_color);
2155 $floor_hsl = wpvr_RGBToHSL($floor_rgb);
2156 if ($floor_hsl->lightness > 200) {
2157 $foreground_color_pointer = '#000000';
2158 }
2159 $html .= '
2160 .wpvr-floor-map .floor-plan-pointer.add-pulse:before {
2161 border: 17px solid ' . esc_attr( $floor_plan_custom_color ) . ';
2162 }
2163 @-webkit-keyframes pulse {
2164 0% {
2165 -webkit-box-shadow: 0 0 0 0 rgba(' . esc_attr( $pointer_pulse[0] ) . ', 0.7);
2166 }
2167 70% {
2168 -webkit-box-shadow: 0 0 0 10px rgba(' . esc_attr( $pointer_pulse[0] ) . ', 0);
2169 }
2170 100% {
2171 -webkit-box-shadow: 0 0 0 0 rgba(' . esc_attr( $pointer_pulse[0] ) . ', 0);
2172 }
2173 }
2174 @keyframes pulse {
2175 0% {
2176 -moz-box-shadow: 0 0 0 0 rgba(' . esc_attr( $pointer_pulse[0] ) . ', 0.7);
2177 box-shadow: 0 0 0 0 rgba(' . esc_attr( $pointer_pulse[0] ) . ', 0.7);
2178 }
2179 70% {
2180 -moz-box-shadow: 0 0 0 10px rgba(' . esc_attr( $pointer_pulse[0] ) . ', 0);
2181 box-shadow: 0 0 0 10px rgba(' . esc_attr( $pointer_pulse[0] ) . ', 0);
2182 }
2183 100% {
2184 -moz-box-shadow: 0 0 0 0 rgba(' . esc_attr( $pointer_pulse[0] ) . ', 0);
2185 box-shadow: 0 0 0 0 rgba(' . esc_attr( $pointer_pulse[0] ) . ', 0);
2186 }
2187 }';
2188 }
2189 $html .= '</style>';
2190
2191 $scene_animation = isset($postdata['sceneAnimation']) ? $postdata['sceneAnimation'] : 'off';
2192
2193 if( $scene_animation === 'on' && is_plugin_active( 'wpvr-pro/wpvr-pro.php' ) ) {
2194 $animation_type = isset($postdata['sceneAnimationName']) ? $postdata['sceneAnimationName'] : 'none';
2195 $animationDuration = isset($postdata['sceneAnimationTransitionDuration']) ? $postdata['sceneAnimationTransitionDuration'] : '500ms';
2196 $animationDelay = isset($postdata['sceneAnimationTransitionDelay']) ? $postdata['sceneAnimationTransitionDelay'] : '0ms';
2197 $animation_css = apply_filters('wpvr_tour_scene_animation',$animation_type,$animationDuration, $animationDelay, $postdata,$id);
2198 $html .= $animation_css;
2199 }
2200
2201 if (wpvr_isMobileDevice()) {
2202 $html .= '<div id="master-container" class="wpvr-cardboard ' . esc_attr( $className ) . ' ' . esc_attr( $enable_cardboard ) . ' " style="max-width:' . esc_attr( $width ) . esc_attr( $width_unit ) . '; width: 100%; height: ' . esc_attr( $mobile_height ) . esc_attr( $mobile_height_unit ) . '; border-radius:' . esc_attr( $radius ) . '; direction:ltr; border : ' . esc_attr( $border_style ) . ' ">';
2203 } else {
2204 $html .= '<div id="master-container" class="wpvr-cardboard ' . esc_attr( $className ) . ' ' . esc_attr( $enable_cardboard ) . '" style="max-width:' . esc_attr( $width ) . esc_attr( $width_unit ) . '; width: 100%; height: ' . esc_attr( $height ) . esc_attr( $height_unit ) . '; border-radius:' . esc_attr( $radius ) . '; direction:ltr; border : ' . esc_attr( $border_style ) . '">';
2205 }
2206 $status = get_option('wpvr_edd_license_status');
2207 $is_cardboard = get_option('wpvr_cardboard_disable');
2208 if ($status !== false && 'valid' == $status && $is_pro && wpvr_isMobileDevice() && $is_cardboard == 'true') {
2209 $html .= '<button class="fullscreen-button">';
2210 $html .= '<span class="expand">';
2211 $html .= '<i class="fas fa-expand" aria-hidden="true"></i>';
2212 $html .= '</span>';
2213
2214 $html .= '<span class="compress">';
2215 $html .= '<i class="fas fa-minimize" aria-hidden="true"></i>';
2216 $html .= '</span>';
2217 $html .= '</button>';
2218 $html .= '<label class="wpvr-cardboard-switcher">
2219 <input type="checkbox" class="vr_mode_change' . $id . '" name="vr_mode_change" value="off">
2220 <span class="switcher-box">
2221 <span class="normal-mode-tooltip">Normal VR Mode</span>
2222 <svg width="78" height="60" viewBox="0 0 78 60" fill="none" xmlns="http://www.w3.org/2000/svg">
2223 <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"/>
2224 </svg>
2225 </span>
2226 </label>';
2227 }
2228
2229 if ($width == 'fullwidth') {
2230 if (wpvr_isMobileDevice()) {
2231 $html .= '<div class="cardboard-vrfullwidth vrfullwidth">';
2232 $html .= '<div id="pano2' . esc_attr( $id ) . '" class="pano-wrap pano-left cardboard-half" style="width: 49%!important; border-radius:' . esc_attr( $radius ). ' ;text-align:center; direction:ltr;" ><div id="center-pointer2' . esc_attr( $id ). '" class="vr-pointer-container"><span class="center-pointer"></span></div></div>';
2233 $html .= '<div id="pano' . esc_attr( $id ) . '" class="pano-wrap pano-right" style="width: 100%; text-align:center; direction:ltr; border-radius:' . esc_attr( $radius ) . '" >';
2234 } else {
2235 $html .= '<div id="pano2' . esc_attr( $id ) . '" class="pano-wrap pano-left" style="width: 49%; border-radius:' . esc_attr( $radius ) . ';"><div id="center-pointer2' . esc_attr( $id ). '" class="vr-pointer-container"><span class="center-pointer"></span></div></div>';
2236 $html .= '<div id="pano' . esc_attr( $id ) . '" class="pano-wrap vrfullwidth" style=" text-align:center; height: ' . esc_attr( $height ) . esc_attr( $height_unit ) . '; border-radius:' . esc_attr( $radius ) . '; direction:ltr;" >';
2237 }
2238 } else {
2239 if (wpvr_isMobileDevice()) {
2240 $html .= '<div id="pano2' . esc_attr( $id ) . '" class="pano-wrap pano-left cardboard-half" style="width: 49%; border-radius:' . esc_attr( $radius ) . ';">
2241 <div id="center-pointer2' . esc_attr( $id ) . '" class="vr-pointer-container">
2242 <span class="center-pointer"></span>
2243 </div>
2244 </div>';
2245 $html .= '<div id="pano' . esc_attr( $id ) . '" class="pano-wrap pano-right" style=" width: 100%; border-radius:' . esc_attr( $radius ) . ';">';
2246 } else {
2247
2248 $html .= '<div id="pano2' . esc_attr( $id ) . '" class="pano-wrap pano-left" style="width: 49%; border-radius:' . esc_attr( $radius ) . ';"><div id="center-pointer2' . esc_attr( $id ) . '" class="vr-pointer-container"><span class="center-pointer"></span></div></div>';
2249
2250 $html .= '<div id="pano' . esc_attr( $id ) . '" class="pano-wrap pano-right" style="width: 100%; border-radius:' . esc_attr( $radius ) . ';">';
2251 }
2252 }
2253 // Vr mode transction scene to scene
2254 if ($status !== false && 'valid' == $status && $is_pro && wpvr_isMobileDevice() && $is_cardboard == 'true') {
2255 $html .= '<div id="center-pointer' . esc_attr( $id ) . '" class="vr-pointer-container" style="display:none"><span class="center-pointer"></span></div>';
2256 }
2257
2258
2259 //===company logo===//
2260 if (isset($postdata['cpLogoSwitch'])) {
2261 $cpLogoImg = $postdata['cpLogoImg'];
2262 $cpLogoContent = $postdata['cpLogoContent'];
2263 if ($postdata['cpLogoSwitch'] == 'on' && 'valid' == $status && $is_pro) {
2264 $html .= '<div id="cp-logo-controls">';
2265 $html .= '<div class="cp-logo-ctrl" id="cp-logo">';
2266 if ($cpLogoImg) {
2267 $html .= '<img loading="lazy" src="' . esc_attr( $cpLogoImg ) . '" alt="Company Logo">';
2268 }
2269
2270 if ($cpLogoContent) {
2271 $html .= '<div class="cp-info">' . esc_html( $cpLogoContent ) . '</div>';
2272 }
2273 $html .= '</div>';
2274 $html .= '</div>';
2275 }
2276 }
2277 //===company logo ends===//
2278
2279 //===Background Tour===//
2280 if (isset($postdata['bg_tour_enabler'])) {
2281
2282 $bg_tour_enabler = $postdata['bg_tour_enabler'];
2283 if ($bg_tour_enabler == 'on') {
2284 $bg_tour_navmenu = $postdata['bg_tour_navmenu'] ?? 'off';
2285 $bg_tour_title = $postdata['bg_tour_title'] ?? '';
2286 $bg_tour_subtitle = $postdata['bg_tour_subtitle'] ?? '';
2287
2288 if ($bg_tour_navmenu == 'on') {
2289 $menuLocations = get_nav_menu_locations();
2290 $menuID = $menuLocations['primary'];
2291 $primaryNav = wp_get_nav_menu_items($menuID);
2292
2293 if ($primaryNav) {
2294 $html .= '<div class="wpvr-navbar-container">';
2295 foreach ($primaryNav as $primaryNav_key => $primaryNav_value) {
2296 if ($primaryNav_value->menu_item_parent == "0") {
2297 $html .= '<li>';
2298 $html .= '<a href="' . esc_url( $primaryNav_value->url ) . '">' . esc_attr( $primaryNav_value->title ) . '</a>';
2299 $html .= '<ul class="wpvr-navbar-dropdown">';
2300 foreach ($primaryNav as $pm_key => $pm_value) {
2301 if ($pm_value->menu_item_parent == $primaryNav_value->ID) {
2302 $html .= '<li>';
2303 $html .= '<a href="' . esc_url( $pm_value->url ) . '">' . esc_attr( $pm_value->title ) . '</a>';
2304 $html .= '</li>';
2305 }
2306 }
2307 $html .= '</ul>';
2308 $html .= '</li>';
2309 }
2310 }
2311 $html .= '</div>';
2312 }
2313 }
2314
2315 if($is_pro && 'valid' == $status ){
2316 $html .= '<div class="wpvr-home-content">';
2317 $html .= '<div class="wpvr-home-title">' . $bg_tour_title . '</div>';
2318 $html .= '<div class="wpvr-home-subtitle">' . $bg_tour_subtitle . '</div>';
2319 $html .= '</div>';
2320 }
2321 }
2322 }
2323 //===Background Tour End===//
2324
2325 //===Custom Control===//
2326 if (isset($custom_control)) {
2327 if ($custom_control['panZoomInSwitch'] == "on" || $custom_control['panZoomOutSwitch'] == "on" || ( $custom_control['gyroSwitch'] == "on" && $custom_control['gyroscopeSwitch'] == "on") || $custom_control['backToHomeSwitch'] == "on") {
2328 $html .= '<div id="zoom-in-out-controls' . esc_attr( $id ) . '" class="zoom-in-out-controls">';
2329
2330 if ($custom_control['backToHomeSwitch'] == "on" && 'valid' == $status && $is_pro) {
2331 $html .= '<div class="ctrl" id="backToHome' . esc_attr( $id ) . '"><i class="' . $custom_control['backToHomeIcon'] . '" style="color:' . esc_attr( $custom_control['backToHomeColor'] ) . ';"></i></div>';
2332 }
2333
2334 if ($custom_control['panZoomInSwitch'] == "on" && 'valid' == $status && $is_pro) {
2335 $html .= '<div class="ctrl" id="zoom-in' . esc_attr( $id ) . '"><i class="' . $custom_control['panZoomInIcon'] . '" style="color:' . esc_attr( $custom_control['panZoomInColor'] ) . ';"></i></div>';
2336 }
2337
2338 if ($custom_control['panZoomOutSwitch'] == "on" && 'valid' == $status && $is_pro) {
2339 $html .= '<div class="ctrl" id="zoom-out' . esc_attr( $id ) . '"><i class="' . $custom_control['panZoomOutIcon'] . '" style="color:' . esc_attr( $custom_control['panZoomOutColor'] ) . ';"></i></div>';
2340 }
2341
2342 if ($custom_control['gyroSwitch'] == "on" && $custom_control['gyroscopeSwitch'] == "on" && 'valid' == $status && $is_pro) {
2343 $html .= '<div class="ctrl" id="gyroscope' . esc_attr( $id ) . '"><i class="' . $custom_control['gyroscopeIcon'] . '" style="color:' . esc_attr( $custom_control['gyroscopeColor'] ) . ';"></i></div>';
2344 }
2345
2346 $html .= '</div>';
2347 }
2348 //===zoom in out Control===//
2349
2350 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) {
2351
2352 //===Custom Control===//
2353 $html .= '<div class="controls" id="controls' . esc_attr( $id ) . '">';
2354
2355 if ($custom_control['panupSwitch'] == "on") {
2356 $html .= '<div class="ctrl pan-up" id="pan-up' . esc_attr( $id ) . '"><i class="' . $custom_control['panupIcon'] . '" style="color:' . esc_attr( $custom_control['panupColor'] ) . ';"></i></div>';
2357 }
2358
2359 if ($custom_control['panDownSwitch'] == "on") {
2360 $html .= '<div class="ctrl pan-down" id="pan-down' . esc_attr( $id ) . '"><i class="' . $custom_control['panDownIcon'] . '" style="color:' . esc_attr( $custom_control['panDownColor'] ) . ';"></i></div>';
2361 }
2362
2363 if ($custom_control['panLeftSwitch'] == "on") {
2364 $html .= '<div class="ctrl pan-left" id="pan-left' . esc_attr( $id ) . '"><i class="' . $custom_control['panLeftIcon'] . '" style="color:' . esc_attr( $custom_control['panLeftColor'] ) . ';"></i></div>';
2365 }
2366
2367 if ($custom_control['panRightSwitch'] == "on") {
2368 $html .= '<div class="ctrl pan-right" id="pan-right' . esc_attr( $id ) . '"><i class="' . $custom_control['panRightIcon'] . '" style="color:' . esc_attr( $custom_control['panRightColor'] ) . ';"></i></div>';
2369 }
2370
2371 if ($custom_control['panFullscreenSwitch'] == "on") {
2372 $html .= '<div class="ctrl fullscreen" id="fullscreen' . esc_attr( $id ) . '"><i class="' . $custom_control['panFullscreenIcon'] . '" style="color:' . esc_attr( $custom_control['panFullscreenColor'] ) . ';"></i></div>';
2373 }
2374 $html .= '</div>';
2375 }
2376 }
2377
2378 //===explainer button===//
2379 $html .= wpvr_render_explainer_button(
2380 isset( $custom_control ) ? $custom_control : null,
2381 $postdata,
2382 $is_pro,
2383 $autoload,
2384 $explainer_right,
2385 $id
2386 );
2387 //===explainer button end===//
2388
2389 //===Scene navigation Control===//
2390 if (isset($postdata['scene_navigation']) && $postdata['scene_navigation'] === 'on' && 'valid' == $status && $is_pro) {
2391 $html .= '<style>
2392 #et-boc .et-l .pnlm-controls-container,
2393 .pnlm-controls-container{
2394 top: 33px;
2395 }
2396
2397 #et-boc .et-l .zoom-in-out-controls,
2398 .zoom-in-out-controls {
2399 top: 37px;
2400 }
2401 </style>';
2402 $html .= '<div id="custom-scene-navigation' . esc_attr( $id ) . '" class="custom-scene-navigation">
2403 <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>
2404 </div>
2405
2406 <div id="custom-scene-navigation-nav' . esc_attr( $id ) . '" class="custom-scene-navigation-nav">
2407 <ul></ul>
2408 </div>
2409 ';
2410 }
2411
2412 //===Scene navigation Control===//
2413
2414 /**
2415 * Generic Form Handler
2416 * Renders a generic form with modal functionality if enabled in post data
2417 *
2418 * @param array $postdata Array containing form configuration data
2419 * @param string $id Unique identifier for the form instance
2420 * @return string $html Generated HTML content
2421 */
2422
2423 // Check if generic form is enabled and validate the setting
2424 if (isset($postdata["genericform"]) && $postdata["genericform"] === 'on' && 'valid' == $status && $is_pro) {
2425
2426 // Process shortcode with fallback handling
2427 $shortcode_content = '';
2428 if (isset($postdata["genericformshortcode"]) && !empty(trim($postdata["genericformshortcode"]))) {
2429 $shortcode_content = do_shortcode( $postdata["genericformshortcode"] );
2430 } else {
2431 $shortcode_content = '<p class="error-message">No shortcode found.</p>';
2432 }
2433
2434 // Generate the form trigger button
2435 $html .= '<div class="generic_form_button" id="generic_form_button_' . esc_attr( $id ) . '">';
2436 $html .= '<div class="generic-form-icon" title ="Generic Form" id="generic_form_target_' . esc_attr( $id ) . '"><i class="fab fa-wpforms" style="color:#f7fffb;"></i></div>';
2437 $html .= '</div>';
2438
2439 // Generate the modal form container
2440 $html .= '<div class="wpvr-generic-form" id="wpvr-generic-form' . esc_attr( $id ) . '" style="display: none">';
2441 $html .= '<span class="close-generic-form"><i class="fa fa-times"></i></span>';
2442 $html .= '<div class="generic-form-container">' . $shortcode_content . '</div>';
2443 $html .= '</div>';
2444 }
2445 //=====custom generic form=====//
2446
2447 //===Floor map button===//
2448 $status = get_option('wpvr_edd_license_status');
2449 if ($status !== false && 'valid' == $status && $is_pro) {
2450 if ($floor_plan_enable == "on" && !empty($floor_plan_image)) {
2451 $html .= '<div class="floor_map_button" id="floor_map_button_' . esc_attr( $id ) . '" style="right:' . esc_attr( $floor_map_right ) . '">';
2452 $html .= '<div class="ctrl" id="floor_map_target_' . esc_attr( $id ) . '"><i class="fas fa-map" style="color:#f7fffb;"></i></div>';
2453 $html .= '</div>';
2454 }
2455 }
2456
2457 //===floor map button===//
2458 if ($vrgallery && 'valid' == $status && $is_pro ) {
2459 //===Carousal setup===//
2460 $size = '';
2461 if ($vrgallery_icon_size) {
2462 $size = 'vrg-icon-size-large';
2463 }
2464 $html .= '<div id="vrgcontrols' . esc_attr( $id ) . '" class="vrgcontrols">';
2465
2466 $html .= '<div class="vrgctrl' . esc_attr( $id ) . ' vrbounce ' . esc_attr( $size ) . '">';
2467 $html .= '</div>';
2468 $html .= '</div>';
2469
2470 $html .= '<div id="sccontrols' . $id . '" class="scene-gallery vrowl-carousel owl-theme ">';
2471 if (isset($panodata["scene-list"])) {
2472 foreach ($panodata["scene-list"] as $panoscenes) {
2473 $scene_key = $panoscenes['scene-id'];
2474 if ($vrgallery_title == 'on') {
2475 $scene_key_title = isset($panoscenes['scene-ititle']) ? esc_html($panoscenes['scene-ititle']) : '';
2476 } else {
2477 $scene_key_title = "";
2478 }
2479
2480 if ($panoscenes['scene-type'] == 'cubemap') {
2481 $img_src_url = $panoscenes['scene-attachment-url-face0'];
2482 } else {
2483 $img_src_url = $panoscenes['scene-attachment-url'];
2484 }
2485
2486
2487 $src_to_id = attachment_url_to_postid($img_src_url);
2488 $thumbnail_array = wp_get_attachment_image_src($src_to_id, 'thumbnail');
2489 if ($thumbnail_array) {
2490 $thumbnail = $thumbnail_array[0];
2491 } else {
2492 $thumbnail = $img_src_url;
2493 }
2494 if( isset($postdata['tourLayout']['layout']) && 'layout1' !== $postdata['tourLayout']['layout']) {
2495 $html .= '<ul><li title="Double 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_' . $id . '" src="' . $thumbnail . '"></li></ul>';
2496 }else {
2497 $html .= '<ul><li title="Double click to view scene"><img loading="lazy" class="scctrl" id="' . $scene_key . '_gallery_' . $id . '" src="' . $thumbnail . '"><span class="scene-title" title="' . esc_attr($scene_key_title) . '">' . $scene_key_title . '</span></li></ul>';
2498 }
2499 }
2500 }
2501 $html .= '</div>';
2502 $html .= '
2503 <div class="owl-nav wpvr_slider_nav">
2504 <button type="button" role="presentation" class="owl-prev wpvr_owl_prev">
2505 <div class="nav-btn prev-slide"><i class="fa fa-angle-left"></i></div>
2506 </button>
2507 <button type="button" role="presentation" class="owl-next wpvr_owl_next">
2508 <div class="nav-btn next-slide"><i class="fa fa-angle-right"></i></div>
2509 </button>
2510 </div>
2511 ';
2512 //===Carousal setup end===//
2513 }
2514
2515 $bg_music = isset($postdata['bg_music']) ? $postdata['bg_music'] : 'off';
2516 $bg_music_url = isset($postdata['bg_music_url']) ? $postdata['bg_music_url'] : '';
2517 $autoplay_bg_music = isset($postdata['autoplay_bg_music']) ? $postdata['autoplay_bg_music'] : 'off';
2518 $loop_bg_music = isset($postdata['loop_bg_music']) ? $postdata['loop_bg_music'] : 'off';
2519
2520 $bg_loop = ($loop_bg_music === 'on') ? 'loop' : '';
2521 $autoplay_attr = ($autoplay_bg_music === 'on') ? 'autoplay' : '';
2522 $audio_muted_attr = ($autoplay_bg_music === 'on') ? 'muted' : '';
2523 $audio_icon_class = 'fa-volume-mute'; // Always start with mute icon
2524
2525 if ($bg_music === 'on' && 'valid' == $status && $is_pro) {
2526 $html .= '<div id="adcontrol' . esc_attr( $id ) . '" class="adcontrol" style="right:' . esc_attr( $audio_right ) . '">';
2527 $html .= '<audio id="vrAudio' . esc_attr($id) . '" class="vrAudioDefault" data-autoplay="' . esc_attr($autoplay_bg_music) . '" onended="audionEnd' . esc_attr($id) . '()" ' . $autoplay_attr . ' ' . $audio_muted_attr . ' ' . $bg_loop . '>
2528 <source src="' . esc_url($bg_music_url) . '" type="audio/mpeg">
2529 Your browser does not support the audio element.
2530 </audio>';
2531 $html .= '<button onclick="playPause' . esc_attr($id) . '()" class="ctrl audio_control" id="audio_control' . esc_attr($id) . '">
2532 <i id="vr-volume' . esc_attr($id) . '" class="wpvrvolumeicon' . esc_attr($id) . ' fas ' . esc_attr($audio_icon_class) . '" style="color:#fff;"></i>
2533 </button>';
2534 $html .= '</div>';
2535 }
2536
2537 //===Explainer video section===//
2538 $explainerContent = "";
2539 if (isset($postdata['explainerContent'])) {
2540 $explainerContent = wpvr_sanitize_iframe_only($postdata['explainerContent']);
2541 }
2542 $html .= '<div class="explainer" id="explainer' . esc_attr( $id ) . '" style="display: none">';
2543 $html .= '<span class="close-explainer-video"><i class="fa fa-times"></i></span>';
2544 $html .= '' . $explainerContent . '';
2545 $html .= '</div>';
2546 //===Explainer video section End===//
2547
2548 //===Floor plan section===//
2549 $floor_map_image = "";
2550 $floor_map_pointer = array();
2551 $floor_map_scene_id = '';
2552 $floor_plan_custom_color = '#cca92c';
2553 $floor_plan_direction_indicator = isset($postdata['floor_plan_direction_indicator']) ? $postdata['floor_plan_direction_indicator'] : 'on';
2554
2555 if (isset($postdata['floor_plan_attachment_url'])) {
2556 $floor_map_image = $postdata['floor_plan_attachment_url'];
2557 $floor_map_pointer = $postdata['floor_plan_pointer_position'];
2558 $floor_map_scene_id = $postdata['floor_plan_data_list'];
2559 $floor_plan_custom_color = $postdata['floor_plan_custom_color'];
2560 }
2561 $html .= '<div class="wpvr-floor-map" id="wpvr-floor-map' . $id . '" style="display: none">';
2562 $html .= '<span class="close-floor-map-plan"><i class="fa fa-times"></i></span>';
2563 $html .= '<img loading="lazy" src="' . $floor_map_image . '">';
2564
2565 foreach ($floor_map_pointer as $key => $pointer_position) {
2566 $html .= '<div class="floor-plan-pointer ui-draggable ui-draggable-handle" scene_id = "' . esc_attr( $floor_map_scene_id[$key]->value ) . '" id="' . esc_attr( $pointer_position->id ) . '" data-top="' . esc_attr( $pointer_position->data_top ) . '" data-left="' . esc_attr( $pointer_position->data_left ) . '" style="' . esc_attr( $pointer_position->style ) . '">
2567
2568 <svg class="floor-pointer-circle" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2569 <circle cx="12" cy="12" r="11.5" stroke="' . esc_attr( $floor_plan_custom_color ) . '"/>
2570 <circle cx="12" cy="12" r="5" fill="' . esc_attr( $foreground_color_pointer ) . '"/>
2571 </svg>';
2572
2573 // Only add the floor pointer flash SVG if floor_plan_direction_indicator is "on"
2574 if ($floor_plan_direction_indicator === "on") {
2575 $html .= '<svg class="floor-pointer-flash" width="54" height="35" viewBox="0 0 54 35" fill="none" xmlns="http://www.w3.org/2000/svg">
2576 <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)"/>
2577 <defs>
2578 <linearGradient id="paint0_linear_1_10" x1="27" y1="4.59807e-08" x2="26.5" y2="28" gradientUnits="userSpaceOnUse">
2579 <stop stop-color="' . esc_attr( $floor_plan_custom_color ) . '" stop-opacity="0"/>
2580 <stop offset="1" stop-color="' . esc_attr( $floor_plan_custom_color ) . '"/>
2581 </linearGradient>
2582 </defs>
2583 </svg>';
2584 }
2585 $html .= '</div>';
2586 }
2587 $html .= '</div>';
2588 //===Floor plan section===//
2589
2590 $html .= '<div class="wpvr-hotspot-tweak-contents-wrapper" style="display: none">';
2591 $html .= '<i class="fa fa-times cross" data-id="' . esc_attr( $id ) . '"></i>';
2592 $html .= '<div class="wpvr-hotspot-tweak-contents-flex">';
2593 $html .= '<div class="wpvr-hotspot-tweak-contents">';
2594 ob_start();
2595 do_action('wpvr_hotspot_tweak_contents', $scene_data);
2596 $hotspot_content = ob_get_clean();
2597 $html .= $hotspot_content;
2598 $html .= '</div>';
2599 $html .= '</div>';
2600 $html .= '</div>';
2601
2602 $html .= '<div class="custom-ifram-wrapper" style="display: none;">';
2603 $html .= '<i class="fa fa-times cross" data-id="' . esc_attr( $id ) . '"></i>';
2604 $html .= '<div class="custom-ifram-flex">';
2605 $html .= '<div class="custom-ifram">';
2606 $html .= '</div>';
2607 $html .= '</div>';
2608 $html .= '</div>';
2609 $html .= '</div>';
2610 $html .= '</div>';
2611
2612
2613 if ("fullwidth" == $width) {
2614 $html .= '</div>';
2615 }
2616 if ($status !== false && 'valid' == $status && $is_pro) {
2617 $call_to_action = isset($postdata['calltoaction']) ? $postdata['calltoaction'] : 'off';
2618 if ('on' == $call_to_action) {
2619 $buttontext = isset($postdata['buttontext']) ? $postdata['buttontext'] : '';
2620 $buttonurl = isset($postdata['buttonurl']) ? $postdata['buttonurl'] : '';
2621 $cta_btn_style = isset($postdata['button_configuration']) ? $postdata['button_configuration'] : array();
2622
2623 $button_open_new_tab = isset($cta_btn_style['button_open_new_tab']) ? $cta_btn_style['button_open_new_tab'] : "off";
2624 $target = '_self';
2625 $button_position = isset($cta_btn_style['button_position']) ? $cta_btn_style['button_position'] : "";
2626 $background_color = isset($cta_btn_style['button_background_color']) ? $cta_btn_style['button_background_color'] : "";
2627 $color = isset($cta_btn_style['button_font_color']) ? $cta_btn_style['button_font_color'] : "";
2628 $font_size = isset($cta_btn_style['button_font_size']) ? $cta_btn_style['button_font_size'] : "";
2629 $font_weight = isset($cta_btn_style['button_font_weight']) ? $cta_btn_style['button_font_weight'] : "";
2630 $text_align = isset($cta_btn_style['button_alignment']) ? $cta_btn_style['button_alignment'] : "";
2631 $text_transform = isset($cta_btn_style['button_transform']) ? $cta_btn_style['button_transform'] : "";
2632 $font_style = isset($cta_btn_style['button_text_style']) ? $cta_btn_style['button_text_style'] : "";
2633 $text_decoration = isset($cta_btn_style['button_text_decoration']) ? $cta_btn_style['button_text_decoration'] : "";
2634 $line_height = isset($cta_btn_style['button_line_height']) ? $cta_btn_style['button_line_height'] : "";
2635 $letter_spacing = isset($cta_btn_style['button_letter_spacing']) ? $cta_btn_style['button_letter_spacing'] : "";
2636 $word_spacing = isset($cta_btn_style['button_word_spacing']) ? $cta_btn_style['button_word_spacing'] : "";
2637
2638 $border_width = isset($cta_btn_style['button_border_width']) ? $cta_btn_style['button_border_width'] : "";
2639 $border_style = isset($cta_btn_style['button_border_style']) ? $cta_btn_style['button_border_style'] : "";
2640 $border_color = isset($cta_btn_style['button_border_color']) ? $cta_btn_style['button_border_color'] : "";
2641 $border_radius = isset($cta_btn_style['button_border_radius']) ? $cta_btn_style['button_border_radius'] : "";
2642
2643 $button_pt = isset($cta_btn_style['button_pt']) ? $cta_btn_style['button_pt'] : "";
2644 $button_pr = isset($cta_btn_style['button_pr']) ? $cta_btn_style['button_pr'] : "";
2645 $button_pb = isset($cta_btn_style['button_pb']) ? $cta_btn_style['button_pb'] : "";
2646 $button_pl = isset($cta_btn_style['button_pl']) ? $cta_btn_style['button_pl'] : "";
2647
2648 if ($button_open_new_tab == 'on') {
2649 $target = '_blank';
2650 }
2651 $style = 'background-color: ' . esc_attr( $background_color ) . ';
2652 color: ' . esc_attr( $color ) . ';
2653 font-size: ' . esc_attr( $font_size ) . 'px;
2654 font-weight: ' . esc_attr( $font_weight ) . ';
2655 text-align: center;
2656 display: inline-block;
2657 text-transform: ' . esc_attr( $text_transform ) . ';
2658 font-style: ' . esc_attr( $font_style ) . ';
2659 text-decoration: ' . esc_attr( $text_decoration ) . ';
2660 line-height: ' . esc_attr( $line_height ) . ';
2661 letter-spacing: ' . esc_attr( $letter_spacing ) . 'px;
2662 word-spacing: ' . esc_attr( $word_spacing ) . 'px;
2663 border: ' . esc_attr( $border_width ) . 'px ' . esc_attr( $border_style ) . ' ' . esc_attr( $border_color ) . ';
2664 border-radius: ' . esc_attr( $border_radius ). 'px;
2665 padding: ' . esc_attr( $button_pt ) . 'px ' . esc_attr( $button_pr ) . 'px ' . esc_attr( $button_pb ) . 'px ' . esc_attr( $button_pl ) . 'px;
2666 ';
2667 $html .= '<div class="wpvr-call-to-action-button position-' . esc_attr( $text_align ) . '" style="max-width:' . esc_attr( $width ) . esc_attr( $width_unit ) . '">
2668 <a href="' . esc_url( $buttonurl ) . '" style="' . esc_attr( $style ) . '" target="' . esc_attr( $target ) . '">' . esc_attr( $buttontext ) . '</a>
2669 </div>';
2670 }
2671 }
2672
2673 //script started
2674
2675 $html .= '<script>';
2676 if (isset($postdata['bg_music']) && $bg_music == 'on' && 'valid' == $status && $is_pro) {
2677 $html .= '
2678 var x' . $id . ' = document.getElementById("vrAudio' . $id . '");
2679 var playing' . $id . ' = false;
2680 var autoplaySupported' . $id . ' = false;
2681 var alertShown' . $id . ' = false;
2682 var autoplayChecked' . $id . ' = false;
2683
2684 function playPause' . $id . '() {
2685 if (playing' . $id . ') {
2686 jQuery("#vr-volume' . $id . '").removeClass("fas fa-volume-up").addClass("fas fa-volume-mute");
2687 x' . $id . '.pause();
2688 jQuery("#audio_control' . $id . '").attr("data-play", "off");
2689 playing' . $id . ' = false;
2690 } else {
2691 x' . $id . '.muted = false;
2692 x' . $id . '.play().then(function() {
2693 jQuery("#vr-volume' . $id . '").removeClass("fas fa-volume-mute").addClass("fas fa-volume-up");
2694 jQuery("#audio_control' . $id . '").attr("data-play", "on");
2695 playing' . $id . ' = true;
2696 }).catch(function(e) {
2697 console.log("Play failed:", e);
2698 });
2699 }
2700 }
2701
2702 function audionEnd' . $id . '() {
2703 playing' . $id . ' = false;
2704 jQuery("#vr-volume' . $id . '").removeClass("fas fa-volume-up").addClass("fas fa-volume-mute");
2705 jQuery("#audio_control' . $id . '").attr("data-play", "off");
2706 }
2707
2708 x' . $id . '.addEventListener("ended", audionEnd' . $id . ');';
2709
2710 if ($autoplay_bg_music == 'on') {
2711 $html .= '
2712
2713 x' . $id . '.addEventListener("loadeddata", function() {
2714 if (!autoplayChecked' . $id . ') {
2715 checkAutoplayStatus' . $id . '();
2716 }
2717 });
2718
2719 x' . $id . '.addEventListener("canplay", function() {
2720 if (!autoplayChecked' . $id . ') {
2721 checkAutoplayStatus' . $id . '();
2722 }
2723 });
2724
2725 x' . $id . '.addEventListener("canplaythrough", function() {
2726 if (!autoplayChecked' . $id . ') {
2727 checkAutoplayStatus' . $id . '();
2728 }
2729 });
2730
2731 setTimeout(function() {
2732 if (!autoplayChecked' . $id . ') {
2733 checkAutoplayStatus' . $id . '();
2734 }
2735 }, 1000);
2736
2737 x' . $id . '.addEventListener("play", function() {
2738 jQuery("#vr-volume' . $id . '").removeClass("fas fa-volume-mute").addClass("fas fa-volume-up");
2739 jQuery("#audio_control' . $id . '").attr("data-play", "on");
2740 playing' . $id . ' = true;
2741 });
2742
2743 x' . $id . '.addEventListener("pause", function() {
2744 if (!playing' . $id . ') {
2745 jQuery("#vr-volume' . $id . '").removeClass("fas fa-volume-up").addClass("fas fa-volume-mute");
2746 jQuery("#audio_control' . $id . '").attr("data-play", "off");
2747 }
2748 });
2749
2750 function checkAutoplayStatus' . $id . '() {
2751 autoplayChecked' . $id . ' = true;
2752
2753 x' . $id . '.muted = true;
2754 var playPromise = x' . $id . '.play();
2755
2756 if (playPromise !== undefined) {
2757 playPromise.then(function () {
2758 if (x' . $id . '.muted || x' . $id . '.volume === 0) {
2759 handleAutoplayBlocked' . $id . '();
2760 } else {
2761 autoplaySupported' . $id . ' = true;
2762 jQuery("#vr-volume' . $id . '").removeClass("fas fa-volume-mute").addClass("fas fa-volume-up");
2763 jQuery("#audio_control' . $id . '").attr("data-play", "on");
2764 playing' . $id . ' = true;
2765 }
2766 }).catch(function () {
2767 handleAutoplayBlocked' . $id . '();
2768 });
2769 } else {
2770 setTimeout(function () {
2771 if (x' . $id . '.paused || x' . $id . '.currentTime === 0) {
2772 handleAutoplayBlocked' . $id . '();
2773 } else {
2774 autoplaySupported' . $id . ' = true;
2775 jQuery("#vr-volume' . $id . '").removeClass("fas fa-volume-mute").addClass("fas fa-volume-up");
2776 jQuery("#audio_control' . $id . '").attr("data-play", "on");
2777 playing' . $id . ' = true;
2778 }
2779 }, 300);
2780 }
2781 }
2782
2783 function handleAutoplayBlocked' . $id . '() {
2784 autoplaySupported' . $id . ' = false;
2785 if (!alertShown' . $id . ') {
2786 alert("Autoplay is not supported in your browser. Please click the audio button to play music.");
2787 alertShown' . $id . ' = true;
2788 }
2789
2790 x' . $id . '.pause();
2791 x' . $id . '.currentTime = 0;
2792 x' . $id . '.muted = true;
2793
2794 jQuery("#vr-volume' . $id . '").removeClass("fas fa-volume-up").addClass("fas fa-volume-mute");
2795 jQuery("#audio_control' . $id . '").attr("data-play", "off");
2796
2797 document.getElementById("pano' . $id . '").addEventListener("click", musicPlay' . $id . ');
2798 document.addEventListener("touchstart", musicPlay' . $id . ', { once: true });
2799 document.addEventListener("click", musicPlay' . $id . ', { once: true });
2800 }
2801
2802 function musicPlay' . $id . '() {
2803 x' . $id . '.muted = false;
2804 var playPromise = x' . $id . '.play();
2805
2806 if (playPromise !== undefined) {
2807 playPromise.then(function () {
2808 jQuery("#vr-volume' . $id . '").removeClass("fas fa-volume-mute").addClass("fas fa-volume-up");
2809 jQuery("#audio_control' . $id . '").attr("data-play", "on");
2810 playing' . $id . ' = true;
2811 }).catch(function(e) {
2812 console.log("Play failed:", e);
2813 });
2814 } else {
2815 setTimeout(function () {
2816 if (!x' . $id . '.paused) {
2817 jQuery("#vr-volume' . $id . '").removeClass("fas fa-volume-mute").addClass("fas fa-volume-up");
2818 jQuery("#audio_control' . $id . '").attr("data-play", "on");
2819 playing' . $id . ' = true;
2820 }
2821 }, 100);
2822 }
2823
2824 document.getElementById("pano' . $id . '").removeEventListener("click", musicPlay' . $id . ');
2825 document.removeEventListener("touchstart", musicPlay' . $id . ');
2826 document.removeEventListener("click", musicPlay' . $id . ');
2827 }
2828 ';
2829 }
2830 }
2831 $html .= 'jQuery(document).ready(function() {';
2832 $html .= 'var response = ' . $response . ';';
2833 $html .= 'var scenes = response[1];';
2834 $html .= 'if(scenes) {';
2835 $html .= 'var scenedata = scenes.scenes;';
2836 $html .= 'for(var i in scenedata) {';
2837 $html .= 'var scenehotspot = scenedata[i].hotSpots;';
2838 $html .= 'for(var i = 0; i < scenehotspot.length; i++) {';
2839 $html .= 'if(scenehotspot[i].type === "info") {';
2840 $html .= ' scenehotspot[i]["clickHandlerFunc"] = wpvrhotspot;';
2841 $html .= '} else if(scenehotspot[i].type === "scene") {';
2842 $html .= ' scenehotspot[i]["clickHandlerArgs"] = scenehotspot[i]["text"];';
2843 $status = get_option('wpvr_edd_license_status');
2844 if ($status !== false && $status == 'valid') {
2845 $html .='if(wpvr_public.is_pro_active) {';
2846 $html .= ' scenehotspot[i]["clickHandlerFunc"] = wpvrhotspotscene;';
2847 $html .='}';
2848 }
2849 $html .= '}';
2850 if (wpvr_isMobileDevice() && get_option('dis_on_hover') == "true") {
2851 } else {
2852 $html .= 'if(scenehotspot[i]["createTooltipArgs"] != "") {';
2853 $html .= 'scenehotspot[i]["createTooltipFunc"] = wpvrtooltip;';
2854 $html .= '}';
2855 }
2856 $html .= '}';
2857 $html .= '}';
2858 $html .= '}';
2859 $html .= 'var panoshow' . $id . ' = pannellum.viewer(response[0]["panoid"], scenes);';
2860 $html .= '
2861 if(!wpvr_public.is_pro_active || !wpvr_public.is_license_active) {
2862 panoshow' . $id . '.on("load", function() {
2863 jQuery(".pnlm-panorama-info").hide();
2864 jQuery(".pnlm-compass").hide();
2865 });
2866
2867 panoshow' . $id . '.on("scenechange", function() {
2868 jQuery(".pnlm-panorama-info").hide();
2869 jQuery(".pnlm-compass").hide();
2870 });
2871 }';
2872
2873 //===Dplicate mode only for vr mode===//
2874 $response2 = json_decode($response);
2875 $response2[1]->compass = false;
2876 $response2[1]->autoRotate = false;
2877 $response = json_encode($response2);
2878 $html .= 'var response_duplicate = ' . $response . ';';
2879 $html .= 'var scenes_duplicate = response_duplicate[1];';
2880
2881 $html .= 'if(scenes_duplicate) {';
2882 $html .= 'var scenedata = scenes_duplicate.scenes;';
2883 $html .= 'for(var i in scenedata) {';
2884 $html .= 'var scenehotspot = scenedata[i].hotSpots;';
2885 $html .= 'for(var i = 0; i < scenehotspot.length; i++) {';
2886 $html .= 'if(scenehotspot[i]["clickHandlerArgs"] != "") {';
2887 $html .= 'scenehotspot[i]["clickHandlerFunc"] = wpvrhotspot;';
2888 $html .= '}';
2889 if (wpvr_isMobileDevice() && get_option('dis_on_hover') == "true") {
2890 } else {
2891 $html .= 'if(scenehotspot[i]["createTooltipArgs"] != "") {';
2892 $html .= 'scenehotspot[i]["createTooltipFunc"] = wpvrtooltip;';
2893 $html .= '}';
2894 }
2895 $html .= '}';
2896 $html .= '}';
2897 $html .= '}';
2898 $html .= 'var vr_mode = "off";';
2899 $status = get_option('wpvr_edd_license_status');
2900 if ($status !== false && 'valid' == $status && $is_pro) {
2901 $html .= 'var panoshow2' . $id . ' = pannellum.viewer("pano2' . $id . '", scenes_duplicate);';
2902
2903 // Show Cardboard Mode in Tour
2904 $html .= '
2905 var tim;
2906 var im = 0;
2907 var active_scene = "' . $default_scene . '";
2908 var c_time;
2909 c_time = new Date();
2910 var timer = c_time.getTime() + 2000;
2911 function panoShowCardBoardOnTrigger(data){
2912 if(scenes_duplicate) {
2913 var scenedata = scenes_duplicate.scenes;
2914 for(var i in scenedata) {
2915 if(active_scene === i) {
2916 var scenehotspot = scenedata[i].hotSpots;
2917 for(var j in scenehotspot) {
2918 var plusFiveYaw = Math.round(scenehotspot[j].yaw) + 5;
2919 var minusFiveYaw = Math.round(scenehotspot[j].yaw) - 5;
2920 var plusFivePitch = Math.round(scenehotspot[j].pitch) + 5;
2921 var minusFivePitch = Math.round(scenehotspot[j].pitch) - 5;
2922 var firstCondition = ( Math.round(data.pitch) > minusFivePitch) && (Math.round(data.pitch) < plusFivePitch) ;
2923 var secCondition = (Math.round(data.yaw) > minusFiveYaw) && (Math.round(data.yaw) < plusFiveYaw);
2924 if(( Math.round(data.pitch) > minusFivePitch) && (Math.round(data.pitch) < plusFivePitch) ){
2925 if((Math.round(data.yaw) > minusFiveYaw) && (Math.round(data.yaw) < plusFiveYaw)){
2926 jQuery(".center-pointer").addClass("wpvr-pluse-effect")
2927 var getScene = scenehotspot[j].sceneId;
2928 if(scenehotspot[j].type == "scene"){
2929 panoshow' . $id . '.loadScene(getScene);
2930 panoshow2' . $id . '.loadScene(getScene);
2931 // var inside_current_time_object = new Date();
2932 // var inside_timer = inside_current_time_object.getTime();
2933 // if(inside_timer > timer) {
2934 // panoshow' . $id . '.loadScene(getScene);
2935 // panoshow2' . $id . '.loadScene(getScene);
2936 // jQuery(".center-pointer").removeClass("wpvr-pluse-effect")
2937 // }
2938 }else{
2939 jQuery(".center-pointer").removeClass("wpvr-pluse-effect")
2940 }
2941 }
2942 else {
2943 jQuery(".center-pointer").removeClass("wpvr-pluse-effect")
2944 c_time = new Date();
2945 timer = c_time.getTime() + 2000;
2946 }
2947 }
2948 else {
2949 c_time = new Date();
2950 timer = c_time.getTime() + 2000;
2951 }
2952 }
2953 }
2954 }
2955 }
2956 };
2957
2958 function vrDeviseOrientation(){
2959 var data = {
2960 pitch: panoshow' . $id . '.getPitch(),
2961 yaw: panoshow' . $id . '.getYaw(),
2962 };
2963 panoShowCardBoardOnTrigger(data);
2964 }
2965 ';
2966 $html .= '
2967
2968 function requestFullScreen(){
2969 var elem = document.getElementById("master-container");
2970 if (elem.requestFullscreen) {
2971 elem.requestFullscreen();
2972 } else if (elem.webkitRequestFullscreen) { /* Safari */
2973 elem.webkitRequestFullscreen();
2974 } else if (elem.msRequestFullscreen) { /* IE11 */
2975 elem.msRequestFullscreen();
2976 }
2977 }
2978 function requestExitFullscreen(){
2979 var elem = document.getElementById("master-container");
2980 if (document.exitFullscreen) {
2981 document.exitFullscreen();
2982 } else if (document.webkitExitFullscreen) { /* Safari */
2983 document.webkitExitFullscreen();
2984 } else if (document.msExitFullscreen) { /* IE11 */
2985 document.msExitFullscreen();
2986 }
2987 }
2988 jQuery(document).on("click",".fullscreen-button .expand",function() {
2989 jQuery(this).hide()
2990 jQuery(this).parent().find(".compress").show()
2991 requestFullScreen()
2992 screen.orientation.lock("landscape-primary")
2993 .then(function() {
2994 })
2995 .catch(function(error) {
2996 alert("Not Supported for this devise");
2997 });
2998
2999 });
3000 jQuery(document).on("click",".fullscreen-button .compress",function() {
3001 jQuery(this).hide()
3002 jQuery(this).parent().find(".expand").show()
3003 requestExitFullscreen()
3004 screen.orientation.unlock();
3005
3006 });
3007
3008 let onLoadAnalytics = false;
3009 let sceneLoadAnalytics = false;
3010 function storeAnalyticsData(data) {
3011 if(wpvr_public.is_pro_active) {
3012 jQuery.ajax({
3013 url: wpvrAnalyticsObj.ajaxUrl,
3014 type: "POST",
3015 data: {
3016 action: "store_scene_hotspot_data",
3017 scene_id: data.scene_id,
3018 tour_id: data.tour_id,
3019 type: data.type,
3020 hotspot_id: data.hotspot_id || "",
3021 user_agent: navigator.userAgent,
3022 device_type: getDeviceType() || "desktop",
3023 nonce: wpvrAnalyticsObj.nonce,
3024 },
3025 success: function (response) {
3026 console.log("Data stored successfully");
3027 },
3028 error: function (error) {
3029 console.log("Error in storing data");
3030 }
3031 });
3032 }
3033 }
3034
3035 function getDeviceType() {
3036 const userAgent = navigator.userAgent.toLowerCase();
3037
3038 if (/mobile|android|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent)) {
3039 return "mobile";
3040 } else if (/tablet|ipad/i.test(userAgent)) {
3041 return "tablet";
3042 } else {
3043 return "desktop";
3044 }
3045 }
3046
3047 panoshow' . $id . '.on("scenechange", function(scene) {
3048 onLoadAnalytics = true;
3049 sceneLoadAnalytics = true;
3050 let scene_id = scene;
3051 let tour_id = ' . $id . ';
3052 let type = "scene";
3053 let hotspot_id = "";
3054 let user_agent = navigator.userAgent;
3055 let device_type = getDeviceType() ? getDeviceType() : "desktop";
3056 storeAnalyticsData({
3057 scene_id: scene_id,
3058 tour_id: tour_id,
3059 type: type,
3060 hotspot_id: hotspot_id,
3061 user_agent: user_agent,
3062 device_type: device_type,
3063 });
3064 if(!wpvr_public.is_pro_active) {
3065 jQuery(".pnlm-panorama-info").hide();
3066 setTimeout(function() {
3067 jQuery(".pnlm-panorama-info").each(function() {
3068 jQuery(this).hide();
3069 });
3070 }, 500);
3071 }
3072 });
3073
3074 panoshow' . $id . '.on("load", function() {
3075 let scene_id = panoshow' . $id . '.getScene();
3076 let tour_id = ' . $id . ';
3077 let type = "scene";
3078 let hotspot_id = "";
3079 let user_agent = navigator.userAgent;
3080 let device_type = getDeviceType() ? getDeviceType() : "desktop";
3081 if(!onLoadAnalytics && !sceneLoadAnalytics){
3082 storeAnalyticsData({
3083 scene_id: scene_id,
3084 tour_id: tour_id,
3085 type: type,
3086 hotspot_id: hotspot_id,
3087 user_agent: user_agent,
3088 device_type: device_type,
3089 });
3090 }
3091 if(!wpvr_public.is_pro_active) {
3092 jQuery(".pnlm-panorama-info").hide();
3093 setTimeout(function() {
3094 jQuery(".pnlm-panorama-info").each(function() {
3095 jQuery(this).hide();
3096 });
3097 }, 500);
3098 }
3099 })
3100
3101 function wpvrhotspotscene(hotSpotDiv, args) {
3102 onLoadAnalytics = true;
3103 let scene_id = panoshow' . $id . '.getScene();
3104 let tour_id = ' . $id . ';
3105 let type = "hotspot";
3106 let hotspot_id = args;
3107 let user_agent = navigator.userAgent;
3108 let device_type = getDeviceType() ? getDeviceType() : "desktop";
3109 storeAnalyticsData({
3110 scene_id: scene_id,
3111 tour_id: tour_id,
3112 type: type,
3113 hotspot_id: hotspot_id,
3114 user_agent: user_agent,
3115 device_type: device_type
3116 });
3117 }';
3118
3119 $html .= '
3120 panoshow' . $id . '.on("scenechange", function (scene){
3121 jQuery(".center-pointer").removeClass("wpvr-pluse-effect")
3122 active_scene = scene;
3123 });
3124 var compassBlock = "";
3125 var infoBlock = "";
3126 var getValue = "";
3127 jQuery(document).on("click",".vr_mode_change' . $id . '",function (){
3128
3129 jQuery("#pano2' . $id . ' .pnlm-load-button").trigger("click");
3130 jQuery("#pano' . $id . ' .pnlm-load-button").trigger("click");
3131
3132 getValue = jQuery(this).val();
3133 var getParent = jQuery(this).parent().parent();
3134 var fullWidthParent = getParent.parent();
3135 var compass = getParent.find("#pano2' . $id . ' .pnlm-compass.pnlm-controls.pnlm-control").css("display");
3136 var panoInfo = getParent.find("#pano' . $id . ' .pnlm-panorama-info").css("display");
3137 if(compass == "block"){
3138 compassBlock = "block";
3139 }
3140 if(panoInfo == "block"){
3141 infoBlock = "block";
3142 }
3143 if (getValue == "off") {
3144 requestFullScreen()
3145 screen.orientation.lock("landscape-primary")
3146 .then(function() {
3147 })
3148 .catch(function(error) {
3149 alert("VR Glass Mode not supported in this device");
3150 });
3151 getParent.find(".pano-wrap").addClass("wpvr-cardboard-disable-event");
3152 // localStorage.setItem("vr_mode", "on");
3153 vr_mode = "on";
3154 jQuery(".vr-mode-title").show();
3155 jQuery(this).val("on");
3156 getParent.find("#pano2' . $id . '").css({
3157 "opacity": "1",
3158 "visibility": "visible",
3159 "position": "relative",
3160 });
3161 gyroSwitch = true;
3162 panoshow' . $id . '.startOrientation();
3163 panoshow2' . $id . '.startOrientation();
3164 panoshow2' . $id . '.setPitch(panoshow' . $id . '.getPitch(), 0);
3165 panoshow2' . $id . '.setYaw(panoshow' . $id . '.getYaw(), 0);
3166 getParent.find("#pano' . $id . ' #zoom-in-out-controls' . $id . '").hide();
3167 getParent.find("#pano' . $id . ' #controls' . $id . '").hide();
3168 getParent.find("#pano' . $id . ' #explainer_button_' . $id . '").hide();
3169 getParent.find("#pano' . $id . ' #generic_form_button_' . $id . '").hide();
3170 getParent.find("#pano' . $id . ' #floor_map_button_' . $id . '").hide();
3171 getParent.find("#pano' . $id . ' #vrgcontrols' . $id . '").hide();
3172 getParent.find("#pano' . $id . ' #sccontrols' . $id . '").hide();
3173 getParent.find("#pano' . $id . ' #adcontrol' . $id . '").hide();
3174 getParent.find("#pano' . $id . ' .owl-nav.wpvr_slider_nav").hide();
3175 getParent.find("#pano' . $id . ' #cp-logo-controls").hide();
3176
3177 getParent.find("#pano' . $id . ' #custom-scene-navigation' . $id . '").hide();
3178
3179 getParent.find("#pano2' . $id . ' .pnlm-controls-container").hide();
3180 getParent.find("#pano' . $id . ' .pnlm-controls-container").hide();
3181
3182 getParent.find("#pano2' . $id . ' .pnlm-compass.pnlm-controls.pnlm-control").hide();
3183 getParent.find("#pano' . $id . ' .pnlm-compass.pnlm-controls.pnlm-control").hide();
3184
3185 getParent.find("#pano2' . $id . ' .pnlm-panorama-info").hide();
3186 getParent.find("#pano' . $id . ' .pnlm-panorama-info").hide();
3187 getParent.find("#pano' . $id . '").addClass("cardboard-half");
3188
3189 getParent.find("#center-pointer' . $id . '").show();
3190 getParent.find(".fullscreen-button").hide();
3191
3192
3193 if (window.DeviceOrientationEvent) {
3194 window.addEventListener("deviceorientation", vrDeviseOrientation);
3195 }
3196
3197 panoshow' . $id . '.on("mousemove", function (data){
3198 panoshow2' . $id . '.setPitch(data.pitch, 0);
3199 panoshow2' . $id . '.setYaw(data.yaw, 0);
3200 panoShowCardBoardOnTrigger(data);
3201
3202 });
3203 panoshow2' . $id . '.on("mousemove", function (data){
3204 panoshow2' . $id . '.setPitch(data.pitch, 0);
3205 panoshow' . $id . '.setYaw(data.yaw, 0);
3206 panoShowCardBoardOnTrigger(data);
3207
3208 });
3209
3210 panoshow' . $id . '.on("zoomchange", function (data){
3211 panoshow2' . $id . '.setHfov(data, 0);
3212 });
3213
3214 panoshow2' . $id . '.on("zoomchange", function (data){
3215 panoshow' . $id . '.setHfov(data, 0);
3216 });
3217
3218 panoshow' . $id . '.on("touchmove", function (data){
3219 panoshow' . $id . '.stopOrientation();
3220 panoshow2' . $id . '.stopOrientation();
3221 panoshow2' . $id . '.setPitch(data.pitch, 0);
3222 panoshow2' . $id . '.setYaw(data.yaw, 0);
3223 panoShowCardBoardOnTrigger(data);
3224
3225 });
3226
3227 panoshow2' . $id . '.on("touchmove", function (data){
3228 panoshow' . $id . '.stopOrientation();
3229 panoshow2' . $id . '.stopOrientation();
3230 panoshow' . $id . '.setPitch(data.pitch, 0);
3231 panoshow' . $id . '.setYaw(data.yaw, 0);
3232 panoShowCardBoardOnTrigger(data);
3233
3234 });
3235
3236 }
3237 else if(getValue == "on") {
3238 screen.orientation.unlock();
3239 requestExitFullscreen();
3240 // localStorage.setItem("vr_mode", "off");
3241 vr_mode = "off";
3242 jQuery(".vr-mode-title").hide();
3243 jQuery(this).val("off");
3244 getParent.find("#pano2' . $id . '").css({
3245 "opacity": "0",
3246 "visibility": "hidden",
3247 "position": "absolute",
3248 });
3249 panoshow' . $id . '.stopOrientation();
3250 panoshow2' . $id . '.stopOrientation();
3251 getParent.find(".pano-wrap").removeClass("wpvr-cardboard-disable-event");
3252 getParent.find("#pano' . $id . ' #zoom-in-out-controls' . $id . '").show();
3253 getParent.find("#pano' . $id . ' #controls' . $id . '").show();
3254 getParent.find("#pano' . $id . ' #explainer_button_' . $id . '").show();
3255 getParent.find("#pano' . $id . ' #generic_form_button_' . $id . '").show();
3256 getParent.find("#pano' . $id . ' #floor_map_button_' . $id . '").show();
3257
3258 getParent.find("#pano2' . $id . ' .pnlm-controls-container").show();
3259 getParent.find("#pano' . $id . ' .pnlm-controls-container").show();
3260 getParent.find("#pano' . $id . ' #vrgcontrols' . $id . '").show();
3261 getParent.find("#pano' . $id . ' #sccontrols' . $id . '").hide();
3262 getParent.find("#pano' . $id . ' #adcontrol' . $id . '").show();
3263 getParent.find("#pano' . $id . ' .owl-nav.wpvr_slider_nav").hide();
3264 getParent.find("#pano' . $id . ' #cp-logo-controls").show();
3265 getParent.find("#pano' . $id . ' #custom-scene-navigation' . $id . '").show();
3266
3267 if(compassBlock == "block"){
3268 getParent.find("#pano2' . $id . ' .pnlm-compass.pnlm-controls.pnlm-control").show();
3269 getParent.find("#pano' . $id . ' .pnlm-compass.pnlm-controls.pnlm-control").show();
3270 }
3271 if(infoBlock == "block"){
3272 getParent.find("#pano2' . $id . ' .pnlm-panorama-info").show();
3273 getParent.find("#pano' . $id . ' .pnlm-panorama-info").show();
3274 }
3275
3276 getParent.find("#pano' . $id . '").removeClass("cardboard-half");
3277 getParent.find("#center-pointer' . $id . '").hide();
3278 getParent.find(".fullscreen-button").hide();
3279 panoshow' . $id . '.off("mousemove");
3280 panoshow' . $id . '.off("touchmove");
3281 panoshow2' . $id . '.off("mousemove");
3282 panoshow2' . $id . '.off("touchmove");
3283 if (window.DeviceOrientationEvent) {
3284 window.removeEventListener("deviceorientation", vrDeviseOrientation);
3285 }
3286 }
3287 });';
3288
3289 $html .= 'panoshow2' . $id . '.on("load", function (){
3290 // if(localStorage.getItem("vr_mode") == "off") {
3291 if( vr_mode == "off") {
3292 jQuery(".vr-mode-title").hide();
3293 }
3294 else {
3295 jQuery("#pano2' . $id . ' .pnlm-compass.pnlm-controls.pnlm-control").css("display","none");
3296 jQuery("#pano' . $id . ' .pnlm-compass.pnlm-controls.pnlm-control").css("display","none");
3297 jQuery("#pano2' . $id . ' .pnlm-panorama-info").hide();
3298 jQuery("#pano' . $id . ' .pnlm-panorama-info").hide();
3299 jQuery(".vr-mode-title").show();
3300 }
3301 });';
3302 }
3303
3304 $html .= 'jQuery("#pano' . $id . ' .wpvr-floor-map .floor-plan-pointer").on("click",function(){
3305 var scene_id = jQuery(this).attr("scene_id");
3306 panoshow' . $id . '.loadScene(scene_id)
3307 jQuery(".floor-plan-pointer").removeClass("add-pulse")
3308 jQuery(this).addClass("add-pulse")
3309 });';
3310
3311 if ($scene_animation == 'on' && is_plugin_active('wpvr-pro/wpvr-pro.php') ) {
3312 $animation_js = apply_filters('wpvr_scene_animation_js', $id, $animation_type, $animationDuration, $animationDelay);
3313 if (!empty($animation_js)) {
3314 $html .= $animation_js;
3315 $html .= 'panoshow' . $id . '.on("load", function (scene){
3316 if (typeof changeScene === "function") {
3317 changeScene();
3318 } else {
3319 console.warn("changeScene function is not defined.");
3320 }
3321 });';
3322 }
3323 }
3324
3325
3326 $html .= '
3327 panoshow' . $id . '.on("mousemove", function (data){
3328 jQuery(".add-pulse").css({"transform":"rotate("+data.yaw+"deg)"});
3329 });
3330 ';
3331
3332
3333
3334 $status = get_option('wpvr_edd_license_status');
3335 if ($status !== false && 'valid' == $status && $is_pro) {
3336 $html .= 'panoshow' . $id . '.on("scenechange", function (scene){
3337 jQuery(".center-pointer").removeClass("wpvr-pluse-effect")
3338 jQuery(".floor-plan-pointer").each(function(index ,element){
3339 var scene_id = jQuery(this).attr("scene_id");
3340 if( active_scene == scene_id ){
3341 jQuery(".floor-plan-pointer").removeClass("add-pulse")
3342 jQuery(this).addClass("add-pulse")
3343 }
3344 });
3345
3346 });';
3347 $html .= 'panoshow' . $id . '.on("load", function (){
3348 if(jQuery(".floor-plan-pointer").length > 0){
3349 jQuery(".floor-plan-pointer").each(function(index ,element){
3350 var scene_id = jQuery(this).attr("scene_id");
3351 if( active_scene == scene_id ){
3352 jQuery(".floor-plan-pointer").removeClass("add-pulse")
3353 jQuery(this).addClass("add-pulse")
3354 }
3355 });
3356 }
3357 });';
3358 }
3359
3360 if ($status !== false && 'valid' == $status && $is_pro) {
3361 $scene_navigation_content_type = isset($postdata['scene_navigation_content_type']) ? $postdata['scene_navigation_content_type'] : 'scene_id';
3362 $html .= '
3363 jQuery("#pano' . $id . ' .custom-scene-navigation").on("click",function(){
3364 jQuery("#custom-scene-navigation-nav' . $id . ' ul").empty()
3365 if(scenes){
3366 var scene_navigation_content_type = ' . "'" . $scene_navigation_content_type . "'" . ';
3367 var sceneList = scenes.scenes;
3368 var getScene = panoshow' . $id . '.getScene();
3369 for (const key in sceneList) {
3370 let title;
3371 if (scene_navigation_content_type === "scene_title") {
3372 if (sceneList[key].title) {
3373 title = sceneList[key].title;
3374
3375 }else{
3376 if(title == "" || title == undefined){
3377 if (sceneList[key].panorama) {
3378 title = getImageNameWithoutExtension(sceneList[key].panorama);
3379 }
3380 }
3381 }
3382 } else if (scene_navigation_content_type === "scene_image_name") {
3383 if (sceneList[key].panorama) {
3384 title = getImageNameWithoutExtension(sceneList[key].panorama);
3385 }
3386 } else {
3387 title = key;
3388 }
3389 if (sceneList.hasOwnProperty(key)) {
3390 if( key === getScene){
3391 jQuery("#custom-scene-navigation-nav' . $id . ' ul").append("<li class=\"scene-navigation-list active\" scene_id= " + key + " >" + title + "</li>");
3392 }else{
3393 jQuery("#custom-scene-navigation-nav' . $id . ' ul").append("<li class=\"scene-navigation-list\" scene_id= " + key + " >" + title + "</li>");
3394 }
3395 }
3396 }
3397 }
3398 jQuery("#custom-scene-navigation-nav' . $id . '").toggleClass("visible");
3399 });
3400 ';
3401
3402 $html .= 'function getImageNameWithoutExtension(imageUrl) {
3403 // Split the URL by "/"
3404 var parts = imageUrl.split("/");
3405
3406 // Get the last part which contains the image name
3407 var imageNameWithExtension = parts[parts.length - 1];
3408
3409 // Split the image name by period (.)
3410 var imageNameParts = imageNameWithExtension.split(".");
3411
3412 // Remove the last part (which is the extension) and join the remaining parts
3413 var imageNameWithoutExtension = imageNameParts.slice(0, -1).join(".");
3414
3415 // Return the image name without extension
3416 return imageNameWithoutExtension;
3417 }';
3418
3419
3420 $html .= '
3421 jQuery("#pano' . $id . ' #custom-scene-navigation-nav' . $id . ' ul").on("click", "li.scene-navigation-list", function() {
3422 if (scenes) {
3423 jQuery(this).siblings("li").removeClass("active");
3424 jQuery(this).addClass("active");
3425
3426 var scene_key = jQuery(this).attr("scene_id");
3427 panoshow' . $id . '.loadScene(scene_key);
3428 }
3429 });
3430 ';
3431 }
3432
3433 //Duplicate mode only for vr mode end===//
3434 $html .= 'panoshow' . $id . '.on("load", function (){
3435
3436 // if(localStorage.getItem("vr_mode") == "off") {
3437 if( vr_mode == "off") {
3438 jQuery(".vr-mode-title").hide();
3439 }
3440 else {
3441 jQuery("#pano2' . $id . ' .pnlm-compass.pnlm-controls.pnlm-control").css("display","none");
3442 jQuery("#pano' . $id . ' .pnlm-compass.pnlm-controls.pnlm-control").css("display","none");
3443 jQuery("#pano2' . $id . ' .pnlm-panorama-info").hide();
3444 jQuery("#pano' . $id . ' .pnlm-panorama-info").hide();
3445 jQuery(".vr-mode-title").show();
3446 }
3447
3448
3449 if (jQuery("#pano' . $id . '").children().children(".pnlm-panorama-info:visible").length > 0) {
3450 jQuery("#controls' . $id . '").css("bottom", "80px");
3451 }
3452 else {
3453 jQuery("#controls' . $id . '").css("bottom", "5px");
3454 }
3455 });';
3456
3457
3458 $html .= '
3459 if (scenes.autoRotate) {
3460 panoshow' . $id . '.on("load", function (){
3461 setTimeout(function(){ panoshow' . $id . '.startAutoRotate(scenes.autoRotate, 0); }, 3000);
3462 });
3463 panoshow' . $id . '.on("scenechange", function (){
3464 setTimeout(function(){ panoshow' . $id . '.startAutoRotate(scenes.autoRotate, 0); }, 3000);
3465 });
3466 }
3467 ';
3468 $html .= 'var touchtime = 0;';
3469 if ($vrgallery) {
3470 if (isset($panodata["scene-list"])) {
3471 foreach ($panodata["scene-list"] as $panoscenes) {
3472 $scene_key = $panoscenes['scene-id'];
3473 $scene_key_gallery = $panoscenes['scene-id'] . '_gallery_' . $id;
3474 $img_src_url = $panoscenes['scene-attachment-url'];
3475 // $html .= 'document.getElementById("' . $scene_key_gallery . '").addEventListener("click", function(e) { ';
3476 // $html .= 'if (touchtime == 0) {';
3477 // $html .= 'touchtime = new Date().getTime();';
3478 // $html .= '} else {';
3479 // $html .= 'if (((new Date().getTime()) - touchtime) < 800) {';
3480 // $html .= 'panoshow' . $id . '.loadScene("' . $scene_key . '");';
3481 // $html .= 'touchtime = 0;';
3482 // $html .= '} else {';
3483 // $html .= 'touchtime = new Date().getTime();';
3484 // $html .= '}';
3485 // $html .= '}';
3486 // $html .= '});';
3487 $html .= '
3488 jQuery(document).on("click","#' . $scene_key_gallery . '",function() {
3489 panoshow' . $id . '.loadScene("' . $scene_key . '");
3490 });
3491 ';
3492 }
3493 }
3494 }
3495
3496
3497
3498 //===Custom Control===//
3499 if (isset($custom_control)) {
3500 if ($custom_control['panupSwitch'] == "on" && $status == 'valid' && $is_pro) {
3501 $html .= 'document.getElementById("pan-up' . $id . '").addEventListener("click", function(e) {';
3502 $html .= 'panoshow' . $id . '.setPitch(panoshow' . $id . '.getPitch() + 10);';
3503 $html .= '});';
3504 }
3505
3506 if ($custom_control['panDownSwitch'] == "on" && $status == 'valid' && $is_pro) {
3507 $html .= 'document.getElementById("pan-down' . $id . '").addEventListener("click", function(e) {';
3508 $html .= 'panoshow' . $id . '.setPitch(panoshow' . $id . '.getPitch() - 10);';
3509 $html .= '});';
3510 }
3511
3512 if ($custom_control['panLeftSwitch'] == "on" && $status == 'valid' && $is_pro) {
3513 $html .= 'document.getElementById("pan-left' . $id . '").addEventListener("click", function(e) {';
3514 $html .= 'panoshow' . $id . '.setYaw(panoshow' . $id . '.getYaw() - 10);';
3515 $html .= '});';
3516 }
3517
3518 if ($custom_control['panRightSwitch'] == "on" && $status == 'valid' && $is_pro) {
3519 $html .= 'document.getElementById("pan-right' . $id . '").addEventListener("click", function(e) {';
3520 $html .= 'panoshow' . $id . '.setYaw(panoshow' . $id . '.getYaw() + 10);';
3521 $html .= '});';
3522 }
3523
3524 if ($custom_control['panZoomInSwitch'] == "on") {
3525 $html .= 'document.getElementById("zoom-in' . $id . '").addEventListener("click", function(e) {';
3526 $html .= 'panoshow' . $id . '.setHfov(panoshow' . $id . '.getHfov() - 10);';
3527 $html .= '});';
3528 }
3529
3530 if ($custom_control['panZoomOutSwitch'] == "on") {
3531 $html .= 'document.getElementById("zoom-out' . $id . '").addEventListener("click", function(e) {';
3532 $html .= 'panoshow' . $id . '.setHfov(panoshow' . $id . '.getHfov() + 10);';
3533 $html .= '});';
3534 }
3535
3536 if ($custom_control['panFullscreenSwitch'] == "on" && $status == 'valid' && $is_pro) {
3537 $html .= 'document.getElementById("fullscreen' . $id . '").addEventListener("click", function(e) {';
3538 $html .= 'panoshow' . $id . '.toggleFullscreen();';
3539 $html .= '});';
3540 $html .= '(function() {';
3541 $html .= 'function wpvrFsChange' . $id . '() {';
3542 $html .= 'var fsIcon = document.querySelector("#fullscreen' . $id . ' i");';
3543 $html .= 'if (!fsIcon) return;';
3544 $html .= 'if (document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement) {';
3545 $html .= 'fsIcon.classList.remove("fa-expand"); fsIcon.classList.add("fa-minimize");';
3546 $html .= '} else {';
3547 $html .= 'fsIcon.classList.remove("fa-minimize"); fsIcon.classList.add("fa-expand");';
3548 $html .= '}';
3549 $html .= '}';
3550 $html .= 'document.addEventListener("fullscreenchange", wpvrFsChange' . $id . ');';
3551 $html .= 'document.addEventListener("webkitfullscreenchange", wpvrFsChange' . $id . ');';
3552 $html .= 'document.addEventListener("mozfullscreenchange", wpvrFsChange' . $id . ');';
3553 $html .= 'document.addEventListener("MSFullscreenChange", wpvrFsChange' . $id . ');';
3554 $html .= '})();';
3555 }
3556
3557 if ($custom_control['backToHomeSwitch'] == "on" && $status == 'valid' && $is_pro) {
3558 $html .= 'document.getElementById("backToHome' . $id . '").addEventListener("click", function(e) {';
3559 $html .= 'panoshow' . $id . '.loadScene("' . $default_scene . '");';
3560 $html .= '});';
3561 }
3562
3563 if ($custom_control['gyroSwitch'] == "on" && $custom_control['gyroscopeSwitch'] == "on" && 'valid' == $status && $is_pro) {
3564 $html .= '
3565 var element = document.getElementById("gyroscope' . $id . '");
3566 var gyroSwitch = true;
3567 var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
3568 var permissionGranted = false;
3569
3570 function requestOrientationPermission() {
3571 if (isIOS) {
3572 if (typeof DeviceOrientationEvent.requestPermission === "function") {
3573 DeviceOrientationEvent.requestPermission()
3574 .then(function(state) {
3575 if (state === "granted") {
3576 permissionGranted = true;
3577 startOrientation();
3578 } else {
3579 element.children[0].style.color = "red";
3580 gyroSwitch = false;
3581 alert("Permission to use motion sensors was denied.");
3582 }
3583 })
3584 .catch(function(error) {
3585 element.children[0].style.color = "red";
3586 gyroSwitch = false;
3587 console.error("Error requesting device orientation permission:", error);
3588 });
3589 }
3590 } else {
3591 startOrientation();
3592 }
3593 }
3594
3595 function startOrientation() {
3596 panoshow' . $id . '.startOrientation();
3597 element.children[0].style.color = "'.$custom_control['gyroscopeColor'].'";
3598 gyroSwitch = true;
3599 }
3600
3601 panoshow' . $id . '.on("load", function() {
3602 if (!isIOS || permissionGranted) {
3603 if (gyroSwitch) {
3604 startOrientation();
3605 } else {
3606 panoshow' . $id . '.stopOrientation();
3607 element.children[0].style.color = "red";
3608 }
3609 }
3610 });
3611
3612 panoshow' . $id . '.on("scenechange", function() {
3613 if (!isIOS || permissionGranted) {
3614 if (panoshow' . $id . '.isOrientationActive()) {
3615 element.children[0].style.color = "'.$custom_control['gyroscopeColor'].'";
3616 } else {
3617 element.children[0].style.color = "red";
3618 }
3619 }
3620 });
3621
3622 panoshow' . $id . '.on("touchstart", function() {
3623 if (!isIOS || permissionGranted) {
3624 if (panoshow' . $id . '.isOrientationActive()) {
3625 gyroSwitch = true;
3626 element.children[0].style.color = "'.$custom_control['gyroscopeColor'].'";
3627 } else {
3628 gyroSwitch = false;
3629 element.children[0].style.color = "red";
3630 }
3631 }
3632 });';
3633
3634 $html .= 'document.getElementById("gyroscope' . $id . '").addEventListener("click", function(e) {
3635 var element = document.getElementById("gyroscope' . $id . '");
3636 if (isIOS && typeof DeviceOrientationEvent.requestPermission === "function" && !permissionGranted) {
3637 requestOrientationPermission();
3638 } else {
3639 if (panoshow' . $id . '.isOrientationActive()) {
3640 panoshow' . $id . '.stopOrientation();
3641 gyroSwitch = false;
3642 element.children[0].style.color = "red";
3643 } else {
3644 startOrientation();
3645 }
3646 }
3647 });';
3648 }
3649 }
3650
3651 $angle_up = '<i class="fa fa-angle-up"></i>';
3652 $angle_down = '<i class="fa fa-angle-down"></i>';
3653 $sin_qout = "'";
3654
3655 //===Explainer Script===//
3656 $html .= '
3657 jQuery(document).on("click","#explainer_button_' . $id . '",function() {
3658 jQuery("#explainer' . $id . '").slideToggle(function(){
3659 var $explainerVideoId = jQuery("#explainer' . $id . '");
3660 var $explainerVideoIframe = $explainerVideoId.find("iframe");
3661 var explainerVideoIframSrc = $explainerVideoIframe.attr("src");
3662 $explainerVideoIframe.attr("src", "");
3663 $explainerVideoIframe.attr("src", explainerVideoIframSrc);
3664 });
3665 });
3666
3667 jQuery(document).on("click", ".close-explainer-video", function() {
3668 var $explainer = jQuery(this).parent(".explainer");
3669 var $iframe = $explainer.find("iframe");
3670 var src = $iframe.attr("src");
3671 $iframe.attr("src", "");
3672 $explainer.hide();
3673 $iframe.attr("src", src);
3674 });
3675
3676 jQuery(document).on("click","#pano' . $id . '",function(event) {
3677 var isActiveModal = event.target.closest(".custom-ifram-wrapper");
3678 var isForm = event.target.closest(".wpvr-hotspot-tweak-contents");
3679 var isHotspot = event.target.closest(".pnlm-hotspot-base");
3680 if( isActiveModal == null && isForm == null && isHotspot == null){
3681 jQuery(".custom-ifram-wrapper .custom-ifram").empty();
3682 jQuery(".custom-ifram-wrapper").hide();
3683 jQuery(this).removeClass("show-modal");
3684 jQuery(".wpvr-hotspot-tweak-contents-wrapper").hide("show-modal");
3685 }else if(isForm != null){
3686 jQuery(this).addClass("show-modal");
3687 }
3688 });
3689
3690 ';
3691 //===Explainer Script End===//
3692
3693 //===generic form script===//
3694 if (isset($postdata["genericform"]) && $postdata["genericform"] === 'on') {
3695 $html .= '
3696 jQuery(document).on("click","#generic_form_button_' . $id . '",function() {
3697 jQuery("#wpvr-generic-form' . $id . '").fadeToggle();
3698 });
3699
3700 jQuery(document).on("click",".close-generic-form",function() {
3701 jQuery(this).parent(".wpvr-generic-form").fadeOut()
3702 });
3703 ';
3704 }
3705
3706 //===generic from script===//
3707
3708 //===Floor map Script===//
3709 $html .= '
3710 jQuery(document).on("click","#floor_map_button_' . $id . '",function() {
3711 jQuery("#wpvr-floor-map' . $id . '").toggle().removeClass("fullwindow");
3712 });
3713
3714 jQuery(document).on("dblclick","#wpvr-floor-map' . $id . '",function(){
3715 jQuery(this).addClass("fullwindow");
3716 jQuery(this).parents(".pano-wrap").addClass("show-modal");
3717 });
3718
3719 jQuery(document).on("click",".close-floor-map-plan",function() {
3720 jQuery(this).parent(".wpvr-floor-map").hide();
3721 jQuery(this).parent(".wpvr-floor-map").removeClass("fullwindow");
3722 jQuery(this).parents(".pano-wrap").removeClass("show-modal");
3723 });
3724
3725 ';
3726 //===Floor map Script End===//
3727
3728 if ($vrgallery_display) {
3729 if (!$autoload) {
3730 $html .= '
3731 jQuery(document).ready(function($){
3732 jQuery("#sccontrols' . $id . '").hide();
3733 jQuery(".vrgctrl' . $id . '").html(' . $sin_qout . $angle_up . $sin_qout . ');
3734 jQuery("#sccontrols' . $id . '").hide();
3735 jQuery("#pano' . $id . ' .wpvr_slider_nav").hide();
3736 });
3737 ';
3738
3739 $html .= '
3740 var slide' . $id . ' = "down";
3741 jQuery(document).on("click","#vrgcontrols' . $id . '",function() {
3742
3743 if (slide' . $id . ' == "up") {
3744 jQuery(".vrgctrl' . $id . '").empty();
3745 jQuery(".vrgctrl' . $id . '").html(' . $sin_qout . $angle_up . $sin_qout . ');
3746 slide' . $id . ' = "down";
3747 jQuery("#pano' . $id . ' .wpvr_slider_nav").slideToggle();
3748 jQuery("#sccontrols' . $id . '").slideToggle(function(){
3749 if (jQuery(".elementor-edit-mode .elementor-widget-container").length) {
3750 if (jQuery(this).is(":visible")) {
3751 jQuery(".elementor-edit-mode .elementor-widget-container .wpvr-cardboard .pnlm-container #sccontrols' . $id . '").css({
3752 "display": "flex",
3753 "justify-content": "center",
3754 "align-items": "center",
3755 "gap": "15px"
3756 });
3757 }
3758 }
3759 if (jQuery(".bricks-is-frontend").length) {
3760 if (jQuery(this).is(":visible")) {
3761 jQuery(".bricks-is-frontend .wpvr-cardboard .pnlm-container #sccontrols' . $id . '").css({
3762 "display": "flex",
3763 "justify-content": "center",
3764 "align-items": "center"
3765 });
3766 }
3767 }
3768 });
3769 }
3770 else {
3771 jQuery(".vrgctrl' . $id . '").empty();
3772 jQuery(".vrgctrl' . $id . '").html(' . $sin_qout . $angle_down . $sin_qout . ');
3773 slide' . $id . ' = "up";
3774 jQuery("#pano' . $id . ' .wpvr_slider_nav").slideToggle();
3775 jQuery("#sccontrols' . $id . '").slideToggle(function(){
3776 if (jQuery(".elementor-edit-mode .elementor-widget-container").length) {
3777 if (jQuery(this).is(":visible")) {
3778 jQuery(".elementor-edit-mode .elementor-widget-container .wpvr-cardboard .pnlm-container #sccontrols' . $id . '").css({
3779 "display": "flex",
3780 "justify-content": "center",
3781 "align-items": "center",
3782 "gap": "15px"
3783 });
3784 }
3785 }
3786 if (jQuery(".bricks-is-frontend").length) {
3787 if (jQuery(this).is(":visible")) {
3788 jQuery(".bricks-is-frontend .wpvr-cardboard .pnlm-container #sccontrols' . $id . '").css({
3789 "display": "flex",
3790 "justify-content": "center",
3791 "align-items": "center"
3792 });
3793 }
3794 }
3795 });
3796 }
3797 });
3798 ';
3799 } else {
3800 $html .= '
3801 jQuery(document).ready(function($){
3802 jQuery("#sccontrols' . $id . '").show();
3803 jQuery("#pano' . $id . ' .wpvr_slider_nav").show();
3804 jQuery(".vrgctrl' . $id . '").html(' . $sin_qout . $angle_down . $sin_qout . ');
3805 });
3806 ';
3807
3808 $html .= '
3809 var slide' . $id . ' = "down";
3810 jQuery(document).on("click","#vrgcontrols' . $id . '",function() {
3811
3812 if (slide' . $id . ' == "up") {
3813 jQuery(".vrgctrl' . $id . '").empty();
3814 jQuery(".vrgctrl' . $id . '").html(' . $sin_qout . $angle_down . $sin_qout . ');
3815 slide' . $id . ' = "down";
3816 }
3817 else {
3818 jQuery(".vrgctrl' . $id . '").empty();
3819 jQuery(".vrgctrl' . $id . '").html(' . $sin_qout . $angle_up . $sin_qout . ');
3820 slide' . $id . ' = "up";
3821 }
3822 jQuery("#pano' . $id . ' .wpvr_slider_nav").slideToggle();
3823 jQuery("#sccontrols' . $id . '").slideToggle(function(){
3824 if (jQuery(".elementor-edit-mode .elementor-widget-container").length) {
3825 if (jQuery(this).is(":visible")) {
3826 jQuery(".elementor-edit-mode .elementor-widget-container .wpvr-cardboard .pnlm-container #sccontrols' . $id . '").css({
3827 "display": "flex",
3828 "justify-content": "center",
3829 "align-items": "center",
3830 "gap": "15px"
3831 });
3832 }
3833 }
3834 if (jQuery(".bricks-is-frontend").length) {
3835 if (jQuery(this).is(":visible")) {
3836 jQuery(".bricks-is-frontend .wpvr-cardboard .pnlm-container #sccontrols' . $id . '").css({
3837 "display": "flex",
3838 "justify-content": "center",
3839 "align-items": "center"
3840 });
3841 }
3842 }
3843 });
3844 });
3845 ';
3846 }
3847 } else {
3848 $html .= '
3849 jQuery(document).ready(function($){
3850 jQuery("#sccontrols' . $id . '").hide();
3851 jQuery("#pano' . $id . ' .wpvr_slider_nav").hide();
3852 jQuery(".vrgctrl' . $id . '").html(' . $sin_qout . $angle_up . $sin_qout . ');
3853 });
3854 ';
3855
3856 $html .= '
3857 var slide' . $id . ' = "down";
3858 jQuery(document).on("click","#vrgcontrols' . $id . '",function() {
3859
3860 if (slide' . $id . ' == "up") {
3861 jQuery(".vrgctrl' . $id . '").empty();
3862 jQuery(".vrgctrl' . $id . '").html(' . $sin_qout . $angle_up . $sin_qout . ');
3863 slide' . $id . ' = "down";
3864 }
3865 else {
3866 jQuery(".vrgctrl' . $id . '").empty();
3867 jQuery(".vrgctrl' . $id . '").html(' . $sin_qout . $angle_down . $sin_qout . ');
3868 slide' . $id . ' = "up";
3869 }
3870 jQuery("#pano' . $id . ' .wpvr_slider_nav").slideToggle();
3871 jQuery("#sccontrols' . $id . '").slideToggle(function(){
3872 if (jQuery(".elementor-edit-mode .elementor-widget-container").length) {
3873 if (jQuery(this).is(":visible")) {
3874 jQuery(".elementor-edit-mode .elementor-widget-container .wpvr-cardboard .pnlm-container #sccontrols' . $id . '").css({
3875 "display": "flex",
3876 "justify-content": "center",
3877 "align-items": "center",
3878 "gap": "15px"
3879 });
3880 }
3881 }
3882 if (jQuery(".bricks-is-frontend").length) {
3883 if (jQuery(this).is(":visible")) {
3884 jQuery(".bricks-is-frontend .wpvr-cardboard .pnlm-container #sccontrols' . $id . '").css({
3885 "display": "flex",
3886 "justify-content": "center",
3887 "align-items": "center"
3888 });
3889 }
3890 }
3891 });
3892 });
3893 ';
3894 }
3895
3896 if (!$autoload) {
3897 $html .= '
3898
3899 jQuery(document).ready(function(){
3900 jQuery("#controls' . $id . '").hide();
3901 jQuery("#zoom-in-out-controls' . $id . '").hide();
3902 jQuery("#adcontrol' . $id . '").hide();
3903 jQuery("#explainer_button_' . $id . '").hide();
3904 jQuery("#generic_form_button_' . $id . '").hide();
3905 jQuery("#floor_map_button_' . $id . '").hide();
3906 jQuery("#vrgcontrols' . $id . '").hide();
3907 jQuery("#cp-logo-controls").hide();
3908 jQuery(".custom-scene-navigation").hide();
3909 jQuery("#pano' . $id . '").find(".pnlm-panorama-info").hide();
3910 });
3911
3912 ';
3913
3914 if ($vrgallery_display) {
3915 $html .= 'var load_once = "true";';
3916 $html .= 'panoshow' . $id . '.on("load", function (){
3917 if (load_once == "true") {
3918 load_once = "false";
3919 jQuery("#sccontrols' . $id . '").slideToggle(function(){
3920 if (jQuery(".elementor-edit-mode .elementor-widget-container").length) {
3921 if (jQuery(this).is(":visible")) {
3922 jQuery(".elementor-edit-mode .elementor-widget-container .wpvr-cardboard .pnlm-container #sccontrols' . $id . '").css("display", "flex");
3923 }
3924 }
3925 if (jQuery(".bricks-is-frontend").length) {
3926 if (jQuery(this).is(":visible")) {
3927 jQuery(".bricks-is-frontend .wpvr-cardboard .pnlm-container #sccontrols' . $id . '").css({
3928 "display": "flex",
3929 "justify-content": "center",
3930 "align-items": "center"
3931 });
3932 }
3933 }
3934 });
3935 jQuery("#pano' . $id . ' .wpvr_slider_nav").slideToggle();
3936 }
3937 });';
3938 }
3939
3940 $html .= 'panoshow' . $id . '.on("load", function (){
3941 jQuery("#controls' . $id . '").show();
3942 jQuery("#zoom-in-out-controls' . $id . '").show();
3943 jQuery("#adcontrol' . $id . '").show();
3944 jQuery("#explainer_button_' . $id . '").show();
3945 jQuery("#generic_form_button_' . $id . '").show();
3946 jQuery("#floor_map_button_' . $id . '").show();
3947 jQuery("#vrgcontrols' . $id . '").show();
3948 jQuery("#cp-logo-controls").show();
3949 jQuery(".custom-scene-navigation").show();
3950 jQuery("#pano' . $id . '").find(".pnlm-panorama-info").show();
3951 });';
3952 }
3953
3954 $previewText = $postdata['previewtext'] ?? 'Click To Load Panorama';
3955 if ($previewText) {
3956 $html .= '
3957 jQuery("#pano' . $id . '").children(".pnlm-ui").find(".pnlm-load-button p").text("' . $previewText. '")
3958 ';
3959 } else {
3960 $html .= '
3961 jQuery("#pano' . $id . '").children(".pnlm-ui").find(".pnlm-load-button p").text("Click To Load Panorama")
3962 ';
3963 }
3964
3965 $html .= 'jQuery("#pano' . $id . ' .pnlm-title-box").on("mouseenter", function(){
3966 jQuery(this).attr("title", jQuery(this).text());
3967 });
3968 jQuery("#pano' . $id . ' .pnlm-title-box").on("mouseleave", function(){
3969 jQuery(this).removeAttr("title");
3970 });';
3971 $html .= '});';
3972 $html .= '</script>';
3973
3974 $tour_data = [];
3975 if(defined("WPVR_PRO_VERSION")){
3976 $tour_data = array(
3977 'explainerControlSwitch' => ( isset( $custom_control['explainerSwitch'] ) && $custom_control['explainerSwitch'] === 'on' ) ? 'on' : 'off',
3978 'floor_plan_enable' => $floor_plan_enable ?? false,
3979 'floor_plan_image' => $floor_plan_image ?? '',
3980 'custom_control' => $custom_control ?? [],
3981 );
3982 }
3983 return apply_filters('wpvr_generate_tour_layout_html', $html ,$postdata ,$id, $tour_data);
3984 }
3985
3986 function sanitize_content_preserve_styles($content) {
3987 // Decode HTML entities first (in case content was encoded in database)
3988 $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8');
3989
3990 // Escape <script> blocks to display as text instead of removing them
3991 $content = preg_replace_callback('/<script\b[^>]*>(.*?)<\/script>/si', function($matches) {
3992 return esc_html($matches[0]); // Convert to plain text
3993 }, $content);
3994
3995 // Strip dangerous URL-based attributes
3996 $content = preg_replace('/(href|action|formaction)\s*=\s*["\']?\s*(javascript|vbscript|data|about):/i', '$1=""', $content);
3997
3998 // Escape inline event handlers (onclick, onhover, etc.) to display as text
3999 $content = preg_replace_callback('/\s*(on\w+)\s*=\s*(["\'])([^"\']*)\2/i', function($matches) {
4000 // Escape the attribute value but keep the attribute name visible as text
4001 return ' ' . esc_html($matches[1]) . '=' . $matches[2] . esc_html($matches[3]) . $matches[2];
4002 }, $content);
4003 $content = preg_replace_callback('/\s*(on\w+)\s*=\s*([^>\s]+)/i', function($matches) {
4004 // Handle unquoted event handlers
4005 return ' ' . esc_html($matches[1]) . '=' . esc_html($matches[2]);
4006 }, $content);
4007
4008 // Remove all unsafe embedded/interactive elements except iframe
4009 $content = preg_replace('/<(object|embed|applet|frame|frameset|meta|link|base|form|input|button|textarea|select|option)\b[^>]*>/i', '', $content);
4010 $content = preg_replace('/<\/(object|embed|applet|frame|frameset|meta|link|base|form|input|button|textarea|select|option)>/i', '', $content);
4011
4012 // Clean style attributes safely
4013 $content = preg_replace_callback('/style\s*=\s*["\']([^"\']*)["\']/', function($matches) {
4014 $style = $matches[1];
4015 $style = preg_replace('/expression\s*\(/i', '', $style);
4016 $style = preg_replace('/(javascript|vbscript|data|about)\s*:/i', '', $style);
4017 $style = preg_replace('/url\s*\(\s*["\']?\s*(javascript|vbscript|data):/i', '', $style);
4018 $style = preg_replace('/behavior\s*:/i', '', $style);
4019 $style = preg_replace('/-moz-binding\s*:/i', '', $style);
4020 return 'style="' . esc_attr($style) . '"';
4021 }, $content);
4022
4023 // Sanitize <style> blocks
4024 $content = preg_replace_callback('/<style\b[^>]*>(.*?)<\/style>/si', function($matches) {
4025 $css = $matches[1];
4026 $css = preg_replace('/(expression|javascript|vbscript|data|about)\s*:/i', '', $css);
4027 $css = preg_replace('/url\s*\(\s*["\']?\s*(javascript|vbscript|data):/i', '', $css);
4028 $css = preg_replace('/behavior\s*:/i', '', $css);
4029 $css = preg_replace('/-moz-binding\s*:/i', '', $css);
4030 return '<style>' . esc_html($css) . '</style>';
4031 }, $content);
4032
4033 // Allow iframes from safe sources only (e.g., YouTube, Vimeo)
4034 $allowed_tags = wp_kses_allowed_html('post');
4035 $allowed_tags['iframe'] = [
4036 'src' => true,
4037 'width' => true,
4038 'height' => true,
4039 'frameborder' => true,
4040 'allowfullscreen' => true,
4041 'class' => true,
4042 'style' => true,
4043 'title' => true,
4044 'allow' => true,
4045 'name' => true,
4046 'referrerpolicy' => true,
4047 'loading' => true,
4048 'sandbox' => true,
4049 ];
4050 $allowed_tags['img'] = [
4051 'src' => true,
4052 'alt' => true,
4053 'title' => true,
4054 'width' => true,
4055 'height' => true,
4056 'class' => true,
4057 'id' => true,
4058 'style' => true,
4059 'loading' => true,
4060 'srcset' => true,
4061 'sizes' => true,
4062 ];
4063
4064 // Apply wp_kses() to keep only allowed tags/attributes
4065 $content = wp_kses($content, $allowed_tags);
4066
4067 // Finally, validate iframe src for security (allow https only, block javascript: etc.)
4068 $content = preg_replace_callback('/<iframe[^>]+src=["\']([^"\']+)["\'][^>]*>(?:<\/iframe>)?/i', function($matches) {
4069 $src = $matches[1];
4070 // Allow any https:// or http:// URL, but block javascript:, data:, vbscript:, etc.
4071 if (preg_match('/^(https?:)?\/\//i', $src) && !preg_match('/^(javascript|data|vbscript|about):/i', $src)) {
4072 return $matches[0]; // keep safe iframe
4073 }
4074 // Strip unsafe iframe
4075 return '';
4076 }, $content);
4077
4078 return $content;
4079 }
4080 function wpvr_hex2rgb($colour)
4081 {
4082 if (isset($colour[0]) && $colour[0] == '#') {
4083 $colour = substr($colour, 1);
4084 }
4085 if (strlen($colour) == 6) {
4086 list($r, $g, $b) = array($colour[0] . $colour[1], $colour[2] . $colour[3], $colour[4] . $colour[5]);
4087 } elseif (strlen($colour) == 3) {
4088 list($r, $g, $b) = array($colour[0] . $colour[0], $colour[1] . $colour[1], $colour[2] . $colour[2]);
4089 } else {
4090 return false;
4091 }
4092 $r = hexdec($r);
4093 $g = hexdec($g);
4094 $b = hexdec($b);
4095 return array($r . ', ' . $g . ', ' . $b);
4096 }
4097
4098 function wpvr_HTMLToRGB($htmlCode)
4099 {
4100 $r = 0;
4101 $g = 0;
4102 $b = 0;
4103 if (isset($htmlCode[0]) && $htmlCode[0] == '#') {
4104 $htmlCode = substr($htmlCode, 1);
4105 }
4106
4107 if (strlen($htmlCode) == 3) {
4108 $htmlCode = $htmlCode[0] . $htmlCode[0] . $htmlCode[1] . $htmlCode[1] . $htmlCode[2] . $htmlCode[2];
4109 }
4110
4111 if (isset($htmlCode[0]) && isset($htmlCode[1])) {
4112 $r = hexdec($htmlCode[0] . $htmlCode[1]);
4113 }
4114 if (isset($htmlCode[2]) && isset($htmlCode[3])) {
4115 $g = hexdec($htmlCode[2] . $htmlCode[3]);
4116 }
4117 if (isset($htmlCode[4]) && isset($htmlCode[5])) {
4118 $b = hexdec($htmlCode[4] . $htmlCode[5]);
4119 }
4120
4121 return $b + ($g << 0x8) + ($r << 0x10);
4122 }
4123
4124 function wpvr_RGBToHSL($RGB)
4125 {
4126 $r = 0xFF & ($RGB >> 0x10);
4127 $g = 0xFF & ($RGB >> 0x8);
4128 $b = 0xFF & $RGB;
4129
4130 $r = ((float)$r) / 255.0;
4131 $g = ((float)$g) / 255.0;
4132 $b = ((float)$b) / 255.0;
4133
4134 $maxC = max($r, $g, $b);
4135 $minC = min($r, $g, $b);
4136
4137 $l = ($maxC + $minC) / 2.0;
4138
4139 if ($maxC == $minC) {
4140 $s = 0;
4141 $h = 0;
4142 } else {
4143 if ($l < .5) {
4144 $s = ($maxC - $minC) / ($maxC + $minC);
4145 } else {
4146 $s = ($maxC - $minC) / (2.0 - $maxC - $minC);
4147 }
4148 if ($r == $maxC) {
4149 $h = ($g - $b) / ($maxC - $minC);
4150 }
4151 if ($g == $maxC) {
4152 $h = 2.0 + ($b - $r) / ($maxC - $minC);
4153 }
4154 if ($b == $maxC) {
4155 $h = 4.0 + ($r - $g) / ($maxC - $minC);
4156 }
4157
4158 $h = $h / 6.0;
4159 }
4160
4161 $h = (int)round(255.0 * $h);
4162 $s = (int)round(255.0 * $s);
4163 $l = (int)round(255.0 * $l);
4164
4165 return (object) array('hue' => $h, 'saturation' => $s, 'lightness' => $l);
4166 }
4167
4168 add_action('rest_api_init', 'wpvr_rest_data_route');
4169 function wpvr_rest_data_route()
4170 {
4171 register_rest_route('wpvr/v1', '/panodata/', array(
4172 'methods' => 'GET',
4173 'callback' => 'wpvr_rest_data_set',
4174 'permission_callback' => 'wpvr_rest_route_permission'
4175 ));
4176 }
4177
4178 function wpvr_rest_route_permission()
4179 {
4180 return true;
4181 }
4182
4183 function wpvr_rest_data_set()
4184 {
4185 $query = new WP_Query(array(
4186 'post_type' => 'wpvr_item',
4187 'posts_per_page' => -1,
4188 ));
4189
4190 $wpvr_list = array();
4191 $list_none = array('value' => 0, 'label' => 'None');
4192 array_push($wpvr_list, $list_none);
4193 while ($query->have_posts()) {
4194 $query->the_post();
4195 $title = mb_convert_encoding(get_the_title(), 'UTF-8', 'HTML-ENTITIES');
4196 $post_id = get_the_ID();
4197 $title = $post_id . ' : ' . $title;
4198 $list_ob = array('value' => $post_id, 'label' => $title);
4199 array_push($wpvr_list, $list_ob);
4200 }
4201
4202 return $wpvr_list;
4203 }
4204
4205 function wpvr_isMobileDevice()
4206 {
4207 if ( ! isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
4208 return false;
4209 }
4210 $user_agent = sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) );
4211 return preg_match("/(android|avantgo|blackberry|bolt|boost|cricket|docomo|fone|hiptop|mini|mobi|palm|phone|pie|tablet|up\.browser|up\.link|webos|wos)/i", $user_agent);
4212 }
4213
4214 function wpvr_directory()
4215 {
4216 $upload = wp_upload_dir();
4217 $upload_dir = $upload['basedir'];
4218 $upload_dir_temp = $upload_dir . '/wpvr/temp/';
4219 if (!is_dir($upload_dir_temp)) {
4220 wp_mkdir_p($upload_dir_temp, 0700);
4221 }
4222 }
4223
4224 add_action('admin_init', 'wpvr_directory');
4225
4226
4227 function wpvr_add_role_cap()
4228 {
4229 $editor_active = get_option('wpvr_editor_active');
4230
4231 $author_active = get_option('wpvr_author_active');
4232
4233 $admin = get_role('administrator');
4234 $admin->add_cap('publish_wpvr_tour');
4235 $admin->add_cap('edit_wpvr_tours');
4236 $admin->add_cap('read_wpvr_tour');
4237 $admin->add_cap('edit_wpvr_tour');
4238 $admin->add_cap('edit_wpvr_tours');
4239 $admin->add_cap('publish_wpvr_tours');
4240 $admin->add_cap('publish_wpvr_tour');
4241 $admin->add_cap('delete_wpvr_tour');
4242 $admin->add_cap('edit_other_wpvr_tours');
4243 $admin->add_cap('delete_other_wpvr_tours');
4244
4245 if ($editor_active == "true") {
4246 $editor = get_role('editor');
4247 if ($editor) {
4248 $editor->add_cap('publish_wpvr_tour');
4249 $editor->add_cap('edit_wpvr_tours');
4250 $editor->add_cap('read_wpvr_tour');
4251 $editor->add_cap('edit_wpvr_tour');
4252 $editor->add_cap('edit_wpvr_tours');
4253 $editor->add_cap('publish_wpvr_tours');
4254 $editor->add_cap('publish_wpvr_tour');
4255 $editor->add_cap('delete_wpvr_tour');
4256 $editor->add_cap('edit_other_wpvr_tours');
4257 $editor->add_cap('delete_other_wpvr_tours');
4258 }
4259 } else {
4260 $editor = get_role('editor');
4261 if ($editor) {
4262 $editor->remove_cap('publish_wpvr_tour');
4263 $editor->remove_cap('edit_wpvr_tours');
4264 $editor->remove_cap('read_wpvr_tour');
4265 $editor->remove_cap('edit_wpvr_tour');
4266 $editor->remove_cap('edit_wpvr_tours');
4267 $editor->remove_cap('publish_wpvr_tours');
4268 $editor->remove_cap('publish_wpvr_tour');
4269 $editor->remove_cap('delete_wpvr_tour');
4270 $editor->remove_cap('edit_other_wpvr_tours');
4271 $editor->remove_cap('delete_other_wpvr_tours');
4272 }
4273 }
4274
4275 if ($author_active == "true") {
4276 $author = get_role('author');
4277 if ($author) {
4278 $author->add_cap('read_wpvr_tour');
4279 $author->add_cap('edit_wpvr_tour');
4280 $author->add_cap('edit_wpvr_tours');
4281 $author->add_cap('publish_wpvr_tours');
4282 $author->add_cap('publish_wpvr_tour');
4283 $author->add_cap('delete_wpvr_tour');
4284 }
4285 } else {
4286 $author = get_role('author');
4287 if ($author) {
4288 $author->remove_cap('read_wpvr_tour');
4289 $author->remove_cap('edit_wpvr_tour');
4290 $author->remove_cap('edit_wpvr_tours');
4291 $author->remove_cap('publish_wpvr_tours');
4292 $author->remove_cap('publish_wpvr_tour');
4293 $author->remove_cap('delete_wpvr_tour');
4294 }
4295 }
4296
4297 if(is_plugin_active( 'dokan-lite/dokan.php' ) || is_plugin_active( 'dokan-pro/dokan.php' )){
4298 $dokan_vendor_active = get_option('dokan_vendor_active');
4299
4300 if( 'true' === $dokan_vendor_active){
4301 $seller = get_role('seller');
4302 if ($seller) {
4303 $seller->add_cap('read_wpvr_tour');
4304 $seller->add_cap('edit_wpvr_tour');
4305 $seller->add_cap('edit_wpvr_tours');
4306 $seller->add_cap('publish_wpvr_tours');
4307 $seller->add_cap('publish_wpvr_tour');
4308 $seller->add_cap('delete_wpvr_tour');
4309 }
4310 } else{
4311 $seller = get_role('seller');
4312 if ($seller) {
4313 $seller->remove_cap('read_wpvr_tour');
4314 $seller->remove_cap('edit_wpvr_tour');
4315 $seller->remove_cap('edit_wpvr_tours');
4316 $seller->remove_cap('publish_wpvr_tours');
4317 $seller->remove_cap('publish_wpvr_tour');
4318 $seller->remove_cap('delete_wpvr_tour');
4319 }
4320 }
4321 }
4322
4323
4324 }
4325
4326 add_action('admin_init', 'wpvr_add_role_cap', 999);
4327
4328
4329
4330 function wpvr_cache_admin_notice()
4331 {
4332 $option = get_option('wpvr_warning');
4333 if (!$option) {
4334 ?>
4335 <div class="notice notice-warning" id="wpvr-warning" style="position: relative;">
4336 <p><?php echo wp_kses_post( __( 'Since you have updated the plugin, please clear the browser cache for smooth functioning. Follow these steps if you are using <a href="https://support.google.com/accounts/answer/32050?co=GENIE.Platform%3DDesktop&hl=en" target="_blank">Google Chrome</a>, <a href="https://support.mozilla.org/en-US/kb/how-clear-firefox-cache" target="_blank">Mozilla Firefox</a>, <a href="https://clear-my-cache.com/en/apple-mac-os/safari.html" target="_blank">Safai</a> or <a href="https://support.microsoft.com/en-us/help/10607/microsoft-edge-view-delete-browser-history" target="_blank">Microsoft Edge</a>', 'wpvr' ) ); ?></p>
4337 <button type="button" id="wpvr-dismissible" class="notice-dismiss"><span class="screen-reader-text">Dismiss this notice.</span></button>
4338 </div>
4339 <?php
4340 }
4341 }
4342 // add_action('admin_notices', 'wpvr_cache_admin_notice');
4343
4344 //===Oxygen widget===//
4345 add_action('plugins_loaded', function () {
4346 if (!class_exists('OxyEl')) {
4347 return;
4348 }
4349 require_once __DIR__ . '/oxygen/oxy-manager.php';
4350 });
4351
4352 add_action('init', 'wpvr_mobile_media_handle');
4353 function wpvr_mobile_media_handle()
4354 {
4355 add_image_size('wpvr_mobile', 4096, 2048); //mobile
4356 }
4357
4358
4359 add_action(
4360 /**
4361 * @param $api \VisualComposer\Modules\Api\Factory
4362 */
4363 'vcv:api',
4364 function ($api) {
4365 $elementsToRegister = [
4366 'wpvrelement',
4367 ];
4368 $pluginBaseUrl = rtrim(plugins_url(basename(__DIR__)), '\\/');
4369 /** @var \VisualComposer\Modules\Elements\ApiController $elementsApi */
4370 $elementsApi = $api->elements;
4371 foreach ($elementsToRegister as $tag) {
4372 $manifestPath = __DIR__ . '/vc/' . $tag . '/manifest.json';
4373 $elementBaseUrl = $pluginBaseUrl . '/vc/' . $tag;
4374 $elementsApi->add($manifestPath, $elementBaseUrl);
4375 }
4376 }
4377 );
4378
4379 function wpvr_redirect_after_activation($plugin)
4380 {
4381 if ($plugin == plugin_basename(__FILE__)) {
4382 $url = admin_url('admin.php?page=rex-wpvr-setup-wizard');
4383 $url = esc_url_raw( $url );
4384 wp_safe_redirect( $url );
4385 exit;
4386 }
4387 }
4388 //add_action('activated_plugin', 'wpvr_redirect_after_activation');
4389
4390 function replace_callback($matches)
4391 {
4392 foreach ($matches as $match) {
4393 return str_replace('<img', '<img decoding="async" ', $match);
4394 }
4395 }
4396
4397
4398 function wpvr_sanitize_iframe_only( $input ) {
4399 // Start with standard allowed post HTML (p, a, strong, etc.)
4400 $allowed_tags = wp_kses_allowed_html( 'post' );
4401
4402 // Explicitly allow <iframe> with specific safe attributes
4403 $allowed_tags['iframe'] = array(
4404 'src' => true,
4405 'width' => true,
4406 'height' => true,
4407 'title' => true,
4408 'frameborder' => true,
4409 'allow' => true,
4410 'allowfullscreen' => true,
4411 'referrerpolicy' => true,
4412 );
4413
4414 // Sanitize input
4415 return wp_kses( $input, $allowed_tags );
4416 }
4417