Incorporate deck.hash into core, #137
authorimakewebthings <[email protected]>
Mon, 2 Dec 2013 23:44:02 +0000 (2 15:44 -0800)
committerimakewebthings <[email protected]>
Mon, 2 Dec 2013 23:44:02 +0000 (2 15:44 -0800)
boilerplate.html
core/deck.core.js
extensions/hash/deck.hash.css [deleted file]
extensions/hash/deck.hash.html [deleted file]
extensions/hash/deck.hash.js [deleted file]
extensions/hash/deck.hash.scss [deleted file]
introduction/index.html
test/index.html
test/spec.core.js
test/spec.hash.js [deleted file]

index bf9adc6..8502645 100644 (file)
@@ -15,7 +15,6 @@
   <link rel="stylesheet" media="screen" href="extensions/menu/deck.menu.css">
   <link rel="stylesheet" media="screen" href="extensions/navigation/deck.navigation.css">
   <link rel="stylesheet" media="screen" href="extensions/status/deck.status.css">
-  <link rel="stylesheet" media="screen" href="extensions/hash/deck.hash.css">
   <link rel="stylesheet" media="screen" href="extensions/scale/deck.scale.css">
 
   <!-- Style theme. More available in /themes/style/ or create your own. -->
@@ -49,7 +48,7 @@
 
     <!-- End slides. -->
 
-  <!-- Begin extension snippets. Add or remove as needed. -->
+    <!-- Begin extension snippets. Add or remove as needed. -->
 
     <!-- deck.navigation snippet -->
     <a href="#" class="deck-prev-link" title="Previous">&#8592;</a>
@@ -70,9 +69,6 @@
       <input type="submit" value="Go">
     </form>
 
-    <!-- deck.hash snippet -->
-    <a href="." title="Permalink to this slide" class="deck-permalink">#</a>
-
     <!-- End extension snippets. -->
   </div>
 
@@ -82,7 +78,6 @@
 
 <!-- Extension JS files. Add or remove as needed. -->
 <script src="core/deck.core.js"></script>
-<script src="extensions/hash/deck.hash.js"></script>
 <script src="extensions/menu/deck.menu.js"></script>
 <script src="extensions/goto/deck.goto.js"></script>
 <script src="extensions/status/deck.status.js"></script>
