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 dw_page.currentIDHighlight(); 13 jQuery('a.fn_top').on('mouseover', dw_page.footnoteDisplay); 14 dw_page.makeToggle('#dw__toc h3','#dw__toc > div'); 15 }, 16 17 /** 18 * Highlight the section when hovering over the appropriate section edit button 19 * 20 * @author Andreas Gohr <andi@splitbrain.org> 21 */ 22 sectionHighlight: function() { 23 jQuery('form.btn_secedit') 24 /* 25 * wrap the editable section in a div 26 */ 27 .each(function () { 28 let $tgt = jQuery(this).parent(); 29 const match = $tgt.attr('class').match(/(\s+|^)editbutton_(\d+)(\s+|$)/); 30 if(!match) return; 31 const nr = match[2]; 32 let $highlight = jQuery(); // holder for elements in the section to be highlighted 33 const $highlightWrap = jQuery('<div class="section_highlight_wrapper"></div>'); 34 35 // the edit button should be part of the highlight 36 $highlight = $highlight.add($tgt); 37 38 // Walk the dom tree in reverse to find the sibling which is or contains the section edit marker 39 while ($tgt.length > 0 && !($tgt.hasClass('sectionedit' + nr) || $tgt.find('.sectionedit' + nr).length)) { 40 $tgt = $tgt.prev(); 41 $highlight = $highlight.add($tgt); 42 } 43 // wrap the elements to be highlighted in the section highlight wrapper 44 $highlight.wrapAll($highlightWrap); 45 }) 46 /* 47 * highlight the section 48 */ 49 .on('mouseover', function () { 50 jQuery(this).parents('.section_highlight_wrapper').addClass('section_highlight'); 51 }) 52 /* 53 * remove highlight 54 */ 55 .on('mouseout', function () { 56 jQuery(this).parents('.section_highlight_wrapper').removeClass('section_highlight'); 57 }); 58 }, 59 60 61 /** 62 * Highlight internal link pointing to current page 63 * 64 * @author Henry Pan <dokuwiki@phy25.com> 65 */ 66 currentIDHighlight: function(){ 67 jQuery('a.wikilink1, a.wikilink2').filter('[data-wiki-id="'+JSINFO.id+'"]').wrap('<span class="curid"></span>'); 68 }, 69 70 /** 71 * Create/get a insitu popup used by the footnotes 72 * 73 * @param target - the DOM element at which the popup should be aligned at 74 * @param popup_id - the ID of the (new) DOM popup 75 * @return the Popup jQuery object 76 */ 77 insituPopup: function(target, popup_id) { 78 // get or create the popup div 79 var $fndiv = jQuery('#' + popup_id); 80 81 // popup doesn't exist, yet -> create it 82 if($fndiv.length === 0){ 83 $fndiv = jQuery(document.createElement('div')) 84 .attr('id', popup_id) 85 .addClass('insitu-footnote JSpopup') 86 .attr('aria-hidden', 'true') 87 .on('mouseleave', function () {jQuery(this).hide().attr('aria-hidden', 'true');}) 88 .attr('role', 'tooltip'); 89 jQuery('.dokuwiki:first').append($fndiv); 90 } 91 92 // position() does not support hidden elements 93 $fndiv.show().position({ 94 my: 'left top', 95 at: 'left center', 96 of: target 97 }).hide(); 98 99 return $fndiv; 100 }, 101 102 /** 103 * Display an insitu footnote popup 104 * 105 * @author Andreas Gohr <andi@splitbrain.org> 106 * @author Chris Smith <chris@jalakai.co.uk> 107 * @author Anika Henke <anika@selfthinker.org> 108 */ 109 footnoteDisplay: function () { 110 var $content = jQuery(jQuery(this).attr('href')) // Footnote text anchor 111 .parent().siblings('.content').clone(); 112 113 if (!$content.length) { 114 return; 115 } 116 117 // prefix ids on any elements with "insitu__" to ensure they remain unique 118 jQuery('[id]', $content).each(function(){ 119 var id = jQuery(this).attr('id'); 120 jQuery(this).attr('id', 'insitu__' + id); 121 }); 122 123 var content = $content.html().trim(); 124 // now put the content into the wrapper 125 dw_page.insituPopup(this, 'insitu__fn').html(content) 126 .show().attr('aria-hidden', 'false'); 127 }, 128 129 /** 130 * Makes an element foldable by clicking its handle 131 * 132 * This is used for the TOC toggling, but can be used for other elements 133 * as well. A state indicator is inserted into the handle and can be styled 134 * by CSS. 135 * 136 * To properly reserve space for the expanded element, the sliding animation is 137 * done on the children of the content. To make that look good and to make sure aria 138 * attributes are assigned correctly, it's recommended to make sure that the content 139 * element contains a single child element only. 140 * 141 * @param {selector} handle What should be clicked to toggle 142 * @param {selector} content This element will be toggled 143 * @param {int} state initial state (-1 = open, 1 = closed) 144 */ 145 makeToggle: function(handle, content, state){ 146 var $handle, $content, $clicky, $child, setClicky; 147 $handle = jQuery(handle); 148 if(!$handle.length) return; 149 $content = jQuery(content); 150 if(!$content.length) return; 151 152 // we animate the children 153 $child = $content.children(); 154 155 // class/display toggling 156 setClicky = function(hiding){ 157 if(hiding){ 158 $clicky.html('<span>+</span>'); 159 $handle.addClass('closed'); 160 $handle.removeClass('open'); 161 }else{ 162 $clicky.html('<span>−</span>'); 163 $handle.addClass('open'); 164 $handle.removeClass('closed'); 165 } 166 }; 167 168 $handle[0].setState = function(state){ 169 var hidden; 170 if(!state) state = 1; 171 172 // Assert that content instantly takes the whole space 173 $content.css('min-height', $content.height()).show(); 174 175 // stop any running animation 176 $child.stop(true, true); 177 178 // was a state given or do we toggle? 179 if(state === -1) { 180 hidden = false; 181 } else if(state === 1) { 182 hidden = true; 183 } else { 184 hidden = $child.is(':hidden'); 185 } 186 187 // update the state 188 setClicky(!hidden); 189 190 // Start animation and assure that $toc is hidden/visible 191 $child.dw_toggle(hidden, function () { 192 $content.toggle(hidden); 193 $content.attr('aria-expanded', hidden); 194 $content.css('min-height',''); // remove min-height again 195 }, true); 196 }; 197 198 // the state indicator 199 $clicky = jQuery(document.createElement('strong')); 200 201 // click function 202 $handle.css('cursor','pointer') 203 .on('click', $handle[0].setState) 204 .prepend($clicky); 205 206 // initial state 207 $handle[0].setState(state); 208 } 209}; 210 211jQuery(dw_page.init); 212