Team:UNITN-Trento/JS/Library/jmslideshow
From 2013.igem.org
(Created page with "/*! * jquery.scrollto.js 0.0.1 - https://github.com/yckart/jquery.scrollto.js * Scroll smooth to any element in your DOM. * * Copyright (c) 2012 Yannick Albert (http://yckart...") |
|||
Line 106: | Line 106: | ||
})(); | })(); | ||
} | } | ||
+ | |||
+ | |||
+ | /*! | ||
+ | * jmpress.js v0.4.0 | ||
+ | * http://shama.github.com/jmpress.js | ||
+ | * | ||
+ | * A jQuery plugin to build a website on the infinite canvas. | ||
+ | * | ||
+ | * Copyright 2012 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 + ","; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Default Settings | ||
+ | */ | ||
+ | var defaults = { | ||
+ | /* CLASSES */ | ||
+ | stepSelector: '.step' | ||
+ | ,containerClass: '' | ||
+ | ,canvasClass: '' | ||
+ | ,areaClass: '' | ||
+ | ,notSupportedClass: 'not-supported' | ||
+ | ,loadedClass: 'loaded' | ||
+ | |||
+ | /* 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 | ||
+ | |||
+ | /* TEST */ | ||
+ | ,test: false | ||
+ | }; | ||
+ | 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 | ||
+ | ,'loadStep': 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; | ||
+ | } | ||
+ | /** | ||
+ | * Load Siblings | ||
+ | * | ||
+ | * @access protected | ||
+ | * @return void | ||
+ | */ | ||
+ | function loadSiblings() { | ||
+ | if (!active) { | ||
+ | return; | ||
+ | } | ||
+ | var siblings = $(active).near( settings.stepSelector ) | ||
+ | .add( $(active).near( settings.stepSelector, true) ) | ||
+ | .add( callCallback.call(this, 'selectPrev', active, { | ||
+ | stepData: $(active).data('stepData') | ||
+ | })) | ||
+ | .add( callCallback.call(this, 'selectNext', active, { | ||
+ | stepData: $(active).data('stepData') | ||
+ | })); | ||
+ | siblings.each(function() { | ||
+ | var step = this; | ||
+ | if ($(step).hasClass( settings.loadedClass )) { | ||
+ | return; | ||
+ | } | ||
+ | setTimeout(function() { | ||
+ | if ($(step).hasClass( settings.loadedClass )) { | ||
+ | return; | ||
+ | } | ||
+ | callCallback.call(jmpress, 'loadStep', step, { | ||
+ | stepData: $(step).data('stepData') | ||
+ | }); | ||
+ | $(step).addClass( settings.loadedClass ); | ||
+ | }, settings.transitionDuration - 100); | ||
+ | }); | ||
+ | if ($(active).hasClass( settings.loadedClass )) { | ||
+ | return; | ||
+ | } | ||
+ | callCallback.call(jmpress, 'loadStep', active, { | ||
+ | stepData: $(active).data('stepData') | ||
+ | }); | ||
+ | $(active).addClass( settings.loadedClass ); | ||
+ | } | ||
+ | /** | ||
+ | * | ||
+ | */ | ||
+ | function getStepParents( el ) { | ||
+ | var parents = []; | ||
+ | var currentEl = el; | ||
+ | while($(currentEl).parent().length && | ||
+ | $(currentEl).parent().is(settings.stepSelector)) { | ||
+ | currentEl = $(currentEl).parent(); | ||
+ | parents.push(currentEl[0]); | ||
+ | } | ||
+ | return parents; | ||
+ | } | ||
+ | /** | ||
+ | * 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; | ||
+ | } | ||
+ | |||
+ | // Sometimes it's possible to trigger focus on first link with some keyboard action. | ||
+ | // Browser in such a case tries to scroll the page to make this element visible | ||
+ | // (even that body overflow is set to hidden) and it breaks our careful positioning. | ||
+ | // | ||
+ | // So, as a lousy (and lazy) workaround we will make the page scroll back to the top | ||
+ | // whenever slide is selected | ||
+ | // | ||
+ | // If you are reading this and know any better way to handle it, I'll be glad to hear about it! | ||
+ | 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 = $(el).parentsUntil(jmpress).filter(settings.stepSelector).filter(step.delegate) || | ||
+ | $(el).near(step.delegate) || | ||
+ | $(el).near(step.delegate, true) || | ||
+ | $(step.delegate, jmpress); | ||
+ | step = delegated.data("stepData"); | ||
+ | } | ||
+ | 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", active, $.extend({ | ||
+ | canvas: canvas | ||
+ | ,area: area | ||
+ | }, callbackData)); | ||
+ | |||
+ | active = el; | ||
+ | activeSubstep = callbackData.substep; | ||
+ | activeDelegated = delegated; | ||
+ | |||
+ | loadSiblings.call(this); | ||
+ | |||
+ | return delegated; | ||
+ | } | ||
+ | /** | ||
+ | * This should fix ANY kind of buggy scrolling | ||
+ | */ | ||
+ | function scrollFix() { | ||
+ | function fix() { | ||
+ | if ($(container)[0].tagName === "BODY") { | ||
+ | window.scrollTo(0, 0); | ||
+ | } | ||
+ | $(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); | ||
+ | } | ||
+ | fix(); | ||
+ | } | ||
+ | /** | ||
+ | * 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 { | ||
+ | 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 | ||
+ | ,deinit: deinit | ||
+ | ,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(); | ||
+ | var supported = ( ua.search(/(iphone)|(ipod)|(android)/) === -1 ); | ||
+ | return supported; | ||
+ | } | ||
+ | |||
+ | // 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 ); | ||
+ | }); | ||
+ | |||
+ | 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] ) { | ||
+ | if ( method.substr(0, 1) === '_' && jmpressmethods.settings().test === false) { | ||
+ | $.error( 'Method ' + method + ' is protected and should only be used internally.' ); | ||
+ | } else { | ||
+ | return jmpressmethods[method].apply( this, Array.prototype.slice.call( arguments, 1 )); | ||
+ | } | ||
+ | } else if ( methods[method] ) { | ||
+ | if ( method.substr(0, 1) === '_' && defaults.test === false) { | ||
+ | $.error( 'Method ' + method + ' is protected and should only be used internally.' ); | ||
+ | } else { | ||
+ | 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] ) { | ||
+ | if ( method.substr(0, 1) === '_' && defaults.test === false) { | ||
+ | $.error( 'Method ' + method + ' is protected and should only be used internally.' ); | ||
+ | } else { | ||
+ | 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 randomString() { | ||
+ | return "" + Math.round(Math.random() * 100000, 0); | ||
+ | } | ||
+ | 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 (!active) { | ||
+ | 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 (!active) { | ||
+ | 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"](); | ||
+ | } | ||
+ | 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 randomString() { | ||
+ | return "" + Math.round(Math.random() * 100000, 0); | ||
+ | } | ||
+ | // TODO allow call of route after init | ||
+ | function routeFunc( jmpress, route, type ) { | ||
+ | for(var i = 0; i < route.length - 1; i++) { | ||
+ | var from = route[i]; | ||
+ | var to = route[i+1]; | ||
+ | $(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(step, eventData, "next"); | ||
+ | }); | ||
+ | $jmpress( 'selectPrev', function( step, eventData ) { | ||
+ | return selectPrevOrNext(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 = 'afterStepLoaded'; | ||
+ | |||
+ | /* FUNCTIONS */ | ||
+ | function randomString() { | ||
+ | return "" + Math.round(Math.random() * 100000, 0); | ||
+ | } | ||
+ | |||
+ | /* REGISTER EVENTS */ | ||
+ | $jmpress('register', afterStepLoaded); | ||
+ | |||
+ | /* HOOKS */ | ||
+ | $jmpress('initStep', function( step, eventData ) { | ||
+ | eventData.stepData.src = $(step).attr('href') || eventData.data.src || false; | ||
+ | }); | ||
+ | $jmpress('loadStep', function( step, eventData ) { | ||
+ | var stepData = eventData.stepData, | ||
+ | href = stepData && stepData.src; | ||
+ | if ( href ) { | ||
+ | $(step).load(href, function(response, status, xhr) { | ||
+ | $(eventData.jmpress).jmpress('fire', afterStepLoaded, step, $.extend({}, eventData, { | ||
+ | response: response | ||
+ | ,status: status | ||
+ | ,xhr: xhr | ||
+ | })); | ||
+ | }); | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | }(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) {} | ||
+ | } | ||
+ | |||
+ | /* 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() { | ||
+ | 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); | ||
+ | } | ||
+ | var shouldBeHash = "#/" + urlItem.attr("id"); | ||
+ | if(window.location.hash !== shouldBeHash) { | ||
+ | window.location.hash = shouldBeHash; | ||
+ | } | ||
+ | } | ||
+ | }); | ||
+ | $(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() { | ||
+ | window.location.hash = "#/" + $(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 recieve 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 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, function( event, delta ) { | ||
+ | delta = delta || event.originalEvent.wheelDelta; | ||
+ | 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); | ||
+ | } | ||
+ | }); | ||
+ | } | ||
+ | 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 ) { | ||
+ | $(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.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 randomString() { | ||
+ | return "" + Math.round(Math.random() * 100000, 0); | ||
+ | } | ||
+ | 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 | ||
+ | */ | ||
+ | (function( $, document, window, undefined ) { | ||
+ | |||
+ | 'use strict'; | ||
+ | |||
+ | /* FUNCTIONS */ | ||
+ | function randomString() { | ||
+ | return "" + Math.round(Math.random() * 100000, 0); | ||
+ | } | ||
+ | |||
+ | /* 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 randomString() { | ||
+ | return "" + Math.round(Math.random() * 100000, 0); | ||
+ | } | ||
+ | |||
+ | 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(" "); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | 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 === -1) { | ||
+ | index = find(listOfSubsteps, other); | ||
+ | } | ||
+ | other = (index === -1 || 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)); |
Revision as of 15:48, 8 August 2013
/*!
* jquery.scrollto.js 0.0.1 - https://github.com/yckart/jquery.scrollto.js * Scroll smooth to any element in your DOM. * * Copyright (c) 2012 Yannick Albert (http://yckart.com) * Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php). * 2013/02/17 **/
$.scrollTo = $.fn.scrollTo = function(x, y, options){
if (!(this instanceof $)) return $.fn.scrollTo.apply($('html, body'), arguments);
options = $.extend({}, { gap: { x: 0, y: 0 }, animation: { easing: 'swing', duration: 600, complete: $.noop, step: $.noop } }, options);
return this.each(function(){ var elem = $(this); elem.stop().animate({ scrollLeft: !isNaN(Number(x)) ? x : $(y).offset().left + options.gap.x, scrollTop: !isNaN(Number(y)) ? y : $(y).offset().top + options.gap.y }, options.animation); });
};
/**
* onHashChange (oHch) event handler * @version onHashChange 1.0 * @copyright Copyright (c) 2010-2012, Gabriele Girelli * @author ggirelli <g.girelli@studenti.unitn.it> */
function oHch(event) {
var getHashValue = function() { var hasValue = window.location.hash.split("#")[1]; if (typeof hasValue == "undefined") { return false; } return hasValue; }
var lastHash = getHashValue();
(function listenHash() { var hash = getHashValue();
if (hash !== lastHash) { event(); lastHash = hash; }
var t = setTimeout(listenHash, 100);
})();
}
/**
* onTimeChange (oTch) event handler * @version onTimeChange 1.0 * @copyright Copyright (c) 2010-2012, Gabriele Girelli * @author ggirelli <g.girelli@studenti.unitn.it> */
function oTch(event) {
var getTimeValue = function() { var d = new Date(); return d.getTime(); } var lastTime = getTimeValue(); (function listenTime() { var time = getTimeValue(); if (time !== lastTime) { event(); lastTime = time; }
var t = setTimeout(listenTime, 100);
})();
}
/**
* onWidthChange (oWri) event handler * @version onWidthChange 1.0 * @copyright Copyright (c) 2010-2012, Gabriele Girelli * @author ggirelli <g.girelli@studenti.unitn.it> */
function oWch(event) {
var getWidthValue = function() { return window.innerWidth; } var lastWidth = getWidthValue(); (function listenWidth() { var width = getWidthValue(); if (width !== lastWidth) { event(); lastWidth = width; }
var w = setTimeout(listenWidth, 100);
})();
}
/*!
* jmpress.js v0.4.0 * http://shama.github.com/jmpress.js * * A jQuery plugin to build a website on the infinite canvas. * * Copyright 2012 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 + ","; }
/** * Default Settings */ var defaults = { /* CLASSES */ stepSelector: '.step' ,containerClass: ,canvasClass: ,areaClass: ,notSupportedClass: 'not-supported' ,loadedClass: 'loaded'
/* 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
/* TEST */ ,test: false }; 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 ,'loadStep': 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; } /** * Load Siblings * * @access protected * @return void */ function loadSiblings() { if (!active) { return; } var siblings = $(active).near( settings.stepSelector ) .add( $(active).near( settings.stepSelector, true) ) .add( callCallback.call(this, 'selectPrev', active, { stepData: $(active).data('stepData') })) .add( callCallback.call(this, 'selectNext', active, { stepData: $(active).data('stepData') })); siblings.each(function() { var step = this; if ($(step).hasClass( settings.loadedClass )) { return; } setTimeout(function() { if ($(step).hasClass( settings.loadedClass )) { return; } callCallback.call(jmpress, 'loadStep', step, { stepData: $(step).data('stepData') }); $(step).addClass( settings.loadedClass ); }, settings.transitionDuration - 100); }); if ($(active).hasClass( settings.loadedClass )) { return; } callCallback.call(jmpress, 'loadStep', active, { stepData: $(active).data('stepData') }); $(active).addClass( settings.loadedClass ); } /** * */ function getStepParents( el ) { var parents = []; var currentEl = el; while($(currentEl).parent().length && $(currentEl).parent().is(settings.stepSelector)) { currentEl = $(currentEl).parent(); parents.push(currentEl[0]); } return parents; } /** * 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; }
// Sometimes it's possible to trigger focus on first link with some keyboard action. // Browser in such a case tries to scroll the page to make this element visible // (even that body overflow is set to hidden) and it breaks our careful positioning. // // So, as a lousy (and lazy) workaround we will make the page scroll back to the top // whenever slide is selected // // If you are reading this and know any better way to handle it, I'll be glad to hear about it! 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 = $(el).parentsUntil(jmpress).filter(settings.stepSelector).filter(step.delegate) || $(el).near(step.delegate) || $(el).near(step.delegate, true) || $(step.delegate, jmpress); step = delegated.data("stepData"); } 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", active, $.extend({ canvas: canvas ,area: area }, callbackData));
active = el; activeSubstep = callbackData.substep; activeDelegated = delegated;
loadSiblings.call(this);
return delegated; } /** * This should fix ANY kind of buggy scrolling */ function scrollFix() { function fix() { if ($(container)[0].tagName === "BODY") { window.scrollTo(0, 0); } $(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); } fix(); } /** * 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 { 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 ,deinit: deinit ,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(); var supported = ( ua.search(/(iphone)|(ipod)|(android)/) === -1 ); return supported; }
// 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 ); });
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] ) { if ( method.substr(0, 1) === '_' && jmpressmethods.settings().test === false) { $.error( 'Method ' + method + ' is protected and should only be used internally.' ); } else { return jmpressmethods[method].apply( this, Array.prototype.slice.call( arguments, 1 )); } } else if ( methods[method] ) { if ( method.substr(0, 1) === '_' && defaults.test === false) { $.error( 'Method ' + method + ' is protected and should only be used internally.' ); } else { 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] ) { if ( method.substr(0, 1) === '_' && defaults.test === false) { $.error( 'Method ' + method + ' is protected and should only be used internally.' ); } else { 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 randomString() { return "" + Math.round(Math.random() * 100000, 0); } 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 (!active) { 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 (!active) { 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"](); } 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 randomString() { return "" + Math.round(Math.random() * 100000, 0); } // TODO allow call of route after init function routeFunc( jmpress, route, type ) { for(var i = 0; i < route.length - 1; i++) { var from = route[i]; var to = route[i+1]; $(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(step, eventData, "next"); }); $jmpress( 'selectPrev', function( step, eventData ) { return selectPrevOrNext(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 = 'afterStepLoaded';
/* FUNCTIONS */ function randomString() { return "" + Math.round(Math.random() * 100000, 0); }
/* REGISTER EVENTS */ $jmpress('register', afterStepLoaded);
/* HOOKS */ $jmpress('initStep', function( step, eventData ) { eventData.stepData.src = $(step).attr('href') || eventData.data.src || false; }); $jmpress('loadStep', function( step, eventData ) { var stepData = eventData.stepData, href = stepData && stepData.src; if ( href ) { $(step).load(href, function(response, status, xhr) { $(eventData.jmpress).jmpress('fire', afterStepLoaded, step, $.extend({}, eventData, { response: response ,status: status ,xhr: xhr })); }); } });
}(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) {} }
/* 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() { 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); } var shouldBeHash = "#/" + urlItem.attr("id"); if(window.location.hash !== shouldBeHash) { window.location.hash = shouldBeHash; } } }); $(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() { window.location.hash = "#/" + $(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 recieve 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 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, function( event, delta ) { delta = delta || event.originalEvent.wheelDelta; 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); } }); } 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 ) { $(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.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 randomString() { return "" + Math.round(Math.random() * 100000, 0); } 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 */
(function( $, document, window, undefined ) {
'use strict';
/* FUNCTIONS */ function randomString() { return "" + Math.round(Math.random() * 100000, 0); }
/* 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 randomString() { return "" + Math.round(Math.random() * 100000, 0); }
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(" "); } } } 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 === -1) { index = find(listOfSubsteps, other); } other = (index === -1 || 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));