index a13854a..35f8d46 100644 (file)
@@ -15,7 +15,7 @@ slides.  More functionality is provided by wholly separate extension modules
 that use the API provided by core.
 */
 (function($, undefined) {
-  var slides, currentIndex, $container;
+  var slides, currentIndex, $container, $fragmentLinks;
 
   var events = {
     /*
@@ -69,6 +69,7 @@ that use the API provided by core.
 
   var options = {};
   var $document = $(document);
+  var $window = $(window);
   var stopPropagation = function(event) {
     event.stopPropagation();
   };
@@ -247,6 +248,70 @@ that use the API provided by core.
     return event;
   };
 
+  var goByHash = function(str) {
+    var id = str.substr(str.indexOf("#") + 1);
+
+    $.each(slides, function(i, $slide) {
+      if ($slide.attr('id') === id) {
+        $.deck('go', i);
+        return false;
+      }
+    });
+
+    // If we don't set these to 0 the container scrolls due to hashchange
+    if (options.preventFragmentScroll) {
+      $.deck('getContainer').scrollLeft(0).scrollTop(0);
+    }
+  };
+
+  var assignSlideId = function(i, $slide) {
+    var currentId = $slide.attr('id');
+    var previouslyAssigned = $slide.data('deckAssignedId') === currentId;
+    if (!currentId || previouslyAssigned) {
+      $slide.attr('id', options.hashPrefix + i);
+      $slide.data('deckAssignedId', options.hashPrefix + i);
+    }
+  };
+
+  var removeContainerHashClass = function(id) {
+    $container.removeClass(options.classes.onPrefix + id);
+  };
+
+  var addContainerHashClass = function(id) {
+    $container.addClass(options.classes.onPrefix + id);
+  };
+
+  var setupHashBehaviors = function() {
+    $fragmentLinks = $();
+    $.each(slides, function(i, $slide) {
+      var hash;
+
+      assignSlideId(i, $slide);
+      hash = '#' + $slide.attr('id');
+      if (hash === window.location.hash) {
+        setTimeout(function() {
+          $.deck('go', i);
+        }, 1);
+      }
+      $fragmentLinks = $fragmentLinks.add('a[href="' + hash + '"]');
+    });
+
+    if (slides.length) {
+      addContainerHashClass($.deck('getSlide').attr('id'));
+    };
+  };
+
+  var changeHash = function(from, to) {
+    var hash = '#' + $.deck('getSlide', to).attr('id');
+    var hashPath = window.location.href.replace(/#.*/, '') + hash;
+
+    removeContainerHashClass($.deck('getSlide', from).attr('id'));
+    addContainerHashClass($.deck('getSlide', to).attr('id'));
+    if (Modernizr.history) {
+      window.history.replaceState({}, "", hashPath);
+    }
+  };
+
   /* Methods exposed in the jQuery.deck namespace */
   var methods = {
 
@@ -300,6 +365,7 @@ that use the API provided by core.
         // re-populate the array of slides
         slides = [];
         initSlidesArray(options.selectors.slides);
+        setupHashBehaviors();
         bindKeyEvents();
         bindTouchEvents();
         $container.scrollLeft(0).scrollTop(0);
@@ -365,6 +431,7 @@ that use the API provided by core.
       $document.trigger(beforeChangeEvent, [currentIndex, index]);
       if (!beforeChangeEvent.isDefaultPrevented()) {
         $document.trigger(events.change, [currentIndex, index]);
+        changeHash(currentIndex, index);
         currentIndex = index;
         updateStates();
       }
@@ -534,6 +601,16 @@ that use the API provided by core.
   options.touch.swipeTolerance
     The number of pixels the users finger must travel to produce a swipe
     gesture.
+
+  options.hashPrefix
+    Every slide that does not have an id is assigned one at initialization.
+    Assigned ids take the form of hashPrefix + slideIndex, e.g., slide-0,
+    slide-12, etc.
+
+  options.preventFragmentScroll
+    When deep linking to a hash of a nested slide, this scrolls the deck
+    container to the top, undoing the natural browser behavior of scrolling
+    to the document fragment on load.
   */
   $.deck.defaults = {
     classes: {
@@ -564,10 +641,27 @@ that use the API provided by core.
       swipeTolerance: 60
     },
 
-    initLockTimeout: 10000
+    initLockTimeout: 10000,
+    hashPrefix: 'slide-',
+    preventFragmentScroll: true
   };
 
   $document.ready(function() {
     $('html').addClass('ready');
   });
+
+  $window.bind('hashchange.deck', function(event) {
+    if (event.originalEvent && event.originalEvent.newURL) {
+      goByHash(event.originalEvent.newURL);
+    }
+    else {
+      goByHash(window.location.hash);
+    }
+  });
+
+  $window.bind('load.deck', function() {
+    if (options.preventFragmentScroll) {
+      $container.scrollLeft(0).scrollTop(0);
+    }
+  });
 })(jQuery);
