page.js 7.1 KB

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