/*!
* pagepiling.js 1.5.4 * * https://github.com/alvarotrigo/pagePiling.js * @license MIT licensed * * Copyright (C) 2016 alvarotrigo.com - A project by Alvaro Trigo */
(function ($, document, window, undefined) {
'use strict';
$.fn.pagepiling = function (custom) { var PP = $.fn.pagepiling; var container = $(this); var lastScrolledDestiny; var lastAnimation = 0; var isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0) || (navigator.maxTouchPoints)); var touchStartY = 0, touchStartX = 0, touchEndY = 0, touchEndX = 0; var scrollings = [];
//Defines the delay to take place before being able to scroll to the next section //BE CAREFUL! Not recommened to change it under 400 for a good behavior in laptops and //Apple devices (laptops, mouses...) var scrollDelay = 600;
// Create some defaults, extending them with any options that were provided var options = $.extend(true, { direction: 'vertical', menu: null, verticalCentered: true, sectionsColor: [], anchors: [], scrollingSpeed: 700, easing: 'easeInQuart', loopBottom: false, loopTop: false, css3: true, navigation: { textColor: '#000', bulletsColor: '#000', position: 'right', tooltips: [] }, normalScrollElements: null, normalScrollElementTouchThreshold: 5, touchSensitivity: 5, keyboardScrolling: true, sectionSelector: '.section', animateAnchor: false,
//events afterLoad: null, onLeave: null, afterRender: null }, custom);
//easeInQuart animation included in the plugin $.extend($.easing,{ easeInQuart: function (x, t, b, c, d) { return c*(t/=d)*t*t*t + b; }});
/** * Defines the scrolling speed */ PP.setScrollingSpeed = function(value){ options.scrollingSpeed = value; };
/** * Adds or remove the possiblity of scrolling through sections by using the mouse wheel or the trackpad. */ PP.setMouseWheelScrolling = function (value){ if(value){ addMouseWheelHandler(); }else{ removeMouseWheelHandler(); } };
/** * Adds or remove the possiblity of scrolling through sections by using the mouse wheel/trackpad or touch gestures. */ PP.setAllowScrolling = function (value){ if(value){ PP.setMouseWheelScrolling(true); addTouchHandler(); }else{ PP.setMouseWheelScrolling(false); removeTouchHandler(); } };
/** * Adds or remove the possiblity of scrolling through sections by using the keyboard arrow keys */ PP.setKeyboardScrolling = function (value){ options.keyboardScrolling = value; };
/** * Moves sectio up */ PP.moveSectionUp = function () { var prev = $('.pp-section.active').prev('.pp-section');
//looping to the bottom if there's no more sections above if (!prev.length && options.loopTop) { prev = $('.pp-section').last(); }
if (prev.length) { scrollPage(prev); } };
/** * Moves sectio down */ PP.moveSectionDown = function () { var next = $('.pp-section.active').next('.pp-section');
//looping to the top if there's no more sections below if(!next.length && options.loopBottom){ next = $('.pp-section').first(); }
if (next.length) { scrollPage(next); } };
/** * Moves the site to the given anchor or index */ PP.moveTo = function (section){ var destiny = ;
if(isNaN(section)){ destiny = $(document).find('[data-anchor="'+section+'"]'); }else{ destiny = $('.pp-section').eq( (section -1) ); }
if(destiny.length > 0){ scrollPage(destiny); } };
//adding internal class names to void problem with common ones $(options.sectionSelector).each(function(){ $(this).addClass('pp-section'); });
//if css3 is not supported, it will use jQuery animations if(options.css3){ options.css3 = support3d(); }
$(container).css({ 'overflow' : 'hidden', '-ms-touch-action': 'none', /* Touch detection for Windows 8 */ 'touch-action': 'none' /* IE 11 on Windows Phone 8.1*/ });
//init PP.setAllowScrolling(true);
//creating the navigation dots if (!$.isEmptyObject(options.navigation) ) { addVerticalNavigation(); }
var zIndex = $('.pp-section').length;
$('.pp-section').each(function (index) { $(this).data('data-index', index); $(this).css('z-index', zIndex);
//if no active section is defined, the 1st one will be the default one if (!index && $('.pp-section.active').length === 0) { $(this).addClass('active'); }
if (typeof options.anchors[index] !== 'undefined') { $(this).attr('data-anchor', options.anchors[index]); }
if (typeof options.sectionsColor[index] !== 'undefined') { $(this).css('background-color', options.sectionsColor[index]); }
if(options.verticalCentered && !$(this).hasClass('pp-scrollable')){ addTableClass($(this)); }
zIndex = zIndex - 1; }).promise().done(function(){ //vertical centered of the navigation + first bullet active if(options.navigation){ $('#pp-nav').css('margin-top', '-' + ($('#pp-nav').height()/2) + 'px'); $('#pp-nav').find('li').eq($('.pp-section.active').index('.pp-section')).find('a').addClass('active'); }
$(window).on('load', function() { scrollToAnchor(); });
$.isFunction( options.afterRender ) && options.afterRender.call( this); });
/** * Enables vertical centering by wrapping the content and the use of table and table-cell */ function addTableClass(element){ element.addClass('pp-table').wrapInner('<div class="pp-tableCell" style="height:100%" />'); }
/** * Retuns `up` or `down` depending on the scrolling movement to reach its destination * from the current section. */ function getYmovement(destiny){ var fromIndex = $('.pp-section.active').index('.pp-section'); var toIndex = destiny.index('.pp-section');
if(fromIndex > toIndex){ return 'up'; } return 'down'; }
/** * Scrolls the page to the given destination */ function scrollPage(destination, animated) { var v ={ destination: destination, animated: animated, activeSection: $('.pp-section.active'), anchorLink: destination.data('anchor'), sectionIndex: destination.index('.pp-section'), toMove: destination, yMovement: getYmovement(destination), leavingSection: $('.pp-section.active').index('.pp-section') + 1 };
//quiting when activeSection is the target element if(v.activeSection.is(destination)){ return; }
if(typeof v.animated === 'undefined'){ v.animated = true; }
if(typeof v.anchorLink !== 'undefined'){ setURLHash(v.anchorLink, v.sectionIndex); }
v.destination.addClass('active').siblings().removeClass('active');
v.sectionsToMove = getSectionsToMove(v);
//scrolling down (moving sections up making them disappear) if (v.yMovement === 'down') { v.translate3d = getTranslate3d(); v.scrolling = '-100%';
if(!options.css3){ v.sectionsToMove.each(function(index){ if(index != v.activeSection.index('.pp-section')){ $(this).css(getScrollProp(v.scrolling)); } }); }
v.animateSection = v.activeSection; }
//scrolling up (moving section down to the viewport) else { v.translate3d = 'translate3d(0px, 0px, 0px)'; v.scrolling = '0';
v.animateSection = destination; }
$.isFunction(options.onLeave) && options.onLeave.call(this, v.leavingSection, (v.sectionIndex + 1), v.yMovement);
performMovement(v);
activateMenuElement(v.anchorLink); activateNavDots(v.anchorLink, v.sectionIndex); lastScrolledDestiny = v.anchorLink;
var timeNow = new Date().getTime(); lastAnimation = timeNow; }
/** * Performs the movement (by CSS3 or by jQuery) */ function performMovement(v){ if(options.css3){ transformContainer(v.animateSection, v.translate3d, v.animated);
v.sectionsToMove.each(function(){ transformContainer($(this), v.translate3d, v.animated); });
setTimeout(function () { afterSectionLoads(v); }, options.scrollingSpeed); }else{ v.scrollOptions = getScrollProp(v.scrolling);
if(v.animated){ v.animateSection.animate( v.scrollOptions, options.scrollingSpeed, options.easing, function () { readjustSections(v); afterSectionLoads(v); }); }else{ v.animateSection.css(getScrollProp(v.scrolling)); setTimeout(function(){ readjustSections(v); afterSectionLoads(v); },400); } } }
/** * Actions to execute after a secion is loaded */ function afterSectionLoads(v){ //callback (afterLoad) if the site is not just resizing and readjusting the slides $.isFunction(options.afterLoad) && options.afterLoad.call(this, v.anchorLink, (v.sectionIndex + 1)); }
function getSectionsToMove(v){ var sectionToMove;
if(v.yMovement === 'down'){ sectionToMove = $('.pp-section').map(function(index){ if (index < v.destination.index('.pp-section')){ return $(this); } }); }else{ sectionToMove = $('.pp-section').map(function(index){ if (index > v.destination.index('.pp-section')){ return $(this); } }); }
return sectionToMove; }
/** * Returns the sections to re-adjust in the background after the section loads. */ function readjustSections(v){ if(v.yMovement === 'up'){ v.sectionsToMove.each(function(index){ $(this).css(getScrollProp(v.scrolling)); }); } }
/** * Gets the property used to create the scrolling effect when using jQuery animations * depending on the plugin direction option. */ function getScrollProp(propertyValue){ if(options.direction === 'vertical'){ return {'top': propertyValue}; } return {'left': propertyValue}; }
/** * Scrolls the site without anymations (usually used in the background without the user noticing it) */ function silentScroll(section, offset){ if (options.css3) { transformContainer(section, getTranslate3d(), false); } else{ section.css(getScrollProp(offset)); } }
/** * Sets the URL hash for a section with slides */ function setURLHash(anchorLink, sectionIndex){ if(options.anchors.length){ location.hash = anchorLink;
setBodyClass(location.hash); }else{ setBodyClass(String(sectionIndex)); } }
/** * Sets a class for the body of the page depending on the active section / slide */ function setBodyClass(text){ //removing the # text = text.replace('#',);
//removing previous anchor classes $('body')[0].className = $('body')[0].className.replace(/\b\s?pp-viewing-[^\s]+\b/g, );
//adding the current anchor $('body').addClass('pp-viewing-' + text); }
//TO DO function scrollToAnchor(){ //getting the anchor link in the URL and deleting the `#` var value = window.location.hash.replace('#', ); var sectionAnchor = value; var section = $(document).find('.pp-section[data-anchor="'+sectionAnchor+'"]');
if(section.length > 0){ //if theres any # scrollPage(section, options.animateAnchor); } }
/** * Determines if the transitions between sections still taking place. * The variable `scrollDelay` adds a "save zone" for devices such as Apple laptops and Apple magic mouses */ function isMoving(){ var timeNow = new Date().getTime(); // Cancel scroll if currently animating or within quiet period if (timeNow - lastAnimation < scrollDelay + options.scrollingSpeed) { return true; } return false; }
//detecting any change on the URL to scroll to the given anchor link //(a way to detect back history button as we play with the hashes on the URL) $(window).on('hashchange', hashChangeHandler);
/** * Actions to do when the hash (#) in the URL changes. */ function hashChangeHandler(){ var value = window.location.hash.replace('#', ).split('/'); var sectionAnchor = value[0];
if(sectionAnchor.length){ /*in order to call scrollpage() only once for each destination at a time It is called twice for each scroll otherwise, as in case of using anchorlinks `hashChange` event is fired on every scroll too.*/ if (sectionAnchor && sectionAnchor !== lastScrolledDestiny) { var section;
if(isNaN(sectionAnchor)){ section = $(document).find('[data-anchor="'+sectionAnchor+'"]'); }else{ section = $('.pp-section').eq( (sectionAnchor -1) ); } scrollPage(section); } } }
/** * Cross browser transformations */ function getTransforms(translate3d) { return { '-webkit-transform': translate3d, '-moz-transform': translate3d, '-ms-transform': translate3d, 'transform': translate3d }; }
/** * Adds a css3 transform property to the container class with or without animation depending on the animated param. */ function transformContainer(element, translate3d, animated) { element.toggleClass('pp-easing', animated);
element.css(getTransforms(translate3d)); }
/** * Sliding with arrow keys, both, vertical and horizontal */ $(document).keydown(function (e) { if(options.keyboardScrolling && !isMoving()){ //Moving the main page with the keyboard arrows if keyboard scrolling is enabled switch (e.which) { //up case 38: case 33: PP.moveSectionUp(); break;
//down case 40: case 34: PP.moveSectionDown(); break;
//Home case 36: PP.moveTo(1); break;
//End case 35: PP.moveTo($('.pp-section').length); break;
//left case 37: PP.moveSectionUp(); break;
//right case 39: PP.moveSectionDown(); break;
default: return; // exit this handler for other keys } } });
/** * If `normalScrollElements` is used, the mouse wheel scrolling will scroll normally * over the defined elements in the option. */ if(options.normalScrollElements){ $(document).on('mouseenter', options.normalScrollElements, function () { PP.setMouseWheelScrolling(false); });
$(document).on('mouseleave', options.normalScrollElements, function(){ PP.setMouseWheelScrolling(true); }); }
/** * Detecting mousewheel scrolling * * http://blogs.sitepointstatic.com/examples/tech/mouse-wheel/index.html * http://www.sitepoint.com/html5-javascript-mouse-wheel/ */ var prevTime = new Date().getTime();
function MouseWheelHandler(e) { var curTime = new Date().getTime();
// cross-browser wheel delta e = e || window.event; var value = e.wheelDelta || -e.deltaY || -e.detail; var delta = Math.max(-1, Math.min(1, value));
var horizontalDetection = typeof e.wheelDeltaX !== 'undefined' || typeof e.deltaX !== 'undefined'; var isScrollingVertically = (Math.abs(e.wheelDeltaX) < Math.abs(e.wheelDelta)) || (Math.abs(e.deltaX ) < Math.abs(e.deltaY) || !horizontalDetection);
//Limiting the array to 150 (lets not waste memory!)
if(scrollings.length > 149){ scrollings.shift(); }
//keeping record of the previous scrollings scrollings.push(Math.abs(value));
//time difference between the last scroll and the current one var timeDiff = curTime-prevTime; prevTime = curTime;
//haven't they scrolled in a while? //(enough to be consider a different scrolling action to scroll another section) if(timeDiff > 200){ //emptying the array, we dont care about old scrollings for our averages scrollings = []; }
if(!isMoving()){ var activeSection = $('.pp-section.active'); var scrollable = isScrollable(activeSection);
var averageEnd = getAverage(scrollings, 10); var averageMiddle = getAverage(scrollings, 70); var isAccelerating = averageEnd >= averageMiddle;
if(isAccelerating && isScrollingVertically){
//scrolling down? if (delta < 0) { scrolling('down', scrollable);
//scrolling up? }else if(delta>0){ scrolling('up', scrollable); } }
return false; } }
/** * Gets the average of the last `number` elements of the given array. */ function getAverage(elements, number){ var sum = 0;
//taking `number` elements from the end to make the average, if there are not enought, 1 var lastElements = elements.slice(Math.max(elements.length - number, 1));
for(var i = 0; i < lastElements.length; i++){ sum = sum + lastElements[i]; }
return Math.ceil(sum/number); }
/** * Determines the way of scrolling up or down: * by 'automatically' scrolling a section or by using the default and normal scrolling. */ function scrolling(type, scrollable){ var check; var scrollSection;
if(type == 'down'){ check = 'bottom'; scrollSection = PP.moveSectionDown; }else{ check = 'top'; scrollSection = PP.moveSectionUp; }
if(scrollable.length > 0 ){ //is the scrollbar at the start/end of the scroll? if(isScrolled(check, scrollable)){ scrollSection(); }else{ return true; } }else{ //moved up/down scrollSection(); } }
/** * Return a boolean depending on whether the scrollable element is at the end or at the start of the scrolling * depending on the given type. */ function isScrolled(type, scrollable){ if(type === 'top'){ return !scrollable.scrollTop(); }else if(type === 'bottom'){ return scrollable.scrollTop() + 1 + scrollable.innerHeight() >= scrollable[0].scrollHeight; } }
/** * Determines whether the active section or slide is scrollable through and scrolling bar */ function isScrollable(activeSection){ return activeSection.filter('.pp-scrollable'); }
/** * Removes the auto scrolling action fired by the mouse wheel and tackpad. * After this function is called, the mousewheel and trackpad movements won't scroll through sections. */ function removeMouseWheelHandler(){ if (container.get(0).addEventListener) { container.get(0).removeEventListener('mousewheel', MouseWheelHandler, false); //IE9, Chrome, Safari, Oper container.get(0).removeEventListener('wheel', MouseWheelHandler, false); //Firefox } else { container.get(0).detachEvent('onmousewheel', MouseWheelHandler); //IE 6/7/8 } }
/** * Adds the auto scrolling action for the mouse wheel and tackpad. * After this function is called, the mousewheel and trackpad movements will scroll through sections */ function addMouseWheelHandler(){ if (container.get(0).addEventListener) { container.get(0).addEventListener('mousewheel', MouseWheelHandler, false); //IE9, Chrome, Safari, Oper container.get(0).addEventListener('wheel', MouseWheelHandler, false); //Firefox } else { container.get(0).attachEvent('onmousewheel', MouseWheelHandler); //IE 6/7/8 } }
/** * Adds the possibility to auto scroll through sections on touch devices. */ function addTouchHandler(){ if(isTouch){ //Microsoft pointers var MSPointer = getMSPointer();
container.off('touchstart ' + MSPointer.down).on('touchstart ' + MSPointer.down, touchStartHandler); container.off('touchmove ' + MSPointer.move).on('touchmove ' + MSPointer.move, touchMoveHandler); } }
/** * Removes the auto scrolling for touch devices. */ function removeTouchHandler(){ if(isTouch){ //Microsoft pointers var MSPointer = getMSPointer();
container.off('touchstart ' + MSPointer.down); container.off('touchmove ' + MSPointer.move); } }
/* * Returns and object with Microsoft pointers (for IE<11 and for IE >= 11) * http://msdn.microsoft.com/en-us/library/ie/dn304886(v=vs.85).aspx */ function getMSPointer(){ var pointer;
//IE >= 11 & rest of browsers if(window.PointerEvent){ pointer = { down: 'pointerdown', move: 'pointermove', up: 'pointerup'}; }
//IE < 11 else{ pointer = { down: 'MSPointerDown', move: 'MSPointerMove', up: 'MSPointerUp'}; }
return pointer; }
/** * Gets the pageX and pageY properties depending on the browser. * https://github.com/alvarotrigo/fullPage.js/issues/194#issuecomment-34069854 */ function getEventsPage(e){ var events = new Array();
events.y = (typeof e.pageY !== 'undefined' && (e.pageY || e.pageX) ? e.pageY : e.touches[0].pageY); events.x = (typeof e.pageX !== 'undefined' && (e.pageY || e.pageX) ? e.pageX : e.touches[0].pageX);
return events; }
/** * As IE >= 10 fires both touch and mouse events when using a mouse in a touchscreen * this way we make sure that is really a touch event what IE is detecting. */ function isReallyTouch(e){ //if is not IE || IE is detecting `touch` or `pen` return typeof e.pointerType === 'undefined' || e.pointerType != 'mouse'; }
/** * Getting the starting possitions of the touch event */ function touchStartHandler(event){ var e = event.originalEvent;
if(isReallyTouch(e)){ var touchEvents = getEventsPage(e); touchStartY = touchEvents.y; touchStartX = touchEvents.x; } }
/* Detecting touch events */ function touchMoveHandler(event){ var e = event.originalEvent;
// additional: if one of the normalScrollElements isn't within options.normalScrollElementTouchThreshold hops up the DOM chain if ( !checkParentForNormalScrollElement(event.target) && isReallyTouch(e) ) {
var activeSection = $('.pp-section.active'); var scrollable = isScrollable(activeSection);
if(!scrollable.length){ event.preventDefault(); }
if (!isMoving()) { var touchEvents = getEventsPage(e); touchEndY = touchEvents.y; touchEndX = touchEvents.x;
//$('body').append('touchEndY: ' + touchEndY + '</div>');
//X movement bigger than Y movement? if (options.direction === 'horizontal' && Math.abs(touchStartX - touchEndX) > (Math.abs(touchStartY - touchEndY))) { //is the movement greater than the minimum resistance to scroll? if (Math.abs(touchStartX - touchEndX) > (container.width() / 100 * options.touchSensitivity)) { if (touchStartX > touchEndX) { scrolling('down', scrollable); } else if (touchEndX > touchStartX) { scrolling('up', scrollable); } } } else { if (Math.abs(touchStartY - touchEndY) > (container.height() / 100 * options.touchSensitivity)) { if (touchStartY > touchEndY) { scrolling('down', scrollable); } else if (touchEndY > touchStartY) { scrolling('up', scrollable); } } } } } }
/** * recursive function to loop up the parent nodes to check if one of them exists in options.normalScrollElements * Currently works well for iOS - Android might need some testing * @param {Element} el target element / jquery selector (in subsequent nodes) * @param {int} hop current hop compared to options.normalScrollElementTouchThreshold * @return {boolean} true if there is a match to options.normalScrollElements */ function checkParentForNormalScrollElement (el, hop) { hop = hop || 0; var parent = $(el).parent();
if (hop < options.normalScrollElementTouchThreshold && parent.is(options.normalScrollElements) ) { return true; } else if (hop == options.normalScrollElementTouchThreshold) { return false; } else { return checkParentForNormalScrollElement(parent, ++hop); } }
/** * Creates a vertical navigation bar. */ function addVerticalNavigation(){$('body').append(' ');
var nav = $('#pp-nav');
nav.css('color', options.navigation.textColor);
nav.addClass(options.navigation.position);
for(var cont = 0; cont < $('.pp-section').length; cont++){ var link = ; if(options.anchors.length){ link = options.anchors[cont]; } if(options.navigation.tooltips !== 'undefined'){ var tooltip = options.navigation.tooltips[cont]; if(typeof tooltip === 'undefined'){ tooltip = ; } }nav.find('ul').append('
}
nav.find('span').css('border-color', options.navigation.bulletsColor); }
/** * Scrolls to the section when clicking the navigation bullet */ $(document).on('click touchstart', '#pp-nav a', function(e){ e.preventDefault(); var index = $(this).parent().index();
scrollPage($('.pp-section').eq(index)); });
/** * Navigation tooltips */ $(document).on({ mouseenter: function(){ var tooltip = $(this).data('tooltip');$(' ').hide().appendTo($(this)).fadeIn(200);
}, mouseleave: function(){ $(this).find('.pp-tooltip').fadeOut(200, function() { $(this).remove(); }); } }, '#pp-nav li');
/** * Activating the website navigation dots according to the given slide name. */ function activateNavDots(name, sectionIndex){ if(options.navigation){ $('#pp-nav').find('.active').removeClass('active'); if(name){ $('#pp-nav').find('a[href="#' + name + '"]').addClass('active'); }else{ $('#pp-nav').find('li').eq(sectionIndex).find('a').addClass('active'); } } }
/** * Activating the website main menu elements according to the given slide name. */ function activateMenuElement(name){ if(options.menu){ $(options.menu).find('.active').removeClass('active'); $(options.menu).find('[data-menuanchor="'+name+'"]').addClass('active'); } }
/** * Checks for translate3d support * @return boolean * http://stackoverflow.com/questions/5661671/detecting-transform-translate3d-support */ function support3d() { var el = document.createElement('p'), has3d, transforms = { 'webkitTransform':'-webkit-transform', 'OTransform':'-o-transform', 'msTransform':'-ms-transform', 'MozTransform':'-moz-transform', 'transform':'transform' };
// Add it to the body to get the computed style. document.body.insertBefore(el, null);
for (var t in transforms) { if (el.style[t] !== undefined) { el.style[t] = 'translate3d(1px,1px,1px)'; has3d = window.getComputedStyle(el).getPropertyValue(transforms[t]); } }
document.body.removeChild(el);
return (has3d !== undefined && has3d.length > 0 && has3d !== 'none'); }
/** * Gets the translate3d property to apply when using css3:true depending on the `direction` option. */ function getTranslate3d(){ if (options.direction !== 'vertical') { return 'translate3d(-100%, 0px, 0px)'; }
return 'translate3d(0px, -100%, 0px)'; }
};
})(jQuery, document, window);