UNPKG

fullpage.js

Version:

Create beautiful fullscreen snap scrolling websites

1,297 lines (1,097 loc) 157 kB
/*! * fullPage 3.1.2 * https://github.com/alvarotrigo/fullPage.js * * @license GPLv3 for open source use only * or Fullpage Commercial License for commercial use * http://alvarotrigo.com/fullPage/pricing/ * * Copyright (C) 2018 http://alvarotrigo.com/fullPage - A project by Alvaro Trigo */ (function( root, window, document, factory, undefined) { if( typeof define === 'function' && define.amd ) { // AMD. Register as an anonymous module. define( function() { root.fullpage = factory(window, document); return root.fullpage; } ); } else if( typeof exports === 'object' ) { // Node. Does not work with strict CommonJS. module.exports = factory(window, document); } else { // Browser globals. window.fullpage = factory(window, document); } }(this, window, document, function(window, document){ 'use strict'; // keeping central set of classnames and selectors var WRAPPER = 'fullpage-wrapper'; var WRAPPER_SEL = '.' + WRAPPER; // slimscroll var SCROLLABLE = 'fp-scrollable'; var SCROLLABLE_SEL = '.' + SCROLLABLE; // util var RESPONSIVE = 'fp-responsive'; var NO_TRANSITION = 'fp-notransition'; var DESTROYED = 'fp-destroyed'; var ENABLED = 'fp-enabled'; var VIEWING_PREFIX = 'fp-viewing'; var ACTIVE = 'active'; var ACTIVE_SEL = '.' + ACTIVE; var COMPLETELY = 'fp-completely'; var COMPLETELY_SEL = '.' + COMPLETELY; // section var SECTION_DEFAULT_SEL = '.section'; var SECTION = 'fp-section'; var SECTION_SEL = '.' + SECTION; var SECTION_ACTIVE_SEL = SECTION_SEL + ACTIVE_SEL; var TABLE_CELL = 'fp-tableCell'; var TABLE_CELL_SEL = '.' + TABLE_CELL; var AUTO_HEIGHT = 'fp-auto-height'; var AUTO_HEIGHT_SEL = '.' + AUTO_HEIGHT; var AUTO_HEIGHT_RESPONSIVE = 'fp-auto-height-responsive'; var AUTO_HEIGHT_RESPONSIVE_SEL = '.' + AUTO_HEIGHT_RESPONSIVE; var NORMAL_SCROLL = 'fp-normal-scroll'; var NORMAL_SCROLL_SEL = '.' + NORMAL_SCROLL; // section nav var SECTION_NAV = 'fp-nav'; var SECTION_NAV_SEL = '#' + SECTION_NAV; var SECTION_NAV_TOOLTIP = 'fp-tooltip'; var SECTION_NAV_TOOLTIP_SEL='.'+SECTION_NAV_TOOLTIP; var SHOW_ACTIVE_TOOLTIP = 'fp-show-active'; // slide var SLIDE_DEFAULT_SEL = '.slide'; var SLIDE = 'fp-slide'; var SLIDE_SEL = '.' + SLIDE; var SLIDE_ACTIVE_SEL = SLIDE_SEL + ACTIVE_SEL; var SLIDES_WRAPPER = 'fp-slides'; var SLIDES_WRAPPER_SEL = '.' + SLIDES_WRAPPER; var SLIDES_CONTAINER = 'fp-slidesContainer'; var SLIDES_CONTAINER_SEL = '.' + SLIDES_CONTAINER; var TABLE = 'fp-table'; // slide nav var SLIDES_NAV = 'fp-slidesNav'; var SLIDES_NAV_SEL = '.' + SLIDES_NAV; var SLIDES_NAV_LINK_SEL = SLIDES_NAV_SEL + ' a'; var SLIDES_ARROW = 'fp-controlArrow'; var SLIDES_ARROW_SEL = '.' + SLIDES_ARROW; var SLIDES_PREV = 'fp-prev'; var SLIDES_PREV_SEL = '.' + SLIDES_PREV; var SLIDES_ARROW_PREV = SLIDES_ARROW + ' ' + SLIDES_PREV; var SLIDES_ARROW_PREV_SEL = SLIDES_ARROW_SEL + SLIDES_PREV_SEL; var SLIDES_NEXT = 'fp-next'; var SLIDES_NEXT_SEL = '.' + SLIDES_NEXT; var SLIDES_ARROW_NEXT = SLIDES_ARROW + ' ' + SLIDES_NEXT; var SLIDES_ARROW_NEXT_SEL = SLIDES_ARROW_SEL + SLIDES_NEXT_SEL; function initialise(containerSelector, options) { var isOK = options && new RegExp('([\\d\\w]{8}-){3}[\\d\\w]{8}|^(?=.*?[A-Y])(?=.*?[a-y])(?=.*?[0-8])(?=.*?[#?!@$%^&*-]).{8,}$').test(options['li'+'cen'+'seK' + 'e' + 'y']) || document.domain.indexOf('al'+'varotri' +'go' + '.' + 'com') > -1; // cache common elements var $htmlBody = $('html, body'); var $html = $('html')[0]; var $body = $('body')[0]; //only once my friend! if(hasClass($html, ENABLED)){ displayWarnings(); return; } var FP = {}; // Creating some defaults, extending them with any options that were provided options = deepExtend({ //navigation menu: false, anchors:[], lockAnchors: false, navigation: false, navigationPosition: 'right', navigationTooltips: [], showActiveTooltip: false, slidesNavigation: false, slidesNavPosition: 'bottom', scrollBar: false, hybrid: false, //scrolling css3: true, scrollingSpeed: 700, autoScrolling: true, fitToSection: true, fitToSectionDelay: 1000, easing: 'easeInOutCubic', easingcss3: 'ease', loopBottom: false, loopTop: false, loopHorizontal: true, continuousVertical: false, continuousHorizontal: false, scrollHorizontally: false, interlockedSlides: false, dragAndMove: false, offsetSections: false, resetSliders: false, fadingEffect: false, normalScrollElements: null, scrollOverflow: false, scrollOverflowReset: false, scrollOverflowHandler: window.fp_scrolloverflow ? window.fp_scrolloverflow.iscrollHandler : null, scrollOverflowOptions: null, touchSensitivity: 5, touchWrapper: typeof containerSelector === 'string' ? $(containerSelector)[0] : containerSelector, bigSectionsDestination: null, //Accessibility keyboardScrolling: true, animateAnchor: true, recordHistory: true, //design controlArrows: true, controlArrowColor: '#fff', verticalCentered: true, sectionsColor : [], paddingTop: 0, paddingBottom: 0, fixedElements: null, responsive: 0, //backwards compabitility with responsiveWiddth responsiveWidth: 0, responsiveHeight: 0, responsiveSlides: false, parallax: false, parallaxOptions: { type: 'reveal', percentage: 62, property: 'translate' }, cards: false, cardsOptions: { perspective: 100, fadeContent: true, fadeBackground: true }, //Custom selectors sectionSelector: SECTION_DEFAULT_SEL, slideSelector: SLIDE_DEFAULT_SEL, //events v2compatible: false, afterLoad: null, onLeave: null, afterRender: null, afterResize: null, afterReBuild: null, afterSlideLoad: null, onSlideLeave: null, afterResponsive: null, lazyLoading: true }, options); //flag to avoid very fast sliding for landscape sliders var slideMoving = false; var isTouchDevice = navigator.userAgent.match(/(iPhone|iPod|iPad|Android|playbook|silk|BlackBerry|BB10|Windows Phone|Tizen|Bada|webOS|IEMobile|Opera Mini)/); var isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0) || (navigator.maxTouchPoints)); var container = typeof containerSelector === 'string' ? $(containerSelector)[0] : containerSelector; var windowsHeight = getWindowHeight(); var windowsWidth = getWindowWidth(); var isResizing = false; var isWindowFocused = true; var lastScrolledDestiny; var lastScrolledSlide; var canScroll = true; var scrollings = []; var controlPressed; var startingSection; var isScrollAllowed = {}; isScrollAllowed.m = { 'up':true, 'down':true, 'left':true, 'right':true }; isScrollAllowed.k = deepExtend({}, isScrollAllowed.m); var MSPointer = getMSPointer(); var events = { touchmove: 'ontouchmove' in window ? 'touchmove' : MSPointer.move, touchstart: 'ontouchstart' in window ? 'touchstart' : MSPointer.down }; var scrollBarHandler; // taken from https://github.com/udacity/ud891/blob/gh-pages/lesson2-focus/07-modals-and-keyboard-traps/solution/modal.js var focusableElementsString = 'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex="0"], [contenteditable]'; //cheks for passive event support var g_supportsPassive = false; try { var opts = Object.defineProperty({}, 'passive', { get: function() { g_supportsPassive = true; } }); window.addEventListener("testPassive", null, opts); window.removeEventListener("testPassive", null, opts); } catch (e) {} //timeouts var resizeId; var resizeHandlerId; var afterSectionLoadsId; var afterSlideLoadsId; var scrollId; var scrollId2; var keydownId; var g_doubleCheckHeightId; var originals = deepExtend({}, options); //deep copy var activeAnimation; var g_initialAnchorsInDom = false; var g_canFireMouseEnterNormalScroll = true; var g_mediaLoadedId; var g_transitionLapseId; var extensions = [ 'parallax', 'scrollOverflowReset', 'dragAndMove', 'offsetSections', 'fadingEffect', 'responsiveSlides', 'continuousHorizontal', 'interlockedSlides', 'scrollHorizontally', 'resetSliders', 'cards', 'dropEffect', 'waterEffect' ]; displayWarnings(); //easeInOutCubic animation included in the plugin window.fp_easings = deepExtend(window.fp_easings, { easeInOutCubic: function (t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t*t + b;return c/2*((t-=2)*t*t + 2) + b; } }); /** * Sets the autoScroll option. * It changes the scroll bar visibility and the history of the site as a result. */ function setAutoScrolling(value, type){ //removing the transformation if(!value){ silentScroll(0); } setVariableState('autoScrolling', value, type); var element = $(SECTION_ACTIVE_SEL)[0]; if(options.autoScrolling && !options.scrollBar){ css($htmlBody, { 'overflow': 'hidden', 'height': '100%' }); setRecordHistory(originals.recordHistory, 'internal'); //for IE touch devices css(container, { '-ms-touch-action': 'none', 'touch-action': 'none' }); if(element != null){ //moving the container up silentScroll(element.offsetTop); } }else{ css($htmlBody, { 'overflow' : 'visible', 'height' : 'initial' }); var recordHistory = !options.autoScrolling ? false : originals.recordHistory; setRecordHistory(recordHistory, 'internal'); //for IE touch devices css(container, { '-ms-touch-action': '', 'touch-action': '' }); //scrolling the page to the section with no animation if (element != null) { var scrollSettings = getScrollSettings(element.offsetTop); scrollSettings.element.scrollTo(0, scrollSettings.options); } } } /** * Defines wheter to record the history for each hash change in the URL. */ function setRecordHistory(value, type){ setVariableState('recordHistory', value, type); } /** * Defines the scrolling speed */ function setScrollingSpeed(value, type){ setVariableState('scrollingSpeed', value, type); } /** * Sets fitToSection */ function setFitToSection(value, type){ setVariableState('fitToSection', value, type); } /** * Sets lockAnchors */ function setLockAnchors(value){ options.lockAnchors = value; } /** * Adds or remove the possibility of scrolling through sections by using the mouse wheel or the trackpad. */ function setMouseWheelScrolling(value){ if(value){ addMouseWheelHandler(); addMiddleWheelHandler(); }else{ removeMouseWheelHandler(); removeMiddleWheelHandler(); } } /** * Adds or remove the possibility of scrolling through sections by using the mouse wheel/trackpad or touch gestures. * Optionally a second parameter can be used to specify the direction for which the action will be applied. * * @param directions string containing the direction or directions separated by comma. */ function setAllowScrolling(value, directions){ if(typeof directions !== 'undefined'){ directions = directions.replace(/ /g,'').split(','); directions.forEach(function (direction){ setIsScrollAllowed(value, direction, 'm'); }); } else{ setIsScrollAllowed(value, 'all', 'm'); } } /** * Adds or remove the mouse wheel hijacking */ function setMouseHijack(value){ if(value){ setMouseWheelScrolling(true); addTouchHandler(); }else{ setMouseWheelScrolling(false); removeTouchHandler(); } } /** * Adds or remove the possibility of scrolling through sections by using the keyboard arrow keys */ function setKeyboardScrolling(value, directions){ if(typeof directions !== 'undefined'){ directions = directions.replace(/ /g,'').split(','); directions.forEach(function(direction){ setIsScrollAllowed(value, direction, 'k'); }); }else{ setIsScrollAllowed(value, 'all', 'k'); options.keyboardScrolling = value; } } /** * Moves the page up one section. */ function moveSectionUp(){ var prev = prevUntil($(SECTION_ACTIVE_SEL)[0], SECTION_SEL); //looping to the bottom if there's no more sections above if (!prev && (options.loopTop || options.continuousVertical)) { prev = last($(SECTION_SEL)); } if (prev != null) { scrollPage(prev, null, true); } } /** * Moves the page down one section. */ function moveSectionDown(){ var next = nextUntil($(SECTION_ACTIVE_SEL)[0], SECTION_SEL); //looping to the top if there's no more sections below if(!next && (options.loopBottom || options.continuousVertical)){ next = $(SECTION_SEL)[0]; } if(next != null){ scrollPage(next, null, false); } } /** * Moves the page to the given section and slide with no animation. * Anchors or index positions can be used as params. */ function silentMoveTo(sectionAnchor, slideAnchor){ setScrollingSpeed (0, 'internal'); moveTo(sectionAnchor, slideAnchor); setScrollingSpeed (originals.scrollingSpeed, 'internal'); } /** * Moves the page to the given section and slide. * Anchors or index positions can be used as params. */ function moveTo(sectionAnchor, slideAnchor){ var destiny = getSectionByAnchor(sectionAnchor); if (typeof slideAnchor !== 'undefined'){ scrollPageAndSlide(sectionAnchor, slideAnchor); }else if(destiny != null){ scrollPage(destiny); } } /** * Slides right the slider of the active section. * Optional `section` param. */ function moveSlideRight(section){ moveSlide('right', section); } /** * Slides left the slider of the active section. * Optional `section` param. */ function moveSlideLeft(section){ moveSlide('left', section); } /** * When resizing is finished, we adjust the slides sizes and positions */ function reBuild(resizing){ if(hasClass(container, DESTROYED)){ return; } //nothing to do if the plugin was destroyed isResizing = true; //updating global vars windowsHeight = getWindowHeight(); windowsWidth = getWindowWidth(); var sections = $(SECTION_SEL); for (var i = 0; i < sections.length; ++i) { var section = sections[i]; var slidesWrap = $(SLIDES_WRAPPER_SEL, section)[0]; var slides = $(SLIDE_SEL, section); //adjusting the height of the table-cell for IE and Firefox if(options.verticalCentered){ css($(TABLE_CELL_SEL, section), {'height': getTableHeight(section) + 'px'}); } css(section, {'height': windowsHeight + 'px'}); //adjusting the position fo the FULL WIDTH slides... if (slides.length > 1) { landscapeScroll(slidesWrap, $(SLIDE_ACTIVE_SEL, slidesWrap)[0]); } } if(options.scrollOverflow){ scrollBarHandler.createScrollBarForAll(); } var activeSection = $(SECTION_ACTIVE_SEL)[0]; var sectionIndex = index(activeSection, SECTION_SEL); //isn't it the first section? if(sectionIndex){ //adjusting the position for the current section silentMoveTo(sectionIndex + 1); } isResizing = false; if(isFunction( options.afterResize ) && resizing){ options.afterResize.call(container, window.innerWidth, window.innerHeight); } if(isFunction( options.afterReBuild ) && !resizing){ options.afterReBuild.call(container); } } /** * Determines whether fullpage.js is in responsive mode or not. */ function isResponsiveMode(){ return hasClass($body, RESPONSIVE); } /** * Turns fullPage.js to normal scrolling mode when the viewport `width` or `height` * are smaller than the set limit values. */ function setResponsive(active){ var isResponsive = isResponsiveMode(); if(active){ if(!isResponsive){ setAutoScrolling(false, 'internal'); setFitToSection(false, 'internal'); hide($(SECTION_NAV_SEL)); addClass($body, RESPONSIVE); if(isFunction( options.afterResponsive )){ options.afterResponsive.call( container, active); } //when on page load, we will remove scrolloverflow if necessary if(options.scrollOverflow){ scrollBarHandler.createScrollBarForAll(); } } } else if(isResponsive){ setAutoScrolling(originals.autoScrolling, 'internal'); setFitToSection(originals.autoScrolling, 'internal'); show($(SECTION_NAV_SEL)); removeClass($body, RESPONSIVE); if(isFunction( options.afterResponsive )){ options.afterResponsive.call( container, active); } } } if(container){ //public functions FP.version = '3.1.1'; FP.setAutoScrolling = setAutoScrolling; FP.setRecordHistory = setRecordHistory; FP.setScrollingSpeed = setScrollingSpeed; FP.setFitToSection = setFitToSection; FP.setLockAnchors = setLockAnchors; FP.setMouseWheelScrolling = setMouseWheelScrolling; FP.setAllowScrolling = setAllowScrolling; FP.setKeyboardScrolling = setKeyboardScrolling; FP.moveSectionUp = moveSectionUp; FP.moveSectionDown = moveSectionDown; FP.silentMoveTo = silentMoveTo; FP.moveTo = moveTo; FP.moveSlideRight = moveSlideRight; FP.moveSlideLeft = moveSlideLeft; FP.fitToSection = fitToSection; FP.reBuild = reBuild; FP.setResponsive = setResponsive; FP.getFullpageData = function(){ return options; }; FP.destroy = destroy; FP.getActiveSection = getActiveSection; FP.getActiveSlide = getActiveSlide; FP.test = { top: '0px', translate3d: 'translate3d(0px, 0px, 0px)', translate3dH: (function(){ var a = []; for(var i = 0; i < $(options.sectionSelector, container).length; i++){ a.push('translate3d(0px, 0px, 0px)'); } return a; })(), left: (function(){ var a = []; for(var i = 0; i < $(options.sectionSelector, container).length; i++){ a.push(0); } return a; })(), options: options, setAutoScrolling: setAutoScrolling }; //functions we want to share across files but which are not //mean to be used on their own by developers FP.shared = { afterRenderActions: afterRenderActions, isNormalScrollElement: false }; window.fullpage_api = FP; //using jQuery initialization? Creating the $.fn.fullpage object if(options.$){ Object.keys(FP).forEach(function (key) { options.$.fn.fullpage[key] = FP[key]; }); } init(); bindEvents(); } function init(){ //if css3 is not supported, it will use jQuery animations if(options.css3){ options.css3 = support3d(); } options.scrollBar = options.scrollBar || options.hybrid; setOptionsFromDOM(); prepareDom(); setAllowScrolling(true); setMouseHijack(true); setAutoScrolling(options.autoScrolling, 'internal'); responsive(); //setting the class for the body element setBodyClass(); if(document.readyState === 'complete'){ scrollToAnchor(); } window.addEventListener('load', scrollToAnchor); //if we use scrollOverflow we'll fire afterRender in the scrolloverflow file if(!options.scrollOverflow){ afterRenderActions(); } doubleCheckHeight(); } function bindEvents(){ //when scrolling... window.addEventListener('scroll', scrollHandler); //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.addEventListener('hashchange', hashChangeHandler); // on window focus window.addEventListener('focus', focusHandler); //when opening a new tab (ctrl + t), `control` won't be pressed when coming back. window.addEventListener('blur', blurHandler); //when resizing the site, we adjust the heights of the sections, slimScroll... window.addEventListener('resize', resizeHandler); //Sliding with arrow keys, both, vertical and horizontal document.addEventListener('keydown', keydownHandler); //to prevent scrolling while zooming document.addEventListener('keyup', keyUpHandler); //Scrolls to the section when clicking the navigation bullet //simulating the jQuery .on('click') event using delegation ['click', 'touchstart'].forEach(function(eventName){ document.addEventListener(eventName, delegatedEvents); }); /** * Applying normalScroll elements. * Ignoring the scrolls over the specified selectors. */ if(options.normalScrollElements){ ['mouseenter', 'touchstart'].forEach(function(eventName){ forMouseLeaveOrTouch(eventName, false); }); ['mouseleave', 'touchend'].forEach(function(eventName){ forMouseLeaveOrTouch(eventName, true); }); } } function delegatedEvents(e){ var target = e.target; if(target && closest(target, SECTION_NAV_SEL + ' a')){ sectionBulletHandler.call(target, e); } else if(matches(target, SECTION_NAV_TOOLTIP_SEL)){ tooltipTextHandler.call(target); } else if(matches(target, SLIDES_ARROW_SEL)){ slideArrowHandler.call(target, e); } else if(matches(target, SLIDES_NAV_LINK_SEL) || closest(target, SLIDES_NAV_LINK_SEL) != null){ slideBulletHandler.call(target, e); } else if(closest(target, options.menu + ' [data-menuanchor]')){ menuItemsHandler.call(target, e); } } function forMouseLeaveOrTouch(eventName, allowScrolling){ //a way to pass arguments to the onMouseEnterOrLeave function document['fp_' + eventName] = allowScrolling; document.addEventListener(eventName, onMouseEnterOrLeave, true); //capturing phase } function onMouseEnterOrLeave(e) { var type = e.type; var isInsideOneNormalScroll = false; var isUsingScrollOverflow = options.scrollOverflow; //onMouseLeave will use the destination target, not the one we are moving away from var target = type === 'mouseleave' ? e.toElement || e.relatedTarget : e.target; //coming from closing a normalScrollElements modal or moving outside viewport? if(target == document || !target){ setMouseHijack(true); if(isUsingScrollOverflow){ options.scrollOverflowHandler.setIscroll(target, true); } return; } if(type === 'touchend'){ g_canFireMouseEnterNormalScroll = false; setTimeout(function(){ g_canFireMouseEnterNormalScroll = true; }, 800); } //preventing mouseenter event to do anything when coming from a touchEnd event //fixing issue #3576 if(type === 'mouseenter' && !g_canFireMouseEnterNormalScroll){ return; } var normalSelectors = options.normalScrollElements.split(','); normalSelectors.forEach(function(normalSelector){ if(!isInsideOneNormalScroll){ var isNormalScrollTarget = matches(target, normalSelector); //leaving a child inside the normalScoll element is not leaving the normalScroll #3661 var isNormalScrollChildFocused = closest(target, normalSelector); if(isNormalScrollTarget || isNormalScrollChildFocused){ if(!FP.shared.isNormalScrollElement){ setMouseHijack(false); if(isUsingScrollOverflow){ options.scrollOverflowHandler.setIscroll(target, false); } } FP.shared.isNormalScrollElement = true; isInsideOneNormalScroll = true; } } }); //not inside a single normal scroll element anymore? if(!isInsideOneNormalScroll && FP.shared.isNormalScrollElement){ setMouseHijack(true); if(isUsingScrollOverflow){ options.scrollOverflowHandler.setIscroll(target, true); } FP.shared.isNormalScrollElement = false; } } /** * Checks the viewport a few times on a define interval of time to * see if it has changed in any of those. If that's the case, it resizes. */ function doubleCheckHeight(){ for(var i = 1; i < 4; i++){ g_doubleCheckHeightId = setTimeout(adjustToNewViewport, 350 * i); } } /** * Adjusts a section to the viewport if it has changed. */ function adjustToNewViewport(){ var newWindowHeight = getWindowHeight(); var newWindowWidth = getWindowWidth(); if(windowsHeight !== newWindowHeight || windowsWidth !== newWindowWidth){ windowsHeight = newWindowHeight; windowsWidth = newWindowWidth; reBuild(true); } } /** * Setting options from DOM elements if they are not provided. */ function setOptionsFromDOM(){ //no anchors option? Checking for them in the DOM attributes if(!options.anchors.length){ var anchorsAttribute = '[data-anchor]'; var anchors = $(options.sectionSelector.split(',').join(anchorsAttribute + ',') + anchorsAttribute, container); if(anchors.length && anchors.length === $(options.sectionSelector, container).length){ g_initialAnchorsInDom = true; anchors.forEach(function(item){ options.anchors.push(item.getAttribute('data-anchor').toString()); }); } } //no tooltips option? Checking for them in the DOM attributes if(!options.navigationTooltips.length){ var tooltipsAttribute = '[data-tooltip]'; var tooltips = $(options.sectionSelector.split(',').join(tooltipsAttribute + ',') + tooltipsAttribute, container); if(tooltips.length){ tooltips.forEach(function(item){ options.navigationTooltips.push(item.getAttribute('data-tooltip').toString()); }); } } } /** * Works over the DOM structure to set it up for the current fullpage options. */ function prepareDom(){ css(container, { 'height': '100%', 'position': 'relative' }); //adding a class to recognize the container internally in the code addClass(container, WRAPPER); addClass($html, ENABLED); //due to https://github.com/alvarotrigo/fullPage.js/issues/1502 windowsHeight = getWindowHeight(); removeClass(container, DESTROYED); //in case it was destroyed before initializing it again addInternalSelectors(); var sections = $(SECTION_SEL); //styling the sections / slides / menu for(var i = 0; i<sections.length; i++){ var sectionIndex = i; var section = sections[i]; var slides = $(SLIDE_SEL, section); var numSlides = slides.length; //caching the original styles to add them back on destroy('all') section.setAttribute('data-fp-styles', section.getAttribute('style')); styleSection(section, sectionIndex); styleMenu(section, sectionIndex); // if there's any slide if (numSlides > 0) { styleSlides(section, slides, numSlides); }else{ if(options.verticalCentered){ addTableClass(section); } } } //fixed elements need to be moved out of the plugin container due to problems with CSS3. if(options.fixedElements && options.css3){ $(options.fixedElements).forEach(function(item){ $body.appendChild(item); }); } //vertical centered of the navigation + active bullet if(options.navigation){ addVerticalNavigation(); } enableYoutubeAPI(); if(options.scrollOverflow){ scrollBarHandler = options.scrollOverflowHandler.init(options); } } /** * Styles the horizontal slides for a section. */ function styleSlides(section, slides, numSlides){ var sliderWidth = numSlides * 100; var slideWidth = 100 / numSlides; var slidesWrapper = document.createElement('div'); slidesWrapper.className = SLIDES_WRAPPER; //fp-slides wrapAll(slides, slidesWrapper); var slidesContainer = document.createElement('div'); slidesContainer.className = SLIDES_CONTAINER; //fp-slidesContainer wrapAll(slides, slidesContainer); css($(SLIDES_CONTAINER_SEL, section), {'width': sliderWidth + '%'}); if(numSlides > 1){ if(options.controlArrows){ createSlideArrows(section); } if(options.slidesNavigation){ addSlidesNavigation(section, numSlides); } } slides.forEach(function(slide) { css(slide, {'width': slideWidth + '%'}); if(options.verticalCentered){ addTableClass(slide); } }); var startingSlide = $(SLIDE_ACTIVE_SEL, section)[0]; //if the slide won't be an starting point, the default will be the first one //the active section isn't the first one? Is not the first slide of the first section? Then we load that section/slide by default. if( startingSlide != null && (index($(SECTION_ACTIVE_SEL), SECTION_SEL) !== 0 || (index($(SECTION_ACTIVE_SEL), SECTION_SEL) === 0 && index(startingSlide) !== 0))){ silentLandscapeScroll(startingSlide, 'internal'); }else{ addClass(slides[0], ACTIVE); } } /** * Styling vertical sections */ function styleSection(section, index){ //if no active section is defined, the 1st one will be the default one if(!index && $(SECTION_ACTIVE_SEL)[0] == null) { addClass(section, ACTIVE); } startingSection = $(SECTION_ACTIVE_SEL)[0]; css(section, {'height': windowsHeight + 'px'}); if(options.paddingTop){ css(section, {'padding-top': options.paddingTop}); } if(options.paddingBottom){ css(section, {'padding-bottom': options.paddingBottom}); } if (typeof options.sectionsColor[index] !== 'undefined') { css(section, {'background-color': options.sectionsColor[index]}); } if (typeof options.anchors[index] !== 'undefined') { section.setAttribute('data-anchor', options.anchors[index]); } } /** * Sets the data-anchor attributes to the menu elements and activates the current one. */ function styleMenu(section, index){ if (typeof options.anchors[index] !== 'undefined') { //activating the menu / nav element on load if(hasClass(section, ACTIVE)){ activateMenuAndNav(options.anchors[index], index); } } //moving the menu outside the main container if it is inside (avoid problems with fixed positions when using CSS3 tranforms) if(options.menu && options.css3 && closest($(options.menu)[0], WRAPPER_SEL) != null){ $(options.menu).forEach(function(menu) { $body.appendChild(menu); }); } } /** * Adds internal classes to be able to provide customizable selectors * keeping the link with the style sheet. */ function addInternalSelectors(){ addClass($(options.sectionSelector, container), SECTION); addClass($(options.slideSelector, container), SLIDE); } /** * Creates the control arrows for the given section */ function createSlideArrows(section){ var arrows = [createElementFromHTML('<div class="' + SLIDES_ARROW_PREV + '"></div>'), createElementFromHTML('<div class="' + SLIDES_ARROW_NEXT + '"></div>')]; after($(SLIDES_WRAPPER_SEL, section)[0], arrows); if(options.controlArrowColor !== '#fff'){ css($(SLIDES_ARROW_NEXT_SEL, section), {'border-color': 'transparent transparent transparent '+options.controlArrowColor}); css($(SLIDES_ARROW_PREV_SEL, section), {'border-color': 'transparent '+ options.controlArrowColor + ' transparent transparent'}); } if(!options.loopHorizontal){ hide($(SLIDES_ARROW_PREV_SEL, section)); } } /** * Creates a vertical navigation bar. */ function addVerticalNavigation(){ var navigation = document.createElement('div'); navigation.setAttribute('id', SECTION_NAV); var divUl = document.createElement('ul'); navigation.appendChild(divUl); appendTo(navigation, $body); var nav = $(SECTION_NAV_SEL)[0]; addClass(nav, 'fp-' + options.navigationPosition); if(options.showActiveTooltip){ addClass(nav, SHOW_ACTIVE_TOOLTIP); } var li = ''; for (var i = 0; i < $(SECTION_SEL).length; i++) { var link = ''; if (options.anchors.length) { link = options.anchors[i]; } li += '<li><a href="#' + link + '"><span class="fp-sr-only">' + getBulletLinkName(i, 'Section') + '</span><span></span></a>'; // Only add tooltip if needed (defined by user) var tooltip = options.navigationTooltips[i]; if (typeof tooltip !== 'undefined' && tooltip !== '') { li += '<div class="' + SECTION_NAV_TOOLTIP + ' fp-' + options.navigationPosition + '">' + tooltip + '</div>'; } li += '</li>'; } $('ul', nav)[0].innerHTML = li; //activating the current active section var bullet = $('li', $(SECTION_NAV_SEL)[0])[index($(SECTION_ACTIVE_SEL)[0], SECTION_SEL)]; addClass($('a', bullet), ACTIVE); } /** * Gets the name for screen readers for a section/slide navigation bullet. */ function getBulletLinkName(i, defaultName, item){ var anchor = defaultName === 'Section' ? options.anchors[i] : item.getAttribute('data-anchor'); return options.navigationTooltips[i] || anchor || defaultName + ' ' + (i+1); } /* * Enables the Youtube videos API so we can control their flow if necessary. */ function enableYoutubeAPI(){ $('iframe[src*="youtube.com/embed/"]', container).forEach(function(item){ addURLParam(item, 'enablejsapi=1'); }); } /** * Adds a new parameter and its value to the `src` of a given element */ function addURLParam(element, newParam){ var originalSrc = element.getAttribute('src'); element.setAttribute('src', originalSrc + getUrlParamSign(originalSrc) + newParam); } /* * Returns the prefix sign to use for a new parameter in an existen URL. * * @return {String} ? | & */ function getUrlParamSign(url){ return ( !/\?/.test( url ) ) ? '?' : '&'; } /** * Actions and callbacks to fire afterRender */ function afterRenderActions(){ var section = $(SECTION_ACTIVE_SEL)[0]; addClass(section, COMPLETELY); lazyLoad(section); lazyLoadOthers(); playMedia(section); if(options.scrollOverflow){ options.scrollOverflowHandler.afterLoad(); } if(isDestinyTheStartingSection() && isFunction(options.afterLoad) ){ fireCallback('afterLoad', { activeSection: section, element: section, direction: null, //for backwards compatibility callback (to be removed in a future!) anchorLink: section.getAttribute('data-anchor'), sectionIndex: index(section, SECTION_SEL) }); } if(isFunction(options.afterRender)){ fireCallback('afterRender'); } } /** * Determines if the URL anchor destiny is the starting section (the one using 'active' class before initialization) */ function isDestinyTheStartingSection(){ var anchor = getAnchorsURL(); var destinationSection = getSectionByAnchor(anchor.section); return !anchor.section || !destinationSection || typeof destinationSection !=='undefined' && index(destinationSection) === index(startingSection); } var isScrolling = false; var lastScroll = 0; //when scrolling... function scrollHandler(){ var currentSection; if(isResizing){ return; } if(!options.autoScrolling || options.scrollBar){ var currentScroll = getScrollTop(); var scrollDirection = getScrollDirection(currentScroll); var visibleSectionIndex = 0; var screen_mid = currentScroll + (getWindowHeight() / 2.0); var isAtBottom = $body.offsetHeight - getWindowHeight() === currentScroll; var sections = $(SECTION_SEL); //when using `auto-height` for a small last section it won't be centered in the viewport if(isAtBottom){ visibleSectionIndex = sections.length - 1; } //is at top? when using `auto-height` for a small first section it won't be centered in the viewport else if(!currentScroll){ visibleSectionIndex = 0; } //taking the section which is showing more content in the viewport else{ for (var i = 0; i < sections.length; ++i) { var section = sections[i]; // Pick the the last section which passes the middle line of the screen. if (section.offsetTop <= screen_mid) { visibleSectionIndex = i; } } } if(isCompletelyInViewPort(scrollDirection)){ if(!hasClass($(SECTION_ACTIVE_SEL)[0], COMPLETELY)){ addClass($(SECTION_ACTIVE_SEL)[0], COMPLETELY); removeClass(siblings($(SECTION_ACTIVE_SEL)[0]), COMPLETELY); } } //geting the last one, the current one on the screen currentSection = sections[visibleSectionIndex]; //setting the visible section as active when manually scrolling //executing only once the first time we reach the section if(!hasClass(currentSection, ACTIVE)){ isScrolling = true; var leavingSection = $(SECTION_ACTIVE_SEL)[0]; var leavingSectionIndex = index(leavingSection, SECTION_SEL) + 1; var yMovement = getYmovement(currentSection); var anchorLink = currentSection.getAttribute('data-anchor'); var sectionIndex = index(currentSection, SECTION_SEL) + 1; var activeSlide = $(SLIDE_ACTIVE_SEL, currentSection)[0]; var slideIndex; var slideAnchorLink; var callbacksParams = { activeSection: leavingSection, sectionIndex: sectionIndex -1, anchorLink: anchorLink, element: currentSection, leavingSection: leavingSectionIndex, direction: yMovement }; if(activeSlide){ slideAnchorLink = activeSlide.getAttribute('data-anchor'); slideIndex = index(activeSlide); } if(canScroll){ addClass(currentSection, ACTIVE); removeClass(siblings(currentSection), ACTIVE); if(isFunction( options.onLeave )){ fireCallback('onLeave', callbacksParams); } if(isFunction( options.afterLoad )){ fireCallback('afterLoad', callbacksParams); } stopMedia(leavingSection); lazyLoad(currentSection); playMedia(currentSection); activateMenuAndNav(anchorLink, sectionIndex - 1); if(options.anchors.length){ //needed to enter in hashChange event when using the menu with anchor links lastScrolledDestiny = anchorLink; } setState(slideIndex, slideAnchorLink, anchorLink, sectionIndex); } //small timeout in order to avoid entering in hashChange event when scrolling is not finished yet clearTimeout(scrollId); scrollId = setTimeout(function(){ isScrolling = false; }, 100); } if(options.fitToSection){ //for the auto adjust of the viewport to fit a whole section