diff --git a/README.md b/README.md index ba951626c..4b8791895 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,15 @@ # Photo Sphere Viewer -[![Bower version](https://badge.fury.io/bo/Photo-Sphere-Viewer.svg)](http://badge.fury.io/bo/Photo-Sphere-Viewer) -[![Online documentation](https://img.shields.io/badge/documentation-online-blue.svg)](https://mistic100.github.io/Photo-Sphere-Viewer) +[![Bower version](https://img.shields.io/bower/v/Photo-Sphere-Viewer.svg?style=flat-square)](http://photo-sphere-viewer.js.org +) +[![CDN](https://img.shields.io/badge/cdn-jsdelivr-%23EB4C36.svg?style=flat-square)](http://www.jsdelivr.com/projects/photo-sphere-viewer) Photo Sphere Viewer is a JavaScript library that allows you to display 360×180 degrees panoramas on any web page. Panoramas must use the equirectangular projection and they can be taken with Photo Sphere, the camera mode brought by Android 4.2 Jelly Bean. Photo Sphere Viewer uses the [Three.js](http://threejs.org) library, so nothing is required for your visitors except for a browser compatible with canvas or, better, WebGL. +## Documentation +http://photo-sphere-viewer.js.org ## License - This library is available under the MIT license. diff --git a/bower.json b/bower.json index 228296eb2..d2e1d84fb 100644 --- a/bower.json +++ b/bower.json @@ -1,8 +1,8 @@ { "name": "Photo-Sphere-Viewer", - "version": "3.0.1", + "version": "3.1.0", "authors": [{ - "name": "Jérémy Heleine", + "name": "Jérémy Heleine", "email": "jeremy.heleine@gmail.com", "homepage": "http://jeremyheleine.me" },{ diff --git a/dist/photo-sphere-viewer.css b/dist/photo-sphere-viewer.css index 9c69ff2a0..e47ee1324 100644 --- a/dist/photo-sphere-viewer.css +++ b/dist/photo-sphere-viewer.css @@ -1,7 +1,7 @@ /*! - * Photo Sphere Viewer 3.0.1 + * Photo Sphere Viewer 3.1.0 * Copyright (c) 2014-2015 Jérémy Heleine - * Copyright (c) 2015 Damien "Mistic" Sorel + * Copyright (c) 2015-2016 Damien "Mistic" Sorel * Licensed under MIT (http://opensource.org/licenses/MIT) */ @@ -131,7 +131,7 @@ position: absolute; width: 20px; height: 7px; - left: 8px; + left: 10px; bottom: 10px; border: 2px solid rgba(255, 255, 255, 0.7); border-top-width: 0; @@ -184,7 +184,7 @@ .psv-navbar .fullscreen-button div:before, .psv-navbar .fullscreen-button div:after { content: ''; position: absolute; - width: 6.0px; + width: 6px; height: 4px; border-style: solid; border-color: rgba(255, 255, 255, 0.7); @@ -329,7 +329,7 @@ width: 100%; height: 100%; } -.psv-hud .marker { +.psv-hud .psv-marker { position: absolute; top: 0; left: 0; @@ -338,7 +338,11 @@ cursor: pointer; display: none; } -.psv-hud .marker.visible { +.psv-hud .psv-marker.transparent { + display: block; + opacity: 0; +} +.psv-hud .psv-marker.visible { display: block; } diff --git a/dist/photo-sphere-viewer.js b/dist/photo-sphere-viewer.js index f84ed4cfc..8855748a7 100644 --- a/dist/photo-sphere-viewer.js +++ b/dist/photo-sphere-viewer.js @@ -1,17 +1,17 @@ /*! - * Photo Sphere Viewer 3.0.1 + * Photo Sphere Viewer 3.1.0 * Copyright (c) 2014-2015 Jérémy Heleine - * Copyright (c) 2015 Damien "Mistic" Sorel + * Copyright (c) 2015-2016 Damien "Mistic" Sorel * Licensed under MIT (http://opensource.org/licenses/MIT) */ (function(root, factory) { - if (typeof define === 'function' && define.amd) { - define(['three'], factory); - } - else { - root.PhotoSphereViewer = factory(root.THREE); - } + if (typeof define === 'function' && define.amd) { + define(['three'], factory); + } + else { + root.PhotoSphereViewer = factory(root.THREE); + } }(this, function(THREE) { "use strict"; @@ -45,13 +45,18 @@ function PhotoSphereViewer(options) { this.config.anim_lat = this.config.default_lat; } this.config.anim_lat = PSVUtils.stayBetween(this.config.anim_lat, -PhotoSphereViewer.HalfPI, PhotoSphereViewer.HalfPI); - + if (this.config.tilt_up_max < this.config.tilt_down_max) { throw new PSVError('tilt_up_max cannot be lower than tilt_down_max'); } + if (this.config.caption && !this.config.navbar) { + this.config.navbar = 'caption'; + } + // references to components - this.container = (typeof this.config.container == 'string') ? document.getElementById(this.config.container) : this.config.container; + this.parent = (typeof this.config.container == 'string') ? document.getElementById(this.config.container) : this.config.container; + this.container = null; this.loader = null; this.navbar = null; this.hud = null; @@ -61,6 +66,7 @@ function PhotoSphereViewer(options) { this.renderer = null; this.scene = null; this.camera = null; + this.mesh = null; this.raycaster = null; this.actions = {}; @@ -93,7 +99,12 @@ function PhotoSphereViewer(options) { // compute zoom level this.prop.zoom_lvl = Math.round((this.config.default_fov - this.config.min_fov) / (this.config.max_fov - this.config.min_fov) * 100); - this.prop.zoom_lvl-= 2 * (this.prop.zoom_lvl - 50); + this.prop.zoom_lvl -= 2 * (this.prop.zoom_lvl - 50); + + // create actual container + this.container = document.createElement('div'); + this.container.classList.add('psv-container'); + this.parent.appendChild(this.container); // init this.setAnimSpeed(this.config.anim_speed); @@ -101,8 +112,7 @@ function PhotoSphereViewer(options) { this.rotate(this.config.default_long, this.config.default_lat); if (this.config.size !== null) { - this.container.style.width = this.config.size.width; - this.container.style.height = this.config.size.height; + this._setViewerSize(this.config.size); } if (this.config.autoload) { @@ -161,12 +171,76 @@ PhotoSphereViewer.DEFAULTS = { markers: [] }; +/** + * Destroy the viewer + */ +PhotoSphereViewer.prototype.destroy = function() { + // remove listeners + window.removeEventListener('resize', this); + document.removeEventListener(PSVUtils.fullscreenEvent(), this); + + if (this.config.mousemove) { + this.hud.container.removeEventListener('mousedown', this); + this.hud.container.removeEventListener('touchstart', this); + window.removeEventListener('mouseup', this); + window.removeEventListener('touchend', this); + this.hud.container.removeEventListener('mousemove', this); + this.hud.container.removeEventListener('touchmove', this); + } + + if (this.config.mousewheel) { + this.hud.container.removeEventListener(PSVUtils.mouseWheelEvent(), this); + } + + // destroy components + if (this.hud) this.hud.destroy(); + if (this.loader) this.loader.destroy(); + if (this.navbar) this.navbar.destroy(); + if (this.panel) this.panel.destroy(); + if (this.tooltip) this.tooltip.destroy(); + + // destroy ThreeJS view + if (this.scene) { + this.scene.remove(this.camera); + this.scene.remove(this.mesh); + } + + if (this.mesh) { + if (this.mesh.material) { + if (this.mesh.material.geometry) this.mesh.material.geometry.dispose(); + if (this.mesh.material.map) this.mesh.material.map.dispose(); + this.mesh.material.dispose(); + } + } + + // remove container + if (this.canvas_container) { + this.container.removeChild(this.canvas_container); + } + this.parent.removeChild(this.container); + + // clean references + this.container = null; + this.loader = null; + this.navbar = null; + this.hud = null; + this.panel = null; + this.tooltip = null; + this.canvas_container = null; + this.renderer = null; + this.scene = null; + this.camera = null; + this.mesh = null; + this.raycaster = null; + this.actions = {}; +}; + /** * Starts to load the panorama * @return (void) */ PhotoSphereViewer.prototype.load = function() { - this.container.classList.add('psv-container', 'loading'); + this.container.classList.add('loading'); // Is canvas supported? if (!PSVUtils.isCanvasSupported()) { @@ -221,12 +295,12 @@ PhotoSphereViewer.prototype._loadXMP = function() { } var pano_data = { - full_width: parseInt(PSVUtils.getAttribute(data, 'FullPanoWidthPixels')), - full_height: parseInt(PSVUtils.getAttribute(data, 'FullPanoHeightPixels')), - cropped_width: parseInt(PSVUtils.getAttribute(data, 'CroppedAreaImageWidthPixels')), - cropped_height: parseInt(PSVUtils.getAttribute(data, 'CroppedAreaImageHeightPixels')), - cropped_x: parseInt(PSVUtils.getAttribute(data, 'CroppedAreaLeftPixels')), - cropped_y: parseInt(PSVUtils.getAttribute(data, 'CroppedAreaTopPixels')), + full_width: parseInt(PSVUtils.getXMPValue(data, 'FullPanoWidthPixels')), + full_height: parseInt(PSVUtils.getXMPValue(data, 'FullPanoHeightPixels')), + cropped_width: parseInt(PSVUtils.getXMPValue(data, 'CroppedAreaImageWidthPixels')), + cropped_height: parseInt(PSVUtils.getXMPValue(data, 'CroppedAreaImageHeightPixels')), + cropped_x: parseInt(PSVUtils.getXMPValue(data, 'CroppedAreaLeftPixels')), + cropped_y: parseInt(PSVUtils.getXMPValue(data, 'CroppedAreaTopPixels')) }; self._loadTexture(pano_data, true); @@ -285,7 +359,7 @@ PhotoSphereViewer.prototype._loadTexture = function(pano_data, in_cache) { cropped_width: img.width, cropped_height: img.height, cropped_x: 0, - cropped_y: 0, + cropped_y: 0 }; } @@ -315,7 +389,7 @@ PhotoSphereViewer.prototype._loadTexture = function(pano_data, in_cache) { var ctx = buffer.getContext('2d'); ctx.drawImage(img, pano_data.cropped_x, pano_data.cropped_y, pano_data.cropped_width, pano_data.cropped_height); - + self.prop.size.image_width = pano_data.cropped_width; self.prop.size.image_height = pano_data.cropped_height; @@ -346,7 +420,7 @@ PhotoSphereViewer.prototype._loadTexture = function(pano_data, in_cache) { */ PhotoSphereViewer.prototype._createScene = function(img) { this._onResize(); - + this.raycaster = new THREE.Raycaster(); // Renderer depends on whether WebGL is supported or not @@ -362,18 +436,18 @@ PhotoSphereViewer.prototype._createScene = function(img) { var texture = new THREE.Texture(img); texture.needsUpdate = true; - // default texture origin is at 1/4 (phiStart=0) of the panorama, I set it at 1/2 (phiStart=PI/2) + // The middle of the panorama is placed at longitude=0 var geometry = new THREE.SphereGeometry(200, 32, 32, -PhotoSphereViewer.HalfPI); - var material = new THREE.MeshBasicMaterial({map: texture, overdraw: true}); + var material = new THREE.MeshBasicMaterial({ map: texture, overdraw: true }); material.side = THREE.DoubleSide; - var mesh = new THREE.Mesh(geometry, material); - mesh.scale.x = -1; + this.mesh = new THREE.Mesh(geometry, material); + this.mesh.scale.x = -1; - this.scene.add(mesh); + this.scene.add(this.mesh); this.canvas_container.appendChild(this.renderer.domElement); // Remove loader - this.container.removeChild(this.loader.container); + this.loader.destroy(); this.loader = null; this.container.classList.remove('loading'); @@ -381,23 +455,19 @@ PhotoSphereViewer.prototype._createScene = function(img) { if (this.config.navbar) { this.container.classList.add('has-navbar'); this.navbar = new PSVNavBar(this); - this.container.appendChild(this.navbar.container); } - + // HUD this.hud = new PSVHUD(this); this.config.markers.forEach(function(marker) { - this.hud.addMarker(marker, true); + this.hud.addMarker(marker, false); }, this); - this.container.appendChild(this.hud.container); - + // Panel this.panel = new PSVPanel(this); - this.container.appendChild(this.panel.container); - + // Tooltip this.tooltip = new PSVTooltip(this); - this.container.appendChild(this.tooltip.container); // Queue animation if (this.config.time_anim !== false) { @@ -414,22 +484,42 @@ PhotoSphereViewer.prototype._createScene = function(img) { * @return (void) */ PhotoSphereViewer.prototype._bindEvents = function() { - window.addEventListener('resize', this._onResize.bind(this)); - document.addEventListener(PSVUtils.fullscreenEvent(), this._fullscreenToggled.bind(this)); - + window.addEventListener('resize', this); + document.addEventListener(PSVUtils.fullscreenEvent(), this); + // all interation events are binded to the HUD only if (this.config.mousemove) { this.hud.container.style.cursor = 'move'; - this.hud.container.addEventListener('mousedown', this._onMouseDown.bind(this)); - this.hud.container.addEventListener('touchstart', this._onTouchStart.bind(this)); - this.hud.container.addEventListener('mouseup', this._onMouseUp.bind(this)); - this.hud.container.addEventListener('touchend', this._onTouchEnd.bind(this)); - this.hud.container.addEventListener('mousemove', this._onMouseMove.bind(this)); - this.hud.container.addEventListener('touchmove', this._onTouchMove.bind(this)); - } - + this.hud.container.addEventListener('mousedown', this); + this.hud.container.addEventListener('touchstart', this); + window.addEventListener('mouseup', this); + window.addEventListener('touchend', this); + this.hud.container.addEventListener('mousemove', this); + this.hud.container.addEventListener('touchmove', this); + } + if (this.config.mousewheel) { - this.hud.container.addEventListener(PSVUtils.mouseWheelEvent(), this._onMouseWheel.bind(this)); + this.hud.container.addEventListener(PSVUtils.mouseWheelEvent(), this); + } +}; + +/** + * Handle events + * @param e (Event) + */ +PhotoSphereViewer.prototype.handleEvent = function(e) { + switch (e.type) { + // @formatter:off + case 'resize': this._onResize(); break; + case 'mousedown': this._onMouseDown(e); break; + case 'touchstart': this._onTouchStart(e); break; + case 'mouseup': this._onMouseUp(e); break; + case 'touchend': this._onTouchEnd(e); break; + case 'mousemove': this._onMouseMove(e); break; + case 'touchmove': this._onTouchMove(e); break; + case PSVUtils.fullscreenEvent(): this._fullscreenToggled(); break; + case PSVUtils.mouseWheelEvent(): this._onMouseWheel(e); break; + // @formatter:on } }; @@ -470,9 +560,9 @@ PhotoSphereViewer.prototype._autorotate = function() { PhotoSphereViewer.prototype.startAutorotate = function() { clearTimeout(this.prop.start_timeout); this.prop.start_timeout = null; - + this.stopAnimation(); - + this._autorotate(); this.trigger('autorotate', true); }; @@ -520,7 +610,7 @@ PhotoSphereViewer.prototype._onResize = function() { * @param height (integer) The new canvas height * @return (void) */ -PhotoSphereViewer.prototype.resize = function (width, height) { +PhotoSphereViewer.prototype.resize = function(width, height) { this.prop.size.width = parseInt(width); this.prop.size.height = parseInt(height); this.prop.size.ratio = this.prop.size.width / this.prop.size.height; @@ -584,11 +674,11 @@ PhotoSphereViewer.prototype._startMove = function(evt) { */ PhotoSphereViewer.prototype._startZoom = function(evt) { var t = [ - {x: parseInt(evt.touches[0].clientX), y: parseInt(evt.touches[0].clientY)}, - {x: parseInt(evt.touches[1].clientX), y: parseInt(evt.touches[1].clientY)} + { x: parseInt(evt.touches[0].clientX), y: parseInt(evt.touches[0].clientY) }, + { x: parseInt(evt.touches[1].clientX), y: parseInt(evt.touches[1].clientY) } ]; - - this.prop.pinch_dist = Math.sqrt(Math.pow(t[0].x-t[1].x, 2) + Math.pow(t[0].y-t[1].y, 2)); + + this.prop.pinch_dist = Math.sqrt(Math.pow(t[0].x - t[1].x, 2) + Math.pow(t[0].y - t[1].y, 2)); this.prop.moving = false; this.prop.zooming = true; @@ -628,7 +718,7 @@ PhotoSphereViewer.prototype._stopMove = function(evt) { this.prop.moved = true; } } - + this.prop.moving = false; this.prop.zooming = false; }; @@ -643,9 +733,9 @@ PhotoSphereViewer.prototype._click = function(evt) { if (evt.defaultPrevented) { return; } - + var boundingRect = this.container.getBoundingClientRect(); - + var data = { client_x: parseInt(evt.clientX - boundingRect.left), client_y: parseInt(evt.clientY - boundingRect.top) @@ -653,27 +743,27 @@ PhotoSphereViewer.prototype._click = function(evt) { var screen = new THREE.Vector2( 2 * data.client_x / this.prop.size.width - 1, - - 2 * data.client_y / this.prop.size.height + 1 + -2 * data.client_y / this.prop.size.height + 1 ); - + this.raycaster.setFromCamera(screen, this.camera); - + var intersects = this.raycaster.intersectObjects(this.scene.children); - - if (intersects.length === 1) { + + if (intersects.length === 1) { var p = intersects[0].point; - var phi = Math.acos(p.y / Math.sqrt(p.x*p.x + p.y*p.y + p.z*p.z)); + var phi = Math.acos(p.y / Math.sqrt(p.x * p.x + p.y * p.y + p.z * p.z)); var theta = Math.atan2(p.x, p.z); - - data.longitude = theta < 0 ? - theta : PhotoSphereViewer.TwoPI - theta; + + data.longitude = theta < 0 ? -theta : PhotoSphereViewer.TwoPI - theta; data.latitude = PhotoSphereViewer.HalfPI - phi; - + var relativeLong = data.longitude / PhotoSphereViewer.TwoPI * this.prop.size.image_width; var relativeLat = data.latitude / PhotoSphereViewer.PI * this.prop.size.image_height; - data.texture_x = parseInt(data.longitude < PhotoSphereViewer.PI ? relativeLong + this.prop.size.image_width/2 : relativeLong - this.prop.size.image_width/2); - data.texture_y = parseInt(this.prop.size.image_height/2 - relativeLat); - + data.texture_x = parseInt(data.longitude < PhotoSphereViewer.PI ? relativeLong + this.prop.size.image_width / 2 : relativeLong - this.prop.size.image_width / 2); + data.texture_y = parseInt(this.prop.size.image_height / 2 - relativeLat); + this.trigger('click', data); } }; @@ -713,7 +803,7 @@ PhotoSphereViewer.prototype._move = function(evt) { if (this.prop.moving) { var x = parseInt(evt.clientX); var y = parseInt(evt.clientY); - + this.rotate( this.prop.longitude - (x - this.prop.mouse_x) * this.config.long_offset, this.prop.latitude + (y - this.prop.mouse_y) * this.config.lat_offset @@ -732,13 +822,13 @@ PhotoSphereViewer.prototype._move = function(evt) { PhotoSphereViewer.prototype._zoom = function(evt) { if (this.prop.zooming) { var t = [ - {x: parseInt(evt.touches[0].clientX), y: parseInt(evt.touches[0].clientY)}, - {x: parseInt(evt.touches[1].clientX), y: parseInt(evt.touches[1].clientY)} + { x: parseInt(evt.touches[0].clientX), y: parseInt(evt.touches[0].clientY) }, + { x: parseInt(evt.touches[1].clientX), y: parseInt(evt.touches[1].clientY) } ]; - - var p = Math.sqrt(Math.pow(t[0].x-t[1].x, 2) + Math.pow(t[0].y-t[1].y, 2)); + + var p = Math.sqrt(Math.pow(t[0].x - t[1].x, 2) + Math.pow(t[0].y - t[1].y, 2)); var delta = 80 * (p - this.prop.pinch_dist) / this.prop.size.width; - + this.zoom(this.prop.zoom_lvl + delta); this.prop.pinch_dist = p; @@ -774,13 +864,13 @@ PhotoSphereViewer.prototype.animate = function(t, p, s) { this.rotate(t, p); return; } - + t = t - Math.floor(t / PhotoSphereViewer.TwoPI) * PhotoSphereViewer.TwoPI; p = PSVUtils.stayBetween(p, this.config.tilt_down_max, this.config.tilt_up_max); var t0 = this.prop.longitude; var p0 = this.prop.latitude; - + // get duration of animation var duration; if (s && typeof s === 'number') { @@ -788,31 +878,31 @@ PhotoSphereViewer.prototype.animate = function(t, p, s) { } else { // desired radial speed - var speed = s ? this.parseAnimSpeed(s) : this.prop.anim_speed; + var speed = s ? this._parseAnimSpeed(s) : this.prop.anim_speed; // get the angle between current position and target - var angle = Math.acos(Math.cos(p0) * Math.cos(p) * Math.cos(t0-t) + Math.sin(p0) * Math.sin(p)); + var angle = Math.acos(Math.cos(p0) * Math.cos(p) * Math.cos(t0 - t) + Math.sin(p0) * Math.sin(p)); duration = angle / speed; } - + var steps = duration * this.prop.fps; - + // longitude offset for shortest arc var tCandidates = [ t - t0, // direct PhotoSphereViewer.TwoPI - t0 + t, // clock-wise cross zero t - t0 - PhotoSphereViewer.TwoPI // counter-clock-wise cross zero ]; - + var tOffset = tCandidates.reduce(function(value, candidate) { return Math.abs(candidate) < Math.abs(value) ? candidate : value; }, Infinity); - + // latitude offset var pOffset = p - p0; - + this.stopAutorotate(); this.stopAnimation(); - + this._animate(tOffset / steps, pOffset / steps, t, p); }; @@ -833,12 +923,12 @@ PhotoSphereViewer.prototype._animate = function(tStep, pStep, tTarget, pTarget) pStep = 0; this.prop.latitude = pTarget; } - + this.rotate( this.prop.longitude + tStep, this.prop.latitude + pStep ); - + if (tStep !== 0 || pStep !== 0) { this.prop.animation_timeout = setTimeout(this._animate.bind(this, tStep, pStep, tTarget, pTarget), 1000 / this.prop.fps); } @@ -865,7 +955,7 @@ PhotoSphereViewer.prototype._onMouseWheel = function(evt) { evt.preventDefault(); evt.stopPropagation(); - var delta = evt.deltaY!==undefined ? -evt.deltaY : (evt.wheelDelta!==undefined ? evt.wheelDelta : -evt.detail); + var delta = evt.deltaY !== undefined ? -evt.deltaY : (evt.wheelDelta !== undefined ? evt.wheelDelta : -evt.detail); if (delta !== 0) { var direction = parseInt(delta / Math.abs(delta)); @@ -934,7 +1024,7 @@ PhotoSphereViewer.prototype.toggleFullscreen = function() { * @param speed (string) The speed, in radians/degrees/revolutions per second/minute * @return (double) radians per second */ -PhotoSphereViewer.prototype.parseAnimSpeed = function(speed) { +PhotoSphereViewer.prototype._parseAnimSpeed = function(speed) { speed = speed.toString().trim(); // Speed extraction @@ -976,7 +1066,7 @@ PhotoSphereViewer.prototype.parseAnimSpeed = function(speed) { default: throw new PSVError('unknown speed unit "' + speed_unit + '"'); } - + return rad_per_second; }; @@ -986,21 +1076,52 @@ PhotoSphereViewer.prototype.parseAnimSpeed = function(speed) { * @return (void) */ PhotoSphereViewer.prototype.setAnimSpeed = function(speed) { - this.prop.anim_speed = this.parseAnimSpeed(speed); + this.prop.anim_speed = this._parseAnimSpeed(speed); +}; + +/** + * Sets the viewer size + * @param size (Object) An object containing the wanted width and height + * @return (void) + */ +PhotoSphereViewer.prototype._setViewerSize = function(size) { + ['width', 'height'].forEach(function(dim) { + if (size[dim]) { + if (/^[0-9.]+$/.test(size[dim])) size[dim] += 'px'; + this.parent.style[dim] = size[dim]; + } + }, this); }; /** - * Adds an action + * Adds an event listener + * If "func" is an object, its "handleEvent" method will be called with an object as paremeter + * - type: name of the event prefixed with "psv:" + * - args: array of action arguments * @param name (string) Action name - * @param f (Function) The handler function + * @param func (Function|Object) The handler function, or an object with an "handleEvent" method * @return (void) */ -PhotoSphereViewer.prototype.on = function(name, f) { +PhotoSphereViewer.prototype.on = function(name, func) { if (!(name in this.actions)) { this.actions[name] = []; } - this.actions[name].push(f); + this.actions[name].push(func); +}; + +/** + * Removes an event listener + * @param name (string) Action name + * @param func (Function|Object) + */ +PhotoSphereViewer.prototype.off = function(name, func) { + if (name in this.actions) { + var idx = this.actions[name].indexOf(func); + if (idx !== -1) { + this.actions[name].splice(idx, 1); + } + } }; /** @@ -1012,19 +1133,29 @@ PhotoSphereViewer.prototype.on = function(name, f) { PhotoSphereViewer.prototype.trigger = function(name, args) { args = Array.prototype.slice.call(arguments, 1); if ((name in this.actions) && this.actions[name].length > 0) { - for (var i = 0, l = this.actions[name].length; i < l; ++i) { - this.actions[name][i].apply(this, args); - } + this.actions[name].forEach(function(func) { + if (typeof func === 'object') { + func.handleEvent({ + type: 'psv:' + name, + args: args + }); + } + else { + func.apply(this, args); + } + }, this); } }; + /** - * Base sub component class + * Base sub-component class * @param psv (PhotoSphereViewer) A PhotoSphereViewer object */ function PSVComponent(psv) { - this.psv = psv; - + this.psv = psv; + this.container = null; + // expose some methods to the viewer if (this.constructor.publicMethods) { this.constructor.publicMethods.forEach(function(method) { @@ -1033,6 +1164,26 @@ function PSVComponent(psv) { } } +/** + * Creates the component + */ +PSVComponent.prototype.create = function() { + this.container = document.createElement('div'); + + this.psv.container.appendChild(this.container); +}; + +/** + * Destroys the component + */ +PSVComponent.prototype.destroy = function() { + this.psv.container.removeChild(this.container); + + this.container = null; + this.psv = null; +}; + + /** * Loader class * @param psv (PhotoSphereViewer) A PhotoSphereViewer object @@ -1041,7 +1192,7 @@ function PSVLoader(psv) { this.psv = psv; this.container = null; this.canvas = null; - + this.create(); } @@ -1051,12 +1202,12 @@ function PSVLoader(psv) { PSVLoader.prototype.create = function() { this.container = document.createElement('div'); this.container.className = 'psv-loader'; - + this.psv.container.appendChild(this.container); this.canvas = document.createElement('canvas'); this.canvas.className = 'loader-canvas'; - + this.canvas.width = this.container.clientWidth; this.canvas.height = this.container.clientWidth; this.container.appendChild(this.canvas); @@ -1075,13 +1226,23 @@ PSVLoader.prototype.create = function() { inner.innerHTML = this.psv.config.loading_txt; } if (inner) { - var a = Math.round(Math.sqrt(2 * Math.pow(this.canvas.width/2-this.tickness/2, 2))); + var a = Math.round(Math.sqrt(2 * Math.pow(this.canvas.width / 2 - this.tickness / 2, 2))); inner.style.maxWidth = a + 'px'; inner.style.maxHeight = a + 'px'; this.container.appendChild(inner); } }; +/** + * Destroys the loader + */ +PSVLoader.prototype.destroy = function() { + this.psv.container.removeChild(this.container); + + this.psv = null; + this.container = null; +}; + /** * Sets the loader progression * @param value (int) from 0 to 100 @@ -1096,164 +1257,246 @@ PSVLoader.prototype.setProgress = function(value) { context.beginPath(); context.arc( - this.canvas.width/2, this.canvas.height/2, - this.canvas.width/2 - this.tickness/2, - -Math.PI/2, value/100 * 2*Math.PI - Math.PI/2 + this.canvas.width / 2, this.canvas.height / 2, + this.canvas.width / 2 - this.tickness / 2, + -Math.PI / 2, value / 100 * 2 * Math.PI - Math.PI / 2 ); context.stroke(); }; + /** * HUD class * @param psv (PhotoSphereViewer) A PhotoSphereViewer object */ function PSVHUD(psv) { PSVComponent.call(this, psv); - - this.container = null; + this.markers = {}; this.currentMarker = null; - + this.create(); } PSVHUD.prototype = Object.create(PSVComponent.prototype); PSVHUD.prototype.constructor = PSVHUD; -PSVHUD.publicMethods = ['addMarker', 'removeMarker', 'getMarker', 'getCurrentMarker', 'gotoMarker', 'hideMarker', 'showMarker', 'toggleMarker']; +PSVHUD.publicMethods = ['addMarker', 'removeMarker', 'updateMarker', 'getMarker', 'getCurrentMarker', 'gotoMarker', 'hideMarker', 'showMarker', 'toggleMarker']; /** - * Creates the elements + * Creates the HUD * @return (void) */ PSVHUD.prototype.create = function() { - this.container = document.createElement('div'); + PSVComponent.prototype.create.call(this); + this.container.className = 'psv-hud'; - + // Markers events via delegation - this.container.addEventListener('mouseenter', this._onMouseEnter.bind(this), true); - this.container.addEventListener('mouseleave', this._onMouseLeave.bind(this), true); - - this.psv.on('_click', this._onClick.bind(this), true); - - this.psv.on('render', this.updatePositions.bind(this)); + this.container.addEventListener('mouseenter', this, true); + this.container.addEventListener('mouseleave', this, true); + + // Viewer events + this.psv.on('_click', this); + this.psv.on('render', this); +}; + +/** + * Destroys the HUD + */ +PSVHUD.prototype.destroy = function() { + this.container.removeEventListener('mouseenter', this); + this.container.removeEventListener('mouseleave', this); + + this.psv.off('_click', this); + this.psv.off('render', this); + + PSVComponent.prototype.destroy.call(this); +}; + +/** + * Handle events + * @param e (Event) + */ +PSVHUD.prototype.handleEvent = function(e) { + switch (e.type) { + // @formatter:off + case 'mouseenter': this._onMouseEnter(e); break; + case 'mouseleave': this._onMouseLeave(e); break; + case 'psv:_click': this._onClick(e.args[0]); break; + case 'psv:render': this.updatePositions(); break; + // @formatter:on + } }; /** * Add a new marker to HUD * @param marker (Object) - * @param noRender (Boolean) disable immediate render + * @param render (Boolean) "false" to disable immediate render * @return (Object) a modified marker object */ -PSVHUD.prototype.addMarker = function(marker, noRender) { +PSVHUD.prototype.addMarker = function(marker, render) { if (!marker.id) { throw new PSVError('missing marker id'); } - + if (this.markers[marker.id]) { throw new PSVError('marker "' + marker.id + '" already exists'); } - - if (!marker.width || !marker.height) { - throw new PSVError('missing marker width/height'); + + if (!marker.image && !marker.html) { + throw new PSVError('missing marker image/html'); } - - if (!marker.image) { - throw new PSVError('missing marker image'); + + if (marker.image && (!marker.width || !marker.height)) { + throw new PSVError('missing marker width/height'); } - - if ((!marker.hasOwnProperty('x') || !marker.hasOwnProperty('y')) & (!marker.hasOwnProperty('latitude') || !marker.hasOwnProperty('longitude'))) { + + if ((!marker.hasOwnProperty('x') || !marker.hasOwnProperty('y')) && (!marker.hasOwnProperty('latitude') || !marker.hasOwnProperty('longitude'))) { throw new PSVError('missing marker position, latitude/longitude or x/y'); } - marker = PSVUtils.clone(marker); - // create DOM marker.$el = document.createElement('div'); - marker.$el.psvMarker = marker; - marker.$el.className = 'marker'; - + marker.$el.id = 'psv-marker-' + marker.id; + marker.$el.className = 'psv-marker'; + + this.markers[marker.id] = marker; // will be replaced by updateMarker + this.container.appendChild(marker.$el); + + return this.updateMarker(PSVUtils.clone(marker), render); + +}; + +/** + * Get a marker by it's id or external object + * @param marker (Mixed) + * @return (Object) + */ +PSVHUD.prototype.getMarker = function(marker) { + var id = typeof marker === 'object' ? marker.id : marker; + + if (!this.markers[id]) { + throw new PSVError('cannot find marker "' + id + '"'); + } + + return this.markers[id]; +}; + +/** + * Get the current selected marker + * @return (Object) + */ +PSVHUD.prototype.getCurrentMarker = function() { + return this.currentMarker; +}; + +/** + * Update a marker + * @param marker (Object) + * @param render (Boolean) "false" to disable immediate render + * @return (Object) a modified marker object + */ +PSVHUD.prototype.updateMarker = function(marker, render) { + var old = this.getMarker(marker); + + // clean some previous data + if (old.className) { + old.$el.classList.remove(old.className); + } + if (old.tooltip) { + old.$el.classList.remove('has-tooltip'); + } + + // merge objects + delete marker.$el; + marker = PSVUtils.deepmerge(old, marker); + + marker.position2D = null; + + // add classes if (marker.className) { marker.$el.classList.add(marker.className); } if (marker.tooltip) { marker.$el.classList.add('has-tooltip'); + if (typeof marker.tooltip === 'string') { + marker.tooltip = { content: marker.tooltip }; + } } - + // set image var style = marker.$el.style; - style.width = marker.width + 'px'; - style.height = marker.height + 'px'; - style.backgroundImage = 'url(' + marker.image + ')'; - + + if (marker.width && marker.height) { + style.width = marker.width + 'px'; + style.height = marker.height + 'px'; + marker.dynamicSize = false; + } + else { + marker.dynamicSize = true; + } + + if (marker.style) { + Object.getOwnPropertyNames(marker.style).forEach(function(prop) { + style[prop] = marker.style[prop]; + }); + } + + if (marker.image) { + style.backgroundImage = 'url(' + marker.image + ')'; + } + else { + marker.$el.innerHTML = marker.html; + } + // parse anchor marker.anchor = PSVUtils.parsePosition(marker.anchor); - + // convert texture coordinates to spherical coordinates if (marker.hasOwnProperty('x') && marker.hasOwnProperty('y')) { var relativeX = marker.x / this.psv.prop.size.image_width * PhotoSphereViewer.TwoPI; var relativeY = marker.y / this.psv.prop.size.image_height * PhotoSphereViewer.PI; - + marker.longitude = relativeX >= PhotoSphereViewer.PI ? relativeX - PhotoSphereViewer.PI : relativeX + PhotoSphereViewer.PI; marker.latitude = PhotoSphereViewer.HalfPI - relativeY; } - + // compute x/y/z position marker.position3D = new THREE.Vector3( -Math.cos(marker.latitude) * Math.sin(marker.longitude), Math.sin(marker.latitude), Math.cos(marker.latitude) * Math.cos(marker.longitude) ); - + if (!marker.hasOwnProperty('visible')) { marker.visible = true; } - + // save + marker.$el.psvMarker = marker; this.markers[marker.id] = marker; - this.container.appendChild(marker.$el); - - if (!noRender) { - this.updatePositions(); - } - - return marker; -}; -/** - * Get a marker by it's id or external object - * @param marker (Mixed) - * @return (Object) - */ -PSVHUD.prototype.getMarker = function(marker) { - var id = typeof marker === 'object' ? marker.id : marker; - - if (!this.markers[id]) { - throw new PSVError('cannot find marker "' + id + '"'); + if (render !== false) { + this.updatePositions(); } - - return this.markers[id]; -}; -/** - * Get the current selected marker - * @return (Object) - */ -PSVHUD.prototype.getCurrentMarker = function() { - return this.currentMarker; + return marker; }; /** * Remove a marker * @param marker (Mixed) - * @param noRender (Boolean) + * @param render (Boolean) "false" to disable immediate render * @return (void) */ -PSVHUD.prototype.removeMarker = function(marker, noRender) { +PSVHUD.prototype.removeMarker = function(marker, render) { marker = this.getMarker(marker); + + marker.$el.parentNode.removeChild(marker.$el); delete this.markers[marker.id]; - - if (!noRender) { + + if (render !== false) { this.updatePositions(); } }; @@ -1295,7 +1538,7 @@ PSVHUD.prototype.showMarker = function(marker) { * @return (void) */ PSVHUD.prototype.toggleMarker = function(marker) { - this.getMarker(marker).visible^= true; + this.getMarker(marker).visible ^= true; this.updatePositions(); }; @@ -1309,15 +1552,15 @@ PSVHUD.prototype.updatePositions = function() { for (var id in this.markers) { var marker = this.markers[id]; var position = this._getMarkerPosition(marker); - + if (this._isMarkerVisible(marker, position)) { marker.position2D = position; - - marker.$el.style.transform = 'translate3D(' + - position.left + 'px, ' + + + marker.$el.style.transform = 'translate3D(' + + position.left + 'px, ' + position.top + 'px, ' + '0px)'; - + if (!marker.$el.classList.contains('visible')) { marker.$el.classList.add('visible'); } @@ -1339,10 +1582,10 @@ PSVHUD.prototype.updatePositions = function() { PSVHUD.prototype._isMarkerVisible = function(marker, position) { return marker.visible && marker.position3D.dot(this.psv.prop.direction) > 0 && - position.left >= 0 && - position.left + marker.width <= this.psv.prop.size.width && - position.top >= 0 && - position.top + marker.height <= this.psv.prop.size.height; + position.left + marker.width >= 0 && + position.left - marker.width <= this.psv.prop.size.width && + position.top + marker.height >= 0 && + position.top - marker.height <= this.psv.prop.size.height; }; /** @@ -1351,6 +1594,16 @@ PSVHUD.prototype._isMarkerVisible = function(marker, position) { * @return (Object) top and left position */ PSVHUD.prototype._getMarkerPosition = function(marker) { + if (marker.dynamicSize) { + // make the marker visible to get it's size + marker.$el.classList.add('transparent'); + var rect = marker.$el.getBoundingClientRect(); + marker.$el.classList.remove('transparent'); + + marker.width = rect.right - rect.left; + marker.height = rect.bottom - rect.top; + } + var vector = marker.position3D.clone(); vector.project(this.psv.camera); @@ -1367,7 +1620,14 @@ PSVHUD.prototype._getMarkerPosition = function(marker) { */ PSVHUD.prototype._onMouseEnter = function(e) { if (e.target && e.target.psvMarker && e.target.psvMarker.tooltip) { - this.psv.tooltip.showTooltip(e.target.psvMarker.tooltip, e.target.psvMarker); + var marker = e.target.psvMarker; + this.psv.tooltip.showTooltip({ + content: marker.tooltip.content, + position: marker.tooltip.position, + top: marker.position2D.top, + left: marker.position2D.left, + marker: marker + }); } }; @@ -1383,24 +1643,25 @@ PSVHUD.prototype._onMouseLeave = function(e) { }; /** - * The mouse button is release : show/hide the panel if threeshold was not reached, or do nothing + * The mouse button is release : show/hide the panel if threshold was not reached, or do nothing * @param e (Event) * @return (void) */ PSVHUD.prototype._onClick = function(e) { if (!this.psv.prop.moved) { - if (e.target && e.target.psvMarker) { - this.currentMarker = e.target.psvMarker; - this.psv.trigger('select-marker', e.target.psvMarker); + var marker; + if (e.target && (marker = PSVUtils.getClosest(e.target, '.psv-marker')) && marker.psvMarker) { + this.currentMarker = marker.psvMarker; + this.psv.trigger('select-marker', marker.psvMarker); e.preventDefault(); // prevent the public "click" event } - else { + else if (this.currentMarker) { this.currentMarker = null; this.psv.trigger('unselect-marker'); } - - if (e.target && e.target.psvMarker && e.target.psvMarker.content) { - this.psv.panel.showPanel(e.target.psvMarker.content); + + if (marker && marker.psvMarker && marker.psvMarker.content) { + this.psv.panel.showPanel(marker.psvMarker.content); } else if (this.psv.panel.prop.opened) { e.preventDefault(); // prevent the public "click" event @@ -1409,6 +1670,7 @@ PSVHUD.prototype._onClick = function(e) { } }; + /*jshint multistr: true */ /** @@ -1417,17 +1679,16 @@ PSVHUD.prototype._onClick = function(e) { */ function PSVPanel(psv) { PSVComponent.call(this, psv); - - this.container = null; + this.content = null; - + this.prop = { mouse_x: 0, mouse_y: 0, mousedown: false, opened: false }; - + this.create(); } @@ -1437,37 +1698,67 @@ PSVPanel.prototype.constructor = PSVPanel; PSVPanel.publicMethods = ['showPanel', 'hidePanel']; /** - * Creates the elements + * Creates the panel * @return (void) */ PSVPanel.prototype.create = function() { - this.container = document.createElement('aside'); + PSVComponent.prototype.create.call(this); + this.container.className = 'psv-panel'; this.container.innerHTML = '\
\
\
'; - + this.content = this.container.querySelector('.content'); - + var closeBtn = this.container.querySelector('.close-button'); closeBtn.addEventListener('click', this.hidePanel.bind(this)); - + // Stop event bubling from panel if (this.psv.config.mousewheel) { this.container.addEventListener(PSVUtils.mouseWheelEvent(), function(e) { e.stopPropagation(); }); } - + // Event for panel resizing + stop bubling var resizer = this.container.querySelector('.resizer'); - resizer.addEventListener('mousedown', this._onMouseDown.bind(this)); - resizer.addEventListener('touchstart', this._onTouchStart.bind(this)); - this.psv.container.addEventListener('mouseup', this._onMouseUp.bind(this)); - this.psv.container.addEventListener('touchend', this._onMouseUp.bind(this)); - this.psv.container.addEventListener('mousemove', this._onMouseMove.bind(this)); - this.psv.container.addEventListener('touchmove', this._onTouchMove.bind(this)); + resizer.addEventListener('mousedown', this); + resizer.addEventListener('touchstart', this); + this.psv.container.addEventListener('mouseup', this); + this.psv.container.addEventListener('touchend', this); + this.psv.container.addEventListener('mousemove', this); + this.psv.container.addEventListener('touchmove', this); +}; + +/** + * Destroys the panel + */ +PSVPanel.prototype.destroy = function() { + this.psv.container.removeEventListener('mousemove', this); + this.psv.container.removeEventListener('touchmove', this); + this.psv.container.removeEventListener('mouseup', this); + this.psv.container.removeEventListener('touchend', this); + + PSVComponent.prototype.destroy.call(this); +}; + +/** + * Handle events + * @param e (Event) + */ +PSVPanel.prototype.handleEvent = function(e) { + switch (e.type) { + // @formatter:off + case 'mousedown': this._onMouseDown(e); break; + case 'touchstart': this._onTouchStart(e); break; + case 'mousemove': this._onMouseMove(e); break; + case 'touchmove': this._onMouseMove(e); break; + case 'mouseup': this._onMouseUp(e); break; + case 'touchend': this._onMouseUp(e); break; + // @formatter:on + } }; /** @@ -1480,7 +1771,7 @@ PSVPanel.prototype.showPanel = function(content, noMargin) { this.content.innerHTML = content; this.content.scrollTop = 0; this.container.classList.add('open'); - + if (noMargin) { if (!this.content.classList.contains('no-margin')) { this.content.classList.add('no-margin'); @@ -1489,7 +1780,7 @@ PSVPanel.prototype.showPanel = function(content, noMargin) { else { this.content.classList.remove('no-margin'); } - + this.prop.opened = true; this.psv.trigger('open-panel'); }; @@ -1582,23 +1873,23 @@ PSVPanel.prototype._onTouchMove = function(evt) { PSVPanel.prototype._resize = function(evt) { var x = parseInt(evt.clientX); var y = parseInt(evt.clientY); - + this.container.style.width = (this.container.offsetWidth - (x - this.prop.mouse_x)) + 'px'; this.prop.mouse_x = x; this.prop.mouse_y = y; }; + /** * Tooltip class * @param psv (PhotoSphereViewer) A PhotoSphereViewer object */ function PSVTooltip(psv) { PSVComponent.call(this, psv); - + this.config = this.psv.config.tooltip; - this.container = null; - + this.create(); } @@ -1607,65 +1898,100 @@ PSVTooltip.prototype.constructor = PSVTooltip; PSVTooltip.publicMethods = ['showTooltip', 'hideTooltip']; -PSVTooltip.leftMap = {0: 'left', 0.5: 'center', 1: 'right'}; -PSVTooltip.topMap = {0: 'top', 0.5: 'center', 1: 'bottom'}; +PSVTooltip.leftMap = { 0: 'left', 0.5: 'center', 1: 'right' }; +PSVTooltip.topMap = { 0: 'top', 0.5: 'center', 1: 'bottom' }; /** - * Creates the elements + * Creates the tooltip * @return (void) */ PSVTooltip.prototype.create = function() { - this.container = document.createElement('div'); + PSVComponent.prototype.create.call(this); + this.container.innerHTML = '
'; this.container.className = 'psv-tooltip'; this.container.style.top = '-1000px'; this.container.style.left = '-1000px'; - - this.psv.on('render', this.hideTooltip.bind(this)); + + this.psv.on('render', this); +}; + +/** + * Destroys the tooltip + */ +PSVTooltip.prototype.destroy = function() { + this.psv.off('render', this); + + PSVComponent.prototype.destroy.call(this); +}; + +/** + * Handle events + * @param e (Event) + */ +PSVTooltip.prototype.handleEvent = function(e) { + switch (e.type) { + // @formatter:off + case 'psv:render': this.hideTooltip(); break; + // @formatter:on + } }; /** * Show the tooltip - * @param tooltip (Mixed) content, className, position - * @param marker (Object) target for positioning: width, height, position2D(top, left) + * @param config (Object) + * - content + * - top + * - left + * - position (default: 'top center') + * - className (optional) + * - marker (optional) -- take marker dimensions in account when positioning the tooltip * @return (void) */ -PSVTooltip.prototype.showTooltip = function(tooltip, marker) { +PSVTooltip.prototype.showTooltip = function(config) { var t = this.container; var c = t.querySelector('.content'); var a = t.querySelector('.arrow'); - - if (typeof tooltip === 'string') { - tooltip = { - content: marker.tooltip, - position: ['top', 'center'] + + if (!config.position) { + config.position = ['top', 'center']; + } + + if (!config.marker) { + config.marker = { + width: 0, + height: 0 }; } - + // parse position - if (typeof tooltip.position === 'string') { - var tempPos = PSVUtils.parsePosition(tooltip.position); - + if (typeof config.position === 'string') { + var tempPos = PSVUtils.parsePosition(config.position); + if (!(tempPos.left in PSVTooltip.leftMap) || !(tempPos.top in PSVTooltip.topMap)) { throw new PSVError('unable to parse tooltip position "' + tooltip.position + '"'); } - - tooltip.position = [PSVTooltip.topMap[tempPos.top], PSVTooltip.leftMap[tempPos.left]]; + + config.position = [PSVTooltip.topMap[tempPos.top], PSVTooltip.leftMap[tempPos.left]]; } - + + if (config.position[0] == 'center' && config.position[1] == 'center') { + throw new PSVError('unable to parse tooltip position "center center"'); + } + t.className = 'psv-tooltip'; // reset the class - if (tooltip.className) { - t.classList.add(tooltip.className); + if (config.className) { + t.classList.add(config.className); } - c.innerHTML = tooltip.content; + c.innerHTML = config.content; t.style.top = '0px'; t.style.left = '0px'; - + // compute size var rect = t.getBoundingClientRect(); var style = { - posClass: tooltip.position.slice(), + posClass: config.position.slice(), width: rect.right - rect.left, height: rect.bottom - rect.top, top: 0, @@ -1673,10 +1999,10 @@ PSVTooltip.prototype.showTooltip = function(tooltip, marker) { arrow_top: 0, arrow_left: 0 }; - + // set initial position - this._computeTooltipPosition(style, marker); - + this._computeTooltipPosition(style, config); + // correct position if overflow var refresh = false; if (style.top < this.config.offset) { @@ -1696,18 +2022,18 @@ PSVTooltip.prototype.showTooltip = function(tooltip, marker) { refresh = true; } if (refresh) { - this._computeTooltipPosition(style, marker); + this._computeTooltipPosition(style, config); } - + // apply position t.style.top = style.top + 'px'; t.style.left = style.left + 'px'; - + a.style.top = style.arrow_top + 'px'; a.style.left = style.arrow_left + 'px'; - + t.classList.add(style.posClass.join('-')); - + // delay for correct transition between the two classes var self = this; setTimeout(function() { @@ -1723,7 +2049,7 @@ PSVTooltip.prototype.showTooltip = function(tooltip, marker) { PSVTooltip.prototype.hideTooltip = function() { this.container.classList.remove('visible'); this.psv.trigger('hide-tooltip'); - + var self = this; setTimeout(function() { self.container.style.top = '-1000px'; @@ -1733,62 +2059,63 @@ PSVTooltip.prototype.hideTooltip = function() { /** * Compute the position of the tooltip and its arrow - * @param style (Object) tooltip style - * @param marker (Object) + * @param style (Object) + * @param config (Object) * @return (void) */ -PSVTooltip.prototype._computeTooltipPosition = function(style, marker) { +PSVTooltip.prototype._computeTooltipPosition = function(style, config) { var topBottom = false; - + switch (style.posClass[0]) { case 'bottom': - style.top = marker.position2D.top + marker.height + this.config.offset + this.config.arrow_size; - style.arrow_top = - this.config.arrow_size * 2; + style.top = config.top + config.marker.height + this.config.offset + this.config.arrow_size; + style.arrow_top = -this.config.arrow_size * 2; topBottom = true; break; - + case 'center': - style.top = marker.position2D.top + marker.height/2 - style.height/2; - style.arrow_top = style.height/2 - this.config.arrow_size; + style.top = config.top + config.marker.height / 2 - style.height / 2; + style.arrow_top = style.height / 2 - this.config.arrow_size; break; - + case 'top': - style.top = marker.position2D.top - style.height - this.config.offset - this.config.arrow_size; + style.top = config.top - style.height - this.config.offset - this.config.arrow_size; style.arrow_top = style.height; topBottom = true; break; } - + switch (style.posClass[1]) { case 'right': if (topBottom) { - style.left = marker.position2D.left; - style.arrow_left = marker.width/2 - this.config.arrow_size; + style.left = config.left; + style.arrow_left = config.marker.width / 2 - this.config.arrow_size; } else { - style.left = marker.position2D.left + marker.width + this.config.offset + this.config.arrow_size; - style.arrow_left = - this.config.arrow_size * 2; + style.left = config.left + config.marker.width + this.config.offset + this.config.arrow_size; + style.arrow_left = -this.config.arrow_size * 2; } break; - + case 'center': - style.left = marker.position2D.left + marker.width/2 - style.width/2; - style.arrow_left = style.width/2 - this.config.arrow_size; + style.left = config.left + config.marker.width / 2 - style.width / 2; + style.arrow_left = style.width / 2 - this.config.arrow_size; break; - + case 'left': if (topBottom) { - style.left = marker.position2D.left - style.width + marker.width; - style.arrow_left = style.width - marker.width/2 - this.config.arrow_size; + style.left = config.left - style.width + config.marker.width; + style.arrow_left = style.width - config.marker.width / 2 - this.config.arrow_size; } else { - style.left = marker.position2D.left - style.width - this.config.offset - this.config.arrow_size; + style.left = config.left - style.width - this.config.offset - this.config.arrow_size; style.arrow_left = style.width; } break; } }; + /** * Navigation bar class * @param psv (PhotoSphereViewer) A PhotoSphereViewer object @@ -1797,18 +2124,18 @@ function PSVNavBar(psv) { PSVComponent.call(this, psv); this.config = this.psv.config.navbar; - this.container = null; this.caption = null; + this.buttons = []; if (this.config === true) { this.config = PSVUtils.clone(PSVNavBar.DEFAULTS); } else if (typeof this.config == 'string') { var map = {}; - this.config.split(/[ ,:]/).forEach(function(button) { - map[button] = true; - }); - this.config = PSVUtils.deepmerge(PSVNavBar.DEFAULTS, map); + Object.keys(PSVNavBar.DEFAULTS).forEach(function(button) { + map[button] = this.config.indexOf(button) !== -1; + }, this); + this.config = map; } this.create(); @@ -1828,42 +2155,37 @@ PSVNavBar.DEFAULTS = { }; /** - * Creates the elements + * Creates the navbar * @return (void) */ PSVNavBar.prototype.create = function() { - // Container - this.container = document.createElement('div'); + PSVComponent.prototype.create.call(this); + this.container.className = 'psv-navbar'; // Autorotate button if (this.config.autorotate) { - var autorotateBtn = new PSVNavBarAutorotateButton(this.psv); - this.container.appendChild(autorotateBtn.button); + this.buttons.push(new PSVNavBarAutorotateButton(this)); } // Zoom buttons if (this.config.zoom) { - var zoomBar = new PSVNavBarZoomButton(this.psv); - this.container.appendChild(zoomBar.button); + this.buttons.push(new PSVNavBarZoomButton(this)); } // Download button if (this.config.download) { - var downloadBtn = new PSVNavBarDownloadButton(this.psv); - this.container.appendChild(downloadBtn.button); + this.buttons.push(new PSVNavBarDownloadButton(this)); } // Markers button if (this.config.markers) { - var markersBtn = new PSVNavBarMarkersButton(this.psv); - this.container.appendChild(markersBtn.button); + this.buttons.push(new PSVNavBarMarkersButton(this)); } // Fullscreen button if (this.config.fullscreen) { - var fullscreenBtn = new PSVNavBarFullscreenButton(this.psv); - this.container.appendChild(fullscreenBtn.button); + this.buttons.push(new PSVNavBarFullscreenButton(this)); } // Caption @@ -1873,6 +2195,19 @@ PSVNavBar.prototype.create = function() { this.setCaption(this.psv.config.caption); }; +/** + * Destroys the navbar + */ +PSVNavBar.prototype.destroy = function() { + this.buttons.forEach(function(button) { + button.destroy(); + }); + + this.buttons.length = 0; + + PSVComponent.prototype.destroy.call(this); +}; + /** * Sets the bar caption * @param (string) html @@ -1887,12 +2222,14 @@ PSVNavBar.prototype.setCaption = function(html) { } }; + /** * Navigation bar button class - * @param psv (PhotoSphereViewer) A PhotoSphereViewer object + * @param navbar (PSVNavBar) A PSVNavBar object */ -function PSVNavBarButton(psv) { - this.psv = psv; +function PSVNavBarButton(navbar) { + this.navbar = navbar; + this.psv = navbar.psv; this.button = null; } @@ -1901,7 +2238,20 @@ function PSVNavBarButton(psv) { * @return (void) */ PSVNavBarButton.prototype.create = function() { - throw new PSVError('Not implemented'); + this.button = document.createElement('div'); + this.button.className = 'psv-button'; + this.navbar.container.appendChild(this.button); +}; + +/** + * Destroys the button + */ +PSVNavBarButton.prototype.destroy = function() { + this.navbar.container.removeChild(this.button); + + this.navbar = null; + this.psv = null; + this.button = null; }; /** @@ -1918,13 +2268,14 @@ PSVNavBarButton.prototype.toggleActive = function(active) { } }; + /** * Navigation bar autorotate button class * @param psv (PhotoSphereViewer) A PhotoSphereViewer object */ function PSVNavBarAutorotateButton(psv) { PSVNavBarButton.call(this, psv); - + this.create(); } @@ -1936,8 +2287,9 @@ PSVNavBarAutorotateButton.prototype.constructor = PSVNavBarAutorotateButton; * @return (void) */ PSVNavBarAutorotateButton.prototype.create = function() { - this.button = document.createElement('div'); - this.button.className = 'psv-button autorotate-button'; + PSVNavBarButton.prototype.create.call(this); + + this.button.classList.add('autorotate-button'); this.button.title = this.psv.config.lang.autorotate; var autorotate_sphere = document.createElement('div'); @@ -1949,17 +2301,18 @@ PSVNavBarAutorotateButton.prototype.create = function() { this.button.appendChild(autorotate_equator); this.button.addEventListener('click', this.psv.toggleAutorotate.bind(this.psv)); - + this.psv.on('autorotate', this.toggleActive.bind(this)); }; + /** * Navigation bar fullscreen button class * @param psv (PhotoSphereViewer) A PhotoSphereViewer object */ function PSVNavBarFullscreenButton(psv) { PSVNavBarButton.call(this, psv); - + this.create(); } @@ -1971,18 +2324,41 @@ PSVNavBarFullscreenButton.prototype.constructor = PSVNavBarFullscreenButton; * @return (void) */ PSVNavBarFullscreenButton.prototype.create = function() { - this.button = document.createElement('div'); - this.button.className = 'psv-button fullscreen-button'; + PSVNavBarButton.prototype.create.call(this); + + this.button.classList.add('fullscreen-button'); this.button.title = this.psv.config.lang.fullscreen; this.button.appendChild(document.createElement('div')); this.button.appendChild(document.createElement('div')); this.button.addEventListener('click', this.psv.toggleFullscreen.bind(this.psv)); - - this.psv.on('fullscreen-updated', this.toggleActive.bind(this)); + + this.psv.on('fullscreen-updated', this); +}; + +/** + * Destroys the button + */ +PSVNavBarFullscreenButton.prototype.destroy = function() { + this.psv.off('fullscreen-updated', this); + + PSVNavBarButton.prototype.destroy.call(this); +}; + +/** + * Handle events + * @param e (Event) + */ +PSVNavBarFullscreenButton.prototype.handleEvent = function(e) { + switch (e.type) { + // @formatter:off + case 'psv:fullscreen-updated': this.toggleActive(); break; + // @formatter:on + } }; + /** * Navigation bar zoom button class * @param psv (PhotoSphereViewer) A PhotoSphereViewer object @@ -1992,7 +2368,7 @@ function PSVNavBarZoomButton(psv) { this.zoom_range = null; this.zoom_value = null; - + this.prop = { mousedown: false }; @@ -2008,8 +2384,9 @@ PSVNavBarZoomButton.prototype.constructor = PSVNavBarZoomButton; * @return (void) */ PSVNavBarZoomButton.prototype.create = function() { - this.button = document.createElement('div'); - this.button.className = 'psv-button zoom-button'; + PSVNavBarButton.prototype.create.call(this); + + this.button.classList.add('zoom-button'); var zoom_minus = document.createElement('div'); zoom_minus.className = 'minus'; @@ -2037,16 +2414,16 @@ PSVNavBarZoomButton.prototype.create = function() { zoom_plus.innerHTML = PhotoSphereViewer.ICONS['zoom-in.svg']; this.button.appendChild(zoom_plus); - this.zoom_range.addEventListener('mousedown', this._initZoomChangeWithMouse.bind(this)); - this.zoom_range.addEventListener('touchstart', this._initZoomChangeByTouch.bind(this)); - this.psv.container.addEventListener('mousemove', this._changeZoomWithMouse.bind(this)); - this.psv.container.addEventListener('touchmove', this._changeZoomByTouch.bind(this)); - this.psv.container.addEventListener('mouseup', this._stopZoomChange.bind(this)); - this.psv.container.addEventListener('touchend', this._stopZoomChange.bind(this)); + this.zoom_range.addEventListener('mousedown', this); + this.zoom_range.addEventListener('touchstart', this); + this.psv.container.addEventListener('mousemove', this); + this.psv.container.addEventListener('touchmove', this); + this.psv.container.addEventListener('mouseup', this); + this.psv.container.addEventListener('touchend', this); zoom_minus.addEventListener('click', this.psv.zoomOut.bind(this.psv)); zoom_plus.addEventListener('click', this.psv.zoomIn.bind(this.psv)); - - this.psv.on('zoom-updated', this._moveZoomValue.bind(this)); + + this.psv.on('zoom-updated', this); var self = this; setTimeout(function() { @@ -2054,6 +2431,38 @@ PSVNavBarZoomButton.prototype.create = function() { }, 0); }; +/** + * Destroys the button + */ +PSVNavBarZoomButton.prototype.destroy = function() { + this.psv.container.removeEventListener('mousemove', this); + this.psv.container.removeEventListener('touchmove', this); + this.psv.container.removeEventListener('mouseup', this); + this.psv.container.removeEventListener('touchend', this); + + this.psv.off('zoom-updated', this); + + PSVNavBarButton.prototype.destroy.call(this); +}; + +/** + * Handle events + * @param e (Event) + */ +PSVNavBarZoomButton.prototype.handleEvent = function(e) { + switch (e.type) { + // @formatter:off + case 'mousedown': this._initZoomChangeWithMouse(e); break; + case 'touchstart': this._initZoomChangeByTouch(e); break; + case 'mousemove': this._changeZoomWithMouse(e); break; + case 'touchmove': this._changeZoomByTouch(e); break; + case 'mouseup': this._stopZoomChange(e); break; + case 'touchend': this._stopZoomChange(e); break; + case 'psv:zoom-updated': this._moveZoomValue(e.args[0]); break; + // @formatter:on + } +}; + /** * Moves the zoom cursor * @param level (integer) Zoom level (between 0 and 100) @@ -2125,13 +2534,14 @@ PSVNavBarZoomButton.prototype._changeZoom = function(x) { } }; + /** * Navigation bar download button class * @param psv (PhotoSphereViewer) A PhotoSphereViewer object */ function PSVNavBarDownloadButton(psv) { PSVNavBarButton.call(this, psv); - + this.create(); } @@ -2143,8 +2553,9 @@ PSVNavBarDownloadButton.prototype.constructor = PSVNavBarDownloadButton; * @return (void) */ PSVNavBarDownloadButton.prototype.create = function() { - this.button = document.createElement('div'); - this.button.className = 'psv-button download-button'; + PSVNavBarButton.prototype.create.call(this); + + this.button.classList.add('download-button'); this.button.title = this.psv.config.lang.download; this.button.appendChild(document.createElement('div')); @@ -2165,7 +2576,6 @@ PSVNavBarDownloadButton.prototype.download = function() { link.click(); }; -/*jshint multistr: true */ /** * Navigation bar markers button class @@ -2173,7 +2583,7 @@ PSVNavBarDownloadButton.prototype.download = function() { */ function PSVNavBarMarkersButton(psv) { PSVNavBarButton.call(this, psv); - + this.prop = { panelOpened: false, panelOpening: false @@ -2190,15 +2600,39 @@ PSVNavBarMarkersButton.prototype.constructor = PSVNavBarMarkersButton; * @return (void) */ PSVNavBarMarkersButton.prototype.create = function() { - this.button = document.createElement('div'); - this.button.className = 'psv-button markers-button'; + PSVNavBarButton.prototype.create.call(this); + + this.button.classList.add('markers-button'); this.button.title = this.psv.config.lang.markers; this.button.innerHTML = PhotoSphereViewer.ICONS['pin.svg']; - + this.button.addEventListener('click', this.toggleMarkers.bind(this)); - - this.psv.on('open-panel', this._onPanelOpened.bind(this)); - this.psv.on('close-panel', this._onPanelClosed.bind(this)); + + this.psv.on('open-panel', this); + this.psv.on('close-panel', this); +}; + +/** + * Destroys the button + */ +PSVNavBarMarkersButton.prototype.destroy = function() { + this.psv.off('open-panel', this); + this.psv.off('close-panel', this); + + PSVNavBarButton.prototype.destroy.call(this); +}; + +/** + * Handle events + * @param e (Event) + */ +PSVNavBarMarkersButton.prototype.handleEvent = function(e) { + switch (e.type) { + // @formatter:off + case 'psv:open-panel': this._onPanelOpened(); break; + case 'psv:close-panel': this._onPanelClosed(); break; + // @formatter:on + } }; /** @@ -2219,30 +2653,35 @@ PSVNavBarMarkersButton.prototype.toggleMarkers = function() { * @return (void) */ PSVNavBarMarkersButton.prototype.showMarkers = function() { - var html = '
\ -

