///
///
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 = '';
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();
};