From 8003f1dc9e7ab23493b26eefbfd6cda314268264 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Thu, 11 May 2017 02:58:40 +0200 Subject: init --- public/assets/js/vendor/jquery.fullpage.js | 3079 ++++++++++++++++++++++++++++ 1 file changed, 3079 insertions(+) create mode 100644 public/assets/js/vendor/jquery.fullpage.js (limited to 'public/assets/js/vendor/jquery.fullpage.js') diff --git a/public/assets/js/vendor/jquery.fullpage.js b/public/assets/js/vendor/jquery.fullpage.js new file mode 100644 index 0000000..74b5640 --- /dev/null +++ b/public/assets/js/vendor/jquery.fullpage.js @@ -0,0 +1,3079 @@ +/*! + * fullPage 2.9.2 + * https://github.com/alvarotrigo/fullPage.js + * @license MIT licensed + * + * Copyright (C) 2015 alvarotrigo.com - A project by Alvaro Trigo + */ +(function(global, factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + define(['jquery'], function($) { + return factory($, global, global.document, global.Math); + }); + } else if (typeof exports === "object" && exports) { + module.exports = factory(require('jquery'), global, global.document, global.Math); + } else { + factory(jQuery, global, global.document, global.Math); + } +})(typeof window !== 'undefined' ? window : this, function($, window, document, Math, undefined) { + '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 SECTION_FIRST_SEL = SECTION_SEL + ':first'; + var SECTION_LAST_SEL = SECTION_SEL + ':last'; + var TABLE_CELL = 'fp-tableCell'; + var TABLE_CELL_SEL = '.' + TABLE_CELL; + var AUTO_HEIGHT = 'fp-auto-height'; + var AUTO_HEIGHT_SEL = '.fp-auto-height'; + var NORMAL_SCROLL = 'fp-normal-scroll'; + var NORMAL_SCROLL_SEL = '.fp-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; + + var $window = $(window); + var $document = $(document); + + // Default options for iScroll.js used when using scrollOverflow + var iscrollOptions = { + scrollbars: true, + mouseWheel: true, + hideScrollbars: false, + fadeScrollbars: false, + disableMouse: true, + interactiveScrollbars: true + }; + + $.fn.fullpage = function(options) { + //only once my friend! + if($('html').hasClass(ENABLED)){ displayWarnings(); return; } + + // common jQuery objects + var $htmlBody = $('html, body'); + var $body = $('body'); + + var FP = $.fn.fullpage; + + // Create some defaults, extending them with any options that were provided + options = $.extend({ + //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: iscrollHandler, + scrollOverflowOptions: null, + touchSensitivity: 5, + normalScrollElementTouchThreshold: 5, + 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, + + //Custom selectors + sectionSelector: SECTION_DEFAULT_SEL, + slideSelector: SLIDE_DEFAULT_SEL, + + //events + 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 = $(this); + var windowsHeight = $window.height(); + var isResizing = false; + var isWindowFocused = true; + var lastScrolledDestiny; + var lastScrolledSlide; + var canScroll = true; + var scrollings = []; + var controlPressed; + var isScrollAllowed = {}; + isScrollAllowed.m = { 'up':true, 'down':true, 'left':true, 'right':true }; + isScrollAllowed.k = $.extend(true,{}, isScrollAllowed.m); + + //timeouts + var resizeId; + var afterSectionLoadsId; + var afterSlideLoadsId; + var scrollId; + var scrollId2; + var keydownId; + var originals = $.extend(true, {}, options); //deep copy + + displayWarnings(); + + //fixing bug in iScroll with links: https://github.com/cubiq/iscroll/issues/783 + iscrollOptions.click = isTouch; // see #2035 + + //extending iScroll options with the user custom ones + iscrollOptions = $.extend(iscrollOptions, options.scrollOverflowOptions); + + //easeInOutCubic animation included in the plugin + $.extend($.easing,{ easeInOutCubic: function (x, 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); + + if(options.autoScrolling && !options.scrollBar){ + $htmlBody.css({ + 'overflow' : 'hidden', + 'height' : '100%' + }); + + setRecordHistory(originals.recordHistory, 'internal'); + + //for IE touch devices + container.css({ + '-ms-touch-action': 'none', + 'touch-action': 'none' + }); + + if(element.length){ + //moving the container up + silentScroll(element.position().top); + } + + }else{ + $htmlBody.css({ + 'overflow' : 'visible', + 'height' : 'initial' + }); + + setRecordHistory(false, 'internal'); + + //for IE touch devices + container.css({ + '-ms-touch-action': '', + 'touch-action': '' + }); + + //scrolling the page to the section with no animation + if (element.length) { + $htmlBody.scrollTop(element.position().top); + } + } + } + + /** + * 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 possiblity 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(','); + + $.each(directions, function (index, direction){ + setIsScrollAllowed(value, direction, 'm'); + }); + } + else 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(','); + + $.each(directions, function (index, direction){ + setIsScrollAllowed(value, direction, 'k'); + }); + }else{ + options.keyboardScrolling = value; + } + } + + /** + * Moves the page up one section. + */ + function moveSectionUp(){ + var prev = $(SECTION_ACTIVE_SEL).prev(SECTION_SEL); + + //looping to the bottom if there's no more sections above + if (!prev.length && (options.loopTop || options.continuousVertical)) { + prev = $(SECTION_SEL).last(); + } + + if (prev.length) { + scrollPage(prev, null, true); + } + } + + /** + * Moves the page down one section. + */ + function moveSectionDown(){ + var next = $(SECTION_ACTIVE_SEL).next(SECTION_SEL); + + //looping to the top if there's no more sections below + if(!next.length && + (options.loopBottom || options.continuousVertical)){ + next = $(SECTION_SEL).first(); + } + + if(next.length){ + 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.length > 0){ + 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(container.hasClass(DESTROYED)){ return; } //nothing to do if the plugin was destroyed + + isResizing = true; + + windowsHeight = $window.height(); //updating global var + + $(SECTION_SEL).each(function(){ + var slidesWrap = $(this).find(SLIDES_WRAPPER_SEL); + var slides = $(this).find(SLIDE_SEL); + + //adjusting the height of the table-cell for IE and Firefox + if(options.verticalCentered){ + $(this).find(TABLE_CELL_SEL).css('height', getTableHeight($(this)) + 'px'); + } + + $(this).css('height', windowsHeight + 'px'); + + //resizing the scrolling divs + if(options.scrollOverflow){ + if(slides.length){ + slides.each(function(){ + createScrollBar($(this)); + }); + }else{ + createScrollBar($(this)); + } + } + + //adjusting the position fo the FULL WIDTH slides... + if (slides.length > 1) { + landscapeScroll(slidesWrap, slidesWrap.find(SLIDE_ACTIVE_SEL)); + } + }); + + var activeSection = $(SECTION_ACTIVE_SEL); + var sectionIndex = activeSection.index(SECTION_SEL); + + //isn't it the first section? + if(sectionIndex){ + //adjusting the position for the current section + silentMoveTo(sectionIndex + 1); + } + + isResizing = false; + $.isFunction( options.afterResize ) && resizing && options.afterResize.call(container); + $.isFunction( options.afterReBuild ) && !resizing && options.afterReBuild.call(container); + } + + /** + * 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 = $body.hasClass(RESPONSIVE); + + if(active){ + if(!isResponsive){ + setAutoScrolling(false, 'internal'); + setFitToSection(false, 'internal'); + $(SECTION_NAV_SEL).hide(); + $body.addClass(RESPONSIVE); + $.isFunction( options.afterResponsive ) && options.afterResponsive.call( container, active); + } + } + else if(isResponsive){ + setAutoScrolling(originals.autoScrolling, 'internal'); + setFitToSection(originals.autoScrolling, 'internal'); + $(SECTION_NAV_SEL).show(); + $body.removeClass(RESPONSIVE); + $.isFunction( options.afterResponsive ) && options.afterResponsive.call( container, active); + } + } + + if($(this).length){ + //public functions + 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.reBuild = reBuild; + FP.setResponsive = setResponsive; + FP.destroy = destroy; + + 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); + + setAutoScrolling(options.autoScrolling, 'internal'); + + //the starting point is a slide? + var activeSlide = $(SECTION_ACTIVE_SEL).find(SLIDE_ACTIVE_SEL); + + //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( activeSlide.length && ($(SECTION_ACTIVE_SEL).index(SECTION_SEL) !== 0 || ($(SECTION_ACTIVE_SEL).index(SECTION_SEL) === 0 && activeSlide.index() !== 0))){ + silentLandscapeScroll(activeSlide); + } + + responsive(); + + //setting the class for the body element + setBodyClass(); + + if(document.readyState === 'complete'){ + scrollToAnchor(); + } + $window.on('load', scrollToAnchor); + } + + function bindEvents(){ + $window + //when scrolling... + .on('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) + .on('hashchange', hashChangeHandler) + + //when opening a new tab (ctrl + t), `control` won't be pressed when coming back. + .blur(blurHandler) + + //when resizing the site, we adjust the heights of the sections, slimScroll... + .resize(resizeHandler); + + $document + //Sliding with arrow keys, both, vertical and horizontal + .keydown(keydownHandler) + + //to prevent scrolling while zooming + .keyup(keyUpHandler) + + //Scrolls to the section when clicking the navigation bullet + .on('click touchstart', SECTION_NAV_SEL + ' a', sectionBulletHandler) + + //Scrolls the slider to the given slide destination for the given section + .on('click touchstart', SLIDES_NAV_LINK_SEL, slideBulletHandler) + + .on('click', SECTION_NAV_TOOLTIP_SEL, tooltipTextHandler); + + //Scrolling horizontally when clicking on the slider controls. + $(SECTION_SEL).on('click touchstart', SLIDES_ARROW_SEL, slideArrowHandler); + + /** + * Applying normalScroll elements. + * Ignoring the scrolls over the specified selectors. + */ + if(options.normalScrollElements){ + $document.on('mouseenter', options.normalScrollElements, function () { + setMouseWheelScrolling(false); + }); + + $document.on('mouseleave', options.normalScrollElements, function(){ + setMouseWheelScrolling(true); + }); + } + } + + /** + * Setting options from DOM elements if they are not provided. + */ + function setOptionsFromDOM(){ + var sections = container.find(options.sectionSelector); + + //no anchors option? Checking for them in the DOM attributes + if(!options.anchors.length){ + options.anchors = sections.filter('[data-anchor]').map(function(){ + return $(this).data('anchor').toString(); + }).get(); + } + + //no tooltips option? Checking for them in the DOM attributes + if(!options.navigationTooltips.length){ + options.navigationTooltips = sections.filter('[data-tooltip]').map(function(){ + return $(this).data('tooltip').toString(); + }).get(); + } + } + + /** + * Works over the DOM structure to set it up for the current fullpage options. + */ + function prepareDom(){ + container.css({ + 'height': '100%', + 'position': 'relative' + }); + + //adding a class to recognize the container internally in the code + container.addClass(WRAPPER); + $('html').addClass(ENABLED); + + //due to https://github.com/alvarotrigo/fullPage.js/issues/1502 + windowsHeight = $window.height(); + + container.removeClass(DESTROYED); //in case it was destroyed before initializing it again + + addInternalSelectors(); + + //styling the sections / slides / menu + $(SECTION_SEL).each(function(index){ + var section = $(this); + var slides = section.find(SLIDE_SEL); + var numSlides = slides.length; + + styleSection(section, index); + styleMenu(section, index); + + // 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).appendTo($body); + } + + //vertical centered of the navigation + active bullet + if(options.navigation){ + addVerticalNavigation(); + } + + enableYoutubeAPI(); + + if(options.scrollOverflow){ + if(document.readyState === 'complete'){ + createScrollBarHandler(); + } + //after DOM and images are loaded + $window.on('load', createScrollBarHandler); + }else{ + afterRenderActions(); + } + } + + /** + * Styles the horizontal slides for a section. + */ + function styleSlides(section, slides, numSlides){ + var sliderWidth = numSlides * 100; + var slideWidth = 100 / numSlides; + + slides.wrapAll('
'); + slides.parent().wrap('
'); + + section.find(SLIDES_CONTAINER_SEL).css('width', sliderWidth + '%'); + + if(numSlides > 1){ + if(options.controlArrows){ + createSlideArrows(section); + } + + if(options.slidesNavigation){ + addSlidesNavigation(section, numSlides); + } + } + + slides.each(function(index) { + $(this).css('width', slideWidth + '%'); + + if(options.verticalCentered){ + addTableClass($(this)); + } + }); + + var startingSlide = section.find(SLIDE_ACTIVE_SEL); + + //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.length && ($(SECTION_ACTIVE_SEL).index(SECTION_SEL) !== 0 || ($(SECTION_ACTIVE_SEL).index(SECTION_SEL) === 0 && startingSlide.index() !== 0))){ + silentLandscapeScroll(startingSlide); + }else{ + slides.eq(0).addClass(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).length === 0) { + section.addClass(ACTIVE); + } + + section.css('height', windowsHeight + 'px'); + + if(options.paddingTop){ + section.css('padding-top', options.paddingTop); + } + + if(options.paddingBottom){ + section.css('padding-bottom', options.paddingBottom); + } + + if (typeof options.sectionsColor[index] !== 'undefined') { + section.css('background-color', options.sectionsColor[index]); + } + + if (typeof options.anchors[index] !== 'undefined') { + section.attr('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(section.hasClass(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 && $(options.menu).closest(WRAPPER_SEL).length){ + $(options.menu).appendTo($body); + } + } + + /** + * Adds internal classes to be able to provide customizable selectors + * keeping the link with the style sheet. + */ + function addInternalSelectors(){ + container.find(options.sectionSelector).addClass(SECTION); + container.find(options.slideSelector).addClass(SLIDE); + } + + /** + * Creates the control arrows for the given section + */ + function createSlideArrows(section){ + section.find(SLIDES_WRAPPER_SEL).after('
'); + + if(options.controlArrowColor!='#fff'){ + section.find(SLIDES_ARROW_NEXT_SEL).css('border-color', 'transparent transparent transparent '+options.controlArrowColor); + section.find(SLIDES_ARROW_PREV_SEL).css('border-color', 'transparent '+ options.controlArrowColor + ' transparent transparent'); + } + + if(!options.loopHorizontal){ + section.find(SLIDES_ARROW_PREV_SEL).hide(); + } + } + + /** + * Creates a vertical navigation bar. + */ + function addVerticalNavigation(){ + $body.append('
    '); + var nav = $(SECTION_NAV_SEL); + + nav.addClass(function() { + return options.showActiveTooltip ? SHOW_ACTIVE_TOOLTIP + ' ' + options.navigationPosition : options.navigationPosition; + }); + + for (var i = 0; i < $(SECTION_SEL).length; i++) { + var link = ''; + if (options.anchors.length) { + link = options.anchors[i]; + } + + var li = '
  • '; + + // Only add tooltip if needed (defined by user) + var tooltip = options.navigationTooltips[i]; + + if (typeof tooltip !== 'undefined' && tooltip !== '') { + li += '
    ' + tooltip + '
    '; + } + + li += '
  • '; + + nav.find('ul').append(li); + } + + //centering it vertically + $(SECTION_NAV_SEL).css('margin-top', '-' + ($(SECTION_NAV_SEL).height()/2) + 'px'); + + //activating the current active section + $(SECTION_NAV_SEL).find('li').eq($(SECTION_ACTIVE_SEL).index(SECTION_SEL)).find('a').addClass(ACTIVE); + } + + /** + * Creates the slim scroll scrollbar for the sections and slides inside them. + */ + function createScrollBarHandler(){ + $(SECTION_SEL).each(function(){ + var slides = $(this).find(SLIDE_SEL); + + if(slides.length){ + slides.each(function(){ + createScrollBar($(this)); + }); + }else{ + createScrollBar($(this)); + } + + }); + afterRenderActions(); + } + + /* + * Enables the Youtube videos API so we can control their flow if necessary. + */ + function enableYoutubeAPI(){ + container.find('iframe[src*="youtube.com/embed/"]').each(function(){ + addURLParam($(this), 'enablejsapi=1'); + }); + } + + /** + * Adds a new parameter and its value to the `src` of a given element + */ + function addURLParam(element, newParam){ + var originalSrc = element.attr('src'); + element.attr('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); + + section.addClass(COMPLETELY); + + if(options.scrollOverflowHandler.afterRender){ + options.scrollOverflowHandler.afterRender(section); + } + lazyLoad(section); + playMedia(section); + options.scrollOverflowHandler.afterLoad(); + + $.isFunction( options.afterLoad ) && options.afterLoad.call(section, section.data('anchor'), (section.index(SECTION_SEL) + 1)); + $.isFunction( options.afterRender ) && options.afterRender.call(container); + } + + + var isScrolling = false; + var lastScroll = 0; + + //when scrolling... + function scrollHandler(){ + var currentSection; + + if(!options.autoScrolling || options.scrollBar){ + var currentScroll = $window.scrollTop(); + var scrollDirection = getScrollDirection(currentScroll); + var visibleSectionIndex = 0; + var screen_mid = currentScroll + ($window.height() / 2.0); + var isAtBottom = $body.height() - $window.height() === currentScroll; + var sections = document.querySelectorAll(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(!$(SECTION_ACTIVE_SEL).hasClass(COMPLETELY)){ + $(SECTION_ACTIVE_SEL).addClass(COMPLETELY).siblings().removeClass(COMPLETELY); + } + } + + //geting the last one, the current one on the screen + currentSection = $(sections).eq(visibleSectionIndex); + + //setting the visible section as active when manually scrolling + //executing only once the first time we reach the section + if(!currentSection.hasClass(ACTIVE)){ + isScrolling = true; + var leavingSection = $(SECTION_ACTIVE_SEL); + var leavingSectionIndex = leavingSection.index(SECTION_SEL) + 1; + var yMovement = getYmovement(currentSection); + var anchorLink = currentSection.data('anchor'); + var sectionIndex = currentSection.index(SECTION_SEL) + 1; + var activeSlide = currentSection.find(SLIDE_ACTIVE_SEL); + var slideIndex; + var slideAnchorLink; + + if(activeSlide.length){ + slideAnchorLink = activeSlide.data('anchor'); + slideIndex = activeSlide.index(); + } + + if(canScroll){ + currentSection.addClass(ACTIVE).siblings().removeClass(ACTIVE); + + $.isFunction( options.onLeave ) && options.onLeave.call( leavingSection, leavingSectionIndex, sectionIndex, yMovement); + $.isFunction( options.afterLoad ) && options.afterLoad.call( currentSection, anchorLink, sectionIndex); + + 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 + clearTimeout(scrollId2); + + scrollId2 = setTimeout(function(){ + //checking fitToSection again in case it was set to false before the timeout delay + if(canScroll && options.fitToSection){ + //allows to scroll to an active section and + //if the section is already active, we prevent firing callbacks + if($(SECTION_ACTIVE_SEL).is(currentSection)){ + isResizing = true; + } + scrollPage($(SECTION_ACTIVE_SEL)); + + isResizing = false; + } + }, options.fitToSectionDelay); + } + } + } + + /** + * Determines whether the active section has seen in its whole or not. + */ + function isCompletelyInViewPort(movement){ + var top = $(SECTION_ACTIVE_SEL).position().top; + var bottom = top + $window.height(); + + if(movement == 'up'){ + return bottom >= ($window.scrollTop() + $window.height()); + } + return top <= $window.scrollTop(); + } + + /** + * Gets the directon of the the scrolling fired by the scroll event. + */ + function getScrollDirection(currentScroll){ + var direction = currentScroll > lastScroll ? 'down' : 'up'; + + lastScroll = currentScroll; + + //needed for auto-height sections to determine if we want to scroll to the top or bottom of the destination + previousDestTop = currentScroll; + + return direction; + } + + /** + * 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){ + if (!isScrollAllowed.m[type]){ + return; + } + var check = (type === 'down') ? 'bottom' : 'top'; + var scrollSection = (type === 'down') ? moveSectionDown : moveSectionUp; + + if(scrollable.length > 0 ){ + //is the scrollbar at the start/end of the scroll? + if(options.scrollOverflowHandler.isScrolled(check, scrollable)){ + scrollSection(); + }else{ + return true; + } + }else{ + // moved up/down + scrollSection(); + } + } + + /* + * Preventing bouncing in iOS #2285 + */ + function preventBouncing(event){ + var e = event.originalEvent; + if(!checkParentForNormalScrollElement(event.target) && options.autoScrolling && isReallyTouch(e)){ + //preventing the easing on iOS devices + event.preventDefault(); + } + } + + var touchStartY = 0; + var touchStartX = 0; + var touchEndY = 0; + var touchEndX = 0; + + /* Detecting touch events + + * As we are changing the top property of the page on scrolling, we can not use the traditional way to detect it. + * This way, the touchstart and the touch moves shows an small difference between them which is the + * used one to determine the direction. + */ + function touchMoveHandler(event){ + var e = event.originalEvent; + var activeSection = $(e.target).closest(SECTION_SEL); + + // additional: if one of the normalScrollElements isn't within options.normalScrollElementTouchThreshold hops up the DOM chain + if (!checkParentForNormalScrollElement(event.target) && isReallyTouch(e) ) { + + if(options.autoScrolling){ + //preventing the easing on iOS devices + event.preventDefault(); + } + + var scrollable = options.scrollOverflowHandler.scrollable(activeSection); + var touchEvents = getEventsPage(e); + + touchEndY = touchEvents.y; + touchEndX = touchEvents.x; + + //if movement in the X axys is greater than in the Y and the currect section has slides... + if (activeSection.find(SLIDES_WRAPPER_SEL).length && Math.abs(touchStartX - touchEndX) > (Math.abs(touchStartY - touchEndY))) { + + //is the movement greater than the minimum resistance to scroll? + if (!slideMoving && Math.abs(touchStartX - touchEndX) > ($window.outerWidth() / 100 * options.touchSensitivity)) { + if (touchStartX > touchEndX) { + if(isScrollAllowed.m.right){ + moveSlideRight(activeSection); //next + } + } else { + if(isScrollAllowed.m.left){ + moveSlideLeft(activeSection); //prev + } + } + } + } + + //vertical scrolling (only when autoScrolling is enabled) + else if(options.autoScrolling && canScroll){ + + //is the movement greater than the minimum resistance to scroll? + if (Math.abs(touchStartY - touchEndY) > ($window.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); + } + } + + /** + * 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'; + } + + /** + * Handler for the touch start event. + */ + function touchStartHandler(event){ + var e = event.originalEvent; + + //stopping the auto scroll to adjust to a section + if(options.fitToSection){ + $htmlBody.stop(); + } + + if(isReallyTouch(e)){ + var touchEvents = getEventsPage(e); + touchStartY = touchEvents.y; + touchStartX = touchEvents.x; + } + } + + /** + * 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); + } + + /** + * 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(); + var isNormalScroll = $(COMPLETELY_SEL).hasClass(NORMAL_SCROLL); + + //autoscrolling and not zooming? + if(options.autoScrolling && !controlPressed && !isNormalScroll){ + // 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)); + + //preventing to scroll the site on mouse wheel when scrollbar is present + if(options.scrollBar){ + e.preventDefault ? e.preventDefault() : e.returnValue = false; + } + + var activeSection = $(SECTION_ACTIVE_SEL); + var scrollable = options.scrollOverflowHandler.scrollable(activeSection); + + //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(canScroll){ + var averageEnd = getAverage(scrollings, 10); + var averageMiddle = getAverage(scrollings, 70); + var isAccelerating = averageEnd >= averageMiddle; + + //to avoid double swipes... + if(isAccelerating && isScrollingVertically){ + //scrolling down? + if (delta < 0) { + scrolling('down', scrollable); + + //scrolling up? + }else { + scrolling('up', scrollable); + } + } + } + + return false; + } + + if(options.fitToSection){ + //stopping the auto scroll to adjust to a section + $htmlBody.stop(); + } + } + + /** + * Slides a slider to the given direction. + * Optional `section` param. + */ + function moveSlide(direction, section){ + var activeSection = typeof section === 'undefined' ? $(SECTION_ACTIVE_SEL) : section; + var slides = activeSection.find(SLIDES_WRAPPER_SEL); + var numSlides = slides.find(SLIDE_SEL).length; + + // more than one slide needed and nothing should be sliding + if (!slides.length || slideMoving || numSlides < 2) { + return; + } + + var currentSlide = slides.find(SLIDE_ACTIVE_SEL); + var destiny = null; + + if(direction === 'left'){ + destiny = currentSlide.prev(SLIDE_SEL); + }else{ + destiny = currentSlide.next(SLIDE_SEL); + } + + //isn't there a next slide in the secuence? + if(!destiny.length){ + //respect loopHorizontal settin + if (!options.loopHorizontal) return; + + if(direction === 'left'){ + destiny = currentSlide.siblings(':last'); + }else{ + destiny = currentSlide.siblings(':first'); + } + } + + slideMoving = true; + + landscapeScroll(slides, destiny, direction); + } + + /** + * Maintains the active slides in the viewport + * (Because the `scroll` animation might get lost with some actions, such as when using continuousVertical) + */ + function keepSlidesPosition(){ + $(SLIDE_ACTIVE_SEL).each(function(){ + silentLandscapeScroll($(this), 'internal'); + }); + } + + var previousDestTop = 0; + /** + * Returns the destination Y position based on the scrolling direction and + * the height of the section. + */ + function getDestinationPosition(element){ + var elemPosition = element.position(); + + //top of the desination will be at the top of the viewport + var position = elemPosition.top; + var isScrollingDown = elemPosition.top > previousDestTop; + var sectionBottom = position - windowsHeight + element.outerHeight(); + var bigSectionsDestination = options.bigSectionsDestination; + + //is the destination element bigger than the viewport? + if(element.outerHeight() > windowsHeight){ + //scrolling up? + if(!isScrollingDown && !bigSectionsDestination || bigSectionsDestination === 'bottom' ){ + position = sectionBottom; + } + } + + //sections equal or smaller than the viewport height && scrolling down? || is resizing and its in the last section + else if(isScrollingDown || (isResizing && element.is(':last-child')) ){ + //The bottom of the destination will be at the bottom of the viewport + position = sectionBottom; + } + + /* + Keeping record of the last scrolled position to determine the scrolling direction. + No conventional methods can be used as the scroll bar might not be present + AND the section might not be active if it is auto-height and didnt reach the middle + of the viewport. + */ + previousDestTop = position; + return position; + } + + /** + * Scrolls the site to the given element and scrolls to the slide if a callback is given. + */ + function scrollPage(element, callback, isMovementUp){ + if(typeof element === 'undefined'){ return; } //there's no element to scroll, leaving the function + + var dtop = getDestinationPosition(element); + var slideAnchorLink; + var slideIndex; + + //local variables + var v = { + element: element, + callback: callback, + isMovementUp: isMovementUp, + dtop: dtop, + yMovement: getYmovement(element), + anchorLink: element.data('anchor'), + sectionIndex: element.index(SECTION_SEL), + activeSlide: element.find(SLIDE_ACTIVE_SEL), + activeSection: $(SECTION_ACTIVE_SEL), + leavingSection: $(SECTION_ACTIVE_SEL).index(SECTION_SEL) + 1, + + //caching the value of isResizing at the momment the function is called + //because it will be checked later inside a setTimeout and the value might change + localIsResizing: isResizing + }; + + //quiting when destination scroll is the same as the current one + if((v.activeSection.is(element) && !isResizing) || (options.scrollBar && $window.scrollTop() === v.dtop && !element.hasClass(AUTO_HEIGHT) )){ return; } + + if(v.activeSlide.length){ + slideAnchorLink = v.activeSlide.data('anchor'); + slideIndex = v.activeSlide.index(); + } + + // If continuousVertical && we need to wrap around + if (options.autoScrolling && options.continuousVertical && typeof (v.isMovementUp) !== "undefined" && + ((!v.isMovementUp && v.yMovement == 'up') || // Intending to scroll down but about to go up or + (v.isMovementUp && v.yMovement == 'down'))) { // intending to scroll up but about to go down + + v = createInfiniteSections(v); + } + + //callback (onLeave) if the site is not just resizing and readjusting the slides + if($.isFunction(options.onLeave) && !v.localIsResizing){ + if(options.onLeave.call(v.activeSection, v.leavingSection, (v.sectionIndex + 1), v.yMovement) === false){ + return; + } + } + + stopMedia(v.activeSection); + + options.scrollOverflowHandler.beforeLeave(); + element.addClass(ACTIVE).siblings().removeClass(ACTIVE); + lazyLoad(element); + options.scrollOverflowHandler.onLeave(); + + + //preventing from activating the MouseWheelHandler event + //more than once if the page is scrolling + canScroll = false; + + setState(slideIndex, slideAnchorLink, v.anchorLink, v.sectionIndex); + + performMovement(v); + + //flag to avoid callingn `scrollPage()` twice in case of using anchor links + lastScrolledDestiny = v.anchorLink; + + //avoid firing it twice (as it does also on scroll) + activateMenuAndNav(v.anchorLink, v.sectionIndex); + } + + /** + * Performs the vertical movement (by CSS3 or by jQuery) + */ + function performMovement(v){ + // using CSS3 translate functionality + if (options.css3 && options.autoScrolling && !options.scrollBar) { + + // The first section can have a negative value in iOS 10. Not quite sure why: -0.0142822265625 + // that's why we round it to 0. + var translate3d = 'translate3d(0px, -' + Math.round(v.dtop) + 'px, 0px)'; + transformContainer(translate3d, true); + + //even when the scrollingSpeed is 0 there's a little delay, which might cause the + //scrollingSpeed to change in case of using silentMoveTo(); + if(options.scrollingSpeed){ + clearTimeout(afterSectionLoadsId); + afterSectionLoadsId = setTimeout(function () { + afterSectionLoads(v); + }, options.scrollingSpeed); + }else{ + afterSectionLoads(v); + } + } + + // using jQuery animate + else{ + var scrollSettings = getScrollSettings(v); + + $(scrollSettings.element).animate( + scrollSettings.options, + options.scrollingSpeed, options.easing).promise().done(function () { //only one single callback in case of animating `html, body` + if(options.scrollBar){ + + /* Hack! + The timeout prevents setting the most dominant section in the viewport as "active" when the user + scrolled to a smaller section by using the mousewheel (auto scrolling) rather than draging the scroll bar. + + When using scrollBar:true It seems like the scroll events still getting propagated even after the scrolling animation has finished. + */ + setTimeout(function(){ + afterSectionLoads(v); + },30); + }else{ + afterSectionLoads(v); + } + }); + } + } + + /** + * Gets the scrolling settings depending on the plugin autoScrolling option + */ + function getScrollSettings(v){ + var scroll = {}; + + if(options.autoScrolling && !options.scrollBar){ + scroll.options = { 'top': -v.dtop}; + scroll.element = WRAPPER_SEL; + }else{ + scroll.options = { 'scrollTop': v.dtop}; + scroll.element = 'html, body'; + } + + return scroll; + } + + /** + * Adds sections before or after the current one to create the infinite effect. + */ + function createInfiniteSections(v){ + // Scrolling down + if (!v.isMovementUp) { + // Move all previous sections to after the active section + $(SECTION_ACTIVE_SEL).after(v.activeSection.prevAll(SECTION_SEL).get().reverse()); + } + else { // Scrolling up + // Move all next sections to before the active section + $(SECTION_ACTIVE_SEL).before(v.activeSection.nextAll(SECTION_SEL)); + } + + // Maintain the displayed position (now that we changed the element order) + silentScroll($(SECTION_ACTIVE_SEL).position().top); + + // Maintain the active slides visible in the viewport + keepSlidesPosition(); + + // save for later the elements that still need to be reordered + v.wrapAroundElements = v.activeSection; + + // Recalculate animation variables + v.dtop = v.element.position().top; + v.yMovement = getYmovement(v.element); + + return v; + } + + /** + * Fix section order after continuousVertical changes have been animated + */ + function continuousVerticalFixSectionOrder (v) { + // If continuousVertical is in effect (and autoScrolling would also be in effect then), + // finish moving the elements around so the direct navigation will function more simply + if (!v.wrapAroundElements || !v.wrapAroundElements.length) { + return; + } + + if (v.isMovementUp) { + $(SECTION_FIRST_SEL).before(v.wrapAroundElements); + } + else { + $(SECTION_LAST_SEL).after(v.wrapAroundElements); + } + + silentScroll($(SECTION_ACTIVE_SEL).position().top); + + // Maintain the active slides visible in the viewport + keepSlidesPosition(); + } + + + /** + * Actions to do once the section is loaded. + */ + function afterSectionLoads (v){ + continuousVerticalFixSectionOrder(v); + + //callback (afterLoad) if the site is not just resizing and readjusting the slides + $.isFunction(options.afterLoad) && !v.localIsResizing && options.afterLoad.call(v.element, v.anchorLink, (v.sectionIndex + 1)); + options.scrollOverflowHandler.afterLoad(); + + if(!v.localIsResizing){ + playMedia(v.element); + } + + v.element.addClass(COMPLETELY).siblings().removeClass(COMPLETELY); + + canScroll = true; + + $.isFunction(v.callback) && v.callback.call(this); + } + + /** + * Lazy loads image, video and audio elements. + */ + function lazyLoad(destiny){ + if (!options.lazyLoading){ + return; + } + + var panel = getSlideOrSection(destiny); + var element; + + panel.find('img[data-src], source[data-src], audio[data-src], iframe[data-src]').each(function(){ + element = $(this); + element.attr('src', element.data('src')); + element.removeAttr('data-src'); + + if(element.is('source')){ + element.closest('video').get(0).load(); + } + }); + } + + /** + * Plays video and audio elements. + */ + function playMedia(destiny){ + var panel = getSlideOrSection(destiny); + + //playing HTML5 media elements + panel.find('video, audio').each(function(){ + var element = $(this).get(0); + + if( element.hasAttribute('data-autoplay') && typeof element.play === 'function' ) { + element.play(); + } + }); + + //youtube videos + panel.find('iframe[src*="youtube.com/embed/"]').each(function(){ + var element = $(this).get(0); + + if ( element.hasAttribute('data-autoplay') ){ + playYoutube(element); + } + + //in case the URL was not loaded yet. On page load we need time for the new URL (with the API string) to load. + element.onload = function() { + if ( element.hasAttribute('data-autoplay') ){ + playYoutube(element); + } + }; + }); + } + + /** + * Plays a youtube video + */ + function playYoutube(element){ + element.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', '*'); + } + + /** + * Stops video and audio elements. + */ + function stopMedia(destiny){ + var panel = getSlideOrSection(destiny); + + //stopping HTML5 media elements + panel.find('video, audio').each(function(){ + var element = $(this).get(0); + + if( !element.hasAttribute('data-keepplaying') && typeof element.pause === 'function' ) { + element.pause(); + } + }); + + //youtube videos + panel.find('iframe[src*="youtube.com/embed/"]').each(function(){ + var element = $(this).get(0); + + if( /youtube\.com\/embed\//.test($(this).attr('src')) && !element.hasAttribute('data-keepplaying')){ + $(this).get(0).contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}','*'); + } + }); + } + + /** + * Gets the active slide (or section) for the given section + */ + function getSlideOrSection(destiny){ + var slide = destiny.find(SLIDE_ACTIVE_SEL); + if( slide.length ) { + destiny = $(slide); + } + + return destiny; + } + + /** + * Scrolls to the anchor in the URL when loading the site + */ + function scrollToAnchor(){ + //getting the anchor link in the URL and deleting the `#` + var value = window.location.hash.replace('#', '').split('/'); + var sectionAnchor = decodeURIComponent(value[0]); + var slideAnchor = decodeURIComponent(value[1]); + + if(sectionAnchor){ //if theres any # + if(options.animateAnchor){ + scrollPageAndSlide(sectionAnchor, slideAnchor); + }else{ + silentMoveTo(sectionAnchor, slideAnchor); + } + } + } + + /** + * 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) + */ + function hashChangeHandler(){ + if(!isScrolling && !options.lockAnchors){ + var value = window.location.hash.replace('#', '').split('/'); + var sectionAnchor = decodeURIComponent(value[0]); + var slideAnchor = decodeURIComponent(value[1]); + + //when moving to a slide in the first section for the first time (first time to add an anchor to the URL) + var isFirstSlideMove = (typeof lastScrolledDestiny === 'undefined'); + var isFirstScrollMove = (typeof lastScrolledDestiny === 'undefined' && typeof slideAnchor === 'undefined' && !slideMoving); + + + 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) && !isFirstSlideMove || isFirstScrollMove || (!slideMoving && lastScrolledSlide != slideAnchor )) { + scrollPageAndSlide(sectionAnchor, slideAnchor); + } + } + } + } + + //Sliding with arrow keys, both, vertical and horizontal + function keydownHandler(e) { + + clearTimeout(keydownId); + + var activeElement = $(':focus'); + + if(!activeElement.is('textarea') && !activeElement.is('input') && !activeElement.is('select') && + activeElement.attr('contentEditable') !== "true" && activeElement.attr('contentEditable') !== '' && + options.keyboardScrolling && options.autoScrolling){ + var keyCode = e.which; + + //preventing the scroll with arrow keys & spacebar & Page Up & Down keys + var keyControls = [40, 38, 32, 33, 34]; + if($.inArray(keyCode, keyControls) > -1){ + e.preventDefault(); + } + + controlPressed = e.ctrlKey; + + keydownId = setTimeout(function(){ + onkeydown(e); + },150); + } + } + + function tooltipTextHandler(){ + $(this).prev().trigger('click'); + } + + //to prevent scrolling while zooming + function keyUpHandler(e){ + if(isWindowFocused){ //the keyup gets fired on new tab ctrl + t in Firefox + controlPressed = e.ctrlKey; + } + } + + //binding the mousemove when the mouse's middle button is released + function mouseDownHandler(e){ + //middle button + if (e.which == 2){ + oldPageY = e.pageY; + container.on('mousemove', mouseMoveHandler); + } + } + + //unbinding the mousemove when the mouse's middle button is released + function mouseUpHandler(e){ + //middle button + if (e.which == 2){ + container.off('mousemove'); + } + } + + //Scrolling horizontally when clicking on the slider controls. + function slideArrowHandler(){ + var section = $(this).closest(SECTION_SEL); + + if ($(this).hasClass(SLIDES_PREV)) { + if(isScrollAllowed.m.left){ + moveSlideLeft(section); + } + } else { + if(isScrollAllowed.m.right){ + moveSlideRight(section); + } + } + } + + //when opening a new tab (ctrl + t), `control` won't be pressed when coming back. + function blurHandler(){ + isWindowFocused = false; + controlPressed = false; + } + + //Scrolls to the section when clicking the navigation bullet + function sectionBulletHandler(e){ + e.preventDefault(); + var index = $(this).parent().index(); + scrollPage($(SECTION_SEL).eq(index)); + } + + //Scrolls the slider to the given slide destination for the given section + function slideBulletHandler(e){ + e.preventDefault(); + var slides = $(this).closest(SECTION_SEL).find(SLIDES_WRAPPER_SEL); + var destiny = slides.find(SLIDE_SEL).eq($(this).closest('li').index()); + + landscapeScroll(slides, destiny); + } + + /** + * Keydown event + */ + function onkeydown(e){ + var shiftPressed = e.shiftKey; + + //do nothing if we can not scroll or we are not using horizotnal key arrows. + if(!canScroll && [37,39].indexOf(e.which) < 0){ + return; + } + + switch (e.which) { + //up + case 38: + case 33: + if(isScrollAllowed.k.up){ + moveSectionUp(); + } + break; + + //down + case 32: //spacebar + if(shiftPressed && isScrollAllowed.k.up){ + moveSectionUp(); + break; + } + /* falls through */ + case 40: + case 34: + if(isScrollAllowed.k.down){ + moveSectionDown(); + } + break; + + //Home + case 36: + if(isScrollAllowed.k.up){ + moveTo(1); + } + break; + + //End + case 35: + if(isScrollAllowed.k.down){ + moveTo( $(SECTION_SEL).length ); + } + break; + + //left + case 37: + if(isScrollAllowed.k.left){ + moveSlideLeft(); + } + break; + + //right + case 39: + if(isScrollAllowed.k.right){ + moveSlideRight(); + } + break; + + default: + return; // exit this handler for other keys + } + } + + /** + * Detecting the direction of the mouse movement. + * Used only for the middle button of the mouse. + */ + var oldPageY = 0; + function mouseMoveHandler(e){ + if(canScroll){ + // moving up + if (e.pageY < oldPageY && isScrollAllowed.m.up){ + moveSectionUp(); + } + + // moving down + else if(e.pageY > oldPageY && isScrollAllowed.m.down){ + moveSectionDown(); + } + } + oldPageY = e.pageY; + } + + /** + * Scrolls horizontal sliders. + */ + function landscapeScroll(slides, destiny, direction){ + var section = slides.closest(SECTION_SEL); + var v = { + slides: slides, + destiny: destiny, + direction: direction, + destinyPos: destiny.position(), + slideIndex: destiny.index(), + section: section, + sectionIndex: section.index(SECTION_SEL), + anchorLink: section.data('anchor'), + slidesNav: section.find(SLIDES_NAV_SEL), + slideAnchor: getAnchor(destiny), + prevSlide: section.find(SLIDE_ACTIVE_SEL), + prevSlideIndex: section.find(SLIDE_ACTIVE_SEL).index(), + + //caching the value of isResizing at the momment the function is called + //because it will be checked later inside a setTimeout and the value might change + localIsResizing: isResizing + }; + v.xMovement = getXmovement(v.prevSlideIndex, v.slideIndex); + + //important!! Only do it when not resizing + if(!v.localIsResizing){ + //preventing from scrolling to the next/prev section when using scrollHorizontally + canScroll = false; + } + + if(options.onSlideLeave){ + + //if the site is not just resizing and readjusting the slides + if(!v.localIsResizing && v.xMovement!=='none'){ + if($.isFunction( options.onSlideLeave )){ + if(options.onSlideLeave.call( v.prevSlide, v.anchorLink, (v.sectionIndex + 1), v.prevSlideIndex, v.xMovement, v.slideIndex ) === false){ + slideMoving = false; + return; + } + } + } + } + + destiny.addClass(ACTIVE).siblings().removeClass(ACTIVE); + + if(!v.localIsResizing){ + stopMedia(v.prevSlide); + lazyLoad(destiny); + } + + if(!options.loopHorizontal && options.controlArrows){ + //hidding it for the fist slide, showing for the rest + section.find(SLIDES_ARROW_PREV_SEL).toggle(v.slideIndex!==0); + + //hidding it for the last slide, showing for the rest + section.find(SLIDES_ARROW_NEXT_SEL).toggle(!destiny.is(':last-child')); + } + + //only changing the URL if the slides are in the current section (not for resize re-adjusting) + if(section.hasClass(ACTIVE)){ + setState(v.slideIndex, v.slideAnchor, v.anchorLink, v.sectionIndex); + } + + performHorizontalMove(slides, v, true); + } + + + function afterSlideLoads(v){ + activeSlidesNavigation(v.slidesNav, v.slideIndex); + + //if the site is not just resizing and readjusting the slides + if(!v.localIsResizing){ + $.isFunction( options.afterSlideLoad ) && options.afterSlideLoad.call( v.destiny, v.anchorLink, (v.sectionIndex + 1), v.slideAnchor, v.slideIndex); + + //needs to be inside the condition to prevent problems with continuousVertical and scrollHorizontally + //and to prevent double scroll right after a windows resize + canScroll = true; + + playMedia(v.destiny); + } + + //letting them slide again + slideMoving = false; + } + + /** + * Performs the horizontal movement. (CSS3 or jQuery) + * + * @param fireCallback {Bool} - determines whether or not to fire the callback + */ + function performHorizontalMove(slides, v, fireCallback){ + var destinyPos = v.destinyPos; + + if(options.css3){ + var translate3d = 'translate3d(-' + Math.round(destinyPos.left) + 'px, 0px, 0px)'; + + addAnimation(slides.find(SLIDES_CONTAINER_SEL)).css(getTransforms(translate3d)); + + afterSlideLoadsId = setTimeout(function(){ + fireCallback && afterSlideLoads(v); + }, options.scrollingSpeed, options.easing); + }else{ + slides.animate({ + scrollLeft : Math.round(destinyPos.left) + }, options.scrollingSpeed, options.easing, function() { + + fireCallback && afterSlideLoads(v); + }); + } + } + + /** + * Sets the state for the horizontal bullet navigations. + */ + function activeSlidesNavigation(slidesNav, slideIndex){ + slidesNav.find(ACTIVE_SEL).removeClass(ACTIVE); + slidesNav.find('li').eq(slideIndex).find('a').addClass(ACTIVE); + } + + var previousHeight = windowsHeight; + + //when resizing the site, we adjust the heights of the sections, slimScroll... + function resizeHandler(){ + //checking if it needs to get responsive + responsive(); + + // rebuild immediately on touch devices + if (isTouchDevice) { + var activeElement = $(document.activeElement); + + //if the keyboard is NOT visible + if (!activeElement.is('textarea') && !activeElement.is('input') && !activeElement.is('select')) { + var currentHeight = $window.height(); + + //making sure the change in the viewport size is enough to force a rebuild. (20 % of the window to avoid problems when hidding scroll bars) + if( Math.abs(currentHeight - previousHeight) > (20 * Math.max(previousHeight, currentHeight) / 100) ){ + reBuild(true); + previousHeight = currentHeight; + } + } + }else{ + //in order to call the functions only when the resize is finished + //http://stackoverflow.com/questions/4298612/jquery-how-to-call-resize-event-only-once-its-finished-resizing + clearTimeout(resizeId); + + resizeId = setTimeout(function(){ + reBuild(true); + }, 350); + } + } + + /** + * Checks if the site needs to get responsive and disables autoScrolling if so. + * A class `fp-responsive` is added to the plugin's container in case the user wants to use it for his own responsive CSS. + */ + function responsive(){ + var widthLimit = options.responsive || options.responsiveWidth; //backwards compatiblity + var heightLimit = options.responsiveHeight; + + //only calculating what we need. Remember its called on the resize event. + var isBreakingPointWidth = widthLimit && $window.outerWidth() < widthLimit; + var isBreakingPointHeight = heightLimit && $window.height() < heightLimit; + + if(widthLimit && heightLimit){ + setResponsive(isBreakingPointWidth || isBreakingPointHeight); + } + else if(widthLimit){ + setResponsive(isBreakingPointWidth); + } + else if(heightLimit){ + setResponsive(isBreakingPointHeight); + } + } + + /** + * Adds transition animations for the given element + */ + function addAnimation(element){ + var transition = 'all ' + options.scrollingSpeed + 'ms ' + options.easingcss3; + + element.removeClass(NO_TRANSITION); + return element.css({ + '-webkit-transition': transition, + 'transition': transition + }); + } + + /** + * Remove transition animations for the given element + */ + function removeAnimation(element){ + return element.addClass(NO_TRANSITION); + } + + /** + * Activating the vertical navigation bullets according to the given slide name. + */ + function activateNavDots(name, sectionIndex){ + if(options.navigation){ + $(SECTION_NAV_SEL).find(ACTIVE_SEL).removeClass(ACTIVE); + if(name){ + $(SECTION_NAV_SEL).find('a[href="#' + name + '"]').addClass(ACTIVE); + }else{ + $(SECTION_NAV_SEL).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_SEL).removeClass(ACTIVE); + $(options.menu).find('[data-menuanchor="'+name+'"]').addClass(ACTIVE); + } + } + + /** + * Sets to active the current menu and vertical nav items. + */ + function activateMenuAndNav(anchor, index){ + activateMenuElement(anchor); + activateNavDots(anchor, index); + } + + /** + * Retuns `up` or `down` depending on the scrolling movement to reach its destination + * from the current section. + */ + function getYmovement(destiny){ + var fromIndex = $(SECTION_ACTIVE_SEL).index(SECTION_SEL); + var toIndex = destiny.index(SECTION_SEL); + if( fromIndex == toIndex){ + return 'none'; + } + if(fromIndex > toIndex){ + return 'up'; + } + return 'down'; + } + + /** + * Retuns `right` or `left` depending on the scrolling movement to reach its destination + * from the current slide. + */ + function getXmovement(fromIndex, toIndex){ + if( fromIndex == toIndex){ + return 'none'; + } + if(fromIndex > toIndex){ + return 'left'; + } + return 'right'; + } + + /** + * Checks if the element needs scrollbar and if the user wants to apply it. + * If so it creates it. + * + * @param {Object} element jQuery object of the section or slide + */ + function createScrollBar(element){ + //User doesn't want scrollbar here? Sayonara baby! + if(element.hasClass('fp-noscroll')) return; + + //needed to make `scrollHeight` work under Opera 12 + element.css('overflow', 'hidden'); + + var scrollOverflowHandler = options.scrollOverflowHandler; + var wrap = scrollOverflowHandler.wrapContent(); + //in case element is a slide + var section = element.closest(SECTION_SEL); + var scrollable = scrollOverflowHandler.scrollable(element); + var contentHeight; + + //if there was scroll, the contentHeight will be the one in the scrollable section + if(scrollable.length){ + contentHeight = scrollOverflowHandler.scrollHeight(element); + }else{ + contentHeight = element.get(0).scrollHeight; + if(options.verticalCentered){ + contentHeight = element.find(TABLE_CELL_SEL).get(0).scrollHeight; + } + } + + var scrollHeight = windowsHeight - parseInt(section.css('padding-bottom')) - parseInt(section.css('padding-top')); + + //needs scroll? + if ( contentHeight > scrollHeight) { + //did we already have an scrollbar ? Updating it + if(scrollable.length){ + scrollOverflowHandler.update(element, scrollHeight); + } + //creating the scrolling + else{ + if(options.verticalCentered){ + element.find(TABLE_CELL_SEL).wrapInner(wrap); + }else{ + element.wrapInner(wrap); + } + scrollOverflowHandler.create(element, scrollHeight); + } + } + //removing the scrolling when it is not necessary anymore + else{ + scrollOverflowHandler.remove(element); + } + + //undo + element.css('overflow', ''); + } + + function addTableClass(element){ + //In case we are styling for the 2nd time as in with reponsiveSlides + if(!element.hasClass(TABLE)){ + element.addClass(TABLE).wrapInner('
    '); + } + } + + function getTableHeight(element){ + var sectionHeight = windowsHeight; + + if(options.paddingTop || options.paddingBottom){ + var section = element; + if(!section.hasClass(SECTION)){ + section = element.closest(SECTION_SEL); + } + + var paddings = parseInt(section.css('padding-top')) + parseInt(section.css('padding-bottom')); + sectionHeight = (windowsHeight - paddings); + } + + return sectionHeight; + } + + /** + * Adds a css3 transform property to the container class with or without animation depending on the animated param. + */ + function transformContainer(translate3d, animated){ + if(animated){ + addAnimation(container); + }else{ + removeAnimation(container); + } + + container.css(getTransforms(translate3d)); + + //syncronously removing the class after the animation has been applied. + setTimeout(function(){ + container.removeClass(NO_TRANSITION); + },10); + } + + /** + * Gets a section by its anchor / index + */ + function getSectionByAnchor(sectionAnchor){ + //section + var section = container.find(SECTION_SEL + '[data-anchor="'+sectionAnchor+'"]'); + if(!section.length){ + section = $(SECTION_SEL).eq( (sectionAnchor -1) ); + } + + return section; + } + + /** + * Gets a slide inside a given section by its anchor / index + */ + function getSlideByAnchor(slideAnchor, section){ + var slides = section.find(SLIDES_WRAPPER_SEL); + var slide = slides.find(SLIDE_SEL + '[data-anchor="'+slideAnchor+'"]'); + + if(!slide.length){ + slide = slides.find(SLIDE_SEL).eq(slideAnchor); + } + + return slide; + } + + /** + * Scrolls to the given section and slide anchors + */ + function scrollPageAndSlide(destiny, slide){ + var section = getSectionByAnchor(destiny); + + //do nothing if there's no section with the given anchor name + if(!section.length) return; + + //default slide + if (typeof slide === 'undefined') { + slide = 0; + } + + //we need to scroll to the section and then to the slide + if (destiny !== lastScrolledDestiny && !section.hasClass(ACTIVE)){ + scrollPage(section, function(){ + scrollSlider(section, slide); + }); + } + //if we were already in the section + else{ + scrollSlider(section, slide); + } + } + + /** + * Scrolls the slider to the given slide destination for the given section + */ + function scrollSlider(section, slideAnchor){ + if(typeof slideAnchor !== 'undefined'){ + var slides = section.find(SLIDES_WRAPPER_SEL); + var destiny = getSlideByAnchor(slideAnchor, section); + + if(destiny.length){ + landscapeScroll(slides, destiny); + } + } + } + + /** + * Creates a landscape navigation bar with dots for horizontal sliders. + */ + function addSlidesNavigation(section, numSlides){ + section.append('
      '); + var nav = section.find(SLIDES_NAV_SEL); + + //top or bottom + nav.addClass(options.slidesNavPosition); + + for(var i=0; i< numSlides; i++){ + nav.find('ul').append('
    • '); + } + + //centering it + nav.css('margin-left', '-' + (nav.width()/2) + 'px'); + + nav.find('li').first().find('a').addClass(ACTIVE); + } + + + /** + * Sets the state of the website depending on the active section/slide. + * It changes the URL hash when needed and updates the body class. + */ + function setState(slideIndex, slideAnchor, anchorLink, sectionIndex){ + var sectionHash = ''; + + if(options.anchors.length && !options.lockAnchors){ + + //isn't it the first slide? + if(slideIndex){ + if(typeof anchorLink !== 'undefined'){ + sectionHash = anchorLink; + } + + //slide without anchor link? We take the index instead. + if(typeof slideAnchor === 'undefined'){ + slideAnchor = slideIndex; + } + + lastScrolledSlide = slideAnchor; + setUrlHash(sectionHash + '/' + slideAnchor); + + //first slide won't have slide anchor, just the section one + }else if(typeof slideIndex !== 'undefined'){ + lastScrolledSlide = slideAnchor; + setUrlHash(anchorLink); + } + + //section without slides + else{ + setUrlHash(anchorLink); + } + } + + setBodyClass(); + } + + /** + * Sets the URL hash. + */ + function setUrlHash(url){ + if(options.recordHistory){ + location.hash = url; + }else{ + //Mobile Chrome doesn't work the normal way, so... lets use HTML5 for phones :) + if(isTouchDevice || isTouch){ + window.history.replaceState(undefined, undefined, '#' + url); + }else{ + var baseUrl = window.location.href.split('#')[0]; + window.location.replace( baseUrl + '#' + url ); + } + } + } + + /** + * Gets the anchor for the given slide / section. Its index will be used if there's none. + */ + function getAnchor(element){ + var anchor = element.data('anchor'); + var index = element.index(); + + //Slide without anchor link? We take the index instead. + if(typeof anchor === 'undefined'){ + anchor = index; + } + + return anchor; + } + + /** + * Sets a class for the body of the page depending on the active section / slide + */ + function setBodyClass(){ + var section = $(SECTION_ACTIVE_SEL); + var slide = section.find(SLIDE_ACTIVE_SEL); + + var sectionAnchor = getAnchor(section); + var slideAnchor = getAnchor(slide); + + var text = String(sectionAnchor); + + if(slide.length){ + text = text + '-' + slideAnchor; + } + + //changing slash for dash to make it a valid CSS style + text = text.replace('/', '-').replace('#',''); + + //removing previous anchor classes + var classRe = new RegExp('\\b\\s?' + VIEWING_PREFIX + '-[^\\s]+\\b', "g"); + $body[0].className = $body[0].className.replace(classRe, ''); + + //adding the current anchor + $body.addClass(VIEWING_PREFIX + '-' + text); + } + + /** + * 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'); + } + + /** + * Removes the auto scrolling action fired by the mouse wheel and trackpad. + * After this function is called, the mousewheel and trackpad movements won't scroll through sections. + */ + function removeMouseWheelHandler(){ + if (document.addEventListener) { + document.removeEventListener('mousewheel', MouseWheelHandler, false); //IE9, Chrome, Safari, Oper + document.removeEventListener('wheel', MouseWheelHandler, false); //Firefox + document.removeEventListener('MozMousePixelScroll', MouseWheelHandler, false); //old Firefox + } else { + document.detachEvent('onmousewheel', MouseWheelHandler); //IE 6/7/8 + } + } + + /** + * Adds the auto scrolling action for the mouse wheel and trackpad. + * After this function is called, the mousewheel and trackpad movements will scroll through sections + * https://developer.mozilla.org/en-US/docs/Web/Events/wheel + */ + function addMouseWheelHandler(){ + var prefix = ''; + var _addEventListener; + + if (window.addEventListener){ + _addEventListener = "addEventListener"; + }else{ + _addEventListener = "attachEvent"; + prefix = 'on'; + } + + // detect available wheel event + var support = 'onwheel' in document.createElement('div') ? 'wheel' : // Modern browsers support "wheel" + document.onmousewheel !== undefined ? 'mousewheel' : // Webkit and IE support at least "mousewheel" + 'DOMMouseScroll'; // let's assume that remaining browsers are older Firefox + + + if(support == 'DOMMouseScroll'){ + document[ _addEventListener ](prefix + 'MozMousePixelScroll', MouseWheelHandler, false); + } + + //handle MozMousePixelScroll in older Firefox + else{ + document[ _addEventListener ](prefix + support, MouseWheelHandler, false); + } + } + + /** + * Binding the mousemove when the mouse's middle button is pressed + */ + function addMiddleWheelHandler(){ + container + .on('mousedown', mouseDownHandler) + .on('mouseup', mouseUpHandler); + } + + /** + * Unbinding the mousemove when the mouse's middle button is released + */ + function removeMiddleWheelHandler(){ + container + .off('mousedown', mouseDownHandler) + .off('mouseup', mouseUpHandler); + } + + /** + * Adds the possibility to auto scroll through sections on touch devices. + */ + function addTouchHandler(){ + if(isTouchDevice || isTouch){ + //Microsoft pointers + var MSPointer = getMSPointer(); + + if(options.autoScrolling){ + $body.off('touchmove ' + MSPointer.move).on('touchmove ' + MSPointer.move, preventBouncing); + } + + $(WRAPPER_SEL) + .off('touchstart ' + MSPointer.down).on('touchstart ' + MSPointer.down, touchStartHandler) + .off('touchmove ' + MSPointer.move).on('touchmove ' + MSPointer.move, touchMoveHandler); + } + } + + /** + * Removes the auto scrolling for touch devices. + */ + function removeTouchHandler(){ + if(isTouchDevice || isTouch){ + //Microsoft pointers + var MSPointer = getMSPointer(); + + $(WRAPPER_SEL) + .off('touchstart ' + MSPointer.down) + .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'}; + } + + //IE < 11 + else{ + pointer = { down: 'MSPointerDown', move: 'MSPointerMove'}; + } + + 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 = []; + + 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); + + //in touch devices with scrollBar:true, e.pageY is detected, but we have to deal with touch events. #1008 + if(isTouch && isReallyTouch(e) && options.scrollBar){ + events.y = e.touches[0].pageY; + events.x = e.touches[0].pageX; + } + + return events; + } + + /** + * Slides silently (with no animation) the active slider to the given slide. + * @param noCallback {bool} true or defined -> no callbacks + */ + function silentLandscapeScroll(activeSlide, noCallbacks){ + setScrollingSpeed (0, 'internal'); + + if(typeof noCallbacks !== 'undefined'){ + //preventing firing callbacks afterSlideLoad etc. + isResizing = true; + } + + landscapeScroll(activeSlide.closest(SLIDES_WRAPPER_SEL), activeSlide); + + if(typeof noCallbacks !== 'undefined'){ + isResizing = false; + } + + setScrollingSpeed(originals.scrollingSpeed, 'internal'); + } + + /** + * Scrolls silently (with no animation) the page to the given Y position. + */ + function silentScroll(top){ + // The first section can have a negative value in iOS 10. Not quite sure why: -0.0142822265625 + // that's why we round it to 0. + var roundedTop = Math.round(top); + + if (options.css3 && options.autoScrolling && !options.scrollBar){ + var translate3d = 'translate3d(0px, -' + roundedTop + 'px, 0px)'; + transformContainer(translate3d, false); + } + else if(options.autoScrolling && !options.scrollBar){ + container.css('top', -roundedTop); + } + else{ + $htmlBody.scrollTop(roundedTop); + } + } + + /** + * Returns the cross-browser transform string. + */ + function getTransforms(translate3d){ + return { + '-webkit-transform': translate3d, + '-moz-transform': translate3d, + '-ms-transform':translate3d, + 'transform': translate3d + }; + } + + /** + * Allowing or disallowing the mouse/swipe scroll in a given direction. (not for keyboard) + * @type m (mouse) or k (keyboard) + */ + function setIsScrollAllowed(value, direction, type){ + switch (direction){ + case 'up': isScrollAllowed[type].up = value; break; + case 'down': isScrollAllowed[type].down = value; break; + case 'left': isScrollAllowed[type].left = value; break; + case 'right': isScrollAllowed[type].right = value; break; + case 'all': + if(type == 'm'){ + setAllowScrolling(value); + }else{ + setKeyboardScrolling(value); + } + } + } + + /* + * Destroys fullpage.js plugin events and optinally its html markup and styles + */ + function destroy(all){ + setAutoScrolling(false, 'internal'); + setAllowScrolling(false); + setKeyboardScrolling(false); + container.addClass(DESTROYED); + + clearTimeout(afterSlideLoadsId); + clearTimeout(afterSectionLoadsId); + clearTimeout(resizeId); + clearTimeout(scrollId); + clearTimeout(scrollId2); + + $window + .off('scroll', scrollHandler) + .off('hashchange', hashChangeHandler) + .off('resize', resizeHandler); + + $document + .off('click touchstart', SECTION_NAV_SEL + ' a') + .off('mouseenter', SECTION_NAV_SEL + ' li') + .off('mouseleave', SECTION_NAV_SEL + ' li') + .off('click touchstart', SLIDES_NAV_LINK_SEL) + .off('mouseover', options.normalScrollElements) + .off('mouseout', options.normalScrollElements); + + $(SECTION_SEL) + .off('click touchstart', SLIDES_ARROW_SEL); + + clearTimeout(afterSlideLoadsId); + clearTimeout(afterSectionLoadsId); + + //lets make a mess! + if(all){ + destroyStructure(); + } + } + + /* + * Removes inline styles added by fullpage.js + */ + function destroyStructure(){ + //reseting the `top` or `translate` properties to 0 + silentScroll(0); + + //loading all the lazy load content + container.find('img[data-src], source[data-src], audio[data-src], iframe[data-src]').each(function(){ + $(this).attr('src', $(this).data('src')); + $(this).removeAttr('data-src'); + }); + + $(SECTION_NAV_SEL + ', ' + SLIDES_NAV_SEL + ', ' + SLIDES_ARROW_SEL).remove(); + + //removing inline styles + $(SECTION_SEL).css( { + 'height': '', + 'background-color' : '', + 'padding': '' + }); + + $(SLIDE_SEL).css( { + 'width': '' + }); + + container.css({ + 'height': '', + 'position': '', + '-ms-touch-action': '', + 'touch-action': '' + }); + + $htmlBody.css({ + 'overflow': '', + 'height': '' + }); + + // remove .fp-enabled class + $('html').removeClass(ENABLED); + + // remove .fp-responsive class + $body.removeClass(RESPONSIVE); + + // remove all of the .fp-viewing- classes + $.each($body.get(0).className.split(/\s+/), function (index, className) { + if (className.indexOf(VIEWING_PREFIX) === 0) { + $body.removeClass(className); + } + }); + + //removing added classes + $(SECTION_SEL + ', ' + SLIDE_SEL).each(function(){ + options.scrollOverflowHandler.remove($(this)); + $(this).removeClass(TABLE + ' ' + ACTIVE); + }); + + removeAnimation(container); + + //Unwrapping content + container.find(TABLE_CELL_SEL + ', ' + SLIDES_CONTAINER_SEL + ', ' + SLIDES_WRAPPER_SEL).each(function(){ + //unwrap not being use in case there's no child element inside and its just text + $(this).replaceWith(this.childNodes); + }); + + //scrolling the page to the top with no animation + $htmlBody.scrollTop(0); + + //removing selectors + var usedSelectors = [SECTION, SLIDE, SLIDES_CONTAINER]; + $.each(usedSelectors, function(index, value){ + $('.' + value).removeClass(value); + }); + } + + /* + * Sets the state for a variable with multiple states (original, and temporal) + * Some variables such as `autoScrolling` or `recordHistory` might change automatically its state when using `responsive` or `autoScrolling:false`. + * This function is used to keep track of both states, the original and the temporal one. + * If type is not 'internal', then we assume the user is globally changing the variable. + */ + function setVariableState(variable, value, type){ + options[variable] = value; + if(type !== 'internal'){ + originals[variable] = value; + } + } + + /** + * Displays warnings + */ + function displayWarnings(){ + var extensions = ['fadingEffect', 'continuousHorizontal', 'scrollHorizontally', 'interlockedSlides', 'resetSliders', 'responsiveSlides', 'offsetSections', 'dragAndMove', 'scrollOverflowReset']; + if($('html').hasClass(ENABLED)){ + showError('error', 'Fullpage.js can only be initialized once and you are doing it multiple times!'); + return; + } + + // Disable mutually exclusive settings + if (options.continuousVertical && + (options.loopTop || options.loopBottom)) { + options.continuousVertical = false; + showError('warn', 'Option `loopTop/loopBottom` is mutually exclusive with `continuousVertical`; `continuousVertical` disabled'); + } + + if(options.scrollBar && options.scrollOverflow){ + showError('warn', 'Option `scrollBar` is mutually exclusive with `scrollOverflow`. Sections with scrollOverflow might not work well in Firefox'); + } + + if(options.continuousVertical && (options.scrollBar || !options.autoScrolling)){ + options.continuousVertical = false; + showError('warn', 'Scroll bars (`scrollBar:true` or `autoScrolling:false`) are mutually exclusive with `continuousVertical`; `continuousVertical` disabled'); + } + + //using extensions? Wrong file! + $.each(extensions, function(index, extension){ + //is the option set to true? + if(options[extension]){ + showError('warn', 'fullpage.js extensions require jquery.fullpage.extensions.min.js file instead of the usual jquery.fullpage.js. Requested: '+ extension); + } + }); + + //anchors can not have the same value as any element ID or NAME + $.each(options.anchors, function(index, name){ + + //case insensitive selectors (http://stackoverflow.com/a/19465187/1081396) + var nameAttr = $document.find('[name]').filter(function() { + return $(this).attr('name') && $(this).attr('name').toLowerCase() == name.toLowerCase(); + }); + + var idAttr = $document.find('[id]').filter(function() { + return $(this).attr('id') && $(this).attr('id').toLowerCase() == name.toLowerCase(); + }); + + if(idAttr.length || nameAttr.length ){ + showError('error', 'data-anchor tags can not have the same value as any `id` element on the site (or `name` element for IE).'); + idAttr.length && showError('error', '"' + name + '" is is being used by another element `id` property'); + nameAttr.length && showError('error', '"' + name + '" is is being used by another element `name` property'); + } + }); + } + + /** + * Shows a message in the console of the given type. + */ + function showError(type, text){ + console && console[type] && console[type]('fullPage: ' + text); + } + + }; //end of $.fn.fullpage + + if(typeof IScroll !== 'undefined'){ + /* + * Turns iScroll `mousewheel` option off dynamically + * https://github.com/cubiq/iscroll/issues/1036 + */ + IScroll.prototype.wheelOn = function () { + this.wrapper.addEventListener('wheel', this); + this.wrapper.addEventListener('mousewheel', this); + this.wrapper.addEventListener('DOMMouseScroll', this); + }; + + /* + * Turns iScroll `mousewheel` option on dynamically + * https://github.com/cubiq/iscroll/issues/1036 + */ + IScroll.prototype.wheelOff = function () { + this.wrapper.removeEventListener('wheel', this); + this.wrapper.removeEventListener('mousewheel', this); + this.wrapper.removeEventListener('DOMMouseScroll', this); + }; + } + + /** + * An object to handle overflow scrolling. + * This uses jquery.slimScroll to accomplish overflow scrolling. + * It is possible to pass in an alternate scrollOverflowHandler + * to the fullpage.js option that implements the same functions + * as this handler. + * + * @type {Object} + */ + var iscrollHandler = { + refreshId: null, + iScrollInstances: [], + + // Enables or disables the mouse wheel for the active section or all slides in it + toggleWheel: function(value){ + var scrollable = $(SECTION_ACTIVE_SEL).find(SCROLLABLE_SEL); + scrollable.each(function(){ + var iScrollInstance = $(this).data('iscrollInstance'); + if(typeof iScrollInstance !== 'undefined' && iScrollInstance){ + if(value){ + iScrollInstance.wheelOn(); + } + else{ + iScrollInstance.wheelOff(); + } + } + }); + }, + + /** + * Turns off iScroll for the destination section. + * When scrolling very fast on some trackpads (and Apple laptops) the inertial scrolling would + * scroll the destination section/slide before the sections animations ends. + */ + onLeave: function(){ + iscrollHandler.toggleWheel(false); + }, + + // Turns off iScroll for the leaving section + beforeLeave: function(){ + iscrollHandler.onLeave() + }, + + // Turns on iScroll on section load + afterLoad: function(){ + iscrollHandler.toggleWheel(true); + }, + + /** + * Called when overflow scrolling is needed for a section. + * + * @param {Object} element jQuery object containing current section + * @param {Number} scrollHeight Current window height in pixels + */ + create: function(element, scrollHeight) { + var scrollable = element.find(SCROLLABLE_SEL); + + scrollable.height(scrollHeight); + scrollable.each(function() { + var $this = $(this); + var iScrollInstance = $this.data('iscrollInstance'); + if (iScrollInstance) { + $.each(iscrollHandler.iScrollInstances, function(){ + $(this).destroy(); + }); + } + + iScrollInstance = new IScroll($this.get(0), iscrollOptions); + iscrollHandler.iScrollInstances.push(iScrollInstance); + + //off by default until the section gets active + iScrollInstance.wheelOff(); + + $this.data('iscrollInstance', iScrollInstance); + }); + }, + + /** + * Return a boolean depending on whether the scrollable element is a + * the end or at the start of the scrolling depending on the given type. + * + * @param {String} type Either 'top' or 'bottom' + * @param {Object} scrollable jQuery object for the scrollable element + * @return {Boolean} + */ + isScrolled: function(type, scrollable) { + var scroller = scrollable.data('iscrollInstance'); + + //no scroller? + if (!scroller) { + return true; + } + + if (type === 'top') { + return scroller.y >= 0 && !scrollable.scrollTop(); + } else if (type === 'bottom') { + return (0 - scroller.y) + scrollable.scrollTop() + 1 + scrollable.innerHeight() >= scrollable[0].scrollHeight; + } + }, + + /** + * Returns the scrollable element for the given section. + * If there are landscape slides, will only return a scrollable element + * if it is in the active slide. + * + * @param {Object} activeSection jQuery object containing current section + * @return {Boolean} + */ + scrollable: function(activeSection){ + // if there are landscape slides, we check if the scrolling bar is in the current one or not + if (activeSection.find(SLIDES_WRAPPER_SEL).length) { + return activeSection.find(SLIDE_ACTIVE_SEL).find(SCROLLABLE_SEL); + } + return activeSection.find(SCROLLABLE_SEL); + }, + + /** + * Returns the scroll height of the wrapped content. + * If this is larger than the window height minus section padding, + * overflow scrolling is needed. + * + * @param {Object} element jQuery object containing current section + * @return {Number} + */ + scrollHeight: function(element) { + return element.find(SCROLLABLE_SEL).children().first().get(0).scrollHeight; + }, + + /** + * Called when overflow scrolling is no longer needed for a section. + * + * @param {Object} element jQuery object containing current section + */ + remove: function(element) { + var scrollable = element.find(SCROLLABLE_SEL); + if (scrollable.length) { + var iScrollInstance = scrollable.data('iscrollInstance'); + iScrollInstance.destroy(); + + scrollable.data('iscrollInstance', null); + } + element.find(SCROLLABLE_SEL).children().first().children().first().unwrap().unwrap(); + }, + + /** + * Called when overflow scrolling has already been setup but the + * window height has potentially changed. + * + * @param {Object} element jQuery object containing current section + * @param {Number} scrollHeight Current window height in pixels + */ + update: function(element, scrollHeight) { + //using a timeout in order to execute the refresh function only once when `update` is called multiple times in a + //short period of time. + //it also comes on handy because iScroll requires the use of timeout when using `refresh`. + clearTimeout(iscrollHandler.refreshId); + iscrollHandler.refreshId = setTimeout(function(){ + $.each(iscrollHandler.iScrollInstances, function(){ + $(this).get(0).refresh(); + }); + }, 150); + + //updating the wrappers height + element.find(SCROLLABLE_SEL).css('height', scrollHeight + 'px').parent().css('height', scrollHeight + 'px'); + }, + + /** + * Called to get any additional elements needed to wrap the section + * content in order to facilitate overflow scrolling. + * + * @return {String|Object} Can be a string containing HTML, + * a DOM element, or jQuery object. + */ + wrapContent: function() { + return '
      '; + } + }; +}); -- cgit v1.2.3-70-g09d2