' + this.psv.config.lang.markers + '

\ - ' + + '
'; + this.prop.panelOpening = true; this.psv.panel.showPanel(html, true); - + this.psv.panel.container.querySelector('.psv-markers-list').addEventListener('click', this._onClickItem.bind(this)); }; @@ -2263,6 +2702,7 @@ PSVNavBarMarkersButton.prototype._onClickItem = function(e) { var li; if (e.target && (li = PSVUtils.getClosest(e.target, 'li')) && li.dataset.psvMarker) { this.psv.hud.gotoMarker(li.dataset.psvMarker, 1000); + this.psv.panel.hidePanel(); } }; @@ -2278,7 +2718,7 @@ PSVNavBarMarkersButton.prototype._onPanelOpened = function() { else { this.prop.panelOpened = false; } - + this.toggleActive(this.prop.panelOpened); }; @@ -2289,10 +2729,11 @@ PSVNavBarMarkersButton.prototype._onPanelOpened = function() { PSVNavBarMarkersButton.prototype._onPanelClosed = function() { this.prop.panelOpened = false; this.prop.panelOpening = false; - + this.toggleActive(this.prop.panelOpened); }; + /** * Custom error used in the lib * http://stackoverflow.com/a/27724419/1207670 @@ -2300,7 +2741,7 @@ PSVNavBarMarkersButton.prototype._onPanelClosed = function() { */ function PSVError(message) { this.message = message; - + // Use V8's native method if available, otherwise fallback if ('captureStackTrace' in Error) { Error.captureStackTrace(this, PSVError); @@ -2314,6 +2755,7 @@ PSVError.prototype = Object.create(Error.prototype); PSVError.prototype.name = 'PSVError'; PSVError.prototype.constructor = PSVError; + /** * Static utilities for PSV */ @@ -2371,7 +2813,7 @@ PSVUtils.hasParent = function(el, parent) { */ PSVUtils.getClosest = function(el, selector) { var matches = el.matches || el.msMatchesSelector; - + do { if (matches.bind(el)(selector)) { return el; @@ -2386,9 +2828,9 @@ PSVUtils.getClosest = function(el, selector) { * @return (string) */ PSVUtils.mouseWheelEvent = function() { - return "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel" - document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel" - "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox + return 'onwheel' in document.createElement('div') ? 'wheel' : // Modern browsers support "wheel" + document.onmousewheel !== undefined ? 'mousewheel' : // Webkit and IE support at least "mousewheel" + 'DOMMouseScroll'; // let's assume that remaining browsers are older Firefox }; /** @@ -2396,8 +2838,17 @@ PSVUtils.mouseWheelEvent = function() { * @return (string) */ PSVUtils.fullscreenEvent = function() { - var map = {'exitFullscreen': 'fullscreenchange', 'webkitExitFullscreen': 'webkitfullscreenchange', 'mozCancelFullScreen': 'mozfullscreenchange', 'msExitFullscreen': 'msFullscreenEnabled'}; - for (var exit in map) if (exit in document) return map[exit]; + var map = { + 'exitFullscreen': 'fullscreenchange', + 'webkitExitFullscreen': 'webkitfullscreenchange', + 'mozCancelFullScreen': 'mozfullscreenchange', + 'msExitFullscreen': 'msFullscreenEnabled' + }; + + for (var exit in map) { + if (exit in document) return map[exit]; + } + return 'fullscreenchange'; }; @@ -2418,9 +2869,19 @@ PSVUtils.stayBetween = function(x, min, max) { * @param attr (string) The wanted attribute * @return (string) The value of the attribute */ -PSVUtils.getAttribute = function(data, attr) { - var a = data.indexOf('GPano:' + attr) + attr.length + 8, b = data.indexOf('"', a); - return data.substring(a, b); +PSVUtils.getXMPValue = function(data, attr) { + var a, b; + // XMP data are stored in children + if ((a = data.indexOf('')) !== -1 && (b = data.indexOf('')) !== -1) { + return data.substring(a, b).replace('', ''); + } + // XMP data are stored in attributes + else if ((a = data.indexOf('GPano:' + attr)) !== -1 && (b = data.indexOf('"', a)) !== -1) { + return data.substring(a + attr.length + 8, b); + } + else { + return null; + } }; /** @@ -2464,18 +2925,22 @@ PSVUtils.getStyle = function(elt, prop) { */ PSVUtils.parsePosition = function(value) { if (!value) { - return {top: 0.5, left: 0.5}; + return { top: 0.5, left: 0.5 }; + } + + if (typeof value === 'object') { + return value; } - + var e = document.createElement('div'); document.body.appendChild(e); e.style.backgroundPosition = value; var parsed = PSVUtils.getStyle(e, 'background-position').match(/^([0-9.]+)% ([0-9.]+)%$/); document.body.removeChild(e); - + return { - left: parsed[1]/100, - top: parsed[2]/100 + left: parsed[1] / 100, + top: parsed[2] / 100 }; }; @@ -2497,28 +2962,32 @@ PSVUtils.deepmerge = function(target, src) { src.forEach(function(e, i) { if (typeof dst[i] === 'undefined') { dst[i] = e; - } else if (typeof e === 'object') { + } + else if (typeof e === 'object') { dst[i] = PSVUtils.deepmerge(target[i], e); - } else { + } + else { if (target.indexOf(e) === -1) { dst.push(e); } } }); - } else { + } + else { if (target && typeof target === 'object') { - Object.keys(target).forEach(function (key) { + Object.keys(target).forEach(function(key) { dst[key] = target[key]; }); } - Object.keys(src).forEach(function (key) { + Object.keys(src).forEach(function(key) { if (typeof src[key] !== 'object' || !src[key]) { dst[key] = src[key]; } else { if (!target[key]) { dst[key] = src[key]; - } else { + } + else { dst[key] = PSVUtils.deepmerge(target[key], src[key]); } } @@ -2537,6 +3006,7 @@ PSVUtils.clone = function(src) { return PSVUtils.deepmerge({}, src); }; + PhotoSphereViewer.ICONS['pin.svg'] = ''; PhotoSphereViewer.ICONS['zoom-in.svg'] = ''; @@ -2544,4 +3014,4 @@ PhotoSphereViewer.ICONS['zoom-in.svg'] = ''; return PhotoSphereViewer; -})); \ No newline at end of file +})); diff --git a/dist/photo-sphere-viewer.min.css b/dist/photo-sphere-viewer.min.css index 73bf89036..9bcd1051c 100644 --- a/dist/photo-sphere-viewer.min.css +++ b/dist/photo-sphere-viewer.min.css @@ -1,8 +1,8 @@ /*! - * Photo Sphere Viewer 3.0.1 + * Photo Sphere Viewer 3.1.0 * Copyright (c) 2014-2015 Jérémy Heleine - * Copyright (c) 2015 Damien "Mistic" Sorel + * Copyright (c) 2015-2016 Damien "Mistic" Sorel * Licensed under MIT (http://opensource.org/licenses/MIT) */ -.psv-container{width:100%;height:100%;margin:0;padding:0;position:relative;background:radial-gradient(#fff,#fdfdfd 16%,#fbfbfb 33%,#f8f8f8 49%,#efefef 66%,#dfdfdf 82%,#bfbfbf 100%);overflow:hidden}.psv-container.loading{text-align:center}.psv-canvas-container{position:absolute;z-index:0}.psv-canvas-container canvas{display:block}.psv-loader{position:relative;color:rgba(61,61,61,.7);width:150px;height:150px;border:10px solid transparent}.psv-loader .loader-canvas{position:absolute;top:0;left:0}.psv-loader,.psv-loader .loader-image,.psv-loader .loader-text{display:inline-block;vertical-align:middle}.psv-loader .loader-text{font:14px sans-serif}.psv-container.loading::before,.psv-loader::before{content:'';display:inline-block;height:100%;vertical-align:middle}.psv-navbar{position:absolute;z-index:100;bottom:0;width:100%;background:rgba(61,61,61,.5)}.psv-navbar,.psv-navbar *{box-sizing:content-box}.psv-navbar .caption{color:rgba(255,255,255,.7);margin:10px;white-space:nowrap;overflow:hidden;text-align:center;font-family:sans-serif}.psv-navbar .psv-button{float:left;padding:10px;position:relative;cursor:pointer;height:20px;background:0 0}.psv-navbar .psv-button.active{background:rgba(255,255,255,.1)}.psv-navbar .psv-button svg{width:100%}.psv-navbar .psv-button svg *{fill:rgba(255,255,255,.7)}.psv-navbar .autorotate-button{width:20px}.psv-navbar .autorotate-button .equator,.psv-navbar .autorotate-button .sphere{border-radius:50%;border-width:1px;border-style:solid;border-color:rgba(255,255,255,.7);transition:.3s ease;transition-property:transform,height,margin}.psv-navbar .autorotate-button .sphere{width:18px;height:18px;transform:rotateY(0)}.psv-navbar .autorotate-button .equator{position:absolute;top:50%;width:18px;height:1.8px;margin-top:-1.9px;transform:rotateY(0)}.psv-navbar .autorotate-button:hover .sphere{transform:rotateY(180deg)}.psv-navbar .autorotate-button:hover .equator{transform:rotateY(180deg);height:4.5px;margin-top:-3.25px}.psv-navbar .download-button{width:20px;height:20px}.psv-navbar .download-button:before{content:'';position:absolute;width:20px;height:7px;left:8px;bottom:10px;border:2px solid rgba(255,255,255,.7);border-top-width:0;border-radius:0;transition:border-radius .2s ease}.psv-navbar .download-button div{position:absolute;width:12px;height:12px;top:50%;left:50%;margin:-8px -6px -6px;transition:margin-top .2s ease}.psv-navbar .download-button div:after,.psv-navbar .download-button div:before{content:'';display:block;margin:0 auto}.psv-navbar .download-button div:before{width:6px;height:6px;background:rgba(255,255,255,.7)}.psv-navbar .download-button div:after{width:0;height:0;border:6px solid transparent;border-top-color:rgba(255,255,255,.7)}.psv-navbar .download-button:hover:before{border-radius:2px}.psv-navbar .download-button:hover div{margin-top:-6px}.psv-navbar .fullscreen-button{float:right;width:26.67px;height:20px}.psv-navbar .fullscreen-button div{position:absolute;width:26.67px;height:20px}.psv-navbar .fullscreen-button div:after,.psv-navbar .fullscreen-button div:before{content:'';position:absolute;width:6px;height:4px;border-style:solid;border-color:rgba(255,255,255,.7);border-width:2px;transition:all .2s ease}.psv-navbar .fullscreen-button div:first-child:before{top:0;left:0;border-right-width:0;border-bottom-width:0}.psv-navbar .fullscreen-button div:first-child:after{top:0;right:0;border-left-width:0;border-bottom-width:0}.psv-navbar .fullscreen-button div:last-child:before{bottom:0;left:0;border-right-width:0;border-top-width:0}.psv-navbar .fullscreen-button div:last-child:after{bottom:0;right:0;border-left-width:0;border-top-width:0}.psv-navbar .fullscreen-button:hover div:after,.psv-navbar .fullscreen-button:hover div:before{width:8.67px;height:6px}.psv-navbar .zoom-button{cursor:default}.psv-navbar .zoom-button .minus,.psv-navbar .zoom-button .plus{float:left;position:relative;cursor:pointer;width:14px;height:14px}.psv-navbar .zoom-button .minus svg,.psv-navbar .zoom-button .plus svg{position:relative;top:20%}.psv-navbar .zoom-button .range{float:left;padding:9.5px 7px}.psv-navbar .zoom-button .range .line{position:relative;cursor:pointer;width:80px;height:1px;background:rgba(255,255,255,.7);transition:all .3s ease}.psv-navbar .zoom-button .range .handle{position:absolute;border-radius:50%;top:-3px;width:7px;height:7px;background:rgba(255,255,255,.7);transform:scale(1);transition:transform .3s ease}.psv-hud,.psv-panel{height:100%;position:absolute}.psv-navbar .zoom-button:hover .range .line{box-shadow:0 0 2px rgba(255,255,255,.7)}.psv-navbar .zoom-button:hover .range .handle{transform:scale(1.3)}.psv-navbar .markers-button{width:20px}.psv-navbar .markers-button svg{transform:scale(1);transition:transform .3s ease}.psv-navbar .markers-button:hover svg{transform:scale(1.2)}.psv-markers-list h1{font:24px sans-serif;margin:1em 0;text-align:center;text-shadow:2px 1px #000}.psv-markers-list ul{list-style:none;margin:0;padding:0;overflow:hidden}.psv-markers-list ul li{clear:both;min-height:20px;padding:.5em 1em;cursor:pointer;transform:translateX(0);transition:transform .3s ease-in-out}.psv-markers-list ul li:before{content:'';position:absolute;top:0;left:0;height:100%;width:10px;margin-left:-10px}.psv-markers-list ul li:nth-child(odd),.psv-markers-list ul li:nth-child(odd):before{background:rgba(255,255,255,.1)}.psv-markers-list ul li:nth-child(even),.psv-markers-list ul li:nth-child(even):before{background:0 0}.psv-markers-list ul li:hover{transform:translateX(10px);transition:transform .1s ease-in-out}.psv-markers-list ul li img{float:left;width:20px}.psv-markers-list ul li p{margin:0;padding:0;padding-left:calc(20px + .5em)}.psv-hud{z-index:50;width:100%}.psv-hud .marker{position:absolute;top:0;left:0;background-size:contain;background-repeat:no-repeat;cursor:pointer;display:none}.psv-hud .marker.visible{display:block}.psv-panel{z-index:100;right:0;width:400px;max-width:calc(100% - 9px);background:rgba(10,10,10,.7);transform:translate3d(100%,0,0);opacity:0;transition-property:opacity,transform;transition-timing-function:ease-in-out;transition-duration:.1s;cursor:default;margin-left:9px}.psv-container.has-navbar .psv-panel{height:calc(100% - 40px)}.psv-panel .close-button{display:none;position:absolute;top:0;left:-24px;width:24px;height:24px;background:rgba(0,0,0,.9)}.psv-panel .close-button::after,.psv-panel .close-button::before{content:'';position:absolute;top:50%;left:4px;width:15px;height:1px;background-color:#fff;transition:.2s ease-in-out;transition-property:width,left,transform}.psv-panel .close-button::before{transform:rotate(45deg)}.psv-panel .close-button::after{transform:rotate(-45deg)}.psv-panel .close-button:hover::after,.psv-panel .close-button:hover::before{left:0;width:23px}.psv-panel .close-button:hover::before{transform:rotate(135deg)}.psv-panel .close-button:hover::after{transform:rotate(45deg)}.psv-panel .resizer{display:none;position:absolute;top:0;left:-9px;width:9px;height:100%;background-color:rgba(0,0,0,.9);cursor:col-resize}.psv-panel .resizer::before{content:'';position:absolute;top:50%;left:1px;margin-top:-14.5px;width:1px;height:1px;box-shadow:1px 0 #fff,3px 0 #fff,5px 0 #fff,1px 2px #fff,3px 2px #fff,5px 2px #fff,1px 4px #fff,3px 4px #fff,5px 4px #fff,1px 6px #fff,3px 6px #fff,5px 6px #fff,1px 8px #fff,3px 8px #fff,5px 8px #fff,1px 10px #fff,3px 10px #fff,5px 10px #fff,1px 12px #fff,3px 12px #fff,5px 12px #fff,1px 14px #fff,3px 14px #fff,5px 14px #fff,1px 16px #fff,3px 16px #fff,5px 16px #fff,1px 18px #fff,3px 18px #fff,5px 18px #fff,1px 20px #fff,3px 20px #fff,5px 20px #fff,1px 22px #fff,3px 22px #fff,5px 22px #fff,1px 24px #fff,3px 24px #fff,5px 24px #fff,1px 26px #fff,3px 26px #fff,5px 26px #fff,1px 28px #fff,3px 28px #fff,5px 28px #fff;background:0 0}.psv-panel .content{width:100%;height:100%;box-sizing:border-box;color:#dcdcdc;font:16px sans-serif;overflow:auto}.psv-panel .content:not(.no-margin){padding:1em}.psv-panel .content.no-interaction{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none}.psv-panel.open{transform:translate3d(0,0,0);opacity:1;transition-duration:.2s}.psv-panel.open .close-button,.psv-panel.open .resizer{display:block}.psv-tooltip{position:absolute;z-index:75;box-sizing:border-box;max-width:200px;background-color:rgba(61,61,61,.8);border-radius:4px;padding:.5em 1em;opacity:0;transition-property:opacity;transition-timing-function:ease-in-out;transition-duration:.1s}.psv-tooltip.bottom-center,.psv-tooltip.bottom-left,.psv-tooltip.center-left,.psv-tooltip.center-right,.psv-tooltip.top-center{transition-property:opacity,transform}.psv-tooltip .content{color:#fff;font:14px sans-serif;text-shadow:0 1px #000}.psv-tooltip .arrow{position:absolute;height:0;width:0;border:7px solid transparent}.psv-tooltip.bottom-center .arrow,.psv-tooltip.bottom-left .arrow,.psv-tooltip.bottom-right .arrow{border-bottom-color:rgba(61,61,61,.8)}.psv-tooltip.top-center .arrow,.psv-tooltip.top-left .arrow,.psv-tooltip.top-right .arrow{border-top-color:rgba(61,61,61,.8)}.psv-tooltip.bottom-center{box-shadow:0 3px 0 rgba(90,90,90,.7);transform:translate3d(0,-5px,0)}.psv-tooltip.center-left{box-shadow:-3px 0 0 rgba(90,90,90,.7);transform:translate3d(5px,0,0)}.psv-tooltip.center-left .arrow{border-left-color:rgba(61,61,61,.8)}.psv-tooltip.top-center{box-shadow:0 -3px 0 rgba(90,90,90,.7);transform:translate3d(0,5px,0)}.psv-tooltip.center-right{box-shadow:3px 0 0 rgba(90,90,90,.7);transform:translate3d(-5px,0,0)}.psv-tooltip.center-right .arrow{border-right-color:rgba(61,61,61,.8)}.psv-tooltip.bottom-left{box-shadow:-3px 3px 0 rgba(90,90,90,.7);transform:translate3d(0,-5px,0)}.psv-tooltip.bottom-right{box-shadow:3px 3px 0 rgba(90,90,90,.7);transform:translate3d(0,-5px,0);transition-property:opacity,transform}.psv-tooltip.top-left,.psv-tooltip.top-right{transform:translate3d(0,5px,0);transition-property:opacity,transform}.psv-tooltip.top-left{box-shadow:-3px -3px 0 rgba(90,90,90,.7)}.psv-tooltip.top-right{box-shadow:3px -3px 0 rgba(90,90,90,.7)}.psv-tooltip.visible{transform:translate3d(0,0,0);opacity:1;transition-duration:.2s} \ No newline at end of file +.psv-tooltip.bottom-center .arrow,.psv-tooltip.bottom-left .arrow,.psv-tooltip.bottom-right .arrow{border-bottom-color:rgba(61,61,61,.8)}.psv-container{width:100%;height:100%;margin:0;padding:0;position:relative;background:radial-gradient(#fff,#fdfdfd 16%,#fbfbfb 33%,#f8f8f8 49%,#efefef 66%,#dfdfdf 82%,#bfbfbf 100%);overflow:hidden}.psv-container.loading{text-align:center}.psv-canvas-container{position:absolute;z-index:0}.psv-canvas-container canvas{display:block}.psv-loader{position:relative;color:rgba(61,61,61,.7);width:150px;height:150px;border:10px solid transparent}.psv-loader .loader-canvas{position:absolute;top:0;left:0}.psv-loader,.psv-loader .loader-image,.psv-loader .loader-text{display:inline-block;vertical-align:middle}.psv-loader .loader-text{font:14px sans-serif}.psv-container.loading::before,.psv-loader::before{content:'';display:inline-block;height:100%;vertical-align:middle}.psv-navbar{position:absolute;z-index:100;bottom:0;width:100%;background:rgba(61,61,61,.5)}.psv-navbar,.psv-navbar *{box-sizing:content-box}.psv-navbar .caption{color:rgba(255,255,255,.7);margin:10px;white-space:nowrap;overflow:hidden;text-align:center;font-family:sans-serif}.psv-navbar .psv-button{float:left;padding:10px;position:relative;cursor:pointer;height:20px;background:0 0}.psv-navbar .psv-button.active{background:rgba(255,255,255,.1)}.psv-navbar .psv-button svg{width:100%}.psv-navbar .psv-button svg *{fill:rgba(255,255,255,.7)}.psv-navbar .autorotate-button{width:20px}.psv-navbar .autorotate-button .equator,.psv-navbar .autorotate-button .sphere{border-radius:50%;border-width:1px;border-style:solid;border-color:rgba(255,255,255,.7);transition:.3s ease;transition-property:transform,height,margin}.psv-navbar .autorotate-button .sphere{width:18px;height:18px;transform:rotateY(0)}.psv-navbar .autorotate-button .equator{position:absolute;top:50%;width:18px;height:1.8px;margin-top:-1.9px;transform:rotateY(0)}.psv-navbar .autorotate-button:hover .sphere{transform:rotateY(180deg)}.psv-navbar .autorotate-button:hover .equator{transform:rotateY(180deg);height:4.5px;margin-top:-3.25px}.psv-navbar .download-button{width:20px;height:20px}.psv-navbar .download-button:before{content:'';position:absolute;width:20px;height:7px;left:10px;bottom:10px;border:2px solid rgba(255,255,255,.7);border-top-width:0;border-radius:0;transition:border-radius .2s ease}.psv-navbar .download-button div{position:absolute;width:12px;height:12px;top:50%;left:50%;margin:-8px -6px -6px;transition:margin-top .2s ease}.psv-navbar .download-button div:after,.psv-navbar .download-button div:before{content:'';display:block;margin:0 auto}.psv-navbar .download-button div:before{width:6px;height:6px;background:rgba(255,255,255,.7)}.psv-navbar .download-button div:after{width:0;height:0;border:6px solid transparent;border-top-color:rgba(255,255,255,.7)}.psv-tooltip.top-center .arrow,.psv-tooltip.top-left .arrow,.psv-tooltip.top-right .arrow{border-top-color:rgba(61,61,61,.8)}.psv-navbar .download-button:hover:before{border-radius:2px}.psv-navbar .download-button:hover div{margin-top:-6px}.psv-navbar .fullscreen-button{float:right;width:26.67px;height:20px}.psv-navbar .fullscreen-button div{position:absolute;width:26.67px;height:20px}.psv-navbar .fullscreen-button div:after,.psv-navbar .fullscreen-button div:before{content:'';position:absolute;width:6px;height:4px;border-style:solid;border-color:rgba(255,255,255,.7);border-width:2px;transition:all .2s ease}.psv-navbar .fullscreen-button div:first-child:before{top:0;left:0;border-right-width:0;border-bottom-width:0}.psv-navbar .fullscreen-button div:first-child:after{top:0;right:0;border-left-width:0;border-bottom-width:0}.psv-navbar .fullscreen-button div:last-child:before{bottom:0;left:0;border-right-width:0;border-top-width:0}.psv-navbar .fullscreen-button div:last-child:after{bottom:0;right:0;border-left-width:0;border-top-width:0}.psv-navbar .fullscreen-button:hover div:after,.psv-navbar .fullscreen-button:hover div:before{width:8.67px;height:6px}.psv-navbar .zoom-button{cursor:default}.psv-navbar .zoom-button .minus,.psv-navbar .zoom-button .plus{float:left;position:relative;cursor:pointer;width:14px;height:14px}.psv-navbar .zoom-button .minus svg,.psv-navbar .zoom-button .plus svg{position:relative;top:20%}.psv-navbar .zoom-button .range{float:left;padding:9.5px 7px}.psv-navbar .zoom-button .range .line{position:relative;cursor:pointer;width:80px;height:1px;background:rgba(255,255,255,.7);transition:all .3s ease}.psv-navbar .zoom-button .range .handle{position:absolute;border-radius:50%;top:-3px;width:7px;height:7px;background:rgba(255,255,255,.7);transform:scale(1);transition:transform .3s ease}.psv-navbar .zoom-button:hover .range .line{box-shadow:0 0 2px rgba(255,255,255,.7)}.psv-navbar .zoom-button:hover .range .handle{transform:scale(1.3)}.psv-navbar .markers-button{width:20px}.psv-navbar .markers-button svg{transform:scale(1);transition:transform .3s ease}.psv-navbar .markers-button:hover svg{transform:scale(1.2)}.psv-markers-list h1{font:24px sans-serif;margin:1em 0;text-align:center;text-shadow:2px 1px #000}.psv-markers-list ul{list-style:none;margin:0;padding:0;overflow:hidden}.psv-markers-list ul li{clear:both;min-height:20px;padding:.5em 1em;cursor:pointer;transform:translateX(0);transition:transform .3s ease-in-out}.psv-markers-list ul li:before{content:'';position:absolute;top:0;left:0;height:100%;width:10px;margin-left:-10px}.psv-markers-list ul li:nth-child(odd),.psv-markers-list ul li:nth-child(odd):before{background:rgba(255,255,255,.1)}.psv-markers-list ul li:nth-child(even),.psv-markers-list ul li:nth-child(even):before{background:0 0}.psv-markers-list ul li:hover{transform:translateX(10px);transition:transform .1s ease-in-out}.psv-markers-list ul li img{float:left;width:20px}.psv-markers-list ul li p{margin:0;padding:0;padding-left:calc(20px + .5em)}.psv-hud{position:absolute;z-index:50;width:100%;height:100%}.psv-hud .psv-marker{position:absolute;top:0;left:0;background-size:contain;background-repeat:no-repeat;cursor:pointer;display:none}.psv-hud .psv-marker.transparent{display:block;opacity:0}.psv-hud .psv-marker.visible{display:block}.psv-panel{position:absolute;z-index:100;right:0;height:100%;width:400px;max-width:calc(100% - 9px);background:rgba(10,10,10,.7);transform:translate3d(100%,0,0);opacity:0;transition-property:opacity,transform;transition-timing-function:ease-in-out;transition-duration:.1s;cursor:default;margin-left:9px}.psv-container.has-navbar .psv-panel{height:calc(100% - 40px)}.psv-panel .close-button{display:none;position:absolute;top:0;left:-24px;width:24px;height:24px;background:rgba(0,0,0,.9)}.psv-panel .close-button::after,.psv-panel .close-button::before{content:'';position:absolute;top:50%;left:4px;width:15px;height:1px;background-color:#fff;transition:.2s ease-in-out;transition-property:width,left,transform}.psv-panel .close-button::before{transform:rotate(45deg)}.psv-panel .close-button::after{transform:rotate(-45deg)}.psv-panel .close-button:hover::after,.psv-panel .close-button:hover::before{left:0;width:23px}.psv-panel .close-button:hover::before{transform:rotate(135deg)}.psv-panel .close-button:hover::after{transform:rotate(45deg)}.psv-panel .resizer{display:none;position:absolute;top:0;left:-9px;width:9px;height:100%;background-color:rgba(0,0,0,.9);cursor:col-resize}.psv-panel .resizer::before{content:'';position:absolute;top:50%;left:1px;margin-top:-14.5px;width:1px;height:1px;box-shadow:1px 0 #fff,3px 0 #fff,5px 0 #fff,1px 2px #fff,3px 2px #fff,5px 2px #fff,1px 4px #fff,3px 4px #fff,5px 4px #fff,1px 6px #fff,3px 6px #fff,5px 6px #fff,1px 8px #fff,3px 8px #fff,5px 8px #fff,1px 10px #fff,3px 10px #fff,5px 10px #fff,1px 12px #fff,3px 12px #fff,5px 12px #fff,1px 14px #fff,3px 14px #fff,5px 14px #fff,1px 16px #fff,3px 16px #fff,5px 16px #fff,1px 18px #fff,3px 18px #fff,5px 18px #fff,1px 20px #fff,3px 20px #fff,5px 20px #fff,1px 22px #fff,3px 22px #fff,5px 22px #fff,1px 24px #fff,3px 24px #fff,5px 24px #fff,1px 26px #fff,3px 26px #fff,5px 26px #fff,1px 28px #fff,3px 28px #fff,5px 28px #fff;background:0 0}.psv-panel .content{width:100%;height:100%;box-sizing:border-box;color:#dcdcdc;font:16px sans-serif;overflow:auto}.psv-panel .content:not(.no-margin){padding:1em}.psv-panel .content.no-interaction{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none}.psv-panel.open{transform:translate3d(0,0,0);opacity:1;transition-duration:.2s}.psv-panel.open .close-button,.psv-panel.open .resizer{display:block}.psv-tooltip{position:absolute;z-index:75;box-sizing:border-box;max-width:200px;background-color:rgba(61,61,61,.8);border-radius:4px;padding:.5em 1em;opacity:0;transition-property:opacity;transition-timing-function:ease-in-out;transition-duration:.1s}.psv-tooltip.bottom-center,.psv-tooltip.bottom-left,.psv-tooltip.bottom-right,.psv-tooltip.center-left,.psv-tooltip.center-right,.psv-tooltip.top-center,.psv-tooltip.top-left,.psv-tooltip.top-right{transition-property:opacity,transform}.psv-tooltip .content{color:#fff;font:14px sans-serif;text-shadow:0 1px #000}.psv-tooltip .arrow{position:absolute;height:0;width:0;border:7px solid transparent}.psv-tooltip.bottom-center{box-shadow:0 3px 0 rgba(90,90,90,.7);transform:translate3d(0,-5px,0)}.psv-tooltip.center-left{box-shadow:-3px 0 0 rgba(90,90,90,.7);transform:translate3d(5px,0,0)}.psv-tooltip.center-left .arrow{border-left-color:rgba(61,61,61,.8)}.psv-tooltip.top-center{box-shadow:0 -3px 0 rgba(90,90,90,.7);transform:translate3d(0,5px,0)}.psv-tooltip.center-right{box-shadow:3px 0 0 rgba(90,90,90,.7);transform:translate3d(-5px,0,0)}.psv-tooltip.center-right .arrow{border-right-color:rgba(61,61,61,.8)}.psv-tooltip.bottom-left{box-shadow:-3px 3px 0 rgba(90,90,90,.7);transform:translate3d(0,-5px,0)}.psv-tooltip.bottom-right{box-shadow:3px 3px 0 rgba(90,90,90,.7);transform:translate3d(0,-5px,0)}.psv-tooltip.top-left{box-shadow:-3px -3px 0 rgba(90,90,90,.7);transform:translate3d(0,5px,0)}.psv-tooltip.top-right{box-shadow:3px -3px 0 rgba(90,90,90,.7);transform:translate3d(0,5px,0)}.psv-tooltip.visible{transform:translate3d(0,0,0);opacity:1;transition-duration:.2s} \ No newline at end of file diff --git a/dist/photo-sphere-viewer.min.js b/dist/photo-sphere-viewer.min.js index 6192f56ad..d86e54fe1 100644 --- a/dist/photo-sphere-viewer.min.js +++ b/dist/photo-sphere-viewer.min.js @@ -1,9 +1,9 @@ /*! - * Photo Sphere Viewer 3.0.1 + * Photo Sphere Viewer 3.1.0 * Copyright (c) 2014-2015 Jérémy Heleine - * Copyright (c) 2015 Damien "Mistic" Sorel + * Copyright (c) 2015-2016 Damien "Mistic" Sorel * Licensed under MIT (http://opensource.org/licenses/MIT) */ -!function(a,b){"function"==typeof define&&define.amd?define(["three"],b):a.PhotoSphereViewer=b(a.THREE)}(this,function(a){"use strict";function b(a){if(!(this instanceof b))return new b(a);if(void 0===a||void 0===a.panorama||void 0===a.container)throw new o("no value given for panorama or container");if(this.config=p.deepmerge(b.DEFAULTS,a),this.config.min_fov=p.stayBetween(this.config.min_fov,1,179),this.config.max_fov=p.stayBetween(this.config.max_fov,1,179),this.config.tilt_up_max=p.stayBetween(this.config.tilt_up_max,-b.HalfPI,b.HalfPI),this.config.tilt_down_max=p.stayBetween(this.config.tilt_down_max,-b.HalfPI,b.HalfPI),null===this.config.default_fov?this.config.default_fov=this.config.max_fov:this.config.default_fov=p.stayBetween(this.config.default_fov,this.config.min_fov,this.config.max_fov),null===this.config.anim_lat&&(this.config.anim_lat=this.config.default_lat),this.config.anim_lat=p.stayBetween(this.config.anim_lat,-b.HalfPI,b.HalfPI),this.config.tilt_up_max"),g=d.substring(e,f);if(-1===e||-1===f||-1===g.indexOf("GPano:"))return void b._loadTexture(!1,!0);var h={full_width:parseInt(p.getAttribute(g,"FullPanoWidthPixels")),full_height:parseInt(p.getAttribute(g,"FullPanoHeightPixels")),cropped_width:parseInt(p.getAttribute(g,"CroppedAreaImageWidthPixels")),cropped_height:parseInt(p.getAttribute(g,"CroppedAreaImageHeightPixels")),cropped_x:parseInt(p.getAttribute(g,"CroppedAreaLeftPixels")),cropped_y:parseInt(p.getAttribute(g,"CroppedAreaTopPixels"))};b._loadTexture(h,!0)}else b.container.textContent="Cannot load image";else 3===a.readyState&&b.loader.setProgress(c+10)},a.onprogress=function(a){if(a.lengthComputable){var d=parseInt(a.loaded/a.total*100);d>c&&(c=d,b.loader.setProgress(c))}},a.onerror=function(){b.container.textContent="Cannot load image"},a.open("GET",this.config.panorama,!0),a.send(null)},b.prototype._loadTexture=function(b,c){var d=new a.ImageLoader,e=this,f=c?100:0;this.config.panorama.match(/^data:image\/[a-z]+;base64/)||d.setCrossOrigin("anonymous");var g=function(a){e.loader.setProgress(100),b||(b={full_width:a.width,full_height:a.height,cropped_width:a.width,cropped_height:a.height,cropped_x:0,cropped_y:0});var c=4096;p.isWebGLSupported()&&(c=p.getMaxTextureWidth());var d=Math.min(b.full_width,c),f=d/b.full_width;b.full_width*=f,b.full_height*=f,b.cropped_width*=f,b.cropped_height*=f,b.cropped_x*=f,b.cropped_y*=f,a.width=b.cropped_width,a.height=b.cropped_height;var g=document.createElement("canvas");g.width=b.full_width,g.height=b.full_height;var h=g.getContext("2d");h.drawImage(a,b.cropped_x,b.cropped_y,b.cropped_width,b.cropped_height),e.prop.size.image_width=b.cropped_width,e.prop.size.image_height=b.cropped_height,e._createScene(g)},h=function(a){if(a.lengthComputable){var b=parseInt(a.loaded/a.total*100);b>f&&(f=b,e.loader.setProgress(f))}},i=function(){e.container.textContent="Cannot load image"};d.load(this.config.panorama,g,h,i)},b.prototype._createScene=function(c){this._onResize(),this.raycaster=new a.Raycaster,this.renderer=p.isWebGLSupported()?new a.WebGLRenderer:new a.CanvasRenderer,this.renderer.setSize(this.prop.size.width,this.prop.size.height),this.camera=new a.PerspectiveCamera(this.config.default_fov,this.prop.size.ratio,1,300),this.camera.position.set(0,0,0),this.scene=new a.Scene,this.scene.add(this.camera);var d=new a.Texture(c);d.needsUpdate=!0;var i=new a.SphereGeometry(200,32,32,-b.HalfPI),j=new a.MeshBasicMaterial({map:d,overdraw:!0});j.side=a.DoubleSide;var k=new a.Mesh(i,j);k.scale.x=-1,this.scene.add(k),this.canvas_container.appendChild(this.renderer.domElement),this.container.removeChild(this.loader.container),this.loader=null,this.container.classList.remove("loading"),this.config.navbar&&(this.container.classList.add("has-navbar"),this.navbar=new h(this),this.container.appendChild(this.navbar.container)),this.hud=new e(this),this.config.markers.forEach(function(a){this.hud.addMarker(a,!0)},this),this.container.appendChild(this.hud.container),this.panel=new f(this),this.container.appendChild(this.panel.container),this.tooltip=new g(this),this.container.appendChild(this.tooltip.container),this.config.time_anim!==!1&&(this.prop.start_timeout=setTimeout(this.startAutorotate.bind(this),this.config.time_anim)),this._bindEvents(),this.trigger("ready"),this.render()},b.prototype._bindEvents=function(){window.addEventListener("resize",this._onResize.bind(this)),document.addEventListener(p.fullscreenEvent(),this._fullscreenToggled.bind(this)),this.config.mousemove&&(this.hud.container.style.cursor="move",this.hud.container.addEventListener("mousedown",this._onMouseDown.bind(this)),this.hud.container.addEventListener("touchstart",this._onTouchStart.bind(this)),this.hud.container.addEventListener("mouseup",this._onMouseUp.bind(this)),this.hud.container.addEventListener("touchend",this._onTouchEnd.bind(this)),this.hud.container.addEventListener("mousemove",this._onMouseMove.bind(this)),this.hud.container.addEventListener("touchmove",this._onTouchMove.bind(this))),this.config.mousewheel&&this.hud.container.addEventListener(p.mouseWheelEvent(),this._onMouseWheel.bind(this))},b.prototype.render=function(){this.prop.direction=new a.Vector3(-Math.cos(this.prop.latitude)*Math.sin(this.prop.longitude),Math.sin(this.prop.latitude),Math.cos(this.prop.latitude)*Math.cos(this.prop.longitude)),this.camera.lookAt(this.prop.direction),this.renderer.render(this.scene,this.camera),this.trigger("render")},b.prototype._autorotate=function(){this.rotate(this.prop.longitude+this.prop.anim_speed/this.prop.fps,this.prop.latitude-(this.prop.latitude-this.config.anim_lat)/200),this.prop.autorotate_timeout=setTimeout(this._autorotate.bind(this),1e3/this.prop.fps)},b.prototype.startAutorotate=function(){clearTimeout(this.prop.start_timeout),this.prop.start_timeout=null,this.stopAnimation(),this._autorotate(),this.trigger("autorotate",!0)},b.prototype.stopAutorotate=function(){clearTimeout(this.prop.start_timeout),this.prop.start_timeout=null,clearTimeout(this.prop.autorotate_timeout),this.prop.autorotate_timeout=null,this.trigger("autorotate",!1)},b.prototype.toggleAutorotate=function(){this.prop.autorotate_timeout?this.stopAutorotate():this.startAutorotate()},b.prototype._onResize=function(){(this.container.clientWidth!=this.prop.size.width||this.container.clientHeight!=this.prop.size.height)&&this.resize(this.container.clientWidth,this.container.clientHeight)},b.prototype.resize=function(a,b){this.prop.size.width=parseInt(a),this.prop.size.height=parseInt(b),this.prop.size.ratio=this.prop.size.width/this.prop.size.height,this.camera&&(this.camera.aspect=this.prop.size.ratio,this.camera.updateProjectionMatrix()),this.renderer&&(this.renderer.setSize(this.prop.size.width,this.prop.size.height),this.render()),this.trigger("size-updated",this.prop.size.width,this.prop.size.height)},b.prototype._onMouseDown=function(a){this._startMove(a)},b.prototype._onTouchStart=function(a){1===a.touches.length?this._startMove(a.touches[0]):2===a.touches.length&&this._startZoom(a)},b.prototype._startMove=function(a){this.prop.mouse_x=this.prop.start_mouse_x=parseInt(a.clientX),this.prop.mouse_y=this.prop.start_mouse_y=parseInt(a.clientY),this.prop.moving=!0,this.prop.moved=!1,this.prop.zooming=!1,this.stopAutorotate(),this.stopAnimation()},b.prototype._startZoom=function(a){var b=[{x:parseInt(a.touches[0].clientX),y:parseInt(a.touches[0].clientY)},{x:parseInt(a.touches[1].clientX),y:parseInt(a.touches[1].clientY)}];this.prop.pinch_dist=Math.sqrt(Math.pow(b[0].x-b[1].x,2)+Math.pow(b[0].y-b[1].y,2)),this.prop.moving=!1,this.prop.zooming=!0,this.stopAutorotate(),this.stopAnimation()},b.prototype._onMouseUp=function(a){this._stopMove(a)},b.prototype._onTouchEnd=function(a){this._stopMove(a.changedTouches[0])},b.prototype._stopMove=function(a){this.prop.moving&&(Math.abs(a.clientX-this.prop.start_mouse_x)j?-j:b.TwoPI-j,e.latitude=b.HalfPI-i;var k=e.longitude/b.TwoPI*this.prop.size.image_width,l=e.latitude/b.PI*this.prop.size.image_height;e.texture_x=parseInt(e.longitude0&&this.zoom(this.prop.zoom_lvl-1)},b.prototype._fullscreenToggled=function(){this.trigger("fullscreen-updated",p.isFullscreenEnabled())},b.prototype.toggleFullscreen=function(){p.isFullscreenEnabled()?p.exitFullscreen():p.requestFullscreen(this.container)},b.prototype.parseAnimSpeed=function(a){a=a.toString().trim();var c=parseFloat(a.replace(/^(-?[0-9]+(?:\.[0-9]*)?).*$/,"$1")),d=a.replace(/^-?[0-9]+(?:\.[0-9]*)?(.*)$/,"$1").trim();d.match(/(pm|per minute)$/)&&(c/=60);var e=0;switch(d){case"dpm":case"degrees per minute":case"dps":case"degrees per second":e=c*Math.PI/180;break;case"radians per minute":case"radians per second":e=c;break;case"rpm":case"revolutions per minute":case"rps":case"revolutions per second":e=c*b.TwoPI;break;default:throw new o('unknown speed unit "'+d+'"')}return e},b.prototype.setAnimSpeed=function(a){this.prop.anim_speed=this.parseAnimSpeed(a)},b.prototype.on=function(a,b){a in this.actions||(this.actions[a]=[]),this.actions[a].push(b)},b.prototype.trigger=function(a,b){if(b=Array.prototype.slice.call(arguments,1),a in this.actions&&this.actions[a].length>0)for(var c=0,d=this.actions[a].length;d>c;++c)this.actions[a][c].apply(this,b)},d.prototype.create=function(){this.container=document.createElement("div"),this.container.className="psv-loader",this.psv.container.appendChild(this.container),this.canvas=document.createElement("canvas"),this.canvas.className="loader-canvas",this.canvas.width=this.container.clientWidth,this.canvas.height=this.container.clientWidth,this.container.appendChild(this.canvas),this.tickness=(this.container.offsetWidth-this.container.clientWidth)/2;var a;if(this.psv.config.loading_img?(a=document.createElement("img"),a.className="loader-image",a.src=this.psv.config.loading_img):this.psv.config.loading_txt&&(a=document.createElement("div"),a.className="loader-text",a.innerHTML=this.psv.config.loading_txt),a){var b=Math.round(Math.sqrt(2*Math.pow(this.canvas.width/2-this.tickness/2,2)));a.style.maxWidth=b+"px",a.style.maxHeight=b+"px",this.container.appendChild(a)}},d.prototype.setProgress=function(a){var b=this.canvas.getContext("2d");b.clearRect(0,0,this.canvas.width,this.canvas.height),b.lineWidth=this.tickness,b.strokeStyle=p.getStyle(this.container,"color"),b.beginPath(),b.arc(this.canvas.width/2,this.canvas.height/2,this.canvas.width/2-this.tickness/2,-Math.PI/2,a/100*2*Math.PI-Math.PI/2),b.stroke()},e.prototype=Object.create(c.prototype),e.prototype.constructor=e,e.publicMethods=["addMarker","removeMarker","getMarker","getCurrentMarker","gotoMarker","hideMarker","showMarker","toggleMarker"],e.prototype.create=function(){this.container=document.createElement("div"),this.container.className="psv-hud",this.container.addEventListener("mouseenter",this._onMouseEnter.bind(this),!0),this.container.addEventListener("mouseleave",this._onMouseLeave.bind(this),!0),this.psv.on("_click",this._onClick.bind(this),!0),this.psv.on("render",this.updatePositions.bind(this))},e.prototype.addMarker=function(c,d){if(!c.id)throw new o("missing marker id");if(this.markers[c.id])throw new o('marker "'+c.id+'" already exists');if(!c.width||!c.height)throw new o("missing marker width/height");if(!c.image)throw new o("missing marker image");if((!c.hasOwnProperty("x")||!c.hasOwnProperty("y"))&(!c.hasOwnProperty("latitude")||!c.hasOwnProperty("longitude")))throw new o("missing marker position, latitude/longitude or x/y");c=p.clone(c),c.$el=document.createElement("div"),c.$el.psvMarker=c,c.$el.className="marker",c.className&&c.$el.classList.add(c.className),c.tooltip&&c.$el.classList.add("has-tooltip");var e=c.$el.style;if(e.width=c.width+"px",e.height=c.height+"px",e.backgroundImage="url("+c.image+")",c.anchor=p.parsePosition(c.anchor),c.hasOwnProperty("x")&&c.hasOwnProperty("y")){var f=c.x/this.psv.prop.size.image_width*b.TwoPI,g=c.y/this.psv.prop.size.image_height*b.PI;c.longitude=f>=b.PI?f-b.PI:f+b.PI,c.latitude=b.HalfPI-g}return c.position3D=new a.Vector3(-Math.cos(c.latitude)*Math.sin(c.longitude),Math.sin(c.latitude),Math.cos(c.latitude)*Math.cos(c.longitude)),c.hasOwnProperty("visible")||(c.visible=!0),this.markers[c.id]=c,this.container.appendChild(c.$el),d||this.updatePositions(),c},e.prototype.getMarker=function(a){var b="object"==typeof a?a.id:a;if(!this.markers[b])throw new o('cannot find marker "'+b+'"');return this.markers[b]},e.prototype.getCurrentMarker=function(){return this.currentMarker},e.prototype.removeMarker=function(a,b){a=this.getMarker(a),delete this.markers[a.id],b||this.updatePositions()},e.prototype.gotoMarker=function(a,b){a=this.getMarker(a),this.psv.animate(a.longitude,a.latitude,b)},e.prototype.hideMarker=function(a){this.getMarker(a).visible=!1,this.updatePositions()},e.prototype.showMarker=function(a){this.getMarker(a).visible=!0,this.updatePositions()},e.prototype.toggleMarker=function(a){this.getMarker(a).visible^=!0,this.updatePositions()},e.prototype.updatePositions=function(){this.psv.camera.updateProjectionMatrix();for(var a in this.markers){var b=this.markers[a],c=this._getMarkerPosition(b);this._isMarkerVisible(b,c)?(b.position2D=c,b.$el.style.transform="translate3D("+c.left+"px, "+c.top+"px, 0px)",b.$el.classList.contains("visible")||b.$el.classList.add("visible")):(b.position2D=null,b.$el.classList.remove("visible"))}},e.prototype._isMarkerVisible=function(a,b){return a.visible&&a.position3D.dot(this.psv.prop.direction)>0&&b.left>=0&&b.left+a.width<=this.psv.prop.size.width&&b.top>=0&&b.top+a.height<=this.psv.prop.size.height},e.prototype._getMarkerPosition=function(a){var b=a.position3D.clone();return b.project(this.psv.camera),{top:(1-b.y)/2*this.psv.prop.size.height-a.height*a.anchor.top,left:(b.x+1)/2*this.psv.prop.size.width-a.width*a.anchor.left}},e.prototype._onMouseEnter=function(a){a.target&&a.target.psvMarker&&a.target.psvMarker.tooltip&&this.psv.tooltip.showTooltip(a.target.psvMarker.tooltip,a.target.psvMarker)},e.prototype._onMouseLeave=function(a){a.target&&a.target.psvMarker&&this.psv.tooltip.hideTooltip()},e.prototype._onClick=function(a){this.psv.prop.moved||(a.target&&a.target.psvMarker?(this.currentMarker=a.target.psvMarker,this.psv.trigger("select-marker",a.target.psvMarker),a.preventDefault()):(this.currentMarker=null,this.psv.trigger("unselect-marker")),a.target&&a.target.psvMarker&&a.target.psvMarker.content?this.psv.panel.showPanel(a.target.psvMarker.content):this.psv.panel.prop.opened&&(a.preventDefault(),this.psv.panel.hidePanel()))},f.prototype=Object.create(c.prototype),f.prototype.constructor=f,f.publicMethods=["showPanel","hidePanel"],f.prototype.create=function(){this.container=document.createElement("aside"),this.container.className="psv-panel",this.container.innerHTML='
',this.content=this.container.querySelector(".content");var a=this.container.querySelector(".close-button");a.addEventListener("click",this.hidePanel.bind(this)),this.psv.config.mousewheel&&this.container.addEventListener(p.mouseWheelEvent(),function(a){a.stopPropagation()});var b=this.container.querySelector(".resizer");b.addEventListener("mousedown",this._onMouseDown.bind(this)),b.addEventListener("touchstart",this._onTouchStart.bind(this)),this.psv.container.addEventListener("mouseup",this._onMouseUp.bind(this)),this.psv.container.addEventListener("touchend",this._onMouseUp.bind(this)),this.psv.container.addEventListener("mousemove",this._onMouseMove.bind(this)),this.psv.container.addEventListener("touchmove",this._onTouchMove.bind(this))},f.prototype.showPanel=function(a,b){this.content.innerHTML=a,this.content.scrollTop=0,this.container.classList.add("open"),b?this.content.classList.contains("no-margin")||this.content.classList.add("no-margin"):this.content.classList.remove("no-margin"),this.prop.opened=!0,this.psv.trigger("open-panel")},f.prototype.hidePanel=function(){this.prop.opened=!1,this.container.classList.remove("open"),this.psv.trigger("close-panel")},f.prototype._onMouseDown=function(a){a.stopPropagation(),this._startResize(a)},f.prototype._onTouchStart=function(a){a.stopPropagation(),this._startResize(a.changedTouches[0])},f.prototype._startResize=function(a){this.prop.mouse_x=parseInt(a.clientX),this.prop.mouse_y=parseInt(a.clientY),this.prop.mousedown=!0,this.content.classList.add("no-interaction")},f.prototype._onMouseUp=function(a){this.prop.mousedown&&(a.stopPropagation(),this.prop.mousedown=!1,this.content.classList.remove("no-interaction"))},f.prototype._onMouseMove=function(a){this.prop.mousedown&&(a.stopPropagation(),this._resize(a))},f.prototype._onTouchMove=function(a){this.prop.mousedown&&(a.stopPropagation(),this._resize(a.changedTouches[0]))},f.prototype._resize=function(a){var b=parseInt(a.clientX),c=parseInt(a.clientY);this.container.style.width=this.container.offsetWidth-(b-this.prop.mouse_x)+"px",this.prop.mouse_x=b,this.prop.mouse_y=c},g.prototype=Object.create(c.prototype),g.prototype.constructor=g,g.publicMethods=["showTooltip","hideTooltip"],g.leftMap={0:"left",.5:"center",1:"right"},g.topMap={0:"top",.5:"center",1:"bottom"},g.prototype.create=function(){this.container=document.createElement("div"),this.container.innerHTML='
',this.container.className="psv-tooltip",this.container.style.top="-1000px",this.container.style.left="-1000px",this.psv.on("render",this.hideTooltip.bind(this))},g.prototype.showTooltip=function(a,b){var c=this.container,d=c.querySelector(".content"),e=c.querySelector(".arrow");if("string"==typeof a&&(a={content:b.tooltip,position:["top","center"]}),"string"==typeof a.position){var f=p.parsePosition(a.position);if(!(f.left in g.leftMap&&f.top in g.topMap))throw new o('unable to parse tooltip position "'+a.position+'"');a.position=[g.topMap[f.top],g.leftMap[f.left]]}c.className="psv-tooltip",a.className&&c.classList.add(a.className),d.innerHTML=a.content,c.style.top="0px",c.style.left="0px";var h=c.getBoundingClientRect(),i={posClass:a.position.slice(),width:h.right-h.left,height:h.bottom-h.top,top:0,left:0,arrow_top:0,arrow_left:0};this._computeTooltipPosition(i,b);var j=!1;i.topthis.psv.prop.size.height-this.config.offset&&(i.posClass[0]="top",j=!0),i.leftthis.psv.prop.size.width-this.config.offset&&(i.posClass[1]="left",j=!0),j&&this._computeTooltipPosition(i,b),c.style.top=i.top+"px",c.style.left=i.left+"px",e.style.top=i.arrow_top+"px",e.style.left=i.arrow_left+"px",c.classList.add(i.posClass.join("-"));var k=this;setTimeout(function(){c.classList.add("visible"),k.psv.trigger("show-tooltip")},100)},g.prototype.hideTooltip=function(){this.container.classList.remove("visible"),this.psv.trigger("hide-tooltip");var a=this;setTimeout(function(){a.container.style.top="-1000px",a.container.style.left="-1000px"},100)},g.prototype._computeTooltipPosition=function(a,b){var c=!1;switch(a.posClass[0]){case"bottom":a.top=b.position2D.top+b.height+this.config.offset+this.config.arrow_size,a.arrow_top=2*-this.config.arrow_size,c=!0;break;case"center":a.top=b.position2D.top+b.height/2-a.height/2,a.arrow_top=a.height/2-this.config.arrow_size;break;case"top":a.top=b.position2D.top-a.height-this.config.offset-this.config.arrow_size,a.arrow_top=a.height,c=!0}switch(a.posClass[1]){case"right":c?(a.left=b.position2D.left,a.arrow_left=b.width/2-this.config.arrow_size):(a.left=b.position2D.left+b.width+this.config.offset+this.config.arrow_size,a.arrow_left=2*-this.config.arrow_size);break;case"center":a.left=b.position2D.left+b.width/2-a.width/2,a.arrow_left=a.width/2-this.config.arrow_size;break;case"left":c?(a.left=b.position2D.left-a.width+b.width,a.arrow_left=a.width-b.width/2-this.config.arrow_size):(a.left=b.position2D.left-a.width-this.config.offset-this.config.arrow_size,a.arrow_left=a.width)}},h.prototype=Object.create(c.prototype),h.prototype.constructor=h,h.publicMethods=["setCaption"],h.DEFAULTS={autorotate:!0,zoom:!0,fullscreen:!0,download:!0,markers:!0},h.prototype.create=function(){if(this.container=document.createElement("div"),this.container.className="psv-navbar",this.config.autorotate){var a=new j(this.psv);this.container.appendChild(a.button)}if(this.config.zoom){var b=new l(this.psv);this.container.appendChild(b.button)}if(this.config.download){var c=new m(this.psv);this.container.appendChild(c.button)}if(this.config.markers){var d=new n(this.psv);this.container.appendChild(d.button)}if(this.config.fullscreen){var e=new k(this.psv);this.container.appendChild(e.button)}this.caption=document.createElement("div"),this.caption.className="caption",this.container.appendChild(this.caption),this.setCaption(this.psv.config.caption)},h.prototype.setCaption=function(a){a?(this.caption.style.display="block",this.caption.innerHTML=a):this.caption.style.display="none"},i.prototype.create=function(){throw new o("Not implemented")},i.prototype.toggleActive=function(a){a?this.button.classList.add("active"):this.button.classList.remove("active")},j.prototype=Object.create(i.prototype),j.prototype.constructor=j,j.prototype.create=function(){this.button=document.createElement("div"),this.button.className="psv-button autorotate-button",this.button.title=this.psv.config.lang.autorotate;var a=document.createElement("div");a.className="sphere",this.button.appendChild(a);var b=document.createElement("div");b.className="equator",this.button.appendChild(b),this.button.addEventListener("click",this.psv.toggleAutorotate.bind(this.psv)),this.psv.on("autorotate",this.toggleActive.bind(this))},k.prototype=Object.create(i.prototype),k.prototype.constructor=k,k.prototype.create=function(){this.button=document.createElement("div"),this.button.className="psv-button fullscreen-button",this.button.title=this.psv.config.lang.fullscreen,this.button.appendChild(document.createElement("div")),this.button.appendChild(document.createElement("div")),this.button.addEventListener("click",this.psv.toggleFullscreen.bind(this.psv)),this.psv.on("fullscreen-updated",this.toggleActive.bind(this))},l.prototype=Object.create(i.prototype),l.prototype.constructor=l,l.prototype.create=function(){this.button=document.createElement("div"),this.button.className="psv-button zoom-button";var a=document.createElement("div");a.className="minus",a.title=this.psv.config.lang.zoomOut,a.innerHTML=b.ICONS["zoom-out.svg"],this.button.appendChild(a);var c=document.createElement("div");c.className="range",this.button.appendChild(c),this.zoom_range=document.createElement("div"),this.zoom_range.className="line",this.zoom_range.title=this.psv.config.lang.zoom,c.appendChild(this.zoom_range),this.zoom_value=document.createElement("div"),this.zoom_value.className="handle",this.zoom_value.title=this.psv.config.lang.zoom,this.zoom_range.appendChild(this.zoom_value);var d=document.createElement("div");d.className="plus",d.title=this.psv.config.lang.zoomIn,d.innerHTML=b.ICONS["zoom-in.svg"],this.button.appendChild(d),this.zoom_range.addEventListener("mousedown",this._initZoomChangeWithMouse.bind(this)),this.zoom_range.addEventListener("touchstart",this._initZoomChangeByTouch.bind(this)),this.psv.container.addEventListener("mousemove",this._changeZoomWithMouse.bind(this)),this.psv.container.addEventListener("touchmove",this._changeZoomByTouch.bind(this)),this.psv.container.addEventListener("mouseup",this._stopZoomChange.bind(this)),this.psv.container.addEventListener("touchend",this._stopZoomChange.bind(this)),a.addEventListener("click",this.psv.zoomOut.bind(this.psv)),d.addEventListener("click",this.psv.zoomIn.bind(this.psv)),this.psv.on("zoom-updated",this._moveZoomValue.bind(this));var e=this;setTimeout(function(){e._moveZoomValue(e.psv.prop.zoom_lvl)},0)},l.prototype._moveZoomValue=function(a){this.zoom_value.style.left=a/100*this.zoom_range.offsetWidth-this.zoom_value.offsetWidth/2+"px"},l.prototype._initZoomChangeWithMouse=function(a){this.prop.mousedown=!0,this._changeZoom(a.clientX)},l.prototype._initZoomChangeByTouch=function(a){this.prop.mousedown=!0,this._changeZoom(a.changedTouches[0].clientX)},l.prototype._stopZoomChange=function(a){this.prop.mousedown=!1},l.prototype._changeZoomWithMouse=function(a){a.preventDefault(),this._changeZoom(a.clientX); -},l.prototype._changeZoomByTouch=function(a){a.preventDefault(),this._changeZoom(a.changedTouches[0].clientX)},l.prototype._changeZoom=function(a){if(this.prop.mousedown){var b=parseInt(a)-this.zoom_range.getBoundingClientRect().left,c=b/this.zoom_range.offsetWidth*100;this.psv.zoom(c)}},m.prototype=Object.create(i.prototype),m.prototype.constructor=m,m.prototype.create=function(){this.button=document.createElement("div"),this.button.className="psv-button download-button",this.button.title=this.psv.config.lang.download,this.button.appendChild(document.createElement("div")),this.button.addEventListener("mouseenter",this.toggleActive.bind(this,!0)),this.button.addEventListener("mouseleave",this.toggleActive.bind(this,!1)),this.button.addEventListener("click",this.download.bind(this))},m.prototype.download=function(){var a=document.createElement("a");a.href=this.psv.config.panorama,a.download=this.psv.config.panorama,this.psv.container.appendChild(a),a.click()},n.prototype=Object.create(i.prototype),n.prototype.constructor=n,n.prototype.create=function(){this.button=document.createElement("div"),this.button.className="psv-button markers-button",this.button.title=this.psv.config.lang.markers,this.button.innerHTML=b.ICONS["pin.svg"],this.button.addEventListener("click",this.toggleMarkers.bind(this)),this.psv.on("open-panel",this._onPanelOpened.bind(this)),this.psv.on("close-panel",this._onPanelClosed.bind(this))},n.prototype.toggleMarkers=function(){this.prop.panelOpened?this.hideMarkers():this.showMarkers()},n.prototype.showMarkers=function(){var a='

'+this.psv.config.lang.markers+"

    ";for(var b in this.psv.hud.markers){var c=this.psv.hud.markers[b],d=c.name||c.id;c.tooltip&&(d="string"==typeof c.tooltip?c.tooltip:c.tooltip.content),a+='
  • '+d+"

  • "}a+="
",this.prop.panelOpening=!0,this.psv.panel.showPanel(a,!0),this.psv.panel.container.querySelector(".psv-markers-list").addEventListener("click",this._onClickItem.bind(this))},n.prototype.hideMarkers=function(){this.psv.panel.hidePanel()},n.prototype._onClickItem=function(a){var b;a.target&&(b=p.getClosest(a.target,"li"))&&b.dataset.psvMarker&&this.psv.hud.gotoMarker(b.dataset.psvMarker,1e3)},n.prototype._onPanelOpened=function(){this.prop.panelOpening?(this.prop.panelOpening=!1,this.prop.panelOpened=!0):this.prop.panelOpened=!1,this.toggleActive(this.prop.panelOpened)},n.prototype._onPanelClosed=function(){this.prop.panelOpened=!1,this.prop.panelOpening=!1,this.toggleActive(this.prop.panelOpened)},o.prototype=Object.create(Error.prototype),o.prototype.name="PSVError",o.prototype.constructor=o;var p={};return p.isCanvasSupported=function(){var a=document.createElement("canvas");return!(!a.getContext||!a.getContext("2d"))},p.isWebGLSupported=function(){var a=document.createElement("canvas");return!(!window.WebGLRenderingContext||!a.getContext("webgl"))},p.getMaxTextureWidth=function(){var a=document.createElement("canvas"),b=a.getContext("webgl");return b.getParameter(b.MAX_TEXTURE_SIZE)},p.hasParent=function(a,b){do if(a===b)return!0;while(a=a.parentNode);return!1},p.getClosest=function(a,b){var c=a.matches||a.msMatchesSelector;do if(c.bind(a)(b))return a;while(a=a.parentElement);return null},p.mouseWheelEvent=function(){return"onwheel"in document.createElement("div")?"wheel":void 0!==document.onmousewheel?"mousewheel":"DOMMouseScroll"},p.fullscreenEvent=function(){var a={exitFullscreen:"fullscreenchange",webkitExitFullscreen:"webkitfullscreenchange",mozCancelFullScreen:"mozfullscreenchange",msExitFullscreen:"msFullscreenEnabled"};for(var b in a)if(b in document)return a[b];return"fullscreenchange"},p.stayBetween=function(a,b,c){return Math.max(b,Math.min(c,a))},p.getAttribute=function(a,b){var c=a.indexOf("GPano:"+b)+b.length+8,d=a.indexOf('"',c);return a.substring(c,d)},p.isFullscreenEnabled=function(){return document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement},p.requestFullscreen=function(a){(a.requestFullscreen||a.mozRequestFullScreen||a.webkitRequestFullscreen||a.msRequestFullscreen).call(a)},p.exitFullscreen=function(a){(document.exitFullscreen||document.mozCancelFullScreen||document.webkitExitFullscreen||document.msExitFullscreen).call(document)},p.getStyle=function(a,b){return window.getComputedStyle(a,null)[b]},p.parsePosition=function(a){if(!a)return{top:.5,left:.5};var b=document.createElement("div");document.body.appendChild(b),b.style.backgroundPosition=a;var c=p.getStyle(b,"background-position").match(/^([0-9.]+)% ([0-9.]+)%$/);return document.body.removeChild(b),{left:c[1]/100,top:c[2]/100}},p.deepmerge=function(a,b){var c=Array.isArray(b),d=c&&[]||{};return c?(a=a||[],d=d.concat(a),b.forEach(function(b,c){"undefined"==typeof d[c]?d[c]=b:"object"==typeof b?d[c]=p.deepmerge(a[c],b):-1===a.indexOf(b)&&d.push(b)})):(a&&"object"==typeof a&&Object.keys(a).forEach(function(b){d[b]=a[b]}),Object.keys(b).forEach(function(c){"object"==typeof b[c]&&b[c]&&a[c]?d[c]=p.deepmerge(a[c],b[c]):d[c]=b[c]})),d},p.clone=function(a){return p.deepmerge({},a)},b.ICONS["pin.svg"]='',b.ICONS["zoom-in.svg"]='',b.ICONS["zoom-out.svg"]='',b}); \ No newline at end of file +!function(a,b){"function"==typeof define&&define.amd?define(["three"],b):a.PhotoSphereViewer=b(a.THREE)}(this,function(a){"use strict";function b(a){if(!(this instanceof b))return new b(a);if(void 0===a||void 0===a.panorama||void 0===a.container)throw new o("no value given for panorama or container");if(this.config=p.deepmerge(b.DEFAULTS,a),this.config.min_fov=p.stayBetween(this.config.min_fov,1,179),this.config.max_fov=p.stayBetween(this.config.max_fov,1,179),this.config.tilt_up_max=p.stayBetween(this.config.tilt_up_max,-b.HalfPI,b.HalfPI),this.config.tilt_down_max=p.stayBetween(this.config.tilt_down_max,-b.HalfPI,b.HalfPI),null===this.config.default_fov?this.config.default_fov=this.config.max_fov:this.config.default_fov=p.stayBetween(this.config.default_fov,this.config.min_fov,this.config.max_fov),null===this.config.anim_lat&&(this.config.anim_lat=this.config.default_lat),this.config.anim_lat=p.stayBetween(this.config.anim_lat,-b.HalfPI,b.HalfPI),this.config.tilt_up_max"),g=d.substring(e,f);if(-1===e||-1===f||-1===g.indexOf("GPano:"))return void b._loadTexture(!1,!0);var h={full_width:parseInt(p.getXMPValue(g,"FullPanoWidthPixels")),full_height:parseInt(p.getXMPValue(g,"FullPanoHeightPixels")),cropped_width:parseInt(p.getXMPValue(g,"CroppedAreaImageWidthPixels")),cropped_height:parseInt(p.getXMPValue(g,"CroppedAreaImageHeightPixels")),cropped_x:parseInt(p.getXMPValue(g,"CroppedAreaLeftPixels")),cropped_y:parseInt(p.getXMPValue(g,"CroppedAreaTopPixels"))};b._loadTexture(h,!0)}else b.container.textContent="Cannot load image";else 3===a.readyState&&b.loader.setProgress(c+10)},a.onprogress=function(a){if(a.lengthComputable){var d=parseInt(a.loaded/a.total*100);d>c&&(c=d,b.loader.setProgress(c))}},a.onerror=function(){b.container.textContent="Cannot load image"},a.open("GET",this.config.panorama,!0),a.send(null)},b.prototype._loadTexture=function(b,c){var d=new a.ImageLoader,e=this,f=c?100:0;this.config.panorama.match(/^data:image\/[a-z]+;base64/)||d.setCrossOrigin("anonymous");var g=function(a){e.loader.setProgress(100),b||(b={full_width:a.width,full_height:a.height,cropped_width:a.width,cropped_height:a.height,cropped_x:0,cropped_y:0});var c=4096;p.isWebGLSupported()&&(c=p.getMaxTextureWidth());var d=Math.min(b.full_width,c),f=d/b.full_width;b.full_width*=f,b.full_height*=f,b.cropped_width*=f,b.cropped_height*=f,b.cropped_x*=f,b.cropped_y*=f,a.width=b.cropped_width,a.height=b.cropped_height;var g=document.createElement("canvas");g.width=b.full_width,g.height=b.full_height;var h=g.getContext("2d");h.drawImage(a,b.cropped_x,b.cropped_y,b.cropped_width,b.cropped_height),e.prop.size.image_width=b.cropped_width,e.prop.size.image_height=b.cropped_height,e._createScene(g)},h=function(a){if(a.lengthComputable){var b=parseInt(a.loaded/a.total*100);b>f&&(f=b,e.loader.setProgress(f))}},i=function(){e.container.textContent="Cannot load image"};d.load(this.config.panorama,g,h,i)},b.prototype._createScene=function(c){this._onResize(),this.raycaster=new a.Raycaster,this.renderer=p.isWebGLSupported()?new a.WebGLRenderer:new a.CanvasRenderer,this.renderer.setSize(this.prop.size.width,this.prop.size.height),this.camera=new a.PerspectiveCamera(this.config.default_fov,this.prop.size.ratio,1,300),this.camera.position.set(0,0,0),this.scene=new a.Scene,this.scene.add(this.camera);var d=new a.Texture(c);d.needsUpdate=!0;var i=new a.SphereGeometry(200,32,32,-b.HalfPI),j=new a.MeshBasicMaterial({map:d,overdraw:!0});j.side=a.DoubleSide,this.mesh=new a.Mesh(i,j),this.mesh.scale.x=-1,this.scene.add(this.mesh),this.canvas_container.appendChild(this.renderer.domElement),this.loader.destroy(),this.loader=null,this.container.classList.remove("loading"),this.config.navbar&&(this.container.classList.add("has-navbar"),this.navbar=new h(this)),this.hud=new e(this),this.config.markers.forEach(function(a){this.hud.addMarker(a,!1)},this),this.panel=new f(this),this.tooltip=new g(this),this.config.time_anim!==!1&&(this.prop.start_timeout=setTimeout(this.startAutorotate.bind(this),this.config.time_anim)),this._bindEvents(),this.trigger("ready"),this.render()},b.prototype._bindEvents=function(){window.addEventListener("resize",this),document.addEventListener(p.fullscreenEvent(),this),this.config.mousemove&&(this.hud.container.style.cursor="move",this.hud.container.addEventListener("mousedown",this),this.hud.container.addEventListener("touchstart",this),window.addEventListener("mouseup",this),window.addEventListener("touchend",this),this.hud.container.addEventListener("mousemove",this),this.hud.container.addEventListener("touchmove",this)),this.config.mousewheel&&this.hud.container.addEventListener(p.mouseWheelEvent(),this)},b.prototype.handleEvent=function(a){switch(a.type){case"resize":this._onResize();break;case"mousedown":this._onMouseDown(a);break;case"touchstart":this._onTouchStart(a);break;case"mouseup":this._onMouseUp(a);break;case"touchend":this._onTouchEnd(a);break;case"mousemove":this._onMouseMove(a);break;case"touchmove":this._onTouchMove(a);break;case p.fullscreenEvent():this._fullscreenToggled();break;case p.mouseWheelEvent():this._onMouseWheel(a)}},b.prototype.render=function(){this.prop.direction=new a.Vector3(-Math.cos(this.prop.latitude)*Math.sin(this.prop.longitude),Math.sin(this.prop.latitude),Math.cos(this.prop.latitude)*Math.cos(this.prop.longitude)),this.camera.lookAt(this.prop.direction),this.renderer.render(this.scene,this.camera),this.trigger("render")},b.prototype._autorotate=function(){this.rotate(this.prop.longitude+this.prop.anim_speed/this.prop.fps,this.prop.latitude-(this.prop.latitude-this.config.anim_lat)/200),this.prop.autorotate_timeout=setTimeout(this._autorotate.bind(this),1e3/this.prop.fps)},b.prototype.startAutorotate=function(){clearTimeout(this.prop.start_timeout),this.prop.start_timeout=null,this.stopAnimation(),this._autorotate(),this.trigger("autorotate",!0)},b.prototype.stopAutorotate=function(){clearTimeout(this.prop.start_timeout),this.prop.start_timeout=null,clearTimeout(this.prop.autorotate_timeout),this.prop.autorotate_timeout=null,this.trigger("autorotate",!1)},b.prototype.toggleAutorotate=function(){this.prop.autorotate_timeout?this.stopAutorotate():this.startAutorotate()},b.prototype._onResize=function(){(this.container.clientWidth!=this.prop.size.width||this.container.clientHeight!=this.prop.size.height)&&this.resize(this.container.clientWidth,this.container.clientHeight)},b.prototype.resize=function(a,b){this.prop.size.width=parseInt(a),this.prop.size.height=parseInt(b),this.prop.size.ratio=this.prop.size.width/this.prop.size.height,this.camera&&(this.camera.aspect=this.prop.size.ratio,this.camera.updateProjectionMatrix()),this.renderer&&(this.renderer.setSize(this.prop.size.width,this.prop.size.height),this.render()),this.trigger("size-updated",this.prop.size.width,this.prop.size.height)},b.prototype._onMouseDown=function(a){this._startMove(a)},b.prototype._onTouchStart=function(a){1===a.touches.length?this._startMove(a.touches[0]):2===a.touches.length&&this._startZoom(a)},b.prototype._startMove=function(a){this.prop.mouse_x=this.prop.start_mouse_x=parseInt(a.clientX),this.prop.mouse_y=this.prop.start_mouse_y=parseInt(a.clientY),this.prop.moving=!0,this.prop.moved=!1,this.prop.zooming=!1,this.stopAutorotate(),this.stopAnimation()},b.prototype._startZoom=function(a){var b=[{x:parseInt(a.touches[0].clientX),y:parseInt(a.touches[0].clientY)},{x:parseInt(a.touches[1].clientX),y:parseInt(a.touches[1].clientY)}];this.prop.pinch_dist=Math.sqrt(Math.pow(b[0].x-b[1].x,2)+Math.pow(b[0].y-b[1].y,2)),this.prop.moving=!1,this.prop.zooming=!0,this.stopAutorotate(),this.stopAnimation()},b.prototype._onMouseUp=function(a){this._stopMove(a)},b.prototype._onTouchEnd=function(a){this._stopMove(a.changedTouches[0])},b.prototype._stopMove=function(a){this.prop.moving&&(Math.abs(a.clientX-this.prop.start_mouse_x)j?-j:b.TwoPI-j,e.latitude=b.HalfPI-i;var k=e.longitude/b.TwoPI*this.prop.size.image_width,l=e.latitude/b.PI*this.prop.size.image_height;e.texture_x=parseInt(e.longitude0&&this.zoom(this.prop.zoom_lvl-1)},b.prototype._fullscreenToggled=function(){this.trigger("fullscreen-updated",p.isFullscreenEnabled())},b.prototype.toggleFullscreen=function(){p.isFullscreenEnabled()?p.exitFullscreen():p.requestFullscreen(this.container)},b.prototype._parseAnimSpeed=function(a){a=a.toString().trim();var c=parseFloat(a.replace(/^(-?[0-9]+(?:\.[0-9]*)?).*$/,"$1")),d=a.replace(/^-?[0-9]+(?:\.[0-9]*)?(.*)$/,"$1").trim();d.match(/(pm|per minute)$/)&&(c/=60);var e=0;switch(d){case"dpm":case"degrees per minute":case"dps":case"degrees per second":e=c*Math.PI/180;break;case"radians per minute":case"radians per second":e=c;break;case"rpm":case"revolutions per minute":case"rps":case"revolutions per second":e=c*b.TwoPI;break;default:throw new o('unknown speed unit "'+d+'"')}return e},b.prototype.setAnimSpeed=function(a){this.prop.anim_speed=this._parseAnimSpeed(a)},b.prototype._setViewerSize=function(a){["width","height"].forEach(function(b){a[b]&&(/^[0-9.]+$/.test(a[b])&&(a[b]+="px"),this.parent.style[b]=a[b])},this)},b.prototype.on=function(a,b){a in this.actions||(this.actions[a]=[]),this.actions[a].push(b)},b.prototype.off=function(a,b){if(a in this.actions){var c=this.actions[a].indexOf(b);-1!==c&&this.actions[a].splice(c,1)}},b.prototype.trigger=function(a,b){b=Array.prototype.slice.call(arguments,1),a in this.actions&&this.actions[a].length>0&&this.actions[a].forEach(function(c){"object"==typeof c?c.handleEvent({type:"psv:"+a,args:b}):c.apply(this,b)},this)},c.prototype.create=function(){this.container=document.createElement("div"),this.psv.container.appendChild(this.container)},c.prototype.destroy=function(){this.psv.container.removeChild(this.container),this.container=null,this.psv=null},d.prototype.create=function(){this.container=document.createElement("div"),this.container.className="psv-loader",this.psv.container.appendChild(this.container),this.canvas=document.createElement("canvas"),this.canvas.className="loader-canvas",this.canvas.width=this.container.clientWidth,this.canvas.height=this.container.clientWidth,this.container.appendChild(this.canvas),this.tickness=(this.container.offsetWidth-this.container.clientWidth)/2;var a;if(this.psv.config.loading_img?(a=document.createElement("img"),a.className="loader-image",a.src=this.psv.config.loading_img):this.psv.config.loading_txt&&(a=document.createElement("div"),a.className="loader-text",a.innerHTML=this.psv.config.loading_txt),a){var b=Math.round(Math.sqrt(2*Math.pow(this.canvas.width/2-this.tickness/2,2)));a.style.maxWidth=b+"px",a.style.maxHeight=b+"px",this.container.appendChild(a)}},d.prototype.destroy=function(){this.psv.container.removeChild(this.container),this.psv=null,this.container=null},d.prototype.setProgress=function(a){var b=this.canvas.getContext("2d");b.clearRect(0,0,this.canvas.width,this.canvas.height),b.lineWidth=this.tickness,b.strokeStyle=p.getStyle(this.container,"color"),b.beginPath(),b.arc(this.canvas.width/2,this.canvas.height/2,this.canvas.width/2-this.tickness/2,-Math.PI/2,a/100*2*Math.PI-Math.PI/2),b.stroke()},e.prototype=Object.create(c.prototype),e.prototype.constructor=e,e.publicMethods=["addMarker","removeMarker","updateMarker","getMarker","getCurrentMarker","gotoMarker","hideMarker","showMarker","toggleMarker"],e.prototype.create=function(){c.prototype.create.call(this),this.container.className="psv-hud",this.container.addEventListener("mouseenter",this,!0),this.container.addEventListener("mouseleave",this,!0),this.psv.on("_click",this),this.psv.on("render",this)},e.prototype.destroy=function(){this.container.removeEventListener("mouseenter",this),this.container.removeEventListener("mouseleave",this),this.psv.off("_click",this),this.psv.off("render",this),c.prototype.destroy.call(this)},e.prototype.handleEvent=function(a){switch(a.type){case"mouseenter":this._onMouseEnter(a);break;case"mouseleave":this._onMouseLeave(a);break;case"psv:_click":this._onClick(a.args[0]);break;case"psv:render":this.updatePositions()}},e.prototype.addMarker=function(a,b){if(!a.id)throw new o("missing marker id");if(this.markers[a.id])throw new o('marker "'+a.id+'" already exists');if(!a.image&&!a.html)throw new o("missing marker image/html");if(a.image&&(!a.width||!a.height))throw new o("missing marker width/height");if(!(a.hasOwnProperty("x")&&a.hasOwnProperty("y")||a.hasOwnProperty("latitude")&&a.hasOwnProperty("longitude")))throw new o("missing marker position, latitude/longitude or x/y");return a.$el=document.createElement("div"),a.$el.id="psv-marker-"+a.id,a.$el.className="psv-marker",this.markers[a.id]=a,this.container.appendChild(a.$el),this.updateMarker(p.clone(a),b)},e.prototype.getMarker=function(a){var b="object"==typeof a?a.id:a;if(!this.markers[b])throw new o('cannot find marker "'+b+'"');return this.markers[b]},e.prototype.getCurrentMarker=function(){return this.currentMarker},e.prototype.updateMarker=function(c,d){var e=this.getMarker(c);e.className&&e.$el.classList.remove(e.className),e.tooltip&&e.$el.classList.remove("has-tooltip"),delete c.$el,c=p.deepmerge(e,c),c.position2D=null,c.className&&c.$el.classList.add(c.className),c.tooltip&&(c.$el.classList.add("has-tooltip"),"string"==typeof c.tooltip&&(c.tooltip={content:c.tooltip}));var f=c.$el.style;if(c.width&&c.height?(f.width=c.width+"px",f.height=c.height+"px",c.dynamicSize=!1):c.dynamicSize=!0,c.style&&Object.getOwnPropertyNames(c.style).forEach(function(a){f[a]=c.style[a]}),c.image?f.backgroundImage="url("+c.image+")":c.$el.innerHTML=c.html,c.anchor=p.parsePosition(c.anchor),c.hasOwnProperty("x")&&c.hasOwnProperty("y")){var g=c.x/this.psv.prop.size.image_width*b.TwoPI,h=c.y/this.psv.prop.size.image_height*b.PI;c.longitude=g>=b.PI?g-b.PI:g+b.PI,c.latitude=b.HalfPI-h}return c.position3D=new a.Vector3(-Math.cos(c.latitude)*Math.sin(c.longitude),Math.sin(c.latitude),Math.cos(c.latitude)*Math.cos(c.longitude)),c.hasOwnProperty("visible")||(c.visible=!0),c.$el.psvMarker=c,this.markers[c.id]=c,d!==!1&&this.updatePositions(),c},e.prototype.removeMarker=function(a,b){a=this.getMarker(a),a.$el.parentNode.removeChild(a.$el),delete this.markers[a.id],b!==!1&&this.updatePositions()},e.prototype.gotoMarker=function(a,b){a=this.getMarker(a),this.psv.animate(a.longitude,a.latitude,b)},e.prototype.hideMarker=function(a){this.getMarker(a).visible=!1,this.updatePositions()},e.prototype.showMarker=function(a){this.getMarker(a).visible=!0,this.updatePositions()},e.prototype.toggleMarker=function(a){this.getMarker(a).visible^=!0,this.updatePositions()},e.prototype.updatePositions=function(){this.psv.camera.updateProjectionMatrix();for(var a in this.markers){var b=this.markers[a],c=this._getMarkerPosition(b);this._isMarkerVisible(b,c)?(b.position2D=c,b.$el.style.transform="translate3D("+c.left+"px, "+c.top+"px, 0px)",b.$el.classList.contains("visible")||b.$el.classList.add("visible")):(b.position2D=null,b.$el.classList.remove("visible"))}},e.prototype._isMarkerVisible=function(a,b){return a.visible&&a.position3D.dot(this.psv.prop.direction)>0&&b.left+a.width>=0&&b.left-a.width<=this.psv.prop.size.width&&b.top+a.height>=0&&b.top-a.height<=this.psv.prop.size.height},e.prototype._getMarkerPosition=function(a){if(a.dynamicSize){a.$el.classList.add("transparent");var b=a.$el.getBoundingClientRect();a.$el.classList.remove("transparent"),a.width=b.right-b.left,a.height=b.bottom-b.top}var c=a.position3D.clone();return c.project(this.psv.camera),{top:(1-c.y)/2*this.psv.prop.size.height-a.height*a.anchor.top,left:(c.x+1)/2*this.psv.prop.size.width-a.width*a.anchor.left}},e.prototype._onMouseEnter=function(a){if(a.target&&a.target.psvMarker&&a.target.psvMarker.tooltip){var b=a.target.psvMarker;this.psv.tooltip.showTooltip({content:b.tooltip.content,position:b.tooltip.position,top:b.position2D.top,left:b.position2D.left,marker:b})}},e.prototype._onMouseLeave=function(a){a.target&&a.target.psvMarker&&this.psv.tooltip.hideTooltip()},e.prototype._onClick=function(a){if(!this.psv.prop.moved){var b;a.target&&(b=p.getClosest(a.target,".psv-marker"))&&b.psvMarker?(this.currentMarker=b.psvMarker,this.psv.trigger("select-marker",b.psvMarker),a.preventDefault()):this.currentMarker&&(this.currentMarker=null,this.psv.trigger("unselect-marker")),b&&b.psvMarker&&b.psvMarker.content?this.psv.panel.showPanel(b.psvMarker.content):this.psv.panel.prop.opened&&(a.preventDefault(),this.psv.panel.hidePanel())}},f.prototype=Object.create(c.prototype),f.prototype.constructor=f,f.publicMethods=["showPanel","hidePanel"],f.prototype.create=function(){c.prototype.create.call(this),this.container.className="psv-panel",this.container.innerHTML='
',this.content=this.container.querySelector(".content");var a=this.container.querySelector(".close-button");a.addEventListener("click",this.hidePanel.bind(this)),this.psv.config.mousewheel&&this.container.addEventListener(p.mouseWheelEvent(),function(a){a.stopPropagation()});var b=this.container.querySelector(".resizer");b.addEventListener("mousedown",this),b.addEventListener("touchstart",this),this.psv.container.addEventListener("mouseup",this),this.psv.container.addEventListener("touchend",this),this.psv.container.addEventListener("mousemove",this),this.psv.container.addEventListener("touchmove",this)},f.prototype.destroy=function(){this.psv.container.removeEventListener("mousemove",this),this.psv.container.removeEventListener("touchmove",this),this.psv.container.removeEventListener("mouseup",this),this.psv.container.removeEventListener("touchend",this),c.prototype.destroy.call(this)},f.prototype.handleEvent=function(a){switch(a.type){case"mousedown":this._onMouseDown(a);break;case"touchstart":this._onTouchStart(a);break;case"mousemove":this._onMouseMove(a);break;case"touchmove":this._onMouseMove(a);break;case"mouseup":this._onMouseUp(a);break;case"touchend":this._onMouseUp(a)}},f.prototype.showPanel=function(a,b){this.content.innerHTML=a,this.content.scrollTop=0,this.container.classList.add("open"),b?this.content.classList.contains("no-margin")||this.content.classList.add("no-margin"):this.content.classList.remove("no-margin"),this.prop.opened=!0,this.psv.trigger("open-panel")},f.prototype.hidePanel=function(){this.prop.opened=!1,this.container.classList.remove("open"),this.psv.trigger("close-panel")},f.prototype._onMouseDown=function(a){a.stopPropagation(),this._startResize(a)},f.prototype._onTouchStart=function(a){a.stopPropagation(),this._startResize(a.changedTouches[0])},f.prototype._startResize=function(a){this.prop.mouse_x=parseInt(a.clientX),this.prop.mouse_y=parseInt(a.clientY),this.prop.mousedown=!0,this.content.classList.add("no-interaction")},f.prototype._onMouseUp=function(a){this.prop.mousedown&&(a.stopPropagation(),this.prop.mousedown=!1,this.content.classList.remove("no-interaction"))},f.prototype._onMouseMove=function(a){this.prop.mousedown&&(a.stopPropagation(),this._resize(a))},f.prototype._onTouchMove=function(a){this.prop.mousedown&&(a.stopPropagation(),this._resize(a.changedTouches[0]))},f.prototype._resize=function(a){var b=parseInt(a.clientX),c=parseInt(a.clientY);this.container.style.width=this.container.offsetWidth-(b-this.prop.mouse_x)+"px",this.prop.mouse_x=b,this.prop.mouse_y=c},g.prototype=Object.create(c.prototype),g.prototype.constructor=g,g.publicMethods=["showTooltip","hideTooltip"],g.leftMap={0:"left",.5:"center",1:"right"},g.topMap={0:"top",.5:"center",1:"bottom"},g.prototype.create=function(){c.prototype.create.call(this),this.container.innerHTML='
',this.container.className="psv-tooltip",this.container.style.top="-1000px",this.container.style.left="-1000px",this.psv.on("render",this)},g.prototype.destroy=function(){this.psv.off("render",this),c.prototype.destroy.call(this)},g.prototype.handleEvent=function(a){switch(a.type){case"psv:render":this.hideTooltip()}},g.prototype.showTooltip=function(a){var b=this.container,c=b.querySelector(".content"),d=b.querySelector(".arrow");if(a.position||(a.position=["top","center"]),a.marker||(a.marker={width:0,height:0}),"string"==typeof a.position){var e=p.parsePosition(a.position);if(!(e.left in g.leftMap&&e.top in g.topMap))throw new o('unable to parse tooltip position "'+tooltip.position+'"');a.position=[g.topMap[e.top],g.leftMap[e.left]]}if("center"==a.position[0]&&"center"==a.position[1])throw new o('unable to parse tooltip position "center center"');b.className="psv-tooltip",a.className&&b.classList.add(a.className),c.innerHTML=a.content,b.style.top="0px",b.style.left="0px";var f=b.getBoundingClientRect(),h={posClass:a.position.slice(),width:f.right-f.left,height:f.bottom-f.top,top:0,left:0,arrow_top:0,arrow_left:0};this._computeTooltipPosition(h,a);var i=!1;h.topthis.psv.prop.size.height-this.config.offset&&(h.posClass[0]="top",i=!0),h.leftthis.psv.prop.size.width-this.config.offset&&(h.posClass[1]="left",i=!0),i&&this._computeTooltipPosition(h,a),b.style.top=h.top+"px",b.style.left=h.left+"px",d.style.top=h.arrow_top+"px",d.style.left=h.arrow_left+"px",b.classList.add(h.posClass.join("-"));var j=this;setTimeout(function(){b.classList.add("visible"),j.psv.trigger("show-tooltip")},100)},g.prototype.hideTooltip=function(){this.container.classList.remove("visible"),this.psv.trigger("hide-tooltip");var a=this;setTimeout(function(){a.container.style.top="-1000px",a.container.style.left="-1000px"},100)},g.prototype._computeTooltipPosition=function(a,b){var c=!1;switch(a.posClass[0]){case"bottom":a.top=b.top+b.marker.height+this.config.offset+this.config.arrow_size,a.arrow_top=2*-this.config.arrow_size,c=!0;break;case"center":a.top=b.top+b.marker.height/2-a.height/2,a.arrow_top=a.height/2-this.config.arrow_size;break;case"top":a.top=b.top-a.height-this.config.offset-this.config.arrow_size,a.arrow_top=a.height,c=!0}switch(a.posClass[1]){case"right":c?(a.left=b.left,a.arrow_left=b.marker.width/2-this.config.arrow_size):(a.left=b.left+b.marker.width+this.config.offset+this.config.arrow_size,a.arrow_left=2*-this.config.arrow_size);break;case"center":a.left=b.left+b.marker.width/2-a.width/2,a.arrow_left=a.width/2-this.config.arrow_size;break;case"left":c?(a.left=b.left-a.width+b.marker.width,a.arrow_left=a.width-b.marker.width/2-this.config.arrow_size):(a.left=b.left-a.width-this.config.offset-this.config.arrow_size,a.arrow_left=a.width)}},h.prototype=Object.create(c.prototype),h.prototype.constructor=h,h.publicMethods=["setCaption"],h.DEFAULTS={autorotate:!0,zoom:!0,fullscreen:!0,download:!0,markers:!0},h.prototype.create=function(){c.prototype.create.call(this),this.container.className="psv-navbar",this.config.autorotate&&this.buttons.push(new j(this)), +this.config.zoom&&this.buttons.push(new l(this)),this.config.download&&this.buttons.push(new m(this)),this.config.markers&&this.buttons.push(new n(this)),this.config.fullscreen&&this.buttons.push(new k(this)),this.caption=document.createElement("div"),this.caption.className="caption",this.container.appendChild(this.caption),this.setCaption(this.psv.config.caption)},h.prototype.destroy=function(){this.buttons.forEach(function(a){a.destroy()}),this.buttons.length=0,c.prototype.destroy.call(this)},h.prototype.setCaption=function(a){a?(this.caption.style.display="block",this.caption.innerHTML=a):this.caption.style.display="none"},i.prototype.create=function(){this.button=document.createElement("div"),this.button.className="psv-button",this.navbar.container.appendChild(this.button)},i.prototype.destroy=function(){this.navbar.container.removeChild(this.button),this.navbar=null,this.psv=null,this.button=null},i.prototype.toggleActive=function(a){a?this.button.classList.add("active"):this.button.classList.remove("active")},j.prototype=Object.create(i.prototype),j.prototype.constructor=j,j.prototype.create=function(){i.prototype.create.call(this),this.button.classList.add("autorotate-button"),this.button.title=this.psv.config.lang.autorotate;var a=document.createElement("div");a.className="sphere",this.button.appendChild(a);var b=document.createElement("div");b.className="equator",this.button.appendChild(b),this.button.addEventListener("click",this.psv.toggleAutorotate.bind(this.psv)),this.psv.on("autorotate",this.toggleActive.bind(this))},k.prototype=Object.create(i.prototype),k.prototype.constructor=k,k.prototype.create=function(){i.prototype.create.call(this),this.button.classList.add("fullscreen-button"),this.button.title=this.psv.config.lang.fullscreen,this.button.appendChild(document.createElement("div")),this.button.appendChild(document.createElement("div")),this.button.addEventListener("click",this.psv.toggleFullscreen.bind(this.psv)),this.psv.on("fullscreen-updated",this)},k.prototype.destroy=function(){this.psv.off("fullscreen-updated",this),i.prototype.destroy.call(this)},k.prototype.handleEvent=function(a){switch(a.type){case"psv:fullscreen-updated":this.toggleActive()}},l.prototype=Object.create(i.prototype),l.prototype.constructor=l,l.prototype.create=function(){i.prototype.create.call(this),this.button.classList.add("zoom-button");var a=document.createElement("div");a.className="minus",a.title=this.psv.config.lang.zoomOut,a.innerHTML=b.ICONS["zoom-out.svg"],this.button.appendChild(a);var c=document.createElement("div");c.className="range",this.button.appendChild(c),this.zoom_range=document.createElement("div"),this.zoom_range.className="line",this.zoom_range.title=this.psv.config.lang.zoom,c.appendChild(this.zoom_range),this.zoom_value=document.createElement("div"),this.zoom_value.className="handle",this.zoom_value.title=this.psv.config.lang.zoom,this.zoom_range.appendChild(this.zoom_value);var d=document.createElement("div");d.className="plus",d.title=this.psv.config.lang.zoomIn,d.innerHTML=b.ICONS["zoom-in.svg"],this.button.appendChild(d),this.zoom_range.addEventListener("mousedown",this),this.zoom_range.addEventListener("touchstart",this),this.psv.container.addEventListener("mousemove",this),this.psv.container.addEventListener("touchmove",this),this.psv.container.addEventListener("mouseup",this),this.psv.container.addEventListener("touchend",this),a.addEventListener("click",this.psv.zoomOut.bind(this.psv)),d.addEventListener("click",this.psv.zoomIn.bind(this.psv)),this.psv.on("zoom-updated",this);var e=this;setTimeout(function(){e._moveZoomValue(e.psv.prop.zoom_lvl)},0)},l.prototype.destroy=function(){this.psv.container.removeEventListener("mousemove",this),this.psv.container.removeEventListener("touchmove",this),this.psv.container.removeEventListener("mouseup",this),this.psv.container.removeEventListener("touchend",this),this.psv.off("zoom-updated",this),i.prototype.destroy.call(this)},l.prototype.handleEvent=function(a){switch(a.type){case"mousedown":this._initZoomChangeWithMouse(a);break;case"touchstart":this._initZoomChangeByTouch(a);break;case"mousemove":this._changeZoomWithMouse(a);break;case"touchmove":this._changeZoomByTouch(a);break;case"mouseup":this._stopZoomChange(a);break;case"touchend":this._stopZoomChange(a);break;case"psv:zoom-updated":this._moveZoomValue(a.args[0])}},l.prototype._moveZoomValue=function(a){this.zoom_value.style.left=a/100*this.zoom_range.offsetWidth-this.zoom_value.offsetWidth/2+"px"},l.prototype._initZoomChangeWithMouse=function(a){this.prop.mousedown=!0,this._changeZoom(a.clientX)},l.prototype._initZoomChangeByTouch=function(a){this.prop.mousedown=!0,this._changeZoom(a.changedTouches[0].clientX)},l.prototype._stopZoomChange=function(a){this.prop.mousedown=!1},l.prototype._changeZoomWithMouse=function(a){a.preventDefault(),this._changeZoom(a.clientX)},l.prototype._changeZoomByTouch=function(a){a.preventDefault(),this._changeZoom(a.changedTouches[0].clientX)},l.prototype._changeZoom=function(a){if(this.prop.mousedown){var b=parseInt(a)-this.zoom_range.getBoundingClientRect().left,c=b/this.zoom_range.offsetWidth*100;this.psv.zoom(c)}},m.prototype=Object.create(i.prototype),m.prototype.constructor=m,m.prototype.create=function(){i.prototype.create.call(this),this.button.classList.add("download-button"),this.button.title=this.psv.config.lang.download,this.button.appendChild(document.createElement("div")),this.button.addEventListener("mouseenter",this.toggleActive.bind(this,!0)),this.button.addEventListener("mouseleave",this.toggleActive.bind(this,!1)),this.button.addEventListener("click",this.download.bind(this))},m.prototype.download=function(){var a=document.createElement("a");a.href=this.psv.config.panorama,a.download=this.psv.config.panorama,this.psv.container.appendChild(a),a.click()},n.prototype=Object.create(i.prototype),n.prototype.constructor=n,n.prototype.create=function(){i.prototype.create.call(this),this.button.classList.add("markers-button"),this.button.title=this.psv.config.lang.markers,this.button.innerHTML=b.ICONS["pin.svg"],this.button.addEventListener("click",this.toggleMarkers.bind(this)),this.psv.on("open-panel",this),this.psv.on("close-panel",this)},n.prototype.destroy=function(){this.psv.off("open-panel",this),this.psv.off("close-panel",this),i.prototype.destroy.call(this)},n.prototype.handleEvent=function(a){switch(a.type){case"psv:open-panel":this._onPanelOpened();break;case"psv:close-panel":this._onPanelClosed()}},n.prototype.toggleMarkers=function(){this.prop.panelOpened?this.hideMarkers():this.showMarkers()},n.prototype.showMarkers=function(){var a='

'+this.psv.config.lang.markers+"

    ";for(var b in this.psv.hud.markers){var c=this.psv.hud.markers[b],d=c.id;c.html?d=c.html:c.tooltip&&(d="string"==typeof c.tooltip?c.tooltip:c.tooltip.content),a+='
  • ',c.image&&(a+=''),a+="

    "+d+"

  • "}a+="
",this.prop.panelOpening=!0,this.psv.panel.showPanel(a,!0),this.psv.panel.container.querySelector(".psv-markers-list").addEventListener("click",this._onClickItem.bind(this))},n.prototype.hideMarkers=function(){this.psv.panel.hidePanel()},n.prototype._onClickItem=function(a){var b;a.target&&(b=p.getClosest(a.target,"li"))&&b.dataset.psvMarker&&(this.psv.hud.gotoMarker(b.dataset.psvMarker,1e3),this.psv.panel.hidePanel())},n.prototype._onPanelOpened=function(){this.prop.panelOpening?(this.prop.panelOpening=!1,this.prop.panelOpened=!0):this.prop.panelOpened=!1,this.toggleActive(this.prop.panelOpened)},n.prototype._onPanelClosed=function(){this.prop.panelOpened=!1,this.prop.panelOpening=!1,this.toggleActive(this.prop.panelOpened)},o.prototype=Object.create(Error.prototype),o.prototype.name="PSVError",o.prototype.constructor=o;var p={};return p.isCanvasSupported=function(){var a=document.createElement("canvas");return!(!a.getContext||!a.getContext("2d"))},p.isWebGLSupported=function(){var a=document.createElement("canvas");return!(!window.WebGLRenderingContext||!a.getContext("webgl"))},p.getMaxTextureWidth=function(){var a=document.createElement("canvas"),b=a.getContext("webgl");return b.getParameter(b.MAX_TEXTURE_SIZE)},p.hasParent=function(a,b){do if(a===b)return!0;while(a=a.parentNode);return!1},p.getClosest=function(a,b){var c=a.matches||a.msMatchesSelector;do if(c.bind(a)(b))return a;while(a=a.parentElement);return null},p.mouseWheelEvent=function(){return"onwheel"in document.createElement("div")?"wheel":void 0!==document.onmousewheel?"mousewheel":"DOMMouseScroll"},p.fullscreenEvent=function(){var a={exitFullscreen:"fullscreenchange",webkitExitFullscreen:"webkitfullscreenchange",mozCancelFullScreen:"mozfullscreenchange",msExitFullscreen:"msFullscreenEnabled"};for(var b in a)if(b in document)return a[b];return"fullscreenchange"},p.stayBetween=function(a,b,c){return Math.max(b,Math.min(c,a))},p.getXMPValue=function(a,b){var c,d;return-1!==(c=a.indexOf(""))&&-1!==(d=a.indexOf(""))?a.substring(c,d).replace("",""):-1!==(c=a.indexOf("GPano:"+b))&&-1!==(d=a.indexOf('"',c))?a.substring(c+b.length+8,d):null},p.isFullscreenEnabled=function(){return document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement},p.requestFullscreen=function(a){(a.requestFullscreen||a.mozRequestFullScreen||a.webkitRequestFullscreen||a.msRequestFullscreen).call(a)},p.exitFullscreen=function(a){(document.exitFullscreen||document.mozCancelFullScreen||document.webkitExitFullscreen||document.msExitFullscreen).call(document)},p.getStyle=function(a,b){return window.getComputedStyle(a,null)[b]},p.parsePosition=function(a){if(!a)return{top:.5,left:.5};if("object"==typeof a)return a;var b=document.createElement("div");document.body.appendChild(b),b.style.backgroundPosition=a;var c=p.getStyle(b,"background-position").match(/^([0-9.]+)% ([0-9.]+)%$/);return document.body.removeChild(b),{left:c[1]/100,top:c[2]/100}},p.deepmerge=function(a,b){var c=Array.isArray(b),d=c&&[]||{};return c?(a=a||[],d=d.concat(a),b.forEach(function(b,c){"undefined"==typeof d[c]?d[c]=b:"object"==typeof b?d[c]=p.deepmerge(a[c],b):-1===a.indexOf(b)&&d.push(b)})):(a&&"object"==typeof a&&Object.keys(a).forEach(function(b){d[b]=a[b]}),Object.keys(b).forEach(function(c){"object"==typeof b[c]&&b[c]&&a[c]?d[c]=p.deepmerge(a[c],b[c]):d[c]=b[c]})),d},p.clone=function(a){return p.deepmerge({},a)},b.ICONS["pin.svg"]='',b.ICONS["zoom-in.svg"]='',b.ICONS["zoom-out.svg"]='',b}); \ No newline at end of file diff --git a/example/index.htm b/example/index.htm index 821c33ea0..8b9afd759 100644 --- a/example/index.htm +++ b/example/index.htm @@ -14,9 +14,8 @@ padding: 0; } #photosphere { - width: 75vw; - height: 75vh; - margin: 12.5vh 12.5vw; + width: 100%; + height: 100%; } @@ -61,6 +60,7 @@

Header Level 3

var PSV = new PhotoSphereViewer({ panorama: 'Bryce-Canyon-National-Park-Mark-Doliner.jpg', container: 'photosphere', + loading_img: 'photosphere-logo.gif', navbar: true, //'fullscreen zoom', caption: 'Bryce Canyon National Park © Mark Doliner', tilt_up_max: Math.PI/4, diff --git a/example/photosphere-logo.gif b/example/photosphere-logo.gif new file mode 100644 index 000000000..199ebdd87 Binary files /dev/null and b/example/photosphere-logo.gif differ diff --git a/package.json b/package.json index 2deb878e1..a89d2368c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Photo-Sphere-Viewer", - "version": "3.0.1", + "version": "3.1.0", "authors": [ { "name": "Jérémy Heleine", diff --git a/src/js/PSVNavBarMarkersButton.js b/src/js/PSVNavBarMarkersButton.js index db5d3dd27..2e5e2adaf 100644 --- a/src/js/PSVNavBarMarkersButton.js +++ b/src/js/PSVNavBarMarkersButton.js @@ -123,6 +123,7 @@ PSVNavBarMarkersButton.prototype._onClickItem = function(e) { var li; if (e.target && (li = PSVUtils.getClosest(e.target, 'li')) && li.dataset.psvMarker) { this.psv.hud.gotoMarker(li.dataset.psvMarker, 1000); + this.psv.panel.hidePanel(); } }; diff --git a/src/js/PSVTooltip.js b/src/js/PSVTooltip.js index 8126cc6e8..ac6ba939f 100644 --- a/src/js/PSVTooltip.js +++ b/src/js/PSVTooltip.js @@ -92,6 +92,10 @@ PSVTooltip.prototype.showTooltip = function(config) { config.position = [PSVTooltip.topMap[tempPos.top], PSVTooltip.leftMap[tempPos.left]]; } + if (config.position[0] == 'center' && config.position[1] == 'center') { + throw new PSVError('unable to parse tooltip position "center center"'); + } + t.className = 'psv-tooltip'; // reset the class if (config.className) { t.classList.add(config.className); diff --git a/src/js/PhotoSphereViewer.js b/src/js/PhotoSphereViewer.js index 9ef40977b..48cf3d7d0 100644 --- a/src/js/PhotoSphereViewer.js +++ b/src/js/PhotoSphereViewer.js @@ -165,8 +165,8 @@ PhotoSphereViewer.prototype.destroy = function() { if (this.config.mousemove) { this.hud.container.removeEventListener('mousedown', this); this.hud.container.removeEventListener('touchstart', this); - this.hud.container.removeEventListener('mouseup', this); - this.hud.container.removeEventListener('touchend', this); + window.removeEventListener('mouseup', this); + window.removeEventListener('touchend', this); this.hud.container.removeEventListener('mousemove', this); this.hud.container.removeEventListener('touchmove', this); } @@ -475,8 +475,8 @@ PhotoSphereViewer.prototype._bindEvents = function() { this.hud.container.style.cursor = 'move'; this.hud.container.addEventListener('mousedown', this); this.hud.container.addEventListener('touchstart', this); - this.hud.container.addEventListener('mouseup', this); - this.hud.container.addEventListener('touchend', this); + window.addEventListener('mouseup', this); + window.addEventListener('touchend', this); this.hud.container.addEventListener('mousemove', this); this.hud.container.addEventListener('touchmove', this); }