|
@@ -0,0 +1,282 @@
|
|
|
+
|
|
|
+ * Copyright (C) 2011 Hakim El Hattab, http:
|
|
|
+ *
|
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
+ * of this software and associated documentation files (the "Software"), to deal
|
|
|
+ * in the Software without restriction, including without limitation the rights
|
|
|
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
+ * copies of the Software, and to permit persons to whom the Software is
|
|
|
+ * furnished to do so, subject to the following conditions:
|
|
|
+ *
|
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
|
+ * all copies or substantial portions of the Software.
|
|
|
+ *
|
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
+ * THE SOFTWARE.
|
|
|
+ */
|
|
|
+
|
|
|
+
|
|
|
+ * Handles the very minimal navigation logic involved in the
|
|
|
+ * slideshow. Including keyboard navigation, touch interaction
|
|
|
+ * and URL history behavior.
|
|
|
+ *
|
|
|
+ * Slides are given unique hash based URL's so that they can be
|
|
|
+ * opened directly. I didn't use the HTML5 History API for this
|
|
|
+ * as it would have required the addition of server side rewrite
|
|
|
+ * rules and hence require more effort for anyone to set up.
|
|
|
+ *
|
|
|
+ * This component can be called from other scripts via a tiny API:
|
|
|
+ * - Slideshow.navigateTo( indexh, indexv );
|
|
|
+ * - Slideshow.navigateLeft();
|
|
|
+ * - Slideshow.navigateRight();
|
|
|
+ * - Slideshow.navigateUp();
|
|
|
+ * - Slideshow.navigateDown();
|
|
|
+ *
|
|
|
+ *
|
|
|
+ * version 0.1:
|
|
|
+ * - First release
|
|
|
+ *
|
|
|
+ * version 0.2:
|
|
|
+ * - Refactored code and added inline documentation
|
|
|
+ * - Slides now have unique URL's
|
|
|
+ * - A basic API to invoke navigation was added
|
|
|
+ *
|
|
|
+ * version 0.3:
|
|
|
+ * - Added licensing terms
|
|
|
+ *
|
|
|
+ *
|
|
|
+ * @author Hakim El Hattab
|
|
|
+ * @version 0.3
|
|
|
+ */
|
|
|
+var Slideshow = (function(){
|
|
|
+
|
|
|
+ var indexh = 0,
|
|
|
+ indexv = 0;
|
|
|
+
|
|
|
+
|
|
|
+ * Activates the main program logic.
|
|
|
+ */
|
|
|
+ function initialize() {
|
|
|
+ document.addEventListener('keydown', onDocumentKeyDown, false);
|
|
|
+ document.addEventListener('touchstart', onDocumentTouchStart, false);
|
|
|
+ window.addEventListener('hashchange', onWindowHashChange, false);
|
|
|
+
|
|
|
+
|
|
|
+ readURL();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * Handler for the document level 'keydown' event.
|
|
|
+ *
|
|
|
+ * @param {Object} event
|
|
|
+ */
|
|
|
+ function onDocumentKeyDown( event ) {
|
|
|
+
|
|
|
+ if( event.keyCode >= 37 && event.keyCode <= 40 ) {
|
|
|
+
|
|
|
+ switch( event.keyCode ) {
|
|
|
+ case 37: navigateLeft(); break;
|
|
|
+ case 39: navigateRight(); break;
|
|
|
+ case 38: navigateUp(); break;
|
|
|
+ case 40: navigateDown(); break;
|
|
|
+ }
|
|
|
+
|
|
|
+ slide();
|
|
|
+
|
|
|
+ event.preventDefault();
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * Handler for the document level 'touchstart' event.
|
|
|
+ *
|
|
|
+ * This enables very basic tap interaction for touch
|
|
|
+ * devices. Added mainly for performance testing of 3D
|
|
|
+ * transforms on iOS but was so happily surprised with
|
|
|
+ * how smoothly it runs so I left it in here. Apple +1
|
|
|
+ *
|
|
|
+ * @param {Object} event
|
|
|
+ */
|
|
|
+ function onDocumentTouchStart( event ) {
|
|
|
+
|
|
|
+
|
|
|
+ if (event.touches.length == 1) {
|
|
|
+ event.preventDefault();
|
|
|
+
|
|
|
+ var point = {
|
|
|
+ x: event.touches[0].clientX,
|
|
|
+ y: event.touches[0].clientY
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ var wt = window.innerWidth * 0.3;
|
|
|
+ var ht = window.innerHeight * 0.3;
|
|
|
+
|
|
|
+ if( point.x < wt ) {
|
|
|
+ navigateLeft();
|
|
|
+ }
|
|
|
+ else if( point.x > window.innerWidth - wt ) {
|
|
|
+ navigateRight();
|
|
|
+ }
|
|
|
+ else if( point.y < ht ) {
|
|
|
+ navigateUp();
|
|
|
+ }
|
|
|
+ else if( point.y > window.innerHeight - ht ) {
|
|
|
+ navigateDown();
|
|
|
+ }
|
|
|
+
|
|
|
+ slide();
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ * Handler for the window level 'hashchange' event.
|
|
|
+ *
|
|
|
+ * @param {Object} event
|
|
|
+ */
|
|
|
+ function onWindowHashChange( event ) {
|
|
|
+ readURL();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * Updates one dimension of slides by showing the slide
|
|
|
+ * with the specified index.
|
|
|
+ *
|
|
|
+ * @param {String} selector A CSS selector that will fetch
|
|
|
+ * the group of slides we are working with
|
|
|
+ * @param {Number} index The index of the slide that should be
|
|
|
+ * shown
|
|
|
+ *
|
|
|
+ * @return {Number} The index of the slide that is now shown,
|
|
|
+ * might differ from the passed in index if it was out of
|
|
|
+ * bounds.
|
|
|
+ */
|
|
|
+ function updateSlides( selector, index ) {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ var slides = Array.prototype.slice.call( document.querySelectorAll( selector ) );
|
|
|
+
|
|
|
+ if( slides.length ) {
|
|
|
+
|
|
|
+ index = Math.max(Math.min(index, slides.length - 1), 0);
|
|
|
+
|
|
|
+ slides[index].setAttribute('class', 'present');
|
|
|
+
|
|
|
+
|
|
|
+ slides.slice(0, index).map(function(element){
|
|
|
+ element.setAttribute('class', 'past');
|
|
|
+ });
|
|
|
+
|
|
|
+
|
|
|
+ slides.slice(index + 1).map(function(element){
|
|
|
+ element.setAttribute('class', 'future');
|
|
|
+ });
|
|
|
+ }
|
|
|
+ else {
|
|
|
+
|
|
|
+
|
|
|
+ index = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return index;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * Updates the visual slides to represent the currently
|
|
|
+ * set indices.
|
|
|
+ */
|
|
|
+ function slide() {
|
|
|
+ indexh = updateSlides( '#main>section', indexh );
|
|
|
+ indexv = updateSlides( 'section.present>section', indexv );
|
|
|
+
|
|
|
+ writeURL();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * Reads the current URL (hash) and navigates accordingly.
|
|
|
+ */
|
|
|
+ function readURL() {
|
|
|
+
|
|
|
+ var bits = window.location.hash.slice(2).split('/');
|
|
|
+
|
|
|
+
|
|
|
+ indexh = bits[0] ? parseInt( bits[0] ) : 0;
|
|
|
+ indexv = bits[1] ? parseInt( bits[1] ) : 0;
|
|
|
+
|
|
|
+ navigateTo( indexh, indexv );
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * Updates the page URL (hash) to reflect the current
|
|
|
+ * navigational state.
|
|
|
+ */
|
|
|
+ function writeURL() {
|
|
|
+ var url = '/';
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if( indexh > 0 || indexv > 0 ) url += indexh
|
|
|
+ if( indexv > 0 ) url += '/' + indexv
|
|
|
+
|
|
|
+ window.location.hash = url;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * Triggers a navigation to the specified indices.
|
|
|
+ *
|
|
|
+ * @param {Number} h The horizontal index of the slide to show
|
|
|
+ * @param {Number} v The vertical index of the slide to show
|
|
|
+ */
|
|
|
+ function navigateTo( h, v ) {
|
|
|
+ indexh = h === undefined ? indexh : h;
|
|
|
+ indexv = v === undefined ? indexv : v;
|
|
|
+
|
|
|
+ slide();
|
|
|
+ }
|
|
|
+
|
|
|
+ function navigateLeft() {
|
|
|
+ indexh --;
|
|
|
+ indexv = 0;
|
|
|
+ slide();
|
|
|
+ }
|
|
|
+ function navigateRight() {
|
|
|
+ indexh ++;
|
|
|
+ indexv = 0;
|
|
|
+ slide();
|
|
|
+ }
|
|
|
+ function navigateUp() {
|
|
|
+ indexv --;
|
|
|
+ slide();
|
|
|
+ }
|
|
|
+ function navigateDown() {
|
|
|
+ indexv ++;
|
|
|
+ slide();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ initialize();
|
|
|
+
|
|
|
+
|
|
|
+ return {
|
|
|
+ navigateTo: navigateTo,
|
|
|
+ navigateLeft: navigateLeft,
|
|
|
+ navigateRight: navigateRight,
|
|
|
+ navigateUp: navigateUp,
|
|
|
+ navigateDown: navigateDown
|
|
|
+ };
|
|
|
+
|
|
|
+})();
|
|
|
+
|