diff --git a/README.md b/README.md index 352cc1bee..58fbc57af 100644 --- a/README.md +++ b/README.md @@ -13,18 +13,23 @@ Photo Sphere Viewer uses the [Three.js](http://threejs.org) library, so nothing * `container` (required): the `div` in which the panorama will be displayed. * `autoload` (optional, default to `true`): `true` to automatically load the panorama, `false` to load it later (with the `.load()` method). * `usexmpdata` (optional, default to `true`): `true` if Photo Sphere Viewer must read XMP data, `false` if it is not necessary. + * `pano_size` (optional, default to null) The panorama size, if cropped (unnecessary if XMP data can be read). * `default_position` (optional, default to `{}`) Defines the default position, the first point seen by the user (e.g. `{long: Math.PI, lat: Math.PI/2}`). * `min_fov` (optional, default to `30`): the minimal field of view, in degrees, between 1 and 179. * `max_fov` (optional, default to `90`): the maximal field of view, in degrees, between 1 and 179. * `allow_user_interactions` (optional, default to `true`): If set to `false`, the user won't be able to interact with the panorama (navigation bar is then disabled). * `tilt_up_max` (optional, default to `Math.PI/2`) The maximal tilt up angle, in radians. * `tilt_down_max` (optional, default to `Math.PI/2`) The maximal tilt down angle, in radians. + * `min_longitude` (optional, default to 0) The minimal longitude to show. + * `max_longitude` (optional, default to 2π) The maximal longitude to show. * `zoom_level` (optional, default to `0`) The default zoom level, between 0 and 100. * `long_offset` (optional, default to `PI/360`): the longitude to travel per pixel moved by mouse/touch. * `lat_offset` (optional, default to `PI/180`): the latitude to travel per pixel moved by mouse/touch. * `time_anim` (optional, default to `2000`): the panorama will be automatically animated after `time_anim` milliseconds (indicate `false` to deactivate it). - * `theta_offset` (deprecated, optional, default to `1440`): the horizontal speed during the automatic animation (we add `PI / theta_offset` to the angle). + * `reverse_anim` (optional, default to true) `true` if horizontal animation must be reversed when min/max longitude is reached (only if the whole circle is not described). * `anim_speed` (optional, default to `2rpm`): animation speed in radians/degrees/revolutions per second/minute. + * `vertical_anim_speed` (optional, default to `2rpm`): vertical animation speed in radians/degrees/revolutions per second/minute. + * `vertical_anim_target` (optional, default to `0`): latitude to target during the autorotate animation, default to the equator. * `navbar` (optional, default to `false`): set to `true`, a navigation bar will be displayed. * `navbar_style` (optional, default to `{}`): a custom style for the navigation bar. See the list below to know what properties are available. * `backgroundColor`: the navigation bar background color (default to `rgba(61, 61, 61, 0.5)`). @@ -42,6 +47,7 @@ Photo Sphere Viewer uses the [Three.js](http://threejs.org) library, so nothing Colors can be in `rgb()`, `rgba()` or hexadecimal format, and the keyword `transparent` is accepted. * `loading_msg` (optional, default to `Loading…`): Loading message. * `loading_img` (optional, default to `null`): the path to the image shown during the loading. + * `loading_html` (optional, default to null) An HTML loader (element to append to the container or string representing the HTML). * `size` (optional, default to `null`): the final size of the panorama container (e.g. `{width: 500, height: 300}`). * `onready` (optional, default to `null`) Function called once the panorama is ready and the first image is displayed. diff --git a/examples/example1.css b/examples/example1.css index 27211d730..1ce18e2bc 100644 --- a/examples/example1.css +++ b/examples/example1.css @@ -1,3 +1,9 @@ +@keyframes spin { + to { + transform: rotate(1turn); + } +} + body { margin: 0; font-family: 'Liberation Sans', sans-serif; @@ -30,6 +36,18 @@ header h1 { margin: 0 auto; } +.loader { + margin: 0 auto; + width: 30px; + height: 30px; + + border: 3px solid transparent; + border-top-color: #0096FF; + border-radius: 50%; + + animation: spin 0.8s ease infinite; +} + p { margin-bottom: 30px; } diff --git a/examples/example1.js b/examples/example1.js index 0226713cb..463b3c0a9 100644 --- a/examples/example1.js +++ b/examples/example1.js @@ -8,7 +8,13 @@ window.onload = function() { function loadPredefinedPanorama(evt) { evt.preventDefault(); + // Loader + var loader = document.createElement('div'); + loader.className = 'loader'; + + // Panorama display var div = document.getElementById('container'); + div.style.height = '30px'; var PSV = new PhotoSphereViewer({ // Path to the panorama @@ -27,7 +33,10 @@ function loadPredefinedPanorama(evt) { size: { width: '100%', height: '500px' - } + }, + + // HTML loader + loading_html: loader }); } @@ -57,7 +66,10 @@ function upload() { size: { width: '100%', height: '500px' - } + }, + + // No XMP data + usexmpdata: false }); }; diff --git a/photo-sphere-viewer.js b/photo-sphere-viewer.js index ebbc645fa..b047b4193 100644 --- a/photo-sphere-viewer.js +++ b/photo-sphere-viewer.js @@ -1,5 +1,5 @@ /* - * Photo Sphere Viewer v2.3.1 + * Photo Sphere Viewer v2.4 * http://jeremyheleine.me/photo-sphere-viewer * * Copyright (c) 2014,2015 Jérémy Heleine @@ -31,6 +31,13 @@ * @param {HTMLElement} args.container - Panorama container (should be a `div` or equivalent) * @param {boolean} [args.autoload=true] - `true` to automatically load the panorama, `false` to load it later (with the {@link PhotoSphereViewer#load|`.load`} method) * @param {boolean} [args.usexmpdata=true] - `true` if Photo Sphere Viewer must read XMP data, `false` if it is not necessary + * @param {object} [args.pano_size=null] - The panorama size, if cropped (unnecessary if XMP data can be read) + * @param {number} [args.pano_size.full_width=null] - The full panorama width, before crop (the image width if `null`) + * @param {number} [args.pano_size.full_height=null] - The full panorama height, before crop (the image height if `null`) + * @param {number} [args.pano_size.cropped_width=null] - The cropped panorama width (the image width if `null`) + * @param {number} [args.pano_size.cropped_height=null] - The cropped panorama height (the image height if `null`) + * @param {number} [args.pano_size.cropped_x=null] - The cropped panorama horizontal offset relative to the full width (middle if `null`) + * @param {number} [args.pano_size.cropped_y=null] - The cropped panorama vertical offset relative to the full height (middle if `null`) * @param {object} [args.default_position] - Defines the default position (the first point seen by the user) * @param {number|string} [args.default_position.long=0] - Default longitude, in radians (or in degrees if indicated, e.g. `'45deg'`) * @param {number|string} [args.default_position.lat=0] - Default latitude, in radians (or in degrees if indicated, e.g. `'45deg'`) @@ -39,12 +46,16 @@ * @param {boolean} [args.allow_user_interactions=true] - If set to `false`, the user won't be able to interact with the panorama (navigation bar is then disabled) * @param {number|string} [args.tilt_up_max=π/2] - The maximal tilt up angle, in radians (or in degrees if indicated, e.g. `'30deg'`) * @param {number|string} [args.tilt_down_max=π/2] - The maximal tilt down angle, in radians (or in degrees if indicated, e.g. `'30deg'`) + * @param {number|string} [args.min_longitude=0] - The minimal longitude to show + * @param {number|string} [args.max_longitude=2π] - The maximal longitude to show * @param {number} [args.zoom_level=0] - The default zoom level, between 0 and 100 * @param {number} [args.long_offset=π/360] - The longitude to travel per pixel moved by mouse/touch * @param {number} [args.lat_offset=π/180] - The latitude to travel per pixel moved by mouse/touch * @param {integer} [args.time_anim=2000] - Delay before automatically animating the panorama in milliseconds, `false` to not animate - * @param {integer} [args.theta_offset=1440] - (deprecated, use `anim_speed` instead) The π fraction to add to theta during the animation + * @param {boolean} [args.reverse_anim=true] - `true` if horizontal animation must be reversed when min/max longitude is reached (only if the whole circle is not described) * @param {string} [args.anim_speed=2rpm] - Animation speed in radians/degrees/revolutions per second/minute + * @param {string} [args.vertical_anim_speed=2rpm] - Vertical animation speed in radians/degrees/revolutions per second/minute + * @param {number|string} [args.vertical_anim_target=0] - Latitude to target during the autorotate animation, default to the equator * @param {boolean} [args.navbar=false] - Display the navigation bar if set to `true` * @param {object} [args.navbar_style] - Style of the navigation bar * @param {string} [args.navbar_style.backgroundColor=rgba(61, 61, 61, 0.5)] - Navigation bar background color @@ -60,6 +71,7 @@ * @param {number} [args.navbar_style.fullscreenThickness=2] - Fullscreen icon thickness in pixels * @param {string} [args.loading_msg=Loading…] - Loading message * @param {string} [args.loading_img=null] - Loading image URL or path (absolute or relative) + * @param {HTMLElement|string} [args.loading_html=null] - An HTML loader (element to append to the container or string representing the HTML) * @param {object} [args.size] - Final size of the panorama container (e.g. {width: 500, height: 300}) * @param {(number|string)} [args.size.width] - Final width in percentage (e.g. `'50%'`) or pixels (e.g. `500` or `'500px'`) ; default to current width * @param {(number|string)} [args.size.height] - Final height in percentage or pixels ; default to current height @@ -152,13 +164,25 @@ var PhotoSphereViewer = function(args) { **/ this.load = function() { - // Loading indicator (text or image if given) - if (!!loading_img) { + container.innerHTML = ''; + + // Loading HTML: HTMLElement + if (!!loading_html && loading_html.nodeType === 1) + container.appendChild(loading_html); + + // Loading HTML: string + else if (!!loading_html && typeof loading_html == 'string') + container.innerHTML = loading_html; + + // Loading image + else if (!!loading_img) { var loading = document.createElement('img'); loading.setAttribute('src', loading_img); loading.setAttribute('alt', loading_msg); container.appendChild(loading); } + + // Loading text else container.textContent = loading_msg; @@ -189,11 +213,11 @@ var PhotoSphereViewer = function(args) { }; // XMP data? - if (readxmp) + if (readxmp && !panorama.match(/^data:image\/[a-z]+;base64/)) loadXMP(); else - createBuffer(false); + createBuffer(); }; /** @@ -236,31 +260,31 @@ var PhotoSphereViewer = function(args) { } xhr.onreadystatechange = function() { - if (xhr.readyState == 4 && xhr.status == 200) { - // Metadata - var binary = xhr.responseText; - var a = binary.indexOf(''); - var data = binary.substring(a, b); - - // No data retrieved - if (a == -1 || b == -1 || data.indexOf('GPano:') == -1) { - createBuffer(false); - return; - } - - // Useful values - var pano_data = { - full_width: parseInt(getAttribute(data, 'FullPanoWidthPixels')), - full_height: parseInt(getAttribute(data, 'FullPanoHeightPixels')), - cropped_width: parseInt(getAttribute(data, 'CroppedAreaImageWidthPixels')), - cropped_height: parseInt(getAttribute(data, 'CroppedAreaImageHeightPixels')), - cropped_x: parseInt(getAttribute(data, 'CroppedAreaLeftPixels')), - cropped_y: parseInt(getAttribute(data, 'CroppedAreaTopPixels')), - }; - - createBuffer(pano_data); + if (xhr.readyState == 4 && xhr.status == 200) { + // Metadata + var binary = xhr.responseText; + var a = binary.indexOf(''); + var data = binary.substring(a, b); + + // No data retrieved + if (a == -1 || b == -1 || data.indexOf('GPano:') == -1) { + createBuffer(false); + return; } - }; + + // Useful values + pano_size = { + full_width: parseInt(getAttribute(data, 'FullPanoWidthPixels')), + full_height: parseInt(getAttribute(data, 'FullPanoHeightPixels')), + cropped_width: parseInt(getAttribute(data, 'CroppedAreaImageWidthPixels')), + cropped_height: parseInt(getAttribute(data, 'CroppedAreaImageHeightPixels')), + cropped_x: parseInt(getAttribute(data, 'CroppedAreaLeftPixels')), + cropped_y: parseInt(getAttribute(data, 'CroppedAreaTopPixels')), + }; + + createBuffer(); + } + }; xhr.open('GET', panorama, true); xhr.send(null); @@ -269,59 +293,68 @@ var PhotoSphereViewer = function(args) { /** * Creates an image in the right dimensions. * @private - * @param {mixed} pano_data - An object containing the panorama XMP data (`false` if there is not) * @return {void} **/ - var createBuffer = function(pano_data) { + var createBuffer = function() { var img = new Image(); img.onload = function() { - // No XMP data? - if (!pano_data) { - pano_data = { - full_width: img.width, - full_height: img.height, - cropped_width: img.width, - cropped_height: img.height, - cropped_x: 0, - cropped_y: 0, - }; - } + // Must the pano size be changed? + var default_pano_size = { + full_width: img.width, + full_height: img.height, + cropped_width: img.width, + cropped_height: img.height, + cropped_x: null, + cropped_y: null, + }; - // Size limit for mobile compatibility - var max_width = 2048; - if (isWebGLSupported()) { - var canvas = document.createElement('canvas'); - var ctx = canvas.getContext('webgl'); - max_width = ctx.getParameter(ctx.MAX_TEXTURE_SIZE); - } + for (attr in pano_size) { + if (pano_size[attr] == null && default_pano_size[attr] !== undefined) + pano_size[attr] = default_pano_size[attr]; + } - // Buffer width (not too big) - var new_width = Math.min(pano_data.full_width, max_width); - var r = new_width / pano_data.full_width; + // Middle if cropped_x/y is null + if (pano_size.cropped_x == null) + pano_size.cropped_x = (pano_size.full_width - pano_size.cropped_width) / 2; - pano_data.full_width = new_width; - pano_data.cropped_width *= r; - pano_data.cropped_x *= r; - img.width = pano_data.cropped_width; + if (pano_size.cropped_y == null) + pano_size.cropped_y = (pano_size.full_height - pano_size.cropped_height) / 2; - // Buffer height (proportional to the width) - pano_data.full_height *= r; - pano_data.cropped_height *= r; - pano_data.cropped_y *= r; - img.height = pano_data.cropped_height; + // Size limit for mobile compatibility + var max_width = 2048; + if (isWebGLSupported()) { + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('webgl'); + max_width = ctx.getParameter(ctx.MAX_TEXTURE_SIZE); + } - // Buffer creation - var buffer = document.createElement('canvas'); - buffer.width = pano_data.full_width; - buffer.height = pano_data.full_height; + // Buffer width (not too big) + var new_width = Math.min(pano_size.full_width, max_width); + var r = new_width / pano_size.full_width; - var ctx = buffer.getContext('2d'); - ctx.drawImage(img, pano_data.cropped_x, pano_data.cropped_y, pano_data.cropped_width, pano_data.cropped_height); + pano_size.full_width = new_width; + pano_size.cropped_width *= r; + pano_size.cropped_x *= r; + img.width = pano_size.cropped_width; - loadTexture(buffer.toDataURL('image/jpeg')); - }; + // Buffer height (proportional to the width) + pano_size.full_height *= r; + pano_size.cropped_height *= r; + pano_size.cropped_y *= r; + img.height = pano_size.cropped_height; + + // Buffer creation + var buffer = document.createElement('canvas'); + buffer.width = pano_size.full_width; + buffer.height = pano_size.full_height; + + var ctx = buffer.getContext('2d'); + ctx.drawImage(img, pano_size.cropped_x, pano_size.cropped_y, pano_size.cropped_width, pano_size.cropped_height); + + loadTexture(buffer.toDataURL('image/jpeg')); + }; // CORS when the panorama is not given as a base64 string if (!panorama.match(/^data:image\/[a-z]+;base64/)) @@ -542,15 +575,33 @@ var PhotoSphereViewer = function(args) { **/ var autorotate = function() { - // Returns to the equator (lat = 0) - lat -= lat / 200; + lat -= (lat - anim_lat_target) * anim_lat_offset; + + long += anim_long_offset; - // Rotates the sphere - long += long_offset; - long -= Math.floor(long / (2.0 * Math.PI)) * 2.0 * Math.PI; + var again = true; + + if (PSV_MIN_LONGITUDE != PSV_MAX_LONGITUDE) { + long = stayBetween(long, PSV_MIN_LONGITUDE, PSV_MAX_LONGITUDE); + + if (long == PSV_MIN_LONGITUDE || long == PSV_MAX_LONGITUDE) { + // Must we reverse the animation or simply stop it? + if (reverse_anim) + anim_long_offset *= -1; + + else { + stopAutorotate(); + again = false; + } + } + } + + long = getAngleMeasure(long); render(); - autorotate_timeout = setTimeout(autorotate, PSV_ANIM_TIMEOUT); + + if (again) + autorotate_timeout = setTimeout(autorotate, PSV_ANIM_TIMEOUT); }; /** @@ -658,6 +709,69 @@ var PhotoSphereViewer = function(args) { } }; + /** + * Returns the current position in radians + * @return {object} A longitude/latitude couple + **/ + + this.getPosition = function() { + return { + longitude: long, + latitude: lat + }; + }; + + /** + * Returns the current position in degrees + * @return {object} A longitude/latitude couple + **/ + + this.getPositionInDegrees = function() { + return { + longitude: long * 180.0 / Math.PI, + latitude: lat * 180.0 / Math.PI + }; + }; + + /** + * Moves to a specific position + * @private + * @param {number|string} longitude - The longitude of the targeted point + * @param {number|string} latitude - The latitude of the targeted point + * @return {void} + **/ + + var moveTo = function(longitude, latitude) { + var long_tmp = parseAngle(longitude); + + if (PSV_MIN_LONGITUDE != PSV_MAX_LONGITUDE) + long_tmp = stayBetween(long_tmp, PSV_MIN_LONGITUDE, PSV_MAX_LONGITUDE); + + var lat_tmp = parseAngle(latitude); + + if (lat_tmp > Math.PI) + lat_tmp -= 2 * Math.PI; + + lat_tmp = stayBetween(lat_tmp, PSV_TILT_DOWN_MAX, PSV_TILT_UP_MAX); + + long = long_tmp; + lat = lat_tmp; + + render(); + }; + + /** + * Moves to a specific position + * @public + * @param {number|string} longitude - The longitude of the targeted point + * @param {number|string} latitude - The latitude of the targeted point + * @return {void} + **/ + + this.moveTo = function(longitude, latitude) { + moveTo(longitude, latitude); + }; + /** * The user wants to move. * @private @@ -796,7 +910,13 @@ var PhotoSphereViewer = function(args) { var move = function(x, y) { if (mousedown) { - long = getAngleMeasure(long + (x - mouse_x) * PSV_LONG_OFFSET); + long += (x - mouse_x) * PSV_LONG_OFFSET; + + if (PSV_MIN_LONGITUDE != PSV_MAX_LONGITUDE) + long = stayBetween(long, PSV_MIN_LONGITUDE, PSV_MAX_LONGITUDE); + + long = getAngleMeasure(long); + lat += (y - mouse_y) * PSV_LAT_OFFSET; lat = stayBetween(lat, PSV_TILT_DOWN_MAX, PSV_TILT_UP_MAX); @@ -861,7 +981,7 @@ var PhotoSphereViewer = function(args) { **/ var onDeviceOrientation = function(coords) { - long = coords.longitude; + long = stayBetween(coords.longitude, PSV_MIN_LONGITUDE, PSV_MAX_LONGITUDE); lat = stayBetween(coords.latitude, PSV_TILT_DOWN_MAX, PSV_TILT_UP_MAX); render(); @@ -959,8 +1079,8 @@ var PhotoSphereViewer = function(args) { **/ var fullscreenToggled = function() { - // Fix the (weird and ugly) Chrome behavior - if (!!document.webkitFullscreenElement) { + // Fix the (weird and ugly) Chrome and IE behaviors + if (!!document.webkitFullscreenElement || !!document.msFullscreenElement) { real_viewer_size.width = container.style.width; real_viewer_size.height = container.style.height; @@ -969,7 +1089,7 @@ var PhotoSphereViewer = function(args) { fitToContainer(); } - else if (!!container.webkitRequestFullscreen) { + else if (!!container.webkitRequestFullscreen || !!container.msRequestFullscreen) { container.style.width = real_viewer_size.width; container.style.height = real_viewer_size.height; fitToContainer(); @@ -1052,13 +1172,13 @@ var PhotoSphereViewer = function(args) { }; /** - * Sets the animation speed. + * Parses an animation speed. * @private * @param {string} speed - The speed, in radians/degrees/revolutions per second/minute - * @return {void} + * @return {number} The speed in radians **/ - var setAnimSpeed = function(speed) { + var parseAnimationSpeed = function(speed) { speed = speed.toString().trim(); // Speed extraction @@ -1109,7 +1229,7 @@ var PhotoSphereViewer = function(args) { } // Longitude offset - long_offset = rad_per_second * PSV_ANIM_TIMEOUT / 1000; + return rad_per_second * PSV_ANIM_TIMEOUT / 1000; }; /** @@ -1212,23 +1332,38 @@ var PhotoSphereViewer = function(args) { var PSV_LONG_OFFSET = (args.long_offset !== undefined) ? parseFloat(args.long_offset) : Math.PI / 360.0; var PSV_LAT_OFFSET = (args.lat_offset !== undefined) ? parseFloat(args.lat_offset) : Math.PI / 180.0; - // Minimal and maximal fields of view in degrees + // Minimum and maximum fields of view in degrees var PSV_FOV_MIN = (args.min_fov !== undefined) ? stayBetween(parseFloat(args.min_fov), 1, 179) : 30; var PSV_FOV_MAX = (args.max_fov !== undefined) ? stayBetween(parseFloat(args.max_fov), 1, 179) : 90; - // Maximal tilt up / down angles + // Minimum tilt up / down angles var PSV_TILT_UP_MAX = (args.tilt_up_max !== undefined) ? stayBetween(parseAngle(args.tilt_up_max), 0, Math.PI / 2.0) : Math.PI / 2.0; var PSV_TILT_DOWN_MAX = (args.tilt_down_max !== undefined) ? -stayBetween(parseAngle(args.tilt_down_max), 0, Math.PI / 2.0) : -Math.PI / 2.0; + // Minimum and maximum visible longitudes ((min = max) => whole circle) + var PSV_MIN_LONGITUDE = (args.min_longitude !== undefined) ? parseAngle(args.min_longitude) : 0; + var PSV_MAX_LONGITUDE = (args.max_longitude !== undefined) ? parseAngle(args.max_longitude) : 0; + + if (PSV_MAX_LONGITUDE < PSV_MIN_LONGITUDE) { + var long_tmp = PSV_MIN_LONGITUDE; + PSV_MIN_LONGITUDE = PSV_MAX_LONGITUDE; + PSV_MAX_LONGITUDE = long_tmp; + } + // Default position - var lat = 0, long = 0; + var lat = 0, long = PSV_MIN_LONGITUDE; if (args.default_position !== undefined) { - if (args.default_position.lat !== undefined) - lat = stayBetween(parseAngle(args.default_position.lat), PSV_TILT_DOWN_MAX, PSV_TILT_UP_MAX); + if (args.default_position.lat !== undefined) { + var lat_angle = parseAngle(args.default_position.lat); + if (lat_angle > Math.PI) + lat_angle -= 2 * Math.PI; + + lat = stayBetween(lat_angle, PSV_TILT_DOWN_MAX, PSV_TILT_UP_MAX); + } if (args.default_position.long !== undefined) - long = parseAngle(args.default_position.long); + long = stayBetween(parseAngle(args.default_position.long), PSV_MIN_LONGITUDE, PSV_MAX_LONGITUDE); } // Default zoom level @@ -1252,14 +1387,28 @@ var PhotoSphereViewer = function(args) { anim_delay = false; } - // Deprecated: horizontal offset for the animation - var long_offset = (args.theta_offset !== undefined) ? Math.PI / parseInt(args.theta_offset) : Math.PI / 1440; - // Horizontal animation speed - if (args.anim_speed !== undefined) - setAnimSpeed(args.anim_speed); - else - setAnimSpeed('2rpm'); + var anim_long_offset = (args.anim_speed !== undefined) ? parseAnimationSpeed(args.anim_speed) : parseAnimationSpeed('2rpm'); + + // Reverse the horizontal animation if autorotate reaches the min/max longitude + var reverse_anim = true; + + if (args.reverse_anim !== undefined) + reverse_anim = !!args.reverse_anim; + + // Vertical animation speed + var anim_lat_offset = (args.vertical_anim_speed !== undefined) ? parseAnimationSpeed(args.vertical_anim_speed) : parseAnimationSpeed('2rpm'); + + // Vertical animation target (default: equator) + var anim_lat_target = 0; + + if (args.vertical_anim_target !== undefined) { + var lat_target_angle = parseAngle(args.vertical_anim_target); + if (lat_target_angle > Math.PI) + lat_target_angle -= 2 * Math.PI; + + anim_lat_target = stayBetween(lat_target_angle, PSV_TILT_DOWN_MAX, PSV_TILT_UP_MAX); + } // Navigation bar var navbar = new PSVNavBar(this); @@ -1299,12 +1448,34 @@ var PhotoSphereViewer = function(args) { // Must we read XMP data? var readxmp = (args.usexmpdata !== undefined) ? !!args.usexmpdata : true; + // Cropped size? + var pano_size = { + full_width: null, + full_height: null, + cropped_width: null, + cropped_height: null, + cropped_x: null, + cropped_y: null + }; + + if (args.pano_size !== undefined) { + for (attr in pano_size) { + if (args.pano_size[attr] !== undefined) + pano_size[attr] = parseInt(args.pano_size[attr]); + } + + readxmp = false; + } + // Loading message var loading_msg = (args.loading_msg !== undefined) ? args.loading_msg.toString() : 'Loading…'; // Loading image var loading_img = (args.loading_img !== undefined) ? args.loading_img.toString() : null; + // Loading HTML + var loading_html = (args.loading_html !== undefined) ? args.loading_html : null; + // Function to call once panorama is ready? if (args.onready !== undefined) this.addAction('ready', args.onready); @@ -1630,6 +1801,7 @@ var PSVNavBarButton = function(psv, type, style) { // Autorotate button button = document.createElement('div'); button.style.cssFloat = 'left'; + button.style.boxSizing = 'inherit'; button.style.padding = '10px'; button.style.width = style.buttonsHeight + 'px'; button.style.height = style.buttonsHeight + 'px'; @@ -1640,6 +1812,7 @@ var PSVNavBarButton = function(psv, type, style) { addEvent(button, 'click', function(){psv.toggleAutorotate();}); var autorotate_sphere = document.createElement('div'); + autorotate_sphere.style.boxSizing = 'inherit'; autorotate_sphere.style.width = autorotate_sphere_width + 'px'; autorotate_sphere.style.height = autorotate_sphere_width + 'px'; autorotate_sphere.style.borderRadius = '50%'; @@ -1647,6 +1820,7 @@ var PSVNavBarButton = function(psv, type, style) { button.appendChild(autorotate_sphere); var autorotate_equator = document.createElement('div'); + autorotate_equator.style.boxSizing = 'inherit'; autorotate_equator.style.width = autorotate_sphere_width + 'px'; autorotate_equator.style.height = autorotate_equator_height + 'px'; autorotate_equator.style.borderRadius = '50%'; @@ -1665,10 +1839,12 @@ var PSVNavBarButton = function(psv, type, style) { // Zoom container button = document.createElement('div'); button.style.cssFloat = 'left'; + button.style.boxSizing = 'inherit'; // Zoom "-" var zoom_minus = document.createElement('div'); zoom_minus.style.cssFloat = 'left'; + zoom_minus.style.boxSizing = 'inherit'; zoom_minus.style.padding = '10px'; zoom_minus.style.height = style.buttonsHeight + 'px'; zoom_minus.style.backgroundColor = style.buttonsBackgroundColor; @@ -1683,12 +1859,14 @@ var PSVNavBarButton = function(psv, type, style) { // Zoom range zoom_range_bg = document.createElement('div'); zoom_range_bg.style.cssFloat = 'left'; + zoom_range_bg.style.boxSizing = 'inherit'; zoom_range_bg.style.padding = (10 + (style.buttonsHeight - style.zoomRangeThickness) / 2) + 'px 5px'; zoom_range_bg.style.backgroundColor = style.buttonsBackgroundColor; zoom_range_bg.style.cursor = 'pointer'; button.appendChild(zoom_range_bg); zoom_range = document.createElement('div'); + zoom_range.style.boxSizing = 'inherit'; zoom_range.style.width = style.zoomRangeWidth + 'px'; zoom_range.style.height = style.zoomRangeThickness + 'px'; zoom_range.style.backgroundColor = style.buttonsColor; @@ -1699,6 +1877,7 @@ var PSVNavBarButton = function(psv, type, style) { zoom_value.style.position = 'absolute'; zoom_value.style.top = ((style.zoomRangeThickness - style.zoomRangeDisk) / 2) + 'px'; zoom_value.style.left = -(style.zoomRangeDisk / 2) + 'px'; + zoom_value.style.boxSizing = 'inherit'; zoom_value.style.width = style.zoomRangeDisk + 'px'; zoom_value.style.height = style.zoomRangeDisk + 'px'; zoom_value.style.borderRadius = '50%'; @@ -1716,6 +1895,7 @@ var PSVNavBarButton = function(psv, type, style) { // Zoom "+" var zoom_plus = document.createElement('div'); zoom_plus.style.cssFloat = 'left'; + zoom_plus.style.boxSizing = 'inherit'; zoom_plus.style.padding = '10px'; zoom_plus.style.height = style.buttonsHeight + 'px'; zoom_plus.style.backgroundColor = style.buttonsBackgroundColor; @@ -1743,6 +1923,7 @@ var PSVNavBarButton = function(psv, type, style) { // Fullscreen button button = document.createElement('div'); button.style.cssFloat = 'right'; + button.style.boxSizing = 'inherit'; button.style.padding = '10px'; button.style.width = fullscreen_width + 'px'; button.style.height = style.buttonsHeight + 'px'; @@ -1754,6 +1935,7 @@ var PSVNavBarButton = function(psv, type, style) { // Fullscreen icon left side var fullscreen_left = document.createElement('div'); fullscreen_left.style.cssFloat = 'left'; + fullscreen_left.style.boxSizing = 'inherit'; fullscreen_left.style.width = style.fullscreenThickness + 'px'; fullscreen_left.style.height = fullscreen_vertical_space + 'px'; fullscreen_left.style.borderStyle = 'solid'; @@ -1764,6 +1946,7 @@ var PSVNavBarButton = function(psv, type, style) { // Fullscreen icon top/bottom sides (first half) var fullscreen_tb_1 = document.createElement('div'); fullscreen_tb_1.style.cssFloat = 'left'; + fullscreen_tb_1.style.boxSizing = 'inherit'; fullscreen_tb_1.style.width = fullscreen_horizontal_border + 'px'; fullscreen_tb_1.style.height = fullscreen_vertical_int + 'px'; fullscreen_tb_1.style.borderStyle = 'solid'; @@ -1774,6 +1957,7 @@ var PSVNavBarButton = function(psv, type, style) { // Fullscreen icon top/bottom sides (second half) var fullscreen_tb_2 = document.createElement('div'); fullscreen_tb_2.style.cssFloat = 'left'; + fullscreen_tb_2.style.boxSizing = 'inherit'; fullscreen_tb_2.style.marginLeft = fullscreen_horizontal_space + 'px'; fullscreen_tb_2.style.width = fullscreen_horizontal_border + 'px'; fullscreen_tb_2.style.height = fullscreen_vertical_int + 'px'; @@ -1785,6 +1969,7 @@ var PSVNavBarButton = function(psv, type, style) { // Fullscreen icon right side var fullscreen_right = document.createElement('div'); fullscreen_right.style.cssFloat = 'left'; + fullscreen_right.style.boxSizing = 'inherit'; fullscreen_right.style.width = style.fullscreenThickness + 'px'; fullscreen_right.style.height = fullscreen_vertical_space + 'px'; fullscreen_right.style.borderStyle = 'solid'; @@ -1810,6 +1995,7 @@ var PSVNavBarButton = function(psv, type, style) { // Gyroscope button button = document.createElement('div'); button.style.cssFloat = 'right'; + button.style.boxSizing = 'inherit'; button.style.padding = '10px'; button.style.width = style.buttonsHeight + 'px'; button.style.height = style.buttonsHeight + 'px'; @@ -1820,6 +2006,7 @@ var PSVNavBarButton = function(psv, type, style) { addEvent(button, 'click', function(){psv.toggleDeviceOrientation();}); var gyroscope_sphere = document.createElement('div'); + gyroscope_sphere.style.boxSizing = 'inherit'; gyroscope_sphere.style.width = gyroscope_sphere_width + 'px'; gyroscope_sphere.style.height = gyroscope_sphere_width + 'px'; gyroscope_sphere.style.borderRadius = '50%'; @@ -1827,6 +2014,7 @@ var PSVNavBarButton = function(psv, type, style) { button.appendChild(gyroscope_sphere); var gyroscope_hor_ellipsis = document.createElement('div'); + gyroscope_hor_ellipsis.style.boxSizing = 'inherit'; gyroscope_hor_ellipsis.style.width = gyroscope_ellipses_big_axis + 'px'; gyroscope_hor_ellipsis.style.height = gyroscope_ellipses_little_axis + 'px'; gyroscope_hor_ellipsis.style.borderRadius = '50%'; @@ -1839,6 +2027,7 @@ var PSVNavBarButton = function(psv, type, style) { button.appendChild(gyroscope_hor_ellipsis); var gyroscope_ver_ellipsis = document.createElement('div'); + gyroscope_ver_ellipsis.style.boxSizing = 'inherit'; gyroscope_ver_ellipsis.style.width = gyroscope_ellipses_little_axis + 'px'; gyroscope_ver_ellipsis.style.height = gyroscope_ellipses_big_axis + 'px'; gyroscope_ver_ellipsis.style.borderRadius = '50%'; @@ -1866,6 +2055,7 @@ var PSVNavBarButton = function(psv, type, style) { button = document.createElement('div'); button.style.cssFloat = 'right'; button.style.position = 'relative'; + button.style.boxSizing = 'inherit'; button.style.padding = '10px'; button.style.width = vr_width + 'px'; button.style.height = style.buttonsHeight + 'px'; @@ -1876,6 +2066,7 @@ var PSVNavBarButton = function(psv, type, style) { // Icon var vr_rect = document.createElement('div'); + vr_rect.style.boxSizing = 'inherit'; vr_rect.style.width = vr_width + 'px'; vr_rect.style.height = style.buttonsHeight + 'px'; vr_rect.style.borderRadius = style.virtualRealityBorderRadius + 'px'; @@ -1883,6 +2074,7 @@ var PSVNavBarButton = function(psv, type, style) { button.appendChild(vr_rect); var left_eye = document.createElement('div'); + left_eye.style.boxSizing = 'inherit'; left_eye.style.width = vr_eye_diameter + 'px'; left_eye.style.height = vr_eye_diameter + 'px'; left_eye.style.position = 'absolute'; @@ -1893,6 +2085,7 @@ var PSVNavBarButton = function(psv, type, style) { button.appendChild(left_eye); var right_eye = document.createElement('div'); + right_eye.style.boxSizing = 'inherit'; right_eye.style.width = vr_eye_diameter + 'px'; right_eye.style.height = vr_eye_diameter + 'px'; right_eye.style.position = 'absolute'; @@ -1903,6 +2096,7 @@ var PSVNavBarButton = function(psv, type, style) { button.appendChild(right_eye); var nose = document.createElement('div'); + nose.style.boxSizing = 'inherit'; nose.style.width = vr_eye_diameter + 'px'; nose.style.height = (style.buttonsHeight / 2) + 'px'; nose.style.position = 'absolute'; diff --git a/photo-sphere-viewer.min.js b/photo-sphere-viewer.min.js index 4cd19fd26..d5a51f27e 100644 --- a/photo-sphere-viewer.min.js +++ b/photo-sphere-viewer.min.js @@ -1 +1 @@ -var PhotoSphereViewer=function(e){var t=function(){var e=document.createElement("canvas");return!(!e.getContext||!e.getContext("2d"))},o=function(){var e=document.createElement("canvas");return!(!window.WebGLRenderingContext||!e.getContext("webgl"))},n=function(e,t,o){e.addEventListener?e.addEventListener(t,o,!1):e.attachEvent("on"+t,o)},i=function(e,t,o){return Math.max(t,Math.min(o,e))},r=function(e,t,o,n){var i=o-e,r=n-t;return i*i+r*r},a=function(e){return e-2*Math.floor(e/(2*Math.PI))*Math.PI};this.load=function(){if(Se){var e=document.createElement("img");e.setAttribute("src",Se),e.setAttribute("alt",Re),se.appendChild(e)}else se.textContent=Re;return ce=document.createElement("div"),ce.style.width="100%",ce.style.height="100%",ce.style.position="relative",ce.style.overflow="hidden",t()?void 0===window.THREE?void console.log("PhotoSphereViewer: Three.js is not loaded."):(ae={width:0,height:0,ratio:0},void(Te?l():d(!1))):void(se.textContent="Canvas is not supported, update your browser!")};var s=function(e,t){var o=e.indexOf("GPano:"+t)+t.length+8,n=e.indexOf('"',o);return e.substring(o,n)},l=function(){var e=null;if(window.XMLHttpRequest)e=new XMLHttpRequest;else{if(!window.ActiveXObject)return void(se.textContent="XHR is not supported, update your browser!");try{e=new ActiveXObject("Msxml2.XMLHTTP")}catch(t){e=new ActiveXObject("Microsoft.XMLHTTP")}}e.onreadystatechange=function(){if(4==e.readyState&&200==e.status){var t=e.responseText,o=t.indexOf(""),i=t.substring(o,n);if(-1==o||-1==n||-1==i.indexOf("GPano:"))return void d(!1);var r={full_width:parseInt(s(i,"FullPanoWidthPixels")),full_height:parseInt(s(i,"FullPanoHeightPixels")),cropped_width:parseInt(s(i,"CroppedAreaImageWidthPixels")),cropped_height:parseInt(s(i,"CroppedAreaImageHeightPixels")),cropped_x:parseInt(s(i,"CroppedAreaLeftPixels")),cropped_y:parseInt(s(i,"CroppedAreaTopPixels"))};d(r)}},e.open("GET",he,!0),e.send(null)},d=function(e){var t=new Image;t.onload=function(){e||(e={full_width:t.width,full_height:t.height,cropped_width:t.width,cropped_height:t.height,cropped_x:0,cropped_y:0});var n=2048;if(o()){var i=document.createElement("canvas"),r=i.getContext("webgl");n=r.getParameter(r.MAX_TEXTURE_SIZE)}var a=Math.min(e.full_width,n),s=a/e.full_width;e.full_width=a,e.cropped_width*=s,e.cropped_x*=s,t.width=e.cropped_width,e.full_height*=s,e.cropped_height*=s,e.cropped_y*=s,t.height=e.cropped_height;var l=document.createElement("canvas");l.width=e.full_width,l.height=e.full_height;var r=l.getContext("2d");r.drawImage(t,e.cropped_x,e.cropped_y,e.cropped_width,e.cropped_height),c(l.toDataURL("image/jpeg"))},he.match(/^data:image\/[a-z]+;base64/)||t.setAttribute("crossOrigin","anonymous"),t.src=he},c=function(e){var t=new THREE.Texture,o=new THREE.ImageLoader,n=function(e){t.needsUpdate=!0,t.image=e,u(t)};o.load(e,n)},u=function(e){void 0!==le.width&&(se.style.width=le.width.css),void 0!==le.height&&(se.style.height=le.height.css),b(),pe=o()?new THREE.WebGLRenderer:new THREE.CanvasRenderer,pe.setSize(ae.width,ae.height),ge=new THREE.Scene,me=new THREE.PerspectiveCamera(G,ae.ratio,1,300),me.position.set(0,0,0),ge.add(me);var t=new THREE.SphereGeometry(200,32,32),i=new THREE.MeshBasicMaterial({map:e,overdraw:!0}),r=new THREE.Mesh(t,i);r.scale.x=-1,ge.add(r),ue=document.createElement("div"),ue.style.position="absolute",ue.style.zIndex=0,ce.appendChild(ue),ne&&(oe.setStyle(ie),oe.create(),ce.appendChild(oe.getBar())),n(window,"resize",b),re&&(n(ue,"mousedown",x),n(document,"mousemove",T),n(ue,"mousemove",L),n(document,"mouseup",_),n(ue,"touchstart",C),n(document,"touchend",_),n(document,"touchmove",R),n(ue,"mousewheel",z),n(ue,"DOMMouseScroll",z)),n(document,"fullscreenchange",P),n(document,"mozfullscreenchange",P),n(document,"webkitfullscreenchange",P),n(document,"MSFullscreenChange",P),Ee.addListener(M),se.innerHTML="",se.appendChild(ce);var a=pe.domElement;a.style.display="block",ue.appendChild(a),h(),Z>0&&F(Z),m(),N("ready")},h=function(){var e=new THREE.Vector3;e.setX(Math.cos(U)*Math.sin(K)),e.setY(Math.sin(U)),e.setZ(Math.cos(U)*Math.cos(K)),me.lookAt(e),null!==ve?ve.render(ge,me):pe.render(ge,me)},p=function(){ve=new THREE.StereoEffect(pe),ve.eyeSeparation=5,ve.setSize(ae.width,ae.height),I(),O(),oe.mustBeHidden(),h(),N("stereo-effect",!0)},g=function(){ve=null,pe.setSize(ae.width,ae.height),oe.mustBeHidden(!1),h(),N("stereo-effect",!1)};this.toggleStereo=function(){null!==ve?g():p()};var m=function(){ee!==!1&&(ke=setTimeout(f,ee))},v=function(){U-=U/200,K+=te,K-=2*Math.floor(K/(2*Math.PI))*Math.PI,h(),Ce=setTimeout(v,Q)},f=function(){v(),N("autorotate",!0)},y=function(){clearTimeout(ke),ke=null,clearTimeout(Ce),Ce=null,N("autorotate",!1)};this.toggleAutorotate=function(){clearTimeout(ke),Ce?y():f()};var b=function(){(se.clientWidth!=ae.width||se.clientHeight!=ae.height)&&w({width:se.clientWidth,height:se.clientHeight})};this.fitToContainer=function(){b()};var w=function(e){ae.width=void 0!==e.width?parseInt(e.width):ae.width,ae.height=void 0!==e.height?parseInt(e.height):ae.height,ae.ratio=ae.width/ae.height,me&&(me.aspect=ae.ratio,me.updateProjectionMatrix()),pe&&(pe.setSize(ae.width,ae.height),h()),ve&&(ve.setSize(ae.width,ae.height),h())},x=function(e){k(parseInt(e.clientX),parseInt(e.clientY))},C=function(e){if(1==e.touches.length){var t=e.touches[0];t.target.parentNode==ue&&k(parseInt(t.clientX),parseInt(t.clientY))}else 2==e.touches.length&&(_(),e.touches[0].target.parentNode==ue&&e.touches[1].target.parentNode==ue&&E(r(e.touches[0].clientX,e.touches[0].clientY,e.touches[1].clientX,e.touches[1].clientY)));L()},k=function(e,t){ye=e,be=t,y(),fe=!0},E=function(e){xe=e,we=!0},_=function(e){fe=!1,we=!1},T=function(e){e.preventDefault(),S(parseInt(e.clientX),parseInt(e.clientY))},R=function(e){if(1==e.touches.length&&fe){var t=e.touches[0];t.target.parentNode==ue&&(e.preventDefault(),S(parseInt(t.clientX),parseInt(t.clientY)))}else if(2==e.touches.length&&e.touches[0].target.parentNode==ue&&e.touches[1].target.parentNode==ue&&we){e.preventDefault();var o=r(e.touches[0].clientX,e.touches[0].clientY,e.touches[1].clientX,e.touches[1].clientY),n=o-xe;if(0!=n){var i=n/Math.abs(n);F(Z+i),xe=o}}},S=function(e,t){fe&&(K=a(K+(e-ye)*$),U+=(t-be)*V,U=i(U,j,Y),ye=e,be=t,h())},I=function(){Ee.start(),y(),N("device-orientation",!0)},H=function(){Ee.stop(),N("device-orientation",!1)};this.toggleDeviceOrientation=function(){Ee.isEventAttached()?H():I()};var M=function(e){K=e.longitude,U=i(e.latitude,j,Y),h()},z=function(e){e.preventDefault(),e.stopPropagation();var t=e.detail?-e.detail:e.wheelDelta;if(0!=t){var o=parseInt(t/Math.abs(t));F(Z+o)}},F=function(e){Z=i(parseInt(Math.round(e)),0,100),me.fov=G+Z/100*(q-G),me.updateProjectionMatrix(),h(),N("zoom-updated",Z)};this.zoom=function(e){F(e)},this.zoomIn=function(){100>Z&&F(Z+1)},this.zoomOut=function(){Z>0&&F(Z-1)};var B=function(){return!!(document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement)},P=function(){document.webkitFullscreenElement?(de.width=se.style.width,de.height=se.style.height,se.style.width="100%",se.style.height="100%",b()):se.webkitRequestFullscreen&&(se.style.width=de.width,se.style.height=de.height,b()),N("fullscreen-mode",B())},O=function(){se.requestFullscreen?se.requestFullscreen():se.mozRequestFullScreen?se.mozRequestFullScreen():se.webkitRequestFullscreen?se.webkitRequestFullscreen():se.msRequestFullscreen&&se.msRequestFullscreen()},A=function(){document.exitFullscreen?document.exitFullscreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen?document.webkitExitFullscreen():document.msExitFullscreen&&document.msExitFullscreen()};this.toggleFullscreen=function(){B()?A():O()};var L=function(){ne&&oe.show()},D=function(e){e=e.toString().trim();var t=parseFloat(e.replace(/^(-?[0-9]+(?:\.[0-9]*)?).*$/,"$1")),o=e.replace(/^-?[0-9]+(?:\.[0-9]*)?(.*)$/,"$1").trim();o.match(/(pm|per minute)$/)&&(t/=60);var n=0;switch(o){case"rpm":case"rev per minute":case"revolutions per minute":case"rps":case"rev per second":case"revolutions per second":n=2*t*Math.PI;break;case"dpm":case"deg per minute":case"degrees per minute":case"dps":case"deg per second":case"degrees per second":n=t*Math.PI/180;break;case"rad per minute":case"radians per minute":case"rad per second":case"radians per second":n=t;break;default:m_anim=!1}te=n*Q/1e3},X=function(e){e=e.toString().trim();var t=parseFloat(e.replace(/^(-?[0-9]+(?:\.[0-9]*)?).*$/,"$1")),o=e.replace(/^-?[0-9]+(?:\.[0-9]*)?(.*)$/,"$1").trim();return"deg"==o&&(t*=Math.PI/180),a(t)},W=function(e){for(dim in e)if("width"==dim||"height"==dim){var t=e[dim].toString().trim(),o=parseFloat(t.replace(/^([0-9]+(?:\.[0-9]*)?).*$/,"$1")),n=t.replace(/^[0-9]+(?:\.[0-9]*)?(.*)$/,"$1").trim();"%"!=n&&(n="px"),le[dim]={css:o+n,unit:n}}};this.addAction=function(e,t){e in _e||(_e[e]=[]),_e[e].push(t)};var N=function(e,t){if(e in _e&&_e[e].length)for(var o=0,n=_e[e].length;n>o;++o)void 0!==t?_e[e][o](t):_e[e][o]()};if(void 0===e||void 0===e.panorama||void 0===e.container)return void console.log("PhotoSphereViewer: no value given for panorama or container");var $=void 0!==e.long_offset?parseFloat(e.long_offset):Math.PI/360,V=void 0!==e.lat_offset?parseFloat(e.lat_offset):Math.PI/180,q=void 0!==e.min_fov?i(parseFloat(e.min_fov),1,179):30,G=void 0!==e.max_fov?i(parseFloat(e.max_fov),1,179):90,Y=void 0!==e.tilt_up_max?i(X(e.tilt_up_max),0,Math.PI/2):Math.PI/2,j=void 0!==e.tilt_down_max?-i(X(e.tilt_down_max),0,Math.PI/2):-Math.PI/2,U=0,K=0;void 0!==e.default_position&&(void 0!==e.default_position.lat&&(U=i(X(e.default_position.lat),j,Y)),void 0!==e.default_position["long"]&&(K=X(e.default_position["long"])));var Z=0;void 0!==e.zoom_level&&(Z=i(parseInt(Math.round(e.zoom_level)),0,100));var J=60,Q=1e3/J,ee=2e3;void 0!==e.time_anim&&(ee="number"==typeof e.time_anim&&e.time_anim>=0?e.time_anim:!1);var te=void 0!==e.theta_offset?Math.PI/parseInt(e.theta_offset):Math.PI/1440;D(void 0!==e.anim_speed?e.anim_speed:"2rpm");var oe=new PSVNavBar(this),ne=void 0!==e.navbar?!!e.navbar:!1,ie=void 0!==e.navbar_style?e.navbar_style:{},re=void 0!==e.allow_user_interactions?!!e.allow_user_interactions:!0;re||(ne=!1);var ae,se=e.container,le={},de={};void 0!==e.size&&W(e.size);var ce,ue,he=e.panorama,pe=null,ge=null,me=null,ve=null,fe=!1,ye=0,be=0,we=!1,xe=0,Ce=null,ke=null,Ee=new Sphoords,_e={},Te=void 0!==e.usexmpdata?!!e.usexmpdata:!0,Re=void 0!==e.loading_msg?e.loading_msg.toString():"Loading…",Se=void 0!==e.loading_img?e.loading_img.toString():null;void 0!==e.onready&&this.addAction("ready",e.onready);var Ie=void 0!==e.autoload?!!e.autoload:!0;Ie&&this.load()},PSVNavBar=function(e){var t=function(e,t){for(var o=0,n=t.length;n>o;++o)if(t[o]==e)return!0;return!1},o=function(e,o){return t(e,h)&&"string"==typeof o&&("transparent"==o||!!o.match(/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/)||!!o.match(/^rgb\((1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])(,\s*(1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])){2}\)$/)||!!o.match(/^rgba\(((1?[0-9]{1,2}|2[0-4][0-9]|25[0-5]),\s*){3}(0(\.[0-9]*)?|1)\)$/))||t(e,p)&&!isNaN(parseFloat(o))&&isFinite(o)&&o>=0};this.setStyle=function(e){for(property in e)property in u&&o(property,e[property])&&(u[property]=e[property])},this.create=function(){r=document.createElement("div"),r.style.backgroundColor=u.backgroundColor,r.style.position="absolute",r.style.zIndex=10,r.style.bottom=0,r.style.width="100%",r.style.boxSizing="content-box",r.style.transition="bottom 0.4s ease-out",a=new PSVNavBarButton(e,"autorotate",u),r.appendChild(a.getButton()),s=new PSVNavBarButton(e,"zoom",u),r.appendChild(s.getButton()),l=new PSVNavBarButton(e,"fullscreen",u),r.appendChild(l.getButton()),Sphoords.isDeviceOrientationSupported&&(d=new PSVNavBarButton(e,"orientation",u),r.appendChild(d.getButton()),c=new PSVNavBarButton(e,"virtual-reality",u),r.appendChild(c.getButton()))},this.getBar=function(){return r};var n=function(){g&&(clearTimeout(g),!m&&v&&(g=setTimeout(i,5e3))),m&&(r.style.bottom=0,m=!1,v&&(g=setTimeout(i,5e3)))};this.show=function(){n()};var i=function(){m||(r.style.bottom=-r.offsetHeight+1+"px",m=!0)};this.hide=function(){i()},this.isHidden=function(){return m},this.mustBeHidden=function(e){v=void 0!==e?!!e:!0,v?i():n()};var r,a,s,l,d,c,u={backgroundColor:"rgba(61, 61, 61, 0.5)",buttonsColor:"rgba(255, 255, 255, 0.7)",buttonsBackgroundColor:"transparent",activeButtonsBackgroundColor:"rgba(255, 255, 255, 0.1)",buttonsHeight:20,autorotateThickness:1,zoomRangeWidth:50,zoomRangeThickness:1,zoomRangeDisk:7,fullscreenRatio:4/3,fullscreenThickness:2,gyroscopeThickness:1,virtualRealityRatio:4/3,virtualRealityBorderRadius:2},h=["backgroundColor","buttonsColor","buttonsBackgroundColor","activeButtonsBackgroundColor"],p=["buttonsHeight","autorotateThickness","zoomRangeWidth","zoomRangeThickness","zoomRangeDisk","fullscreenRatio","fullscreenThickness"],g=null,m=!1,v=!1},PSVNavBarButton=function(e,t,o){var n=function(e,t,o){e.addEventListener?e.addEventListener(t,o,!1):e.attachEvent("on"+t,o)},i=function(){switch(t){case"autorotate":var i=o.buttonsHeight-2*o.autorotateThickness,p=i/10;l=document.createElement("div"),l.style.cssFloat="left",l.style.padding="10px",l.style.width=o.buttonsHeight+"px",l.style.height=o.buttonsHeight+"px",l.style.backgroundColor=o.buttonsBackgroundColor,l.style.position="relative",l.style.cursor="pointer",n(l,"click",function(){e.toggleAutorotate()});var f=document.createElement("div");f.style.width=i+"px",f.style.height=i+"px",f.style.borderRadius="50%",f.style.border=o.autorotateThickness+"px solid "+o.buttonsColor,l.appendChild(f);var y=document.createElement("div");y.style.width=i+"px",y.style.height=p+"px",y.style.borderRadius="50%",y.style.border=o.autorotateThickness+"px solid "+o.buttonsColor,y.style.position="absolute",y.style.top="50%",y.style.marginTop=-(p/2+o.autorotateThickness)+"px",l.appendChild(y),e.addAction("autorotate",d);break;case"zoom":l=document.createElement("div"),l.style.cssFloat="left";var b=document.createElement("div");b.style.cssFloat="left",b.style.padding="10px",b.style.height=o.buttonsHeight+"px",b.style.backgroundColor=o.buttonsBackgroundColor,b.style.lineHeight=o.buttonsHeight+"px",b.style.color=o.buttonsColor,b.style.cursor="pointer",b.textContent="-",n(b,"click",function(){e.zoomOut()}),l.appendChild(b),r=document.createElement("div"),r.style.cssFloat="left",r.style.padding=10+(o.buttonsHeight-o.zoomRangeThickness)/2+"px 5px",r.style.backgroundColor=o.buttonsBackgroundColor,r.style.cursor="pointer",l.appendChild(r),a=document.createElement("div"),a.style.width=o.zoomRangeWidth+"px",a.style.height=o.zoomRangeThickness+"px",a.style.backgroundColor=o.buttonsColor,a.style.position="relative",r.appendChild(a),s=document.createElement("div"),s.style.position="absolute",s.style.top=(o.zoomRangeThickness-o.zoomRangeDisk)/2+"px",s.style.left=-(o.zoomRangeDisk/2)+"px",s.style.width=o.zoomRangeDisk+"px",s.style.height=o.zoomRangeDisk+"px",s.style.borderRadius="50%",s.style.backgroundColor=o.buttonsColor,e.addAction("zoom-updated",c),n(r,"mousedown",u),n(r,"touchstart",h),n(document,"mousemove",m),n(document,"touchmove",v),n(document,"mouseup",g),n(document,"touchend",g),a.appendChild(s);var w=document.createElement("div");w.style.cssFloat="left",w.style.padding="10px",w.style.height=o.buttonsHeight+"px",w.style.backgroundColor=o.buttonsBackgroundColor,w.style.lineHeight=o.buttonsHeight+"px",w.style.color=o.buttonsColor,w.style.cursor="pointer",w.textContent="+",n(w,"click",function(){e.zoomIn()}),l.appendChild(w);break;case"fullscreen":var x=o.buttonsHeight*o.fullscreenRatio,C=.3*o.buttonsHeight,k=(o.buttonsHeight-C)/2,E=.3*x,_=(x-E)/2-o.fullscreenThickness,T=o.buttonsHeight-2*o.fullscreenThickness;l=document.createElement("div"),l.style.cssFloat="right",l.style.padding="10px",l.style.width=x+"px",l.style.height=o.buttonsHeight+"px",l.style.backgroundColor=o.buttonsBackgroundColor,l.style.cursor="pointer",n(l,"click",function(){e.toggleFullscreen()});var R=document.createElement("div");R.style.cssFloat="left",R.style.width=o.fullscreenThickness+"px",R.style.height=C+"px",R.style.borderStyle="solid",R.style.borderColor=o.buttonsColor+" transparent",R.style.borderWidth=k+"px 0",l.appendChild(R);var S=document.createElement("div");S.style.cssFloat="left",S.style.width=_+"px",S.style.height=T+"px",S.style.borderStyle="solid",S.style.borderColor=o.buttonsColor+" transparent",S.style.borderWidth=o.fullscreenThickness+"px 0",l.appendChild(S);var I=document.createElement("div");I.style.cssFloat="left",I.style.marginLeft=E+"px",I.style.width=_+"px",I.style.height=T+"px",I.style.borderStyle="solid",I.style.borderColor=o.buttonsColor+" transparent",I.style.borderWidth=o.fullscreenThickness+"px 0",l.appendChild(I);var H=document.createElement("div");H.style.cssFloat="left",H.style.width=o.fullscreenThickness+"px",H.style.height=C+"px",H.style.borderStyle="solid",H.style.borderColor=o.buttonsColor+" transparent",H.style.borderWidth=k+"px 0",l.appendChild(H);var M=document.createElement("div");M.style.clear="left",l.appendChild(M),e.addAction("fullscreen-mode",d);break;case"orientation":var z=o.buttonsHeight-2*o.gyroscopeThickness,F=z-4*o.gyroscopeThickness,B=z/10;l=document.createElement("div"),l.style.cssFloat="right",l.style.padding="10px",l.style.width=o.buttonsHeight+"px",l.style.height=o.buttonsHeight+"px",l.style.backgroundColor=o.buttonsBackgroundColor,l.style.position="relative",l.style.cursor="pointer",n(l,"click",function(){e.toggleDeviceOrientation()});var P=document.createElement("div");P.style.width=z+"px",P.style.height=z+"px",P.style.borderRadius="50%",P.style.border=o.gyroscopeThickness+"px solid "+o.buttonsColor,l.appendChild(P);var O=document.createElement("div");O.style.width=F+"px",O.style.height=B+"px",O.style.borderRadius="50%",O.style.border=o.gyroscopeThickness+"px solid "+o.buttonsColor,O.style.position="absolute",O.style.top="50%",O.style.left="50%",O.style.marginTop=-(B/2+o.gyroscopeThickness)+"px",O.style.marginLeft=-(F/2+o.gyroscopeThickness)+"px",l.appendChild(O);var A=document.createElement("div");A.style.width=B+"px",A.style.height=F+"px",A.style.borderRadius="50%",A.style.border=o.gyroscopeThickness+"px solid "+o.buttonsColor,A.style.position="absolute",A.style.top="50%",A.style.left="50%",A.style.marginTop=-(F/2+o.gyroscopeThickness)+"px",A.style.marginLeft=-(B/2+o.gyroscopeThickness)+"px",l.appendChild(A),e.addAction("device-orientation",d);break;case"virtual-reality":var L=o.buttonsHeight*o.virtualRealityRatio,D=L/4,X=D/2;l=document.createElement("div"),l.style.cssFloat="right",l.style.position="relative",l.style.padding="10px",l.style.width=L+"px",l.style.height=o.buttonsHeight+"px",l.style.backgroundColor=o.buttonsBackgroundColor,l.style.cursor="pointer",n(l,"click",function(){e.toggleStereo()});var W=document.createElement("div");W.style.width=L+"px",W.style.height=o.buttonsHeight+"px",W.style.borderRadius=o.virtualRealityBorderRadius+"px",W.style.backgroundColor=o.buttonsColor,l.appendChild(W);var N=document.createElement("div");N.style.width=D+"px",N.style.height=D+"px",N.style.position="absolute",N.style.top=X+10+"px",N.style.left=X+10+"px",N.style.borderRadius="50%",N.style.backgroundColor=o.backgroundColor,l.appendChild(N);var $=document.createElement("div");$.style.width=D+"px",$.style.height=D+"px",$.style.position="absolute",$.style.top=X+10+"px",$.style.right=X+10+"px",$.style.borderRadius="50%",$.style.backgroundColor=o.backgroundColor,l.appendChild($);var V=document.createElement("div");V.style.width=D+"px",V.style.height=o.buttonsHeight/2+"px",V.style.position="absolute",V.style.left="50%",V.style.bottom="10px",V.style.marginLeft=-(D/2)+"px",V.style.borderTopLeftRadius="50% 60%",V.style.borderTopRightRadius="50% 60%",V.style.backgroundColor=o.backgroundColor,l.appendChild(V),e.addAction("stereo-effect",d)}};this.getButton=function(){return l};var r,a,s,l,d=function(e){e?l.style.backgroundColor=o.activeButtonsBackgroundColor:l.style.backgroundColor=o.buttonsBackgroundColor},c=function(e){s.style.left=e/100*o.zoomRangeWidth-o.zoomRangeDisk/2+"px"},u=function(e){p(parseInt(e.clientX))},h=function(e){var t=e.touches[0];(t.target==r||t.target==a||t.target==s)&&p(parseInt(t.clientX))},p=function(e){y=!0,f(e)},g=function(e){y=!1},m=function(e){e.preventDefault(),f(parseInt(e.clientX))},v=function(e){var t=e.touches[0];(t.target==r||t.target==a||t.target==s)&&(e.preventDefault(),f(parseInt(t.clientX)))},f=function(t){if(y){var n=t-a.getBoundingClientRect().left,i=n/o.zoomRangeWidth*100;e.zoom(i)}},y=!1;i()},Sphoords=function(){var e=function(){var e=navigator.userAgent;return/Gecko\/[0-9.]+/.test(e)?"Gecko":/Chrome\/[0-9.]+/.test(e)?"Blink":/AppleWebKit\/[0-9.]+/.test(e)?"WebKit":/Trident\/[0-9.]+/.test(e)?"Trident":/Opera\/[0-9.]+/.test(e)?"Presto":"Gecko"},t=function(e){return e-360*Math.floor(e/360)};this.start=function(){return Sphoords.isDeviceOrientationSupported?(window.addEventListener("deviceorientation",o,!1),i=!0,!0):(console.log("Device Orientation API not supported"),!1)},this.stop=function(){i&&(window.removeEventListener("deviceorientation",o,!1),i=!1)},this.toggle=function(){i?this.stop():this.start()},this.isEventAttached=function(){return i};var o=function(e){c=Sphoords.getScreenOrientation();var o=0,i=0;switch(c){case"portrait-primary":o=e.alpha+e.gamma,i=e.beta-90;break;case"landscape-primary":if(o=e.alpha+e.beta-90,i=-e.gamma-90,Math.abs(e.beta)>90)switch(u){case"Blink":i+=180;break;case"Gecko":default:i=-i}break;case"landscape-secondary":if(o=e.alpha-e.beta+90,i=e.gamma-90,Math.abs(e.beta)>90)switch(u){case"Blink":i+=180;break;case"Gecko":default:i=-i}break;case"portrait-secondary":o=e.alpha-e.gamma,i=180-(e.beta-90),i=270-e.beta}i=t(i),i>=180&&(i-=360),r=t(o),a=Math.max(-90,Math.min(90,i)),s=r*d,l=a*d,n()};this.getCoordinates=function(){return{longitude:s,latitude:l}},this.getCoordinatesInDegrees=function(){return{longitude:r,latitude:a}},this.getScreenOrientation=function(){return c},this.addListener=function(e){h.push(e)};var n=function(){if(h.length)for(var e=0,t=h.length;t>e;++e)h[e]({longitude:s,latitude:l})},i=!1,r=0,a=0,s=0,l=0,d=Math.PI/180,c=Sphoords.getScreenOrientation(),u=e(),h=[]};Sphoords.getScreenOrientation=function(){var e=null;return screen.orientation?e=screen.orientation:screen.mozOrientation?e=screen.mozOrientation:screen.msOrientation&&(e=screen.msOrientation),null!==e&&"object"==typeof e?e.type:e},Sphoords.isDeviceOrientationSupported=!1,function(){function e(t){null!==t&&null!==t.alpha&&(Sphoords.isDeviceOrientationSupported=!0,window.removeEventListener("deviceorientation",e))}window.DeviceOrientationEvent&&null!==Sphoords.getScreenOrientation()&&window.addEventListener("deviceorientation",e)}(); \ No newline at end of file +var PhotoSphereViewer=function(e){var t=function(){var e=document.createElement("canvas");return!(!e.getContext||!e.getContext("2d"))},n=function(){var e=document.createElement("canvas");return!(!window.WebGLRenderingContext||!e.getContext("webgl"))},o=function(e,t,n){e.addEventListener?e.addEventListener(t,n,!1):e.attachEvent("on"+t,n)},i=function(e,t,n){return Math.max(t,Math.min(n,e))},r=function(e,t,n,o){var i=n-e,r=o-t;return i*i+r*r},a=function(e){return e-2*Math.floor(e/(2*Math.PI))*Math.PI};this.load=function(){if(ve.innerHTML="",De&&1===De.nodeType)ve.appendChild(De);else if(De&&"string"==typeof De)ve.innerHTML=De;else if(Le){var e=document.createElement("img");e.setAttribute("src",Le),e.setAttribute("alt",Ae),ve.appendChild(e)}else ve.textContent=Ae;return be=document.createElement("div"),be.style.width="100%",be.style.height="100%",be.style.position="relative",be.style.overflow="hidden",t()?void 0===window.THREE?void console.log("PhotoSphereViewer: Three.js is not loaded."):(me={width:0,height:0,ratio:0},void(Be&&!we.match(/^data:image\/[a-z]+;base64/)?l():d())):void(ve.textContent="Canvas is not supported, update your browser!")};var s=function(e,t){var n=e.indexOf("GPano:"+t)+t.length+8,o=e.indexOf('"',n);return e.substring(n,o)},l=function(){var e=null;if(window.XMLHttpRequest)e=new XMLHttpRequest;else{if(!window.ActiveXObject)return void(ve.textContent="XHR is not supported, update your browser!");try{e=new ActiveXObject("Msxml2.XMLHTTP")}catch(t){e=new ActiveXObject("Microsoft.XMLHTTP")}}e.onreadystatechange=function(){if(4==e.readyState&&200==e.status){var t=e.responseText,n=t.indexOf(""),i=t.substring(n,o);if(-1==n||-1==o||-1==i.indexOf("GPano:"))return void d(!1);Oe={full_width:parseInt(s(i,"FullPanoWidthPixels")),full_height:parseInt(s(i,"FullPanoHeightPixels")),cropped_width:parseInt(s(i,"CroppedAreaImageWidthPixels")),cropped_height:parseInt(s(i,"CroppedAreaImageHeightPixels")),cropped_x:parseInt(s(i,"CroppedAreaLeftPixels")),cropped_y:parseInt(s(i,"CroppedAreaTopPixels"))},d()}},e.open("GET",we,!0),e.send(null)},d=function(){var e=new Image;e.onload=function(){var t={full_width:e.width,full_height:e.height,cropped_width:e.width,cropped_height:e.height,cropped_x:null,cropped_y:null};for(attr in Oe)null==Oe[attr]&&void 0!==t[attr]&&(Oe[attr]=t[attr]);null==Oe.cropped_x&&(Oe.cropped_x=(Oe.full_width-Oe.cropped_width)/2),null==Oe.cropped_y&&(Oe.cropped_y=(Oe.full_height-Oe.cropped_height)/2);var o=2048;if(n()){var i=document.createElement("canvas"),r=i.getContext("webgl");o=r.getParameter(r.MAX_TEXTURE_SIZE)}var a=Math.min(Oe.full_width,o),s=a/Oe.full_width;Oe.full_width=a,Oe.cropped_width*=s,Oe.cropped_x*=s,e.width=Oe.cropped_width,Oe.full_height*=s,Oe.cropped_height*=s,Oe.cropped_y*=s,e.height=Oe.cropped_height;var l=document.createElement("canvas");l.width=Oe.full_width,l.height=Oe.full_height;var r=l.getContext("2d");r.drawImage(e,Oe.cropped_x,Oe.cropped_y,Oe.cropped_width,Oe.cropped_height),c(l.toDataURL("image/jpeg"))},we.match(/^data:image\/[a-z]+;base64/)||e.setAttribute("crossOrigin","anonymous"),e.src=we},c=function(e){var t=new THREE.Texture,n=new THREE.ImageLoader,o=function(e){t.needsUpdate=!0,t.image=e,u(t)};n.load(e,o)},u=function(e){void 0!==ye.width&&(ve.style.width=ye.width.css),void 0!==ye.height&&(ve.style.height=ye.height.css),b(),Ce=n()?new THREE.WebGLRenderer:new THREE.CanvasRenderer,Ce.setSize(me.width,me.height),ke=new THREE.Scene,_e=new THREE.PerspectiveCamera(Y,me.ratio,1,300),_e.position.set(0,0,0),ke.add(_e);var t=new THREE.SphereGeometry(200,32,32),i=new THREE.MeshBasicMaterial({map:e,overdraw:!0}),r=new THREE.Mesh(t,i);r.scale.x=-1,ke.add(r),xe=document.createElement("div"),xe.style.position="absolute",xe.style.zIndex=0,be.appendChild(xe),he&&(ue.setStyle(pe),ue.create(),be.appendChild(ue.getBar())),o(window,"resize",b),ge&&(o(xe,"mousedown",C),o(document,"mousemove",z),o(xe,"mousemove",D),o(document,"mouseup",S),o(xe,"touchstart",k),o(document,"touchend",S),o(document,"touchmove",T),o(xe,"mousewheel",P),o(xe,"DOMMouseScroll",P)),o(document,"fullscreenchange",O),o(document,"mozfullscreenchange",O),o(document,"webkitfullscreenchange",O),o(document,"MSFullscreenChange",O),Pe.addListener(H),ve.innerHTML="",ve.appendChild(be);var a=Ce.domElement;a.style.display="block",xe.appendChild(a),h(),ne>0&&F(ne),m(),$("ready")},h=function(){var e=new THREE.Vector3;e.setX(Math.cos(Q)*Math.sin(ee)),e.setY(Math.sin(Q)),e.setZ(Math.cos(Q)*Math.cos(ee)),_e.lookAt(e),null!==Ee?Ee.render(ke,_e):Ce.render(ke,_e)},p=function(){Ee=new THREE.StereoEffect(Ce),Ee.eyeSeparation=5,Ee.setSize(me.width,me.height),I(),A(),ue.mustBeHidden(),h(),$("stereo-effect",!0)},g=function(){Ee=null,Ce.setSize(me.width,me.height),ue.mustBeHidden(!1),h(),$("stereo-effect",!1)};this.toggleStereo=function(){null!==Ee?g():p()};var m=function(){re!==!1&&(He=setTimeout(y,re))},v=function(){Q-=(Q-de)*le,ee+=ae;var e=!0;K!=Z&&(ee=i(ee,K,Z),(ee==K||ee==Z)&&(se?ae*=-1:(f(),e=!1))),ee=a(ee),h(),e&&(Me=setTimeout(v,ie))},y=function(){v(),$("autorotate",!0)},f=function(){clearTimeout(He),He=null,clearTimeout(Me),Me=null,$("autorotate",!1)};this.toggleAutorotate=function(){clearTimeout(He),Me?f():y()};var b=function(){(ve.clientWidth!=me.width||ve.clientHeight!=me.height)&&x({width:ve.clientWidth,height:ve.clientHeight})};this.fitToContainer=function(){b()};var x=function(e){me.width=void 0!==e.width?parseInt(e.width):me.width,me.height=void 0!==e.height?parseInt(e.height):me.height,me.ratio=me.width/me.height,_e&&(_e.aspect=me.ratio,_e.updateProjectionMatrix()),Ce&&(Ce.setSize(me.width,me.height),h()),Ee&&(Ee.setSize(me.width,me.height),h())};this.getPosition=function(){return{longitude:ee,latitude:Q}},this.getPositionInDegrees=function(){return{longitude:180*ee/Math.PI,latitude:180*Q/Math.PI}};var w=function(e,t){var n=W(e);K!=Z&&(n=i(n,K,Z));var o=W(t);o>Math.PI&&(o-=2*Math.PI),o=i(o,U,j),ee=n,Q=o,h()};this.moveTo=function(e,t){w(e,t)};var C=function(e){_(parseInt(e.clientX),parseInt(e.clientY))},k=function(e){if(1==e.touches.length){var t=e.touches[0];t.target.parentNode==xe&&_(parseInt(t.clientX),parseInt(t.clientY))}else 2==e.touches.length&&(S(),e.touches[0].target.parentNode==xe&&e.touches[1].target.parentNode==xe&&E(r(e.touches[0].clientX,e.touches[0].clientY,e.touches[1].clientX,e.touches[1].clientY)));D()},_=function(e,t){ze=e,Te=t,f(),Se=!0},E=function(e){Ie=e,Re=!0},S=function(e){Se=!1,Re=!1},z=function(e){e.preventDefault(),R(parseInt(e.clientX),parseInt(e.clientY))},T=function(e){if(1==e.touches.length&&Se){var t=e.touches[0];t.target.parentNode==xe&&(e.preventDefault(),R(parseInt(t.clientX),parseInt(t.clientY)))}else if(2==e.touches.length&&e.touches[0].target.parentNode==xe&&e.touches[1].target.parentNode==xe&&Re){e.preventDefault();var n=r(e.touches[0].clientX,e.touches[0].clientY,e.touches[1].clientX,e.touches[1].clientY),o=n-Ie;if(0!=o){var i=o/Math.abs(o);F(ne+i),Ie=n}}},R=function(e,t){Se&&(ee+=(e-ze)*q,K!=Z&&(ee=i(ee,K,Z)),ee=a(ee),Q+=(t-Te)*V,Q=i(Q,U,j),ze=e,Te=t,h())},I=function(){Pe.start(),f(),$("device-orientation",!0)},M=function(){Pe.stop(),$("device-orientation",!1)};this.toggleDeviceOrientation=function(){Pe.isEventAttached()?M():I()};var H=function(e){ee=i(e.longitude,K,Z),Q=i(e.latitude,U,j),h()},P=function(e){e.preventDefault(),e.stopPropagation();var t=e.detail?-e.detail:e.wheelDelta;if(0!=t){var n=parseInt(t/Math.abs(t));F(ne+n)}},F=function(e){ne=i(parseInt(Math.round(e)),0,100),_e.fov=Y+ne/100*(G-Y),_e.updateProjectionMatrix(),h(),$("zoom-updated",ne)};this.zoom=function(e){F(e)},this.zoomIn=function(){100>ne&&F(ne+1)},this.zoomOut=function(){ne>0&&F(ne-1)};var B=function(){return!!(document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement)},O=function(){document.webkitFullscreenElement||document.msFullscreenElement?(fe.width=ve.style.width,fe.height=ve.style.height,ve.style.width="100%",ve.style.height="100%",b()):(ve.webkitRequestFullscreen||ve.msRequestFullscreen)&&(ve.style.width=fe.width,ve.style.height=fe.height,b()),$("fullscreen-mode",B())},A=function(){ve.requestFullscreen?ve.requestFullscreen():ve.mozRequestFullScreen?ve.mozRequestFullScreen():ve.webkitRequestFullscreen?ve.webkitRequestFullscreen():ve.msRequestFullscreen&&ve.msRequestFullscreen()},L=function(){document.exitFullscreen?document.exitFullscreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen?document.webkitExitFullscreen():document.msExitFullscreen&&document.msExitFullscreen()};this.toggleFullscreen=function(){B()?L():A()};var D=function(){he&&ue.show()},X=function(e){e=e.toString().trim();var t=parseFloat(e.replace(/^(-?[0-9]+(?:\.[0-9]*)?).*$/,"$1")),n=e.replace(/^-?[0-9]+(?:\.[0-9]*)?(.*)$/,"$1").trim();n.match(/(pm|per minute)$/)&&(t/=60);var o=0;switch(n){case"rpm":case"rev per minute":case"revolutions per minute":case"rps":case"rev per second":case"revolutions per second":o=2*t*Math.PI;break;case"dpm":case"deg per minute":case"degrees per minute":case"dps":case"deg per second":case"degrees per second":o=t*Math.PI/180;break;case"rad per minute":case"radians per minute":case"rad per second":case"radians per second":o=t;break;default:m_anim=!1}return o*ie/1e3},W=function(e){e=e.toString().trim();var t=parseFloat(e.replace(/^(-?[0-9]+(?:\.[0-9]*)?).*$/,"$1")),n=e.replace(/^-?[0-9]+(?:\.[0-9]*)?(.*)$/,"$1").trim();return"deg"==n&&(t*=Math.PI/180),a(t)},N=function(e){for(dim in e)if("width"==dim||"height"==dim){var t=e[dim].toString().trim(),n=parseFloat(t.replace(/^([0-9]+(?:\.[0-9]*)?).*$/,"$1")),o=t.replace(/^[0-9]+(?:\.[0-9]*)?(.*)$/,"$1").trim();"%"!=o&&(o="px"),ye[dim]={css:n+o,unit:o}}};this.addAction=function(e,t){e in Fe||(Fe[e]=[]),Fe[e].push(t)};var $=function(e,t){if(e in Fe&&Fe[e].length)for(var n=0,o=Fe[e].length;o>n;++n)void 0!==t?Fe[e][n](t):Fe[e][n]()};if(void 0===e||void 0===e.panorama||void 0===e.container)return void console.log("PhotoSphereViewer: no value given for panorama or container");var q=void 0!==e.long_offset?parseFloat(e.long_offset):Math.PI/360,V=void 0!==e.lat_offset?parseFloat(e.lat_offset):Math.PI/180,G=void 0!==e.min_fov?i(parseFloat(e.min_fov),1,179):30,Y=void 0!==e.max_fov?i(parseFloat(e.max_fov),1,179):90,j=void 0!==e.tilt_up_max?i(W(e.tilt_up_max),0,Math.PI/2):Math.PI/2,U=void 0!==e.tilt_down_max?-i(W(e.tilt_down_max),0,Math.PI/2):-Math.PI/2,K=void 0!==e.min_longitude?W(e.min_longitude):0,Z=void 0!==e.max_longitude?W(e.max_longitude):0;if(K>Z){var J=K;K=Z,Z=J}var Q=0,ee=K;if(void 0!==e.default_position){if(void 0!==e.default_position.lat){var te=W(e.default_position.lat);te>Math.PI&&(te-=2*Math.PI),Q=i(te,U,j)}void 0!==e.default_position["long"]&&(ee=i(W(e.default_position["long"]),K,Z))}var ne=0;void 0!==e.zoom_level&&(ne=i(parseInt(Math.round(e.zoom_level)),0,100));var oe=60,ie=1e3/oe,re=2e3;void 0!==e.time_anim&&(re="number"==typeof e.time_anim&&e.time_anim>=0?e.time_anim:!1);var ae=X(void 0!==e.anim_speed?e.anim_speed:"2rpm"),se=!0;void 0!==e.reverse_anim&&(se=!!e.reverse_anim);var le=X(void 0!==e.vertical_anim_speed?e.vertical_anim_speed:"2rpm"),de=0;if(void 0!==e.vertical_anim_target){var ce=W(e.vertical_anim_target);ce>Math.PI&&(ce-=2*Math.PI),de=i(ce,U,j)}var ue=new PSVNavBar(this),he=void 0!==e.navbar?!!e.navbar:!1,pe=void 0!==e.navbar_style?e.navbar_style:{},ge=void 0!==e.allow_user_interactions?!!e.allow_user_interactions:!0;ge||(he=!1);var me,ve=e.container,ye={},fe={};void 0!==e.size&&N(e.size);var be,xe,we=e.panorama,Ce=null,ke=null,_e=null,Ee=null,Se=!1,ze=0,Te=0,Re=!1,Ie=0,Me=null,He=null,Pe=new Sphoords,Fe={},Be=void 0!==e.usexmpdata?!!e.usexmpdata:!0,Oe={full_width:null,full_height:null,cropped_width:null,cropped_height:null,cropped_x:null,cropped_y:null};if(void 0!==e.pano_size){for(attr in Oe)void 0!==e.pano_size[attr]&&(Oe[attr]=parseInt(e.pano_size[attr]));Be=!1}var Ae=void 0!==e.loading_msg?e.loading_msg.toString():"Loading…",Le=void 0!==e.loading_img?e.loading_img.toString():null,De=void 0!==e.loading_html?e.loading_html:null;void 0!==e.onready&&this.addAction("ready",e.onready);var Xe=void 0!==e.autoload?!!e.autoload:!0;Xe&&this.load()},PSVNavBar=function(e){var t=function(e,t){for(var n=0,o=t.length;o>n;++n)if(t[n]==e)return!0;return!1},n=function(e,n){return t(e,h)&&"string"==typeof n&&("transparent"==n||!!n.match(/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/)||!!n.match(/^rgb\((1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])(,\s*(1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])){2}\)$/)||!!n.match(/^rgba\(((1?[0-9]{1,2}|2[0-4][0-9]|25[0-5]),\s*){3}(0(\.[0-9]*)?|1)\)$/))||t(e,p)&&!isNaN(parseFloat(n))&&isFinite(n)&&n>=0};this.setStyle=function(e){for(property in e)property in u&&n(property,e[property])&&(u[property]=e[property])},this.create=function(){r=document.createElement("div"),r.style.backgroundColor=u.backgroundColor,r.style.position="absolute",r.style.zIndex=10,r.style.bottom=0,r.style.width="100%",r.style.boxSizing="content-box",r.style.transition="bottom 0.4s ease-out",a=new PSVNavBarButton(e,"autorotate",u),r.appendChild(a.getButton()),s=new PSVNavBarButton(e,"zoom",u),r.appendChild(s.getButton()),l=new PSVNavBarButton(e,"fullscreen",u),r.appendChild(l.getButton()),Sphoords.isDeviceOrientationSupported&&(d=new PSVNavBarButton(e,"orientation",u),r.appendChild(d.getButton()),c=new PSVNavBarButton(e,"virtual-reality",u),r.appendChild(c.getButton()))},this.getBar=function(){return r};var o=function(){g&&(clearTimeout(g),!m&&v&&(g=setTimeout(i,5e3))),m&&(r.style.bottom=0,m=!1,v&&(g=setTimeout(i,5e3)))};this.show=function(){o()};var i=function(){m||(r.style.bottom=-r.offsetHeight+1+"px",m=!0)};this.hide=function(){i()},this.isHidden=function(){return m},this.mustBeHidden=function(e){v=void 0!==e?!!e:!0,v?i():o()};var r,a,s,l,d,c,u={backgroundColor:"rgba(61, 61, 61, 0.5)",buttonsColor:"rgba(255, 255, 255, 0.7)",buttonsBackgroundColor:"transparent",activeButtonsBackgroundColor:"rgba(255, 255, 255, 0.1)",buttonsHeight:20,autorotateThickness:1,zoomRangeWidth:50,zoomRangeThickness:1,zoomRangeDisk:7,fullscreenRatio:4/3,fullscreenThickness:2,gyroscopeThickness:1,virtualRealityRatio:4/3,virtualRealityBorderRadius:2},h=["backgroundColor","buttonsColor","buttonsBackgroundColor","activeButtonsBackgroundColor"],p=["buttonsHeight","autorotateThickness","zoomRangeWidth","zoomRangeThickness","zoomRangeDisk","fullscreenRatio","fullscreenThickness"],g=null,m=!1,v=!1},PSVNavBarButton=function(e,t,n){var o=function(e,t,n){e.addEventListener?e.addEventListener(t,n,!1):e.attachEvent("on"+t,n)},i=function(){switch(t){case"autorotate":var i=n.buttonsHeight-2*n.autorotateThickness,p=i/10;l=document.createElement("div"),l.style.cssFloat="left",l.style.boxSizing="inherit",l.style.padding="10px",l.style.width=n.buttonsHeight+"px",l.style.height=n.buttonsHeight+"px",l.style.backgroundColor=n.buttonsBackgroundColor,l.style.position="relative",l.style.cursor="pointer",o(l,"click",function(){e.toggleAutorotate()});var y=document.createElement("div");y.style.boxSizing="inherit",y.style.width=i+"px",y.style.height=i+"px",y.style.borderRadius="50%",y.style.border=n.autorotateThickness+"px solid "+n.buttonsColor,l.appendChild(y);var f=document.createElement("div");f.style.boxSizing="inherit",f.style.width=i+"px",f.style.height=p+"px",f.style.borderRadius="50%",f.style.border=n.autorotateThickness+"px solid "+n.buttonsColor,f.style.position="absolute",f.style.top="50%",f.style.marginTop=-(p/2+n.autorotateThickness)+"px",l.appendChild(f),e.addAction("autorotate",d);break;case"zoom":l=document.createElement("div"),l.style.cssFloat="left",l.style.boxSizing="inherit";var b=document.createElement("div");b.style.cssFloat="left",b.style.boxSizing="inherit",b.style.padding="10px",b.style.height=n.buttonsHeight+"px",b.style.backgroundColor=n.buttonsBackgroundColor,b.style.lineHeight=n.buttonsHeight+"px",b.style.color=n.buttonsColor,b.style.cursor="pointer",b.textContent="-",o(b,"click",function(){e.zoomOut()}),l.appendChild(b),r=document.createElement("div"),r.style.cssFloat="left",r.style.boxSizing="inherit",r.style.padding=10+(n.buttonsHeight-n.zoomRangeThickness)/2+"px 5px",r.style.backgroundColor=n.buttonsBackgroundColor,r.style.cursor="pointer",l.appendChild(r),a=document.createElement("div"),a.style.boxSizing="inherit",a.style.width=n.zoomRangeWidth+"px",a.style.height=n.zoomRangeThickness+"px",a.style.backgroundColor=n.buttonsColor,a.style.position="relative",r.appendChild(a),s=document.createElement("div"),s.style.position="absolute",s.style.top=(n.zoomRangeThickness-n.zoomRangeDisk)/2+"px",s.style.left=-(n.zoomRangeDisk/2)+"px",s.style.boxSizing="inherit",s.style.width=n.zoomRangeDisk+"px",s.style.height=n.zoomRangeDisk+"px",s.style.borderRadius="50%",s.style.backgroundColor=n.buttonsColor,e.addAction("zoom-updated",c),o(r,"mousedown",u),o(r,"touchstart",h),o(document,"mousemove",m),o(document,"touchmove",v),o(document,"mouseup",g),o(document,"touchend",g),a.appendChild(s);var x=document.createElement("div");x.style.cssFloat="left",x.style.boxSizing="inherit",x.style.padding="10px",x.style.height=n.buttonsHeight+"px",x.style.backgroundColor=n.buttonsBackgroundColor,x.style.lineHeight=n.buttonsHeight+"px",x.style.color=n.buttonsColor,x.style.cursor="pointer",x.textContent="+",o(x,"click",function(){e.zoomIn()}),l.appendChild(x);break;case"fullscreen":var w=n.buttonsHeight*n.fullscreenRatio,C=.3*n.buttonsHeight,k=(n.buttonsHeight-C)/2,_=.3*w,E=(w-_)/2-n.fullscreenThickness,S=n.buttonsHeight-2*n.fullscreenThickness;l=document.createElement("div"),l.style.cssFloat="right",l.style.boxSizing="inherit",l.style.padding="10px",l.style.width=w+"px",l.style.height=n.buttonsHeight+"px",l.style.backgroundColor=n.buttonsBackgroundColor,l.style.cursor="pointer",o(l,"click",function(){e.toggleFullscreen()});var z=document.createElement("div");z.style.cssFloat="left",z.style.boxSizing="inherit",z.style.width=n.fullscreenThickness+"px",z.style.height=C+"px",z.style.borderStyle="solid",z.style.borderColor=n.buttonsColor+" transparent",z.style.borderWidth=k+"px 0",l.appendChild(z);var T=document.createElement("div");T.style.cssFloat="left",T.style.boxSizing="inherit",T.style.width=E+"px",T.style.height=S+"px",T.style.borderStyle="solid",T.style.borderColor=n.buttonsColor+" transparent",T.style.borderWidth=n.fullscreenThickness+"px 0",l.appendChild(T);var R=document.createElement("div");R.style.cssFloat="left",R.style.boxSizing="inherit",R.style.marginLeft=_+"px",R.style.width=E+"px",R.style.height=S+"px",R.style.borderStyle="solid",R.style.borderColor=n.buttonsColor+" transparent",R.style.borderWidth=n.fullscreenThickness+"px 0",l.appendChild(R);var I=document.createElement("div");I.style.cssFloat="left",I.style.boxSizing="inherit",I.style.width=n.fullscreenThickness+"px",I.style.height=C+"px",I.style.borderStyle="solid",I.style.borderColor=n.buttonsColor+" transparent",I.style.borderWidth=k+"px 0",l.appendChild(I);var M=document.createElement("div");M.style.clear="left",l.appendChild(M),e.addAction("fullscreen-mode",d);break;case"orientation":var H=n.buttonsHeight-2*n.gyroscopeThickness,P=H-4*n.gyroscopeThickness,F=H/10;l=document.createElement("div"),l.style.cssFloat="right",l.style.boxSizing="inherit",l.style.padding="10px",l.style.width=n.buttonsHeight+"px",l.style.height=n.buttonsHeight+"px",l.style.backgroundColor=n.buttonsBackgroundColor,l.style.position="relative",l.style.cursor="pointer",o(l,"click",function(){e.toggleDeviceOrientation()});var B=document.createElement("div");B.style.boxSizing="inherit",B.style.width=H+"px",B.style.height=H+"px",B.style.borderRadius="50%",B.style.border=n.gyroscopeThickness+"px solid "+n.buttonsColor,l.appendChild(B);var O=document.createElement("div");O.style.boxSizing="inherit",O.style.width=P+"px",O.style.height=F+"px",O.style.borderRadius="50%",O.style.border=n.gyroscopeThickness+"px solid "+n.buttonsColor,O.style.position="absolute",O.style.top="50%",O.style.left="50%",O.style.marginTop=-(F/2+n.gyroscopeThickness)+"px",O.style.marginLeft=-(P/2+n.gyroscopeThickness)+"px",l.appendChild(O);var A=document.createElement("div");A.style.boxSizing="inherit",A.style.width=F+"px",A.style.height=P+"px",A.style.borderRadius="50%",A.style.border=n.gyroscopeThickness+"px solid "+n.buttonsColor,A.style.position="absolute",A.style.top="50%",A.style.left="50%",A.style.marginTop=-(P/2+n.gyroscopeThickness)+"px",A.style.marginLeft=-(F/2+n.gyroscopeThickness)+"px",l.appendChild(A),e.addAction("device-orientation",d);break;case"virtual-reality":var L=n.buttonsHeight*n.virtualRealityRatio,D=L/4,X=D/2;l=document.createElement("div"),l.style.cssFloat="right",l.style.position="relative",l.style.boxSizing="inherit",l.style.padding="10px",l.style.width=L+"px",l.style.height=n.buttonsHeight+"px",l.style.backgroundColor=n.buttonsBackgroundColor,l.style.cursor="pointer",o(l,"click",function(){e.toggleStereo()});var W=document.createElement("div");W.style.boxSizing="inherit",W.style.width=L+"px",W.style.height=n.buttonsHeight+"px",W.style.borderRadius=n.virtualRealityBorderRadius+"px",W.style.backgroundColor=n.buttonsColor,l.appendChild(W);var N=document.createElement("div");N.style.boxSizing="inherit",N.style.width=D+"px",N.style.height=D+"px",N.style.position="absolute",N.style.top=X+10+"px",N.style.left=X+10+"px",N.style.borderRadius="50%",N.style.backgroundColor=n.backgroundColor,l.appendChild(N);var $=document.createElement("div");$.style.boxSizing="inherit",$.style.width=D+"px",$.style.height=D+"px",$.style.position="absolute",$.style.top=X+10+"px",$.style.right=X+10+"px",$.style.borderRadius="50%",$.style.backgroundColor=n.backgroundColor,l.appendChild($);var q=document.createElement("div");q.style.boxSizing="inherit",q.style.width=D+"px",q.style.height=n.buttonsHeight/2+"px",q.style.position="absolute",q.style.left="50%",q.style.bottom="10px",q.style.marginLeft=-(D/2)+"px",q.style.borderTopLeftRadius="50% 60%",q.style.borderTopRightRadius="50% 60%",q.style.backgroundColor=n.backgroundColor,l.appendChild(q),e.addAction("stereo-effect",d)}};this.getButton=function(){return l};var r,a,s,l,d=function(e){e?l.style.backgroundColor=n.activeButtonsBackgroundColor:l.style.backgroundColor=n.buttonsBackgroundColor},c=function(e){s.style.left=e/100*n.zoomRangeWidth-n.zoomRangeDisk/2+"px"},u=function(e){p(parseInt(e.clientX))},h=function(e){var t=e.touches[0];(t.target==r||t.target==a||t.target==s)&&p(parseInt(t.clientX))},p=function(e){f=!0,y(e)},g=function(e){f=!1},m=function(e){e.preventDefault(),y(parseInt(e.clientX))},v=function(e){var t=e.touches[0];(t.target==r||t.target==a||t.target==s)&&(e.preventDefault(),y(parseInt(t.clientX)))},y=function(t){if(f){var o=t-a.getBoundingClientRect().left,i=o/n.zoomRangeWidth*100;e.zoom(i)}},f=!1;i()},Sphoords=function(){var e=function(){var e=navigator.userAgent;return/Gecko\/[0-9.]+/.test(e)?"Gecko":/Chrome\/[0-9.]+/.test(e)?"Blink":/AppleWebKit\/[0-9.]+/.test(e)?"WebKit":/Trident\/[0-9.]+/.test(e)?"Trident":/Opera\/[0-9.]+/.test(e)?"Presto":"Gecko"},t=function(e){return e-360*Math.floor(e/360)};this.start=function(){return Sphoords.isDeviceOrientationSupported?(window.addEventListener("deviceorientation",n,!1),i=!0,!0):(console.log("Device Orientation API not supported"),!1)},this.stop=function(){i&&(window.removeEventListener("deviceorientation",n,!1),i=!1)},this.toggle=function(){i?this.stop():this.start()},this.isEventAttached=function(){return i};var n=function(e){c=Sphoords.getScreenOrientation();var n=0,i=0;switch(c){case"portrait-primary":n=e.alpha+e.gamma,i=e.beta-90;break;case"landscape-primary":if(n=e.alpha+e.beta-90,i=-e.gamma-90,Math.abs(e.beta)>90)switch(u){case"Blink":i+=180;break;case"Gecko":default:i=-i}break;case"landscape-secondary":if(n=e.alpha-e.beta+90,i=e.gamma-90,Math.abs(e.beta)>90)switch(u){case"Blink":i+=180;break;case"Gecko":default:i=-i}break;case"portrait-secondary":n=e.alpha-e.gamma,i=180-(e.beta-90),i=270-e.beta}i=t(i),i>=180&&(i-=360),r=t(n),a=Math.max(-90,Math.min(90,i)),s=r*d,l=a*d,o()};this.getCoordinates=function(){return{longitude:s,latitude:l}},this.getCoordinatesInDegrees=function(){return{longitude:r,latitude:a}},this.getScreenOrientation=function(){return c},this.addListener=function(e){h.push(e)};var o=function(){if(h.length)for(var e=0,t=h.length;t>e;++e)h[e]({longitude:s,latitude:l})},i=!1,r=0,a=0,s=0,l=0,d=Math.PI/180,c=Sphoords.getScreenOrientation(),u=e(),h=[]};Sphoords.getScreenOrientation=function(){var e=null;return screen.orientation?e=screen.orientation:screen.mozOrientation?e=screen.mozOrientation:screen.msOrientation&&(e=screen.msOrientation),null!==e&&"object"==typeof e?e.type:e},Sphoords.isDeviceOrientationSupported=!1,function(){function e(t){null!==t&&null!==t.alpha&&(Sphoords.isDeviceOrientationSupported=!0,window.removeEventListener("deviceorientation",e))}window.DeviceOrientationEvent&&null!==Sphoords.getScreenOrientation()&&window.addEventListener("deviceorientation",e)}(); \ No newline at end of file diff --git a/src/PSVNavBar.js b/src/PSVNavBar.js index 9388dc0d5..b2300d54d 100644 --- a/src/PSVNavBar.js +++ b/src/PSVNavBar.js @@ -1,5 +1,5 @@ /* - * Photo Sphere Viewer v2.3.1 + * Photo Sphere Viewer v2.4 * http://jeremyheleine.me/photo-sphere-viewer * * Copyright (c) 2014,2015 Jérémy Heleine diff --git a/src/PSVNavBarButton.js b/src/PSVNavBarButton.js index f337f3ab3..38272b37c 100644 --- a/src/PSVNavBarButton.js +++ b/src/PSVNavBarButton.js @@ -1,5 +1,5 @@ /* - * Photo Sphere Viewer v2.3.1 + * Photo Sphere Viewer v2.4 * http://jeremyheleine.me/photo-sphere-viewer * * Copyright (c) 2014,2015 Jérémy Heleine @@ -64,6 +64,7 @@ var PSVNavBarButton = function(psv, type, style) { // Autorotate button button = document.createElement('div'); button.style.cssFloat = 'left'; + button.style.boxSizing = 'inherit'; button.style.padding = '10px'; button.style.width = style.buttonsHeight + 'px'; button.style.height = style.buttonsHeight + 'px'; @@ -74,6 +75,7 @@ var PSVNavBarButton = function(psv, type, style) { addEvent(button, 'click', function(){psv.toggleAutorotate();}); var autorotate_sphere = document.createElement('div'); + autorotate_sphere.style.boxSizing = 'inherit'; autorotate_sphere.style.width = autorotate_sphere_width + 'px'; autorotate_sphere.style.height = autorotate_sphere_width + 'px'; autorotate_sphere.style.borderRadius = '50%'; @@ -81,6 +83,7 @@ var PSVNavBarButton = function(psv, type, style) { button.appendChild(autorotate_sphere); var autorotate_equator = document.createElement('div'); + autorotate_equator.style.boxSizing = 'inherit'; autorotate_equator.style.width = autorotate_sphere_width + 'px'; autorotate_equator.style.height = autorotate_equator_height + 'px'; autorotate_equator.style.borderRadius = '50%'; @@ -99,10 +102,12 @@ var PSVNavBarButton = function(psv, type, style) { // Zoom container button = document.createElement('div'); button.style.cssFloat = 'left'; + button.style.boxSizing = 'inherit'; // Zoom "-" var zoom_minus = document.createElement('div'); zoom_minus.style.cssFloat = 'left'; + zoom_minus.style.boxSizing = 'inherit'; zoom_minus.style.padding = '10px'; zoom_minus.style.height = style.buttonsHeight + 'px'; zoom_minus.style.backgroundColor = style.buttonsBackgroundColor; @@ -117,12 +122,14 @@ var PSVNavBarButton = function(psv, type, style) { // Zoom range zoom_range_bg = document.createElement('div'); zoom_range_bg.style.cssFloat = 'left'; + zoom_range_bg.style.boxSizing = 'inherit'; zoom_range_bg.style.padding = (10 + (style.buttonsHeight - style.zoomRangeThickness) / 2) + 'px 5px'; zoom_range_bg.style.backgroundColor = style.buttonsBackgroundColor; zoom_range_bg.style.cursor = 'pointer'; button.appendChild(zoom_range_bg); zoom_range = document.createElement('div'); + zoom_range.style.boxSizing = 'inherit'; zoom_range.style.width = style.zoomRangeWidth + 'px'; zoom_range.style.height = style.zoomRangeThickness + 'px'; zoom_range.style.backgroundColor = style.buttonsColor; @@ -133,6 +140,7 @@ var PSVNavBarButton = function(psv, type, style) { zoom_value.style.position = 'absolute'; zoom_value.style.top = ((style.zoomRangeThickness - style.zoomRangeDisk) / 2) + 'px'; zoom_value.style.left = -(style.zoomRangeDisk / 2) + 'px'; + zoom_value.style.boxSizing = 'inherit'; zoom_value.style.width = style.zoomRangeDisk + 'px'; zoom_value.style.height = style.zoomRangeDisk + 'px'; zoom_value.style.borderRadius = '50%'; @@ -150,6 +158,7 @@ var PSVNavBarButton = function(psv, type, style) { // Zoom "+" var zoom_plus = document.createElement('div'); zoom_plus.style.cssFloat = 'left'; + zoom_plus.style.boxSizing = 'inherit'; zoom_plus.style.padding = '10px'; zoom_plus.style.height = style.buttonsHeight + 'px'; zoom_plus.style.backgroundColor = style.buttonsBackgroundColor; @@ -177,6 +186,7 @@ var PSVNavBarButton = function(psv, type, style) { // Fullscreen button button = document.createElement('div'); button.style.cssFloat = 'right'; + button.style.boxSizing = 'inherit'; button.style.padding = '10px'; button.style.width = fullscreen_width + 'px'; button.style.height = style.buttonsHeight + 'px'; @@ -188,6 +198,7 @@ var PSVNavBarButton = function(psv, type, style) { // Fullscreen icon left side var fullscreen_left = document.createElement('div'); fullscreen_left.style.cssFloat = 'left'; + fullscreen_left.style.boxSizing = 'inherit'; fullscreen_left.style.width = style.fullscreenThickness + 'px'; fullscreen_left.style.height = fullscreen_vertical_space + 'px'; fullscreen_left.style.borderStyle = 'solid'; @@ -198,6 +209,7 @@ var PSVNavBarButton = function(psv, type, style) { // Fullscreen icon top/bottom sides (first half) var fullscreen_tb_1 = document.createElement('div'); fullscreen_tb_1.style.cssFloat = 'left'; + fullscreen_tb_1.style.boxSizing = 'inherit'; fullscreen_tb_1.style.width = fullscreen_horizontal_border + 'px'; fullscreen_tb_1.style.height = fullscreen_vertical_int + 'px'; fullscreen_tb_1.style.borderStyle = 'solid'; @@ -208,6 +220,7 @@ var PSVNavBarButton = function(psv, type, style) { // Fullscreen icon top/bottom sides (second half) var fullscreen_tb_2 = document.createElement('div'); fullscreen_tb_2.style.cssFloat = 'left'; + fullscreen_tb_2.style.boxSizing = 'inherit'; fullscreen_tb_2.style.marginLeft = fullscreen_horizontal_space + 'px'; fullscreen_tb_2.style.width = fullscreen_horizontal_border + 'px'; fullscreen_tb_2.style.height = fullscreen_vertical_int + 'px'; @@ -219,6 +232,7 @@ var PSVNavBarButton = function(psv, type, style) { // Fullscreen icon right side var fullscreen_right = document.createElement('div'); fullscreen_right.style.cssFloat = 'left'; + fullscreen_right.style.boxSizing = 'inherit'; fullscreen_right.style.width = style.fullscreenThickness + 'px'; fullscreen_right.style.height = fullscreen_vertical_space + 'px'; fullscreen_right.style.borderStyle = 'solid'; @@ -244,6 +258,7 @@ var PSVNavBarButton = function(psv, type, style) { // Gyroscope button button = document.createElement('div'); button.style.cssFloat = 'right'; + button.style.boxSizing = 'inherit'; button.style.padding = '10px'; button.style.width = style.buttonsHeight + 'px'; button.style.height = style.buttonsHeight + 'px'; @@ -254,6 +269,7 @@ var PSVNavBarButton = function(psv, type, style) { addEvent(button, 'click', function(){psv.toggleDeviceOrientation();}); var gyroscope_sphere = document.createElement('div'); + gyroscope_sphere.style.boxSizing = 'inherit'; gyroscope_sphere.style.width = gyroscope_sphere_width + 'px'; gyroscope_sphere.style.height = gyroscope_sphere_width + 'px'; gyroscope_sphere.style.borderRadius = '50%'; @@ -261,6 +277,7 @@ var PSVNavBarButton = function(psv, type, style) { button.appendChild(gyroscope_sphere); var gyroscope_hor_ellipsis = document.createElement('div'); + gyroscope_hor_ellipsis.style.boxSizing = 'inherit'; gyroscope_hor_ellipsis.style.width = gyroscope_ellipses_big_axis + 'px'; gyroscope_hor_ellipsis.style.height = gyroscope_ellipses_little_axis + 'px'; gyroscope_hor_ellipsis.style.borderRadius = '50%'; @@ -273,6 +290,7 @@ var PSVNavBarButton = function(psv, type, style) { button.appendChild(gyroscope_hor_ellipsis); var gyroscope_ver_ellipsis = document.createElement('div'); + gyroscope_ver_ellipsis.style.boxSizing = 'inherit'; gyroscope_ver_ellipsis.style.width = gyroscope_ellipses_little_axis + 'px'; gyroscope_ver_ellipsis.style.height = gyroscope_ellipses_big_axis + 'px'; gyroscope_ver_ellipsis.style.borderRadius = '50%'; @@ -300,6 +318,7 @@ var PSVNavBarButton = function(psv, type, style) { button = document.createElement('div'); button.style.cssFloat = 'right'; button.style.position = 'relative'; + button.style.boxSizing = 'inherit'; button.style.padding = '10px'; button.style.width = vr_width + 'px'; button.style.height = style.buttonsHeight + 'px'; @@ -310,6 +329,7 @@ var PSVNavBarButton = function(psv, type, style) { // Icon var vr_rect = document.createElement('div'); + vr_rect.style.boxSizing = 'inherit'; vr_rect.style.width = vr_width + 'px'; vr_rect.style.height = style.buttonsHeight + 'px'; vr_rect.style.borderRadius = style.virtualRealityBorderRadius + 'px'; @@ -317,6 +337,7 @@ var PSVNavBarButton = function(psv, type, style) { button.appendChild(vr_rect); var left_eye = document.createElement('div'); + left_eye.style.boxSizing = 'inherit'; left_eye.style.width = vr_eye_diameter + 'px'; left_eye.style.height = vr_eye_diameter + 'px'; left_eye.style.position = 'absolute'; @@ -327,6 +348,7 @@ var PSVNavBarButton = function(psv, type, style) { button.appendChild(left_eye); var right_eye = document.createElement('div'); + right_eye.style.boxSizing = 'inherit'; right_eye.style.width = vr_eye_diameter + 'px'; right_eye.style.height = vr_eye_diameter + 'px'; right_eye.style.position = 'absolute'; @@ -337,6 +359,7 @@ var PSVNavBarButton = function(psv, type, style) { button.appendChild(right_eye); var nose = document.createElement('div'); + nose.style.boxSizing = 'inherit'; nose.style.width = vr_eye_diameter + 'px'; nose.style.height = (style.buttonsHeight / 2) + 'px'; nose.style.position = 'absolute'; diff --git a/src/PhotoSphereViewer.js b/src/PhotoSphereViewer.js index c223b225e..d4c1bc500 100644 --- a/src/PhotoSphereViewer.js +++ b/src/PhotoSphereViewer.js @@ -1,5 +1,5 @@ /* - * Photo Sphere Viewer v2.3.1 + * Photo Sphere Viewer v2.4 * http://jeremyheleine.me/photo-sphere-viewer * * Copyright (c) 2014,2015 Jérémy Heleine @@ -31,6 +31,13 @@ * @param {HTMLElement} args.container - Panorama container (should be a `div` or equivalent) * @param {boolean} [args.autoload=true] - `true` to automatically load the panorama, `false` to load it later (with the {@link PhotoSphereViewer#load|`.load`} method) * @param {boolean} [args.usexmpdata=true] - `true` if Photo Sphere Viewer must read XMP data, `false` if it is not necessary + * @param {object} [args.pano_size=null] - The panorama size, if cropped (unnecessary if XMP data can be read) + * @param {number} [args.pano_size.full_width=null] - The full panorama width, before crop (the image width if `null`) + * @param {number} [args.pano_size.full_height=null] - The full panorama height, before crop (the image height if `null`) + * @param {number} [args.pano_size.cropped_width=null] - The cropped panorama width (the image width if `null`) + * @param {number} [args.pano_size.cropped_height=null] - The cropped panorama height (the image height if `null`) + * @param {number} [args.pano_size.cropped_x=null] - The cropped panorama horizontal offset relative to the full width (middle if `null`) + * @param {number} [args.pano_size.cropped_y=null] - The cropped panorama vertical offset relative to the full height (middle if `null`) * @param {object} [args.default_position] - Defines the default position (the first point seen by the user) * @param {number|string} [args.default_position.long=0] - Default longitude, in radians (or in degrees if indicated, e.g. `'45deg'`) * @param {number|string} [args.default_position.lat=0] - Default latitude, in radians (or in degrees if indicated, e.g. `'45deg'`) @@ -39,12 +46,16 @@ * @param {boolean} [args.allow_user_interactions=true] - If set to `false`, the user won't be able to interact with the panorama (navigation bar is then disabled) * @param {number|string} [args.tilt_up_max=π/2] - The maximal tilt up angle, in radians (or in degrees if indicated, e.g. `'30deg'`) * @param {number|string} [args.tilt_down_max=π/2] - The maximal tilt down angle, in radians (or in degrees if indicated, e.g. `'30deg'`) + * @param {number|string} [args.min_longitude=0] - The minimal longitude to show + * @param {number|string} [args.max_longitude=2π] - The maximal longitude to show * @param {number} [args.zoom_level=0] - The default zoom level, between 0 and 100 * @param {number} [args.long_offset=π/360] - The longitude to travel per pixel moved by mouse/touch * @param {number} [args.lat_offset=π/180] - The latitude to travel per pixel moved by mouse/touch * @param {integer} [args.time_anim=2000] - Delay before automatically animating the panorama in milliseconds, `false` to not animate - * @param {integer} [args.theta_offset=1440] - (deprecated, use `anim_speed` instead) The π fraction to add to theta during the animation + * @param {boolean} [args.reverse_anim=true] - `true` if horizontal animation must be reversed when min/max longitude is reached (only if the whole circle is not described) * @param {string} [args.anim_speed=2rpm] - Animation speed in radians/degrees/revolutions per second/minute + * @param {string} [args.vertical_anim_speed=2rpm] - Vertical animation speed in radians/degrees/revolutions per second/minute + * @param {number|string} [args.vertical_anim_target=0] - Latitude to target during the autorotate animation, default to the equator * @param {boolean} [args.navbar=false] - Display the navigation bar if set to `true` * @param {object} [args.navbar_style] - Style of the navigation bar * @param {string} [args.navbar_style.backgroundColor=rgba(61, 61, 61, 0.5)] - Navigation bar background color @@ -60,6 +71,7 @@ * @param {number} [args.navbar_style.fullscreenThickness=2] - Fullscreen icon thickness in pixels * @param {string} [args.loading_msg=Loading…] - Loading message * @param {string} [args.loading_img=null] - Loading image URL or path (absolute or relative) + * @param {HTMLElement|string} [args.loading_html=null] - An HTML loader (element to append to the container or string representing the HTML) * @param {object} [args.size] - Final size of the panorama container (e.g. {width: 500, height: 300}) * @param {(number|string)} [args.size.width] - Final width in percentage (e.g. `'50%'`) or pixels (e.g. `500` or `'500px'`) ; default to current width * @param {(number|string)} [args.size.height] - Final height in percentage or pixels ; default to current height @@ -152,13 +164,25 @@ var PhotoSphereViewer = function(args) { **/ this.load = function() { - // Loading indicator (text or image if given) - if (!!loading_img) { + container.innerHTML = ''; + + // Loading HTML: HTMLElement + if (!!loading_html && loading_html.nodeType === 1) + container.appendChild(loading_html); + + // Loading HTML: string + else if (!!loading_html && typeof loading_html == 'string') + container.innerHTML = loading_html; + + // Loading image + else if (!!loading_img) { var loading = document.createElement('img'); loading.setAttribute('src', loading_img); loading.setAttribute('alt', loading_msg); container.appendChild(loading); } + + // Loading text else container.textContent = loading_msg; @@ -189,11 +213,11 @@ var PhotoSphereViewer = function(args) { }; // XMP data? - if (readxmp) + if (readxmp && !panorama.match(/^data:image\/[a-z]+;base64/)) loadXMP(); else - createBuffer(false); + createBuffer(); }; /** @@ -236,31 +260,31 @@ var PhotoSphereViewer = function(args) { } xhr.onreadystatechange = function() { - if (xhr.readyState == 4 && xhr.status == 200) { - // Metadata - var binary = xhr.responseText; - var a = binary.indexOf(''); - var data = binary.substring(a, b); - - // No data retrieved - if (a == -1 || b == -1 || data.indexOf('GPano:') == -1) { - createBuffer(false); - return; - } - - // Useful values - var pano_data = { - full_width: parseInt(getAttribute(data, 'FullPanoWidthPixels')), - full_height: parseInt(getAttribute(data, 'FullPanoHeightPixels')), - cropped_width: parseInt(getAttribute(data, 'CroppedAreaImageWidthPixels')), - cropped_height: parseInt(getAttribute(data, 'CroppedAreaImageHeightPixels')), - cropped_x: parseInt(getAttribute(data, 'CroppedAreaLeftPixels')), - cropped_y: parseInt(getAttribute(data, 'CroppedAreaTopPixels')), - }; - - createBuffer(pano_data); + if (xhr.readyState == 4 && xhr.status == 200) { + // Metadata + var binary = xhr.responseText; + var a = binary.indexOf(''); + var data = binary.substring(a, b); + + // No data retrieved + if (a == -1 || b == -1 || data.indexOf('GPano:') == -1) { + createBuffer(false); + return; } - }; + + // Useful values + pano_size = { + full_width: parseInt(getAttribute(data, 'FullPanoWidthPixels')), + full_height: parseInt(getAttribute(data, 'FullPanoHeightPixels')), + cropped_width: parseInt(getAttribute(data, 'CroppedAreaImageWidthPixels')), + cropped_height: parseInt(getAttribute(data, 'CroppedAreaImageHeightPixels')), + cropped_x: parseInt(getAttribute(data, 'CroppedAreaLeftPixels')), + cropped_y: parseInt(getAttribute(data, 'CroppedAreaTopPixels')), + }; + + createBuffer(); + } + }; xhr.open('GET', panorama, true); xhr.send(null); @@ -269,59 +293,68 @@ var PhotoSphereViewer = function(args) { /** * Creates an image in the right dimensions. * @private - * @param {mixed} pano_data - An object containing the panorama XMP data (`false` if there is not) * @return {void} **/ - var createBuffer = function(pano_data) { + var createBuffer = function() { var img = new Image(); img.onload = function() { - // No XMP data? - if (!pano_data) { - pano_data = { - full_width: img.width, - full_height: img.height, - cropped_width: img.width, - cropped_height: img.height, - cropped_x: 0, - cropped_y: 0, - }; - } + // Must the pano size be changed? + var default_pano_size = { + full_width: img.width, + full_height: img.height, + cropped_width: img.width, + cropped_height: img.height, + cropped_x: null, + cropped_y: null, + }; - // Size limit for mobile compatibility - var max_width = 2048; - if (isWebGLSupported()) { - var canvas = document.createElement('canvas'); - var ctx = canvas.getContext('webgl'); - max_width = ctx.getParameter(ctx.MAX_TEXTURE_SIZE); - } + for (attr in pano_size) { + if (pano_size[attr] == null && default_pano_size[attr] !== undefined) + pano_size[attr] = default_pano_size[attr]; + } - // Buffer width (not too big) - var new_width = Math.min(pano_data.full_width, max_width); - var r = new_width / pano_data.full_width; + // Middle if cropped_x/y is null + if (pano_size.cropped_x == null) + pano_size.cropped_x = (pano_size.full_width - pano_size.cropped_width) / 2; - pano_data.full_width = new_width; - pano_data.cropped_width *= r; - pano_data.cropped_x *= r; - img.width = pano_data.cropped_width; + if (pano_size.cropped_y == null) + pano_size.cropped_y = (pano_size.full_height - pano_size.cropped_height) / 2; - // Buffer height (proportional to the width) - pano_data.full_height *= r; - pano_data.cropped_height *= r; - pano_data.cropped_y *= r; - img.height = pano_data.cropped_height; + // Size limit for mobile compatibility + var max_width = 2048; + if (isWebGLSupported()) { + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('webgl'); + max_width = ctx.getParameter(ctx.MAX_TEXTURE_SIZE); + } - // Buffer creation - var buffer = document.createElement('canvas'); - buffer.width = pano_data.full_width; - buffer.height = pano_data.full_height; + // Buffer width (not too big) + var new_width = Math.min(pano_size.full_width, max_width); + var r = new_width / pano_size.full_width; - var ctx = buffer.getContext('2d'); - ctx.drawImage(img, pano_data.cropped_x, pano_data.cropped_y, pano_data.cropped_width, pano_data.cropped_height); + pano_size.full_width = new_width; + pano_size.cropped_width *= r; + pano_size.cropped_x *= r; + img.width = pano_size.cropped_width; - loadTexture(buffer.toDataURL('image/jpeg')); - }; + // Buffer height (proportional to the width) + pano_size.full_height *= r; + pano_size.cropped_height *= r; + pano_size.cropped_y *= r; + img.height = pano_size.cropped_height; + + // Buffer creation + var buffer = document.createElement('canvas'); + buffer.width = pano_size.full_width; + buffer.height = pano_size.full_height; + + var ctx = buffer.getContext('2d'); + ctx.drawImage(img, pano_size.cropped_x, pano_size.cropped_y, pano_size.cropped_width, pano_size.cropped_height); + + loadTexture(buffer.toDataURL('image/jpeg')); + }; // CORS when the panorama is not given as a base64 string if (!panorama.match(/^data:image\/[a-z]+;base64/)) @@ -542,15 +575,33 @@ var PhotoSphereViewer = function(args) { **/ var autorotate = function() { - // Returns to the equator (lat = 0) - lat -= lat / 200; + lat -= (lat - anim_lat_target) * anim_lat_offset; + + long += anim_long_offset; + + var again = true; - // Rotates the sphere - long += long_offset; - long -= Math.floor(long / (2.0 * Math.PI)) * 2.0 * Math.PI; + if (PSV_MIN_LONGITUDE != PSV_MAX_LONGITUDE) { + long = stayBetween(long, PSV_MIN_LONGITUDE, PSV_MAX_LONGITUDE); + + if (long == PSV_MIN_LONGITUDE || long == PSV_MAX_LONGITUDE) { + // Must we reverse the animation or simply stop it? + if (reverse_anim) + anim_long_offset *= -1; + + else { + stopAutorotate(); + again = false; + } + } + } + + long = getAngleMeasure(long); render(); - autorotate_timeout = setTimeout(autorotate, PSV_ANIM_TIMEOUT); + + if (again) + autorotate_timeout = setTimeout(autorotate, PSV_ANIM_TIMEOUT); }; /** @@ -658,6 +709,69 @@ var PhotoSphereViewer = function(args) { } }; + /** + * Returns the current position in radians + * @return {object} A longitude/latitude couple + **/ + + this.getPosition = function() { + return { + longitude: long, + latitude: lat + }; + }; + + /** + * Returns the current position in degrees + * @return {object} A longitude/latitude couple + **/ + + this.getPositionInDegrees = function() { + return { + longitude: long * 180.0 / Math.PI, + latitude: lat * 180.0 / Math.PI + }; + }; + + /** + * Moves to a specific position + * @private + * @param {number|string} longitude - The longitude of the targeted point + * @param {number|string} latitude - The latitude of the targeted point + * @return {void} + **/ + + var moveTo = function(longitude, latitude) { + var long_tmp = parseAngle(longitude); + + if (PSV_MIN_LONGITUDE != PSV_MAX_LONGITUDE) + long_tmp = stayBetween(long_tmp, PSV_MIN_LONGITUDE, PSV_MAX_LONGITUDE); + + var lat_tmp = parseAngle(latitude); + + if (lat_tmp > Math.PI) + lat_tmp -= 2 * Math.PI; + + lat_tmp = stayBetween(lat_tmp, PSV_TILT_DOWN_MAX, PSV_TILT_UP_MAX); + + long = long_tmp; + lat = lat_tmp; + + render(); + }; + + /** + * Moves to a specific position + * @public + * @param {number|string} longitude - The longitude of the targeted point + * @param {number|string} latitude - The latitude of the targeted point + * @return {void} + **/ + + this.moveTo = function(longitude, latitude) { + moveTo(longitude, latitude); + }; + /** * The user wants to move. * @private @@ -796,7 +910,13 @@ var PhotoSphereViewer = function(args) { var move = function(x, y) { if (mousedown) { - long = getAngleMeasure(long + (x - mouse_x) * PSV_LONG_OFFSET); + long += (x - mouse_x) * PSV_LONG_OFFSET; + + if (PSV_MIN_LONGITUDE != PSV_MAX_LONGITUDE) + long = stayBetween(long, PSV_MIN_LONGITUDE, PSV_MAX_LONGITUDE); + + long = getAngleMeasure(long); + lat += (y - mouse_y) * PSV_LAT_OFFSET; lat = stayBetween(lat, PSV_TILT_DOWN_MAX, PSV_TILT_UP_MAX); @@ -861,7 +981,7 @@ var PhotoSphereViewer = function(args) { **/ var onDeviceOrientation = function(coords) { - long = coords.longitude; + long = stayBetween(coords.longitude, PSV_MIN_LONGITUDE, PSV_MAX_LONGITUDE); lat = stayBetween(coords.latitude, PSV_TILT_DOWN_MAX, PSV_TILT_UP_MAX); render(); @@ -959,8 +1079,8 @@ var PhotoSphereViewer = function(args) { **/ var fullscreenToggled = function() { - // Fix the (weird and ugly) Chrome behavior - if (!!document.webkitFullscreenElement) { + // Fix the (weird and ugly) Chrome and IE behaviors + if (!!document.webkitFullscreenElement || !!document.msFullscreenElement) { real_viewer_size.width = container.style.width; real_viewer_size.height = container.style.height; @@ -969,7 +1089,7 @@ var PhotoSphereViewer = function(args) { fitToContainer(); } - else if (!!container.webkitRequestFullscreen) { + else if (!!container.webkitRequestFullscreen || !!container.msRequestFullscreen) { container.style.width = real_viewer_size.width; container.style.height = real_viewer_size.height; fitToContainer(); @@ -1052,13 +1172,13 @@ var PhotoSphereViewer = function(args) { }; /** - * Sets the animation speed. + * Parses an animation speed. * @private * @param {string} speed - The speed, in radians/degrees/revolutions per second/minute - * @return {void} + * @return {number} The speed in radians **/ - var setAnimSpeed = function(speed) { + var parseAnimationSpeed = function(speed) { speed = speed.toString().trim(); // Speed extraction @@ -1109,7 +1229,7 @@ var PhotoSphereViewer = function(args) { } // Longitude offset - long_offset = rad_per_second * PSV_ANIM_TIMEOUT / 1000; + return rad_per_second * PSV_ANIM_TIMEOUT / 1000; }; /** @@ -1212,23 +1332,38 @@ var PhotoSphereViewer = function(args) { var PSV_LONG_OFFSET = (args.long_offset !== undefined) ? parseFloat(args.long_offset) : Math.PI / 360.0; var PSV_LAT_OFFSET = (args.lat_offset !== undefined) ? parseFloat(args.lat_offset) : Math.PI / 180.0; - // Minimal and maximal fields of view in degrees + // Minimum and maximum fields of view in degrees var PSV_FOV_MIN = (args.min_fov !== undefined) ? stayBetween(parseFloat(args.min_fov), 1, 179) : 30; var PSV_FOV_MAX = (args.max_fov !== undefined) ? stayBetween(parseFloat(args.max_fov), 1, 179) : 90; - // Maximal tilt up / down angles + // Minimum tilt up / down angles var PSV_TILT_UP_MAX = (args.tilt_up_max !== undefined) ? stayBetween(parseAngle(args.tilt_up_max), 0, Math.PI / 2.0) : Math.PI / 2.0; var PSV_TILT_DOWN_MAX = (args.tilt_down_max !== undefined) ? -stayBetween(parseAngle(args.tilt_down_max), 0, Math.PI / 2.0) : -Math.PI / 2.0; + // Minimum and maximum visible longitudes ((min = max) => whole circle) + var PSV_MIN_LONGITUDE = (args.min_longitude !== undefined) ? parseAngle(args.min_longitude) : 0; + var PSV_MAX_LONGITUDE = (args.max_longitude !== undefined) ? parseAngle(args.max_longitude) : 0; + + if (PSV_MAX_LONGITUDE < PSV_MIN_LONGITUDE) { + var long_tmp = PSV_MIN_LONGITUDE; + PSV_MIN_LONGITUDE = PSV_MAX_LONGITUDE; + PSV_MAX_LONGITUDE = long_tmp; + } + // Default position - var lat = 0, long = 0; + var lat = 0, long = PSV_MIN_LONGITUDE; if (args.default_position !== undefined) { - if (args.default_position.lat !== undefined) - lat = stayBetween(parseAngle(args.default_position.lat), PSV_TILT_DOWN_MAX, PSV_TILT_UP_MAX); + if (args.default_position.lat !== undefined) { + var lat_angle = parseAngle(args.default_position.lat); + if (lat_angle > Math.PI) + lat_angle -= 2 * Math.PI; + + lat = stayBetween(lat_angle, PSV_TILT_DOWN_MAX, PSV_TILT_UP_MAX); + } if (args.default_position.long !== undefined) - long = parseAngle(args.default_position.long); + long = stayBetween(parseAngle(args.default_position.long), PSV_MIN_LONGITUDE, PSV_MAX_LONGITUDE); } // Default zoom level @@ -1252,14 +1387,28 @@ var PhotoSphereViewer = function(args) { anim_delay = false; } - // Deprecated: horizontal offset for the animation - var long_offset = (args.theta_offset !== undefined) ? Math.PI / parseInt(args.theta_offset) : Math.PI / 1440; - // Horizontal animation speed - if (args.anim_speed !== undefined) - setAnimSpeed(args.anim_speed); - else - setAnimSpeed('2rpm'); + var anim_long_offset = (args.anim_speed !== undefined) ? parseAnimationSpeed(args.anim_speed) : parseAnimationSpeed('2rpm'); + + // Reverse the horizontal animation if autorotate reaches the min/max longitude + var reverse_anim = true; + + if (args.reverse_anim !== undefined) + reverse_anim = !!args.reverse_anim; + + // Vertical animation speed + var anim_lat_offset = (args.vertical_anim_speed !== undefined) ? parseAnimationSpeed(args.vertical_anim_speed) : parseAnimationSpeed('2rpm'); + + // Vertical animation target (default: equator) + var anim_lat_target = 0; + + if (args.vertical_anim_target !== undefined) { + var lat_target_angle = parseAngle(args.vertical_anim_target); + if (lat_target_angle > Math.PI) + lat_target_angle -= 2 * Math.PI; + + anim_lat_target = stayBetween(lat_target_angle, PSV_TILT_DOWN_MAX, PSV_TILT_UP_MAX); + } // Navigation bar var navbar = new PSVNavBar(this); @@ -1299,12 +1448,34 @@ var PhotoSphereViewer = function(args) { // Must we read XMP data? var readxmp = (args.usexmpdata !== undefined) ? !!args.usexmpdata : true; + // Cropped size? + var pano_size = { + full_width: null, + full_height: null, + cropped_width: null, + cropped_height: null, + cropped_x: null, + cropped_y: null + }; + + if (args.pano_size !== undefined) { + for (attr in pano_size) { + if (args.pano_size[attr] !== undefined) + pano_size[attr] = parseInt(args.pano_size[attr]); + } + + readxmp = false; + } + // Loading message var loading_msg = (args.loading_msg !== undefined) ? args.loading_msg.toString() : 'Loading…'; // Loading image var loading_img = (args.loading_img !== undefined) ? args.loading_img.toString() : null; + // Loading HTML + var loading_html = (args.loading_html !== undefined) ? args.loading_html : null; + // Function to call once panorama is ready? if (args.onready !== undefined) this.addAction('ready', args.onready);