From 10632c2ebc6f0096a0cda214a60c5a8a8560f445 Mon Sep 17 00:00:00 2001 From: Henrik Ingo Date: Sat, 21 Oct 2017 13:22:19 +0300 Subject: [PATCH] Add rel goto and skip plugins to build.js too --- build.js | 7 +- js/impress.js | 385 +++++++++++++++++++++++++++++++++++++++++ src/plugins/rel/rel.js | 22 ++- 3 files changed, 404 insertions(+), 10 deletions(-) diff --git a/build.js b/build.js index b359316..2f39eef 100644 --- a/build.js +++ b/build.js @@ -6,8 +6,11 @@ buildify() .concat(['src/lib/gc.js']) .concat(['src/lib/util.js']) // Plugins from src/plugins - .concat(['src/plugins/navigation/navigation.js', - 'src/plugins/resize/resize.js']) + .concat(['src/plugins/goto/goto.js', + 'src/plugins/navigation/navigation.js', + 'src/plugins/rel/rel.js', + 'src/plugins/resize/resize.js', + 'src/plugins/stop/stop.js']) .save('js/impress.js'); /* * Disabled until uglify supports ES6: https://github.com/mishoo/UglifyJS2/issues/448 diff --git a/js/impress.js b/js/impress.js index 79ed495..09f34d9 100644 --- a/js/impress.js +++ b/js/impress.js @@ -1110,6 +1110,181 @@ } )( document, window ); +/** + * Goto Plugin + * + * The goto plugin is a pre-stepleave plugin. It is executed before impress:stepleave, + * and will alter the destination where to transition next. + * + * Example: + * + * + *
+ * + * + *
+ * + * + *
+ * + * See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values for a table + * of what strings to use for each key. + * + * Copyright 2016-2017 Henrik Ingo (@henrikingo) + * Released under the MIT license. + */ +/* global window, document, impress */ + +( function( document, window ) { + "use strict"; + var lib; + + var isNumber = function( numeric ) { + return !isNaN( numeric ); + }; + + var goto = function( event ) { + if ( ( !event ) || ( !event.target ) ) { + return; + } + + var data = event.target.dataset; + var steps = document.querySelectorAll( ".step" ); + + // Data-goto-key-list="" & data-goto-next-list="" ////////////////////////////////////////// + if ( data.gotoKeyList !== undefined && + data.gotoNextList !== undefined && + event.origEvent !== undefined && + event.origEvent.key !== undefined ) { + var keylist = data.gotoKeyList.split( " " ); + var nextlist = data.gotoNextList.split( " " ); + + if ( keylist.length !== nextlist.length ) { + window.console.log( + "impress goto plugin: data-goto-key-list and data-goto-next-list don't match:" + ); + window.console.log( keylist ); + window.console.log( nextlist ); + + // Don't return, allow the other categories to work despite this error + } else { + var index = keylist.indexOf( event.origEvent.key ); + if ( index >= 0 ) { + var next = nextlist[ index ]; + if ( isNumber( next ) ) { + event.detail.next = steps[ next ]; + + // If the new next element has its own transitionDuration, we're responsible + // for setting that on the event as well + event.detail.transitionDuration = lib.util.toNumber( + event.detail.next.dataset.transitionDuration, + event.detail.transitionDuration + ); + return; + } else { + var newTarget = document.getElementById( next ); + if ( newTarget && newTarget.classList.contains( "step" ) ) { + event.detail.next = newTarget; + event.detail.transitionDuration = lib.util.toNumber( + event.detail.next.dataset.transitionDuration, + event.detail.transitionDuration + ); + return; + } else { + window.console.log( "impress goto plugin: " + next + + " is not a step in this impress presentation." ); + } + } + } + } + } + + // Data-goto-next="" & data-goto-prev="" /////////////////////////////////////////////////// + + // Handle event.target data-goto-next attribute + if ( isNumber( data.gotoNext ) && event.detail.reason === "next" ) { + event.detail.next = steps[ data.gotoNext ]; + + // If the new next element has its own transitionDuration, we're responsible for setting + // that on the event as well + event.detail.transitionDuration = lib.util.toNumber( + event.detail.next.dataset.transitionDuration, event.detail.transitionDuration + ); + return; + } + if ( data.gotoNext && event.detail.reason === "next" ) { + var newTarget = document.getElementById( data.gotoNext ); // jshint ignore:line + if ( newTarget && newTarget.classList.contains( "step" ) ) { + event.detail.next = newTarget; + event.detail.transitionDuration = lib.util.toNumber( + event.detail.next.dataset.transitionDuration, + event.detail.transitionDuration + ); + return; + } else { + window.console.log( "impress goto plugin: " + data.gotoNext + + " is not a step in this impress presentation." ); + } + } + + // Handle event.target data-goto-prev attribute + if ( isNumber( data.gotoPrev ) && event.detail.reason === "prev" ) { + event.detail.next = steps[ data.gotoPrev ]; + event.detail.transitionDuration = lib.util.toNumber( + event.detail.next.dataset.transitionDuration, event.detail.transitionDuration + ); + return; + } + if ( data.gotoPrev && event.detail.reason === "prev" ) { + var newTarget = document.getElementById( data.gotoPrev ); // jshint ignore:line + if ( newTarget && newTarget.classList.contains( "step" ) ) { + event.detail.next = newTarget; + event.detail.transitionDuration = lib.util.toNumber( + event.detail.next.dataset.transitionDuration, event.detail.transitionDuration + ); + return; + } else { + window.console.log( "impress goto plugin: " + data.gotoPrev + + " is not a step in this impress presentation." ); + } + } + + // Data-goto="" /////////////////////////////////////////////////////////////////////////// + + // Handle event.target data-goto attribute + if ( isNumber( data.goto ) ) { + event.detail.next = steps[ data.goto ]; + event.detail.transitionDuration = lib.util.toNumber( + event.detail.next.dataset.transitionDuration, event.detail.transitionDuration + ); + return; + } + if ( data.goto ) { + var newTarget = document.getElementById( data.goto ); // jshint ignore:line + if ( newTarget && newTarget.classList.contains( "step" ) ) { + event.detail.next = newTarget; + event.detail.transitionDuration = lib.util.toNumber( + event.detail.next.dataset.transitionDuration, event.detail.transitionDuration + ); + return; + } else { + window.console.log( "impress goto plugin: " + data.goto + + " is not a step in this impress presentation." ); + } + } + }; + + // Register the plugin to be called in pre-stepleave phase + impress.addPreStepLeavePlugin( goto ); + +} )( document, window ); + + /** * Navigation events plugin * @@ -1282,6 +1457,180 @@ } )( document ); +/** + * Relative Positioning Plugin + * + * This plugin provides support for defining the coordinates of a step relative + * to the previous step. This is often more convenient when creating presentations, + * since as you add, remove or move steps, you may not need to edit the positions + * as much as is the case with the absolute coordinates supported by impress.js + * core. + * + * Example: + * + * + *
+ * + * Following html attributes are supported for step elements: + * + * data-rel-x + * data-rel-y + * data-rel-z + * + * These values are also inherited from the previous step. This makes it easy to + * create a boring presentation where each slide shifts for example 1000px down + * from the previous. + * + * In addition to plain numbers, which are pixel values, it is also possible to + * define relative positions as a multiple of screen height and width, using + * a unit of "h" and "w", respectively, appended to the number. + * + * Example: + * + *
+ * + * This plugin is a *pre-init plugin*. It is called synchronously from impress.js + * core at the beginning of `impress().init()`. This allows it to process its own + * data attributes first, and possibly alter the data-x, data-y and data-z attributes + * that will then be processed by `impress().init()`. + * + * (Another name for this kind of plugin might be called a *filter plugin*, but + * *pre-init plugin* is more generic, as a plugin might do whatever it wants in + * the pre-init stage.) + * + * Copyright 2016 Henrik Ingo (@henrikingo) + * Released under the MIT license. + */ + +/* global document, window */ + +( function( document, window ) { + "use strict"; + + var startingState = {}; + + /** + * Copied from core impress.js. We currently lack a library mechanism to + * to share utility functions like this. + */ + var toNumber = function( numeric, fallback ) { + return isNaN( numeric ) ? ( fallback || 0 ) : Number( numeric ); + }; + + /** + * Extends toNumber() to correctly compute also relative-to-screen-size values 5w and 5h. + * + * Returns the computed value in pixels with w/h postfix removed. + */ + var toNumberAdvanced = function( numeric, fallback ) { + if ( typeof numeric !== "string" ) { + return toNumber( numeric, fallback ); + } + var ratio = numeric.match( /^([+-]*[\d\.]+)([wh])$/ ); + if ( ratio == null ) { + return toNumber( numeric, fallback ); + } else { + var value = parseFloat( ratio[ 1 ] ); + var multiplier = ratio[ 2 ] === "w" ? window.innerWidth : window.innerHeight; + return value * multiplier; + } + }; + + var computeRelativePositions = function( el, prev ) { + var data = el.dataset; + + if ( !prev ) { + + // For the first step, inherit these defaults + prev = { x:0, y:0, z:0, relative: { x:0, y:0, z:0 } }; + } + + var step = { + x: toNumber( data.x, prev.x ), + y: toNumber( data.y, prev.y ), + z: toNumber( data.z, prev.z ), + relative: { + x: toNumberAdvanced( data.relX, prev.relative.x ), + y: toNumberAdvanced( data.relY, prev.relative.y ), + z: toNumberAdvanced( data.relZ, prev.relative.z ) + } + }; + + // Relative position is ignored/zero if absolute is given. + // Note that this also has the effect of resetting any inherited relative values. + if ( data.x !== undefined ) { + step.relative.x = 0; + } + if ( data.y !== undefined ) { + step.relative.y = 0; + } + if ( data.z !== undefined ) { + step.relative.z = 0; + } + + // Apply relative position to absolute position, if non-zero + // Note that at this point, the relative values contain a number value of pixels. + step.x = step.x + step.relative.x; + step.y = step.y + step.relative.y; + step.z = step.z + step.relative.z; + + return step; + }; + + var rel = function( root ) { + var steps = root.querySelectorAll( ".step" ); + var prev; + startingState[ root.id ] = []; + for ( var i = 0; i < steps.length; i++ ) { + var el = steps[ i ]; + startingState[ root.id ].push( { + el: el, + x: el.getAttribute( "data-x" ), + y: el.getAttribute( "data-y" ), + z: el.getAttribute( "data-z" ) + } ); + var step = computeRelativePositions( el, prev ); + + // Apply relative position (if non-zero) + el.setAttribute( "data-x", step.x ); + el.setAttribute( "data-y", step.y ); + el.setAttribute( "data-z", step.z ); + prev = step; + } + }; + + // Register the plugin to be called in pre-init phase + window.impress.addPreInitPlugin( rel ); + + // Register teardown callback to reset the data.x, .y, .z values. + document.addEventListener( "impress:init", function( event ) { + var root = event.target; + event.detail.api.lib.gc.addCallback( function() { + var steps = startingState[ root.id ]; + var step; + while ( step = steps.pop() ) { + if ( step.x === null ) { + step.el.removeAttribute( "data-x" ); + } else { + step.el.setAttribute( "data-x", step.x ); + } + if ( step.y === null ) { + step.el.removeAttribute( "data-y" ); + } else { + step.el.setAttribute( "data-y", step.y ); + } + if ( step.z === null ) { + step.el.removeAttribute( "data-z" ); + } else { + step.el.setAttribute( "data-z", step.z ); + } + } + delete startingState[ root.id ]; + } ); + }, false ); +} )( document, window ); + + /** * Resize plugin * @@ -1316,3 +1665,39 @@ } )( document, window ); + +/** + * Stop Plugin + * + * Example: + * + * + *
+ * + * Copyright 2016 Henrik Ingo (@henrikingo) + * Released under the MIT license. + */ +/* global document, window */ +( function( document, window ) { + "use strict"; + + var stop = function( event ) { + if ( ( !event ) || ( !event.target ) ) { + return; + } + + if ( event.target.classList.contains( "stop" ) ) { + if ( event.detail.reason === "next" ) { + return false; + } + } + }; + + // Register the plugin to be called in pre-stepleave phase + // The weight makes this plugin run fairly early. + window.impress.addPreStepLeavePlugin( stop, 2 ); + +} )( document, window ); + diff --git a/src/plugins/rel/rel.js b/src/plugins/rel/rel.js index 4a0204a..c57fe54 100644 --- a/src/plugins/rel/rel.js +++ b/src/plugins/rel/rel.js @@ -47,10 +47,17 @@ ( function( document, window ) { "use strict"; - var lib; var startingState = {}; + /** + * Copied from core impress.js. We currently lack a library mechanism to + * to share utility functions like this. + */ + var toNumber = function( numeric, fallback ) { + return isNaN( numeric ) ? ( fallback || 0 ) : Number( numeric ); + }; + /** * Extends toNumber() to correctly compute also relative-to-screen-size values 5w and 5h. * @@ -58,11 +65,11 @@ */ var toNumberAdvanced = function( numeric, fallback ) { if ( typeof numeric !== "string" ) { - return lib.util.toNumber( numeric, fallback ); + return toNumber( numeric, fallback ); } var ratio = numeric.match( /^([+-]*[\d\.]+)([wh])$/ ); if ( ratio == null ) { - return lib.util.toNumber( numeric, fallback ); + return toNumber( numeric, fallback ); } else { var value = parseFloat( ratio[ 1 ] ); var multiplier = ratio[ 2 ] === "w" ? window.innerWidth : window.innerHeight; @@ -80,9 +87,9 @@ } var step = { - x: lib.util.toNumber( data.x, prev.x ), - y: lib.util.toNumber( data.y, prev.y ), - z: lib.util.toNumber( data.z, prev.z ), + x: toNumber( data.x, prev.x ), + y: toNumber( data.y, prev.y ), + z: toNumber( data.z, prev.z ), relative: { x: toNumberAdvanced( data.relX, prev.relative.x ), y: toNumberAdvanced( data.relY, prev.relative.y ), @@ -139,8 +146,7 @@ // Register teardown callback to reset the data.x, .y, .z values. document.addEventListener( "impress:init", function( event ) { var root = event.target; - lib = event.detail.api.lib; - lib.gc.addCallback( function() { + event.detail.api.lib.gc.addCallback( function() { var steps = startingState[ root.id ]; var step; while ( step = steps.pop() ) {