summaryrefslogtreecommitdiffstats
path: root/doc/backends/deckjs/deck.js/core/deck.core.js
diff options
context:
space:
mode:
Diffstat (limited to 'doc/backends/deckjs/deck.js/core/deck.core.js')
-rw-r--r--doc/backends/deckjs/deck.js/core/deck.core.js748
1 files changed, 748 insertions, 0 deletions
diff --git a/doc/backends/deckjs/deck.js/core/deck.core.js b/doc/backends/deckjs/deck.js/core/deck.core.js
new file mode 100644
index 00000000..a8adefe7
--- /dev/null
+++ b/doc/backends/deckjs/deck.js/core/deck.core.js
@@ -0,0 +1,748 @@
+/*!
+Deck JS - deck.core
+Copyright (c) 2011-2014 Caleb Troughton
+Dual licensed under the MIT license.
+https://github.com/imakewebthings/deck.js/blob/master/MIT-license.txt
+*/
+
+/*
+The deck.core module provides all the basic functionality for creating and
+moving through a deck. It does so by applying classes to indicate the state of
+the deck and its slides, allowing CSS to take care of the visual representation
+of each state. It also provides methods for navigating the deck and inspecting
+its state, as well as basic key bindings for going to the next and previous
+slides. More functionality is provided by wholly separate extension modules
+that use the API provided by core.
+*/
+(function($, undefined) {
+ var slides, currentIndex, $container, $fragmentLinks;
+
+ var events = {
+ /*
+ This event fires at the beginning of a slide change, before the actual
+ change occurs. Its purpose is to give extension authors a way to prevent
+ the slide change from occuring. This is done by calling preventDefault
+ on the event object within this event. If that is done, the deck.change
+ event will never be fired and the slide will not change.
+ */
+ beforeChange: 'deck.beforeChange',
+
+ /*
+ This event fires whenever the current slide changes, whether by way of
+ next, prev, or go. The callback function is passed two parameters, from
+ and to, equal to the indices of the old slide and the new slide
+ respectively. If preventDefault is called on the event within this handler
+ the slide change does not occur.
+
+ $(document).bind('deck.change', function(event, from, to) {
+ alert('Moving from slide ' + from + ' to ' + to);
+ });
+ */
+ change: 'deck.change',
+
+ /*
+ This event fires at the beginning of deck initialization. This event makes
+ a good hook for preprocessing extensions looking to modify the DOM before
+ the deck is fully initialized. It is also possible to halt the deck.init
+ event from firing while you do things in beforeInit. This can be done by
+ calling lockInit on the event object passed to this event. The init can be
+ released by calling releaseInit.
+
+ $(document).bind('deck.beforeInit', function(event) {
+ event.lockInit(); // halts deck.init event
+ window.setTimeout(function() {
+ event.releaseInit(); // deck.init will now fire 2 seconds later
+ }, 2000);
+ });
+
+ The init event will be fired regardless of locks after
+ options.initLockTimeout milliseconds.
+ */
+ beforeInitialize: 'deck.beforeInit',
+
+ /*
+ This event fires at the end of deck initialization. Extensions should
+ implement any code that relies on user extensible options (key bindings,
+ element selectors, classes) within a handler for this event. Native
+ events associated with Deck JS should be scoped under a .deck event
+ namespace, as with the example below:
+
+ var $d = $(document);
+ $.deck.defaults.keys.myExtensionKeycode = 70; // 'h'
+ $d.bind('deck.init', function() {
+ $d.bind('keydown.deck', function(event) {
+ if (event.which === $.deck.getOptions().keys.myExtensionKeycode) {
+ // Rock out
+ }
+ });
+ });
+ */
+ initialize: 'deck.init'
+ };
+
+ var options = {};
+ var $document = $(document);
+ var $window = $(window);
+ var stopPropagation = function(event) {
+ event.stopPropagation();
+ };
+
+ var updateContainerState = function() {
+ var oldIndex = $container.data('onSlide');
+ $container.removeClass(options.classes.onPrefix + oldIndex);
+ $container.addClass(options.classes.onPrefix + currentIndex);
+ $container.data('onSlide', currentIndex);
+ };
+
+ var updateChildCurrent = function() {
+ var $oldCurrent = $('.' + options.classes.current);
+ var $oldParents = $oldCurrent.parentsUntil(options.selectors.container);
+ var $newCurrent = slides[currentIndex];
+ var $newParents = $newCurrent.parentsUntil(options.selectors.container);
+ $oldParents.removeClass(options.classes.childCurrent);
+ $newParents.addClass(options.classes.childCurrent);
+ };
+
+ var removeOldSlideStates = function() {
+ var $all = $();
+ $.each(slides, function(i, el) {
+ $all = $all.add(el);
+ });
+ $all.removeClass([
+ options.classes.before,
+ options.classes.previous,
+ options.classes.current,
+ options.classes.next,
+ options.classes.after
+ ].join(' '));
+ };
+
+ var addNewSlideStates = function() {
+ slides[currentIndex].addClass(options.classes.current);
+ if (currentIndex > 0) {
+ slides[currentIndex-1].addClass(options.classes.previous);
+ }
+ if (currentIndex + 1 < slides.length) {
+ slides[currentIndex+1].addClass(options.classes.next);
+ }
+ if (currentIndex > 1) {
+ $.each(slides.slice(0, currentIndex - 1), function(i, $slide) {
+ $slide.addClass(options.classes.before);
+ });
+ }
+ if (currentIndex + 2 < slides.length) {
+ $.each(slides.slice(currentIndex+2), function(i, $slide) {
+ $slide.addClass(options.classes.after);
+ });
+ }
+ };
+
+ var setAriaHiddens = function() {
+ $(options.selectors.slides).each(function() {
+ var $slide = $(this);
+ var isSub = $slide.closest('.' + options.classes.childCurrent).length;
+ var isBefore = $slide.hasClass(options.classes.before) && !isSub;
+ var isPrevious = $slide.hasClass(options.classes.previous) && !isSub;
+ var isNext = $slide.hasClass(options.classes.next);
+ var isAfter = $slide.hasClass(options.classes.after);
+ var ariaHiddenValue = isBefore || isPrevious || isNext || isAfter;
+ $slide.attr('aria-hidden', ariaHiddenValue);
+ });
+ };
+
+ var updateStates = function() {
+ updateContainerState();
+ updateChildCurrent();
+ removeOldSlideStates();
+ addNewSlideStates();
+ if (options.setAriaHiddens) {
+ setAriaHiddens();
+ }
+ };
+
+ var initSlidesArray = function(elements) {
+ if ($.isArray(elements)) {
+ $.each(elements, function(i, element) {
+ slides.push($(element));
+ });
+ }
+ else {
+ $(elements).each(function(i, element) {
+ slides.push($(element));
+ });
+ }
+ };
+
+ var bindKeyEvents = function() {
+ var editables = [
+ 'input',
+ 'textarea',
+ 'select',
+ 'button',
+ 'meter',
+ 'progress',
+ '[contentEditable]'
+ ].join(', ');
+
+ $document.unbind('keydown.deck').bind('keydown.deck', function(event) {
+ var isNext = event.which === options.keys.next;
+ var isPrev = event.which === options.keys.previous;
+ isNext = isNext || $.inArray(event.which, options.keys.next) > -1;
+ isPrev = isPrev || $.inArray(event.which, options.keys.previous) > -1;
+
+ if (isNext) {
+ methods.next();
+ event.preventDefault();
+ }
+ else if (isPrev) {
+ methods.prev();
+ event.preventDefault();
+ }
+ });
+
+ $document.undelegate(editables, 'keydown.deck', stopPropagation);
+ $document.delegate(editables, 'keydown.deck', stopPropagation);
+ };
+
+ var bindTouchEvents = function() {
+ var startTouch;
+ var direction = options.touch.swipeDirection;
+ var tolerance = options.touch.swipeTolerance;
+ var listenToHorizontal = ({ both: true, horizontal: true })[direction];
+ var listenToVertical = ({ both: true, vertical: true })[direction];
+
+ $container.unbind('touchstart.deck');
+ $container.bind('touchstart.deck', function(event) {
+ if (!startTouch) {
+ startTouch = $.extend({}, event.originalEvent.targetTouches[0]);
+ }
+ });
+
+ $container.unbind('touchmove.deck');
+ $container.bind('touchmove.deck', function(event) {
+ $.each(event.originalEvent.changedTouches, function(i, touch) {
+ if (!startTouch || touch.identifier !== startTouch.identifier) {
+ return true;
+ }
+ var xDistance = touch.screenX - startTouch.screenX;
+ var yDistance = touch.screenY - startTouch.screenY;
+ var leftToRight = xDistance > tolerance && listenToHorizontal;
+ var rightToLeft = xDistance < -tolerance && listenToHorizontal;
+ var topToBottom = yDistance > tolerance && listenToVertical;
+ var bottomToTop = yDistance < -tolerance && listenToVertical;
+
+ if (leftToRight || topToBottom) {
+ $.deck('prev');
+ startTouch = undefined;
+ }
+ else if (rightToLeft || bottomToTop) {
+ $.deck('next');
+ startTouch = undefined;
+ }
+ return false;
+ });
+
+ if (listenToVertical) {
+ event.preventDefault();
+ }
+ });
+
+ $container.unbind('touchend.deck');
+ $container.bind('touchend.deck', function(event) {
+ $.each(event.originalEvent.changedTouches, function(i, touch) {
+ if (startTouch && touch.identifier === startTouch.identifier) {
+ startTouch = undefined;
+ }
+ });
+ });
+ };
+
+ var indexInBounds = function(index) {
+ return typeof index === 'number' && index >=0 && index < slides.length;
+ };
+
+ var createBeforeInitEvent = function() {
+ var event = $.Event(events.beforeInitialize);
+ event.locks = 0;
+ event.done = $.noop;
+ event.lockInit = function() {
+ ++event.locks;
+ };
+ event.releaseInit = function() {
+ --event.locks;
+ if (!event.locks) {
+ event.done();
+ }
+ };
+ 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 = {
+
+ /*
+ jQuery.deck(selector, options)
+
+ selector: string | jQuery | array
+ options: object, optional
+
+ Initializes the deck, using each element matched by selector as a slide.
+ May also be passed an array of string selectors or jQuery objects, in
+ which case each selector in the array is considered a slide. The second
+ parameter is an optional options object which will extend the default
+ values.
+
+ Users may also pass only an options object to init. In this case the slide
+ selector will be options.selectors.slides which defaults to .slide.
+
+ $.deck('.slide');
+
+ or
+
+ $.deck([
+ '#first-slide',
+ '#second-slide',
+ '#etc'
+ ]);
+ */
+ init: function(opts) {
+ var beforeInitEvent = createBeforeInitEvent();
+ var overrides = opts;
+
+ if (!$.isPlainObject(opts)) {
+ overrides = arguments[1] || {};
+ $.extend(true, overrides, {
+ selectors: {
+ slides: arguments[0]
+ }
+ });
+ }
+
+ options = $.extend(true, {}, $.deck.defaults, overrides);
+ slides = [];
+ currentIndex = 0;
+ $container = $(options.selectors.container);
+
+ // Hide the deck while states are being applied to kill transitions
+ $container.addClass(options.classes.loading);
+
+ // populate the array of slides for pre-init
+ initSlidesArray(options.selectors.slides);
+ // Pre init event for preprocessing hooks
+ beforeInitEvent.done = function() {
+ // re-populate the array of slides
+ slides = [];
+ initSlidesArray(options.selectors.slides);
+ setupHashBehaviors();
+ bindKeyEvents();
+ bindTouchEvents();
+ $container.scrollLeft(0).scrollTop(0);
+
+ if (slides.length) {
+ updateStates();
+ }
+
+ // Show deck again now that slides are in place
+ $container.removeClass(options.classes.loading);
+ $document.trigger(events.initialize);
+ };
+
+ $document.trigger(beforeInitEvent);
+ if (!beforeInitEvent.locks) {
+ beforeInitEvent.done();
+ }
+ window.setTimeout(function() {
+ if (beforeInitEvent.locks) {
+ if (window.console) {
+ window.console.warn('Something locked deck initialization\
+ without releasing it before the timeout. Proceeding with\
+ initialization anyway.');
+ }
+ beforeInitEvent.done();
+ }
+ }, options.initLockTimeout);
+ },
+
+ /*
+ jQuery.deck('go', index)
+
+ index: integer | string
+
+ Moves to the slide at the specified index if index is a number. Index is
+ 0-based, so $.deck('go', 0); will move to the first slide. If index is a
+ string this will move to the slide with the specified id. If index is out
+ of bounds or doesn't match a slide id the call is ignored.
+ */
+ go: function(indexOrId) {
+ var beforeChangeEvent = $.Event(events.beforeChange);
+ var index;
+
+ /* Number index, easy. */
+ if (indexInBounds(indexOrId)) {
+ index = indexOrId;
+ }
+ /* Id string index, search for it and set integer index */
+ else if (typeof indexOrId === 'string') {
+ $.each(slides, function(i, $slide) {
+ if ($slide.attr('id') === indexOrId) {
+ index = i;
+ return false;
+ }
+ });
+ }
+ if (typeof index === 'undefined') {
+ return;
+ }
+
+ /* Trigger beforeChange. If nothing prevents the change, trigger
+ the slide change. */
+ $document.trigger(beforeChangeEvent, [currentIndex, index]);
+ if (!beforeChangeEvent.isDefaultPrevented()) {
+ $document.trigger(events.change, [currentIndex, index]);
+ changeHash(currentIndex, index);
+ currentIndex = index;
+ updateStates();
+ }
+ },
+
+ /*
+ jQuery.deck('next')
+
+ Moves to the next slide. If the last slide is already active, the call
+ is ignored.
+ */
+ next: function() {
+ methods.go(currentIndex+1);
+ },
+
+ /*
+ jQuery.deck('prev')
+
+ Moves to the previous slide. If the first slide is already active, the
+ call is ignored.
+ */
+ prev: function() {
+ methods.go(currentIndex-1);
+ },
+
+ /*
+ jQuery.deck('getSlide', index)
+
+ index: integer, optional
+
+ Returns a jQuery object containing the slide at index. If index is not
+ specified, the current slide is returned.
+ */
+ getSlide: function(index) {
+ index = typeof index !== 'undefined' ? index : currentIndex;
+ if (!indexInBounds(index)) {
+ return null;
+ }
+ return slides[index];
+ },
+
+ /*
+ jQuery.deck('getSlides')
+
+ Returns all slides as an array of jQuery objects.
+ */
+ getSlides: function() {
+ return slides;
+ },
+
+ /*
+ jQuery.deck('getTopLevelSlides')
+
+ Returns all slides that are not subslides.
+ */
+ getTopLevelSlides: function() {
+ var topLevelSlides = [];
+ var slideSelector = options.selectors.slides;
+ var subSelector = [slideSelector, slideSelector].join(' ');
+ $.each(slides, function(i, $slide) {
+ if (!$slide.is(subSelector)) {
+ topLevelSlides.push($slide);
+ }
+ });
+ return topLevelSlides;
+ },
+
+ /*
+ jQuery.deck('getNestedSlides', index)
+
+ index: integer, optional
+
+ Returns all the nested slides of the current slide. If index is
+ specified it returns the nested slides of the slide at that index.
+ If there are no nested slides this will return an empty array.
+ */
+ getNestedSlides: function(index) {
+ var targetIndex = index == null ? currentIndex : index;
+ var $targetSlide = $.deck('getSlide', targetIndex);
+ var $nesteds = $targetSlide.find(options.selectors.slides);
+ var nesteds = $nesteds.get();
+ return $.map(nesteds, function(slide, i) {
+ return $(slide);
+ });
+ },
+
+
+ /*
+ jQuery.deck('getContainer')
+
+ Returns a jQuery object containing the deck container as defined by the
+ container option.
+ */
+ getContainer: function() {
+ return $container;
+ },
+
+ /*
+ jQuery.deck('getOptions')
+
+ Returns the options object for the deck, including any overrides that
+ were defined at initialization.
+ */
+ getOptions: function() {
+ return options;
+ },
+
+ /*
+ jQuery.deck('extend', name, method)
+
+ name: string
+ method: function
+
+ Adds method to the deck namespace with the key of name. This doesn’t
+ give access to any private member data — public methods must still be
+ used within method — but lets extension authors piggyback on the deck
+ namespace rather than pollute jQuery.
+
+ $.deck('extend', 'alert', function(msg) {
+ alert(msg);
+ });
+
+ // Alerts 'boom'
+ $.deck('alert', 'boom');
+ */
+ extend: function(name, method) {
+ methods[name] = method;
+ }
+ };
+
+ /* jQuery extension */
+ $.deck = function(method, arg) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (methods[method]) {
+ return methods[method].apply(this, args);
+ }
+ else {
+ return methods.init(method, arg);
+ }
+ };
+
+ /*
+ The default settings object for a deck. All deck extensions should extend
+ this object to add defaults for any of their options.
+
+ options.classes.after
+ This class is added to all slides that appear after the 'next' slide.
+
+ options.classes.before
+ This class is added to all slides that appear before the 'previous'
+ slide.
+
+ options.classes.childCurrent
+ This class is added to all elements in the DOM tree between the
+ 'current' slide and the deck container. For standard slides, this is
+ mostly seen and used for nested slides.
+
+ options.classes.current
+ This class is added to the current slide.
+
+ options.classes.loading
+ This class is applied to the deck container during loading phases and is
+ primarily used as a way to short circuit transitions between states
+ where such transitions are distracting or unwanted. For example, this
+ class is applied during deck initialization and then removed to prevent
+ all the slides from appearing stacked and transitioning into place
+ on load.
+
+ options.classes.next
+ This class is added to the slide immediately following the 'current'
+ slide.
+
+ options.classes.onPrefix
+ This prefix, concatenated with the current slide index, is added to the
+ deck container as you change slides.
+
+ options.classes.previous
+ This class is added to the slide immediately preceding the 'current'
+ slide.
+
+ options.selectors.container
+ Elements matched by this CSS selector will be considered the deck
+ container. The deck container is used to scope certain states of the
+ deck, as with the onPrefix option, or with extensions such as deck.goto
+ and deck.menu.
+
+ options.selectors.slides
+ Elements matched by this selector make up the individual deck slides.
+ If a user chooses to pass the slide selector as the first argument to
+ $.deck() on initialization it does the same thing as passing in this
+ option and this option value will be set to the value of that parameter.
+
+ options.keys.next
+ The numeric keycode used to go to the next slide.
+
+ options.keys.previous
+ The numeric keycode used to go to the previous slide.
+
+ options.touch.swipeDirection
+ The direction swipes occur to cause slide changes. Can be 'horizontal',
+ 'vertical', or 'both'. Any other value or a falsy value will disable
+ swipe gestures for navigation.
+
+ options.touch.swipeTolerance
+ The number of pixels the users finger must travel to produce a swipe
+ gesture.
+
+ options.initLockTimeout
+ The number of milliseconds the init event will wait for BeforeInit event
+ locks to be released before firing the init event regardless.
+
+ 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.
+
+ options.setAriaHiddens
+ When set to true, deck.js will set aria hidden attributes for slides
+ that do not appear onscreen according to a typical heirarchical
+ deck structure. You may want to turn this off if you are using a theme
+ where slides besides the current slide are visible on screen and should
+ be accessible to screenreaders.
+ */
+ $.deck.defaults = {
+ classes: {
+ after: 'deck-after',
+ before: 'deck-before',
+ childCurrent: 'deck-child-current',
+ current: 'deck-current',
+ loading: 'deck-loading',
+ next: 'deck-next',
+ onPrefix: 'on-slide-',
+ previous: 'deck-previous'
+ },
+
+ selectors: {
+ container: '.deck-container',
+ slides: '.slide'
+ },
+
+ keys: {
+ // enter, space, page down, right arrow, down arrow,
+ next: [13, 32, 34, 39, 40],
+ // backspace, page up, left arrow, up arrow
+ previous: [8, 33, 37, 38]
+ },
+
+ touch: {
+ swipeDirection: 'horizontal',
+ swipeTolerance: 60
+ },
+
+ initLockTimeout: 10000,
+ hashPrefix: 'slide-',
+ preventFragmentScroll: true,
+ setAriaHiddens: 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);