Version Description
- Library updated
- Fixed default icon issue on android.
=
Download this release
Release Info
Developer | rextheme |
Plugin | 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 +5 -1
- admin/lib/pannellum/.gitignore +0 -8
- admin/lib/pannellum/.npmignore +0 -8
- admin/lib/pannellum/src/js/pannellum.js +92 -42
- public/lib/pannellum/.gitignore +0 -8
- public/lib/pannellum/.npmignore +0 -8
- public/lib/pannellum/src/js/pannellum.js +106 -48
- wpvr.php +1 -1
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.
|
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
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
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 >
|
1438 |
config.yaw -= 360;
|
1439 |
-
} else if (config.yaw < -
|
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 >
|
1481 |
config.yaw -= 360;
|
1482 |
-
} else if (config.yaw < -
|
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 |
-
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 [-
|
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(-
|
2575 |
-
config.maxYaw = Math.max(-
|
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
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
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 >
|
1429 |
config.yaw -= 360;
|
1430 |
-
} else if (config.yaw < -
|
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 >
|
1472 |
config.yaw -= 360;
|
1473 |
-
} else if (config.yaw < -
|
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 |
-
|
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 |
-
|
2361 |
-
|
2362 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 [-
|
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(-
|
2558 |
-
config.maxYaw = Math.max(-
|
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.
|
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+
|