/// /// var GigapixelArtZoomViewer = function (parentId, trackEventCallback) { 'use strict'; var self = this; var parentDiv = document.getElementById(parentId); var viewer, cameraController, hotspots, overlayTextElem, cardElem, currentHotspot; function trackEvent(name) { if (trackEventCallback) { trackEventCallback(name); } } function showOverlayText(text) { if (overlayTextElem == null) { overlayTextElem = $('
').css({ position: 'absolute', top: 0, bottom: 0, left: 0, right: 0, margin: 'auto', height: 100, 'text-align': 'center', }); $(parentDiv).append(overlayTextElem); } $(overlayTextElem).html(text); } function hideOverlayText() { if (overlayTextElem) { $(overlayTextElem).remove(); overlayTextElem = null; } } function showCard(hotspot) { if (currentHotspot != hotspot) { currentHotspot = hotspot; if (cardElem == null) { cardElem = $('').appendTo(parentDiv); } if (hotspot.link) { cardElem.attr('id', 'viewerCard' + hotspot.id).attr('href', hotspot.link).click(function () { trackEvent('viewerCard'); }); } else { cardElem.removeAttr('href'); } var html = '
{title}
{names}
More...
'; html = html.replace('{thumb}', 'thumbnails/' + hotspot.thumb + '.jpg'); html = html.replace('{title}', hotspot.title); var names = hotspot.names && hotspot.names.length > 0 ? hotspot.names.join(', ') : null; html = html.replace('{names}', names && (names != hotspot.title) ? '
' + names + '
' : ''); cardElem.html(html).fadeIn(); if (!hotspot.link) { $('.more', cardElem).hide(); } trackEvent('showViewerCard'); trackEvent('showViewerCard' + hotspot.id); } } function hideCard() { $(cardElem).fadeOut(); currentHotspot = null; } function resizeHandler() { self.updateViewportSize(); } function createButton(parent, html, clickHandler) { return $(html).appendTo(parent).on('dragstart', false).on('click', clickHandler); } function createHoldButton(parent, html, mouseDownHandler, mouseUpHandler) { var button = $(html).appendTo(parent).on('click dragstart', false); return button.on('mousedown touchstart', function (event) { if (event.which == 1) { button.on('mouseleave.holdButton touchleave.holdButton', mouseUpHandler).on('mouseenter.holdButton touchenter.holdButton', mouseDownHandler); $(document).on('mouseup.holdButton touchend.holdButton', function (event) { if (event.which == 1) { button.off('.holdButton'); $(document).off('.holdButton'); return mouseUpHandler(); } }); event.preventDefault(); return mouseDownHandler(); } }); } function createViewer(rml, options) { var renderer = (options && options.serverSupportsCors) ? 'auto' : 'css'; viewer = new RwwViewer(parentDiv, { rml: rml, renderer: renderer, // Note: default or 'webgl' will not work until Photosynth tiles are served with CORS headers. hideAttribution: true }); cameraController = viewer.getActiveCameraController(); // Limit the view to stay within the angular bounds of the panorama. cameraController.setKeepViewWithinBounds(true); var initPose = options ? options.initPose : null; if (initPose && initPose.fov != null) { cameraController.setVerticalFov(initPose.fov); } else { cameraController.setVerticalFov(MathHelper.degreesToRadians(70)); } if (initPose && initPose.topPitch != null && initPose.leftHeading != null) { var aspectRatio = $(parentDiv).width() / $(parentDiv).height(); var verticalFov = cameraController.getVerticalFov(); var horizontalFov = Viewport.convertVerticalToHorizontalFieldOfView(aspectRatio, verticalFov); cameraController.setPitchAndHeading(initPose.topPitch - verticalFov / 2, initPose.leftHeading + horizontalFov / 2, false); } cameraController.viewChangeCallback = function () { var aspectRatio = $(parentDiv).width() / $(parentDiv).height(); var pitchHeading = cameraController.getPitchAndHeading(); var verticalFov = cameraController.getVerticalFov(); var horizontalFov = verticalFov * aspectRatio; var smallerFov = Math.min(horizontalFov, verticalFov); var screenLeft = pitchHeading[1] - horizontalFov / 2; var screenRight = pitchHeading[1] + horizontalFov / 2; var screenTop = pitchHeading[0] + verticalFov / 2; var screenBottom = pitchHeading[0] - verticalFov / 2; var closestHotspot = null; var closestSquaredDistance = 1e38; var minSizeFactor = 0.01; var maxSizeFactor = 1.7; var hotspotScale = 0.35; var twoPi = 2 * Math.PI; for (var i in hotspots) { // See if the current field of view is close // enough to the hotspot size. var hotspot = hotspots[i]; var size = hotspot.pos[2]; var sizeFactor = smallerFov / size; if (sizeFactor >= minSizeFactor && sizeFactor <= maxSizeFactor) { // See if the hotspot bounds intersect the screen bounds. var hotspotHeading = hotspot.pos[1]; hotspotHeading -= twoPi * Math.round((hotspotHeading - pitchHeading[1]) / twoPi); var hotspotHalfSize = hotspotScale * size; var hotspotLeft = hotspotHeading - hotspotHalfSize; var hotspotRight = hotspotHeading + hotspotHalfSize; var hotspotTop = hotspot.pos[0] + hotspotHalfSize; var hotspotBottom = hotspot.pos[0] - hotspotHalfSize; if (hotspotLeft < screenRight && hotspotRight > screenLeft && hotspotBottom < screenTop && hotspotTop > screenBottom) { // See if this is the closest hotspot to the screen center. var deltaPitch = hotspot.pos[0] - pitchHeading[0]; var deltaHeading = hotspotHeading - pitchHeading[1]; var squaredDistance = deltaPitch * deltaPitch + deltaHeading * deltaHeading; if (squaredDistance < closestSquaredDistance) { closestHotspot = hotspot; closestSquaredDistance = squaredDistance; } } } } if (closestHotspot) { showCard(closestHotspot); } else { hideCard(); } }; window.addEventListener('resize', resizeHandler, false); // Disable press-and-hold visuals and context menus. $(parentDiv).on('MSHoldVisual contextmenu', function (event) { event.preventDefault(); }); // Add buttons. var buttonContainer = $('
').appendTo(parentDiv); createButton(buttonContainer, '', function () { cameraController.zoomIn(); trackEvent('zoomInButton'); return false; }); createButton(buttonContainer, '', function () { cameraController.zoomOut(); trackEvent('zoomOutButton'); return false; }); createHoldButton(buttonContainer, '', function () { cameraController.startRotateHeading(-1); trackEvent('panLeftButton'); return false; }, function () { cameraController.stopRotateHeading(); return false; }); createHoldButton(buttonContainer, '', function () { cameraController.startRotatePitch(1); trackEvent('panUpButton'); return false; }, function () { cameraController.stopRotatePitch(); return false; }); createHoldButton(buttonContainer, '', function () { cameraController.startRotatePitch(-1); trackEvent('panDownButton'); return false; }, function () { cameraController.stopRotatePitch(); return false; }); createHoldButton(buttonContainer, '', function () { cameraController.startRotateHeading(1); trackEvent('panRightButton'); return false; }, function () { cameraController.stopRotateHeading(); return false; }); if ($.fullscreen.isNativelySupported()) { var fullScreenButtonContainer = $('
').appendTo(buttonContainer); createButton(fullScreenButtonContainer, '', function () { $(document.body).fullscreen(); trackEvent('fullScreenButton'); return false; }); createButton(fullScreenButtonContainer, '', function () { $.fullscreen.exit(); trackEvent('exitFullScreenButton'); return false; }).hide(); $(document).on('fscreenchange', function (event, isFullScreen, fullScreenElement) { isFullScreen = isFullScreen && fullScreenElement === document.body; $('#fullScreenButton').toggle(!isFullScreen); $('#exitFullScreenButton').toggle(isFullScreen); trackEvent(isFullScreen ? 'fullScreenEnter' : 'fullScreenExit'); }); } } function findArtist(gigapixelArtZoomData, id) { for (var i in gigapixelArtZoomData.artists) { var artist = gigapixelArtZoomData.artists[i]; if (artist.id == id) { return artist; } } return null; } function createHotspots(gigapixelArtZoomData) { hotspots = []; for (var i in gigapixelArtZoomData.locations) { var location = gigapixelArtZoomData.locations[i]; if (location.pos.length == 3) { var artist = findArtist(gigapixelArtZoomData, location.id); var hotspot = { id: location.id, pos: location.pos, thumb: location.thumb, title: artist ? artist.title : location.id, names: artist ? artist.names : undefined, link: artist && (artist.desc.length || artist.piece.length) ? '#Artists/' + location.id : undefined }; hotspots.push(hotspot); } } } this.loadPano = function (jsonUri, gigapixelArtZoomData, options) { // Make sure the browser is supported. var serverSupportsCors = options && options.serverSupportsCors; if (!GigapixelArtZoomViewer.isBrowserSupported(serverSupportsCors)) { return; } if (!options.keepPrevPanoVisibleDuringLoading) { self.dispose(); showOverlayText('Loading...'); } PhotosynthRml.createFromSameDomainJsonUri(jsonUri, function (rml, error) { if (options.keepPrevPanoVisibleDuringLoading) { self.dispose(); } if (rml == null) { showOverlayText('There was an error loading the panorama.'); trackEvent('errorLoadingPanorama'); } else { hideOverlayText(); createHotspots(gigapixelArtZoomData); createViewer(rml, options); } }, null, options.photosynthServer, options.contentHostOverride); }; this.dispose = function () { if (viewer) { viewer.dispose(); viewer = null; } window.removeEventListener('resize', resizeHandler, false); hideOverlayText(); }; this.updateViewportSize = function () { if (viewer != null && viewer.setViewportSize != null) { viewer.setViewportSize(parentDiv.offsetWidth, parentDiv.offsetHeight); } }; this.zoom = function (scaleFactor) { cameraController.zoom(scaleFactor); }; this.getCameraPose = function () { var cameraPitchHeading = cameraController.getPitchAndHeading(); var cameraVerticalFov = cameraController.getVerticalFov(); return { pitch: cameraPitchHeading[0], heading: cameraPitchHeading[1], fov: cameraVerticalFov }; }; this.setCameraPose = function (pose, useAnimation, animationCompletedCallback) { if (useAnimation) { cameraController.animateToPose(pose.pitch, pose.heading, pose.fov, animationCompletedCallback); } else { cameraController.setPitchAndHeading(pose.pitch, pose.heading, false); cameraController.setVerticalFov(pose.fov, false); } }; this.getPoseFromInitialPose = function (initPose) { var aspectRatio = $(parentDiv).width() / $(parentDiv).height(); var verticalFov = initPose.fov; var horizontalFov = Viewport.convertVerticalToHorizontalFieldOfView(aspectRatio, verticalFov); var pitch = initPose.topPitch - verticalFov / 2; var heading = initPose.leftHeading + horizontalFov / 2; return { fov: verticalFov, pitch: pitch, heading: heading }; }; }; GigapixelArtZoomViewer.isBrowserSupported = function (serverSupportsCors) { return serverSupportsCors ? RendererCheckWebGL.isValidBrowser() || RendererCheckCSS3D.isValidBrowser() : RendererCheckCSS3D.isValidBrowser(); };