diff --git a/src/plugins/toolbar/README.md b/src/plugins/toolbar/README.md new file mode 100644 index 0000000..4b2b9d2 --- /dev/null +++ b/src/plugins/toolbar/README.md @@ -0,0 +1,41 @@ +Toolbar plugin +==================== + +This plugin provides a generic graphical toolbar. Other plugins that +want to expose a button or other widget, can add those to this toolbar. + +Using a single consolidated toolbar for all GUI widgets makes it easier +to position and style the toolbar rather than having to do that for lots +of different divs. + +To add/activate the toolbar in your presentation, add this div: + +
+ +Styling the toolbar is left to presentation author. Here's an example CSS: + + .impress-enabled div#impress-toolbar { + position: fixed; + right: 1px; + bottom: 1px; + opacity: 0.6; + } + .impress-enabled div#impress-toolbar > span { + margin-right: 10px; + } + +The [mouse-timeout](../mouse-timeout/README.md) plugin can be leveraged to hide +the toolbar from sight, and only make it visible when mouse is moved. + + body.impress-mouse-timeout div#impress-toolbar { + display: none; + } + +If you're writing a plugin and would like to add a widget to the toolbar, see +[the top of the source file for further instructions](toolbar.js). + + +Author +------ + +Henrik Ingo (@henrikingo), 2016 diff --git a/src/plugins/toolbar/toolbar.js b/src/plugins/toolbar/toolbar.js new file mode 100644 index 0000000..aa019b0 --- /dev/null +++ b/src/plugins/toolbar/toolbar.js @@ -0,0 +1,157 @@ +/** + * Toolbar plugin + * + * This plugin provides a generic graphical toolbar. Other plugins that + * want to expose a button or other widget, can add those to this toolbar. + * + * Using a single consolidated toolbar for all GUI widgets makes it easier + * to position and style the toolbar rather than having to do that for lots + * of different divs. + * + * + * *** For presentation authors: ***************************************** + * + * To add/activate the toolbar in your presentation, add this div: + * + * + * + * Styling the toolbar is left to presentation author. Here's an example CSS: + * + * .impress-enabled div#impress-toolbar { + * position: fixed; + * right: 1px; + * bottom: 1px; + * opacity: 0.6; + * } + * .impress-enabled div#impress-toolbar > span { + * margin-right: 10px; + * } + * + * The [mouse-timeout](../mouse-timeout/README.md) plugin can be leveraged to hide + * the toolbar from sight, and only make it visible when mouse is moved. + * + * body.impress-mouse-timeout div#impress-toolbar { + * display: none; + * } + * + * + * *** For plugin authors ********************************************** + * + * To add a button to the toolbar, trigger the `impress:toolbar:appendChild` + * or `impress:toolbar:insertBefore` events as appropriate. The detail object + * should contain following parameters: + * + * { group : 1, // integer. Widgets with the same group are grouped inside + * // the same element. + * html : "", // The html to add. + * callback : "mycallback", // Toolbar plugin will trigger event + * // `impress:toolbar:added:mycallback` when done. + * before: element } // The reference element for an insertBefore() call. + * + * You should also listen to the `impress:toolbar:added:mycallback` event. At + * this point you can find the new widget in the DOM, and for example add an + * event listener to it. + * + * You are free to use any integer for the group. It's ok to leave gaps. It's + * ok to co-locate with widgets for another plugin, if you think they belong + * together. + * + * See navigation-ui for an example. + * + * Copyright 2016 Henrik Ingo (@henrikingo) + * Released under the MIT license. + */ + +/* global document */ + +( function( document ) { + "use strict"; + var toolbar = document.getElementById( "impress-toolbar" ); + var groups = []; + + /** + * Get the span element that is a child of toolbar, identified by index. + * + * If span element doesn't exist yet, it is created. + * + * Note: Because of Run-to-completion, this is not a race condition. + * https://developer.mozilla.org/en/docs/Web/JavaScript/EventLoop#Run-to-completion + * + * :param: index Method will return the element + */ + var getGroupElement = function( index ) { + var id = "impress-toolbar-group-" + index; + if ( !groups[ index ] ) { + groups[ index ] = document.createElement( "span" ); + groups[ index ].id = id; + var nextIndex = getNextGroupIndex( index ); + if ( nextIndex === undefined ) { + toolbar.appendChild( groups[ index ] ); + } else { + toolbar.insertBefore( groups[ index ], groups[ nextIndex ] ); + } + } + return groups[ index ]; + }; + + /** + * Get the span element from groups[] that is immediately after given index. + * + * This can be used to find the reference node for an insertBefore() call. + * If no element exists at a larger index, returns undefined. (In this case, + * you'd use appendChild() instead.) + * + * Note that index needn't itself exist in groups[]. + */ + var getNextGroupIndex = function( index ) { + var i = index + 1; + while ( !groups[ i ] && i < groups.length ) { + i++; + } + if ( i < groups.length ) { + return i; + } + }; + + // API + // Other plugins can add and remove buttons by sending them as events. + // In return, toolbar plugin will trigger events when button was added. + if ( toolbar ) { + /** + * Append a widget inside toolbar span element identified by given group index. + * + * :param: e.detail.group integer specifying the span element where widget will be placed + * :param: e.detail.element a dom element to add to the toolbar + */ + toolbar.addEventListener( "impress:toolbar:appendChild", function( e ) { + var group = getGroupElement( e.detail.group ); + group.appendChild( e.detail.element ); + } ); + + /** + * Add a widget to toolbar using insertBefore() DOM method. + * + * :param: e.detail.before the reference dom element, before which new element is added + * :param: e.detail.element a dom element to add to the toolbar + */ + toolbar.addEventListener( "impress:toolbar:insertBefore", function( e ) { + toolbar.insertBefore( e.detail.element, e.detail.before ); + } ); + + /** + * Remove the widget in e.detail.remove. + */ + toolbar.addEventListener( "impress:toolbar:removeWidget", function( e ) { + toolbar.removeChild( e.detail.remove ); + } ); + + document.addEventListener( "impress:init", function( event ) { + var api = event.detail.api; + api.lib.gc.addCallback( function() { + toolbar.innerHTML = ""; + groups = []; + } ); + } ); + } // If toolbar + +} )( document );