/*! * multiscroll.js 0.1.8 beta * https://github.com/alvarotrigo/multiscroll.js * @license mit licensed * * copyright (c) 2016 alvarotrigo.com - a project by alvaro trigo */ (function($, window, document, math, undefined) { $.fn.multiscroll = function(options) { var ms = $.fn.multiscroll; // create some defaults, extending them with any options that were provided options = $.extend({ 'verticalcentered' : true, 'scrollingspeed': 700, 'easing': 'easeinquart', 'menu': false, 'sectionscolor': [], 'anchors':[], 'navigation': false, 'navigationposition': 'right', 'navigationcolor': '#000', 'navigationtooltips': [], 'loopbottom': false, 'looptop': false, 'css3': false, 'paddingtop': 0, 'paddingbottom': 0, 'fixedelements': null, 'normalscrollelements': null, 'keyboardscrolling': true, 'touchsensitivity': 5, // custom selectors 'sectionselector': '.ms-section', 'leftselector': '.ms-left', 'rightselector': '.ms-right', //events 'afterload': null, 'onleave': null, 'afterrender': null, 'afterresize': null }, options); //defines the delay to take place before being able to scroll to the next section //be careful! not recommened to change it under 400 for a good behavior in laptops and //apple devices (laptops, mouses...) var scrolldelay = 600; var istouch = (('ontouchstart' in window) || (navigator.msmaxtouchpoints > 0) || (navigator.maxtouchpoints)); // adding class namef for right and left blocks if (options.rightselector !== '.ms-right') { $(options.rightselector).addclass('ms-right'); } if (options.leftselector !== '.ms-left') { $(options.leftselector).addclass('ms-left'); } var numbersections = $('.ms-left').find('.ms-section').length; var ismoving = false; var nav; var windowheight = $(window).height(); var windowwidth = $(window).width(); addmousewheelhandler(); addtouchhandler(); //if css3 is not supported, it will use jquery animations if(options.css3){ options.css3 = support3d(); } $('html, body').css({ 'overflow' : 'hidden', 'height' : '100%' }); //adding class names to each sections if (options.sectionselector !== '.ms-section') { $(options.sectionselector).each(function(){ $(this).addclass('ms-section'); }); } //creating the navigation dots if (options.navigation) { $('body').append('
'); nav = $('#multiscroll-nav'); nav.css('color', options.navigationcolor); nav.addclass(options.navigationposition); } $('.ms-left .ms-section, .ms-right .ms-section').each(function(){ var sectionindex = $(this).index(); if(options.paddingtop || options.paddingbottom){ $(this).css('padding', options.paddingtop + ' 0 ' + options.paddingbottom + ' 0'); } if (typeof options.sectionscolor[sectionindex] !== 'undefined') { $(this).css('background-color', options.sectionscolor[sectionindex]); } if (typeof options.anchors[sectionindex] !== 'undefined') { $(this).attr('data-anchor', options.anchors[sectionindex]); } if(options.verticalcentered){ addtableclass($(this)); } //only for the left panel if($(this).closest('.ms-left').length && options.navigation) { var link = ''; if(options.anchors.length){ link = options.anchors[sectionindex]; } var tooltip = options.navigationtooltips[sectionindex]; if(typeof tooltip === 'undefined'){ tooltip = ''; } if (options.navigation) { nav.find('ul').append('
  • '); } } }); //inverting the right panel $('.ms-right').html( $('.ms-right').find('.ms-section').get().reverse()); $('.ms-left .ms-section, .ms-right .ms-section').each(function(){ var sectionindex = $(this).index(); $(this).css({ 'height': '100%' }); if(!sectionindex && options.navigation ){ //activating the navigation bullet nav.find('li').eq(sectionindex).find('a').addclass('active'); } }).promise().done(function(){ //if no active section is defined, the 1st one will be the default one if(!$('.ms-left .ms-section.active').length){ $('.ms-right').find('.ms-section').last().addclass('active'); $('.ms-left').find('.ms-section').first().addclass('active'); } //vertical centered of the navigation + first bullet active if(options.navigation){ nav.css('margin-top', '-' + (nav.height()/2) + 'px'); } $.isfunction( options.afterrender ) && options.afterrender.call( this); //scrolling to the defined active section and adjusting right and left panels silentscroll(); //setting the class for the body element setbodyclass(); $(window).on('load', function() { scrolltoanchor(); }); }); //detecting any change on the url to scroll to the given anchor link //(a way to detect back history button as we play with the hashes on the url) $(window).on('hashchange', hashchangehandler); function hashchangehandler(){ var value = window.location.hash.replace('#', ''); var sectionanchor = value; if(sectionanchor.length){ var section = $('.ms-left').find('[data-anchor="'+sectionanchor+'"]'); var isfirstscrollmove = (typeof lastscrolleddestiny === 'undefined' ); if (isfirstscrollmove || sectionanchor !== lastscrolleddestiny){ scrollpage(section); } } }; /** * sliding with arrow keys, both, vertical and horizontal */ $(document).keydown(keydownhandler); var keydownid; function keydownhandler(e) { cleartimeout(keydownid); var activeelement = $(document.activeelement); if(!activeelement.is('textarea') && !activeelement.is('input') && !activeelement.is('select') && options.keyboardscrolling){ 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(); } keydownid = settimeout(function(){ onkeydown(e); },150); } } /** * sliding with arrow keys, both, vertical and horizontal */ function onkeydown(e){ var shiftpressed = e.shiftkey; switch (e.which) { //up case 38: case 33: ms.movesectionup(); break; //down case 32: //spacebar if(shiftpressed){ ms.movesectionup(); break; } case 40: case 34: ms.movesectiondown(); break; //home case 36: ms.moveto(1); break; //end case 35: ms.moveto( $('.ms-left .ms-section').length); break; default: return; // exit this handler for other keys } } /** * disabling any action when pressing of the mouse wheel (chrome, ie, opera, safari) */ $(document).mousedown(function(e) { if(e.button == 1){ e.preventdefault(); return false; } }); function navclickhandler(e) { e.preventdefault(); var index = $(this).parent().index(); scrollpage($('.ms-left .ms-section').eq(index)); } //navigation action $(document).on('click', '#multiscroll-nav a', navclickhandler); function navmouseenterhandler() { var tooltip = $(this).data('tooltip'); $('
    ' + tooltip + '
    ').hide().appendto($(this)).fadein(200); } function navmouseleavehandler() { $(this).find('.multiscroll-tooltip').fadeout(200, function() { $(this).remove(); }); } //navigation tooltips $(document).on({ mouseenter: navmouseenterhandler, mouseleave: navmouseleavehandler }, '#multiscroll-nav li'); if(options.normalscrollelements){ $(document).on('mouseenter', options.normalscrollelements, function () { ms.setmousewheelscrolling(false); }); $(document).on('mouseleave', options.normalscrollelements, function(){ ms.setmousewheelscrolling(true); }); } //when resizing the site, we adjust the heights of the sections $(window).on('resize', doneresizing); /** * when resizing is finished, we adjust the slides sizes and positions */ function doneresizing() { windowheight = $(window).height(); $('.ms-tablecell').each(function() { $(this).css({ height: gettableheight($(this).parent()) }); }); silentscroll(); $.isfunction( options.afterresize ) && options.afterresize.call( this); } function silentscroll(){ //moving the right section to the bottom if(options.css3){ transformcontainer($('.ms-left'), 'translate3d(0px, -' + $('.ms-left').find('.ms-section.active').position().top + 'px, 0px)', false); transformcontainer($('.ms-right'), 'translate3d(0px, -' + $('.ms-right').find('.ms-section.active').position().top + 'px, 0px)', false); }else{ $('.ms-left').css('top', -$('.ms-left').find('.ms-section.active').position().top ); $('.ms-right').css('top', -$('.ms-right').find('.ms-section.active').position().top ); } } ms.movesectionup = function(){ var prev = $('.ms-left .ms-section.active').prev('.ms-section'); if(!prev.length && options.looptop){ prev = $('.ms-left .ms-section').last(); } if (prev.length) { scrollpage(prev); } }; ms.movesectiondown = function (){ var next = $('.ms-left .ms-section.active').next('.ms-section'); if(!next.length && options.loopbottom ){ next = $('.ms-left .ms-section').first(); } if(next.length){ scrollpage(next); } }; ms.moveto = function (section){ var destiny = ''; if(isnan(section)){ destiny = $('.ms-left [data-anchor="'+section+'"]'); }else{ destiny = $('.ms-left .ms-section').eq( (section -1) ); } scrollpage(destiny); }; function scrollpage(leftdestination){ var leftdestinationindex = leftdestination.index(); var rightdestination = $('.ms-right').find('.ms-section').eq( numbersections -1 - leftdestinationindex); var rightdestinationindex = numbersections - 1 - leftdestinationindex; var anchorlink = leftdestination.data('anchor'); var activesection = $('.ms-left .ms-section.active'); var leavingsection = activesection.index() + 1; var ymovement = getymovement(leftdestination); //preventing from activating the mousewheelhandler event //more than once if the page is scrolling ismoving = true; var toppos = { 'left' : leftdestination.position().top, 'right': rightdestination.position().top }; rightdestination.addclass('active').siblings().removeclass('active'); leftdestination.addclass('active').siblings().removeclass('active'); seturlhash(anchorlink); // use css3 translate functionality or... if (options.css3){ //callback (onleave) $.isfunction(options.onleave) && options.onleave.call(this, leavingsection, (leftdestinationindex + 1), ymovement); var translate3dleft = 'translate3d(0px, -' + toppos['left'] + 'px, 0px)'; var translate3dright = 'translate3d(0px, -' + toppos['right'] + 'px, 0px)'; transformcontainer($('.ms-left'), translate3dleft, true); transformcontainer($('.ms-right'), translate3dright, true); settimeout(function () { //callback (afterload) $.isfunction(options.afterload) && options.afterload.call(this, anchorlink, (leftdestinationindex + 1)); settimeout(function () { ismoving = false; }, scrolldelay); }, options.scrollingspeed); }else{ //callback (onleave) $.isfunction(options.onleave) && options.onleave.call(this, leavingsection, (leftdestinationindex + 1), ymovement); $('.ms-left').animate({ 'top': -toppos['left'] }, options.scrollingspeed, options.easing, function(){ $.isfunction(options.afterload) && options.afterload.call(this, anchorlink, (leftdestinationindex + 1)); settimeout(function () { ismoving = false; }, scrolldelay); }); $('.ms-right').animate({ 'top': -toppos['right'] }, options.scrollingspeed, options.easing); } //flag to avoid callingn `scrollpage()` twice in case of using anchor links lastscrolleddestiny = anchorlink; activatemenuelement(anchorlink); activatenavdots(anchorlink, leftdestinationindex); } /** * removes the auto scrolling action fired by the mouse wheel and tackpad. * after this function is called, the mousewheel and trackpad movements won't scroll through sections. */ function removemousewheelhandler(){ if (document.addeventlistener) { document.removeeventlistener('mousewheel', mousewheelhandler, false); //ie9, chrome, safari, oper document.removeeventlistener('wheel', mousewheelhandler, false); //firefox } else { document.detachevent("onmousewheel", mousewheelhandler); //ie 6/7/8 } } /** * adds the auto scrolling action for the mouse wheel and tackpad. * after this function is called, the mousewheel and trackpad movements will scroll through sections */ function addmousewheelhandler(){ if (document.addeventlistener) { document.addeventlistener("mousewheel", mousewheelhandler, false); //ie9, chrome, safari, oper document.addeventlistener("wheel", mousewheelhandler, false); //firefox } else { document.attachevent("onmousewheel", mousewheelhandler); //ie 6/7/8 } } /** * detecting mousewheel scrolling * * http://blogs.sitepointstatic.com/examples/tech/mouse-wheel/index.html * http://www.sitepoint.com/html5-javascript-mouse-wheel/ */ function mousewheelhandler(e) { // cross-browser wheel delta e = window.event || e; var delta = math.max(-1, math.min(1, (e.wheeldelta || -e.deltay || -e.detail))); if (!ismoving) { //if theres any # //scrolling down? if (delta < 0) { ms.movesectiondown(); } //scrolling up? else { ms.movesectionup(); } } return false; } /** * adds a css3 transform property to the container class with or without animation depending on the animated param. */ function transformcontainer(container, translate3d, animated){ container.toggleclass('ms-easing', animated); container.css(gettransforms(translate3d)); } /** * returns the transform styles for all browsers */ function gettransforms(translate3d){ return { '-webkit-transform': translate3d, '-moz-transform': translate3d, '-ms-transform':translate3d, 'transform': translate3d }; } /** * activating the website navigation dots according to the given slide name. */ function activatenavdots(name, sectionindex){ if(options.navigation){ $('#multiscroll-nav').find('.active').removeclass('active'); if(name){ $('#multiscroll-nav').find('a[href="#' + name + '"]').addclass('active'); }else{ $('#multiscroll-nav').find('li').eq(sectionindex).find('a').addclass('active'); } } } /** * activating the website main menu elements according to the given slide name. */ function activatemenuelement(name){ if(options.menu){ $(options.menu).find('.active').removeclass('active'); $(options.menu).find('[data-menuanchor="'+name+'"]').addclass('active'); } } /** * retuns `up` or `down` depending on the scrolling movement to reach its destination * from the current section. */ function getymovement(destiny){ var fromindex = $('.ms-left .ms-section.active').index(); var toindex = destiny.index(); if(fromindex > toindex){ return 'up'; } return 'down'; } /** * sets the url hash for a section with slides */ function seturlhash(anchorlink){ if(options.anchors.length){ location.hash = anchorlink; } setbodyclass(); } /** * sets a class for the body of the page depending on the active section / slide */ function setbodyclass(){ var section = $('.ms-left .ms-section.active'); var sectionanchor = section.data('anchor'); var sectionindex = section.index(); var text = string(sectionindex); if(options.anchors.length){ text = sectionanchor; } //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?' + 'ms-viewing' + '-[^\\s]+\\b', "g"); $('body')[0].classname = $('body')[0].classname.replace(classre, ''); //adding the current anchor $('body').addclass('ms-viewing-' + 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"); } /** * wraps an element in order to center it vertically by using a class style. */ function addtableclass(element){ element.addclass('ms-table').wrapinner('
    '); } /** * gets the height of the section after removing the paddings. */ function gettableheight(section){ var sectionheight = windowheight; if(options.paddingtop || options.paddingbottom){ var paddings = parseint(section.css('padding-top')) + parseint(section.css('padding-bottom')); sectionheight = (windowheight - paddings); } return sectionheight; } /** * gets the height of the section after removing the paddings. */ function gettablewidth(section){ var sectionwidth = windowwidth; if(options.paddingtop || options.paddingbottom){ var paddings = parseint(section.css('padding-top')) + parseint(section.css('padding-bottom')); sectionheight = (windowheight - paddings); } return sectionheight; } /** * scrolls the page to the existent anchor in the url */ function scrolltoanchor(){ //getting the anchor link in the url and deleting the `#` var sectionanchor = window.location.hash.replace('#', ''); var section = $('.ms-left .ms-section[data-anchor="'+sectionanchor+'"]'); if(sectionanchor.length){ //if theres any # scrollpage(section); } } /** * adds or remove the possiblity of scrolling through sections by using the keyboard arrow keys */ ms.setkeyboardscrolling = function (value){ options.keyboardscrolling = value; }; /** * adds or remove the possiblity of scrolling through sections by using the mouse wheel or the trackpad. */ ms.setmousewheelscrolling = function (value){ if(value){ addmousewheelhandler(); }else{ removemousewheelhandler(); } }; /** * defines the scrolling speed */ ms.setscrollingspeed = function(value){ options.scrollingspeed = value; }; 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; if(isreallytouch(e)){ //preventing the easing on ios devices event.preventdefault(); var activesection = $('.ms-left .ms-section.active'); if (!ismoving) { //if theres any # var touchevents = geteventspage(e); touchendy = touchevents['y']; touchendx = touchevents['x']; //is the movement greater than the minimum resistance to scroll? if (math.abs(touchstarty - touchendy) > ($(window).height() / 100 * options.touchsensitivity)) { if (touchstarty > touchendy) { ms.movesectiondown(); } else if (touchendy > touchstarty) { ms.movesectionup(); } } } } } /** * 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 to get he coordinates of the starting touch */ function touchstarthandler(event){ var e = event.originalevent; if(isreallytouch(e)){ var touchevents = geteventspage(e); touchstarty = touchevents['y']; touchstartx = touchevents['x']; } } /** * adds the possibility to auto scroll through sections on touch devices. */ function addtouchhandler(){ if(istouch){ //microsoft pointers mspointer = getmspointer(); $(document).off('touchstart ' + mspointer.down).on('touchstart ' + mspointer.down, touchstarthandler); $(document).off('touchmove ' + mspointer.move).on('touchmove ' + mspointer.move, touchmovehandler); } } /** * removes the auto scrolling for touch devices. */ function removetouchhandler(){ if(istouch){ //microsoft pointers mspointer = getmspointer(); $(document).off('touchstart ' + mspointer.down); $(document).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 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)){ events.y = e.touches[0].pagey; events.x = e.touches[0].pagex; } return events; } /** * destroy multiscroll.js plugin's events */ ms.destroy = function() { ms.setkeyboardscrolling(false); ms.setmousewheelscrolling(false); removetouchhandler(); $(window) .off('hashchange', hashchangehandler) .off('resize', doneresizing); $(document) .off('mouseenter', '#multiscroll-nav li') .off('mouseleave', '#multiscroll-nav li') .off('click', '#multiscroll-nav a'); }; /** * build multiscroll.js plugin's events after destroy */ ms.build = function() { ms.setkeyboardscrolling(true); ms.setmousewheelscrolling(true); addtouchhandler(); $(window) .on('hashchange', hashchangehandler) .on('resize', doneresizing); $(document) .on('mouseenter', '#multiscroll-nav li', navmouseenterhandler) .on('mouseleave', '#multiscroll-nav li', navmouseleavehandler) .on('click', '#multiscroll-nav a', navclickhandler); }; }; })(jquery, window, document, math);