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


<%= link_to image_tag(n.picture_robust.big_thumb),n.picture_robust.try(:url) %> +

+
+ <% end %> +
+

+ <%= raw(n.title) %> <%#= link_to fa_icon("edit"), edit_rubrik_neuigkeit_path(n.rubrik, n), remote: true if can? :edit, n %> +

+
+ <%= raw(n.text) %> + <% if n.has_calentries? %> +
+ <%= fa_icon("calendar 2x") %> + <%= n.relevant_calentry.text %> +
+<% end %> +
+
+
+ + + + +
+<% i = i+1 %> +<% end %> +
+ + + diff --git a/app/views/neuigkeiten/_neuigkeit_view.html.erb b/app/views/neuigkeiten/_neuigkeit_view.html.erb index f29e612..3d9a78e 100644 --- a/app/views/neuigkeiten/_neuigkeit_view.html.erb +++ b/app/views/neuigkeiten/_neuigkeit_view.html.erb @@ -16,7 +16,7 @@ <% end %>
<% unless neuigkeit_view.published? %> -
Not Published <%= link_to "Publish", publish_rubrik_neuigkeit_path(@neuigkeit.rubrik,@neuigkeit),remote:true, class: :btn if can? :publish, neuigkeit_view %>
+
Not Published <%= link_to "Publish", publish_rubrik_neuigkeit_path(neuigkeit_view.rubrik,neuigkeit_view),remote:true, class: :btn if can? :publish, neuigkeit_view %>
<% end %> <% unless neuigkeit_view.origurl.nil? || neuigkeit_view.origurl.empty? %>
<%= link_to "Zitiert von "+ neuigkeit_view.origurl, neuigkeit_view.origurl %> diff --git a/app/views/themes/blue2/neuigkeiten/_menu.html.erb b/app/views/themes/blue2/neuigkeiten/_menu.html.erb index a0b1f9d..0b666b5 100644 --- a/app/views/themes/blue2/neuigkeiten/_menu.html.erb +++ b/app/views/themes/blue2/neuigkeiten/_menu.html.erb @@ -1,40 +1,40 @@
- <%= link_to(fa_icon("edit"), edit_rubrik_neuigkeit_path( @neuigkeit.rubrik, @neuigkeit), remote: true , class: "btn", title: "Edit") if can? :edit, @neuigkeit + <%= link_to(fa_icon("edit"), edit_rubrik_neuigkeit_path( menu.rubrik, menu), remote: true , class: "btn", title: "Edit") if can? :edit, menu %> - <%= link_to(fa_icon("paperclip"), "#", class: "btn", id: "attachments-form-open", title: "Attachments") if can? :edit, @neuigkeit + <%= link_to(fa_icon("paperclip"), "#", class: "btn", id: "attachments-form-open", title: "Attachments") if can? :edit, menu %> - <%= link_to(fa_icon("calendar-o"), new_calentry_path(:object_id=>@neuigkeit.id, :object_type=>"Neuigkeit"), :remote=>true, class: :btn) if can? :edit, @neuigkeit + <%= link_to(fa_icon("calendar-o"), new_calentry_path(:object_id=>menu.id, :object_type=>"Neuigkeit"), :remote=>true, class: :btn) if can? :edit, menu %> - <%= link_to(fa_icon("link") , "#", id: "findlink-open",title: "Neue Verknüpfung" , class: "btn") if can? :find_link, @neuigkeit + <%= link_to(fa_icon("link") , "#", id: "findlink-open",title: "Neue Verknüpfung" , class: "btn") if can? :find_link, menu %> - <%= new_question_for(@neuigkeit, fa_icon("question")) if can? :new, Survey::Question %> + <%= new_question_for(menu, fa_icon("question")) if can? :new, Survey::Question %> - <%= link_to(fa_icon("globe"), publish_rubrik_neuigkeit_path(@neuigkeit.rubrik,@neuigkeit), title: I18n.t('neuigkeit.publish'), class: "btn") if can?(:publish, @neuigkeit) && !@neuigkeit.published? + <%= link_to(fa_icon("globe"), publish_rubrik_neuigkeit_path(menu.rubrik,menu), title: I18n.t('neuigkeit.publish'), class: "btn") if can?(:publish, menu) && !menu.published? %> - <%= link_to(fa_stack("globe","ban"), unpublish_rubrik_neuigkeit_path(@neuigkeit.rubrik,@neuigkeit), :remote=>true, class: "btn stacked") if can?(:unpublish, @neuigkeit) && @neuigkeit.published? + <%= link_to(fa_stack("globe","ban"), unpublish_rubrik_neuigkeit_path(menu.rubrik,menu), :remote=>true, class: "btn stacked") if can?(:unpublish, menu) && menu.published? %> - <% if @neuigkeit.published? %> + <% if menu.published? %> <% end %>
- <%= render_new_attachments_for(@neuigkeit, fa_icon("plus"), {class: "btn"}) %> + <%= render_new_attachments_for(menu, fa_icon("plus"), {class: "btn"}) %>