<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. -->
<!-- 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">←</a>
<input type="submit" value="Go">
</form>
- <!-- deck.hash snippet -->
- <a href="." title="Permalink to this slide" class="deck-permalink">#</a>
-
<!-- End extension snippets. -->
</div>
<!-- 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>
@@ -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);
+++ /dev/null
-.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;
-}
+++ /dev/null
-<!-- 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
+++ /dev/null
-/*!
-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
+++ /dev/null
-.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
<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 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 & a permalink anchor with each slide 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 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>
<!-- 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>
@@ -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() {
+++ /dev/null
-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