/* THIS IS GLOBALLY INCLUDED ON EVERY PAGE. SO PUT STUFF HERE YOU NEED TO BE ON *EVERY* PAGE OF THE WEBSITE Previously called inc-htmlheader.js NOTE We do not currently minify this file as it runs PHP and if we minify it then it doesn't work properly */ /* FIX MASONARY SCROLLING / SCROLL POSITION FIX ============================================ https://docs.google.com/document/d/1-1PAigX6sEZtajLeOvMBZYN8qTHWzJ6SD1VsMCY_Vtc/edit# PROBLEM ======= When pressing the back/forward button the scroll position is in the wrong place, this is due to the scroll position being set on the browser before the masonary has initlaised SOLUTION ======== Store the scroll position in session storage for each page, then if the back/forward button is pressed load the scroll position from the session stoarge instead of relying on the browser sessionStorage info - https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage beforeunload even info - https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event */ // Store if we want to scroll to allow scroll to session or not (EG if we have just refreshed page, we dont want to scroll) // as if the user has refreshed the page for instance, it would be silly to override the scroll (as its not needed) var bIgnoreScrollToSession = false; // On load (self contained) $(function () { // Store the path name var sPathName = document.location.pathname; // Store the last page visited var sLastPageVisited = ""; // If we have a last page visited (https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) if( sessionStorage["sLastPageVisited"] ) { // Store the last page visited () sLastPageVisited = sessionStorage.getItem("sLastPageVisited"); } // If the last page visited isn't empty if( sLastPageVisited != "" ) { // If we have just refreshed the page if( sLastPageVisited == sPathName ) { // Set the scroll top to the top of the page( this will stop fScrollToSession doing anything) $(document).scrollTop(0); bIgnoreScrollToSession = true; } } // If there is a grid (masonary) if( $('.grid').length ) { // Store when the images have been loaded meaning the masonary layout has been loaded $('.grid').imagesLoaded( function() { // Scroll to the session once the masonary has loaded fScrollToSession( sPathName ); }); } // If there is no grid (masonary) else { // Scroll to the required location instantly fScrollToSession( sPathName ); } // Add event listener (https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event) window.addEventListener("beforeunload", function(event) { // Store the scroll position var nScrollPosition = $(document).scrollTop(); // Store the scroll position in storage sessionStorage.setItem("nScrollPosition_" + sPathName, nScrollPosition.toString()); // Store the last page visted sessionStorage.setItem("sLastPageVisited", sPathName); }); }); // Scrolls to a specific stored session point as requried when the page loads function fScrollToSession( sPathName ) { // If the session storage for this page has been set, then we have viewed this page before if (sessionStorage["nScrollPosition_" + sPathName]) { // If the page has a scroll position defined already, then we must of clicked the back link so we should scroll to the stored position if( $(document).scrollTop() >= 50 && sessionStorage.getItem("nScrollPosition_" + sPathName ) > 0 && !bIgnoreScrollToSession) { // Set the scroll top to the sessions position $(document).scrollTop( sessionStorage.getItem("nScrollPosition_" + sPathName ) ); } } } // On docuent ready $(document).ready(function() { /* SET THE INITAL STATE OF THE SEARCH HEADER IF ON MOBILE ====================================================== When we are on mobile, we need to detect if the scroll position is out of range of the header, if it is then we need to disable it. */ // If the width is less than 650px if( $(window).width() <= 650 ) { // If the scroll top is out of range if( $(this).scrollTop() > ( $(window).height() * 4) ) { // If the bar is hidden if(!bIsNavbarHidden) { // Hide the header $("#floatingHeader").addClass("hide-header"); // Set is hidden to true bIsNavbarHidden = true; } } // If the scroll top is in range else { // If the bar isn't hidden if(bIsNavbarHidden) { // Show the header $("#floatingHeader").removeClass("hide-header"); // Set is hidden to false bIsNavbarHidden = false; } } } }); // Store if the navbar is hidden or not. var bIsNavbarHidden = false; $(document).scroll(function() { // Store the window height var nWindowHeight = $(window).height(); // If the window width is greater than 980px if( $(window).width() > 980 ) { // If the scroll position is greater than the container height if( $(this).scrollTop() > nWindowHeight ) { // If the bar is hidden if(!bIsNavbarHidden) { // Hide the container $("#navContainer").hide(500); // Set is hidden to true bIsNavbarHidden = true; } } // Othweise else { // If the bar isn't hidden if(bIsNavbarHidden) { // Show the container $("#navContainer").show(500); // Set is hidden to false bIsNavbarHidden = false; } } } // If the window is on mobile else { // If the width is less than 650px if( $(window).width() <= 650 ) { // If the scroll top is out of range if( $(this).scrollTop() > ( nWindowHeight * 4) ) { // If the bar is hidden if(!bIsNavbarHidden) { // Hide the header $("#floatingHeader").addClass("hide-header"); // Set is hidden to true bIsNavbarHidden = true; } } // If the scroll top is in range else { // If the bar isn't hidden if(bIsNavbarHidden) { // Show the header $("#floatingHeader").removeClass("hide-header"); // Set is hidden to false bIsNavbarHidden = false; } } } } }); function fMenu_ShowHide() { $('#mnav').toggleClass('mnav-wrapper-expanded'); } $('#menubutton').on('click',function(){ fMenu_ShowHide(); }); function fSubMenu(sMenuName) { var sElementID = "#mnav-" + sMenuName; $(sElementID).toggleClass('mnav-wrapper-expanded'); } function fMenuBack(sMenuName) { var sElementID = "#mnav-" + sMenuName; $(sElementID).toggleClass('mnav-wrapper-expanded'); } function fShowPageMessage(sPageMessage, bHideCloseButton = false, bShowWishlistButton = false){ $("#page-message").html(sPageMessage); $("#page-message-modal").modal('show'); $('body').addClass("no-padding"); $('#page-message-modal').addClass("no-padding"); if (bHideCloseButton==true) { document.getElementById('modalCloseButton').style.display='none'; } else { document.getElementById('modalCloseButton').style.display=''; } if (!bShowWishlistButton) { document.getElementById('modalWishlistButton').style.display='none'; } else { document.getElementById('modalWishlistButton').style.display=''; } } function fShowPageMessages() { if(aMessages.length > 0) { var sMessage = aMessages.shift(); fShowPageMessage(sMessage); $('#page-message-modal').on('hidden', function () { fShowPageMessages(); }); } } function fShowPageMessage_HTMLFromURL(sURLToGrabHTMLFrom, bHideCloseButton = false){ $.get( sURLToGrabHTMLFrom, function( data ) { $("#page-message").html(data); $("#page-message-modal").modal('show'); $('body').addClass("no-padding"); $('#page-message-modal').addClass("no-padding"); if (bHideCloseButton==true) { document.getElementById('modalCloseButton').style.display='none'; } else { document.getElementById('modalCloseButton').style.display=''; } }); } /* fToggleFullscreenSearch() ========================= Simply toggles the full screen search bar to display/hide. It also disables scrolling, and provides callbacks for when the search is opened or closed */ /* DECALRE REQUIRED VARIABLES FOR fToggleFullscreenSearch ====================================================== */ // Store if the full screen is open or not var bIsFullscreenSearchOpen = false; // Store the scroll position when we open the modal var nLastStoredTopPosition = 0; // Store a function callback for on open var fOnSearchFullscreenOpen = false; // Store a function callback for on close var fOnSearchFullscreenClose = false; /* FUNCTION ======== */ function fToggleFullscreenSearch() { // Hide the loader incase it is still open $("#loader-wrapper").hide(); // Toggle the search $("#full-screen-search").toggle(); // If the full screen is closed if(!bIsFullscreenSearchOpen) { // Store the last known scroll position so we can scroll back to the position required when we close nLastStoredTopPosition = parseInt( $(window).scrollTop() ); // Set the body to position fixed to remove the scroller $("body").css( "position", "fixed" ); // Set the top position of the body $("body").css( "top", nLastStoredTopPosition + "px" ); // If there is a callback defined if(fOnSearchFullscreenOpen) { // Call it fOnSearchFullscreenOpen(); } // Set the background of the header to its normal colour $("#floatingHeader").css('background', 'rgba(10,10,10,1)' ); } // If the full screen is open else { // Reset the position css tag of the body $("body").css( "position", "" ); // Reset the top style of the body $("body").css( "top", "" ); // Scroll to the position we were before we opened the search box $('html, body').animate( { scrollTop: nLastStoredTopPosition }, 1); // If there is a callback defined if(fOnSearchFullscreenClose) { // Call it fOnSearchFullscreenClose(nLastStoredTopPosition); // Set the is searching to false so users can search again bIsSearchingOnFullscreen = false; } } // Toggle the bIsFullscreenSearchOpen boolean bIsFullscreenSearchOpen = !bIsFullscreenSearchOpen; } /* fApplyFullscreenSearch(oEvent) ============================== Handless all logic for searching using the full screen search on mobile. */ /* DECALRE REQUIRED VARIABLES FOR fToggleFullscreenSearch ====================================================== */ // Store if we are currently searching or not var bIsSearchingOnFullscreen = false; /* FUNCTION ======== */ function fApplyFullscreenSearch(oEvent) { // If we have already made a search query if(bIsSearchingOnFullscreen) { // We can't make another search until the other one is done, so return false return false; } // Prevent browsers default action oEvent.preventDefault(); // Toggle the load icon $("#loader-wrapper").toggle(); // Set the input to not active (Removes keyboard on mobile) $("#mobile-search-input").blur(); // Set the searching on mobile boolean to true bIsSearchingOnFullscreen = true; /* SEARCH LOGIC GOES FOR MOBILE HERE! */ // Return false so the form doesn't do anything! return false; } /* fApplyDesktopSearch() ===================== Called when the search button is pressed on desktop, if the button is pressed and the search bar isn't open it just opens it. Otherwise it will do the search! */ function fApplyDesktopSearch() { // If the desktop search button has the desktop-search class if( $("#desktop-search-button").hasClass("desktop-search") ) { // submit the form document.fSearchTop.submit(); } // If the desktop search button hasn't got the desktop-search class else { // Just toggle the seach open fToggleDesktopSearch(); } } /* fToggleDesktopSearch() ====================== Opens or closes the desktop search bar depending on the current state of the desktop search. */ function fToggleDesktopSearch() { // If the search bar is closed but has been pressed to open if( !$("#desktop-search-button").hasClass("desktop-search") ) { // Make the input focus $("#desktop-search-input").focus(); // Set timeout for 200ms setTimeout ( // Set timeout function function() { // Simply toggle the class on $("#desktop-search-close").toggleClass("desktop-search-close-active"); } , 200) } // If the search bar is being closed else { // Simply toggle the class on $("#desktop-search-close").toggleClass("desktop-search-close-active"); } // Add the active state to desktop search $("#desktop-search-button").toggleClass("desktop-search"); /* HANDLE INPUT CLASS SWAPPING =========================== */ $("#desktop-search-input").toggleClass("desktop-search-input-disabled"); $("#desktop-search-input").toggleClass("desktop-search-input-active" ); } /* CURRENCY SELECTOR ================= Please note that for the catagory_listing this is also done in catalogue_list.js for the bottom page currency selection in the document.ready function (line 54 as of writing this) */ $( "#currencyselector_nCurrency_ID" ).change(function() { // update cookie var nCurrency_ID = $( "#currencyselector_nCurrency_ID" ).val(); //alert(nCurrency_ID); $.cookie("nCurrency_ID", nCurrency_ID, { path: '/', expires: 365 }); // reload page location.reload(); }); /* CURRENCY SELECTOR HEADER NAVIGATION =================================== */ $( "#HeaderCurrencySelect" ).change(function() { // update cookie var nCurrency_ID = $( "#HeaderCurrencySelect" ).val(); $.cookie("nCurrency_ID", nCurrency_ID, { path: '/', expires: 365 }); // reload page location.reload(); }); /* lazyload.js =========== Include minified lazy load */ /* lazyload.js =========== https://docs.google.com/document/d/1QjE8BaIA1pxVMXJzsBVR3dVMcq2bHHa-aUpIsGj_c4A/edit# This file uses IntersectionObserver to load images when they reach a specific point in the viewport. PLEASE READ ME !!!! PLEASE READ ME !!!! PLEASE READ ME !!!! PLEASE READ ME !!!! PLEASE READ ME !!!! PLEASE READ ME !!!! =================== There are bits in this file that are AB / MZ specific, please remove the AB / MZ specific stuff to have this work on other platforms, you might want to download the Pure White Lines repo and take a look at the lazyload there if in doubt If you make any changes here you'll need to minify and put IN lazyload.min.php If you make any changes here you'll need to minify and put IN lazyload.min.php If you make any changes here you'll need to minify and put IN lazyload.min.php If you make any changes here you'll need to minify and put IN lazyload.min.php If you make any changes here you'll need to minify and put IN lazyload.min.php If you make any changes here you'll need to minify and put IN lazyload.min.php If you make any changes here you'll need to minify and put IN lazyload.min.php If you make any changes here you'll need to minify and put IN lazyload.min.php If you make any changes here you'll need to minify and put IN lazyload.min.php If you make any changes here you'll need to minify and put IN lazyload.min.php If you make any changes here you'll need to minify and put IN lazyload.min.php WHY LAZY LOAD? ============== Everytime we want to load an image a request goes out and the image is loaded. This can significantly make loading times of websites poor, so we use a technique called lazyload. Simply put it just makes the images load when the specified viewport goes into a certain range of the image. In the case of PH9 we load images when they reach viewport height + ( viewport height / 2 ) meaning when the image is half of the screens height away from coming in to view, we load the image. HOW TO USE ========== 1) Include this file 2) Give an image the require class A) If you want to modify a HTML img tag use the class "lazy-img" B) If you want to modify the CSS background-image property use "lazy-img-bg" 3) Set the 'data-src' attribute to the actual image src, and remove the 'src' from the image 4) Include IntersectionObserver polyfill Want different image on mobile compared to desktop? simply -: add src-desktop-size onto the image (where size is the pic size suffix for desktop (EG size2)) add src-mobile-size onto the image (where size is the pic size suffix for mobile (EG size3)) Make sure that the data-src is the high resolution / desktop version EXAMPLE ======= BACKGROUND IMAGE EXAMPLE ========================
NORMAL IMAGE EXAMPLE ==================== My Image */ // Store if masonary has loaded or not var bHasMasonaryLoaded = false; // Store if we need to load mobile version of image instead var bLoadMobileImageInstead = false; // Store all images that have the mobile version of the image // loaded if applicable var aImageElementsThatHaveMobileImageLoaded = []; /* ON DOCUMENT LOAD ================ */ $( document ).ready(function() { // On window resize $(window).resize( function() { // Store the last state var bWasLoadingOnMobile = bLoadMobileImageInstead; // Store if we need to load the mobile versions of the image instead (if present) bLoadMobileImageInstead = $(window).width() <= 760; // If we were loading mobile device images, but we no longer are loading mobile device images if( bWasLoadingOnMobile && !bLoadMobileImageInstead ) { // Loop through all the images that we have loaded for mobile devices for( var i = 0; i < aImageElementsThatHaveMobileImageLoaded.length; i++ ) { // Store the image reference var oImage = aImageElementsThatHaveMobileImageLoaded[i]; // Store the image source that we want to laod var sImageSource = oImage.data("src"); // Set the image src to the data-src oImage.attr('src', sImageSource); // Antiques Boutique only, when the image has loaded oImage.on( "load", function() { // Call the on image load function fOnImageLoad( $(this) ); }); } // Remove all elements from array aImageElementsThatHaveMobileImageLoaded = []; } }); $(window).resize(); // Call the lazyloader fInitLazyLoad(); // If there is a grid if( $('.grid').length ) { // When the images in the grid have been loaded (Realistically this should be called instantly) $('.grid').imagesLoaded( function() { // Antiques Boutique only // Set a timeout to refresh the masonary layout after 200ms // This is so when users hit the back button the masonary doesn't look poop setTimeout( function () { // Force the masonry to resize $(".grid").masonry("layout"); }, 200); // Log that the masonary has loaded bHasMasonaryLoaded = true; }); } }); /* fInitLazyLoad() =============== Handles the lazy load IN/OUT ====== PARAM 1 - NOTHING(VOID) RETURNS - NOTHING(VOID) FLOW ==== 1) CREATE CONFIG 2) CREATE OBSERVER 3) INITIALISE OBSERVER */ function fInitLazyLoad() { /* 1) CREATE CONFIG ================ */ // Set the inital margin value for the lazy load var nMargin = $(window).height() / 1.25; // If the user is on mobile, or the screen width is a certain width (In mobile range) if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || $(window).width() <= 760 ) { // Set the margin as a bigger ratio than inital, to ensure images load in time for the image to reach viewport nMargin = $(window).height() * 1.5; } // Set the default config for IntersectionObserver var oConfig = { // Set the root to null so the viewport is used instead of a DOM element root : null, // Set the root margin, so we can start loading ahead of time rootMargin : nMargin + 'px 0px', // Set the threshold threshold : 0.01 }; /* 2) CREATE OBSERVER ================== This is where we actually do the image loading */ // Create an instance of IntersectionObserver var oObserver = new IntersectionObserver( function(aEntries, oSelf) { // Loop through all elements we are observing for( var nIndex = 0; nIndex < aEntries.length; nIndex++ ) { // Store this instance of the entry var oEntry = aEntries[nIndex]; // If the entry is intersecting if( oEntry.intersectionRatio > 0) { // Store a jquery instance of this image var oImage = $(oEntry.target); // If the image is a lazy image if( oImage.hasClass("lazy-img") ) { // Store the image source that we want to laod var sImageSource = oImage.data("src"); // If we are instead wanting to load the mobile version of the image // as we are on a mobile device if( bLoadMobileImageInstead ) { // If the required data in order to handle the different image sizes is set if( oImage.data("src-desktop-size") != undefined && oImage.data("src-mobile-size") != undefined ) { // Replace the image source wiht the mobile size (EG desktop size is size2, replace with size3) sImageSource = sImageSource.replace( oImage.data("src-desktop-size"), oImage.data("src-mobile-size")); // Add the item to the array aImageElementsThatHaveMobileImageLoaded.push(oImage); } } // Set the image src to the data-src oImage.attr('src', sImageSource); // Antiques Boutique only, when the image has loaded oImage.on( "load", function() { // Call the on image load function fOnImageLoad( $(this) ); }); // Remove the lazy-image class oImage.removeClass("lazy-img"); } else // If the image is a lazy image background if( oImage.hasClass("lazy-img-bg") ) { // Store the image source that we want to laod var sImageSource = oImage.data("src"); // If we are instead wanting to load the mobile version of the image // as we are on a mobile device if( bLoadMobileImageInstead ) { // If the required data in order to handle the different image sizes is set if( oImage.data("src-desktop-size") != undefined && oImage.data("src-mobile-size") != undefined ) { // Replace the image source wiht the mobile size (EG desktop size is size2, replace with size3) sImageSource = sImageSource.replace( oImage.data("src-desktop-size"), oImage.data("src-mobile-size")); // Add the item to the array aImageElementsThatHaveMobileImageLoaded.push(oImage); } } // Set the image src to the data-src oImage.css('background-image', "url('" + sImageSource + "')" ); // Remove the lazy-image class oImage.removeClass("lazy-img-bg"); } // Stop observing this image oSelf.unobserve(oEntry.target); } }; }, oConfig); /* 3) INITIALISE OBSERVER ====================== */ // Grab all the images with a class of lazy-img var aImages = document.querySelectorAll('.lazy-img'); // Grab all background images var aBackgroundImages = document.querySelectorAll('.lazy-img-bg'); // Itterate over all images for( var nIndex = 0; nIndex < aImages.length; nIndex++ ) { // If the IntersectionObserver isn't supported then just load the image if( !('IntersectionObserver' in window) ) { // Store a jquery instance of this image var oImage = $( aImages[nIndex] ); // Set the image src to the data-src oImage.attr('src', oImage.data("src") ); // Remove the lazy-image class oImage.removeClass("lazy-img"); // Remove the filter blur if it has it oImage.removeClass("blur-image"); } // If the IntersectionObserver is supported else { // Call the observer to observe oObserver.observe( aImages[nIndex] ); } } // Itterate over all background images for( var nIndex = 0; nIndex < aBackgroundImages.length; nIndex++ ) { // If the IntersectionObserver isn't supported then just load the background image if( !('IntersectionObserver' in window) ) { // Store a jquery instance of this background image var oImage = $( aBackgroundImages[nIndex] ); // Set the background image src to the data-src oImage.css('background-image', 'url(' + oImage.data("src") + ')' ); // Remove the lazy-image class oImage.removeClass("lazy-img-bg"); // Remove the filter blur if it has it oImage.removeClass("blur-image"); } // If the IntersectionObserver is supported else { // Call the observer to observe oObserver.observe( aBackgroundImages[nIndex] ); } } } /* fOnImageLoad() ============== This function is called when the lazyload has finished loading an image. Is it required for AntiquesBoutique as we use masonry javascript plugin. This means that when the image is loaded we need to tell the masonry script that the window needs to be resized as something has changed. We do this by calling "layout" All this function needs to do (AS of writing) is check if the parent has the class item-i to test if it is a masonary item. Then if it is we just call the "layout" function of the masonry object to resize it for us. IN/OUT ====== PARAM 1 : Jquery image DOM object (JQuery Image DOM) RETURNS : Nothing! (Void) */ function fOnImageLoad( oImage ) { // If the image is a masonary element if( oImage.parent().hasClass("item-i") && bHasMasonaryLoaded ) { // If there is a grid if( $('.grid').length ) { // Force the masonry to resize $(".grid").masonry("layout"); } } // Remove the filter blur if it has it oImage.removeClass("blur-image"); }