/**
* jmpress.js v0.4.5
* http://jmpressjs.github.com/jmpress.js
*
* A jQuery plugin to build a website on the infinite canvas.
*
* Copyright 2013 Kyle Robinson Young @shama & Tobias Koppers @sokra
* Licensed MIT
* http://www.opensource.org/licenses/mit-license.php
*
* Based on the foundation laid by Bartek Szopka @bartaz
*/ /*
* core.js
* The core of jmpress.js
*/
( function ( $, document, window, undefined ) {
'use strict';
/**
* Set supported prefixes
*
* @access protected
* @return Function to get prefixed property
*/
var pfx = ( function () {
var style = document.createElement( 'dummy' ).style,
prefixes = 'Webkit Moz O ms Khtml'.split( ' ' ),
memory = {};
return function ( prop ) {
if ( typeof memory[ prop ] === 'undefined' ) {
var ucProp = prop.charAt( 0 ).toUpperCase() + prop.substr( 1 ),
props = ( prop + ' ' + prefixes.join( ucProp + ' ' ) + ucProp ).split( ' ' );
memory[ prop ] = null;
for ( var i in props ) {
if ( style[ props[ i ] ] !== undefined ) {
memory[ prop ] = props[ i ];
break;
}
}
}
return memory[ prop ];
};
} )();
/**
* map ex. "WebkitTransform" to "-webkit-transform"
*/
function mapProperty( name ) {
if ( ! name ) {
return;
}
var index = 1 + name.substr( 1 ).search( /[A-Z]/ );
var prefix = name.substr( 0, index ).toLowerCase();
var postfix = name.substr( index ).toLowerCase();
return '-' + prefix + '-' + postfix;
}
function addComma( attribute ) {
if ( ! attribute ) {
return '';
}
return attribute + ',';
}
/**
* Return an jquery object only if it's not empty
*/
function ifNotEmpty( el ) {
if ( el.length > 0 ) {
return el;
}
return null;
}
/**
* Default Settings
*/
var defaults = {
/* CLASSES */
stepSelector: '.step',
containerClass: '',
canvasClass: '',
areaClass: '',
notSupportedClass: 'not-supported',
/* CONFIG */
fullscreen: true,
/* ANIMATION */
animation: {
transformOrigin: 'top left',
transitionProperty:
addComma( mapProperty( pfx( 'transform' ) ) ) +
addComma( mapProperty( pfx( 'perspective' ) ) ) +
'opacity',
transitionDuration: '1s',
transitionDelay: '500ms',
transitionTimingFunction: 'ease-in-out',
transformStyle: 'preserve-3d',
},
transitionDuration: 1500,
};
var callbacks = {
beforeChange: 1,
beforeInitStep: 1,
initStep: 1,
beforeInit: 1,
afterInit: 1,
beforeDeinit: 1,
afterDeinit: 1,
applyStep: 1,
unapplyStep: 1,
setInactive: 1,
beforeActive: 1,
setActive: 1,
selectInitialStep: 1,
selectPrev: 1,
selectNext: 1,
selectHome: 1,
selectEnd: 1,
idle: 1,
applyTarget: 1,
};
for ( var callbackName in callbacks ) {
defaults[ callbackName ] = [];
}
/**
* Initialize jmpress
*/
function init( args ) {
args = $.extend( true, {}, args || {} );
// accept functions and arrays of functions as callbacks
var callbackArgs = {};
var callbackName = null;
for ( callbackName in callbacks ) {
callbackArgs[ callbackName ] = $.isFunction( args[ callbackName ] )
? [ args[ callbackName ] ]
: args[ callbackName ];
args[ callbackName ] = [];
}
// MERGE SETTINGS
var settings = $.extend( true, {}, defaults, args );
for ( callbackName in callbacks ) {
if ( callbackArgs[ callbackName ] ) {
Array.prototype.push.apply( settings[ callbackName ], callbackArgs[ callbackName ] );
}
}
/*** MEMBER VARS ***/
var jmpress = $( this ),
container = null,
area = null,
oldStyle = {
container: '',
area: '',
},
canvas = null,
current = null,
active = false,
activeSubstep = null,
activeDelegated = false;
/*** MEMBER FUNCTIONS ***/
// functions have to be called with this
/**
* Init a single step
*
* @param element the element of the step
* @param idx number of step
*/
function doStepInit( element, idx ) {
var data = dataset( element );
var step = {
oldStyle: $( element ).attr( 'style' ) || '',
};
var callbackData = {
data: data,
stepData: step,
};
callCallback.call( this, 'beforeInitStep', $( element ), callbackData );
step.delegate = data.delegate;
callCallback.call( this, 'initStep', $( element ), callbackData );
$( element ).data( 'stepData', step );
if ( ! $( element ).attr( 'id' ) ) {
$( element ).attr( 'id', 'step-' + ( idx + 1 ) );
}
callCallback.call( this, 'applyStep', $( element ), callbackData );
}
/**
* Deinit a single step
*
* @param element the element of the step
*/
function doStepDeinit( element ) {
var stepData = $( element ).data( 'stepData' );
$( element ).attr( 'style', stepData.oldStyle );
callCallback.call( this, 'unapplyStep', $( element ), {
stepData: stepData,
} );
}
/**
* Reapplies stepData to the element
*
* @param element
*/
function doStepReapply( element ) {
callCallback.call( this, 'unapplyStep', $( element ), {
stepData: element.data( 'stepData' ),
} );
callCallback.call( this, 'applyStep', $( element ), {
stepData: element.data( 'stepData' ),
} );
}
/**
* Completly deinit jmpress
*
*/
function deinit() {
if ( active ) {
callCallback.call( this, 'setInactive', active, {
stepData: $( active ).data( 'stepData' ),
reason: 'deinit',
} );
}
if ( current.jmpressClass ) {
$( jmpress ).removeClass( current.jmpressClass );
}
callCallback.call( this, 'beforeDeinit', $( this ), {} );
$( settings.stepSelector, jmpress ).each( function ( idx ) {
doStepDeinit.call( jmpress, this );
} );
container.attr( 'style', oldStyle.container );
if ( settings.fullscreen ) {
$( 'html' ).attr( 'style', '' );
}
area.attr( 'style', oldStyle.area );
$( canvas )
.children()
.each( function () {
jmpress.append( $( this ) );
} );
if ( settings.fullscreen ) {
canvas.remove();
} else {
canvas.remove();
area.remove();
}
callCallback.call( this, 'afterDeinit', $( this ), {} );
$( jmpress ).data( 'jmpressmethods', false );
}
/**
* Call a callback
*
* @param callbackName String callback which should be called
* @param element some arguments to the callback
* @param eventData
*/
function callCallback( callbackName, element, eventData ) {
eventData.settings = settings;
eventData.current = current;
eventData.container = container;
eventData.parents = element ? getStepParents( element ) : null;
eventData.current = current;
eventData.jmpress = this;
var result = {};
$.each( settings[ callbackName ], function ( idx, callback ) {
result.value = callback.call( jmpress, element, eventData ) || result.value;
} );
return result.value;
}
/**
*
*/
function getStepParents( el ) {
return $( el ).parentsUntil( jmpress ).not( jmpress ).filter( settings.stepSelector );
}
/**
* Reselect the active step
*
* @param String type reason of reselecting step
*/
function reselect( type ) {
return select( { step: active, substep: activeSubstep }, type );
}
/**
* Select a given step
*
* @param el element to select
* @param type reason of changing step
* @return Object element selected
*/
function select( el, type ) {
var substep;
if ( $.isPlainObject( el ) ) {
substep = el.substep;
el = el.step;
}
if ( typeof el === 'string' ) {
el = jmpress.find( el ).first();
}
if ( ! el || ! $( el ).data( 'stepData' ) ) {
return false;
}
scrollFix.call( this );
var step = $( el ).data( 'stepData' );
var cancelSelect = false;
callCallback.call( this, 'beforeChange', el, {
stepData: step,
reason: type,
cancel: function () {
cancelSelect = true;
},
} );
if ( cancelSelect ) {
return undefined;
}
var target = {};
var delegated = el;
if ( $( el ).data( 'stepData' ).delegate ) {
delegated =
ifNotEmpty(
$( el ).parentsUntil( jmpress ).filter( settings.stepSelector ).filter( step.delegate )
) ||
ifNotEmpty( $( el ).near( step.delegate ) ) ||
ifNotEmpty( $( el ).near( step.delegate, true ) ) ||
ifNotEmpty( $( step.delegate, jmpress ) );
if ( delegated ) {
step = delegated.data( 'stepData' );
} else {
// Do not delegate if expression not found
delegated = el;
}
}
if ( activeDelegated ) {
callCallback.call( this, 'setInactive', activeDelegated, {
stepData: $( activeDelegated ).data( 'stepData' ),
delegatedFrom: active,
reason: type,
target: target,
nextStep: delegated,
nextSubstep: substep,
nextStepData: step,
} );
}
var callbackData = {
stepData: step,
delegatedFrom: el,
reason: type,
target: target,
substep: substep,
prevStep: activeDelegated,
prevSubstep: activeSubstep,
prevStepData: activeDelegated && $( activeDelegated ).data( 'stepData' ),
};
callCallback.call( this, 'beforeActive', delegated, callbackData );
callCallback.call( this, 'setActive', delegated, callbackData );
// Set on step class on root element
if ( current.jmpressClass ) {
$( jmpress ).removeClass( current.jmpressClass );
}
$( jmpress ).addClass( ( current.jmpressClass = 'step-' + $( delegated ).attr( 'id' ) ) );
if ( current.jmpressDelegatedClass ) {
$( jmpress ).removeClass( current.jmpressDelegatedClass );
}
$( jmpress ).addClass(
( current.jmpressDelegatedClass = 'delegating-step-' + $( el ).attr( 'id' ) )
);
callCallback.call(
this,
'applyTarget',
delegated,
$.extend(
{
canvas: canvas,
area: area,
beforeActive: activeDelegated,
},
callbackData
)
);
active = el;
activeSubstep = callbackData.substep;
activeDelegated = delegated;
if ( current.idleTimeout ) {
clearTimeout( current.idleTimeout );
}
current.idleTimeout = setTimeout( function () {
callCallback.call( this, 'idle', delegated, callbackData );
}, Math.max( 1, settings.transitionDuration - 100 ) );
return delegated;
}
/**
* This should fix ANY kind of buggy scrolling
*/
function scrollFix() {
( function fix() {
if ( $( container )[ 0 ].tagName === 'BODY' ) {
try {
window.scrollTo( 0, 0 );
} catch ( e ) {}
}
$( container ).scrollTop( 0 );
$( container ).scrollLeft( 0 );
function check() {
if ( $( container ).scrollTop() !== 0 || $( container ).scrollLeft() !== 0 ) {
fix();
}
}
setTimeout( check, 1 );
setTimeout( check, 10 );
setTimeout( check, 100 );
setTimeout( check, 200 );
setTimeout( check, 400 );
} )();
}
/**
* Alias for select
*/
function goTo( el ) {
return select.call( this, el, 'jump' );
}
/**
* Goto Next Slide
*
* @return Object newly active slide
*/
function next() {
return select.call(
this,
callCallback.call( this, 'selectNext', active, {
stepData: $( active ).data( 'stepData' ),
substep: activeSubstep,
} ),
'next'
);
}
/**
* Goto Previous Slide
*
* @return Object newly active slide
*/
function prev() {
return select.call(
this,
callCallback.call( this, 'selectPrev', active, {
stepData: $( active ).data( 'stepData' ),
substep: activeSubstep,
} ),
'prev'
);
}
/**
* Goto First Slide
*
* @return Object newly active slide
*/
function home() {
return select.call(
this,
callCallback.call( this, 'selectHome', active, {
stepData: $( active ).data( 'stepData' ),
} ),
'home'
);
}
/**
* Goto Last Slide
*
* @return Object newly active slide
*/
function end() {
return select.call(
this,
callCallback.call( this, 'selectEnd', active, {
stepData: $( active ).data( 'stepData' ),
} ),
'end'
);
}
/**
* Manipulate the canvas
*
* @param props
* @return Object
*/
function canvasMod( props ) {
css( canvas, props || {} );
return $( canvas );
}
/**
* Return current step
*
* @return Object
*/
function getActive() {
return activeDelegated && $( activeDelegated );
}
/**
* fire a callback
*
* @param callbackName
* @param element
* @param eventData
* @return void
*/
function fire( callbackName, element, eventData ) {
if ( ! callbacks[ callbackName ] ) {
$.error( 'callback ' + callbackName + ' is not registered.' );
} else {
return callCallback.call( this, callbackName, element, eventData );
}
}
/**
* PUBLIC METHODS LIST
*/
jmpress.data( 'jmpressmethods', {
select: select,
reselect: reselect,
scrollFix: scrollFix,
goTo: goTo,
next: next,
prev: prev,
home: home,
end: end,
canvas: canvasMod,
container: function () {
return container;
},
settings: function () {
return settings;
},
active: getActive,
current: function () {
return current;
},
fire: fire,
init: function ( step ) {
doStepInit.call( this, $( step ), current.nextIdNumber++ );
},
deinit: function ( step ) {
if ( step ) {
doStepDeinit.call( this, $( step ) );
} else {
deinit.call( this );
}
},
reapply: doStepReapply,
} );
/**
* Check for support
* This will be removed in near future, when support is coming
*
* @access protected
* @return void
*/
function checkSupport() {
var ua = navigator.userAgent.toLowerCase();
return ua.search( /(iphone)|(ipod)|(android)/ ) === -1 || ua.search( /(chrome)/ ) !== -1;
}
// BEGIN INIT
// CHECK FOR SUPPORT
if ( checkSupport() === false ) {
if ( settings.notSupportedClass ) {
jmpress.addClass( settings.notSupportedClass );
}
return;
} else {
if ( settings.notSupportedClass ) {
jmpress.removeClass( settings.notSupportedClass );
}
}
// grabbing all steps
var steps = $( settings.stepSelector, jmpress );
// GERNERAL INIT OF FRAME
container = jmpress;
area = $( '<div />' );
canvas = $( '<div />' );
$( jmpress )
.children()
.filter( steps )
.each( function () {
canvas.append( $( this ) );
} );
if ( settings.fullscreen ) {
container = $( 'body' );
$( 'html' ).css( {
overflow: 'hidden',
} );
area = jmpress;
}
oldStyle.area = area.attr( 'style' ) || '';
oldStyle.container = container.attr( 'style' ) || '';
if ( settings.fullscreen ) {
container.css( {
height: '100%',
} );
jmpress.append( canvas );
} else {
container.css( {
position: 'relative',
} );
area.append( canvas );
jmpress.append( area );
}
$( container ).addClass( settings.containerClass );
$( area ).addClass( settings.areaClass );
$( canvas ).addClass( settings.canvasClass );
document.documentElement.style.height = '100%';
container.css( {
overflow: 'hidden',
} );
var props = {
position: 'absolute',
transitionDuration: '0s',
};
props = $.extend( {}, settings.animation, props );
css( area, props );
css( area, {
top: '50%',
left: '50%',
perspective: '1000px',
} );
css( canvas, props );
current = {};
callCallback.call( this, 'beforeInit', null, {} );
// INITIALIZE EACH STEP
steps.each( function ( idx ) {
doStepInit.call( jmpress, this, idx );
} );
current.nextIdNumber = steps.length;
callCallback.call( this, 'afterInit', null, {} );
// START
select.call( this, callCallback.call( this, 'selectInitialStep', 'init', {} ) );
if ( settings.initClass ) {
$( steps ).removeClass( settings.initClass );
}
}
/**
* Return default settings
*
* @return Object
*/
function getDefaults() {
return defaults;
}
/**
* Register a callback or a jmpress function
*
* @access public
* @param name String the name of the callback or function
* @param func Function? the function to be added
*/
function register( name, func ) {
if ( $.isFunction( func ) ) {
if ( methods[ name ] ) {
$.error( 'function ' + name + ' is already registered.' );
} else {
methods[ name ] = func;
}
} else {
if ( callbacks[ name ] ) {
$.error( 'callback ' + name + ' is already registered.' );
} else {
callbacks[ name ] = 1;
defaults[ name ] = [];
}
}
}
/**
* Set CSS on element w/ prefixes
*
* @return Object element which properties were set
*
* TODO: Consider bypassing pfx and blindly set as jQuery
* already checks for support
*/
function css( el, props ) {
var key,
pkey,
cssObj = {};
for ( key in props ) {
if ( props.hasOwnProperty( key ) ) {
pkey = pfx( key );
if ( pkey !== null ) {
cssObj[ pkey ] = props[ key ];
}
}
}
$( el ).css( cssObj );
return el;
}
/**
* Return dataset for element
*
* @param el element
* @return Object
*/
function dataset( el ) {
if ( $( el )[ 0 ].dataset ) {
return $.extend( {}, $( el )[ 0 ].dataset );
}
function toCamelcase( str ) {
str = str.split( '-' );
for ( var i = 1; i < str.length; i++ ) {
str[ i ] = str[ i ].substr( 0, 1 ).toUpperCase() + str[ i ].substr( 1 );
}
return str.join( '' );
}
var returnDataset = {};
var attrs = $( el )[ 0 ].attributes;
$.each( attrs, function ( idx, attr ) {
if ( attr.nodeName.substr( 0, 5 ) === 'data-' ) {
returnDataset[ toCamelcase( attr.nodeName.substr( 5 ) ) ] = attr.nodeValue;
}
} );
return returnDataset;
}
/**
* Returns true, if jmpress is initialized
*
* @return bool
*/
function initialized() {
return !! $( this ).data( 'jmpressmethods' );
}
/**
* PUBLIC STATIC METHODS LIST
*/
var methods = {
init: init,
initialized: initialized,
deinit: function () {},
css: css,
pfx: pfx,
defaults: getDefaults,
register: register,
dataset: dataset,
};
/**
* $.jmpress()
*/
$.fn.jmpress = function ( method ) {
function f() {
var jmpressmethods = $( this ).data( 'jmpressmethods' );
if ( jmpressmethods && jmpressmethods[ method ] ) {
return jmpressmethods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ) );
} else if ( methods[ method ] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ) );
} else if ( callbacks[ method ] && jmpressmethods ) {
var settings = jmpressmethods.settings();
var func = Array.prototype.slice.call( arguments, 1 )[ 0 ];
if ( $.isFunction( func ) ) {
settings[ method ] = settings[ method ] || [];
settings[ method ].push( func );
}
} else if ( typeof method === 'object' || ! method ) {
return init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.jmpress' );
}
// to allow chaining
return this;
}
var args = arguments;
var result;
$( this ).each( function ( idx, element ) {
result = f.apply( element, args );
} );
return result;
};
$.extend( {
jmpress: function ( method ) {
if ( methods[ method ] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ) );
} else if ( callbacks[ method ] ) {
// plugin interface
var func = Array.prototype.slice.call( arguments, 1 )[ 0 ];
if ( $.isFunction( func ) ) {
defaults[ method ].push( func );
} else {
$.error(
'Second parameter should be a function: $.jmpress( callbackName, callbackFunction )'
);
}
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.jmpress' );
}
},
} );
} )( jQuery, document, window );
/*
* near.js
* Find steps near each other
*/
( function ( $, document, window, undefined ) {
'use strict';
// add near( selector, backwards = false) to jquery
function checkAndGo( elements, func, selector, backwards ) {
var next;
elements.each( function ( idx, element ) {
if ( backwards ) {
next = func( element, selector, backwards );
if ( next ) {
return false;
}
}
if ( $( element ).is( selector ) ) {
next = element;
return false;
}
if ( ! backwards ) {
next = func( element, selector, backwards );
if ( next ) {
return false;
}
}
} );
return next;
}
function findNextInChildren( item, selector, backwards ) {
var children = $( item ).children();
if ( backwards ) {
children = $( children.get().reverse() );
}
return checkAndGo( children, findNextInChildren, selector, backwards );
}
function findNextInSiblings( item, selector, backwards ) {
return checkAndGo(
$( item )[ backwards ? 'prevAll' : 'nextAll' ](),
findNextInChildren,
selector,
backwards
);
}
function findNextInParents( item, selector, backwards ) {
var next;
var parents = $( item ).parents();
parents = $( parents.get() );
$.each( parents.get(), function ( idx, element ) {
if ( backwards && $( element ).is( selector ) ) {
next = element;
return false;
}
next = findNextInSiblings( element, selector, backwards );
if ( next ) {
return false;
}
} );
return next;
}
$.fn.near = function ( selector, backwards ) {
var array = [];
$( this ).each( function ( idx, element ) {
var near =
( backwards ? false : findNextInChildren( element, selector, backwards ) ) ||
findNextInSiblings( element, selector, backwards ) ||
findNextInParents( element, selector, backwards );
if ( near ) {
array.push( near );
}
} );
return $( array );
};
} )( jQuery, document, window );
/*
* transform.js
* The engine that powers the transforms or falls back to other methods
*/
( function ( $, document, window, undefined ) {
'use strict';
/* FUNCTIONS */
function toCssNumber( number ) {
return Math.round( 10000 * number ) / 10000 + '';
}
/**
* 3D and 2D engines
*/
var engines = {
3: {
transform: function ( el, data ) {
var transform = 'translate(-50%,-50%)';
$.each( data, function ( idx, item ) {
var coord = [ 'X', 'Y', 'Z' ];
var i;
if ( item[ 0 ] === 'translate' ) {
// ["translate", x, y, z]
transform +=
' translate3d(' +
toCssNumber( item[ 1 ] || 0 ) +
'px,' +
toCssNumber( item[ 2 ] || 0 ) +
'px,' +
toCssNumber( item[ 3 ] || 0 ) +
'px)';
} else if ( item[ 0 ] === 'rotate' ) {
var order = item[ 4 ] ? [ 1, 2, 3 ] : [ 3, 2, 1 ];
for ( i = 0; i < 3; i++ ) {
transform +=
' rotate' +
coord[ order[ i ] - 1 ] +
'(' +
toCssNumber( item[ order[ i ] ] || 0 ) +
'deg)';
}
} else if ( item[ 0 ] === 'scale' ) {
for ( i = 0; i < 3; i++ ) {
transform += ' scale' + coord[ i ] + '(' + toCssNumber( item[ i + 1 ] || 1 ) + ')';
}
}
} );
$.jmpress( 'css', el, $.extend( {}, { transform: transform } ) );
},
},
2: {
transform: function ( el, data ) {
var transform = 'translate(-50%,-50%)';
$.each( data, function ( idx, item ) {
var coord = [ 'X', 'Y' ];
if ( item[ 0 ] === 'translate' ) {
// ["translate", x, y, z]
transform +=
' translate(' +
toCssNumber( item[ 1 ] || 0 ) +
'px,' +
toCssNumber( item[ 2 ] || 0 ) +
'px)';
} else if ( item[ 0 ] === 'rotate' ) {
transform += ' rotate(' + toCssNumber( item[ 3 ] || 0 ) + 'deg)';
} else if ( item[ 0 ] === 'scale' ) {
for ( var i = 0; i < 2; i++ ) {
transform += ' scale' + coord[ i ] + '(' + toCssNumber( item[ i + 1 ] || 1 ) + ')';
}
}
} );
$.jmpress( 'css', el, $.extend( {}, { transform: transform } ) );
},
},
1: {
// CHECK IF SUPPORT IS REALLY NEEDED?
// this not even work without scaling...
// it may better to display the normal view
transform: function ( el, data ) {
var anitarget = { top: 0, left: 0 };
$.each( data, function ( idx, item ) {
var coord = [ 'X', 'Y' ];
if ( item[ 0 ] === 'translate' ) {
// ["translate", x, y, z]
anitarget.left = Math.round( item[ 1 ] || 0 ) + 'px';
anitarget.top = Math.round( item[ 2 ] || 0 ) + 'px';
}
} );
el.animate( anitarget, 1000 ); // TODO: Use animation duration
},
},
};
/**
* Engine to power cross-browser translate, scale and rotate.
*/
var engine = ( function () {
if ( $.jmpress( 'pfx', 'perspective' ) ) {
return engines[ 3 ];
} else if ( $.jmpress( 'pfx', 'transform' ) ) {
return engines[ 2 ];
} else {
// CHECK IF SUPPORT IS REALLY NEEDED?
return engines[ 1 ];
}
} )();
$.jmpress( 'defaults' ).reasonableAnimation = {};
$.jmpress( 'initStep', function ( step, eventData ) {
var data = eventData.data;
var stepData = eventData.stepData;
var pf = parseFloat;
$.extend( stepData, {
x: pf( data.x ) || 0,
y: pf( data.y ) || 0,
z: pf( data.z ) || 0,
r: pf( data.r ) || 0,
phi: pf( data.phi ) || 0,
rotate: pf( data.rotate ) || 0,
rotateX: pf( data.rotateX ) || 0,
rotateY: pf( data.rotateY ) || 0,
rotateZ: pf( data.rotateZ ) || 0,
revertRotate: false,
scale: pf( data.scale ) || 1,
scaleX: pf( data.scaleX ) || false,
scaleY: pf( data.scaleY ) || false,
scaleZ: pf( data.scaleZ ) || 1,
} );
} );
$.jmpress( 'afterInit', function ( nil, eventData ) {
var stepSelector = eventData.settings.stepSelector,
current = eventData.current;
current.perspectiveScale = 1;
current.maxNestedDepth = 0;
var nestedSteps = $( eventData.jmpress ).find( stepSelector ).children( stepSelector );
while ( nestedSteps.length ) {
current.maxNestedDepth++;
nestedSteps = nestedSteps.children( stepSelector );
}
} );
$.jmpress( 'applyStep', function ( step, eventData ) {
$.jmpress( 'css', $( step ), {
position: 'absolute',
transformStyle: 'preserve-3d',
} );
if ( eventData.parents.length > 0 ) {
$.jmpress( 'css', $( step ), {
top: '50%',
left: '50%',
} );
}
var sd = eventData.stepData;
var transform = [
[
'translate',
sd.x || sd.r * Math.sin( ( sd.phi * Math.PI ) / 180 ),
sd.y || -sd.r * Math.cos( ( sd.phi * Math.PI ) / 180 ),
sd.z,
],
[ 'rotate', sd.rotateX, sd.rotateY, sd.rotateZ || sd.rotate, true ],
[ 'scale', sd.scaleX || sd.scale, sd.scaleY || sd.scale, sd.scaleZ || sd.scale ],
];
engine.transform( step, transform );
} );
$.jmpress( 'setActive', function ( element, eventData ) {
var target = eventData.target;
var step = eventData.stepData;
var tf = ( target.transform = [] );
target.perspectiveScale = 1;
for ( var i = eventData.current.maxNestedDepth; i > ( eventData.parents.length || 0 ); i-- ) {
tf.push( [ 'scale' ], [ 'rotate' ], [ 'translate' ] );
}
tf.push( [
'scale',
1 / ( step.scaleX || step.scale ),
1 / ( step.scaleY || step.scale ),
1 / step.scaleZ,
] );
tf.push( [ 'rotate', -step.rotateX, -step.rotateY, -( step.rotateZ || step.rotate ) ] );
tf.push( [
'translate',
-( step.x || step.r * Math.sin( ( step.phi * Math.PI ) / 180 ) ),
-( step.y || -step.r * Math.cos( ( step.phi * Math.PI ) / 180 ) ),
-step.z,
] );
target.perspectiveScale *= step.scaleX || step.scale;
$.each( eventData.parents, function ( idx, element ) {
var step = $( element ).data( 'stepData' );
tf.push( [
'scale',
1 / ( step.scaleX || step.scale ),
1 / ( step.scaleY || step.scale ),
1 / step.scaleZ,
] );
tf.push( [ 'rotate', -step.rotateX, -step.rotateY, -( step.rotateZ || step.rotate ) ] );
tf.push( [
'translate',
-( step.x || step.r * Math.sin( ( step.phi * Math.PI ) / 180 ) ),
-( step.y || -step.r * Math.cos( ( step.phi * Math.PI ) / 180 ) ),
-step.z,
] );
target.perspectiveScale *= step.scaleX || step.scale;
} );
$.each( tf, function ( idx, item ) {
if ( item[ 0 ] !== 'rotate' ) {
return;
}
function lowRotate( name ) {
if ( eventData.current[ 'rotate' + name + '-' + idx ] === undefined ) {
eventData.current[ 'rotate' + name + '-' + idx ] = item[ name ] || 0;
}
var cur = eventData.current[ 'rotate' + name + '-' + idx ],
tar = item[ name ] || 0,
curmod = cur % 360,
tarmod = tar % 360;
if ( curmod < 0 ) {
curmod += 360;
}
if ( tarmod < 0 ) {
tarmod += 360;
}
var diff = tarmod - curmod;
if ( diff < -180 ) {
diff += 360;
} else if ( diff > 180 ) {
diff -= 360;
}
eventData.current[ 'rotate' + name + '-' + idx ] = item[ name ] = cur + diff;
}
lowRotate( 1 );
lowRotate( 2 );
lowRotate( 3 );
} );
} );
$.jmpress( 'applyTarget', function ( active, eventData ) {
var target = eventData.target,
props,
step = eventData.stepData,
settings = eventData.settings,
zoomin = target.perspectiveScale * 1.3 < eventData.current.perspectiveScale,
zoomout = target.perspectiveScale > eventData.current.perspectiveScale * 1.3;
// extract first scale from transform
var lastScale = -1;
$.each( target.transform, function ( idx, item ) {
if ( item.length <= 1 ) {
return;
}
if (
item[ 0 ] === 'rotate' &&
item[ 1 ] % 360 === 0 &&
item[ 2 ] % 360 === 0 &&
item[ 3 ] % 360 === 0
) {
return;
}
if ( item[ 0 ] === 'scale' ) {
lastScale = idx;
} else {
return false;
}
} );
if ( lastScale !== eventData.current.oldLastScale ) {
zoomin = zoomout = false;
eventData.current.oldLastScale = lastScale;
}
var extracted = [];
if ( lastScale !== -1 ) {
while ( lastScale >= 0 ) {
if ( target.transform[ lastScale ][ 0 ] === 'scale' ) {
extracted.push( target.transform[ lastScale ] );
target.transform[ lastScale ] = [ 'scale' ];
}
lastScale--;
}
}
var animation = settings.animation;
if ( settings.reasonableAnimation[ eventData.reason ] ) {
animation = $.extend( {}, animation, settings.reasonableAnimation[ eventData.reason ] );
}
props = {
// to keep the perspective look similar for different scales
// we need to 'scale' the perspective, too
perspective: Math.round( target.perspectiveScale * 1000 ) + 'px',
};
props = $.extend( {}, animation, props );
if ( ! zoomin ) {
props.transitionDelay = '0s';
}
if ( ! eventData.beforeActive ) {
props.transitionDuration = '0s';
props.transitionDelay = '0s';
}
$.jmpress( 'css', eventData.area, props );
engine.transform( eventData.area, extracted );
props = $.extend( {}, animation );
if ( ! zoomout ) {
props.transitionDelay = '0s';
}
if ( ! eventData.beforeActive ) {
props.transitionDuration = '0s';
props.transitionDelay = '0s';
}
eventData.current.perspectiveScale = target.perspectiveScale;
$.jmpress( 'css', eventData.canvas, props );
engine.transform( eventData.canvas, target.transform );
} );
} )( jQuery, document, window );
/*
* active.js
* Set the active classes on steps
*/
( function ( $, document, window, undefined ) {
'use strict';
var $jmpress = $.jmpress;
/* DEFINES */
var activeClass = 'activeClass',
nestedActiveClass = 'nestedActiveClass';
/* DEFAULTS */
var defaults = $jmpress( 'defaults' );
defaults[ nestedActiveClass ] = 'nested-active';
defaults[ activeClass ] = 'active';
/* HOOKS */
$jmpress( 'setInactive', function ( step, eventData ) {
var settings = eventData.settings,
activeClassSetting = settings[ activeClass ],
nestedActiveClassSettings = settings[ nestedActiveClass ];
if ( activeClassSetting ) {
$( step ).removeClass( activeClassSetting );
}
if ( nestedActiveClassSettings ) {
$.each( eventData.parents, function ( idx, element ) {
$( element ).removeClass( nestedActiveClassSettings );
} );
}
} );
$jmpress( 'setActive', function ( step, eventData ) {
var settings = eventData.settings,
activeClassSetting = settings[ activeClass ],
nestedActiveClassSettings = settings[ nestedActiveClass ];
if ( activeClassSetting ) {
$( step ).addClass( activeClassSetting );
}
if ( nestedActiveClassSettings ) {
$.each( eventData.parents, function ( idx, element ) {
$( element ).addClass( nestedActiveClassSettings );
} );
}
} );
} )( jQuery, document, window );
/*
* circular.js
* Repeat from start after end
*/
( function ( $, document, window, undefined ) {
'use strict';
var $jmpress = $.jmpress;
/* FUNCTIONS */
function firstSlide( step, eventData ) {
return $( this ).find( eventData.settings.stepSelector ).first();
}
function prevOrNext( jmpress, step, eventData, prev ) {
if ( ! step ) {
return false;
}
var stepSelector = eventData.settings.stepSelector;
step = $( step );
do {
var item = step.near( stepSelector, prev );
if ( item.length === 0 || item.closest( jmpress ).length === 0 ) {
item = $( jmpress ).find( stepSelector )[ prev ? 'last' : 'first' ](); // eslint-disable-line no-unexpected-multiline
}
if ( ! item.length ) {
return false;
}
step = item;
} while ( step.data( 'stepData' ).exclude );
return step;
}
/* HOOKS */
$jmpress( 'initStep', function ( step, eventData ) {
eventData.stepData.exclude =
eventData.data.exclude && [ 'false', 'no' ].indexOf( eventData.data.exclude ) === -1;
} );
$jmpress( 'selectInitialStep', firstSlide );
$jmpress( 'selectHome', firstSlide );
$jmpress( 'selectEnd', function ( step, eventData ) {
return $( this ).find( eventData.settings.stepSelector ).last();
} );
$jmpress( 'selectPrev', function ( step, eventData ) {
return prevOrNext( this, step, eventData, true );
} );
$jmpress( 'selectNext', function ( step, eventData ) {
return prevOrNext( this, step, eventData );
} );
} )( jQuery, document, window );
/*
* start.js
* Set the first step to start on
*/
( function ( $, document, window, undefined ) {
'use strict';
/* HOOKS */
$.jmpress( 'selectInitialStep', function ( nil, eventData ) {
return eventData.settings.start;
} );
} )( jQuery, document, window );
/*
* ways.js
* Control the flow of the steps
*/
( function ( $, document, window, undefined ) {
'use strict';
var $jmpress = $.jmpress;
/* FUNCTIONS */
function routeFunc( jmpress, route, type ) {
for ( var i = 0; i < route.length - 1; i++ ) {
var from = route[ i ];
var to = route[ i + 1 ];
if ( $( jmpress ).jmpress( 'initialized' ) ) {
$( from, jmpress ).data( 'stepData' )[ type ] = to;
} else {
$( from, jmpress ).attr( 'data-' + type, to );
}
}
}
function selectPrevOrNext( step, eventData, attr, prev ) {
var stepData = eventData.stepData;
if ( stepData[ attr ] ) {
var near = $( step ).near( stepData[ attr ], prev );
if ( near && near.length ) {
return near;
}
near = $( stepData[ attr ], this )[ prev ? 'last' : 'first' ]();
if ( near && near.length ) {
return near;
}
}
}
/* EXPORTED FUNCTIONS */
$jmpress( 'register', 'route', function ( route, unidirectional, reversedRoute ) {
if ( typeof route === 'string' ) {
route = [ route, route ];
}
routeFunc( this, route, reversedRoute ? 'prev' : 'next' );
if ( ! unidirectional ) {
routeFunc( this, route.reverse(), reversedRoute ? 'next' : 'prev' );
}
} );
/* HOOKS */
$jmpress( 'initStep', function ( step, eventData ) {
for ( var attr in { next: 1, prev: 1 } ) {
eventData.stepData[ attr ] = eventData.data[ attr ];
}
} );
$jmpress( 'selectNext', function ( step, eventData ) {
return selectPrevOrNext.call( this, step, eventData, 'next' );
} );
$jmpress( 'selectPrev', function ( step, eventData ) {
return selectPrevOrNext.call( this, step, eventData, 'prev', true );
} );
} )( jQuery, document, window );
/*
* ajax.js
* Load steps via ajax
*/
( function ( $, document, window, undefined ) {
'use strict';
var $jmpress = $.jmpress;
/* DEFINES */
var afterStepLoaded = 'ajax:afterStepLoaded',
loadStep = 'ajax:loadStep';
/* REGISTER EVENTS */
$jmpress( 'register', loadStep );
$jmpress( 'register', afterStepLoaded );
/* DEFAULTS */
$jmpress( 'defaults' ).ajaxLoadedClass = 'loaded';
/* HOOKS */
$jmpress( 'initStep', function ( step, eventData ) {
eventData.stepData.src = $( step ).attr( 'href' ) || eventData.data.src || false;
eventData.stepData.srcLoaded = false;
} );
$jmpress( loadStep, function ( step, eventData ) {
var stepData = eventData.stepData,
href = stepData && stepData.src,
settings = eventData.settings;
if ( href ) {
$( step ).addClass( settings.ajaxLoadedClass );
stepData.srcLoaded = true;
$( step ).load( href, function ( response, status, xhr ) {
$( eventData.jmpress ).jmpress(
'fire',
afterStepLoaded,
step,
$.extend( {}, eventData, {
response: response,
status: status,
xhr: xhr,
} )
);
} );
}
} );
$jmpress( 'idle', function ( step, eventData ) {
if ( ! step ) {
return;
}
var settings = eventData.settings,
jmpress = $( this ),
stepData = eventData.stepData;
var siblings = $( step )
.add( $( step ).near( settings.stepSelector ) )
.add( $( step ).near( settings.stepSelector, true ) )
.add(
jmpress.jmpress( 'fire', 'selectPrev', step, {
stepData: $( step ).data( 'stepData' ),
} )
)
.add(
jmpress.jmpress( 'fire', 'selectNext', step, {
stepData: $( step ).data( 'stepData' ),
} )
);
siblings.each( function () {
var step = this,
stepData = $( step ).data( 'stepData' );
if ( ! stepData.src || stepData.srcLoaded ) {
return;
}
jmpress.jmpress( 'fire', loadStep, step, {
stepData: $( step ).data( 'stepData' ),
} );
} );
} );
$jmpress( 'setActive', function ( step, eventData ) {
var stepData = $( step ).data( 'stepData' );
if ( ! stepData.src || stepData.srcLoaded ) {
return;
}
$( this ).jmpress( 'fire', loadStep, step, {
stepData: $( step ).data( 'stepData' ),
} );
} );
} )( jQuery, document, window );
/*
* hash.js
* Detect and set the URL hash
*/
( function ( $, document, window, undefined ) {
'use strict';
var $jmpress = $.jmpress,
hashLink = "a[href^='#']";
/* FUNCTIONS */
function randomString() {
return '' + Math.round( Math.random() * 100000, 0 );
}
/**
* getElementFromUrl
*
* @return String or undefined
*/
function getElementFromUrl( settings ) {
// get id from url # by removing `#` or `#/` from the beginning,
// so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work
// TODO SECURITY check user input to be valid!
try {
var el = $( '#' + window.location.hash.replace( /^#\/?/, '' ) );
return el.length > 0 && el.is( settings.stepSelector ) ? el : undefined;
} catch ( e ) {}
}
function setHash( stepid ) {
var shouldBeHash = '#/' + stepid;
if ( window.history && window.history.pushState ) {
// shouldBeHash = "#" + stepid;
// consider this for future versions
// it has currently issues, when startup with a link with hash (webkit)
if ( window.location.hash !== shouldBeHash ) {
window.history.pushState( {}, '', shouldBeHash );
}
} else {
if ( window.location.hash !== shouldBeHash ) {
window.location.hash = shouldBeHash;
}
}
}
/* DEFAULTS */
$jmpress( 'defaults' ).hash = {
use: true,
update: true,
bindChange: true,
// NOTICE: {use: true, update: false, bindChange: true}
// will cause a error after clicking on a link to the current step
};
/* HOOKS */
$jmpress( 'selectInitialStep', function ( step, eventData ) {
var settings = eventData.settings,
hashSettings = settings.hash,
current = eventData.current,
jmpress = $( this );
eventData.current.hashNamespace = '.jmpress-' + randomString();
// HASH CHANGE EVENT
if ( hashSettings.use ) {
if ( hashSettings.bindChange ) {
$( window ).bind( 'hashchange' + current.hashNamespace, function ( event ) {
var urlItem = getElementFromUrl( settings );
if ( jmpress.jmpress( 'initialized' ) ) {
jmpress.jmpress( 'scrollFix' );
}
if ( urlItem && urlItem.length ) {
if ( urlItem.attr( 'id' ) !== jmpress.jmpress( 'active' ).attr( 'id' ) ) {
jmpress.jmpress( 'select', urlItem );
}
setHash( urlItem.attr( 'id' ) );
}
event.preventDefault();
} );
$( hashLink ).on( 'click' + current.hashNamespace, function ( event ) {
var href = $( this ).attr( 'href' );
try {
if ( $( href ).is( settings.stepSelector ) ) {
jmpress.jmpress( 'select', href );
event.preventDefault();
event.stopPropagation();
}
} catch ( e ) {}
} );
}
return getElementFromUrl( settings );
}
} );
$jmpress( 'afterDeinit', function ( nil, eventData ) {
$( hashLink ).off( eventData.current.hashNamespace );
$( window ).unbind( eventData.current.hashNamespace );
} );
$jmpress( 'setActive', function ( step, eventData ) {
var settings = eventData.settings,
current = eventData.current;
// `#/step-id` is used instead of `#step-id` to prevent default browser
// scrolling to element in hash
if ( settings.hash.use && settings.hash.update ) {
clearTimeout( current.hashtimeout );
current.hashtimeout = setTimeout( function () {
setHash( $( eventData.delegatedFrom ).attr( 'id' ) );
}, settings.transitionDuration + 200 );
}
} );
} )( jQuery, document, window );
/*
* keyboard.js
* Keyboard event mapping and default keyboard actions
*/
( function ( $, document, window, undefined ) {
'use strict';
var $jmpress = $.jmpress,
jmpressNext = 'next',
jmpressPrev = 'prev';
/* FUNCTIONS */
function randomString() {
return '' + Math.round( Math.random() * 100000, 0 );
}
function stopEvent( event ) {
event.preventDefault();
event.stopPropagation();
}
/* DEFAULTS */
$jmpress( 'defaults' ).keyboard = {
use: true,
keys: {
33: jmpressPrev, // pg up
37: jmpressPrev, // left
38: jmpressPrev, // up
9: jmpressNext + ':' + jmpressPrev, // tab
32: jmpressNext, // space
34: jmpressNext, // pg down
39: jmpressNext, // right
40: jmpressNext, // down
36: 'home', // home
35: 'end', // end
},
ignore: {
INPUT: [
32, // space
37, // left
38, // up
39, // right
40, // down
],
TEXTAREA: [
32, // space
37, // left
38, // up
39, // right
40, // down
],
SELECT: [
38, // up
40, // down
],
},
tabSelector: 'a[href]:visible, :input:visible',
};
/* HOOKS */
$jmpress( 'afterInit', function ( nil, eventData ) {
var settings = eventData.settings,
keyboardSettings = settings.keyboard,
ignoreKeyboardSettings = keyboardSettings.ignore,
current = eventData.current,
jmpress = $( this );
// tabindex make it focusable so that it can receive key events
if ( ! settings.fullscreen ) {
jmpress.attr( 'tabindex', 0 );
}
current.keyboardNamespace = '.jmpress-' + randomString();
// KEYPRESS EVENT: this fixes a Opera bug
$( settings.fullscreen ? document : jmpress ).bind(
'keypress' + current.keyboardNamespace,
function ( event ) {
for ( var nodeName in ignoreKeyboardSettings ) {
if (
event.target.nodeName === nodeName &&
ignoreKeyboardSettings[ nodeName ].indexOf( event.which ) !== -1
) {
return;
}
}
if ( ( event.which >= 37 && event.which <= 40 ) || event.which === 32 ) {
stopEvent( event );
}
}
);
// KEYDOWN EVENT
$( settings.fullscreen ? document : jmpress ).bind(
'keydown' + current.keyboardNamespace,
function ( event ) {
var eventTarget = $( event.target );
if (
( ! settings.fullscreen && ! eventTarget.closest( jmpress ).length ) ||
! keyboardSettings.use
) {
return;
}
for ( var nodeName in ignoreKeyboardSettings ) {
if (
eventTarget[ 0 ].nodeName === nodeName &&
ignoreKeyboardSettings[ nodeName ].indexOf( event.which ) !== -1
) {
return;
}
}
var reverseSelect = false;
var nextFocus;
if ( event.which === 9 ) {
// tab
if ( ! eventTarget.closest( jmpress.jmpress( 'active' ) ).length ) {
if ( ! event.shiftKey ) {
nextFocus = jmpress
.jmpress( 'active' )
.find( 'a[href], :input' )
.filter( ':visible' )
.first();
} else {
reverseSelect = true;
}
} else {
nextFocus = eventTarget.near( keyboardSettings.tabSelector, event.shiftKey );
if (
! $( nextFocus ).closest( settings.stepSelector ).is( jmpress.jmpress( 'active' ) )
) {
nextFocus = undefined;
}
}
if ( nextFocus && nextFocus.length > 0 ) {
nextFocus.focus();
jmpress.jmpress( 'scrollFix' );
stopEvent( event );
return;
} else {
if ( event.shiftKey ) {
reverseSelect = true;
}
}
}
var action = keyboardSettings.keys[ event.which ];
if ( typeof action === 'string' ) {
if ( action.indexOf( ':' ) !== -1 ) {
action = action.split( ':' );
action = event.shiftKey ? action[ 1 ] : action[ 0 ];
}
jmpress.jmpress( action );
stopEvent( event );
} else if ( $.isFunction( action ) ) {
action.call( jmpress, event );
} else if ( action ) {
jmpress.jmpress.apply( jmpress, action );
stopEvent( event );
}
if ( reverseSelect ) {
// tab
nextFocus = jmpress
.jmpress( 'active' )
.find( 'a[href], :input' )
.filter( ':visible' )
.last();
nextFocus.focus();
jmpress.jmpress( 'scrollFix' );
}
}
);
} );
$jmpress( 'afterDeinit', function ( nil, eventData ) {
$( document ).unbind( eventData.current.keyboardNamespace );
} );
} )( jQuery, document, window );
/*
* viewport.js
* Scale to fit a given viewport
*/
( function ( $, document, window, undefined ) {
'use strict';
function randomString() {
return '' + Math.round( Math.random() * 100000, 0 );
}
var browser = ( function () {
var ua = navigator.userAgent.toLowerCase();
var match =
/(chrome)[ \/]([\w.]+)/.exec( ua ) ||
/(webkit)[ \/]([\w.]+)/.exec( ua ) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
/(msie) ([\w.]+)/.exec( ua ) ||
( ua.indexOf( 'compatible' ) < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ) ||
[];
return match[ 1 ] || '';
} )();
var defaults = $.jmpress( 'defaults' );
defaults.viewPort = {
width: false,
height: false,
maxScale: 0,
minScale: 0,
zoomable: 0,
zoomBindMove: true,
zoomBindWheel: true,
};
var keys = defaults.keyboard.keys;
keys[ browser === 'mozilla' ? 107 : 187 ] = 'zoomIn'; // +
keys[ browser === 'mozilla' ? 109 : 189 ] = 'zoomOut'; // -
defaults.reasonableAnimation.resize = {
transitionDuration: '0s',
transitionDelay: '0ms',
};
defaults.reasonableAnimation.zoom = {
transitionDuration: '0s',
transitionDelay: '0ms',
};
$.jmpress( 'initStep', function ( step, eventData ) {
for ( var variable in {
viewPortHeight: 1,
viewPortWidth: 1,
viewPortMinScale: 1,
viewPortMaxScale: 1,
viewPortZoomable: 1,
} ) {
eventData.stepData[ variable ] =
eventData.data[ variable ] && parseFloat( eventData.data[ variable ] );
}
} );
$.jmpress( 'afterInit', function ( nil, eventData ) {
var jmpress = this;
eventData.current.viewPortNamespace = '.jmpress-' + randomString();
$( window ).bind( 'resize' + eventData.current.viewPortNamespace, function ( event ) {
$( jmpress ).jmpress( 'reselect', 'resize' );
} );
eventData.current.userZoom = 0;
eventData.current.userTranslateX = 0;
eventData.current.userTranslateY = 0;
if ( eventData.settings.viewPort.zoomBindWheel ) {
$( eventData.settings.fullscreen ? document : this ).bind(
'mousewheel' +
eventData.current.viewPortNamespace +
' DOMMouseScroll' +
eventData.current.viewPortNamespace,
function ( event, delta ) {
delta =
delta || event.originalEvent.wheelDelta || -event.originalEvent.detail /* mozilla */;
var direction = delta / Math.abs( delta );
if ( direction < 0 ) {
$( eventData.jmpress ).jmpress(
'zoomOut',
event.originalEvent.x,
event.originalEvent.y
);
} else if ( direction > 0 ) {
$( eventData.jmpress ).jmpress(
'zoomIn',
event.originalEvent.x,
event.originalEvent.y
);
}
return false;
}
);
}
if ( eventData.settings.viewPort.zoomBindMove ) {
$( eventData.settings.fullscreen ? document : this )
.bind( 'mousedown' + eventData.current.viewPortNamespace, function ( event ) {
if ( eventData.current.userZoom ) {
eventData.current.userTranslating = { x: event.clientX, y: event.clientY };
event.preventDefault();
event.stopImmediatePropagation();
}
} )
.bind( 'mousemove' + eventData.current.viewPortNamespace, function ( event ) {
var userTranslating = eventData.current.userTranslating;
if ( userTranslating ) {
$( jmpress ).jmpress(
'zoomTranslate',
event.clientX - userTranslating.x,
event.clientY - userTranslating.y
);
userTranslating.x = event.clientX;
userTranslating.y = event.clientY;
event.preventDefault();
event.stopImmediatePropagation();
}
} )
.bind( 'mouseup' + eventData.current.viewPortNamespace, function ( event ) {
if ( eventData.current.userTranslating ) {
eventData.current.userTranslating = undefined;
event.preventDefault();
event.stopImmediatePropagation();
}
} );
}
} );
function maxAbs( value, range ) {
return Math.max( Math.min( value, range ), -range );
}
function zoom( x, y, direction ) {
var current = $( this ).jmpress( 'current' ),
settings = $( this ).jmpress( 'settings' ),
stepData = $( this ).jmpress( 'active' ).data( 'stepData' ),
container = $( this ).jmpress( 'container' );
if ( current.userZoom === 0 && direction < 0 ) {
return;
}
var zoomableSteps = stepData.viewPortZoomable || settings.viewPort.zoomable;
if ( current.userZoom === zoomableSteps && direction > 0 ) {
return;
}
current.userZoom += direction;
var halfWidth = $( container ).innerWidth() / 2,
halfHeight = $( container ).innerHeight() / 2;
x = x ? x - halfWidth : x;
y = y ? y - halfHeight : y;
// TODO this is not perfect... too much math... :(
current.userTranslateX = maxAbs(
current.userTranslateX - ( direction * x ) / current.zoomOriginWindowScale / zoomableSteps,
( halfWidth * current.userZoom * current.userZoom ) / zoomableSteps
);
current.userTranslateY = maxAbs(
current.userTranslateY - ( direction * y ) / current.zoomOriginWindowScale / zoomableSteps,
( halfHeight * current.userZoom * current.userZoom ) / zoomableSteps
);
$( this ).jmpress( 'reselect', 'zoom' );
}
$.jmpress( 'register', 'zoomIn', function ( x, y ) {
zoom.call( this, x || 0, y || 0, 1 );
} );
$.jmpress( 'register', 'zoomOut', function ( x, y ) {
zoom.call( this, x || 0, y || 0, -1 );
} );
$.jmpress( 'register', 'zoomTranslate', function ( x, y ) {
var current = $( this ).jmpress( 'current' ),
settings = $( this ).jmpress( 'settings' ),
stepData = $( this ).jmpress( 'active' ).data( 'stepData' ),
container = $( this ).jmpress( 'container' );
var zoomableSteps = stepData.viewPortZoomable || settings.viewPort.zoomable;
var halfWidth = $( container ).innerWidth(),
halfHeight = $( container ).innerHeight();
current.userTranslateX = maxAbs(
current.userTranslateX + x / current.zoomOriginWindowScale,
( halfWidth * current.userZoom * current.userZoom ) / zoomableSteps
);
current.userTranslateY = maxAbs(
current.userTranslateY + y / current.zoomOriginWindowScale,
( halfHeight * current.userZoom * current.userZoom ) / zoomableSteps
);
$( this ).jmpress( 'reselect', 'zoom' );
} );
$.jmpress( 'afterDeinit', function ( nil, eventData ) {
$( eventData.settings.fullscreen ? document : this ).unbind(
eventData.current.viewPortNamespace
);
$( window ).unbind( eventData.current.viewPortNamespace );
} );
$.jmpress( 'setActive', function ( step, eventData ) {
var viewPort = eventData.settings.viewPort;
var viewPortHeight = eventData.stepData.viewPortHeight || viewPort.height;
var viewPortWidth = eventData.stepData.viewPortWidth || viewPort.width;
var viewPortMaxScale = eventData.stepData.viewPortMaxScale || viewPort.maxScale;
var viewPortMinScale = eventData.stepData.viewPortMinScale || viewPort.minScale;
// Correct the scale based on the window's size
var windowScaleY = viewPortHeight && $( eventData.container ).innerHeight() / viewPortHeight;
var windowScaleX = viewPortWidth && $( eventData.container ).innerWidth() / viewPortWidth;
var windowScale =
( windowScaleX || windowScaleY ) &&
Math.min( windowScaleX || windowScaleY, windowScaleY || windowScaleX );
if ( windowScale ) {
windowScale = windowScale || 1;
if ( viewPortMaxScale ) {
windowScale = Math.min( windowScale, viewPortMaxScale );
}
if ( viewPortMinScale ) {
windowScale = Math.max( windowScale, viewPortMinScale );
}
var zoomableSteps =
eventData.stepData.viewPortZoomable || eventData.settings.viewPort.zoomable;
if ( zoomableSteps ) {
var diff = 1 / windowScale - 1 / viewPortMaxScale;
diff /= zoomableSteps;
windowScale = 1 / ( 1 / windowScale - diff * eventData.current.userZoom );
}
eventData.target.transform.reverse();
if ( eventData.current.userTranslateX && eventData.current.userTranslateY ) {
eventData.target.transform.push( [
'translate',
eventData.current.userTranslateX,
eventData.current.userTranslateY,
0,
] );
} else {
eventData.target.transform.push( [ 'translate' ] );
}
eventData.target.transform.push( [ 'scale', windowScale, windowScale, 1 ] );
eventData.target.transform.reverse();
eventData.target.perspectiveScale /= windowScale;
}
eventData.current.zoomOriginWindowScale = windowScale;
} );
$.jmpress( 'setInactive', function ( step, eventData ) {
if (
! eventData.nextStep ||
! step ||
$( eventData.nextStep ).attr( 'id' ) !== $( step ).attr( 'id' )
) {
eventData.current.userZoom = 0;
eventData.current.userTranslateX = 0;
eventData.current.userTranslateY = 0;
}
} );
} )( jQuery, document, window );
/*
* mouse.js
* Clicking to select a step
*/
( function ( $, document, window, undefined ) {
'use strict';
var $jmpress = $.jmpress;
/* FUNCTIONS */
function randomString() {
return '' + Math.round( Math.random() * 100000, 0 );
}
/* DEFAULTS */
$jmpress( 'defaults' ).mouse = {
clickSelects: true,
};
/* HOOKS */
$jmpress( 'afterInit', function ( nil, eventData ) {
var settings = eventData.settings,
stepSelector = settings.stepSelector,
current = eventData.current,
jmpress = $( this );
current.clickableStepsNamespace = '.jmpress-' + randomString();
jmpress.bind( 'click' + current.clickableStepsNamespace, function ( event ) {
if ( ! settings.mouse.clickSelects || current.userZoom ) {
return;
}
// get clicked step
var clickedStep = $( event.target ).closest( stepSelector );
// clicks on the active step do default
if ( clickedStep.is( jmpress.jmpress( 'active' ) ) ) {
return;
}
if ( clickedStep.length ) {
// select the clicked step
jmpress.jmpress( 'select', clickedStep[ 0 ], 'click' );
event.preventDefault();
event.stopPropagation();
}
} );
} );
$jmpress( 'afterDeinit', function ( nil, eventData ) {
$( this ).unbind( eventData.current.clickableStepsNamespace );
} );
} )( jQuery, document, window );
/*
* mobile.js
* Adds support for swipe on touch supported browsers
*/
( function ( $, document, window, undefined ) {
'use strict';
var $jmpress = $.jmpress;
/* FUNCTIONS */
function randomString() {
return '' + Math.round( Math.random() * 100000, 0 );
}
/* HOOKS */
$jmpress( 'afterInit', function ( step, eventData ) {
var settings = eventData.settings,
current = eventData.current,
jmpress = eventData.jmpress;
current.mobileNamespace = '.jmpress-' + randomString();
var data,
start = [ 0, 0 ];
$( settings.fullscreen ? document : jmpress )
.bind( 'touchstart' + current.mobileNamespace, function ( event ) {
data = event.originalEvent.touches[ 0 ];
start = [ data.pageX, data.pageY ];
} )
.bind( 'touchmove' + current.mobileNamespace, function ( event ) {
data = event.originalEvent.touches[ 0 ];
event.preventDefault();
return false;
} )
.bind( 'touchend' + current.mobileNamespace, function ( event ) {
var end = [ data.pageX, data.pageY ],
diff = [ end[ 0 ] - start[ 0 ], end[ 1 ] - start[ 1 ] ];
if ( Math.max( Math.abs( diff[ 0 ] ), Math.abs( diff[ 1 ] ) ) > 50 ) {
diff = Math.abs( diff[ 0 ] ) > Math.abs( diff[ 1 ] ) ? diff[ 0 ] : diff[ 1 ];
$( jmpress ).jmpress( diff > 0 ? 'prev' : 'next' );
event.preventDefault();
return false;
}
} );
} );
$jmpress( 'afterDeinit', function ( nil, eventData ) {
var settings = eventData.settings,
current = eventData.current,
jmpress = eventData.jmpress;
$( settings.fullscreen ? document : jmpress ).unbind( current.mobileNamespace );
} );
} )( jQuery, document, window );
/*
* templates.js
* The amazing template engine
*/
( function ( $, document, window, undefined ) {
'use strict';
var $jmpress = $.jmpress,
templateFromParentIdent = '_template_',
templateFromApplyIdent = '_applied_template_';
/* STATIC VARS */
var templates = {};
/* FUNCTIONS */
function addUndefined( target, values, prefix ) {
for ( var name in values ) {
var targetName = name;
if ( prefix ) {
targetName = prefix + targetName.substr( 0, 1 ).toUpperCase() + targetName.substr( 1 );
}
if ( $.isPlainObject( values[ name ] ) ) {
addUndefined( target, values[ name ], targetName );
} else if ( target[ targetName ] === undefined ) {
target[ targetName ] = values[ name ];
}
}
}
function applyChildrenTemplates( children, templateChildren ) {
if ( $.isArray( templateChildren ) ) {
if ( templateChildren.length < children.length ) {
$.error( 'more nested steps than children in template' );
} else {
children.each( function ( idx, child ) {
child = $( child );
var tmpl = child.data( templateFromParentIdent ) || {};
addUndefined( tmpl, templateChildren[ idx ] );
child.data( templateFromParentIdent, tmpl );
} );
}
} else if ( $.isFunction( templateChildren ) ) {
children.each( function ( idx, child ) {
child = $( child );
var tmpl = child.data( templateFromParentIdent ) || {};
addUndefined( tmpl, templateChildren( idx, child, children ) );
child.data( templateFromParentIdent, tmpl );
} );
} // TODO: else if(object)
}
function applyTemplate( data, element, template, eventData ) {
if ( template.children ) {
var children = element.children( eventData.settings.stepSelector );
applyChildrenTemplates( children, template.children );
}
applyTemplateData( data, template );
}
function applyTemplateData( data, template ) {
addUndefined( data, template );
}
/* HOOKS */
$jmpress( 'beforeInitStep', function ( step, eventData ) {
step = $( step );
var data = eventData.data,
templateFromAttr = data.template,
templateFromApply = step.data( templateFromApplyIdent ),
templateFromParent = step.data( templateFromParentIdent );
if ( templateFromAttr ) {
$.each( templateFromAttr.split( ' ' ), function ( idx, tmpl ) {
var template = templates[ tmpl ];
applyTemplate( data, step, template, eventData );
} );
}
if ( templateFromApply ) {
applyTemplate( data, step, templateFromApply, eventData );
}
if ( templateFromParent ) {
applyTemplate( data, step, templateFromParent, eventData );
step.data( templateFromParentIdent, null );
if ( templateFromParent.template ) {
$.each( templateFromParent.template.split( ' ' ), function ( idx, tmpl ) {
var template = templates[ tmpl ];
applyTemplate( data, step, template, eventData );
} );
}
}
} );
$jmpress( 'beforeInit', function ( nil, eventData ) {
var data = $jmpress( 'dataset', this ),
dataTemplate = data.template,
stepSelector = eventData.settings.stepSelector;
if ( dataTemplate ) {
var template = templates[ dataTemplate ];
applyChildrenTemplates(
$( this )
.find( stepSelector )
.filter( function () {
return ! $( this ).parent().is( stepSelector );
} ),
template.children
);
}
} );
/* EXPORTED FUNCTIONS */
$jmpress( 'register', 'template', function ( name, tmpl ) {
if ( templates[ name ] ) {
templates[ name ] = $.extend( true, {}, templates[ name ], tmpl );
} else {
templates[ name ] = $.extend( true, {}, tmpl );
}
} );
$jmpress( 'register', 'apply', function ( selector, tmpl ) {
if ( ! tmpl ) {
// TODO ERROR because settings not found
var stepSelector = $( this ).jmpress( 'settings' ).stepSelector;
applyChildrenTemplates(
$( this )
.find( stepSelector )
.filter( function () {
return ! $( this ).parent().is( stepSelector );
} ),
selector
);
} else if ( $.isArray( tmpl ) ) {
applyChildrenTemplates( $( selector ), tmpl );
} else {
var template;
if ( typeof tmpl === 'string' ) {
template = templates[ tmpl ];
} else {
template = $.extend( true, {}, tmpl );
}
$( selector ).each( function ( idx, element ) {
element = $( element );
var tmpl = element.data( templateFromApplyIdent ) || {};
addUndefined( tmpl, template );
element.data( templateFromApplyIdent, tmpl );
} );
}
} );
} )( jQuery, document, window );
/*
* jqevents.js
* Fires jQuery events
*/
( function ( $, document, window, undefined ) {
'use strict';
/* HOOKS */
// the events should not bubble up the tree
// elsewise nested jmpress would cause buggy behavior
$.jmpress( 'setActive', function ( step, eventData ) {
if ( eventData.prevStep !== step ) {
$( step ).triggerHandler( 'enterStep' );
}
} );
$.jmpress( 'setInactive', function ( step, eventData ) {
if ( eventData.nextStep !== step ) {
$( step ).triggerHandler( 'leaveStep' );
}
} );
} )( jQuery, document, window );
/*
* animation.js
* Apply custom animations to steps
*/
( function ( $, document, window, undefined ) {
'use strict';
function parseSubstepInfo( str ) {
var arr = str.split( ' ' );
var className = arr[ 0 ];
var config = {
willClass: 'will-' + className,
doClass: 'do-' + className,
hasClass: 'has-' + className,
};
var state = '';
for ( var i = 1; i < arr.length; i++ ) {
var s = arr[ i ];
switch ( state ) {
case '':
if ( s === 'after' ) {
state = 'after';
} else {
$.warn( "unknown keyword in '" + str + "'. '" + s + "' unknown." );
}
break;
case 'after':
if ( s.match( /^[1-9][0-9]*m?s?/ ) ) {
var value = parseFloat( s );
if ( s.indexOf( 'ms' ) !== -1 ) {
value *= 1;
} else if ( s.indexOf( 's' ) !== -1 ) {
value *= 1000;
} else if ( s.indexOf( 'm' ) !== -1 ) {
value *= 60000;
}
config.delay = value;
} else {
config.after = Array.prototype.slice.call( arr, i ).join( ' ' );
i = arr.length;
}
}
}
return config;
}
function find( array, selector, start, end ) {
end = end || array.length - 1;
start = start || 0;
for ( var i = start; i < end + 1; i++ ) {
if ( $( array[ i ].element ).is( selector ) ) {
return i;
}
}
}
function addOn( list, substep, delay ) {
$.each( substep._on, function ( idx, child ) {
list.push( { substep: child.substep, delay: child.delay + delay } );
addOn( list, child.substep, child.delay + delay );
} );
}
$.jmpress( 'defaults' ).customAnimationDataAttribute = 'jmpress';
$.jmpress( 'afterInit', function ( nil, eventData ) {
eventData.current.animationTimeouts = [];
eventData.current.animationCleanupWaiting = [];
} );
$.jmpress( 'applyStep', function ( step, eventData ) {
// read custom animation from elements
var substepsData = {};
var listOfSubsteps = [];
$( step )
.find( '[data-' + eventData.settings.customAnimationDataAttribute + ']' )
.each( function ( idx, element ) {
if ( $( element ).closest( eventData.settings.stepSelector ).is( step ) ) {
listOfSubsteps.push( { element: element } );
}
} );
if ( listOfSubsteps.length === 0 ) {
return;
}
$.each( listOfSubsteps, function ( idx, substep ) {
substep.info = parseSubstepInfo(
$( substep.element ).data( eventData.settings.customAnimationDataAttribute )
);
$( substep.element ).addClass( substep.info.willClass );
substep._on = [];
substep._after = null;
} );
var current = { _after: undefined, _on: [], info: {} }; // virtual zero step
$.each( listOfSubsteps, function ( idx, substep ) {
var other = substep.info.after;
if ( other ) {
if ( other === 'step' ) {
other = current;
} else if ( other === 'prev' ) {
other = listOfSubsteps[ idx - 1 ];
} else {
var index = find( listOfSubsteps, other, 0, idx - 1 );
if ( index === undefined ) {
index = find( listOfSubsteps, other );
}
other =
index === undefined || index === idx
? listOfSubsteps[ idx - 1 ]
: listOfSubsteps[ index ];
}
} else {
other = listOfSubsteps[ idx - 1 ];
}
if ( other ) {
if ( ! substep.info.delay ) {
if ( ! other._after ) {
other._after = substep;
return;
}
other = other._after;
}
other._on.push( { substep: substep, delay: substep.info.delay || 0 } );
}
} );
if ( current._after === undefined && current._on.length === 0 ) {
var startStep = find( listOfSubsteps, eventData.stepData.startSubstep ) || 0;
current._after = listOfSubsteps[ startStep ];
}
var substepsInOrder = [];
function findNextFunc( idx, item ) {
if ( item.substep._after ) {
current = item.substep._after;
return false;
}
}
do {
var substepList = [ { substep: current, delay: 0 } ];
addOn( substepList, current, 0 );
substepsInOrder.push( substepList );
current = null;
$.each( substepList, findNextFunc );
} while ( current );
substepsData.list = substepsInOrder;
$( step ).data( 'substepsData', substepsData );
} );
$.jmpress( 'unapplyStep', function ( step, eventData ) {
var substepsData = $( step ).data( 'substepsData' );
if ( substepsData ) {
$.each( substepsData.list, function ( idx, activeSubsteps ) {
$.each( activeSubsteps, function ( idx, substep ) {
if ( substep.substep.info.willClass ) {
$( substep.substep.element ).removeClass( substep.substep.info.willClass );
}
if ( substep.substep.info.hasClass ) {
$( substep.substep.element ).removeClass( substep.substep.info.hasClass );
}
if ( substep.substep.info.doClass ) {
$( substep.substep.element ).removeClass( substep.substep.info.doClass );
}
} );
} );
}
} );
$.jmpress( 'setActive', function ( step, eventData ) {
var substepsData = $( step ).data( 'substepsData' );
if ( ! substepsData ) {
return;
}
if ( eventData.substep === undefined ) {
eventData.substep = eventData.reason === 'prev' ? substepsData.list.length - 1 : 0;
}
var substep = eventData.substep;
$.each( eventData.current.animationTimeouts, function ( idx, timeout ) {
clearTimeout( timeout );
} );
eventData.current.animationTimeouts = [];
$.each( substepsData.list, function ( idx, activeSubsteps ) {
var applyHas = idx < substep;
var applyDo = idx <= substep;
$.each( activeSubsteps, function ( idx, substep ) {
if ( substep.substep.info.hasClass ) {
$( substep.substep.element )[ ( applyHas ? 'add' : 'remove' ) + 'Class' ](
substep.substep.info.hasClass
);
}
function applyIt() {
$( substep.substep.element ).addClass( substep.substep.info.doClass );
}
if ( applyDo && ! applyHas && substep.delay && eventData.reason !== 'prev' ) {
if ( substep.substep.info.doClass ) {
$( substep.substep.element ).removeClass( substep.substep.info.doClass );
eventData.current.animationTimeouts.push( setTimeout( applyIt, substep.delay ) );
}
} else {
if ( substep.substep.info.doClass ) {
$( substep.substep.element )[ ( applyDo ? 'add' : 'remove' ) + 'Class' ](
substep.substep.info.doClass
);
}
}
} );
} );
} );
$.jmpress( 'setInactive', function ( step, eventData ) {
if ( eventData.nextStep === step ) {
return;
}
function cleanupAnimation( substepsData ) {
$.each( substepsData.list, function ( idx, activeSubsteps ) {
$.each( activeSubsteps, function ( idx, substep ) {
if ( substep.substep.info.hasClass ) {
$( substep.substep.element ).removeClass( substep.substep.info.hasClass );
}
if ( substep.substep.info.doClass ) {
$( substep.substep.element ).removeClass( substep.substep.info.doClass );
}
} );
} );
}
$.each( eventData.current.animationCleanupWaiting, function ( idx, item ) {
cleanupAnimation( item );
} );
eventData.current.animationCleanupWaiting = [];
var substepsData = $( step ).data( 'substepsData' );
if ( substepsData ) {
eventData.current.animationCleanupWaiting.push( substepsData );
}
} );
$.jmpress( 'selectNext', function ( step, eventData ) {
if ( eventData.substep === undefined ) {
return;
}
var substepsData = $( step ).data( 'substepsData' );
if ( ! substepsData ) {
return;
}
if ( eventData.substep < substepsData.list.length - 1 ) {
return { step: step, substep: eventData.substep + 1 };
}
} );
$.jmpress( 'selectPrev', function ( step, eventData ) {
if ( eventData.substep === undefined ) {
return;
}
var substepsData = $( step ).data( 'substepsData' );
if ( ! substepsData ) {
return;
}
if ( eventData.substep > 0 ) {
return { step: step, substep: eventData.substep - 1 };
}
} );
} )( jQuery, document, window );
/*
* jmpress.toggle plugin
* For binding a key to toggle de/initialization of jmpress.js.
*/
/*!
* plugin for jmpress.js v0.4.5
*
* Copyright 2013 Kyle Robinson Young @shama & Tobias Koppers @sokra
* Licensed MIT
* http://www.opensource.org/licenses/mit-license.php
*/ ( function ( $, document, window, undefined ) {
'use strict';
$.jmpress( 'register', 'toggle', function ( key, config, initial ) {
var jmpress = this;
$( document ).bind( 'keydown', function ( event ) {
if ( event.keyCode === key ) {
if ( $( jmpress ).jmpress( 'initialized' ) ) {
$( jmpress ).jmpress( 'deinit' );
} else {
$( jmpress ).jmpress( config );
}
}
} );
if ( initial ) {
$( jmpress ).jmpress( config );
}
} );
} )( jQuery, document, window );
/*
* jmpress.secondary plugin
* Apply a secondary animation when step is selected.
*/
( function ( $, document, window, undefined ) {
'use strict';
$.jmpress( 'initStep', function ( step, eventData ) {
for ( var name in eventData.data ) {
if ( name.indexOf( 'secondary' ) === 0 ) {
eventData.stepData[ name ] = eventData.data[ name ];
}
}
} );
function exchangeIf( childStepData, condition, step ) {
if (
childStepData.secondary &&
childStepData.secondary.split( ' ' ).indexOf( condition ) !== -1
) {
for ( var name in childStepData ) {
if ( name.length > 9 && name.indexOf( 'secondary' ) === 0 ) {
var tmp = childStepData[ name ];
var normal = name.substr( 9 );
normal = normal.substr( 0, 1 ).toLowerCase() + normal.substr( 1 );
childStepData[ name ] = childStepData[ normal ];
childStepData[ normal ] = tmp;
}
}
$( this ).jmpress( 'reapply', $( step ) );
}
}
$.jmpress( 'beforeActive', function ( step, eventData ) {
exchangeIf.call( eventData.jmpress, $( step ).data( 'stepData' ), 'self', step );
var parent = $( step ).parent();
$( parent )
.children( eventData.settings.stepSelector )
.each( function ( idx, child ) {
var childStepData = $( child ).data( 'stepData' );
exchangeIf.call( eventData.jmpress, childStepData, 'siblings', child );
} );
function grandchildrenFunc( idx, child ) {
var childStepData = $( child ).data( 'stepData' );
exchangeIf.call( eventData.jmpress, childStepData, 'grandchildren', child );
}
for ( var i = 1; i < eventData.parents.length; i++ ) {
$( eventData.parents[ i ] ).children( eventData.settings.stepSelector ).each();
}
} );
$.jmpress( 'setInactive', function ( step, eventData ) {
exchangeIf.call( eventData.jmpress, $( step ).data( 'stepData' ), 'self', step );
var parent = $( step ).parent();
$( parent )
.children( eventData.settings.stepSelector )
.each( function ( idx, child ) {
var childStepData = $( child ).data( 'stepData' );
exchangeIf.call( eventData.jmpress, childStepData, 'siblings', child );
} );
function grandchildrenFunc( idx, child ) {
var childStepData = $( child ).data( 'stepData' );
exchangeIf.call( eventData.jmpress, childStepData, 'grandchildren', child );
}
for ( var i = 1; i < eventData.parents.length; i++ ) {
$( eventData.parents[ i ] )
.children( eventData.settings.stepSelector )
.each( grandchildrenFunc );
}
} );
} )( jQuery, document, window );
/*
* jmpress.duration plugin
* For auto advancing steps after a given duration and optionally displaying a
* progress bar.
*/
( function ( $, document, window, undefined ) {
'use strict';
$.jmpress( 'defaults' ).duration = {
defaultValue: -1,
defaultAction: 'next',
barSelector: undefined,
barProperty: 'width',
barPropertyStart: '0',
barPropertyEnd: '100%',
};
$.jmpress( 'initStep', function ( step, eventData ) {
eventData.stepData.duration =
eventData.data.duration && parseInt( eventData.data.duration, 10 );
eventData.stepData.durationAction = eventData.data.durationAction;
} );
$.jmpress( 'setInactive', function ( step, eventData ) {
var settings = eventData.settings,
durationSettings = settings.duration,
current = eventData.current;
var dur = eventData.stepData.duration || durationSettings.defaultValue;
if ( current.durationTimeout ) {
if ( durationSettings.barSelector ) {
var css = {
transitionProperty: durationSettings.barProperty,
transitionDuration: '0',
transitionDelay: '0',
transitionTimingFunction: 'linear',
};
css[ durationSettings.barProperty ] = durationSettings.barPropertyStart;
var bars = $( durationSettings.barSelector );
$.jmpress( 'css', bars, css );
bars.each( function ( idx, element ) {
var next = $( element ).next();
var parent = $( element ).parent();
$( element ).detach();
if ( next.length ) {
next.insertBefore( element );
} else {
parent.append( element );
}
} );
}
clearTimeout( current.durationTimeout );
delete current.durationTimeout;
}
} );
$.jmpress( 'setActive', function ( step, eventData ) {
var settings = eventData.settings,
durationSettings = settings.duration,
current = eventData.current;
var dur = eventData.stepData.duration || durationSettings.defaultValue;
if ( dur && dur > 0 ) {
if ( durationSettings.barSelector ) {
var css = {
transitionProperty: durationSettings.barProperty,
transitionDuration: dur - ( settings.transitionDuration * 2 ) / 3 - 100 + 'ms',
transitionDelay: ( settings.transitionDuration * 2 ) / 3 + 'ms',
transitionTimingFunction: 'linear',
};
css[ durationSettings.barProperty ] = durationSettings.barPropertyEnd;
$.jmpress( 'css', $( durationSettings.barSelector ), css );
}
var jmpress = this;
if ( current.durationTimeout ) {
clearTimeout( current.durationTimeout );
current.durationTimeout = undefined;
}
current.durationTimeout = setTimeout( function () {
var action = eventData.stepData.durationAction || durationSettings.defaultAction;
$( jmpress ).jmpress( action );
}, dur );
}
} );
} )( jQuery, document, window );
/*
* jmpress.presentation-mode plugin
* Display a window for the presenter with notes and a control and view of the
* presentation
*/
( function ( $, document, window, undefined ) {
'use strict';
var $jmpress = $.jmpress;
var PREFIX = 'jmpress-presentation-';
/* FUNCTIONS */
function randomString() {
return '' + Math.round( Math.random() * 100000, 0 );
}
/* DEFAULTS */
$jmpress( 'defaults' ).presentationMode = {
use: true,
url: 'presentation-screen.html',
notesUrl: false,
transferredValues: [ 'userZoom', 'userTranslateX', 'userTranslateY' ],
};
$jmpress( 'defaults' ).keyboard.keys[ 80 ] = 'presentationPopup'; // p key
/* HOOKS */
$jmpress( 'afterInit', function ( nil, eventData ) {
var current = eventData.current;
current.selectMessageListeners = [];
if ( eventData.settings.presentationMode.use ) {
window.addEventListener( 'message', function ( event ) {
// We do not test orgin, because we want to accept messages
// from all orgins
try {
if ( typeof event.data !== 'string' || event.data.indexOf( PREFIX ) !== 0 ) {
return;
}
var json = JSON.parse( event.data.slice( PREFIX.length ) );
switch ( json.type ) {
case 'select':
$.each( eventData.settings.presentationMode.transferredValues, function (
idx,
name
) {
eventData.current[ name ] = json[ name ];
} );
if (
/[a-z0-9\-]+/i.test( json.targetId ) &&
typeof json.substep in { number: 1, undefined: 1 }
) {
$( eventData.jmpress ).jmpress(
'select',
{ step: '#' + json.targetId, substep: json.substep },
json.reason
);
} else {
$.error(
'For security reasons the targetId must match /[a-z0-9\\-]+/i and substep must be a number.'
);
}
break;
case 'listen':
current.selectMessageListeners.push( event.source );
break;
case 'ok':
clearTimeout( current.presentationPopupTimeout );
break;
case 'read':
try {
event.source.postMessage(
PREFIX +
JSON.stringify( {
type: 'url',
url: window.location.href,
notesUrl: eventData.settings.presentationMode.notesUrl,
} ),
'*'
);
} catch ( e ) {
$.error( 'Cannot post message to source: ' + e );
}
break;
default:
throw 'Unknown message type: ' + json.type;
}
} catch ( e ) {
$.error( 'Received message is malformed: ' + e );
}
} );
try {
if ( window.parent && window.parent !== window ) {
window.parent.postMessage(
PREFIX +
JSON.stringify( {
type: 'afterInit',
} ),
'*'
);
}
} catch ( e ) {
$.error( 'Cannot post message to parent: ' + e );
}
}
} );
$jmpress( 'afterDeinit', function ( nil, eventData ) {
if ( eventData.settings.presentationMode.use ) {
try {
if ( window.parent && window.parent !== window ) {
window.parent.postMessage(
PREFIX +
JSON.stringify( {
type: 'afterDeinit',
} ),
'*'
);
}
} catch ( e ) {
$.error( 'Cannot post message to parent: ' + e );
}
}
} );
$jmpress( 'setActive', function ( step, eventData ) {
var stepId = $( eventData.delegatedFrom ).attr( 'id' ),
substep = eventData.substep,
reason = eventData.reason;
$.each( eventData.current.selectMessageListeners, function ( idx, listener ) {
try {
var msg = {
type: 'select',
targetId: stepId,
substep: substep,
reason: reason,
};
$.each( eventData.settings.presentationMode.transferredValues, function ( idx, name ) {
msg[ name ] = eventData.current[ name ];
} );
listener.postMessage( PREFIX + JSON.stringify( msg ), '*' );
} catch ( e ) {
$.error( 'Cannot post message to listener: ' + e );
}
} );
} );
$jmpress( 'register', 'presentationPopup', function () {
function trySend() {
jmpress.jmpress( 'current' ).presentationPopupTimeout = setTimeout( trySend, 100 );
try {
popup.postMessage(
PREFIX +
JSON.stringify( {
type: 'url',
url: window.location.href,
notesUrl: jmpress.jmpress( 'settings' ).presentationMode.notesUrl,
} ),
'*'
);
} catch ( e ) {}
}
var jmpress = $( this ),
popup;
if ( jmpress.jmpress( 'settings' ).presentationMode.use ) {
popup = window.open( $( this ).jmpress( 'settings' ).presentationMode.url );
jmpress.jmpress( 'current' ).presentationPopupTimeout = setTimeout( trySend, 100 );
}
} );
} )( jQuery, document, window );