(function ($) {
//These variables can be changed
var smoothScroll = 5;
//Do not change these variables
var curShotDimensions;
var curZoomDimensions;
var moveInterval = null;
var mx, my;
var targetZoom;
var currentShotIndex = 0;
var maxPortraitImageWidth = 315;
var maxLandscapeImageWidth = 385;
var orientation = "portrait";
var totalImages = 0;
var thumbNailsLoaded = 0;
var navStartIndex = 0;
var navLiMargin = 10;
var isCarouselScrolling = false;
if (Next.Settings.UI.IsInnovationDesktopResponsiveEnabled) {
maxPortraitImageWidth = 364;
}
var isIeVersionOlderThan9 = jqueryUpgradeEnabledOnPage
? navigator.userAgent.toLowerCase().indexOf('msie') > -1 && document.documentMode < 9
: $.browser.msie && $.browser.version < 9;
var methods = {
///////////////////////Init/////////////////////
init: function (options) {
methods.Vars.$zoomComponent = this;
methods.Vars.Init(options);
methods.addShotControlHandlers();
methods.addCarouselScrolling();
//Add some handlers
if (methods.isMobile()) {
methods.buildForMobile();
} else if (!methods.Vars.SingleModalMode){
methods.addShotHandlers();
}
// Close SuperZoom or Zoom on esc key.
$(document).keyup(function (event) {
if (event.keyCode === 27) {
if ($(".SuperZoom").css("opacity") === 1) {
$(".SuperZoomBox").click();
} else if ($(".zoomBoxWrapper").hasClass("expanded")) {
$(".ZoomComponent .close").click();
}
}
});
if( Next.Settings.UI.IsInnovationDesktopResponsiveEnabled){
if (window.addEventListener) {
window.addEventListener('resize', function () {
//methods.updateSuperZoomBoxHeight();
if ($(".zoomBoxWrapper").hasClass("expanded")) {
$(".ZoomComponent .close").click();
}
});
}
}
},
Vars: {
shotData: null,
$zoomComponent: null,
$thumbnailNav: null,
$zoomBoxWrapper: null,
SingleModalMode: false,
SocialPLP: false,
StyleWithEnabled: false,
Init: function (options) {
var $this = this;
$this.shotData = shotData;
$this.$zoomBox = $(".zoomBox");
$this.$zoomBoxWrapper = $(".zoomBoxWrapper");
$this.$thumbnailNav = $(".ThumbNailNav");
if (options && options["SingleModalMode"] == true) {
$this.SingleModalMode = true;
maxPortraitImageWidth = 284;
maxLandscapeImageWidth = 284;
}
if (options && options["StyleWithEnabled"] == true) {
$this.StyleWithEnabled = true;
}
else {
$this.StyleWithEnabled = Next.Settings.Channel.EnableDesktopHorizontalStyleWith;
}
$this.SocialPLP = Next.Settings.UI.IsSocialPLPEnabled && window.isSocialPLPPage;
}
},
buildNavClip: function (currentItem, initialLoad,isDefaultStyle) {
methods.buildVideo(currentItem);
var $newUL = $("
");
var pageType = methods.Vars.$zoomComponent.attr("data-pagetype");
var media = [];
if (pageType === "shot") {
if (Next.Settings.Channel.EnableItemLevelSingleProductPage && typeof (currentItem.Media) !== "undefined" && currentItem.Media !== null && currentItem.Media.length > 1 && !initialLoad) {
media = currentItem.Media;
} else {
if (this.StyleWithEnabled && !isDefaultStyle) {
media = [{ name: $.trim(currentItem.ItemNumber).replace(/-/g, ""), level: "item" }];
}
else {
media = methods.Vars.shotData.Media;
}
}
}
if (pageType === "style" && ItemLevelImagery && currentItem.Media !== undefined && currentItem.Media !== null)
media = currentItem.Media;
//Set Book Media IF default item
var isDefaultItem = methods.IsDefaultItem(currentItem.ItemNumber);
if (Next.Settings.Channel.PliNewMediaFormatIsEnabled && Next.Settings.Channel.PliBehaviourIsEnabled) {
var currentItemNumber = ProductPage.RemoveDashFromItemNumber(currentItem.ItemNumber);
var defaultThumbImageForCurrentItem = [{ name: currentItemNumber, imageType: "", level: "", alsoItemLevel: "", outlet: "", pageNo: "", shotType: "", webType: "" }];
//For style pages
if (pageType === "style" && ItemLevelImagery) {
media = currentItem.Media || defaultThumbImageForCurrentItem;
} else {
if (isDefaultItem) {
//Media for default item
media = methods.Vars.shotData.Media;
} else {
//Media for current item selection
media = currentItem.Media || defaultThumbImageForCurrentItem;
}
}
}
if (media === undefined) {
media = [];
}
var formatAlt = null;
// Add colour to alt text if item level media, there is a colour and the colour does not already precede the title.
// This is insufficient because even the title is usually wrong:
if (currentItem.Media) {
formatAlt = currentItem.title;
if (currentItem.Colour && currentItem.title.indexOf(currentItem.Colour) === -1) {
formatAlt = currentItem.Colour + " " + currentItem.title;
}
}
var bookPathStructure;
if (Next.Settings.Channel.PliNewMediaFormatIsEnabled && Next.Settings.Channel.PliBehaviourIsEnabled) {
bookPathStructure = {
hrefPathTemplate: methods.Vars.shotData.ShotBasePath + "{book}" + methods.Vars.shotData.ShotViewPath + "{pageNumber}/{filetitle}.jpg",
relPathTemplate: methods.Vars.shotData.ShotBasePath + "{book}" + methods.Vars.shotData.ShotZoomPath + "{pageNumber}/{filetitle}.jpg",
srcPathTemplate: methods.Vars.shotData.ShotBasePath + "{book}" + methods.Vars.shotData.MiniShotPath + "{pageNumber}/{filetitle}.jpg"
};
} else {
bookPathStructure = {
hrefPathTemplate: methods.Vars.shotData.ShotBasePath + book + methods.Vars.shotData.ShotViewPath + pageNumber + "/" + "{filetitle}.jpg",
relPathTemplate: methods.Vars.shotData.ShotBasePath + book + methods.Vars.shotData.ShotZoomPath + pageNumber + "/" + "{filetitle}.jpg",
srcPathTemplate: methods.Vars.shotData.ShotBasePath + book + methods.Vars.shotData.MiniShotPath + pageNumber + "/" + "{filetitle}.jpg"
};
}
var itemPathStructure = {
hrefPathTemplate: window.cdnURL + "/Common/Items/Default/Default/ItemImages/AltItemShot/315x472/{filetitle}.jpg",
relPathTemplate: window.cdnURL + "/Common/Items/Default/Default/ItemImages/AltItemZoom/{filetitle}.jpg",
srcPathTemplate: window.cdnURL + "/Common/Items/Default/Default/ItemImages/AltItemThumb/{filetitle}.jpg"
};
if (Next.Settings.Channel.PliNewMediaFormatIsEnabled && Next.Settings.Channel.PliBehaviourIsEnabled) {
$(media).each(function () {
var img = this;
var struc = img.level === "book"
? bookPathStructure
: itemPathStructure;
//Default item
if (img.level === "book" && isDefaultItem) {
if (img.outlet && img.pageNo) {
updateShotImageStruc(img.outlet, img.pageNo);
} else {
updateShotImageStruc(book, pageNumber);
}
//Other items
} else if (img.level === "book") {
updateShotImageStruc(img.outlet, img.pageNo);
}
function updateShotImageStruc(book, pageNo) {
if (book.length > 0 && pageNo.length > 0) {
struc.hrefPathTemplate = struc.hrefPathTemplate.replace("{book}", book).replace("{pageNumber}", pageNo);
struc.relPathTemplate = struc.relPathTemplate.replace("{book}", book).replace("{pageNumber}", pageNo);
struc.srcPathTemplate = struc.srcPathTemplate.replace("{book}", book).replace("{pageNumber}", pageNo);
}
}
var $newLi = $(" ");
$newLi
.attr("data-shot-type", img.shotType)
.attr("data-level", img.level)
.attr("data-image-type", img.imageType)
.find("a")
.attr("href", struc.hrefPathTemplate.replace("{filetitle}", img.name))
.attr("rel", struc.relPathTemplate.replace("{filetitle}", img.name))
.append("")
.find("img")
.attr("src", struc.srcPathTemplate.replace("{filetitle}", img.name))
.attr("alt", formatAlt)
.attr("title", formatAlt)
if (img.level === "book") {
$newLi
.attr("data-also-item-level", img.alsoItemLevel)
.attr("data-web-type", img.webType)
.attr("data-outlet", img.outlet)
.attr("data-page-no", img.pageNo)
}
$newLi.appendTo($newUL);
});
} else if (Next.Settings.Channel.PliNewMediaFormatIsEnabled) {
$(media).each(function () {
var img = this;
var struc;
if (img.level === "book" && book.length > 0 && pageNumber.length > 0 && (img.alsoItemLevel == "" || img.alsoItemLevel == "false")) {
struc = bookPathStructure;
} else if (img.level === "book" && img.alsoItemLevel === "true") {
struc = itemPathStructure;
//Replace the image name's "-" with nothing
img.name = img.name.replace("-", "");
} else {
//img.level="item"
struc = itemPathStructure;
}
if (img.level === "item") {
$(" ")
.attr("data-shot-type", img.shotType)
.attr("data-level", img.level)
.attr("data-image-type", img.imageType)
.attr("data-web-type", img.webType)
.find("a")
.attr("href", struc.hrefPathTemplate.replace("{filetitle}", img.name))
.attr("rel", struc.relPathTemplate.replace("{filetitle}", img.name))
.append("")
.find("img")
.attr("src", struc.srcPathTemplate.replace("{filetitle}", img.name))
.attr("alt", formatAlt)
.attr("title", formatAlt)
.end().end()
.appendTo($newUL);
}
else if (img.level === "book" && (img.alsoItemLevel === "true" || img.alsoItemLevel === "")) {
// We've got a book image that is also an item level image, so we need to show it;
// OR
// We've got a book image that doesn't include the alsoItemLevel attribute, so it must be root imagery: we need to show it
$(" ")
.attr("data-shot-type", img.shotType)
.attr("data-level", img.level)
.attr("data-also-item-level", img.alsoItemLevel)
.attr("data-image-type", img.imageType)
.attr("data-web-type", img.webType)
.attr("data-outlet", img.outlet)
.attr("data-page-no", img.pageNo)
.find("a")
.attr("href", struc.hrefPathTemplate.replace("{filetitle}", img.name))
.attr("rel", struc.relPathTemplate.replace("{filetitle}", img.name))
.append("")
.find("img")
.attr("src", struc.srcPathTemplate.replace("{filetitle}", img.name))
.attr("alt", formatAlt)
.attr("title", formatAlt)
.end().end()
.appendTo($newUL);
}
// else must be book only fit/colour imagery (from publishedXml) - nothing needed because book-only level items are being ignored when behaviour is off
});
} else {
$(media).each(function () {
var img = this;
var struc = (img.level === "book" && book.length > 0 && pageNumber.length > 0) ? bookPathStructure : itemPathStructure;
//Exclude new outlet and pageNo attributes if PIM publishes the new format of XML before PLI feature switch is enabled
if (img.outlet == "" && img.pageNo == "") {
$(" ")
.find("a")
.attr("href", struc.hrefPathTemplate.replace("{filetitle}", img.name))
.attr("rel", struc.relPathTemplate.replace("{filetitle}", img.name))
.append("")
.find("img")
.attr("src", struc.srcPathTemplate.replace("{filetitle}", img.name))
.attr("alt", formatAlt)
.attr("title", formatAlt)
.end().end()
.appendTo($newUL);
}
});
}
$newUL.find("li:first-child").addClass("first").attr("alt", currentItem.title).attr("title", currentItem.title);
$newUL.find("li:last-child").addClass("last").append("");
$(".ThumbNailNav ul li a").unbind('click');
$(".ThumbNailNavClip ul").replaceWith($newUL);
//Create click handlers for carousel
$(".ThumbNailNav ul li a").bind('click', function () {
var $a = $(this);
//Pass href/path of new shot image to updateShotImage
var href = $a.attr("href");
var alt = $a.find("img").attr("alt");
methods.updateShotImage(href, null, alt);
//set new target image
targetZoom = $a.attr("rel");
//Update selected image styling
$(".ThumbNailNav li").removeClass("selected");
$a.parent().addClass("selected");
//Store current shot index (to allow us to page from elsewhere)
currentShotIndex = $a.parent().index();
//If the zoom box is already opened, call the expandZoomBox method which will take care
//of loading the required zoom image
if (methods.Vars.$zoomBoxWrapper.hasClass("expanded")) {
methods.expandZoomBox();
}
methods.TrackZoomGAEvents("PDP", "tap", "pdp-image-carousel-product-image-selected-seed-" + currentShotIndex);
return false;
});
//jQuery upgrade - size() is removed
if (!jqueryUpgradeEnabledOnPage) {
totalImages = $(".ThumbNailNav ul img").size();
} else {
totalImages = $(".ThumbNailNav ul img").length;
}
thumbNailsLoaded = 0;
currentShotIndex = -1;
// Reset the nav scroll position to the first for lifestyle shot, otherwise second.
if (methods.Vars.shotData.Media) {
currentShotIndex = 0;
}
navStartIndex = currentShotIndex;
$(".ThumbNailNav ul img").each(function () {
if(!this.complete) {
$(this).on("load",function () {
thumbNailsLoaded++;
}); //Else, the image is cached, proceed..
} else {
thumbNailsLoaded++;
}
});
if (totalImages < 2) {
$(".shotNavPrev, .shotNavNext").hide();
} else {
$(".shotNavPrev, .shotNavNext").show();
}
if (totalImages===0) {
$(".carouselPrevious, .carouselNext").hide();
} else {
$(".carouselPrevious, .carouselNext").show();
}
var imgLoadWaitsRemaining = 1;
var imgLoadLastCount = -1;
(function wait() {
if (thumbNailsLoaded < totalImages) {
if (imgLoadLastCount !== thumbNailsLoaded) {
imgLoadWaitsRemaining = 10;
imgLoadLastCount = thumbNailsLoaded;
} else {
imgLoadWaitsRemaining -= 1;
}
if (imgLoadWaitsRemaining === 0) {
thumbNailsLoaded = totalImages;
}
setTimeout(wait, 100);
} else {
if (currentShotIndex !== -1) {
var $tn = $(".ThumbNailNav li").eq(currentShotIndex);
$tn.addClass("selected");
}
methods.setPortraitNavSize();
}
}());
},
buildVideo: function (currentItem) {
var videoHtml;
if (Next.Settings.Channel.VideoCtaThumbNailEnabled) {
videoHtml = "#ZoomComponent .video";
}
else
{ //PDPReskin video
videoHtml = ".videoReskin .video";
}
$(videoHtml).hide();
if (currentItem.Video) {
var video = encodeURIComponent(currentItem.Video);
var $a = $(videoHtml).find("a");
$a.data("file", video);
var videoUrl = methods.Vars.shotData.VideoUrl.replace("{filename}", video);
if (Next.Settings.Channel.VideoCtaThumbNailEnabled) {
$("").on("load",function() {
// Only show video container if image loads
$a.css("background-image", "url(" + videoUrl + ")");
$(videoHtml).show();
}).attr("src", videoUrl);
} else
{
$(videoHtml).show();
}
}
},
isMobile: function () {
//List mobile agents
var agents = ['android', 'webos', 'iphone', 'blackberry'];
if (navigator.userAgent.match(/iPhone/i)) {
return true;
}
//Check is user is on mobile device
for (var i = 0; i < agents.length; i++) {
if (navigator.userAgent.match('/' + agents[i] + '/i')) {
return true;
}
}
return false;
},
Settings: {
startPositionX: null,
endPositionX: null,
startPositionY: null,
endPositionY: null,
TargetElement: null,
PageWidth: null,
Next: null,
Prev: null
},
SettingsCarousel: {
startPositionX: null,
endPositionX: null,
startPositionY: null,
endPositionY: null,
TargetElement: null,
PageWidth: null,
Next: null,
Prev: null
},
SettingsCarouselZoom: {
startPositionX: null,
endPositionX: null,
startPositionY: null,
endPositionY: null,
TargetElement: null,
PageWidth: null,
Next: null,
Prev: null
},
TouchInit: function (options) {
methods.Settings.TargetElement = options.TargetElement;
methods.Settings.Next = $(methods.Settings.TargetElement).find(".shotNavNext");
methods.Settings.Prev = $(methods.Settings.TargetElement).find(".shotNavPrev");
methods.Settings.PageWidth = $(methods.Settings.TargetElement).width()
methods.AddTouch();
// This is required to swipe the carousel with mouse on desktop to test, uncomment it to enable on desktop, comment it before checking in the code
//$( methods.Settings.TargetElement).find("a").each(function () {
// $(this).removeAttr("onclick").click(function () {
// return false;
// });
// });
},
AddTouch: function () {
var config = {
touchStart: methods.touchStart,
touchMove: methods.touchMove,
touchEnd: methods.touchEnd,
useMouse: true
}
$(methods.Settings.TargetElement).NXTouch(config);
},
touchStart: function (e, NXTouchObj) {
methods.Settings.startPositionX = NXTouchObj.X;
methods.Settings.startPositionY = NXTouchObj.Y;
},
touchMove: function (e, NXTouchObj) {
if (Math.abs(methods.Settings.startPositionY - NXTouchObj.Y) > 10) {
window.scrollBy(0, methods.Settings.startPositionY - NXTouchObj.Y);
}
},
touchEnd: function (e, NXTouchObj) {
methods.Settings.endPositionX = NXTouchObj.X;
methods.Settings.endPositionY = NXTouchObj.Y;
if (Math.abs(methods.Settings.startPositionX - methods.Settings.endPositionX) > methods.Settings.PageWidth / 3) {
if (methods.Settings.endPositionX < methods.Settings.startPositionX) {
methods.Settings.Next.click();
}
else {
methods.Settings.Prev.click();
}
}
},
TouchInitCarousel: function (options) {
methods.SettingsCarousel.TargetElement = options.TargetElement;
methods.SettingsCarousel.Next = $(methods.SettingsCarousel.TargetElement).find(".carouselNext");
methods.SettingsCarousel.Prev = $(methods.SettingsCarousel.TargetElement).find(".carouselPrevious");
methods.SettingsCarousel.PageWidth = $(methods.SettingsCarousel.TargetElement).width()
methods.AddTouchCarousel();
// This is required to swipe the carousel with mouse on desktop to test, uncomment it to enable on desktop, comment it before checking in the code
//$( methods.Settings.TargetElement).find("a").each(function () {
// $(this).removeAttr("onclick").click(function () {
// return false;
// });
//});
},
AddTouchCarousel: function () {
var config = {
touchStart: methods.touchStartCarousel,
touchMove: methods.touchMoveCarousel,
touchEnd: methods.touchEndCarousel,
useMouse: true
}
$(methods.SettingsCarousel.TargetElement).NXTouch(config);
},
touchStartCarousel: function (e, NXTouchObj) {
methods.SettingsCarousel.startPositionY = NXTouchObj.Y;
},
touchMoveCarousel: function (e, NXTouchObj) {
},
touchEndCarousel: function (e, NXTouchObj) {
methods.SettingsCarousel.endPositionY = NXTouchObj.Y;
if (Math.abs(methods.SettingsCarousel.startPositionY - methods.SettingsCarousel.endPositionY) > methods.SettingsCarousel.PageWidth / 2) {
if (methods.SettingsCarousel.endPositionY < methods.SettingsCarousel.startPositionY) {
methods.SettingsCarousel.Next.click();
}
else {
methods.SettingsCarousel.Prev.click();
}
}
},
TouchInitCarouselZoom: function (options) {
methods.SettingsCarouselZoom.TargetElement = options.TargetElement;
methods.SettingsCarouselZoom.Next = $(methods.SettingsCarouselZoom.TargetElement).find(".carouselNext");
methods.SettingsCarouselZoom.Prev = $(methods.SettingsCarouselZoom.TargetElement).find(".carouselPrevious");
methods.SettingsCarouselZoom.PageWidth = $(methods.SettingsCarouselZoom.TargetElement).width()
methods.AddTouchCarouselZoom();
// This is required to swipe the carousel with mouse on desktop to test, uncomment it to enable on desktop, comment it before checking in the code
//$( methods.settings.targetelement).find("a").each(function () {
// $(this).removeattr("onclick").click(function () {
// return false;
// });
//});
},
AddTouchCarouselZoom: function () {
var config = {
touchStart: methods.touchStartCarouselZoom,
touchMove: methods.touchMoveCarouselZoom,
touchEnd: methods.touchEndCarouselZoom,
useMouse: true
}
$(methods.SettingsCarouselZoom.TargetElement).NXTouch(config);
},
touchStartCarouselZoom: function (e, NXTouchObj) {
methods.SettingsCarouselZoom.startPositionY = NXTouchObj.Y;
},
touchMoveCarouselZoom: function (e, NXTouchObj) {
},
touchEndCarouselZoom: function (e, NXTouchObj) {
methods.SettingsCarouselZoom.endPositionY = NXTouchObj.Y;
if (Math.abs(methods.SettingsCarouselZoom.startPositionY - methods.SettingsCarouselZoom.endPositionY) > methods.SettingsCarouselZoom.PageWidth / 2) {
if (methods.SettingsCarouselZoom.endPositionY < methods.SettingsCarouselZoom.startPositionY) {
methods.SettingsCarouselZoom.Next.click();
} else {
methods.SettingsCarouselZoom.Prev.click();
}
}
},
initZoomComponents: function ($image) {
//Get dimensions of new image once it's loaded
var $shotViewImg = methods.Vars.$zoomComponent.find(".ShotView img");
var sOffset = $shotViewImg.offset();
var imgObj = methods.getImageDimensions($image);
var heightFactor = imgObj.width / imgObj.height;
var shotView = methods.Vars.$zoomComponent.find(".ShotView");
//Set orientation and position carousel navigation accordingly
//if (imgObj.width > imgObj.height) {
if (false) {
orientation = "landscape";
methods.setLandscapeNavSize();
methods.Vars.$zoomComponent.addClass("Landscape");
methods.Vars.$zoomComponent.removeClass("Portrait");
methods.Vars.$zoomComponent.append(methods.Vars.$thumbnailNav);
if (imgObj.width > maxLandscapeImageWidth) {
imgObj.width = maxLandscapeImageWidth;
imgObj.height = imgObj.width / heightFactor;
$image.height(imgObj.height);
$image.width(imgObj.width);
}
} else {
orientation = "portrait";
//methods.setPortraitNavSize();
methods.Vars.$zoomComponent.find(".shotmedia").prepend(methods.Vars.$thumbnailNav);
methods.Vars.$zoomComponent.addClass("Portrait");
methods.Vars.$zoomComponent.removeClass("Landscape");
//Now check that the image isn't too wide
//If it is, resize it to max width and set height by the same factor
if (imgObj.width > maxPortraitImageWidth) {
imgObj.width = maxPortraitImageWidth;
imgObj.height = imgObj.width / heightFactor;
$image.height(imgObj.height);
$image.width(imgObj.width);
}
}
curShotDimensions = imgObj;
// Replace current/existing image
$shotViewImg.replaceWith($image);
if (!Next.Settings.UI.IsInnovationDesktopResponsiveEnabled) {
methods.Vars.$zoomComponent.find(".ShotView, .shotWrapper").height(imgObj.height);
}
if (imgObj.height > 472) {
$(".zoomBoxWrapper").removeClass("stage2");// GJ - What is this???
}
//Reposition loupe to make sure it stays within the shot view - unless mouse is already over shotview
if (!methods.Vars.$zoomComponent.find(".ShotView").hasClass("shotViewHover")) {
var loupe = methods.Vars.$zoomComponent.find(".ZoomComponent .loupe");
if (loupe.length) {
$(loupe).offset({ left: sOffset.left, top: sOffset.top });
}
}
if (!Next.Settings.UI.IsInnovationDesktopResponsiveEnabled){
//Set height of shot view based on image dimensions
shotView.height(imgObj.height);
$(".shotWrapper").height(imgObj.height);
}
//Store current image dimensions
curShotDimensions = imgObj;
},
buildForMobile: function () {
var phoneZoomOpen = $("Zoom");
var superZoomOpen = methods.Vars.$zoomComponent.find(".SuperZoomOpen");
superZoomOpen.css("display", "none");
$(".ShotView").append(phoneZoomOpen);
phoneZoomOpen.click(function () {
methods.openPhoneZoom();
});
methods.Vars.$zoomComponent.find(".ShotView").bind("click", function () {
methods.openPhoneZoom();
});
var options = { TargetElement: '.ShotView' };
methods.TouchInit(options);
var optionsImgCarousel = { TargetElement: '.ThumbNailNav' };
methods.TouchInitCarousel(optionsImgCarousel);
},
/////Update image in shot view /////////////////////////////////
updateShotImage: function (path, callback, alt) {
//Dynamically load new shot image
if (!jqueryUpgradeEnabledOnPage) {
$("").load(function () {
if (alt) {
$(this).attr({ "title": alt, "alt": alt });
}
methods.initZoomComponents($(this));
if (callback) {
callback();
}
try {
if (ProductPage) {
ProductPage.Positioning.ResizeItemScroller();
}
} catch (ignore) {
}
}).attr('src', path);
} else {
$("").on("load", function () {
if (alt) {
$(this).attr({ "title": alt, "alt": alt });
}
methods.initZoomComponents($(this));
if (callback) {
callback();
}
try {
if (ProductPage) {
ProductPage.Positioning.ResizeItemScroller();
}
} catch (ignore) {
}
}).attr('src', path);
}
},
//////////////////////This function allows images to be added 'externally' to the control
//////////////////////e.g. $("#ZoomComponent").ZoomComponent('addZoomItem', 'shot.jpg', 'zoom.jpg');
addZoomItem: function (params, callBack) {
methods.updateShotImage(params.SmallImage, callBack, params.Description);
targetZoom = params.LargeImage;
if ($(".zoomBoxWrapper").hasClass("expanded")) {
methods.expandZoomBox();
}
},
///////////////////////Add event handlers for the shot view/////////////////////
addShotHandlers: function () {
//Add loupe element
var $loupe = $(""),
$shotView = methods.Vars.$zoomComponent.find(".ShotView");
$shotView.append($loupe);
if (/Android|webOS|iPhone|iPad|iPod|mobile|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
&& !Next.Settings.UI.PlpDesktopJumpinessFix) {
isTouchEnabled = true;
} else {
isTouchEnabled = false;
}
if (!isTouchEnabled) {
//Shotview click - toggle loupe and zoom item
$shotView.bind("click", function (e) {
if (methods.Vars.$zoomBoxWrapper.hasClass("locked")) {
return false;
}
//Position loupe to mouse
$loupe.offset({ left: e.pageX - 25, top: e.pageY - 25 });
//Toggle between zoom on/off
if ($shotView.hasClass("magOn")) {
$shotView.removeClass("magOn");
$shotView.find(".loupe").fadeOut(300);
methods.collapseZoomBox();
} else {
$(".shotWrapper").addClass("live");
$shotView.addClass("magOn");
$shotView.find(".loupe").fadeIn(300);
methods.expandZoomBox();
}
});
//Mouse move
$shotView.bind('mousemove', this, function (e) {
mx = e.pageX;
my = e.pageY;
});
//Mouse hover/leave
$shotView.hover(
function () {
$shotView.addClass("shotViewHover");
moveInterval = setTimeout(function () { methods.moveLoupe(); }, 30);
methods.toggleShotControls(true);
},
function () {
$shotView.removeClass("shotViewHover");
clearTimeout(moveInterval);
methods.toggleShotControls(false);
});
} else {//touch enabled devices
var ipadShotView = document.getElementById("ShotView");
$(ipadShotView).on("click", function() {
methods.openSuperZoom();
});
if (navigator.maxTouchPoints > 0) {//ie11 touch device
ipadShotView.addEventListener('pointerdown', function (e) {
$('.ShotView').css({ "touch-action": "none" });
methods.handlePointerDown(e, this, $shotView);
});
ipadShotView.addEventListener('pointermove', function (e) {
methods.handlePointerMove(e);
}, false);
}
else if (navigator.msMaxTouchPoints > 0) {//IE10 touch device
ipadShotView.addEventListener('MSPointerDown', function (e) {
$('.ShotView').css({ "-ms-touch-action": "none" });
methods.handlePointerDown(e, this, $shotView);
});
ipadShotView.addEventListener('MSPointerMove', function (e) {
methods.handlePointerMove(e);
}, false);
}
else {//Else - is iPad
//ipadShotView.addEventListener('touchstart', function (e) {
// $loupe.offset({ left: e.changedTouches[0].pageX - 25, top: e.changedTouches[0].pageY - 25 });
// mx = e.targetTouches[0].pageX;
// my = e.targetTouches[0].pageY;
// if ($(ipadShotView).hasClass("magOn")) {
// return false;
// }
// $(".shotWrapper").addClass("live");
// $(ipadShotView).addClass("magOn");
// $(ipadShotView).find(".loupe").fadeIn(300);
// methods.expandZoomBox();
// moveInterval = setTimeout(function () { methods.moveLoupe(); }, 30);
// $shotView.addClass("shotViewHover");
//});
//ipadShotView.addEventListener('touchmove', function (e) {
// e.preventDefault();
// mx = e.targetTouches[0].pageX;
// my = e.targetTouches[0].pageY;
//}, false);
}
}
},
handlePointerDown: function (e, obj, shotViewObj) {
$(".loupe").offset({ left: e.pageX - 25, top: e.pageY - 25 });
mx = e.pageX;
my = e.pageY;
if ($(obj).hasClass("magOn")) {
return false;
}
$(".shotWrapper").addClass("live");
$(obj).addClass("magOn");
$(obj).find(".loupe").fadeIn(300);
methods.expandZoomBox();
moveInterval = setTimeout(function () { methods.moveLoupe(); }, 30);
shotViewObj.addClass("shotViewHover");
},
handlePointerMove: function (e) {
mx = e.pageX;
my = e.pageY;
},
updateShotDimensions: function ($shotImg) {
if (Next.Settings.UI.IsInnovationDesktopResponsiveEnabled) {
curShotDimensions.width = $shotImg.width();
curShotDimensions.height = $shotImg.height() + 3;
}
},
//////////////////////////Move magnifying glass when user moves over shot view/////////////////////////////
moveLoupe: function () {
if (!curShotDimensions)
return;
//Set loupeValue here - quicker than measuring
var loupeWidth = 85;
var loupeHeight = 85;
if (methods.Vars.SocialPLP) {
loupeWidth = 60;
loupeHeight = 60;
}
//Set the target position for the loupe to follow mouse position - and then centre the lopue around mouse position
var $shotViewImg = methods.Vars.$zoomComponent.find(".ShotView img");
methods.updateShotDimensions($shotViewImg);
var curShotDimensionWidth = curShotDimensions.width;
var curShotDimensionsHeight = curShotDimensions.height;
//User may be navigating using left/right arrows - there will some instances where the image hasn't yet loaded
//but the user is still over the shotview - need to check that the image is loaded before we can position loupe
if ($shotViewImg.length) {
var targetX = mx - loupeWidth / 2;
var targetY = my - loupeHeight / 2;
//Limit loupe to only move within shot view
if (targetX < $shotViewImg.offset().left) {
targetX = $shotViewImg.offset().left;
}
if (targetY < $shotViewImg.offset().top) {
targetY = $shotViewImg.offset().top;
}
if (targetX > ($shotViewImg.offset().left + curShotDimensionWidth - loupeWidth)) {
targetX = $shotViewImg.offset().left + curShotDimensionWidth - loupeWidth;
}
if (targetY > ($shotViewImg.offset().top + curShotDimensionsHeight - loupeHeight)) {
targetY = $shotViewImg.offset().top + curShotDimensionsHeight - loupeHeight;
}
//Need to round the position - positioning an element to 0.5 accuracy in IE
//casues it to wobble - IE can't decide where to place the element so it randomly rounds up or
//down the positioning causing the wobble
targetX = Math.round(targetX);
targetY = Math.round(targetY);
//Positon the loupe
$(".loupe").offset({ left: targetX, top: targetY });
//Check if we have a zoomed image to position
var $zoomImg = methods.Vars.$zoomBox.find(".zoomBoxImage");
if ($zoomImg.length) {
//Need to map loupe movement to zoom image movement
//Caluculate 1% of zoom image dimensions
var widthFactor = (curZoomDimensions.width / 100);
var heightFactor = (curZoomDimensions.height / 100);
//Get loupe position
var loupeOffsetX = targetX - $shotViewImg.offset().left;
//Get loupe position as a percentage
var percentX = loupeOffsetX / ((curShotDimensions.width - loupeWidth) / 100);
//Same as above for Y axis
var loupeOffsetY = targetY - $shotViewImg.offset().top;
var percentY = loupeOffsetY / ((curShotDimensions.height - loupeHeight) / 100);
//Get current zoom position
var curZoomLeft = $zoomImg.position().left;
var curZoomTop = $zoomImg.position().top;
//Set zoom window dimensions
var zoomBoxWidth = 550;
var zoomBoxHeight = 472;
if (Next.Settings.UI.IsInnovationDesktopResponsiveEnabled && $(window).width() >= 1280) {
zoomBoxHeight = 545;
}
if (orientation === "landscape") {
zoomBoxHeight = 466;
}
if (methods.Vars.SocialPLP) {
zoomBoxHeight = 247;
}
//Set the position the zoom image should move to to match loupe position
var targetZoomLeft = 0 - (percentX * widthFactor) + (zoomBoxWidth / 100 * percentX);
var targetZoomTop = 0 - (percentY * heightFactor) + (zoomBoxHeight / 100 * percentY);
//Calculate how far zoom image needs to move to get to target position
var difLeft = Math.floor(curZoomLeft - targetZoomLeft);
var difTop = Math.floor(curZoomTop - targetZoomTop);
//Now divide required distance by a given number to give the delayed movement effect.
//As this function calls itself while the mouse is over the shot view, the image will continue to move while
//the mouse is not moving, gradually slowing down as it reaches the correct position
var left = Math.floor(curZoomLeft - (difLeft / smoothScroll));
var top = Math.floor(curZoomTop - (difTop / smoothScroll));
//Position the zoom image
$zoomImg.css("left", left).css("top", top);
}
}
//If the mouse is still over the shot view, call this method again in a given timespan
if ($(".ShotView").hasClass("shotViewHover")) {
moveInterval = setTimeout(function () { methods.moveLoupe(); }, 30);
}
},
bindZoomBoxImage: function (img, additionalPadding) {
methods.Vars.$zoomBox.show();
methods.Vars.$zoomBoxWrapper.show();
//When zoom image is loaded, set its display to none so we can fade it in
$(img).css("display", "none");
//Add identifier class
$(img).addClass("zoomBoxImage");
//Add image to zoom box
methods.Vars.$zoomBox.append(img);
//set its starting co-ordinates
$(img).css("top", 0).css("left", 0);
//Get and set variable to store image dimensions
curZoomDimensions = methods.getImageDimensions(img);
// If the zoom area isn't yet expanded, set its hidden/initial state
if (!methods.Vars.$zoomBoxWrapper.hasClass("expanded")) {
methods.Vars.$zoomBoxWrapper.width(0);
methods.Vars.$zoomBoxWrapper.height(curShotDimensions.height + additionalPadding);
methods.Vars.$zoomBox.css("opacity", 0);
}
//Animate width
methods.Vars.$zoomBoxWrapper.animate({
width: 552
}, 500, 'easeOutCubic', function () {
var zoomBoxHeight = methods.Vars.$zoomBox.outerHeight();
if(Next.Settings.UI.IsInnovationDesktopResponsiveEnabled){
zoomBoxHeight += 2;
}
//If the shot view is smaller than the requierd zoom area, animate height as well
if (curShotDimensions.height < zoomBoxHeight) {
methods.Vars.$zoomBoxWrapper.addClass("stage2");
methods.Vars.$zoomBoxWrapper.animate({
height: zoomBoxHeight//466 + additionalPadding
}, 500, 'easeOutCubic', function () {
$(img).fadeIn(500);
methods.Vars.$zoomBox.animate({ opacity: 1 }, 500);
methods.Vars.$zoomBoxWrapper.removeClass("locked").addClass("expanded");
});
} else {
$(img).fadeIn(500);
methods.Vars.$zoomBox.animate({ opacity: 1 }, 500);
methods.Vars.$zoomBoxWrapper.removeClass("locked").addClass("expanded");
}
});
},
///////////////////////Expand the zoom box/////////////////////
expandZoomBox: function () {
//Create zoom area if it doesn't already exist
var additionalPadding = 0;
var zoomImage = new Image();
var zoomImagePath = targetZoom;
//Need to account for slightly different design of landscape container box having a slight padding
if (orientation === "landscape") {
additionalPadding = 10;
}
//Fade out any existing image
if (methods.Vars.$zoomBox.find("img").length) {
methods.Vars.$zoomBox.find("img").fadeOut(200, function () {
$(this).remove();
});
}
////Lock the box to prevent starting other animations while we animate
methods.Vars.$zoomBoxWrapper.addClass("locked");
//This fixes cached images not showing in IE8 and below
if (!jqueryUpgradeEnabledOnPage) {
if ($.browser.msie && $.browser.version < 9) {
zoomImagePath += "?rnd=" + Math.random().toString(36).substring(7);
zoomImage.src = zoomImagePath;
}
//When the image loads, pass to the binding method
$(zoomImage).load(function () {
methods.bindZoomBoxImage(zoomImage, additionalPadding);
}).attr('src', zoomImagePath);
}
else
{
if (navigator.userAgent.toLowerCase().indexOf('msie') > -1 && document.documentMode < 9) {
zoomImagePath += "?rnd=" + Math.random().toString(36).substring(7);
zoomImage.src = zoomImagePath;
}
//When the image loads, pass to the binding method
$(zoomImage).on("load", function () {
methods.bindZoomBoxImage(zoomImage, additionalPadding);
}).attr('src', zoomImagePath);
}
methods.TrackZoomGAEvents("PDP", "tap", "pdp-product-image-expand-zoom");
},
///////////////////////Close the zoom box/////////////////////
collapseZoomBox: function () {
var additionalPadding = 0;
var heightAnimateTime = 500;
//Need to account for slightly different design of landscape container box having a slight padding
if (orientation === "landscape") {
additionalPadding = 10;
}
methods.Vars.$zoomBoxWrapper.addClass("locked");
methods.Vars.$zoomBox.animate({ opacity: 0 }, 500);
methods.Vars.$zoomBox.find("img").fadeOut(300, function () {
methods.Vars.$zoomBox.find("img").remove();
});
if (methods.Vars.$zoomBoxWrapper.height() === curShotDimensions.height) {
heightAnimateTime = 0;
}
methods.Vars.$zoomBoxWrapper.animate({
height: curShotDimensions.height + additionalPadding
}, heightAnimateTime, 'easeOutCubic', function () {
methods.Vars.$zoomBoxWrapper.removeClass("stage2");
methods.Vars.$zoomBoxWrapper.animate({
width: 0
}, 500, 'easeOutCubic', function () {
methods.Vars.$zoomBoxWrapper.removeClass("expanded").removeClass("locked");
methods.Vars.$zoomBox.hide();
methods.Vars.$zoomBoxWrapper.hide();
$(".shotWrapper").removeClass("live");
});
});
},
toggleShotControls: function (display) {
var opacityAnimationSpeed = 300;
if (isIeVersionOlderThan9) {
opacityAnimationSpeed = 0;
}
if (display) {
methods.Vars.$zoomComponent.find(".SuperZoomOpen .Text").stop();
methods.Vars.$zoomComponent.find(".SuperZoomOpen").stop().animate({ opacity: 1 }, 200, function () {
methods.Vars.$zoomComponent.find(".SuperZoomOpen").stop().animate({ width: 105 }, 400, 'easeOutBack', function () {
methods.Vars.$zoomComponent.find(".SuperZoomOpen .Text").stop().animate({ opacity: 1 }, 200);
});
});
methods.Vars.$zoomComponent.find(".shotNavPrev").stop().animate({ opacity: 1 }, opacityAnimationSpeed);
methods.Vars.$zoomComponent.find(".shotNavNext").stop().animate({ opacity: 1 }, opacityAnimationSpeed);
} else {
methods.Vars.$zoomComponent.find(".SuperZoomOpen").stop();
methods.Vars.$zoomComponent.find(".SuperZoomOpen .Text").stop().animate({ opacity: 0 }, 200, function () {
methods.Vars.$zoomComponent.find(".SuperZoomOpen").stop().animate({ width: 24 }, 400, 'easeInBack', function () {
methods.Vars.$zoomComponent.find(".SuperZoomOpen").stop().animate({ opacity: 0 }, 200);
});
});
methods.Vars.$zoomComponent.find(".shotNavPrev").stop().animate({ opacity: 0 }, opacityAnimationSpeed);
methods.Vars.$zoomComponent.find(".shotNavNext").stop().animate({ opacity: 0 }, opacityAnimationSpeed);
}
},
//////////////////Add shot controls functionality///////////////////
addShotControlHandlers: function () {
var $superZoomOpen = methods.Vars.$zoomComponent.find(".SuperZoomOpen");
var $shotNavPrev = methods.Vars.$zoomComponent.find(".shotNavPrev");
var $shotNavNext = methods.Vars.$zoomComponent.find(".shotNavNext");
var $shotView = methods.Vars.$zoomComponent.find(".ShotView");
var $closeZoomBox = methods.Vars.$zoomComponent.find(".close");
$shotNavPrev.bind("click", function (event) {
//Stop click bubbling to shotview
event.stopPropagation();
//Decrement current shot index
currentShotIndex--;
if (Next.Settings.Channel.PliNewMediaFormatIsEnabled && Next.Settings.Channel.PliBehaviourIsEnabled) {
while (!$(methods.Vars.$thumbnailNav.find("li").eq(currentShotIndex)).is(":visible")) {
currentShotIndex--;
if (currentShotIndex < 0) {
currentShotIndex = totalImages - 1;
if ($(methods.Vars.$thumbnailNav.find("li").eq(currentShotIndex)).is(":visible")) {
break;
}
}
}
}
else {
//Limit current shotindex to 0, if reached, go to end
if (currentShotIndex < 0) {
currentShotIndex = totalImages - 1;
}
}
//Find the target thumbnail to set it to live
var $targetLI = methods.Vars.$thumbnailNav.find("li").eq(currentShotIndex);
//Fire the a click on that item
$targetLI.find("a").click();
//If we're in portrait mode
if (orientation === "portrait") {
//If the thumbnail we've just set live is outside of clipping region
//move the carousel - either up of down
if ($targetLI.offset().top < $("#ZoomNavStart").offset().top) {
methods.carouselScroll("down", true);
}
if ($targetLI.offset().top > $("#ZoomNavEnd").offset().top) {
methods.carouselScroll("up", true);
}
} else {
//Else - we're in landscape mode, do the same as above for left and right
if ($targetLI.offset().left < $("#ZoomNavStart").offset().left) {
methods.carouselScroll("right", true);
}
if (($targetLI.offset().left + $targetLI.width()) > $("#ZoomNavEnd").offset().left) {
methods.carouselScroll("left", true);
}
}
});
//Exactly the same as above, but for the "next" shotview control button
$shotNavNext.bind("click", function (event) {
event.stopPropagation();
currentShotIndex++;
if (Next.Settings.Channel.PliNewMediaFormatIsEnabled && Next.Settings.Channel.PliBehaviourIsEnabled) {
while (!$(methods.Vars.$thumbnailNav.find("li").eq(currentShotIndex)).is(":visible")) {
currentShotIndex++;
if (currentShotIndex > totalImages - 1) {
currentShotIndex = 0;
break;
}
}
} else {
if (currentShotIndex > totalImages - 1) {
currentShotIndex = 0;
}
}
var targetLI = methods.Vars.$thumbnailNav.find("li").eq(currentShotIndex);
$(targetLI).find("a").click();
if (orientation === "portrait") {
if ($(targetLI).offset().top < $("#ZoomNavStart").offset().top) {
methods.carouselScroll("down", true);
}
if ($(targetLI).offset().top > $("#ZoomNavEnd").offset().top) {
methods.carouselScroll("up", true);
}
} else {
if ($(targetLI).offset().left < $("#ZoomNavStart").offset().left) {
methods.carouselScroll("right", true);
}
if (($(targetLI).offset().left + $(targetLI).width()) > $("#ZoomNavEnd").offset().left) {
methods.carouselScroll("left", true);
}
}
});
$closeZoomBox.bind("click", function (event) {
event.stopPropagation();
event.preventDefault();
methods.collapseZoomBox();
$shotView.removeClass("magOn");
$shotView.find(".loupe").fadeOut(300);
});
$superZoomOpen.bind("click", function (event) {
if (history.pushState)
history.pushState("zoom", "title");
event.stopPropagation();
methods.openSuperZoom();
});
},
setLandscapeNavSize: function () {
//Check all thumbnails have loaded before we set the navigation
if (totalImages > thumbNailsLoaded) {
setTimeout(function () { methods.setLandscapeNavSize(); }, 100);
return false;
}
var totalWidth = 0;
$(".ZoomComponent .ThumbNailNav ul li").each(function () {
//TODO - Calculate border and margin - rather than 2 + 8
totalWidth += $(this).width() + 2 + 8;
});
if (totalWidth < 350) {
$(".ZoomComponent .ThumbNailNav a.carouselPrevious, .ZoomComponent .ThumbNailNav a.carouselNext").addClass("disabled");
}
$(".ZoomComponent .ThumbNailNav ul").width(totalWidth + 5);
if ($("#lastItemImageGrab").length) {
if (ProductPage) {
ProductPage.Positioning.ResizeItemScroller();
}
}
},
/////// Resize clipped nav region on a portrait layout ////////////////////
setPortraitNavSize: function () {
//Now that all images have loaded, we can measure the carousel navigation
//The point of doing this is that we may have differently sized thumbnails in the navigation
//so we can't just set imageHeight * 4, for example
var ulHeight = $(".ZoomComponent .ThumbNailNav ul").height();
$(".ZoomComponent .ThumbNailNav a.carouselPrevious").addClass("disabled");
//Limit to set height
var maxULHeight = Next.Settings.UI.IsInnovationDesktopResponsiveEnabled ? 307 : 320;
var ulHeightPadding = Next.Settings.UI.IsInnovationDesktopResponsiveEnabled ? 2 : 0;
if (methods.Vars.SocialPLP) {
maxULHeight = 195;
}
if (ulHeight > maxULHeight) {
ulHeight = maxULHeight;
$(".ZoomComponent .ThumbNailNav a.carouselNext").removeClass("disabled");
} else {
//The list of items didn't extend beyond the page limit, we wont need scrolling
$(".ZoomComponent .ThumbNailNav a.carouselNext").addClass("disabled");
}
//resize the clipping region
if (methods.Vars.SocialPLP) {
$(".ZoomComponent .video").css("margin-top", 343 - ulHeight);
} else {
$(".ZoomComponent .ThumbNailNavClip").height(ulHeight + ulHeightPadding);
$(".ZoomComponent .ThumbNailNav").height(ulHeight + 60);
}
if ($("#lastItemImageGrab").length) {
if (ProductPage) {
ProductPage.Positioning.ResizeItemScroller();
}
}
},
///////This function handles carousel navigation scrolling
carouselScroll: function (direction, keepLiveItemVisible) {
if (isCarouselScrolling) {
return false;
}
isCarouselScrolling = true;
var itemWidth;
var itemHeight;
var $navControl;
var navEndOffset;
var lastItemOffset;
var $currentStartItem;
var animationTime = 400;
var easing = "easeOutCubic";
if (keepLiveItemVisible) {
easing = "linear";
animationTime = 200;
}
switch (direction) {
case "up":
$navControl = methods.Vars.$thumbnailNav.find("a.carouselNext");
if ($navControl.hasClass("disabled") && !keepLiveItemVisible) {
isCarouselScrolling = false;
return false;
}
methods.Vars.$thumbnailNav.find("a.carouselPrevious").removeClass("disabled");
$currentStartItem = methods.Vars.$thumbnailNav.find("li").eq(navStartIndex);
//TODO - Calculate bottom margin
// itemHeight = Next.Settings.UI.IsInnovationDesktopResponsiveEnabled ? $currentStartItem.outerHeight(true) : $currentStartItem.outerHeight() + navLiMargin;
itemHeight = $currentStartItem.outerHeight() +navLiMargin;
//Added step function to check where we are in the list. Stops the list from infinite looping and allowing double clicks
//to move past the end points
methods.Vars.$thumbnailNav.find("ul").animate({ top: "-=" + itemHeight },
{
duration: animationTime,
easing: easing,
complete: function () {
isCarouselScrolling = false;
lastItemOffset = $("#lastZoomNavItem").offset().top;
navEndOffset = $("#ZoomNavEnd").offset().top;
if (lastItemOffset < navEndOffset) {
methods.Vars.$thumbnailNav.find("a.carouselNext").addClass("disabled");
} else {
methods.Vars.$thumbnailNav.find("a.carouselNext").removeClass("disabled");
}
//If required, we need to keep the live item visible in the carousel nav
if (keepLiveItemVisible) {
var targetLI = $(".ZoomComponent .ThumbNailNav ul li.selected");
if ($(targetLI).offset().top < $("#ZoomNavStart").offset().top) {
methods.carouselScroll("down", true);
}
if ($(targetLI).offset().top > $("#ZoomNavEnd").offset().top) {
methods.carouselScroll("up", true);
}
}
}
});
navStartIndex++;
break;
case "down":
$navControl = methods.Vars.$thumbnailNav.find("a.carouselPrevious");
if ($navControl.hasClass("disabled") || navStartIndex === 0) {
isCarouselScrolling = false;
return false;
}
methods.Vars.$thumbnailNav.find("a.carouselNext").removeClass("disabled");
$currentStartItem = methods.Vars.$thumbnailNav.find("li").eq(navStartIndex - 1);
//TODO - Calculate bottom margin
// itemHeight = Next.Settings.UI.IsInnovationDesktopResponsiveEnabled ? $currentStartItem.outerHeight(true) : $currentStartItem.outerHeight() + navLiMargin;
itemHeight = $currentStartItem.outerHeight() +navLiMargin;
//Added step function to check where we are in the list. Stops the list from infinite looping and allowing double clicks
//to move past the end points
methods.Vars.$thumbnailNav.find("ul").animate({ top: "+=" + itemHeight },
{
duration: animationTime,
easing: easing,
complete: function () {
isCarouselScrolling = false;
lastItemOffset = $(".ZoomComponent .ThumbNailNav ul li.first").offset().top;
navEndOffset = $("#ZoomNavStart").offset().top;
if (lastItemOffset > navEndOffset) {
methods.Vars.$thumbnailNav.find("a.carouselPrevious").addClass("disabled");
} else {
methods.Vars.$thumbnailNav.find("a.carouselPrevious").removeClass("disabled");
}
//If required, we need to keep the live item visible in the carousel nav
if (keepLiveItemVisible) {
var targetLI = $(".ZoomComponent .ThumbNailNav ul li.selected");
if ($(targetLI).offset().top < $("#ZoomNavStart").offset().top) {
methods.carouselScroll("down", true);
}
if ($(targetLI).offset().top > $("#ZoomNavEnd").offset().top) {
methods.carouselScroll("up", true);
}
}
if (navStartIndex === 0) {
methods.Vars.$thumbnailNav.find("a.carouselPrevious").addClass("disabled");
}
}
});
navStartIndex--;
break;
case "left":
$navControl = methods.Vars.$thumbnailNav.find("a.carouselNext");
if ($navControl.hasClass("disabled") && !keepLiveItemVisible) {
isCarouselScrolling = false;
return false;
}
methods.Vars.$thumbnailNav.find("a.carouselPrevious").removeClass("disabled");
$currentStartItem = methods.Vars.$thumbnailNav.find("li").eq(navStartIndex);
//TODO - Calculate border and bottom margin
itemWidth = $currentStartItem.width() + 2 + 8;
//Added step function to check where we are in the list. Stops the list from infinite looping and allowing double clicks
//to move past the end points
methods.Vars.$thumbnailNav.find("ul").animate({ left: "-=" + itemWidth },
{
duartion: animationTime,
easing: easing,
complete: function () {
isCarouselScrolling = false;
lastItemOffset = $("#lastZoomNavItem").offset().left;
navEndOffset = $("#ZoomNavEnd").offset().left;
if (lastItemOffset < navEndOffset) {
methods.Vars.$thumbnailNav.find("a.carouselNext").addClass("disabled");
} else {
methods.Vars.$thumbnailNav.find("a.carouselNext").removeClass("disabled");
}
//If required, we need to keep the live item visible in the carousel nav
if (keepLiveItemVisible) {
var targetLI = $(".ZoomComponent .ThumbNailNav ul li.selected");
if ($(targetLI).offset().left < $("#ZoomNavStart").offset().left) {
methods.carouselScroll("right", true);
}
if (($(targetLI).offset().left + $(targetLI).width()) > $("#ZoomNavEnd").offset().left) {
methods.carouselScroll("left", true);
}
}
}
});
navStartIndex++;
break;
case "right":
$navControl = methods.Vars.$thumbnailNav.find("a.carouselPrevious");
if ($(this).hasClass("disabled") || navStartIndex === 0) {
isCarouselScrolling = false;
return false;
}
methods.Vars.$thumbnailNav.find("a.carouselNext").removeClass("disabled");
$currentStartItem = methods.Vars.$thumbnailNav.find("li").eq(navStartIndex - 1);
//TODO - Calculate border and bottom margin
itemWidth = $currentStartItem.width() + 2 + 8;
//Added step function to check where we are in the list. Stops the list from infinite looping and allowing double clicks
//to move past the end points
methods.Vars.$thumbnailNav.find("ul").animate({ left: "+=" + itemWidth },
{
duration: animationTime,
easing: easing,
complete: function () {
isCarouselScrolling = false;
lastItemOffset = $("#lastZoomNavItem").offset().left;
navEndOffset = $("#ZoomNavEnd").offset().left;
if (lastItemOffset < navEndOffset) {
methods.Vars.$thumbnailNav.find("a.carouselNext").addClass("disabled");
} else {
methods.Vars.$thumbnailNav.find("a.carouselNext").removeClass("disabled");
}
//If required, we need to keep the live item visible in the carousel nav
if (keepLiveItemVisible) {
var targetLI = $(".ZoomComponent .ThumbNailNav ul li.selected");
if ($(targetLI).offset().left < $("#ZoomNavStart").offset().left) {
methods.carouselScroll("right", true);
}
if (($(targetLI).offset().left + $(targetLI).width()) > $("#ZoomNavEnd").offset().left) {
methods.carouselScroll("left", true);
}
}
if (navStartIndex === 0) {
methods.Vars.$thumbnailNav.find("a.carouselPrevious").addClass("disabled");
}
}
});
navStartIndex--;
break;
}
},
//Add carousel nav controls event handlers
addCarouselScrolling: function () {
switch (orientation) {
case "portrait":
methods.Vars.$thumbnailNav.find("a.carouselPrevious").bind("click", function () {
methods.carouselScroll("down");
});
methods.Vars.$thumbnailNav.find("a.carouselNext").bind("click", function () {
methods.carouselScroll("up");
});
break;
case "landscape":
methods.Vars.$thumbnailNav.find("a.carouselPrevious").bind("click", function () {
methods.carouselScroll("right");
});
methods.Vars.$thumbnailNav.find("a.carouselNext").bind("click", function () {
methods.carouselScroll("left");
});
break;
}
},
openSuperZoom: function () {
var targetHeight = $(window).height() - 10;
var targetWidth = $(window).width() - 10;
var params = { defaultImage: targetZoom };
var $superZoomNav = methods.Vars.$thumbnailNav.clone();
var $superZoomDiv = $("");
//Clean up shot view settings
moveInterval = null;
methods.collapseZoomBox();
$(".ShotView").removeClass("magOn");
$(".ShotView").find(".loupe").fadeOut(300);
methods.Vars.$zoomBoxWrapper.removeClass("locked");
//Pass the carousel nav to the superzoom for re-use
$superZoomNav.removeClass("ThumbNailNav")
.addClass("SuperThumbNailNav")
.height("")
.css("visibility", "hidden");
$superZoomNav.find(".ThumbNailNavClip > ul").css({ "left": "", "top": "0" });
methods.addCarouselScrolling();
$superZoomDiv.append($superZoomNav);
$superZoomNav.SuperZoomComponent(params);
$("body").css("overflow", "hidden");
if (targetWidth > 1917) {
targetWidth = 1917;
}
if (targetHeight > 2700) {
targetHeight = 2700;
}
$superZoomDiv.css("opacity", 0).css({
"top": $(document).scrollTop() + 5,
"width": targetWidth,
"height": targetHeight
});
$("section.CurrentContent").append($superZoomDiv);
$superZoomDiv.animate({ opacity: 1 }, 300);
if (isTouchEnabled) {
if (navigator.userAgent.toLowerCase().indexOf("windows") != -1 && navigator.userAgent.toLowerCase().indexOf("touch") != -1) {
$superZoomNav.SuperZoomComponent("initForWin8"); //for Surface device
} else {
$superZoomNav.SuperZoomComponent("initForIpad");
}
var carouselSuperZoom = { TargetElement: '.SuperThumbNailNav' };
methods.TouchInitCarouselZoom(carouselSuperZoom);
}
methods.TrackZoomGAEvents("PDP", "tap", "pdp-product-image-open-superzoom");
},
openPhoneZoom: function () {
var phoneZoomWrapper = $("");
var phoneZoomCloser = $("Done");
phoneZoomWrapper.click(function () {
$(".phoneZoomWrapper").remove();
$("body").css("overflow-x", "");
});
phoneZoomWrapper.append(phoneZoomCloser);
phoneZoomCloser.click(function () {
$(".phoneZoomWrapper").remove();
$("body").css("overflow-x", "");
});
if (!jqueryUpgradeEnabledOnPage) {
$("").load(function () {
//Get dimensions of new image once it's loaded
var imgObj = methods.getImageDimensions($(this));
phoneZoomWrapper.append($(this));
phoneZoomWrapper.width(imgObj.width);
phoneZoomWrapper.height(imgObj.height);
$("section.CurrentContent").append(phoneZoomWrapper);
$("body").css("overflow-x", "auto");
}).attr('src', targetZoom);
} else {
$("").on("load", function () {
//Get dimensions of new image once it's loaded
var imgObj = methods.getImageDimensions($(this));
phoneZoomWrapper.append($(this));
phoneZoomWrapper.width(imgObj.width);
phoneZoomWrapper.height(imgObj.height);
$("section.CurrentContent").append(phoneZoomWrapper);
$("body").css("overflow-x", "auto");
}).attr('src', targetZoom);
}
},
///////////////////////Get Object Dimensions/////////////////////
getImageDimensions: function (image) {
var oldIE = isIeVersionOlderThan9;
//Different browsers require different treatment for measuring dynamically loaded images
//Chrome stuggles to measure images if they are already cached, earlier versions of IE don't support
//naturalHeight/width - need to try a few different methods to ensure we get a result
//First try to get image dimensions using naturalHeight/width
var imgHeight = $(image).prop('naturalHeight');
var imgWidth = $(image).prop('naturalWidth');
//If that doesn't work, try to measure the image the 'standard' jQuery way
if (imgHeight === undefined || imgHeight === 0) {
imgHeight = $(image).height();
imgWidth = $(image).width();
}
//If we still have no dimensions, bind a hidden copy of the image to the body and measure that
if (imgHeight === undefined || imgHeight === 0 || oldIE) {
var img = $(image).clone();
$(img).css("display", "none");
$("body").append(img);
imgHeight = $(img).height();
imgWidth = $(img).width();
$(img).remove();
}
//Finally, if we still have no dimensions (and we 99.99% should by now), hard code the most common dimensions
if (imgHeight === undefined || imgHeight === 0) {
imgHeight = 472;
imgWidth = 315;
}
var imgObj = {
height: imgHeight,
width: imgWidth
};
return imgObj;
},
TrackZoomGAEvents: function (Category, Action, Label, Value) {
try {
if (window.TrackGAEvent) {
TrackGAEvent(Category, Action, Label, Value);
}
} catch (e) { }
},
hidePublicationImagesAndUpdateLiItems: function (shotType) {
if (Next.Settings.Channel.PliNewMediaFormatIsEnabled && Next.Settings.Channel.PliBehaviourIsEnabled) {
var imageTypeAttribute = "[data-shot-type='" + shotType + "']";
var visibleImagesCount = totalImages - $(imageTypeAttribute).length;
for (var i = 0; i < $(imageTypeAttribute).length; i++) {
var index = $(imageTypeAttribute).eq(i).index();
//Don't hide the best shot(first) image
if (index == 0) {
//Decrement the visibleImagesCount value
visibleImagesCount--;
} else {
//Hide
$(imageTypeAttribute).eq(i).hide();
}
}
methods.publicationLevelImageryHelperMethods.hideChevronsBasedOnVisibleImagesCount(visibleImagesCount);
methods.publicationLevelImageryHelperMethods.updateLastLiElementClass();
methods.setPortraitNavSize();
}
},
hidePublicationImagesAndUpdateLiItemsForAttribute: function (attributeName, attributeValue) {
if (Next.Settings.Channel.PliNewMediaFormatIsEnabled && Next.Settings.Channel.PliBehaviourIsEnabled) {
var imageTypeAttribute = ".shotmedia [" + attributeName + "='" + attributeValue + "']";
var visibleImagesCount = totalImages - $(imageTypeAttribute).length;
for (var i = 0; i < $(imageTypeAttribute).length; i++) {
var index = $(imageTypeAttribute).eq(i).index();
//Don't hide the best shot(first) image
if (index == 0) {
//Decrement the visibleImagesCount value
visibleImagesCount--;
} else {
//Hide
$(imageTypeAttribute).eq(i).hide();
}
}
methods.publicationLevelImageryHelperMethods.hideChevronsBasedOnVisibleImagesCount(visibleImagesCount);
methods.publicationLevelImageryHelperMethods.updateLastLiElementClass();
methods.setPortraitNavSize();
}
},
publicationLevelImageryHelperMethods: {
hideChevronsBasedOnVisibleImagesCount: function (visibleImagesCount) {
if (visibleImagesCount < 2) {
$(".shotNavPrev, .shotNavNext").hide();
} else {
$(".shotNavPrev, .shotNavNext").show();
}
if (visibleImagesCount == 0) {
$(".carouselPrevious, .carouselNext").hide();
} else {
$(".carouselPrevious, .carouselNext").show();
}
},
updateLastLiElementClass: function () {
//Add "last" class to last li if if doesn't have
if (!$(".ThumbNailNavClip ul li:visible:last").hasClass("last")) {
$(".ThumbNailNavClip ul li").removeClass("last");
$(".ThumbNailNavClip ul li:visible:last").addClass("last");
}
}
},
/**
* The default item from the page is the one whose imagery is at the root level at the end of SiteXML - as from the SpreadEngine DB.
* These images will always be book images and their file names will contain the item's PID.
* E.g. PID="123-456" mediaName = "123-456s" or "123456s2" etc.
* So, we can use this fact to determine whether the supplied currentItemNumber is that of the default item.
* @param {string} currentItemNumber
*/
IsDefaultItem: function (currentItemNumber) {
if (Next.Settings.Channel.PliNewMediaFormatIsEnabled && Next.Settings.Channel.PliBehaviourIsEnabled) {
if (typeof currentItemNumber === "string" && methods.Vars.shotData.Media && methods.Vars.shotData.Media.length > 0) {
return RegExp(currentItemNumber.replace("-","-?")).test(methods.Vars.shotData.Media[0].name);
}
}
return false;
}
};
$.fn.ZoomComponent = function (method) {
// Method calling
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.ZoomComponent');
}
};
})(jQuery);
/////////////////////// Super Zoom ////////////////////////////////////
(function ($) {
var mx, my;
var superMoveInterval = null;
var currentZoomImageDimensions;
var smoothScroll = 5;
var curTopNavItemIndex = 0;
var startDragX, startDragY;
var startImageOffsetX, startImageOffsetY;
var isSuperZoomDragging = false;
//var currentScale = 1;
//var lastUsedScale = 1;
var navLiMargin = 10;
var carouselScrollAnimationSpeed = 300;
var isCarouselScrolling = false;
var methods = {
///////////////////////Init////////////////////////////////////
init: function (params) {
var ul = this.find("ul");
//Reset width and height of ul
$(ul).width(51);
setTimeout(function () {
methods.setSuperNavHeight(ul);
}, 500);
methods.buildSuperZoomBox(this, params.defaultImage);
methods.addNavEventHandlers(this);
window.onresize = function () {
var $superZoom = $(".SuperZoom");
var $superZoomBox = methods.Vars.$superZoomBox;
var $superThumbnailNav = methods.Vars.$superThumbnailNav;
var $thumbnailNavNavClip = $superThumbnailNav.find(".ThumbNailNavClip");
$superZoom.css({
"width": $(window).width() - 10,
"height": $(window).height() - 10
});
$superZoomBox.css({
"width": $superZoom.width() - $superThumbnailNav.outerWidth(),
"height": $superZoom.height()
});
methods.setSuperNavHeight($thumbnailNavNavClip.find("ul"));
};
},
Vars: {
$superZoom: null,
$superZoomBox: null,
$superThumbnailNav: null,
$thumbnailNavNavClip: null
},
setSuperNavHeight: function (ul) {
var $ul = $(ul);
var $SuperThumbNailNav = methods.Vars.$superThumbnailNav;
var $ThumbNailNavClip = $SuperThumbNailNav.find(".ThumbNailNavClip");
var $carouselPrevious = $SuperThumbNailNav.find(".carouselPrevious");
var $carouselNext = $SuperThumbNailNav.find(".carouselNext");
var ulHeight = $ul.height();
var maxClipHeight = $(window).height() - 100;
$carouselPrevious.addClass("disabled");
if (maxClipHeight < ulHeight) {
$ThumbNailNavClip.height(maxClipHeight);
$carouselNext.removeClass("disabled");
} else {
$ThumbNailNavClip.height("");
$SuperThumbNailNav.height("");
$carouselNext.addClass("disabled");
}
if ($ul.children().length > 1) {
$SuperThumbNailNav.css("visibility", "visible");
}
},
////////////////////Build Super Zoom Box ////////////////////////
buildSuperZoomBox: function (nav, defaultImage) {
//Create new element
var SuperZoomBox = $("");
//Set initial target dimensions
var targetHeight = $(window).height();
var targetWidth = $(window).width();
//Limit dimensions and account for navigation
if (targetWidth > 1917) {
targetWidth = 1800;
} else {
targetWidth -= 80; //navigation width
}
if (targetHeight > 2700) {
targetHeight = 2680;
} else {
targetHeight -= 10;
}
if (isTouchEnabled) {
targetWidth -= 20;
} else {
targetWidth -= 5;
}
//Set dimensions
$(SuperZoomBox).css({
"width": targetWidth,
"height": targetHeight
});
//Add the super zoom box
$(nav).after(SuperZoomBox);
//Build close button html
var $closeBtn = $("")
.addClass("close")
.attr("href", "#");
$(SuperZoomBox).append($closeBtn);
if (defaultImage) {
methods.loadSuperZoomImage(defaultImage);
} else {
//If an image is already selected in the nav, load it into the superzoom
var selectedItem = $(nav).find("ul li.selected");
if (selectedItem.length) {
var targetPath = $(selectedItem).find("a").attr("rel");
methods.loadSuperZoomImage(targetPath);
}
}
if (!isTouchEnabled) {
//Close superzoom on click
$(SuperZoomBox).bind("click", function (e) {
e.stopPropagation();
methods.closeSuperZoom();
});
//Capture mouse move
$(SuperZoomBox).bind('mousemove', this, function (e) {
mx = e.pageX;
my = e.pageY;
});
//Mouse hover/leave
$(SuperZoomBox).hover(
function () {
$(SuperZoomBox).addClass("SuperZoomBoxHover");
superMoveInterval = setTimeout(function () { methods.superMove(); }, 30);
//methods.toggleShotControls(true);
},
function () {
$(SuperZoomBox).removeClass("SuperZoomBoxHover");
clearTimeout(superMoveInterval);
//methods.toggleShotControls(false);
});
}
methods.Vars.$superThumbnailNav = nav;
methods.Vars.$superZoomBox = $(SuperZoomBox);
methods.addControlHandlers($closeBtn);
},
initForIpad: function () {
var superZoomBoxDiv = document.getElementById("SuperZoomBoxDiv");
superZoomBoxDiv.parentNode.addEventListener('touchmove', function (e) {
e.preventDefault();
}, false);
superZoomBoxDiv.addEventListener('touchstart', function (e) {
$("#SuperZoomBoxDiv").addClass("SuperZoomBoxHover");
mx = e.targetTouches[0].pageX;
my = e.targetTouches[0].pageY;
methods.superMobileMove();
}, false);
superZoomBoxDiv.addEventListener('touchmove', function (e) {
e.preventDefault();
mx = e.targetTouches[0].pageX;
my = e.targetTouches[0].pageY;
methods.superMobileMove();
}, false);
superZoomBoxDiv.addEventListener('touchend', function () {
methods.Vars.$superZoomBox.removeClass("SuperZoomBoxHover");
methods.handlePointerUp();
}, false);
},
initForWin8: function () {
var superZoomBoxDiv = document.getElementById("SuperZoomBoxDiv");
if (navigator.maxTouchPoints > 0) {//IE11
$(superZoomBoxDiv).css({ "touch-action": "none" });
}
else if (navigator.msMaxTouchPoints > 0) {//IE10
$(superZoomBoxDiv).css({ "-ms-touch-action": "none" });
}
if (navigator.maxTouchPoints > 0) {//IE11
superZoomBoxDiv.parentNode.addEventListener('pointermove', function (e) {
e.preventDefault();
}, false);
}
else if (navigator.msMaxTouchPoints > 0) {//IE10
superZoomBoxDiv.parentNode.addEventListener('MSPointerMove', function (e) {
e.preventDefault();
}, false);
}
if (navigator.maxTouchPoints > 0) {//IE11
superZoomBoxDiv.addEventListener('pointerdown', function (e) {
if (e.pointerType) {
if (e.pointerType === "touch") {
methods.handlePointerEvent(e);
}
}
}, false);
superZoomBoxDiv.addEventListener('pointermove', function (e) {
if (e.pointerType) {
if (e.pointerType === "touch") {
methods.handlePointerEvent(e);
}
}
}, false);
superZoomBoxDiv.addEventListener('pointerup', function (e) {
if (e.pointerType) {
if (e.pointerType === "touch") {
methods.handlePointerUp(e);
}
}
}, false);
}
else if (navigator.msMaxTouchPoints > 0) {//IE10
superZoomBoxDiv.addEventListener('MSPointerDown', function (e) {
if (e.pointerType) {
if (e.pointerType === e.MSPOINTER_TYPE_TOUCH) {
methods.handlePointerEvent(e);
methods.superMobileMove();
}
else {
return false;
}
}
}, false);
superZoomBoxDiv.addEventListener('MSPointerMove', function (e) {
if (e.pointerType) {
if (e.pointerType === e.MSPOINTER_TYPE_TOUCH) {
methods.handlePointerEvent(e);
methods.superMobileMove();
}
else {
return false;
}
}
}, false);
superZoomBoxDiv.addEventListener('MSPointerUp', function (e) {
if (e.pointerType) {
if (e.pointerType === e.MSPOINTER_TYPE_TOUCH) {
methods.handlePointerUp();
}
else {
return false;
}
}
}, false);
}
},
handlePointerEvent: function (e) {
mx = e.pageX;
my = e.pageY;
},
handlePointerUp: function () {
isSuperZoomDragging = false;
},
addControlHandlers: function (closeZoomBox) {
//Add click handler for close button
$(closeZoomBox).bind("click", function (event) {
if (history.state == "zoom")
window.history.back();
event.stopPropagation();
event.preventDefault();
methods.closeSuperZoom();
});
},
closeSuperZoom: function () {
//Close the super zoom box
$(".SuperZoom").fadeOut(300, function () {
$(".SuperZoom").remove();
superMoveInterval = null;
$("body").css("overflow", "");
});
},
initDragParams: function (superZoomImage) {
startDragX = mx;
startDragY = my;
startImageOffsetX = $(superZoomImage).position().left;
startImageOffsetY = $(superZoomImage).position().top;
},
///////////////Device super move///////////////////////////
superMove: function () {
var superZoomBox = methods.Vars.$superZoomBox;
var superZoomImage = methods.Vars.$superZoomBox.find("img");
if ($(superZoomImage).length) {
var superZoomBoxWidth = $(superZoomBox).width();
var superZoomBoxHeight = $(superZoomBox).height();
var mousePercentX = (mx - $(superZoomBox).offset().left) / (superZoomBoxWidth / 100);
var mousePercentY = (my - $(superZoomBox).offset().top) / (superZoomBoxHeight / 100);
var currentImageX = $(superZoomImage).offset().left;
var currentImageY = $(superZoomImage).offset().top;
var superImageFactorX = currentZoomImageDimensions.width / 100;
var superImageFactorY = currentZoomImageDimensions.height / 100;
var endTargetX = 0 - (superImageFactorX * mousePercentX) + (superZoomBoxWidth * mousePercentX / 100) + $(superZoomBox).offset().left;
var endTargetY = 0 - (superImageFactorY * mousePercentY) + (superZoomBoxHeight * mousePercentY / 100) + $(superZoomBox).offset().top;
var difX = currentImageX - endTargetX;
var difY = currentImageY - endTargetY;
var targetX = currentImageX - (difX / smoothScroll);
var targetY = currentImageY - (difY / smoothScroll);
targetX = Math.round(targetX);
targetY = Math.round(targetY);
$(superZoomImage).offset({ left: targetX, top: targetY });
}
if ($(superZoomBox).hasClass("SuperZoomBoxHover")) {
superMoveInterval = setTimeout(function () { methods.superMove(); }, 30);
}
},
superMobileMove: function () {
var $superZoomBox = methods.Vars.$superZoomBox;
var $superZoomImage = methods.Vars.$superZoomBox.find("img");
if ($superZoomImage.length) {
if (!isSuperZoomDragging) {
methods.initDragParams($superZoomImage);
isSuperZoomDragging = true;
}
var superZoomBoxWidth = $superZoomBox.width();
var superZoomBoxHeight = $superZoomBox.height();
var mouseDiffX = mx - startDragX;
var mouseDiffY = my - startDragY;
var targetMoveX = mouseDiffX + startImageOffsetX;
var targetMoveY = mouseDiffY + startImageOffsetY;
if (targetMoveX > 0) {
targetMoveX = 0;
}
if (targetMoveY > 0) {
targetMoveY = 0;
}
var minX = 0 - ($superZoomImage.width() - superZoomBoxWidth);
var minY = 0 - ($superZoomImage.height() - superZoomBoxHeight);
if (targetMoveX < minX) {
targetMoveX = minX;
}
if (targetMoveY < minY) {
targetMoveY = minY;
}
if ($superZoomImage.width() < superZoomBoxWidth) {
targetMoveX = 0;
}
if ($superZoomImage.height() < superZoomBoxHeight) {
targetMoveY = 0;
}
var cssString = 'translate3d(' + targetMoveX + 'px ,' + targetMoveY + 'px,0)';
$superZoomImage.css('-webkit-transform', cssString);
$superZoomImage.css('-moz-transform', cssString);
$superZoomImage.css('transform', cssString);
}
},
////////////////////Add nav handler ////////////////////////
addNavEventHandlers: function (nav) {
var $nav = $(nav);
//Nav item selection
$nav.find("li").each(function () {
$(this).find("a").bind("click", function () {
methods.loadSuperZoomImage($(this).attr("rel"));
$(this).closest("ul").find("li").removeClass("selected");
$(this).parent().addClass("selected");
return false;
});
});
//Nav scrolling
$nav.find("a.carouselNext").bind("click", function () {
if ($(this).hasClass("disabled")
|| isCarouselScrolling) {
return false;
}
isCarouselScrolling = true;
$(nav).find("a.carouselPrevious").removeClass("disabled");
var ul = $(nav).find("ul");
var topVisibleItem = $(ul).find("li").eq(curTopNavItemIndex);
var distance = $(topVisibleItem).outerHeight() + navLiMargin;
curTopNavItemIndex++;
$(ul).animate({ top: "-=" + distance },
{
duration: carouselScrollAnimationSpeed,
complete: function () {
isCarouselScrolling = false;
var lastItemOffset = ul.find("li:last-child").offset().top + distance;
var navEndOffset = methods.Vars.$superThumbnailNav.find("#ZoomNavEnd").offset().top + navLiMargin;
if (lastItemOffset < navEndOffset) {
$(nav).find("a.carouselNext").addClass("disabled");
}
}
});
});
//Nav scrolling
$nav.find("a.carouselPrevious").bind("click", function () {
if ($(this).hasClass("disabled")
|| isCarouselScrolling) {
return false;
}
isCarouselScrolling = true;
$(nav).find("a.carouselNext").removeClass("disabled");
var ul = $(nav).find("ul");
var topVisibleItem = $(ul).find("li").eq(curTopNavItemIndex - 1);
var distance = $(topVisibleItem).outerHeight() + navLiMargin;
curTopNavItemIndex--;
$(ul).animate({ top: "+=" + distance },
{
duration: carouselScrollAnimationSpeed,
complete: function () {
isCarouselScrolling = false;
if (curTopNavItemIndex <= 0) {
$(nav).find("a.carouselPrevious").addClass("disabled");
}
}
});
});
},
/////Bind an image to the superzoom container and set image dimensions
bindSuperZoomImage: function (img) {
//Fade out any existing super zoom image
methods.Vars.$superZoomBox.find("img").fadeOut(200, function () {
$(this).remove();
});
//Initially hide the new image so we can fade it in
$(img).hide();
//Append the image to the superzoom container
methods.Vars.$superZoomBox.append(img);
//Fade image in
$(img).fadeIn(300);
//Set initial position - the dynamic position will kick in immediately so this
//is just a starting point
$(img).css("top", 0).css("left", 0);
//Store the new image dimensions
currentZoomImageDimensions = methods.getImageDimensions(img);
},
//////////////////Load Super Zoom Image///////////////////////
loadSuperZoomImage: function (path) {
//Create new image
var tmpImg = new Image();
//This fixes cached images not showing in IE8 and below
if (!jqueryUpgradeEnabledOnPage) {
if ($.browser.msie && $.browser.version < 9) {
path += "?rnd=" + Math.random().toString(36).substring(7);
tmpImg.src = path;
}
//On image load, pass to the binding method
$(tmpImg).load(function () {
methods.bindSuperZoomImage(tmpImg);
}).attr("src", path);
} else {
if (navigator.userAgent.toLowerCase().indexOf('msie') > -1 && document.documentMode < 9) {
path += "?rnd=" + Math.random().toString(36).substring(7);
tmpImg.src = path;
}
//On image load, pass to the binding method
$(tmpImg).on("load", function () {
methods.bindSuperZoomImage(tmpImg);
}).attr("src", path);
}
return false;
},
///////////////////////Get Object Dimensions/////////////////////
getImageDimensions: function (image) {
//Different browsers require different treatment for measuring dynamically loaded images
//Chrome stuggles to measure images if they are already cached, earlier versions of IE don't support
//naturalHeight/width - need to try a few different methods to ensure we get a result
//First try to get image dimensions using naturalHeight/width
var imgHeight = $(image).prop('naturalHeight');
var imgWidth = $(image).prop('naturalWidth');
//If that doesn't work, try to measure the image the 'standard' jQuery way
if (imgHeight === undefined || imgHeight === 0) {
imgHeight = $(image).height();
imgWidth = $(image).width();
}
//Finally, if we still have no dimensions, bind a hidden copy of the image to the body and measure that
if (imgHeight === undefined || imgHeight === 0) {
var img = $(image).clone();
$(img).css("display", "none");
$("body").append(img);
imgHeight = $(img).height();
imgWidth = $(img).width();
$(img).remove();
}
var imgObj = {
height: imgHeight,
width: imgWidth
};
return imgObj;
}
};
$.fn.SuperZoomComponent = function (method) {
// Method calling
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.SuperZoomComponent');
}
};
})(jQuery);