PageSourceSearch

https://jackdreilly.github.io/presentations/cdc-2012/lib/deck.core.js

js jackdreilly.github.io collected 2026-10-03 09:21:39 UTC 14,048 bytes, 498 lines download raw bytes

1/*!
2Deck JS - deck.core
3Copyright (c) 2011 Caleb Troughton
4Dual licensed under the MIT license and GPL license.
5https://github.com/imakewebthings/deck.js/blob/master/MIT-license.txt
6https://github.com/imakewebthings/deck.js/blob/master/GPL-license.txt
7*/
8
9/*
10The deck.core module provides all the basic functionality for creating and
11moving through a deck.  It does so by applying classes to indicate the state of
12the deck and its slides, allowing CSS to take care of the visual representation
13of each state.  It also provides methods for navigating the deck and inspecting
14its state, as well as basic key bindings for going to the next and previous
15slides.  More functionality is provided by wholly separate extension modules
16that use the API provided by core.
17*/
18(function($, deck, document, undefined) {
19	var slides, // Array of all the uh, slides...
20	current, // Array index of the current slide
21	$container, // Keeping this cached
22	
23	events = {
24		/*
25		This event fires whenever the current slide changes, whether by way of
26		next, prev, or go. The callback function is passed two parameters, from
27		and to, equal to the indices of the old slide and the new slide
28		respectively. If preventDefault is called on the event within this handler
29		the slide change does not occur.
30		
31		$(document).bind('deck.change', function(event, from, to) {
32		   alert('Moving from slide ' + from + ' to ' + to);
33		});
34		*/
35		change: 'deck.change',
36		
37		/*
38		This event fires at the beginning of deck initialization, after the options
39		are set but before the slides array is created.  This event makes a good hook
40		for preprocessing extensions looking to modify the deck.
41		*/
42		beforeInitialize: 'deck.beforeInit',
43		
44		/*
45		This event fires at the end of deck initialization. Extensions should
46		implement any code that relies on user extensible options (key bindings,
47		element selectors, classes) within a handler for this event. Native
48		events associated with Deck JS should be scoped under a .deck event
49		namespace, as with the example below:
50		
51		var $d = $(document);
52		$.deck.defaults.keys.myExtensionKeycode = 70; // 'h'
53		$d.bind('deck.init', function() {
54		   $d.bind('keydown.deck', function(event) {
55		      if (event.which === $.deck.getOptions().keys.myExtensionKeycode) {
56		         // Rock out
57		      }
58		   });
59		});
60		*/
61		initialize: 'deck.init' 
62	},
63	
64	options = {},
65	$d = $(document),
66	
67	/*
68	Internal function. Updates slide and container classes based on which
69	slide is the current slide.
70	*/
71	updateStates = function() {
72		var oc = options.classes,
73		osc = options.selectors.container,
74		old = $container.data('onSlide'),
75		$all = $();
76		
77		// Container state
78		$container.removeClass(oc.onPrefix + old)
79			.addClass(oc.onPrefix + current)
80			.data('onSlide', current);
81		
82		// Remove and re-add child-current classes for nesting
83		$('.' + oc.current).parentsUntil(osc).removeClass(oc.childCurrent);
84		slides[current].parentsUntil(osc).addClass(oc.childCurrent);
85		
86		// Remove previous states
87		$.each(slides, function(i, el) {
88			$all = $all.add(el);
89		});
90		$all.removeClass([
91			oc.before,
92			oc.previous,
93			oc.current,
94			oc.next,
95			oc.after
96		].join(" "));
97		
98		// Add new states back in
99		slides[current].addClass(oc.current);
100		if (current > 0) {
101			slides[current-1].addClass(oc.previous);
102		}
103		if (current + 1 < slides.length) {
104			slides[current+1].addClass(oc.next);
105		}
106		if (current > 1) {
107			$.each(slides.slice(0, current - 1), function(i, el) {
108				el.addClass(oc.before);
109			});
110		}
111		if (current + 2 < slides.length) {
112			$.each(slides.slice(current+2), function(i, el) {
113				el.addClass(oc.after);
114			});
115		}
116	},
117	
118	/* Methods exposed in the jQuery.deck namespace */
119	methods = {
120		
121		/*
122		jQuery.deck(selector, options)
123		
124		selector: string | jQuery | array
125		options: object, optional
126				
127		Initializes the deck, using each element matched by selector as a slide.
128		May also be passed an array of string selectors or jQuery objects, in
129		which case each selector in the array is considered a slide. The second
130		parameter is an optional options object which will extend the default
131		values.
132		
133		$.deck('.slide');
134		
135		or
136		
137		$.deck([
138		   '#first-slide',
139		   '#second-slide',
140		   '#etc'
141		]);
142		*/	
143		init: function(elements, opts) {
144			var startTouch,
145			tolerance,
146			esp = function(e) {
147				e.stopPropagation();
148			};
149			
150			options = $.extend(true, {}, $[deck].defaults, opts);
151			slides = [];
152			current = 0;
153			$container = $(options.selectors.container);
154			tolerance = options.touch.swipeTolerance;
155			
156			// Pre init event for preprocessing hooks
157			$d.trigger(events.beforeInitialize);
158			
159			// Hide the deck while states are being applied to kill transitions
160			$container.addClass(options.classes.loading);
161			
162			// Fill slides array depending on parameter type
163			if ($.isArray(elements)) {
164				$.each(elements, function(i, e) {
165					slides.push($(e));
166				});
167			}
168			else {
169				$(elements).each(function(i, e) {
170					slides.push($(e));
171				});
172			}
173			
174			/* Remove any previous bindings, and rebind key events */
175			$d.unbind('keydown.deck').bind('keydown.deck', function(e) {
176				if (e.which === options.keys.next || $.inArray(e.which, options.keys.next) > -1) {
177					methods.next();
178					e.preventDefault();
179				}
180				else if (e.which === options.keys.previous || $.inArray(e.which, options.keys.previous) > -1) {
181					methods.prev();
182					e.preventDefault();
183				}
184			})
185			/* Stop propagation of key events within editable elements */
186			.undelegate('input, textarea, select, button, meter, progress, [contentEditable]', 'keydown', esp)
187			.delegate('input, textarea, select, button, meter, progress, [contentEditable]', 'keydown', esp);
188			
189			/* Bind touch events for swiping between slides on touch devices */
190			$container.unbind('touchstart.deck').bind('touchstart.deck', function(e) {
191				if (!startTouch) {
192					startTouch = $.extend({}, e.originalEvent.targetTouches[0]);
193				}
194			})
195			.unbind('touchmove.deck').bind('touchmove.deck', function(e) {
196				$.each(e.originalEvent.changedTouches, function(i, t) {
197					if (startTouch && t.identifier === startTouch.identifier) {
198						if (t.screenX - startTouch.screenX > tolerance || t.screenY - startTouch.screenY > tolerance) {
199							$[deck]('prev');
200							startTouch = undefined;
201						}
202						else if (t.screenX - startTouch.screenX < -1 * tolerance || t.screenY - startTouch.screenY < -1 * tolerance) {
203							$[deck]('next');
204							startTouch = undefined;
205						}
206						return false;
207					}
208				});
209				e.preventDefault();
210			})
211			.unbind('touchend.deck').bind('touchend.deck', function(t) {
212				$.each(t.originalEvent.changedTouches, function(i, t) {
213					if (startTouch && t.identifier === startTouch.identifier) {
214						startTouch = undefined;
215					}
216				});
217			})
218			.scrollLeft(0).scrollTop(0);
219			
220			/*
221			Kick iframe videos, which dont like to redraw w/ transforms.
222			Remove this if Webkit ever fixes it.
223			 */
224			$.each(slides, function(i, $el) {
225				$el.unbind('webkitTransitionEnd.deck').bind('webkitTransitionEnd.deck',
226				function(event) {
227					if ($el.hasClass($[deck]('getOptions').classes.current)) {
228						var embeds = $(this).find('iframe').css('opacity', 0);
229						window.setTimeout(function() {
230							embeds.css('opacity', 1);
231						}, 100);
232					}
233				});
234			});
235			
236			if (slides.length) {
237				updateStates();
238			}
239			
240			// Show deck again now that slides are in place
241			$container.removeClass(options.classes.loading);
242			$d.trigger(events.initialize);
243		},
244		
245		/*
246		jQuery.deck('go', index)
247		
248		index: integer | string
249		
250		Moves to the slide at the specified index if index is a number. Index is
251		0-based, so $.deck('go', 0); will move to the first slide. If index is a
252		string this will move to the slide with the specified id. If index is out
253		of bounds or doesn't match a slide id the call is ignored.
254		*/
255		go: function(index) {
256			var e = $.Event(events.change),
257			ndx;
258			
259			/* Number index, easy. */
260			if (typeof index === 'number' && index >= 0 && index < slides.length) {
261				ndx = index;
262			}
263			/* Id string index, search for it and set integer index */
264			else if (typeof index === 'string') {
265				$.each(slides, function(i, $slide) {
266					if ($slide.attr('id') === index) {
267						ndx = i;
268						return false;
269					}
270				});
271			};
272			
273			/* Out of bounds, id doesn't exist, illegal input, eject */
274			if (typeof ndx === 'undefined') return;
275			
276			$d.trigger(e, [current, ndx]);
277			if (e.isDefaultPrevented()) {
278				/* Trigger the event again and undo the damage done by extensions. */
279				$d.trigger(events.change, [ndx, current]);
280			}
281			else {
282				current = ndx;
283				updateStates();
284			}
285		},
286		
287		/*
288		jQuery.deck('next')
289		
290		Moves to the next slide. If the last slide is already active, the call
291		is ignored.
292		*/
293		next: function() {
294			methods.go(current+1);
295		},
296		
297		/*
298		jQuery.deck('prev')
299		
300		Moves to the previous slide. If the first slide is already active, the
301		call is ignored.
302		*/
303		prev: function() {
304			methods.go(current-1);
305		},
306		
307		/*
308		jQuery.deck('getSlide', index)
309		
310		index: integer, optional
311		
312		Returns a jQuery object containing the slide at index. If index is not
313		specified, the current slide is returned.
314		*/
315		getSlide: function(index) {
316			var i = typeof index !== 'undefined' ? index : current;
317			if (typeof i != 'number' || i < 0 || i >= slides.length) return null;
318			return slides[i];
319		},
320		
321		/*
322		jQuery.deck('getSlides')
323		
324		Returns all slides as an array of jQuery objects.
325		*/
326		getSlides: function() {
327			return slides;
328		},
329		
330		/*
331		jQuery.deck('getContainer')
332		
333		Returns a jQuery object containing the deck container as defined by the
334		container option.
335		*/
336		getContainer: function() {
337			return $container;
338		},
339		
340		/*
341		jQuery.deck('getOptions')
342		
343		Returns the options object for the deck, including any overrides that
344		were defined at initialization.
345		*/
346		getOptions: function() {
347			return options;
348		},
349		
350		/*
351		jQuery.deck('extend', name, method)
352		
353		name: string
354		method: function
355		
356		Adds method to the deck namespace with the key of name. This doesn’t
357		give access to any private member data — public methods must still be
358		used within method — but lets extension authors piggyback on the deck
359		namespace rather than pollute jQuery.
360		
361		$.deck('extend', 'alert', function(msg) {
362		   alert(msg);
363		});
364
365		// Alerts 'boom'
366		$.deck('alert', 'boom');
367		*/
368		extend: function(name, method) {
369			methods[name] = method;
370		}
371	};
372	
373	/* jQuery extension */
374	$[deck] = function(method, arg) {
375		if (methods[method]) {
376			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
377		}
378		else {
379			return methods.init(method, arg);
380		}
381	};
382	
383	/*
384	The default settings object for a deck. All deck extensions should extend
385	this object to add defaults for any of their options.
386	
387	options.classes.after
388		This class is added to all slides that appear after the 'next' slide.
389	
390	options.classes.before
391		This class is added to all slides that appear before the 'previous'
392		slide.
393		
394	options.classes.childCurrent
395		This class is added to all elements in the DOM tree between the
396		'current' slide and the deck container. For standard slides, this is
397		mostly seen and used for nested slides.
398		
399	options.classes.current
400		This class is added to the current slide.
401		
402	options.classes.loading
403		This class is applied to the deck container during loading phases and is
404		primarily used as a way to short circuit transitions between states
405		where such transitions are distracting or unwanted.  For example, this
406		class is applied during deck initialization and then removed to prevent
407		all the slides from appearing stacked and transitioning into place
408		on load.
409		
410	options.classes.next
411		This class is added to the slide immediately following the 'current'
412		slide.
413		
414	options.classes.onPrefix
415		This prefix, concatenated with the current slide index, is added to the
416		deck container as you change slides.
417		
418	options.classes.previous
419		This class is added to the slide immediately preceding the 'current'
420		slide.
421		
422	options.selectors.container
423		Elements matched by this CSS selector will be considered the deck
424		container. The deck container is used to scope certain states of the
425		deck, as with the onPrefix option, or with extensions such as deck.goto
426		and deck.menu.
427		
428	options.keys.next
429		The numeric keycode used to go to the next slide.
430		
431	options.keys.previous
432		The numeric keycode used to go to the previous slide.
433		
434	options.touch.swipeTolerance
435		The number of pixels the users finger must travel to produce a swipe
436		gesture.
437	*/
438	$[deck].defaults = {
439		classes: {
440			after: 'deck-after',
441			before: 'deck-before',
442			childCurrent: 'deck-child-current',
443			current: 'deck-current',
444			loading: 'deck-loading',
445			next: 'deck-next',
446			onPrefix: 'on-slide-',
447			previous: 'deck-previous'
448		},
449		
450		selectors: {
451			container: '.deck-container'
452		},
453		
454		keys: {
455			// enter, space, page down, right arrow, down arrow,
456			next: [13, 32, 34, 39, 40],
457			// backspace, page up, left arrow, up arrow
458			previous: [8, 33, 37, 38]
459		},
460		
461		touch: {
462			swipeTolerance: 60
463		}
464	};
465	
466	$d.ready(function() {
467		$('html').addClass('ready');
468	});
469	
470	/*
471	FF + Transforms + Flash video don't get along...
472	Firefox will reload and start playing certain videos after a
473	transform.  Blanking the src when a previously shown slide goes out
474	of view prevents this.
475	*/
476	$d.bind('deck.change', function(e, from, to) {
477		var oldFrames = $[deck]('getSlide', from).find('iframe'),
478		newFrames = $[deck]('getSlide', to).find('iframe');
479		
480		oldFrames.each(function() {
481	    	var $this = $(this),
482	    	curSrc = $this.attr('src');
483            
484            if(curSrc) {
485            	$this.data('deck-src', curSrc).attr('src', '');
486            }
487		});
488		
489		newFrames.each(function() {
490			var $this = $(this),
491			originalSrc = $this.data('deck-src');
492			
493			if (originalSrc) {
494				$this.attr('src', originalSrc);
495			}
496		});
497	});
498})(jQuery, 'deck', document);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.