PluginProbe
WP VR – 360 Panorama and Virtual Tour Builder / 8.5.73
WP VR – 360 Panorama and Virtual Tour Builder v8.5.73
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.73, at wpvr.php

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