summaryrefslogtreecommitdiffstats
path: root/doc/backends/deckjs/deck.js/extensions/toc/deck.toc.js
blob: b16bd6a943313353f91fa80eea78a2c33bb6ef8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
/*!
Deck JS - deck.toc
Copyright (c) 2011 Remi BARRAQUAND
Dual licensed under the MIT license and GPL license.
https://github.com/imakewebthings/deck.js/blob/master/MIT-license.txt
https://github.com/imakewebthings/deck.js/blob/master/GPL-license.txt
*/

/*
This module provides a support for TOC to the deck.
*/

(function($, deck, undefined) {
    var $d = $(document);
    var $toc;
        
    /*
	Extends defaults/options.
	
        options.classes.toc
		This class is added to the deck container when showing the slide
                toc.
	
	options.keys.toc
		The numeric keycode used to toggle between showing and hiding 
                the slide toc.
    
	options.selectors.toc
		The element matching this selector displays the toc.
    
	options.selectors.tocTitle
		The element matching this selector displays the current title
                of the slide i.e the current h1.

        options.selectors.tocSection
		The element matching this selector displays the current section
                of the slide i.e the current h2.
	
        options.selectors.tocSubSection
		The element matching this selector displays the current 
                subsection of the slide i.e the current h3.
    
        options.selectors.tocSubSubSection
		The element matching this selector displays the current 
                subsubsection of the slide i.e the current h4.
	*/
    $.extend(true, $[deck].defaults, {
        classes: {
            toc: 'deck-toc-frame'
        },
		
        keys: {
            toc: 84 // t
        },
                
        selectors: {
            toc: '.deck-toc',
            tocTitle: '.deck-toc-h1',
            tocSection: '.deck-toc-h2',
            tocSubSection: '.deck-toc-h3',
            tocSubSubSection: '.deck-toc-h4',
            tocStatus: '.deck-toc-status'
        }
    });

    /*
	jQuery.deck('showToc')
	
	Shows the slide toc by adding the class specified by the toc class option
	to the deck container.
	*/
    $[deck]('extend', 'showToc', function() {
        $[deck]('getContainer').addClass($[deck]('getOptions').classes.toc);
    });
	
    /*
	jQuery.deck('hideToc')
	
	Hides the slide toc by removing the class specified by the toc class
	option from the deck container.
	*/
    $[deck]('extend', 'hideToc', function() {
        $[deck]('getContainer').removeClass($[deck]('getOptions').classes.toc);
    });

    /*
	jQuery.deck('toggleToc')
	
	Toggles between showing and hiding the TOC.
	*/
    $[deck]('extend', 'toggleToc', function() {
        $[deck]('getContainer').hasClass($[deck]('getOptions').classes.toc) ? 
        $[deck]('hideToc') : $[deck]('showToc');
    });
        
    /*
        jQuery.deck('Init')
        */
    $d.bind('deck.init', function() {
        var opts = $[deck]('getOptions');
        var container = $[deck]('getContainer');
        
        /* Bind key events */
        $d.unbind('keydown.decktoc').bind('keydown.decktoc', function(e) {
            if (e.which === opts.keys.toc || $.inArray(e.which, opts.keys.toc) > -1) {
                $[deck]('toggleToc');
                e.preventDefault();
            }
        });
        
        /* Hide TOC panel when user click on container */
        container.click(function(e){
            $[deck]('hideToc');
        });
                
        /* Init TOC and append it to the document */
        $toc = new TOC();
        $($[deck]('getOptions').selectors.toc).append($toc.root);
                
        /* Go through all slides */
        $.each($[deck]('getSlides'), function(i, $el) {
            var slide = $[deck]('getSlide',i);
            //var tocElementFound = false;
            
            /* If there is a toc item, push it in the TOC */
            for(var level=1; level<6; level++) {
                if( slide.find("h"+level).length > 0) {
                    var tocTitle = "";
                    var $tocElement = slide.find("h"+level+":first");
                    if( $tocElement.attr("title") != undefined && $tocElement.attr("title") != "") {
                        tocTitle = $tocElement.attr("title");
                    } else {
                        tocTitle = $tocElement.text();
                    }
                    $toc.push(level, tocTitle, slide);
                    $toc.tag(slide);
                    //tocElementFound = true;
                }
            }
            
            /* Tag the slide with the current TOC level */
            $toc.tag(slide);
        });
    })
    /* Update current slide number with each change event */
    .bind('deck.change', function(e, from, to) {
        var opts = $[deck]('getOptions');
        var slideTo = $[deck]('getSlide', to);
        var container = $[deck]('getContainer');
		
        if (container.hasClass($[deck]('getOptions').classes.toc)) {
            container.scrollTop(slideTo.offset().top);
        }
            
        /* update toc status */
        if( slideTo.data("toc") ) {
            // reset
            $(opts.selectors.tocTitle).text("");
            $(opts.selectors.tocSection).text("");
            $(opts.selectors.tocSubSection).text("");
            $(opts.selectors.tocSubSubSection).text("");

            if( slideTo.hasClass('hide-toc-status') ) {
                $(opts.selectors.tocStatus).hide();
            } else {
                $(opts.selectors.tocStatus).show();
                // update according to the current context
                var $context = $toc.context(slideTo.data('toc'))            
                for(var level=1; level<=$context.length; level++) {
                    switch(level) {
                        case 1: 
                            $(opts.selectors.tocTitle).text($context[level-1]);
                            break;
                        case 2: 
                            $(opts.selectors.tocSection).text($context[level-1]);
                            break;
                        case 3: 
                            $(opts.selectors.tocSubSection).text($context[level-1]); 
                            break;
                        case 4: 
                            $(opts.selectors.tocSubSubSection).text($context[level-1]); 
                            break;
                    }
                }
            } 
        }
    });
        
    /*
        Simple TOC manager (must be improved)
        */
    var TOC = function() {
        
        this.root = $("<ul/>", {"class":"toc"});
            
        /* 
            Push new item in the TOC 
          
            depth is the level (e.g. 1 for h1, 2 for h2, etc.)
            title is the toc-item title
            slide is the slide that provides the toc-element
            */
        this.push = function(depth,title,slide) {
            inc(depth);
                
            /* Create toc element */
            var $tocElm = $("<li />", {
                id: "toc-"+($c.join('-'))
            }).data({ // keep track of the slide in case...
                slide: slide,
                title: title
            }).append($("<a />", { // create an hyperlink
                href: "#"+$(slide).attr('id'),
                text: title
            })).append($("<ul />"));
                                
            /* insert it at the right place */
            var $target = this.root;
            if( depth > 1) {
                $target = ($target.find("li#toc-"+($c.slice(0,$c.length-1).join('-')))).children("ul");
            }
            $tocElm.appendTo($target);
        };
        
        /*
            Tag the slide with the current TOC level.
        
            slide is the slide to tag
            */
        this.tag = function(slide) {
            slide.data({
                toc: $c.slice(0)
            });
        }
        
        /*
            Get the current TOC context
        
            path is the current path in the TOC
            */
        this.context = function(path) {
            $context = new Array();
            var $target = this.root;
            for(var depth=0; depth<path.length; depth++) {
                var tocElm = $target.find("li#toc-"+(path.slice(0,depth+1).join('-')))
                $context.push(tocElm.data('title'));
                $target = (tocElm).children("ul");
            }
            
            return $context;
        }
            
        /* cursor */
        var $c = [-1];
        function inc(depth) {
            var current_depth = $c.length;
            if(depth>current_depth) {
                for(i=current_depth;i<depth;i++) {
                    $c.push(0);
                }
            } else if( current_depth>depth) {
                for(i=depth;i<current_depth;i++) {
                    $c.pop();
                    $c[depth-1]++
                }
            } else {
                $c[depth-1]++
            }
        }
    }
})(jQuery, 'deck');