diff --git a/extensions/hash/deck.hash.css b/extensions/hash/deck.hash.css
deleted file mode 100644 (file)
index 6fd564b..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-.deck-permalink {
-  display: none;
-  position: absolute;
-  z-index: 4;
-  bottom: 30px;
-  right: 0;
-  width: 48px;
-  text-align: center;
-}
-
-.no-history .deck-container:hover .deck-permalink {
-  display: block;
-}
diff --git a/extensions/hash/deck.hash.html b/extensions/hash/deck.hash.html
deleted file mode 100644 (file)
index 102579c..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-<!-- Place the following snippet at the bottom of the deck container. -->
-<a href="." title="Permalink to this slide" class="deck-permalink">#</a>
\ No newline at end of file
diff --git a/extensions/hash/deck.hash.js b/extensions/hash/deck.hash.js
deleted file mode 100644 (file)
index 24200b7..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/*!
-Deck JS - deck.hash
-Copyright (c) 2011 Caleb Troughton
-Dual licensed under the MIT license.
-https://github.com/imakewebthings/deck.js/blob/master/MIT-license.txt
-*/
-
-/*
-This module adds deep linking to individual slides, enables internal links
-to slides within decks, and updates the address bar with the hash as the user
-moves through the deck. A permalink anchor is also updated. Standard themes
-hide this link in browsers that support the History API, and show it for
-those that do not. Slides that do not have an id are assigned one according to
-the hashPrefix option. In addition to the on-slide container state class
-kept by core, this module adds an on-slide state class that uses the id of each
-slide.
-*/
-(function ($, undefined) {
-  var $document = $(document);
-  var $window = $(window);
-
-  /* Collection of internal fragment links in the deck */
-  var $fragmentLinks;
-
-  /*
-  Internal only function.  Given a string, extracts the id from the hash,
-  matches it to the appropriate slide, and navigates there.
-  */
-  var goByHash = function(str) {
-    var id = str.substr(str.indexOf("#") + 1);
-    var slides = $.deck('getSlides');
-
-    $.each(slides, function(i, $slide) {
-      if ($slide.attr('id') === id) {
-        $.deck('go', i);
-        return false;
-      }
-    });
-
-    // If we don't set these to 0 the container scrolls due to hashchange
-    if ($.deck('getOptions').preventFragmentScroll) {
-      $.deck('getContainer').scrollLeft(0).scrollTop(0);
-    }
-  };
-
-  var assignSlideId = function(i, $slide) {
-    var options = $.deck('getOptions');
-    var currentId = $slide.attr('id');
-    var previouslyAssigned = $slide.data('deckAssignedId') === currentId;
-    if (!currentId || previouslyAssigned) {
-      $slide.attr('id', options.hashPrefix + i);
-      $slide.data('deckAssignedId', options.hashPrefix + i);
-    }
-  };
-
-  var removeContainerStateClass = function(id) {
-    var options = $.deck('getOptions');
-    $.deck('getContainer').removeClass(options.classes.onPrefix + id);
-  };
-
-  var addContainerStateClass = function(id) {
-    var options = $.deck('getOptions');
-    $.deck('getContainer').addClass(options.classes.onPrefix + id);
-  };
-
-  /*
-  Extends defaults/options.
-
-  options.selectors.hashLink
-    The element matching this selector has its href attribute updated to
-    the hash of the current slide as the user navigates through the deck.
-
-  options.hashPrefix
-    Every slide that does not have an id is assigned one at initialization.
-    Assigned ids take the form of hashPrefix + slideIndex, e.g., slide-0,
-    slide-12, etc.
-
-  options.preventFragmentScroll
-    When deep linking to a hash of a nested slide, this scrolls the deck
-    container to the top, undoing the natural browser behavior of scrolling
-    to the document fragment on load.
-  */
-  $.extend(true, $.deck.defaults, {
-    selectors: {
-      hashLink: '.deck-permalink'
-    },
-
-    hashPrefix: 'slide-',
-    preventFragmentScroll: true
-  });
-
-
-  $document.bind('deck.init', function() {
-    var options = $.deck('getOptions');
-    var slides = $.deck('getSlides');
-
-    $fragmentLinks = $();
-    $.each(slides, function(i, $slide) {
-      var hash;
-
-      assignSlideId(i, $slide);
-      hash = '#' + $slide.attr('id');
-      if (hash === window.location.hash) {
-        setTimeout(function() {
-          $.deck('go', i);
-        }, 1);
-      }
-      $fragmentLinks = $fragmentLinks.add('a[href="' + hash + '"]');
-    });
-
-    /* Set up first id container state class */
-    if (slides.length) {
-      addContainerStateClass($.deck('getSlide').attr('id'));
-    };
-  });
-
-  /* Update permalink, address bar, and state class on a slide change */
-  $document.bind('deck.change', function(event, from, to) {
-    var hash = '#' + $.deck('getSlide', to).attr('id');
-    var hashPath = window.location.href.replace(/#.*/, '') + hash;
-    var options = $.deck('getOptions');
-
-    removeContainerStateClass($.deck('getSlide', from).attr('id'));
-    addContainerStateClass($.deck('getSlide', to).attr('id'));
-    $(options.selectors.hashLink).attr('href', hashPath);
-    if (Modernizr.history) {
-      window.history.replaceState({}, "", hashPath);
-    }
-  });
-
-  $window.bind('hashchange.deckhash', function(event) {
-    if (event.originalEvent && event.originalEvent.newURL) {
-      goByHash(event.originalEvent.newURL);
-    }
-    else {
-      goByHash(window.location.hash);
-    }
-  })
-
-  $window.bind('load', function() {
-    if ($.deck('getOptions').preventFragmentScroll) {
-      $.deck('getContainer').scrollLeft(0).scrollTop(0);
-    }
-  });
-})(jQuery);
\ No newline at end of file
diff --git a/extensions/hash/deck.hash.scss b/extensions/hash/deck.hash.scss
deleted file mode 100644 (file)
index 1ef77b5..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-.deck-permalink {
-  display:none;
-  position:absolute;
-  z-index:4;
-  bottom:30px;
-  right:0;
-  width:48px;
-  text-align:center;
-}
-
-.no-history .deck-container:hover .deck-permalink {
-  display:block;
-}
\ No newline at end of file
index 3f52b61..a8ac5c6 100644 (file)
@@ -19,7 +19,6 @@
   <link rel="stylesheet" media="screen" href="../extensions/menu/deck.menu.css">
   <link rel="stylesheet" media="screen" href="../extensions/navigation/deck.navigation.css">
   <link rel="stylesheet" media="screen" href="../extensions/status/deck.status.css">
-  <link rel="stylesheet" media="screen" href="../extensions/hash/deck.hash.css">
   <link rel="stylesheet" media="screen" href="../extensions/scale/deck.scale.css">
 
   <!-- Style theme. More available in /themes/style/ or create your own. -->
           <strong>deck.goto</strong>: Adds a shortcut key to jump to any slide number.  Hit g, type in the slide number, and hit&nbsp;enter.
         </li>
 
-        <li class="slide" id="extensions-hash">
-          <strong>deck.hash</strong>: Enables internal linking within slides, deep linking to individual slides, and updates the address bar &amp; a permalink anchor with each slide&nbsp;change.
-        </li>
-
         <li class="slide" id="extensions-menu">
           <strong>deck.menu</strong>: Adds a menu view, letting you see all slides in a grid. Hit m to toggle to menu view, continue navigating your deck, and hit m to return to normal view. Touch devices can double-tap the deck to switch between&nbsp;views.
         </li>
       <datalist id="goto-datalist"></datalist>
       <input type="submit" value="Go">
     </form>
-
-    <!-- deck.hash snippet -->
-    <a href="." title="Permalink to this slide" class="deck-permalink">#</a>
   </div>
 
 <script src="../jquery.min.js"></script>
 
 <!-- Deck Core and extensions -->
 <script src="../core/deck.core.js"></script>
-<script src="../extensions/hash/deck.hash.js"></script>
 <script src="../extensions/menu/deck.menu.js"></script>
 <script src="../extensions/goto/deck.goto.js"></script>
 <script src="../extensions/status/deck.status.js"></script>
index ffa1ccc..333122e 100644 (file)
@@ -12,7 +12,6 @@
 
   <!-- include source files here... -->
   <script type="text/javascript" src="../core/deck.core.js"></script>
-  <script type="text/javascript" src="../extensions/hash/deck.hash.js"></script>
   <script type="text/javascript" src="../extensions/menu/deck.menu.js"></script>
   <script type="text/javascript" src="../extensions/goto/deck.goto.js"></script>
   <script type="text/javascript" src="../extensions/status/deck.status.js"></script>
index f86f6ac..7da35ba 100755 (executable)
@@ -394,6 +394,67 @@ describe('Deck JS', function() {
         });
       });
     });
