Ridiculous giant update
[deck.js.git] / core / deck.core.js
blob220bfca8e95d56f26ca398dc4e8beb3f2c227ba8
1 /*!
2 Deck JS - deck.core - v1.0
3 Copyright (c) 2011 Caleb Troughton
4 Dual licensed under the MIT license and GPL license.
5 https://github.com/imakewebthings/deck.js/blob/master/MIT-license.txt
6 https://github.com/imakewebthings/deck.js/blob/master/GPL-license.txt
7 */
9 /*
10 The deck.core module provides all the basic functionality for creating and
11 moving through a deck.  It does so by applying classes to indicate the state of
12 the deck and its slides, allowing CSS to take care of the visual representation
13 of each state.  It also provides methods for navigating the deck and inspecting
14 its state, as well as basic key bindings for going to the next and previous
15 slides.  More functionality is provided by wholly separate extension modules
16 that use the API provided by core.
18 (function($, deck, document, undefined) {
19         var slides, // Array of all the uh, slides...
20         current, // Array index of the current slide
21         
22         events = {
23                 /*
24                 This event fires whenever the current slide changes, whether by way of
25                 next, prev, or go. The callback function is passed two parameters, from
26                 and to, equal to the indices of the old slide and the new slide
27                 respectively.
28                 
29                 $(document).bind('deck.change', function(event, from, to) {
30                    alert('Moving from slide ' + from + ' to ' + to);
31                 });
32                 */
33                 change: 'deck.change',
34                 
35                 /*
36                 This event fires at the end of deck initialization. Extensions should
37                 implement any code that relies on user extensible options (key bindings,
38                 element selectors, classes) within a handler for this event. Native
39                 events associated with Deck JS should be scoped under a .deck event
40                 namespace, as with the example below:
41                 
42                 var $d = $(document);
43                 $.deck.defaults.keys.myExtensionKeycode = 70; // 'h'
44                 $d.bind('deck.init', function() {
45                    $d.bind('keydown.deck', function(event) {
46                       if (event.which == $.deck.getOptions().keys.myExtensionKeycode) {
47                          // Rock out
48                       }
49                    });
50                 });
51                 */
52                 initialize: 'deck.init' 
53         },
54         
55         options = {},
56         $d = $(document),
57         
58         /*
59         Internal function. Updates slide and container classes based on which
60         slide is the current slide.
61         */
62         updateStates = function() {
63                 var oc = options.classes,
64                 osc = options.selectors.container,
65                 $container = $(osc),
66                 old = $container.data('onSlide'),
67                 $all = $();
68                 
69                 // Container state
70                 $container.removeClass(oc.onPrefix + old)
71                         .addClass(oc.onPrefix + current)
72                         .data('onSlide', current);
73                 
74                 // Remove and re-add child-current classes for nesting
75                 $('.' + oc.current).parentsUntil(osc).removeClass(oc.childCurrent);
76                 slides[current].parentsUntil(osc).addClass(oc.childCurrent);
77                 
78                 // Remove previous states
79                 $.each(slides, function(i, el) {
80                         $all = $all.add(el);
81                 });
82                 $all.removeClass([
83                         oc.before,
84                         oc.previous,
85                         oc.current,
86                         oc.next,
87                         oc.after
88                 ].join(" "));
89                 
90                 // Add new states back in
91                 slides[current].addClass(oc.current);
92                 if (current > 0) {
93                         slides[current-1].addClass(oc.previous);
94                 }
95                 if (current + 1 < slides.length) {
96                         slides[current+1].addClass(oc.next);
97                 }
98                 if (current > 1) {
99                         $.each(slides.slice(0, current - 1), function(i, el) {
100                                 el.addClass(oc.before);
101                         });
102                 }
103                 if (current + 2 < slides.length) {
104                         $.each(slides.slice(current+2), function(i, el) {
105                                 el.addClass(oc.after);
106                         });
107                 }
108         },
109         
110         /* Methods exposed in the jQuery.deck namespace */
111         methods = {
112                 
113                 /*
114                 jQuery.deck(selector, options)
115                 
116                 selector: string | jQuery | array
117                 options: object, optional
118                                 
119                 Initializes the deck, using each element matched by selector as a slide.
120                 May also be passed an array of string selectors or jQuery objects, in
121                 which case each selector in the array is considered a slide. The second
122                 parameter is an optional options object which will extend the default
123                 values.
124                 
125                 $.deck('.slide');
126                 
127                 or
128                 
129                 $.deck([
130                    '#first-slide',
131                    '#second-slide',
132                    '#etc'
133                 ]);
134                 */      
135                 init: function(elements, opts) {
136                         $.extend(true, options, $[deck].defaults, opts);
137                         slides = [];
138                         current = 0;
139                         
140                         // Fill slides array depending on parameter type
141                         if ($.isArray(elements)) {
142                                 $.each(elements, function(i, e) {
143                                         slides.push($(e));
144                                 });
145                         }
146                         else {
147                                 $(elements).each(function(i, e) {
148                                         slides.push($(e));
149                                 });
150                         }
151                         
152                         /* Remove any previous bindings, and rebind key events */
153                         $d.unbind('keydown.deck').bind('keydown.deck', function(e) {
154                                 switch (e.which) {
155                                         case options.keys.next:
156                                                 methods.next();
157                                                 break;
158                                         case options.keys.previous:
159                                                 methods.prev();
160                                                 break;
161                                 }
162                         });
163                         
164                         updateStates();
165                         $d.trigger(events.initialize);
166                 },
167                 
168                 /*
169                 jQuery.deck('go', index)
170                 
171                 index: integer
172                 
173                 Moves to the slide at the specified index. Index is 0-based, so
174                 $.deck('go', 0); will move to the first slide. If index is out of bounds
175                 or not a number the call is ignored.
176                 */
177                 go: function(index) {
178                         if (typeof index != 'number' || index < 0 || index >= slides.length) return;
179                         
180                         $d.trigger(events.change, [current, index]);
181                         current = index;
182                         updateStates();
183                 },
184                 
185                 /*
186                 jQuery.deck('next')
187                 
188                 Moves to the next slide. If the last slide is already active, the call
189                 is ignored.
190                 */
191                 next: function() {
192                         methods.go(current+1);
193                 },
194                 
195                 /*
196                 jQuery.deck('prev')
197                 
198                 Moves to the previous slide. If the first slide is already active, the
199                 call is ignored.
200                 */
201                 prev: function() {
202                         methods.go(current-1);
203                 },
204                 
205                 /*
206                 jQuery.deck('getSlide', index)
207                 
208                 index: integer, optional
209                 
210                 Returns a jQuery object containing the slide at index. If index is not
211                 specified, the current slide is returned.
212                 */
213                 getSlide: function(index) {
214                         var i = index ? index : current;
215                         if (typeof i != 'number' || i < 0 || i >= slides.length) return null;
216                         return slides[i];
217                 },
218                 
219                 /*
220                 jQuery.deck('getSlides')
221                 
222                 Returns all slides as an array of jQuery objects.
223                 */
224                 getSlides: function() {
225                         return slides;
226                 },
227                 
228                 /*
229                 jQuery.deck('getContainer')
230                 
231                 Returns a jQuery object containing the deck container as defined by the
232                 container option.
233                 */
234                 getContainer: function() {
235                         return $(options.selectors.container);
236                 },
237                 
238                 /*
239                 jQuery.deck('getOptions')
240                 
241                 Returns the options object for the deck, including any overrides that
242                 were defined at initialization.
243                 */
244                 getOptions: function() {
245                         return options;
246                 },
247                 
248                 /*
249                 jQuery.deck('extend', name, method)
250                 
251                 name: string
252                 method: function
253                 
254                 Adds method to the deck namespace with the key of name. This doesn’t
255                 give access to any private member data — public methods must still be
256                 used within method — but lets extension authors piggyback on the deck
257                 namespace rather than pollute jQuery.
258                 
259                 $.deck('extend', 'alert', function(msg) {
260                    alert(msg);
261                 });
263                 // Alerts 'boom'
264                 $.deck('alert', 'boom');
265                 */
266                 extend: function(name, method) {
267                         methods[name] = method;
268                 }
269         };
270         
271         /* jQuery extension */
272         $[deck] = function(method, arg) {
273                 if (methods[method]) {
274                         return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
275                 }
276                 else {
277                         return methods.init(method, arg);
278                 }
279         };
280         
281         /*
282         The default settings object for a deck. All deck extensions should extend
283         this object to add defaults for any of their options.
284         
285         options.classes.after
286                 This class is added to all slides that appear after the 'next' slide.
287         
288         options.classes.before
289                 This class is added to all slides that appear before the 'previous'
290                 slide.
291                 
292         options.classes.childCurrent
293                 This class is added to all elements in the DOM tree between the
294                 'current' slide and the deck container. For standard slides, this is
295                 mostly seen and used for nested slides.
296                 
297         options.classes.current
298                 This class is added to the current slide.
299                 
300         options.classes.next
301                 This class is added to the slide immediately following the 'current'
302                 slide.
303                 
304         options.classes.onPrefix
305                 This prefix, concatenated with the current slide index, is added to the
306                 deck container as you change slides.
307                 
308         options.classes.previous
309                 This class is added to the slide immediately preceding the 'current'
310                 slide.
311                 
312         options.selectors.container
313                 Elements matched by this CSS selector will be considered the deck
314                 container. The deck container is used to scope certain states of the
315                 deck, as with the onPrefix option, or with extensions such as deck.goto
316                 and deck.menu.
317                 
318         options.keys.next
319                 The numeric keycode used to go to the next slide.
320                 
321         options.keys.previous
322                 The numeric keycode used to go to the previous slide.
323         */
324         $[deck].defaults = {
325                 classes: {
326                         after: 'deck-after',
327                         before: 'deck-before',
328                         childCurrent: 'deck-child-current',
329                         current: 'deck-current',
330                         next: 'deck-next',
331                         onPrefix: 'on-slide-',
332                         previous: 'deck-previous'
333                 },
334                 
335                 selectors: {
336                         container: '.deck-container'
337                 },
338                 
339                 keys: {
340                         next: 39, // right arrow key
341                         previous: 37 // left arrow key
342                 }
343         };
344         
345         $d.ready(function() {
346                 $('html').addClass('ready');
347         });
348 })(jQuery, 'deck', document);