/*!
* 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);