+
+    describe('hash/id assignments', function() {
+      beforeEach(function() {
+        $.deck('.slide');
+      });
+
+      it('should assign ids to slides that do not have them', function() {
+        var slides = $.deck('getSlides');
+        $.each(slides, function(i, $e) {
+          expect($e.attr('id')).toBeTruthy();
+        });
+      });
+
+      it('should reassign ids on reinitialization', function() {
+        var $firstSlide = $.deck('getSlide', 0);
+        var firstID = $firstSlide.attr('id');
+
+        $firstSlide.before('<div class="slide"></div>');
+        $.deck('.slide');
+        expect($firstSlide).not.toHaveId(firstID);
+      });
+
+      it('should update container with a state class including the slide id', function() {
+        var $c = $.deck('getContainer');
+        var osp = defaults.classes.onPrefix;
+
+        expect($c).toHaveClass(osp + $.deck('getSlide', 0).attr('id'));
+        $.deck('next');
+        expect($c).toHaveClass(osp + $.deck('getSlide', 1).attr('id'));
+        $.deck('next');
+        expect($c).not.toHaveClass(osp + $.deck('getSlide', 1).attr('id'));
+        expect($c).toHaveClass(osp + $.deck('getSlide', 2).attr('id'));
+      });
+
+      it('should use existing ids if they exist', function() {
+        expect($('#custom-id')).toExist();
+      });
+
+      it('should update the URL on slide change (if supported)', function() {
+        if (Modernizr.history) {
+          $.deck('go', 3);
+          expect(window.location.hash).toEqual('#slide-3');
+        }
+      });
+
+      it('should deep link to slide on deck init', function() {
+        window.location.hash = "#slide-3";
+        $.deck('.slide');
+        waitsFor(function() {
+          return $.deck('getSlide').attr('id') === 'slide-3';
+        });
+      });
+
+      it('should follow internal hash links using hashchange (if supported)', function() {
+        window.location.hash = "#slide-3";
+        // Hashchange event doesn't fire right when the hash changes?
+        waitsFor(function() {
+          return $.deck('getSlide').attr('id') === 'slide-3';
+        }, 'hash to change to slide-3', 2000);
+      });
+    });
   });
 
   describe('empty deck', function() {
diff --git a/test/spec.hash.js b/test/spec.hash.js
deleted file mode 100644 (file)
index 3b9ae01..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-describe('Deck JS Hash Extension', function() {
-  beforeEach(function() {
-    loadFixtures('standard.html');
-    if (Modernizr.history) {
-      history.replaceState({}, "", "#")
-    }
-    else {
-      window.location.hash = '#';
-    }
-    $.deck('.slide');
-  });
-
-  it('should assign ids to slides that do not have them', function() {
-    var slides = $.deck('getSlides');
-    $.each(slides, function(i, $e) {
-      expect($e.attr('id')).toBeTruthy();
-    });
-  });
-
-  it('should reassign ids on reinitialization', function() {
-    var $firstSlide = $.deck('getSlide', 0);
-    var firstID = $firstSlide.attr('id');
-
-    $firstSlide.before('<div class="slide"></div>');
-    $.deck('.slide');
-    expect($firstSlide).not.toHaveId(firstID);
-  });
-
-   it('should update container with a state class including the slide id', function() {
-      var $c = $.deck('getContainer');
-      var osp = defaults.classes.onPrefix;
-
-      expect($c).toHaveClass(osp + $.deck('getSlide', 0).attr('id'));
-      $.deck('next');
-      expect($c).toHaveClass(osp + $.deck('getSlide', 1).attr('id'));
-      $.deck('next');
-      expect($c).not.toHaveClass(osp + $.deck('getSlide', 1).attr('id'));
-      expect($c).toHaveClass(osp + $.deck('getSlide', 2).attr('id'));
-   });
-
-  it('should update the href on slide change', function() {
-    var $hashLink = $(defaults.selectors.hashLink);
-    $.deck('go', 3);
-    expect($hashLink.attr('href')).toMatch('#slide-3');
-  });
-
-  it('should use existing ids if they exist', function() {
-    var $hashLink = $(defaults.selectors.hashLink);
-    $.deck('go', 1);
-    expect($hashLink.attr('href')).toMatch('#custom-id');
-  });
-
-  it('should update the URL on slide change (if supported)', function() {
-    if (Modernizr.history) {
-      $.deck('go', 3);
-      expect(window.location.hash).toEqual('#slide-3');
-    }
-  });
-
-  it('should deep link to slide on deck init', function() {
-    window.location.hash = "#slide-3";
-    $.deck('.slide');
-    waitsFor(function() {
-      return $.deck('getSlide').attr('id') === 'slide-3';
-    });
-  });
-
-  it('should follow internal hash links using hashchange (if supported)', function() {
-    window.location.hash = "#slide-3";
-    // Hashchange event doesn't fire right when the hash changes?
-    waitsFor(function() {
-      return $.deck('getSlide').attr('id') === 'slide-3';
-    }, 'hash to change to slide-3', 2000);
-  });
-
-  it('should follow internal hash links on click', function() {
-    /* Triggered clicks dont generate hashchanges, so until I find
-    a way to do this in an automated fashion, needs to be hand tested. */
-  });
-});
\ No newline at end of file