/* index.js ======== Javascript applied to the home page. HOW THE SLIDER WORKS (PRODUCTS SLIDER) ====================================== The slider for this theme works by having a div positioned absolute, and the left of the slider adjusted to fake the carasol effect. We then move images/products to the start or end of the slider as required. We use Linear interpolation (lerp) to smoothly fade it out https://docs.google.com/document/d/1lf4B8EJa8TO5lJn7r_Z3bDvGaNvmFXBPsMbn_lCOjok/edit#heading=h.jmgn0ahc1mo5 */ /* HOW THE SLIDESHOW WORKS ======================= The slideshow for this theme is in here, it simply calls fLoadNextSlide() on an set interval (nSlideTimeBeforeChange) which loads the next slide by adding the slideshow-slide-active class. The slideshow pre-loads the images to ensure that the slideshow will work correctly, this is done in fSlideshowInit() https://docs.google.com/document/d/1lf4B8EJa8TO5lJn7r_Z3bDvGaNvmFXBPsMbn_lCOjok/edit#heading=h.jmgn0ahc1mo5 */ /* FUNCTIONS ========= fOnWindowScroll() - Called when the user scrolls, handles scroll logic fLoadNextSlide() - Loads the next slide on the slideshow fSlideshowInit() - Initalises the slideshow fOnProductMouseDown() - Handles mouse down event for when the product is clicked fOnProductMouseUp() - Hadnels the logic of what happens when a product is clicked fLerp() - Maths lerp function (between 2 values) fOnElementMove() - The event called when the products slider is moved fEnableDraggableElementHorizontally() - Enabled a DOM element to be dragged left or right fSmoothScrollProducts() - Smoothly scrolls the slider fMoveProductsSliderLeft() - Moves the slider right (when left arrow click) fMoveProductsSliderRight() - Moves the slider left (when right arrow click) */ /* GLOBAL VARIABLES ================ */ // Cache the header DOM element var oHeaderElem = ""; // Store if the header background colour has been added or not var bHasHeaderBackgroundBeenAdded = false; // Store if we have resized or not var bHasResized = false; // Store the product section wrapper element var oProductSectionElement = ""; // Set if we should cancel the smooth scroll var bCancelSmoothScroll = false; // Store if the slider left / right were pressed var bSliderArrowsClicked = false; // Store the last product clicked (mouse down to determin if the product has been clicked or not) var oLastProductClicked = false; // Store the amount the mouse has moved var nMouseMovedTotal_X = 0; var nMouseMovedTotal_Y = 0; // Store the last left before the product slider left has been moved (EG the left position of the container before the action takes place) var nLeftBeforeProductSliderLeftMoved = 0; // Store the last left before the product slider right has been moved var nLeftBeforeProductSliderRightMoved = 0; // Store all the slides (slideshow) var aSlideshowSlides = []; // Store the current slide var nCurrentSlide = 0; // Store the next slide index var nNextSlide = 0; // Store the slide timer in ms var nSlideTimeBeforeChange = 5000; // Store the resize timer var oResizeTimer; // store if we have scaled to mobile or not var bHasScaledToMobile = false; /* ON DOCUMENT READY ================= Called when the document has loaded everything required. Simply initalises everything. */ $( document ).ready( function() { // If we have the products section enabled if( $(".product-section-products-wrapper").length ) { // Add the minumum height body / html classes $("body").addClass("min-height-body-html"); $("html").addClass("min-height-body-html"); } // Initalise the slideshow fSlideshowInit(); // Store the header element oHeaderElem = $(".header"); // Store the product section element oProductSectionElement = $(".product-section-products-wrapper"); // If we have a products section if( oProductSectionElement.length ) { // Enable draggable element for the product section products wrapper fEnableDraggableElementHorizontally( oProductSectionElement[0] ); // If we have 4 products if( $(".product").length == 4 ) { // Then we need to ensure that the left position of the slider is set to 7 (half of padding of 15, so it's evenly spaced) oProductSectionElement[0].style.left = "-7px"; } // On mouse down for product $(".index-product").on('mousedown', function() { // Call relevant even handler fOnProductMouseDown( $(this) ); }); // On touch down for product $(".index-product").on('touchstart', function() { // Call relevant even handler fOnProductMouseDown( $(this) ); }); // On touch up for product $(".index-product").on('touchend', function() { // Call relevant even handler fOnProductMouseUp( $(this) ); }); // On mouse up for product $(".index-product").on('mouseup', function() { // Call relevant even handler fOnProductMouseUp( $(this) ); }); // On mouse click for product $(".index-product").on('click', function( oEvent ) { // Stop the click oEvent.preventDefault(); }); // Add the scroll event to call fOnWindowScroll $(window).scroll( fOnWindowScroll ); // Call it from intial load to set the original required values fOnWindowScroll(); // On window resize $(window).on('resize', function() { /* PREVENT RESIZE ANIMATIONS ========================= When we resize on the homepage since we are animating the height by 5s transition speed, it makes the reszing really slow, so when we resize we quickly cnacel all transition speeds, then re-enable the transitions by removing the class shortly after */ // Add the class that stops all transforms to the body class $("body").addClass("resize-animation-stopper"); // Clear the timer clearTimeout(oResizeTimer); // Set resize timer to equal set timeout function oResizeTimer = setTimeout( function() { // Remove the resize animation stopper class $("body").removeClass("resize-animation-stopper"); }, 400); /* RESIZE HANDLER ============== To ensure we don't waste resources, we only want to handle this resize every 50ms so here we simply ensure that the resize functionality embed can only be called every 50ms by controlling bHasResized */ // If we are resizing on desktop if( $(window).width() > 991 ) { // If we have scaled to mobile, and now going back to desktop if( bHasScaledToMobile ) { // Remove the style $(".product-section-products-wrapper").removeAttr("style"); // Loop through each product $(".product").each( function() { $(this).removeAttr("style"); }) // Ensure the wrapper is correctly displayed $(".product-section-products-wrapper").removeAttr("style"); // If element exists if( $(".product-section-height-fix-square").length ) { // Ensure the wrapper is correctly displayed $(".product-section-height-fix-square").removeAttr("style"); } // If element exists if( $(".product-section-height-fix-portrait").length ) { // Ensure the wrapper is correctly displayed $(".product-section-height-fix-portrait").removeAttr("style"); } // Store that we we haven't scaled to mobile bHasScaledToMobile = false; } // If we haven't resized if( !bHasResized ) { // Set resized to true bHasResized = true; // Store the direction we need to adjust the product section in var sDirection = ""; // If the element's left position is 0 on resize then there must be empty space to the left, so simulate the scrolling of the products if( oProductSectionElement.position().left >= 0 ) { // Store the correct direction sDirection = "right"; } // If the element's left position is less than the window's width on resize then there must be empty space to the right, so simulate the scrolling of the products if( oProductSectionElement.position().left + oProductSectionElement.width() <= $(window).width() ) { // Store the correct direction sDirection = "left"; } // If we have a direction if( sDirection != "" ) { // Call on element move to fix the positioning of the slide fOnElementMove( oProductSectionElement, sDirection ); } // Timeout for 50ms setTimeout(function () { // Put has resized to false bHasResized = false; }, 50); } // If we have less than 4 products and we are resizing on desktop if( $(".product").length < 4 ) { // Then we need to ensure that the left position of the slider is set to 0 oProductSectionElement[0].style.left = "0px"; } // If we only have 4 products else if( $(".product").length == 4) { // Then we need to ensure that the left position of the slider is set to 7 (half of padding-left of product, so it's evenly spaced) oProductSectionElement[0].style.left = "-7px"; } } // If we are resizing on mobile else if( !bHasScaledToMobile ) { // Make the left 0px $(".product-section-products-wrapper").css ( { left : "0px", display : "block", position : "relative" } ); // Loop through each product $(".product").each( function() { $(this).css ( { display : "block" } ) }) // Ensure the wrapper is correctly displayed $(".product-section-products-wrapper").css ( { display : "block", position : "relative", height : "auto" } ); // If element exists if( $(".product-section-height-fix-square").length ) { // Ensure the wrapper is correctly displayed $(".product-section-height-fix-square").css ( { display : "block", position : "relative", height : "auto" } ); } // If element exists if( $(".product-section-height-fix-portrait").length ) { // Ensure the wrapper is correctly displayed $(".product-section-height-fix-portrait").css ( { display : "block", position : "relative", height : "auto" } ); } // Store that we have scaled to mobile bHasScaledToMobile = true; } }); } // IF the products section is disabled else { // There is a white gap caused by padding that may be needed on other pages, but isn't on home page // So if the element that is causing it exists for this page if( $("#dialogAddToBasket_Inner").length != 0 ) { // Set the padding to remove the white space $("#dialogAddToBasket_Inner").css( { padding : "0px" } ); } } // Manually trigger resize to set initals $(window).trigger('resize'); }); /* fOnWindowScroll() ================= Called when the user scrolls, handles all scrolling logic like adding the background colour to the header */ function fOnWindowScroll() { // Cache the scroll position var nScrollPos = $(window).scrollTop(); // If the scroll position is greater than 50 if( nScrollPos >= 50 ) { // If the header background colour hasn't been added if( !bHasHeaderBackgroundBeenAdded ) { // Add the background class onto the header element oHeaderElem.addClass("header-add-background"); // After 800s (Once header fadein animation is done) setTimeout(function () { // If the header state has changed ( If it is false, that means the state has gone back to its original state of no background in which case we don't want the background added) if( bHasHeaderBackgroundBeenAdded ) { // Add the navigation background class $(".header-navigation-link").addClass("nav-element-add-background"); } }, 800); // Remove the bottom border $(".header-inner-wrapper").removeClass("header-inner-wrapper-border-bottom"); // Store that the header background has been added bHasHeaderBackgroundBeenAdded = true; } } // If the scrollpos is less than 50 else { // If the header background colour has been added if( bHasHeaderBackgroundBeenAdded ) { // Remove the background class onto the header element oHeaderElem.removeClass("header-add-background"); // Remove the navigation background class $(".header-navigation-link").removeClass("nav-element-add-background"); // Add the bottom border $(".header-inner-wrapper").addClass("header-inner-wrapper-border-bottom"); // Store that the header background has been removed bHasHeaderBackgroundBeenAdded = false; } } } /* fLoadNextSlide() ================= Loads the next slide in the slideshow sequence by adding / removing slideshow-slide-active */ function fLoadNextSlide() { // Store the new slide index nNextSlide = nCurrentSlide + 1; // If we are currently on the last slide if( nNextSlide >= aSlideshowSlides.length ) { // Show the first slide nNextSlide = 0; } // Set the opacity of the new slide $("#slide-" + nNextSlide ).css( { opacity : 0 } ); // Add the active class to the slideshow $("#slide-" + nNextSlide ).addClass("slideshow-slide-active"); // Remove the parent active status $("#slide-" + nNextSlide).parent().addClass("slideshow-slide-parent-active"); // Fade the current slide out $("#slide-" + nCurrentSlide ).fadeTo(2000, 0); // Fade the new slide in $("#slide-" + nNextSlide ).fadeTo( 2000, 1, function() { // Remove the active status from the old slide $("#slide-" + nCurrentSlide ).removeClass("slideshow-slide-active"); // Remove the parent active status $("#slide-" + nCurrentSlide).parent().removeClass("slideshow-slide-parent-active"); // Update the index nCurrentSlide = nNextSlide; }); // Set timeout setTimeout(function () { // Load the next slide after time fLoadNextSlide(); }, nSlideTimeBeforeChange); } /* fSlideshowInit() ================ Initalises the slideshow by pre-loading images and storing all slideshow elements in an array */ function fSlideshowInit() { // Store itteration index var nIntterationIndex = 0; // Load all the slides $(".slideshow-slide").each( function() { // Add this slide to the array aSlideshowSlides.push( $(this) ); // If this is the first slide if( nIntterationIndex == 0 ) { // Store the current slide nCurrentSlide = nIntterationIndex; // If we have more than 1 slide if( $(".slideshow-slide").length != 1 ) { // Add the slide active to this slide $(this).addClass("slideshow-slide-active"); // Add the slide active to this slide (parent) $(this).parent().addClass("slideshow-slide-parent-active"); // Fade the current slide in $(this).fadeTo(1000, 1); } // If we don't have more than 1 slide else { // Fade the current slide in $(this).fadeTo(1000, 1); } } // Store the background url var sBackgroundImageURL = $(this).css('background-image'); // Remove the url paramater so we just get the URL sBackgroundImageURL = sBackgroundImageURL.replace('url(','').replace(')','').replace(/\"/gi, ""); // Preload the image by attatching it to an image, and the removing it when it has loaded $('').attr( 'src', sBackgroundImageURL ).on('load', function() { // Remove the image $(this).remove(); }); // Increment the index nIntterationIndex++; }); // If we have more than one slide in the slideshow the lets start it, otherwise we don't have to do anything if( nIntterationIndex > 1 ) { // Set timeout setTimeout(function () { // Load the next slide fLoadNextSlide(); }, nSlideTimeBeforeChange); } } /* fOnProductMouseDown() ===================== Called when the user clicks down on a product */ function fOnProductMouseDown( oObjectOnMouseDown ) { // If we have pressed left click if( window.event.which == 1 || !window.event.clientX ) { // Store the last product clicked oLastProductClicked = oObjectOnMouseDown; } } /* fOnProductMouseUp() =================== Called when the user clicks on a product */ function fOnProductMouseUp( oObjectOnMouseUp ) { // If we have pressed left click or we are on mobile if( window.event.which == 1 || !window.event.clientX ) { // If the last product we had the mouse down on is the same product that we have just released the mouse on, then we clicked it if( oLastProductClicked.is( oObjectOnMouseUp ) ) { // If we have moved less than 30px then we can safly say that the user is trying to click instead of drag if( nMouseMovedTotal_X <= 30 && nMouseMovedTotal_Y <= 15 ) { // If animations have been enabled if( typeof fRedirectAndFadeWebsiteOut == 'function' ) { // Redirect the webpage (We use this function if the animation is enabled) fRedirectAndFadeWebsiteOut( oLastProductClicked.attr("href") ); } // If animation has been disabled else { // Redirect window.location = oLastProductClicked.attr("href"); } } } // Reset the last product clicked oLastProductClicked = false; } } /* fLerp() ======= Maths lerp function */ function fLerp( nStart, nEnd, nAmt ) { // Return the lerp value. return (1 - nAmt) * nStart + nAmt * nEnd; } /* fOnElementMove() ================ Handles what happens when the element is moved */ function fOnElementMove( oTargetElement, sDirection ) { // If we are on desktop if( $(window).width() > 991 ) { // Store element as jquery oTargetElement = $(oTargetElement); // If the element is the product section products wrapper if( oTargetElement.hasClass("product-section-products-wrapper") ) { // If we are moving to the left if( sDirection == "left" ) { // Loop through every product $(".product").each( function() { // Store the product var oProduct = $(this); // If we are moving to the left if( sDirection == "left" ) { // If the position of this product is out of bounds if( oProduct.offset().left < -oProduct.outerWidth() ) { // Move the product to the far right of the products wrapper oProduct.appendTo( oTargetElement ); // Move the target element to the correct position oTargetElement[0].style.left = (oTargetElement[0].offsetLeft + oProduct.outerWidth() ) + "px"; } } }); } // If we are moving to the right else if( sDirection == "right") { // Loop through every product but reverse the order (this is to ensure we add the products in the correct order to maintain their position) $( $(".product").get().reverse() ).each( function() { // Store the product var oProduct = $(this); // If the position is out of bounds if( oProduct.offset().left > $(window).width() ) { // Move the product to the far right of the products wrapper oProduct.prependTo( oTargetElement ); // Move the target element to the correct position oTargetElement[0].style.left = (oTargetElement[0].offsetLeft - oProduct.outerWidth(true) ) + "px"; } }); } } } } /* fEnableDraggableElementHorizontally() ===================================== Enables the dragging of an element only horizontally */ function fEnableDraggableElementHorizontally( oElement ) { // Store the drag difference between the last move and this move var nDragDiff = 0; // Store the last left position for this element var nLastLeft = 0; // Store the direction this element is moving in var sMoveDirection = ""; // Store the mouse position var nMouseX = 0; var nMouseY = 0; // Store the last mouse x position var nLastMouseX = 0; var nLastMouseY = 0; /* fOnMouseDownOnElement() ======================= */ function fOnMouseDownOnElement( oEvent ) { // If we have more than 4 products if( $(".product").length > 4 || ( ( $(window).width() <= 991) && $(".product").length > 2 ) ) { // Reset the total amount the mouse has moved nMouseMovedTotal_X = 0; nMouseMovedTotal_Y = 0; // If the event client x is not defined (meaning we are on mobile) if( !oEvent.clientX ) { // Store the touch var oTouch = oEvent.originalEvent.touches[0]; // Store mouse posiitons nLastMouseX = nMouseX - oTouch.pageX; nLastMouseY = nMouseY - oTouch.pageY; nMouseX = oTouch.pageX; nMouseY = oTouch.pageY; } // If they are defined else { // Store mouse posiitons nLastMouseX = nMouseX - oEvent.clientX; nMouseX = oEvent.clientX; } // Cancel the smooth scroll bCancelSmoothScroll = true; // When the user is moving the element $(oElement).on("mousemove", fOnMouseMoveOnElement); $(oElement).on("touchmove", fOnMouseMoveOnElement); // When the user releases this item $(window).on("mouseup", fOnMouseUpOnWindow); $(window).on("touchend", fOnMouseUpOnWindow); } } /* fOnMouseUpOnWindow() ==================== */ function fOnMouseUpOnWindow( oEvent ) { // Stop default oEvent.preventDefault(); // Remove the events $(window).unbind("mouseup", fOnMouseUpOnWindow); $(window).unbind("touchend", fOnMouseUpOnWindow); $(oElement).unbind("mousemove", fOnMouseMoveOnElement); $(oElement).unbind("touchmove", fOnMouseMoveOnElement); // If we are on desktop if( $(window).width() > 991 ) { // If the drag dif is too high if( nDragDiff >= 20 ) { // Clamp nDragDiff = 20; } // If the drag diff is too low else if( nDragDiff <= -20 ) { // Clamp nDragDiff = -20; } // Ensure that we aren't canceling the smooth scroll bCancelSmoothScroll = false; // Request the animation frame requestAnimationFrame( function() { // Smooth scroll the products fSmoothScrollProducts( oElement, Math.round( (nLastLeft + (nDragDiff * 4) ) ), 0, false ); } ); } } /* fOnMouseMoveOnElement() ======================= */ function fOnMouseMoveOnElement( oEvent ) { // Ensure that the event is correctly stored oEvent = oEvent || window.event; // If the event client x is not defined (meaning we are on mobile) if( !oEvent.clientX ) { // Store the touch var oTouch = oEvent.originalEvent.touches[0]; // Store mouse posiitons nLastMouseX = nMouseX - oTouch.pageX; nLastMouseY = nMouseY - oTouch.pageY; nMouseX = oTouch.pageX; nMouseY = oTouch.pageY; } // If they are defined else { // Store mouse posiitons nLastMouseX = nMouseX - oEvent.clientX; nMouseX = oEvent.clientX; } // Store the amount the mouse has been moved nMouseMovedTotal_X += Math.abs( nLastMouseX ); nMouseMovedTotal_Y += Math.abs( nLastMouseY ); // Store the last left position of the element nLastLeft = parseInt(oElement.style.left, 10); // Store the difference in drag values nDragDiff = -( parseInt( oElement.style.left, 10 ) - ( oElement.offsetLeft - nLastMouseX ) ); // If we are on desktop, then handle the actions if( $(window).width() > 991 ) { // Prevent default element behaviour oEvent.preventDefault(); // If it's not a number if( isNaN( nDragDiff ) ) { // Make it so it is a number nDragDiff = 0; } // If we have not moved more in the y direction then we have in the x direction if( Math.abs( nLastMouseY ) <= Math.abs( nLastMouseX ) && Math.abs( nLastMouseX ) >= 4 ) { // Move the element to the left / right oElement.style.left = (oElement.offsetLeft - nLastMouseX) + "px"; // If the drag difference is less than 0 if( nDragDiff < 0 ) { // We have moved the item to the left sMoveDirection = "left"; } else // If the drag difference is more than 0 if( nDragDiff > 0 ) { // We have moved the item to the right sMoveDirection = "right"; } // We haven't dragged at all else { // We haven't moved so unset to move direction sMoveDirection = ""; } // Store the on element move event fOnElementMove( oElement, sMoveDirection ); } } } // When the user clicks this item $(oElement).on('mousedown', fOnMouseDownOnElement); $(oElement).on('touchstart', fOnMouseDownOnElement); } /* fSmoothScrollProducts() ======================= Smoothyl scrolls the products between 2 values by using the fLerp function */ function fSmoothScrollProducts( oElement, nNewLeftPosition, nLastLerpValue, fOnTickCallback ) { // If we aren't canceling the smooth scroll (this is done incase the user grabs the slide again whilst it is smooth scrolling) if( !bCancelSmoothScroll ) { // Store the left var nLeft = oProductSectionElement[0].style.left ; // If the left is not valid if( !nLeft || nLeft == "" ) { // Set the left as 0 nLeft = 0; } // Store this lerp value var nLerpValue = Math.round( fLerp( parseInt(nLeft, 10) , nNewLeftPosition, 0.1 ) ); // Move the element to the left oElement.style.left = nLerpValue + "px"; // If we still need to move left if( parseInt(oElement.style.left, 10) != nNewLeftPosition ) { // If the lerp value is not the same, then we can carry on lerping, otherwise it is stuck/done so we gotta cancel if( nLerpValue != nLastLerpValue ) { // Request the animation frame requestAnimationFrame( function() { // Smooth scroll the products fSmoothScrollProducts( oElement, nNewLeftPosition, nLerpValue, fOnTickCallback ); } ); } } // If the tick callback isn't empty if( fOnTickCallback != false ) { // Call it fOnTickCallback( oElement, nNewLeftPosition, nLastLerpValue ); } } } /* fMoveProductsSliderLeft() ========================= Moves the products slider left */ function fMoveProductsSliderLeft() { // IF we can move the slider if( !bSliderArrowsClicked ) { // Make it so we can't click the arrows bSliderArrowsClicked = true; // After a delay make it so we can click arrows again setTimeout(function () { // Make it so we can click the arrows bSliderArrowsClicked = false; }, 300); // Request the animation frame requestAnimationFrame( function() { // Store the last product (Product to the far right that is due to be placed) var oLastProduct = $(".product").last(); // If the last product is not in view if( oLastProduct.offset().left > $(window).width() ) { // Append the last product onto the far left side oLastProduct.prependTo( oProductSectionElement ); // Move the target element to the correct position oProductSectionElement[0].style.left = (oProductSectionElement[0].offsetLeft - oLastProduct.outerWidth() ) + "px"; } // Store the left var nLeft = oProductSectionElement[0].style.left ; // If the left is not valid if( !nLeft || nLeft == "" ) { // Set the left as 0 nLeft = 0; } // Store the left as parsed int nLeft = parseInt( nLeft, 10); // Store the required data for the last left before the product slider has been moved (lerped) nLeftBeforeProductSliderLeftMoved = nLeft; // Smooth scroll the products fSmoothScrollProducts( oProductSectionElement[0], nLeft + oLastProduct.width(), 0, function() { /* SMOOTH SCROLL FIX LEFT ====================== As we only want to move the product if it is out of the window view, we must include a fix to only move the product when the item is no longer in view. This will cancel the fSmoothScrollProducts, calculate the new position it needs to lerp to and then initate fSmoothScrollProducts again with the new correct position */ // If the last product is no longer in view whilst the lerp is happening, then we can add the first product to the back of the line if( oLastProduct.offset().left > $(window).width() ) { // Store the left var nLeft = oProductSectionElement[0].style.left ; // If the left is not valid if( !nLeft || nLeft == "" ) { // Set the left as 0 nLeft = 0; } // Store the left as parsed int nLeft = parseInt( nLeft, 10); // Store the amount the slider has been moved already (this is useful for later) var nSliderAmountMoved = Math.abs( nLeftBeforeProductSliderLeftMoved ) + nLeft; // Append the first product onto the far right side oLastProduct.prependTo( oProductSectionElement ); // Move the target element to the correct position oProductSectionElement[0].style.left = (oProductSectionElement[0].offsetLeft - oLastProduct.outerWidth() ) + "px"; // Store the left nLeft = oProductSectionElement[0].style.left ; // If the left is not valid if( !nLeft || nLeft == "" ) { // Set the left as 0 nLeft = 0; } // Store the left as parsed int nLeft = parseInt( nLeft, 10); // Cancel the current lerp that is happening bCancelSmoothScroll = true; // After a short time (couple of frames) setTimeout(function () { // Allow smooth scroll again bCancelSmoothScroll = false; // Call the smooth products to move to the new position fSmoothScrollProducts( oProductSectionElement[0], nLeft + ( oLastProduct.outerWidth() - nSliderAmountMoved ), 0, false ); }, 20); } } ); } ); } } /* fMoveProductsSliderRight() ========================== Moves the products slider right */ function fMoveProductsSliderRight() { // IF we can move the slider if( !bSliderArrowsClicked ) { // Make it so we can't click the arrows bSliderArrowsClicked = true; // After a delay make it so we can click arrows again setTimeout(function () { // Make it so we can click the arrows bSliderArrowsClicked = false; }, 300); // Store the first product (Product to the far right that is due to be placed) var oFirstProduct = $(".product").first(); // If the first product is not in view if( oFirstProduct.offset().left < -oFirstProduct.outerWidth() ) { // Append the first product onto the far right side oFirstProduct.appendTo( oProductSectionElement ); // Move the target element to the correct position oProductSectionElement[0].style.left = (oProductSectionElement[0].offsetLeft + oFirstProduct.outerWidth() ) + "px"; } // Store the left var nLeft = oProductSectionElement[0].style.left ; // If the left is not valid if( !nLeft || nLeft == "" ) { // Set the left as 0 nLeft = 0; } // Store the left as parsed int nLeft = parseInt( nLeft, 10); // Store the required data for the last left before the product slider has been moved (lerped) nLeftBeforeProductSliderRightMoved = nLeft; // Smooth scroll the products fSmoothScrollProducts( oProductSectionElement[0], nLeft - oFirstProduct.width(), 0, function( oElement, nNewLeftPosition, nLastLerpValue ) { /* SMOOTH SCROLL FIX RIGHT ======================= As we only want to move the product if it is out of the window view, we must include a fix to only move the product when the item is no longer in view. This will cancel the fSmoothScrollProducts, calculate the new position it needs to lerp to and then initate fSmoothScrollProducts again with the new correct position */ // If the first product is no longer in view whilst the lerp is happening, then we can add the first product to the back of the line if( oFirstProduct.offset().left < -oFirstProduct.outerWidth() ) { // Store the left position of the slider var nLeft = oProductSectionElement[0].style.left ; // If the left is not valid if( !nLeft || nLeft == "" ) { // Set the left as 0 nLeft = 0; } // Store the left as parsed int nLeft = parseInt( nLeft, 10); // Store the amount the slider has been moved already (this is useful for later calculations) var nSliderAmountMoved = nLeftBeforeProductSliderRightMoved - nLeft; // If the slider is less than the product width, then there must be some shit maths somewhere so just clamp as a last resort if( nSliderAmountMoved <= -oFirstProduct.outerWidth() ) { // Clamp the slider amount moved nSliderAmountMoved = -oFirstProduct.outerWidth(); } // Same goes for if its more than product width else if( nSliderAmountMoved >= oFirstProduct.outerWidth() ) { // Clamp the slider amount moved nSliderAmountMoved = oFirstProduct.outerWidth(); } // Append the first product onto the far right side (at the end of the div) oFirstProduct.appendTo( oProductSectionElement ); // Move the product slider wrapper to the correct position oProductSectionElement[0].style.left = (oProductSectionElement[0].offsetLeft + oFirstProduct.outerWidth() ) + "px"; // Store the left nLeft = oProductSectionElement[0].style.left ; // If the left is not valid if( !nLeft || nLeft == "" ) { // Set the left as 0 nLeft = 0; } // Store the left as parsed int nLeft = parseInt( nLeft, 10); // Cancel the current lerp that is happening bCancelSmoothScroll = true; // Request animation frame requestAnimationFrame( function() { // Allow smooth scroll again bCancelSmoothScroll = false; // Call the smooth products to move to the new position fSmoothScrollProducts( oProductSectionElement[0], nLeft - ( oFirstProduct.outerWidth() - nSliderAmountMoved ), 0, false ); }); } } ); } }