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