xref: /dokuwiki/lib/scripts/page.js (revision 940dbfe468f779c7c88783a4cecb82c4dc97143b)
1/**
2 * Page behaviours
3 *
4 * This class adds various behaviours to the rendered page
5 */
6dw_page = {
7    /**
8     * initialize page behaviours
9     */
10    init: function(){
11        dw_page.sectionHighlight();
12        jQuery('a.fn_top').mouseover(dw_page.footnoteDisplay);
13        dw_page.makeToggle('#dw__toc h3','#dw__toc > div');
14    },
15
16    /**
17     * Highlight the section when hovering over the appropriate section edit button
18     *
19     * @author Andreas Gohr <andi@splitbrain.org>
20     */
21    sectionHighlight: function() {
22        jQuery('form.btn_secedit')
23            .mouseover(function(){
24                var $tgt = jQuery(this).parent(),
25                    nr = $tgt.attr('class').match(/(\s+|^)editbutton_(\d+)(\s+|$)/)[2];
26
27                // Walk the DOM tree up (first previous siblings, then parents)
28                // until boundary element
29                while($tgt.length > 0 && !$tgt.hasClass('sectionedit' + nr)) {
30                    // $.last gives the DOM-ordered last element:
31                    // prev if present, else parent.
32                    $tgt = $tgt.prev().add($tgt.parent()).last();
33                    $tgt.addClass('section_highlight');
34                }
35            })
36            .mouseout(function(){
37                jQuery('.section_highlight').removeClass('section_highlight');
38            });
39    },
40
41    /**
42     * Create/get a insitu popup used by the footnotes
43     *
44     * @param target - the DOM element at which the popup should be aligned at
45     * @param popup_id - the ID of the (new) DOM popup
46     * @return the Popup jQuery object
47     */
48    insituPopup: function(target, popup_id) {
49        // get or create the popup div
50        var $fndiv = jQuery('#' + popup_id);
51
52        // popup doesn't exist, yet -> create it
53        if($fndiv.length === 0){
54            $fndiv = jQuery(document.createElement('div'))
55                .attr('id', popup_id)
56                .addClass('insitu-footnote JSpopup')
57                .mouseleave(function () {jQuery(this).hide();});
58            jQuery('.dokuwiki:first').append($fndiv);
59        }
60
61        // position() does not support hidden elements
62        $fndiv.show().position({
63            my: 'left top',
64            at: 'left center',
65            of: target
66        }).hide();
67
68        return $fndiv;
69    },
70
71    /**
72     * Display an insitu footnote popup
73     *
74     * @author Andreas Gohr <andi@splitbrain.org>
75     * @author Chris Smith <chris@jalakai.co.uk>
76     */
77    footnoteDisplay: function () {
78        var content = jQuery(jQuery(this).attr('href')) // Footnote text anchor
79                      .closest('div.fn').html();
80
81        if (content === null){
82            return;
83        }
84
85        // strip the leading content anchors and their comma separators
86        content = content.replace(/((^|\s*,\s*)<sup>.*?<\/sup>)+\s*/gi, '');
87
88        // prefix ids on any elements with "insitu__" to ensure they remain unique
89        content = content.replace(/\bid=(['"])([^"']+)\1/gi,'id="insitu__$2');
90
91        // now put the content into the wrapper
92        dw_page.insituPopup(this, 'insitu__fn').html(content).show();
93    },
94
95    /**
96     * Makes an element foldable by clicking its handle
97     *
98     * This is used for the TOC toggling, but can be used for other elements
99     * as well. A state indicator is inserted into the handle and can be styled
100     * by CSS.
101     *
102     * @param selector handle What should be clicked to toggle
103     * @param selector content This element will be toggled
104     */
105    makeToggle: function(handle, content, state){
106        var $handle, $content, $clicky, $child, setClicky;
107        $handle = jQuery(handle);
108        if(!$handle.length) return;
109        $content = jQuery(content);
110        if(!$content.length) return;
111
112        // we animate the children
113        $child = $content.children();
114
115        // class/display toggling
116        setClicky = function(hiding){
117            if(hiding){
118                $clicky.html('<span>+</span>');
119                $handle.addClass('closed');
120                $handle.removeClass('open');
121            }else{
122                $clicky.html('<span>−</span>');
123                $handle.addClass('open');
124                $handle.removeClass('closed');
125            }
126        };
127
128        $handle[0].setState = function(state){
129            var hidden;
130            if(!state) state = 1;
131
132            // Assert that content instantly takes the whole space
133            $content.css('min-height', $content.height()).show();
134
135            // stop any running animation
136            $child.stop(true, true);
137
138            // was a state given or do we toggle?
139            if(state === -1) {
140                hidden = false;
141            } else if(state === 1) {
142                hidden = true;
143            } else {
144                hidden = $child.is(':hidden');
145            }
146
147            // update the state
148            setClicky(!hidden);
149
150            // Start animation and assure that $toc is hidden/visible
151            $child.dw_toggle(hidden, function () {
152                $content.toggle(hidden);
153                $content.css('min-height',''); // remove min-height again
154            });
155        };
156
157        // the state indicator
158        $clicky = jQuery(document.createElement('strong'));
159
160        // click function
161        $handle.css('cursor','pointer')
162               .click($handle[0].setState)
163               .prepend($clicky);
164
165        // initial state
166        $handle[0].setState(state);
167    }
168};
169
170jQuery(dw_page.init);
171