WP VR – 360 Panorama and virtual tour creator for WordPress - Version 4.5.0

Version Description

  • Library updated
  • Fixed default icon issue on android.

=

Download this release

Release Info

Developer rextheme
Plugin Icon 128x128 WP VR – 360 Panorama and virtual tour creator for WordPress
Version 4.5.0
Comparing to
See all releases

Code changes from version 4.4.0 to 4.5.0

README.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://rextheme.com/wp-vr-360-panorama-and-virtual-tour-creator-fo
4
  Tags: virtual tour, real estate tour, panorama, panorama viewer, virtual tour, 360 panorama, interactive tour
5
  Requires at least: 4.0
6
  Tested up to: 5.3
7
- Stable tag: 4.4.0
8
  Requires PHP: 5.6
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -346,5 +346,9 @@ Simply add "/plugins/wpvr" to exclusion field (or use the location where you sto
346
  * Draggable error fix
347
  * Control plugin scripts and styles to load them on specific pages only.
348
 
 
 
 
 
349
  == Upgrade Notice ==
350
  Please do update the WP VR to the latest version. Each update makes it sure your plugin is supporting all tour features.  
4
  Tags: virtual tour, real estate tour, panorama, panorama viewer, virtual tour, 360 panorama, interactive tour
5
  Requires at least: 4.0
6
  Tested up to: 5.3
7
+ Stable tag: 4.5.0
8
  Requires PHP: 5.6
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
346
  * Draggable error fix
347
  * Control plugin scripts and styles to load them on specific pages only.
348
 
349
+ = 4.5.0 =
350
+ * Library updated
351
+ * Fixed default icon issue on android.
352
+
353
  == Upgrade Notice ==
354
  Please do update the WP VR to the latest version. Each update makes it sure your plugin is supporting all tour features.  
admin/lib/pannellum/.gitignore DELETED
@@ -1,8 +0,0 @@
1
- # Ignore builds
2
- build/**
3
-
4
- # Ignore OS X stuff
5
- .DS_Store
6
-
7
- # Ignore generated docs
8
- utils/doc/generated_docs
 
 
 
 
 
 
 
 
admin/lib/pannellum/.npmignore DELETED
@@ -1,8 +0,0 @@
1
- # Ignore generated docs
2
- utils/doc/generated_docs
3
-
4
- # Ignore build utilities
5
- utils/build
6
-
7
- # Ignore examples
8
- examples
 
 
 
 
 
 
 
 
admin/lib/pannellum/src/js/pannellum.js CHANGED
@@ -120,7 +120,7 @@ defaultConfig.strings = {
120
  // Labels
121
  loadButtonLabel: 'Click to<br>Load<br>Panorama',
122
  loadingLabel: 'Loading...',
123
- bylineLabel: ' %s', // One substitution: author
124
 
125
  // Errors
126
  noPanoramaError: 'No panorama image was specified.',
@@ -158,11 +158,9 @@ uiContainer.appendChild(dragFix);
158
  // Display about information on right click
159
  var aboutMsg = document.createElement('span');
160
  aboutMsg.className = 'pnlm-about-msg';
161
-
162
  //==wpvr custom rextheme link==//
163
  aboutMsg.innerHTML = '<a href="https://rextheme.com/docs/wpvr-360-panorama-and-virtual-tour-creator-for-wordpress/" target="_blank">Rextheme</a>';
164
  //==wpvr custom rextheme link end==//
165
-
166
  uiContainer.appendChild(aboutMsg);
167
  dragFix.addEventListener('contextmenu', aboutMessage);
168
 
@@ -258,22 +256,15 @@ controls.orientation.addEventListener('mousedown', function(e) {e.stopPropagatio
258
  controls.orientation.addEventListener('touchstart', function(e) {e.stopPropagation();});
259
  controls.orientation.addEventListener('pointerdown', function(e) {e.stopPropagation();});
260
  controls.orientation.className = 'pnlm-orientation-button pnlm-orientation-button-inactive pnlm-sprite pnlm-controls pnlm-control';
261
- var orientationSupport, startOrientationIfSupported = false;
262
- function deviceOrientationTest(e) {
263
- window.removeEventListener('deviceorientation', deviceOrientationTest);
264
- if (e && e.alpha !== null && e.beta !== null && e.gamma !== null) {
265
- controls.container.appendChild(controls.orientation);
266
- orientationSupport = true;
267
- if (startOrientationIfSupported)
268
- startOrientation();
269
- } else {
270
- orientationSupport = false;
271
- }
272
- }
273
- if (window.DeviceOrientationEvent) {
274
- window.addEventListener('deviceorientation', deviceOrientationTest);
275
- } else {
276
- orientationSupport = false;
277
  }
278
 
279
  // Compass
@@ -406,7 +397,7 @@ function init() {
406
  anError(config.strings.fileAccessError.replace('%s', a.outerHTML));
407
  }
408
  var img = this.response;
409
- parseGPanoXMP(img);
410
  infoDisplay.load.msg.innerHTML = '';
411
  };
412
  xhr.onprogress = function(e) {
@@ -532,7 +523,7 @@ function onImageLoad() {
532
  * @private
533
  * @param {Image} image - Image to read XMP metadata from.
534
  */
535
- function parseGPanoXMP(image) {
536
  var reader = new FileReader();
537
  reader.addEventListener('loadend', function() {
538
  var img = reader.result;
@@ -608,6 +599,35 @@ function parseGPanoXMP(image) {
608
 
609
  // Load panorama
610
  panoImage.src = window.URL.createObjectURL(image);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
611
  });
612
  if (reader.readAsBinaryString !== undefined)
613
  reader.readAsBinaryString(image);
@@ -1434,9 +1454,9 @@ function render() {
1434
 
1435
  if (config.autoRotate !== false) {
1436
  // When auto-rotating this check needs to happen first (see issue #764)
1437
- if (config.yaw > 180) {
1438
  config.yaw -= 360;
1439
- } else if (config.yaw < -180) {
1440
  config.yaw += 360;
1441
  }
1442
  }
@@ -1477,9 +1497,9 @@ function render() {
1477
  if (!(config.autoRotate !== false)) {
1478
  // When not auto-rotating, this check needs to happen after the
1479
  // previous check (see issue #698)
1480
- if (config.yaw > 180) {
1481
  config.yaw -= 360;
1482
- } else if (config.yaw < -180) {
1483
  config.yaw += 360;
1484
  }
1485
  }
@@ -1739,7 +1759,7 @@ function createHotSpot(hs) {
1739
  if (config.basePath && !absoluteURL(imgp))
1740
  imgp = config.basePath + imgp;
1741
  a = document.createElement('a');
1742
- a.href = sanitizeURL(hs.URL ? hs.URL : imgp);
1743
  a.target = '_blank';
1744
  span.appendChild(a);
1745
  var image = document.createElement('img');
@@ -1751,7 +1771,7 @@ function createHotSpot(hs) {
1751
  span.style.maxWidth = 'initial';
1752
  } else if (hs.URL) {
1753
  a = document.createElement('a');
1754
- a.href = sanitizeURL(hs.URL);
1755
  if (hs.attributes) {
1756
  for (var key in hs.attributes) {
1757
  a.setAttribute(key, hs.attributes[key]);
@@ -2025,7 +2045,7 @@ function processOptions(isPreview) {
2025
  var authorText = escapeHTML(config[key]);
2026
  if (config.authorURL) {
2027
  var authorLink = document.createElement('a');
2028
- authorLink.href = sanitizeURL(config['authorURL']);
2029
  authorLink.target = '_blank';
2030
  authorLink.innerHTML = escapeHTML(config[key]);
2031
  authorText = authorLink.outerHTML;
@@ -2036,7 +2056,7 @@ function processOptions(isPreview) {
2036
 
2037
  case 'fallback':
2038
  var link = document.createElement('a');
2039
- link.href = sanitizeURL(config[key]);
2040
  link.target = '_blank';
2041
  link.textContent = 'Click here to view this panorama in an alternative viewer.';
2042
  var message = document.createElement('p');
@@ -2100,12 +2120,8 @@ function processOptions(isPreview) {
2100
  break;
2101
 
2102
  case 'orientationOnByDefault':
2103
- if (config[key]) {
2104
- if (orientationSupport === undefined)
2105
- startOrientationIfSupported = true;
2106
- else if (orientationSupport === true)
2107
- startOrientation();
2108
- }
2109
  break;
2110
  }
2111
  }
@@ -2365,7 +2381,7 @@ function stopOrientation() {
2365
  */
2366
  function startOrientation() {
2367
  if (typeof DeviceMotionEvent.requestPermission === 'function') {
2368
- DeviceOrientationEvent.requestPermission().then(response => {
2369
  if (response == 'granted') {
2370
  orientation = 1;
2371
  window.addEventListener('deviceorientation', orientationListener);
@@ -2402,15 +2418,49 @@ function escapeHTML(s) {
2402
  * The URL cannot be of protocol 'javascript'.
2403
  * @private
2404
  * @param {string} url - URL to sanitize
 
2405
  * @returns {string} Sanitized URL
2406
  */
2407
- function sanitizeURL(url) {
2408
- if (url.trim().toLowerCase().indexOf('javascript:') === 0) {
 
 
 
 
 
 
 
 
 
 
 
2409
  return 'about:blank';
2410
  }
2411
  return url;
2412
  }
2413
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2414
  /**
2415
  * Removes possibility of XSS atacks with URLs for CSS.
2416
  * The URL will be sanitized with `sanitizeURL()` and single quotes
@@ -2509,7 +2559,7 @@ this.setPitchBounds = function(bounds) {
2509
  * @returns {number} Yaw in degrees
2510
  */
2511
  this.getYaw = function() {
2512
- return config.yaw;
2513
  };
2514
 
2515
  /**
@@ -2564,15 +2614,15 @@ this.getYawBounds = function() {
2564
  };
2565
 
2566
  /**
2567
- * Set the minimum and maximum allowed yaws (in degrees [-180, 180]).
2568
  * @memberof Viewer
2569
  * @instance
2570
  * @param {number[]} bounds - [minimum yaw, maximum yaw]
2571
  * @returns {Viewer} `this`
2572
  */
2573
  this.setYawBounds = function(bounds) {
2574
- config.minYaw = Math.max(-180, Math.min(bounds[0], 180));
2575
- config.maxYaw = Math.max(-180, Math.min(bounds[1], 180));
2576
  return this;
2577
  };
2578
 
120
  // Labels
121
  loadButtonLabel: 'Click to<br>Load<br>Panorama',
122
  loadingLabel: 'Loading...',
123
+ bylineLabel: 'by %s', // One substitution: author
124
 
125
  // Errors
126
  noPanoramaError: 'No panorama image was specified.',
158
  // Display about information on right click
159
  var aboutMsg = document.createElement('span');
160
  aboutMsg.className = 'pnlm-about-msg';
 
161
  //==wpvr custom rextheme link==//
162
  aboutMsg.innerHTML = '<a href="https://rextheme.com/docs/wpvr-360-panorama-and-virtual-tour-creator-for-wordpress/" target="_blank">Rextheme</a>';
163
  //==wpvr custom rextheme link end==//
 
164
  uiContainer.appendChild(aboutMsg);
165
  dragFix.addEventListener('contextmenu', aboutMessage);
166
 
256
  controls.orientation.addEventListener('touchstart', function(e) {e.stopPropagation();});
257
  controls.orientation.addEventListener('pointerdown', function(e) {e.stopPropagation();});
258
  controls.orientation.className = 'pnlm-orientation-button pnlm-orientation-button-inactive pnlm-sprite pnlm-controls pnlm-control';
259
+ var orientationSupport = false;
260
+ if (window.DeviceOrientationEvent && location.protocol == 'https:' &&
261
+ navigator.userAgent.toLowerCase().indexOf('mobi') >= 0) {
262
+ // This user agent check is here because there's no way to check if a
263
+ // device has an inertia measurement unit. We used to be able to check if a
264
+ // DeviceOrientationEvent had non-null values, but with iOS 13 requiring a
265
+ // permission prompt to access such events, this is no longer possible.
266
+ controls.container.appendChild(controls.orientation);
267
+ orientationSupport = true;
 
 
 
 
 
 
 
268
  }
269
 
270
  // Compass
397
  anError(config.strings.fileAccessError.replace('%s', a.outerHTML));
398
  }
399
  var img = this.response;
400
+ parseGPanoXMP(img, p);
401
  infoDisplay.load.msg.innerHTML = '';
402
  };
403
  xhr.onprogress = function(e) {
523
  * @private
524
  * @param {Image} image - Image to read XMP metadata from.
525
  */
526
+ function parseGPanoXMP(image, url) {
527
  var reader = new FileReader();
528
  reader.addEventListener('loadend', function() {
529
  var img = reader.result;
599
 
600
  // Load panorama
601
  panoImage.src = window.URL.createObjectURL(image);
602
+ panoImage.onerror = function() {
603
+ // If the image fails to load, we check the Content Security Policy
604
+ // headers and see if they block loading images as blobs. If they
605
+ // do, we load the image directly from the URL. While this should
606
+ // allow the image to load, it does prevent parsing of XMP data.
607
+ function getCspHeaders() {
608
+ if (!window.fetch)
609
+ return null;
610
+ return window.fetch(document.location.href)
611
+ .then(function(resp){
612
+ return resp.headers.get('Content-Security-Policy');
613
+ });
614
+ }
615
+ getCspHeaders().then(function(cspHeaders) {
616
+ if (cspHeaders) {
617
+ var invalidImgSource = cspHeaders.split(";").find(function(p) {
618
+ var matchstring = p.match(/img-src(.*)/);
619
+ if (matchstring) {
620
+ return !matchstring[1].includes("blob");
621
+ }
622
+ });
623
+ if (invalidImgSource) {
624
+ console.log('CSP blocks blobs; reverting to URL.');
625
+ panoImage.crossOrigin = config.crossOrigin;
626
+ panoImage.src = url;
627
+ }
628
+ }
629
+ });
630
+ }
631
  });
632
  if (reader.readAsBinaryString !== undefined)
633
  reader.readAsBinaryString(image);
1454
 
1455
  if (config.autoRotate !== false) {
1456
  // When auto-rotating this check needs to happen first (see issue #764)
1457
+ if (config.yaw > 360) {
1458
  config.yaw -= 360;
1459
+ } else if (config.yaw < -360) {
1460
  config.yaw += 360;
1461
  }
1462
  }
1497
  if (!(config.autoRotate !== false)) {
1498
  // When not auto-rotating, this check needs to happen after the
1499
  // previous check (see issue #698)
1500
+ if (config.yaw > 360) {
1501
  config.yaw -= 360;
1502
+ } else if (config.yaw < -360) {
1503
  config.yaw += 360;
1504
  }
1505
  }
1759
  if (config.basePath && !absoluteURL(imgp))
1760
  imgp = config.basePath + imgp;
1761
  a = document.createElement('a');
1762
+ a.href = sanitizeURL(hs.URL ? hs.URL : imgp, true);
1763
  a.target = '_blank';
1764
  span.appendChild(a);
1765
  var image = document.createElement('img');
1771
  span.style.maxWidth = 'initial';
1772
  } else if (hs.URL) {
1773
  a = document.createElement('a');
1774
+ a.href = sanitizeURL(hs.URL, true);
1775
  if (hs.attributes) {
1776
  for (var key in hs.attributes) {
1777
  a.setAttribute(key, hs.attributes[key]);
2045
  var authorText = escapeHTML(config[key]);
2046
  if (config.authorURL) {
2047
  var authorLink = document.createElement('a');
2048
+ authorLink.href = sanitizeURL(config['authorURL'], true);
2049
  authorLink.target = '_blank';
2050
  authorLink.innerHTML = escapeHTML(config[key]);
2051
  authorText = authorLink.outerHTML;
2056
 
2057
  case 'fallback':
2058
  var link = document.createElement('a');
2059
+ link.href = sanitizeURL(config[key], true);
2060
  link.target = '_blank';
2061
  link.textContent = 'Click here to view this panorama in an alternative viewer.';
2062
  var message = document.createElement('p');
2120
  break;
2121
 
2122
  case 'orientationOnByDefault':
2123
+ if (config[key])
2124
+ startOrientation();
 
 
 
 
2125
  break;
2126
  }
2127
  }
2381
  */
2382
  function startOrientation() {
2383
  if (typeof DeviceMotionEvent.requestPermission === 'function') {
2384
+ DeviceOrientationEvent.requestPermission().then(function(response) {
2385
  if (response == 'granted') {
2386
  orientation = 1;
2387
  window.addEventListener('deviceorientation', orientationListener);
2418
  * The URL cannot be of protocol 'javascript'.
2419
  * @private
2420
  * @param {string} url - URL to sanitize
2421
+ * @param {boolean} href - True if URL is for link (blocks data URIs)
2422
  * @returns {string} Sanitized URL
2423
  */
2424
+ function sanitizeURL(url, href) {
2425
+ try {
2426
+ var decoded_url = decodeURIComponent(unescape(url)).replace(/[^\w:]/g, '').toLowerCase();
2427
+ } catch (e) {
2428
+ return 'about:blank';
2429
+ }
2430
+ if (decoded_url.indexOf('javascript:') === 0 ||
2431
+ decoded_url.indexOf('vbscript:') === 0) {
2432
+ console.log('Script URL removed.');
2433
+ return 'about:blank';
2434
+ }
2435
+ if (href && decoded_url.indexOf('data:') === 0) {
2436
+ console.log('Data URI removed from link.');
2437
  return 'about:blank';
2438
  }
2439
  return url;
2440
  }
2441
 
2442
+ /**
2443
+ * Unescapes HTML entities.
2444
+ * Copied from Marked.js 0.7.0.
2445
+ * @private
2446
+ * @param {string} url - URL to sanitize
2447
+ * @param {boolean} href - True if URL is for link (blocks data URIs)
2448
+ * @returns {string} Sanitized URL
2449
+ */
2450
+ function unescape(html) {
2451
+ // Explicitly match decimal, hex, and named HTML entities
2452
+ return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) {
2453
+ n = n.toLowerCase();
2454
+ if (n === 'colon') return ':';
2455
+ if (n.charAt(0) === '#') {
2456
+ return n.charAt(1) === 'x'
2457
+ ? String.fromCharCode(parseInt(n.substring(2), 16))
2458
+ : String.fromCharCode(+n.substring(1));
2459
+ }
2460
+ return '';
2461
+ });
2462
+ }
2463
+
2464
  /**
2465
  * Removes possibility of XSS atacks with URLs for CSS.
2466
  * The URL will be sanitized with `sanitizeURL()` and single quotes
2559
  * @returns {number} Yaw in degrees
2560
  */
2561
  this.getYaw = function() {
2562
+ return (config.yaw + 540) % 360 - 180;
2563
  };
2564
 
2565
  /**
2614
  };
2615
 
2616
  /**
2617
+ * Set the minimum and maximum allowed yaws (in degrees [-360, 360]).
2618
  * @memberof Viewer
2619
  * @instance
2620
  * @param {number[]} bounds - [minimum yaw, maximum yaw]
2621
  * @returns {Viewer} `this`
2622
  */
2623
  this.setYawBounds = function(bounds) {
2624
+ config.minYaw = Math.max(-360, Math.min(bounds[0], 360));
2625
+ config.maxYaw = Math.max(-360, Math.min(bounds[1], 360));
2626
  return this;
2627
  };
2628
 
public/lib/pannellum/.gitignore DELETED
@@ -1,8 +0,0 @@
1
- # Ignore builds
2
- build/**
3
-
4
- # Ignore OS X stuff
5
- .DS_Store
6
-
7
- # Ignore generated docs
8
- utils/doc/generated_docs
 
 
 
 
 
 
 
 
public/lib/pannellum/.npmignore DELETED
@@ -1,8 +0,0 @@
1
- # Ignore generated docs
2
- utils/doc/generated_docs
3
-
4
- # Ignore build utilities
5
- utils/build
6
-
7
- # Ignore examples
8
- examples
 
 
 
 
 
 
 
 
public/lib/pannellum/src/js/pannellum.js CHANGED
@@ -120,7 +120,7 @@ defaultConfig.strings = {
120
  // Labels
121
  loadButtonLabel: 'Click to<br>Load<br>Panorama',
122
  loadingLabel: 'Loading...',
123
- bylineLabel: ' %s', // One substitution: author
124
 
125
  // Errors
126
  noPanoramaError: 'No panorama image was specified.',
@@ -158,11 +158,9 @@ uiContainer.appendChild(dragFix);
158
  // Display about information on right click
159
  var aboutMsg = document.createElement('span');
160
  aboutMsg.className = 'pnlm-about-msg';
161
-
162
  //==wpvr custom rextheme link==//
163
  aboutMsg.innerHTML = '<a href="https://rextheme.com/docs/wpvr-360-panorama-and-virtual-tour-creator-for-wordpress/" target="_blank">Rextheme</a>';
164
  //==wpvr custom rextheme link end==//
165
-
166
  uiContainer.appendChild(aboutMsg);
167
  dragFix.addEventListener('contextmenu', aboutMessage);
168
 
@@ -258,22 +256,15 @@ controls.orientation.addEventListener('mousedown', function(e) {e.stopPropagatio
258
  controls.orientation.addEventListener('touchstart', function(e) {e.stopPropagation();});
259
  controls.orientation.addEventListener('pointerdown', function(e) {e.stopPropagation();});
260
  controls.orientation.className = 'pnlm-orientation-button pnlm-orientation-button-inactive pnlm-sprite pnlm-controls pnlm-control';
261
- var orientationSupport, startOrientationIfSupported = false;
262
- function deviceOrientationTest(e) {
263
- window.removeEventListener('deviceorientation', deviceOrientationTest);
264
- if (e && e.alpha !== null && e.beta !== null && e.gamma !== null) {
265
- controls.container.appendChild(controls.orientation);
266
- orientationSupport = true;
267
- if (startOrientationIfSupported)
268
- startOrientation();
269
- } else {
270
- orientationSupport = false;
271
- }
272
- }
273
- if (window.DeviceOrientationEvent) {
274
- window.addEventListener('deviceorientation', deviceOrientationTest);
275
- } else {
276
- orientationSupport = false;
277
  }
278
 
279
  // Compass
@@ -406,7 +397,7 @@ function init() {
406
  anError(config.strings.fileAccessError.replace('%s', a.outerHTML));
407
  }
408
  var img = this.response;
409
- parseGPanoXMP(img);
410
  infoDisplay.load.msg.innerHTML = '';
411
  };
412
  xhr.onprogress = function(e) {
@@ -532,7 +523,7 @@ function onImageLoad() {
532
  * @private
533
  * @param {Image} image - Image to read XMP metadata from.
534
  */
535
- function parseGPanoXMP(image) {
536
  var reader = new FileReader();
537
  reader.addEventListener('loadend', function() {
538
  var img = reader.result;
@@ -608,6 +599,35 @@ function parseGPanoXMP(image) {
608
 
609
  // Load panorama
610
  panoImage.src = window.URL.createObjectURL(image);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
611
  });
612
  if (reader.readAsBinaryString !== undefined)
613
  reader.readAsBinaryString(image);
@@ -1425,9 +1445,9 @@ function render() {
1425
 
1426
  if (config.autoRotate !== false) {
1427
  // When auto-rotating this check needs to happen first (see issue #764)
1428
- if (config.yaw > 180) {
1429
  config.yaw -= 360;
1430
- } else if (config.yaw < -180) {
1431
  config.yaw += 360;
1432
  }
1433
  }
@@ -1468,9 +1488,9 @@ function render() {
1468
  if (!(config.autoRotate !== false)) {
1469
  // When not auto-rotating, this check needs to happen after the
1470
  // previous check (see issue #698)
1471
- if (config.yaw > 180) {
1472
  config.yaw -= 360;
1473
- } else if (config.yaw < -180) {
1474
  config.yaw += 360;
1475
  }
1476
  }
@@ -1599,8 +1619,6 @@ function computeQuaternion(alpha, beta, gamma) {
1599
  * @param {DeviceOrientationEvent} event - Device orientation event.
1600
  */
1601
  function orientationListener(e) {
1602
- if (e.hasOwnProperty('requestPermission'))
1603
- e.requestPermission()
1604
  var q = computeQuaternion(e.alpha, e.beta, e.gamma).toEulerAngles();
1605
  if (typeof(orientation) == 'number' && orientation < 10) {
1606
  // This kludge is necessary because iOS sometimes provides a few stale
@@ -1690,9 +1708,9 @@ function renderInitCallback() {
1690
  }
1691
  loaded = true;
1692
 
1693
- fireEvent('load');
1694
-
1695
  animateInit();
 
 
1696
  }
1697
 
1698
  /**
@@ -1732,7 +1750,7 @@ function createHotSpot(hs) {
1732
  if (config.basePath && !absoluteURL(imgp))
1733
  imgp = config.basePath + imgp;
1734
  a = document.createElement('a');
1735
- a.href = sanitizeURL(hs.URL ? hs.URL : imgp);
1736
  a.target = '_blank';
1737
  span.appendChild(a);
1738
  var image = document.createElement('img');
@@ -1744,7 +1762,7 @@ function createHotSpot(hs) {
1744
  span.style.maxWidth = 'initial';
1745
  } else if (hs.URL) {
1746
  a = document.createElement('a');
1747
- a.href = sanitizeURL(hs.URL);
1748
  if (hs.attributes) {
1749
  for (var key in hs.attributes) {
1750
  a.setAttribute(key, hs.attributes[key]);
@@ -2018,7 +2036,7 @@ function processOptions(isPreview) {
2018
  var authorText = escapeHTML(config[key]);
2019
  if (config.authorURL) {
2020
  var authorLink = document.createElement('a');
2021
- authorLink.href = sanitizeURL(config['authorURL']);
2022
  authorLink.target = '_blank';
2023
  authorLink.innerHTML = escapeHTML(config[key]);
2024
  authorText = authorLink.outerHTML;
@@ -2029,7 +2047,7 @@ function processOptions(isPreview) {
2029
 
2030
  case 'fallback':
2031
  var link = document.createElement('a');
2032
- link.href = sanitizeURL(config[key]);
2033
  link.target = '_blank';
2034
  link.textContent = 'Click here to view this panorama in an alternative viewer.';
2035
  var message = document.createElement('p');
@@ -2093,12 +2111,8 @@ function processOptions(isPreview) {
2093
  break;
2094
 
2095
  case 'orientationOnByDefault':
2096
- if (config[key]) {
2097
- if (orientationSupport === undefined)
2098
- startOrientationIfSupported = true;
2099
- else if (orientationSupport === true)
2100
- startOrientation();
2101
- }
2102
  break;
2103
  }
2104
  }
@@ -2357,9 +2371,19 @@ function stopOrientation() {
2357
  * @private
2358
  */
2359
  function startOrientation() {
2360
- orientation = 1;
2361
- window.addEventListener('deviceorientation', orientationListener);
2362
- controls.orientation.classList.add('pnlm-orientation-button-active');
 
 
 
 
 
 
 
 
 
 
2363
  }
2364
 
2365
  /**
@@ -2385,15 +2409,49 @@ function escapeHTML(s) {
2385
  * The URL cannot be of protocol 'javascript'.
2386
  * @private
2387
  * @param {string} url - URL to sanitize
 
2388
  * @returns {string} Sanitized URL
2389
  */
2390
- function sanitizeURL(url) {
2391
- if (url.trim().toLowerCase().indexOf('javascript:') === 0) {
 
 
 
 
 
 
 
 
 
 
 
2392
  return 'about:blank';
2393
  }
2394
  return url;
2395
  }
2396
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2397
  /**
2398
  * Removes possibility of XSS atacks with URLs for CSS.
2399
  * The URL will be sanitized with `sanitizeURL()` and single quotes
@@ -2492,7 +2550,7 @@ this.setPitchBounds = function(bounds) {
2492
  * @returns {number} Yaw in degrees
2493
  */
2494
  this.getYaw = function() {
2495
- return config.yaw;
2496
  };
2497
 
2498
  /**
@@ -2547,15 +2605,15 @@ this.getYawBounds = function() {
2547
  };
2548
 
2549
  /**
2550
- * Set the minimum and maximum allowed yaws (in degrees [-180, 180]).
2551
  * @memberof Viewer
2552
  * @instance
2553
  * @param {number[]} bounds - [minimum yaw, maximum yaw]
2554
  * @returns {Viewer} `this`
2555
  */
2556
  this.setYawBounds = function(bounds) {
2557
- config.minYaw = Math.max(-180, Math.min(bounds[0], 180));
2558
- config.maxYaw = Math.max(-180, Math.min(bounds[1], 180));
2559
  return this;
2560
  };
2561
 
120
  // Labels
121
  loadButtonLabel: 'Click to<br>Load<br>Panorama',
122
  loadingLabel: 'Loading...',
123
+ bylineLabel: 'by %s', // One substitution: author
124
 
125
  // Errors
126
  noPanoramaError: 'No panorama image was specified.',
158
  // Display about information on right click
159
  var aboutMsg = document.createElement('span');
160
  aboutMsg.className = 'pnlm-about-msg';
 
161
  //==wpvr custom rextheme link==//
162
  aboutMsg.innerHTML = '<a href="https://rextheme.com/docs/wpvr-360-panorama-and-virtual-tour-creator-for-wordpress/" target="_blank">Rextheme</a>';
163
  //==wpvr custom rextheme link end==//
 
164
  uiContainer.appendChild(aboutMsg);
165
  dragFix.addEventListener('contextmenu', aboutMessage);
166
 
256
  controls.orientation.addEventListener('touchstart', function(e) {e.stopPropagation();});
257
  controls.orientation.addEventListener('pointerdown', function(e) {e.stopPropagation();});
258
  controls.orientation.className = 'pnlm-orientation-button pnlm-orientation-button-inactive pnlm-sprite pnlm-controls pnlm-control';
259
+ var orientationSupport = false;
260
+ if (window.DeviceOrientationEvent && location.protocol == 'https:' &&
261
+ navigator.userAgent.toLowerCase().indexOf('mobi') >= 0) {
262
+ // This user agent check is here because there's no way to check if a
263
+ // device has an inertia measurement unit. We used to be able to check if a
264
+ // DeviceOrientationEvent had non-null values, but with iOS 13 requiring a
265
+ // permission prompt to access such events, this is no longer possible.
266
+ controls.container.appendChild(controls.orientation);
267
+ orientationSupport = true;
 
 
 
 
 
 
 
268
  }
269
 
270
  // Compass
397
  anError(config.strings.fileAccessError.replace('%s', a.outerHTML));
398
  }
399
  var img = this.response;
400
+ parseGPanoXMP(img, p);
401
  infoDisplay.load.msg.innerHTML = '';
402
  };
403
  xhr.onprogress = function(e) {
523
  * @private
524
  * @param {Image} image - Image to read XMP metadata from.
525
  */
526
+ function parseGPanoXMP(image, url) {
527
  var reader = new FileReader();
528
  reader.addEventListener('loadend', function() {
529
  var img = reader.result;
599
 
600
  // Load panorama
601
  panoImage.src = window.URL.createObjectURL(image);
602
+ panoImage.onerror = function() {
603
+ // If the image fails to load, we check the Content Security Policy
604
+ // headers and see if they block loading images as blobs. If they
605
+ // do, we load the image directly from the URL. While this should
606
+ // allow the image to load, it does prevent parsing of XMP data.
607
+ function getCspHeaders() {
608
+ if (!window.fetch)
609
+ return null;
610
+ return window.fetch(document.location.href)
611
+ .then(function(resp){
612
+ return resp.headers.get('Content-Security-Policy');
613
+ });
614
+ }
615
+ getCspHeaders().then(function(cspHeaders) {
616
+ if (cspHeaders) {
617
+ var invalidImgSource = cspHeaders.split(";").find(function(p) {
618
+ var matchstring = p.match(/img-src(.*)/);
619
+ if (matchstring) {
620
+ return !matchstring[1].includes("blob");
621
+ }
622
+ });
623
+ if (invalidImgSource) {
624
+ console.log('CSP blocks blobs; reverting to URL.');
625
+ panoImage.crossOrigin = config.crossOrigin;
626
+ panoImage.src = url;
627
+ }
628
+ }
629
+ });
630
+ }
631
  });
632
  if (reader.readAsBinaryString !== undefined)
633
  reader.readAsBinaryString(image);
1445
 
1446
  if (config.autoRotate !== false) {
1447
  // When auto-rotating this check needs to happen first (see issue #764)
1448
+ if (config.yaw > 360) {
1449
  config.yaw -= 360;
1450
+ } else if (config.yaw < -360) {
1451
  config.yaw += 360;
1452
  }
1453
  }
1488
  if (!(config.autoRotate !== false)) {
1489
  // When not auto-rotating, this check needs to happen after the
1490
  // previous check (see issue #698)
1491
+ if (config.yaw > 360) {
1492
  config.yaw -= 360;
1493
+ } else if (config.yaw < -360) {
1494
  config.yaw += 360;
1495
  }
1496
  }
1619
  * @param {DeviceOrientationEvent} event - Device orientation event.
1620
  */
1621
  function orientationListener(e) {
 
 
1622
  var q = computeQuaternion(e.alpha, e.beta, e.gamma).toEulerAngles();
1623
  if (typeof(orientation) == 'number' && orientation < 10) {
1624
  // This kludge is necessary because iOS sometimes provides a few stale
1708
  }
1709
  loaded = true;
1710
 
 
 
1711
  animateInit();
1712
+
1713
+ fireEvent('load');
1714
  }
1715
 
1716
  /**
1750
  if (config.basePath && !absoluteURL(imgp))
1751
  imgp = config.basePath + imgp;
1752
  a = document.createElement('a');
1753
+ a.href = sanitizeURL(hs.URL ? hs.URL : imgp, true);
1754
  a.target = '_blank';
1755
  span.appendChild(a);
1756
  var image = document.createElement('img');
1762
  span.style.maxWidth = 'initial';
1763
  } else if (hs.URL) {
1764
  a = document.createElement('a');
1765
+ a.href = sanitizeURL(hs.URL, true);
1766
  if (hs.attributes) {
1767
  for (var key in hs.attributes) {
1768
  a.setAttribute(key, hs.attributes[key]);
2036
  var authorText = escapeHTML(config[key]);
2037
  if (config.authorURL) {
2038
  var authorLink = document.createElement('a');
2039
+ authorLink.href = sanitizeURL(config['authorURL'], true);
2040
  authorLink.target = '_blank';
2041
  authorLink.innerHTML = escapeHTML(config[key]);
2042
  authorText = authorLink.outerHTML;
2047
 
2048
  case 'fallback':
2049
  var link = document.createElement('a');
2050
+ link.href = sanitizeURL(config[key], true);
2051
  link.target = '_blank';
2052
  link.textContent = 'Click here to view this panorama in an alternative viewer.';
2053
  var message = document.createElement('p');
2111
  break;
2112
 
2113
  case 'orientationOnByDefault':
2114
+ if (config[key])
2115
+ startOrientation();
 
 
 
 
2116
  break;
2117
  }
2118
  }
2371
  * @private
2372
  */
2373
  function startOrientation() {
2374
+ if (typeof DeviceMotionEvent.requestPermission === 'function') {
2375
+ DeviceOrientationEvent.requestPermission().then(function(response) {
2376
+ if (response == 'granted') {
2377
+ orientation = 1;
2378
+ window.addEventListener('deviceorientation', orientationListener);
2379
+ controls.orientation.classList.add('pnlm-orientation-button-active');
2380
+ }
2381
+ });
2382
+ } else {
2383
+ orientation = 1;
2384
+ window.addEventListener('deviceorientation', orientationListener);
2385
+ controls.orientation.classList.add('pnlm-orientation-button-active');
2386
+ }
2387
  }
2388
 
2389
  /**
2409
  * The URL cannot be of protocol 'javascript'.
2410
  * @private
2411
  * @param {string} url - URL to sanitize
2412
+ * @param {boolean} href - True if URL is for link (blocks data URIs)
2413
  * @returns {string} Sanitized URL
2414
  */
2415
+ function sanitizeURL(url, href) {
2416
+ try {
2417
+ var decoded_url = decodeURIComponent(unescape(url)).replace(/[^\w:]/g, '').toLowerCase();
2418
+ } catch (e) {
2419
+ return 'about:blank';
2420
+ }
2421
+ if (decoded_url.indexOf('javascript:') === 0 ||
2422
+ decoded_url.indexOf('vbscript:') === 0) {
2423
+ console.log('Script URL removed.');
2424
+ return 'about:blank';
2425
+ }
2426
+ if (href && decoded_url.indexOf('data:') === 0) {
2427
+ console.log('Data URI removed from link.');
2428
  return 'about:blank';
2429
  }
2430
  return url;
2431
  }
2432
 
2433
+ /**
2434
+ * Unescapes HTML entities.
2435
+ * Copied from Marked.js 0.7.0.
2436
+ * @private
2437
+ * @param {string} url - URL to sanitize
2438
+ * @param {boolean} href - True if URL is for link (blocks data URIs)
2439
+ * @returns {string} Sanitized URL
2440
+ */
2441
+ function unescape(html) {
2442
+ // Explicitly match decimal, hex, and named HTML entities
2443
+ return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) {
2444
+ n = n.toLowerCase();
2445
+ if (n === 'colon') return ':';
2446
+ if (n.charAt(0) === '#') {
2447
+ return n.charAt(1) === 'x'
2448
+ ? String.fromCharCode(parseInt(n.substring(2), 16))
2449
+ : String.fromCharCode(+n.substring(1));
2450
+ }
2451
+ return '';
2452
+ });
2453
+ }
2454
+
2455
  /**
2456
  * Removes possibility of XSS atacks with URLs for CSS.
2457
  * The URL will be sanitized with `sanitizeURL()` and single quotes
2550
  * @returns {number} Yaw in degrees
2551
  */
2552
  this.getYaw = function() {
2553
+ return (config.yaw + 540) % 360 - 180;
2554
  };
2555
 
2556
  /**
2605
  };
2606
 
2607
  /**
2608
+ * Set the minimum and maximum allowed yaws (in degrees [-360, 360]).
2609
  * @memberof Viewer
2610
  * @instance
2611
  * @param {number[]} bounds - [minimum yaw, maximum yaw]
2612
  * @returns {Viewer} `this`
2613
  */
2614
  this.setYawBounds = function(bounds) {
2615
+ config.minYaw = Math.max(-360, Math.min(bounds[0], 360));
2616
+ config.maxYaw = Math.max(-360, Math.min(bounds[1], 360));
2617
  return this;
2618
  };
2619
 
wpvr.php CHANGED
@@ -16,7 +16,7 @@
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: 4.4.0
20
  * Author: Rextheme
21
  * Author URI: http://rextheme.com/
22
  * License: GPL-2.0+
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: 4.5.0
20
  * Author: Rextheme
21
  * Author URI: http://rextheme.com/
22
  * License: GPL-2.0+