xref: /template/sprintdoc/js/sidebar.js (revision 2710cc95d8cd175d44c258f33a8d4199b2c3cf5b)
1/**
2 * Sets up the sidebar behaviour
3 *
4 *  @author Andreas Gohr <gohr@cosmocode.de>
5 *  @author Michael Große <gohr@cosmocode.de>
6 *  @author Jana Deutschlaender <deutschlaender@cosmocode.de>
7 */
8jQuery(function () {
9    var $nav = jQuery('#dokuwiki__aside');
10    if (!$nav.length) return;
11
12    // id of the current sidebar, used to differentiate states
13    var sidebarId = '';
14    var divId = jQuery('#sidebarId');
15    if (typeof divId !== 'undefined') {
16        sidebarId = divId.attr('class').split(' ')[0];
17    }
18
19        /**
20         * closes sidebar
21         */
22    var setWideContent = function () {
23            $nav.find('div.nav-panel').hide(); // close all panels
24            jQuery('body').addClass('wide-content');
25            removeToggleStorage();
26            window.sessionStorage.setItem('wide-content', true);
27        },
28
29        /**
30         * removes information about the toggle-state
31         */
32        removeToggleStorage = function () {
33            for (var index=0; index <= window.sessionStorage.length; index += 1) {
34                var item = window.sessionStorage.getItem('sidebar-section-' + sidebarId + '-' + index + '-open');
35                if (!item) {
36                    continue;
37                }
38                window.sessionStorage.setItem('sidebar-section-' + sidebarId + '-' + index + '-open', 'false');
39            }
40        },
41
42        /**
43         * opens the sidebar
44         */
45        setDefaultContent = function () {
46            jQuery('body').removeClass('wide-content');
47            window.sessionStorage.setItem('wide-content', false);
48        },
49
50        /**
51         * Accessibility helper, focuses the first link witih the given element
52         *
53         * @param {jQuery} $elem
54         */
55        focusFirstSubLink = function ($elem) {
56            $elem.find('a').first().focus();
57        },
58
59        removeOpenStates = function() {
60            $nav.find('.is-open').removeClass('is-open');
61        },
62
63        /**
64         * Toggle a navigation panel
65         *
66         * @param {jQuery} $toggler The a toggler
67         */
68         toggleNav = function ($toggler) {
69            var $panel = $toggler.parent().next('div.nav-panel');
70            var isOpen = $panel.is(':visible');
71            // open sidebar on interaction
72            setDefaultContent();
73            // toggle the panel, focus first link after opening
74            $panel.dw_toggle(!isOpen, function () {
75                if (!isOpen) {
76                    focusFirstSubLink($panel);
77                    $toggler.addClass('is-open');
78                } else {
79                    $toggler.removeClass('is-open');
80                }
81            });
82            window.sessionStorage.setItem('sidebar-section-' + sidebarId + '-' + $toggler.data('index') + '-open', !isOpen);
83        },
84
85        /**
86         * Initialize the content navigation
87         *
88         * It mangles the sidebar content and handles inline Icon configuration
89         */
90        initContentNav = function () {
91            var $main = $nav.find('nav.nav-main');
92            if (!$main.length) return;
93
94            if(jQuery('body').hasClass('wide-content')) {
95                removeToggleStorage();
96            }
97
98            var ELEMENT = JSINFO.template.sprintdoc.sidebar_toggle_elements;
99            var $elements = $main.find(ELEMENT);
100            $elements.each(function (index) {
101                var $me = jQuery(this),
102
103                // prepare text and the optional icon
104                    data = $me.text().split('@', 2),
105                    text = data[0].trim();
106
107                var $icon = jQuery('<span class="ico">')
108                    .text(text.substr(0, 1).toUpperCase() + text.substr(1, 1).toLowerCase())
109                    .wrapInner('<strong>');
110                if (data[1]) {
111                    var src = data[1].trim();
112                    $icon.load(DOKU_BASE + 'lib/tpl/sprintdoc/svg.php?svg=' + src + '&e=1'); // directly embed
113                }
114
115                // make the new toggler
116                var $toggler = jQuery('<a>')
117                        .attr('href', '#')
118                        .attr('role', 'heading')
119                        .attr('aria-level', '2')
120                        .text(text)
121                        .wrapInner('<span class="lbl">')
122                        .prepend($icon)
123                        .data('index', index)
124                    ;
125                $toggler = jQuery('<div class="nav">').prepend($toggler);
126
127
128                // wrap all following siblings til the next element in a wrapper
129                var $wrap = jQuery('<div>')
130                    .addClass('nav-panel');
131                var $sibs = $me.nextAll();
132
133                for (var i = 0; i < $sibs.length; i++) {
134                    var $sib = jQuery($sibs[i]);
135                    if ($sib.is(ELEMENT)) break;
136                    $sib.detach().appendTo($wrap);
137                    addContentMenuCurrentStates($sib, $toggler);
138                }
139                $wrap.insertAfter($me);
140
141                // replace element with toggler
142                $me.replaceWith($toggler);
143
144                if ($toggler.parent('li').length) {
145                    $toggler.parent('li').addClass('toggler');
146                }
147
148                if (window.sessionStorage.getItem('sidebar-section-' + sidebarId + '-' + index + '-open') === 'true') {
149                    $wrap.css('display', 'block');
150                    setTogglerClass($toggler,'is-open');
151                }
152
153            });
154
155            // fade in the navigation (was hidden until now
156            $main.css({opacity: 0, visibility: "visible"}).animate({opacity: 1}, 200);
157        },
158
159        /**
160         * Initialize the open/close toggling of menu entries
161         */
162        initMenuHandling = function () {
163            $nav.on('click', 'div.nav a', function (e) {
164                toggleNav(jQuery(this));
165                e.preventDefault();
166            });
167        },
168
169        /**
170         * adds a given class to the toggler link
171         * @param $toggler link or parent of link to whom the class is added
172         * @param classVal class to be added
173         */
174        setTogglerClass = function ($toggler, classVal) {
175            if($toggler.is('a')) {
176                $toggler.addClass(classVal);
177            } else {
178                $toggler.find('a').addClass(classVal);
179            }
180        },
181
182        /**
183         * marks a $toggler link as active if the following menu has an active state
184         * @param $menuObj jQuery Object of the menu / container
185         * @param $toggler
186         */
187        addContentMenuCurrentStates = function ($menuObj, $toggler) {
188            if($menuObj[0] && String($menuObj[0].innerHTML).indexOf('curid') > 0) {
189                setTogglerClass($toggler,'is-active');
190            }
191        },
192
193        /**
194         * Make sure the content area is always as high as the sidebar
195         */
196        initContentMinHeight = function () {
197            var $sidebar = jQuery('.page-wrapper').find('> .tools').find('.col-xs-12');
198            if ($sidebar.length == 1) {
199                var num = parseFloat($sidebar.height());
200                if (!isNaN(num)) {
201                    jQuery('#dokuwiki__content').css('minHeight', num + 100);
202                }
203            }
204        },
205
206        /**
207         * Initialize the sidebar handle behaviour
208         */
209        initSidebarToggling = function () {
210            var $toggler = jQuery('.togglelink.page_main-content').find('a');
211            $toggler.click(function (e) {
212                e.preventDefault();
213                if (jQuery('body').hasClass('wide-content')) {
214                    setDefaultContent();
215                } else {
216                    setWideContent();
217                    removeOpenStates();
218                }
219            });
220
221            if (window.sessionStorage.getItem('wide-content') === 'true') {
222                setWideContent();
223            }
224        },
225
226        /**
227         * Show sidebar when accessing the search
228         */
229        initSearchToggling = function () {
230            jQuery('.toggleSearch').find('a').click(function (e) {
231                setDefaultContent();
232                e.preventDefault();
233                jQuery('#qsearch__in').focus();
234            });
235
236        },
237
238        /**
239         * Open and close the sidebar in mobile view
240         */
241        initMobileToggling = function () {
242            jQuery('.menu-togglelink').find('a').click(function (e) {
243                e.preventDefault();
244                var $body = jQuery('body');
245                $body.toggleClass('show-mobile-sidebar');
246            });
247        },
248
249        /**
250         * set is-active class if body has at least one of the given selectors
251         * @param selectorArray Array of selectors
252         * @param $nav container in which the $toggler is situated
253         */
254        setActive = function(selectorArray, $nav) {
255            for(var i=0; i< selectorArray.length; i++) {
256                var mode = selectorArray[i];
257                if(jQuery('body').is('.do-'+mode)){
258                    setTogglerClass($nav.find('.nav'),'is-active');
259                    $nav.find('a[href*="do='+mode+'"]').wrapAll('<span class="curid"></span>');
260                }
261            }
262        },
263
264        /**
265         * sets active states in site tool menu and user tool menu for certain modes
266         * adds sessionStorage behaviour equivalent approach to content menus
267         *
268         */
269        initTemplateMenues = function () {
270            var $body = jQuery('body'),
271                $siteTools = $nav.find('> .nav-sitetools'),
272                $userTools = $nav.find('> .nav-usermenu'),
273                $templateMenus = $nav.find('> nav:not(.nav-main)'),
274
275                stModes = ['recent', 'media', 'index'],
276                utModes = ['profile','admin'],
277                isWideContent = false;
278
279            /* set active states for site tool menu and user tool menu */
280            setActive(stModes,$siteTools);
281            setActive(utModes,$userTools);
282
283            if($body.is('.do-show') && $body.is('.wide-content')) {
284                window.sessionStorage.setItem('wide-content', true);
285                isWideContent = true;
286            }
287
288
289
290            /* set data attributes for sessionStorage and check onload if one of the template menus should be opened */
291            $templateMenus.each(function( index ) {
292                var $t = jQuery(this).find('.nav'),
293                    y = $nav.find('.nav-main').find('.nav').length,
294                    $toggler = ($t.is('a')) ? $t : $t.find('a:last'),
295                    tIndex = y + index;
296                $toggler.data('index', tIndex);
297
298                var item = window.sessionStorage.getItem('sidebar-section-' + sidebarId + '-' + tIndex + '-open');
299                if (item) {
300                    if(isWideContent) {
301                        window.sessionStorage.setItem('sidebar-section-' + sidebarId + '-' + tIndex + '-open', 'false');
302                    } else {
303                        if (item === 'true') {
304                            jQuery(this).find('.nav-panel').css('display', 'block');
305                            setTogglerClass($toggler, 'is-open');
306                        }
307                    }
308                }
309                //console.log(window.sessionStorage);
310
311            });
312
313        };
314
315
316    // main
317    initContentNav();
318    initSidebarToggling();
319    initTemplateMenues();
320    initMenuHandling();
321    initContentMinHeight();
322    initSearchToggling();
323    initMobileToggling();
324});
325
326