From 00540a38be97858e71163f53dddf8dee53185b1d Mon Sep 17 00:00:00 2001
From: Andreas Gohr <andi@splitbrain.org>
Date: Sat, 10 Dec 2005 20:37:09 +0100
Subject: [PATCH] unobstrusive JS for TOC, better onload handling

This path adds more unobstrusive JavaScript for the TOC handling. It also
loads JavaScript initialiezers as soon as the DOM is parsed for Mozilla-based
Browsers as described at http://dean.edwards.name/weblog/2005/09/busted/ - a
IE solution was not chosen yet.

darcs-hash:20051210193709-7ad00-771461e56d9661caf9ca733a6d617f009e24d0b7.gz
---
 inc/parser/xhtml.php       |  5 ++-
 lib/exe/js.php             | 10 +++++-
 lib/scripts/events.js      | 52 +++++++++++++++++++++++++++++-
 lib/scripts/script.js      | 65 +++++++++++++++++++++++++-------------
 lib/tpl/default/design.css | 11 ++++---
 5 files changed, 111 insertions(+), 32 deletions(-)

diff --git a/inc/parser/xhtml.php b/inc/parser/xhtml.php
index f6dcbc038..73c2ada1c 100644
--- a/inc/parser/xhtml.php
+++ b/inc/parser/xhtml.php
@@ -86,11 +86,10 @@ class Doku_Renderer_xhtml extends Doku_Renderer {
     function toc_open() {
         global $lang;
         $this->doc .= '<div class="toc">'.DOKU_LF;
-        $this->doc .= '<div class="tocheader">';
-        $this->doc .= ' <script type="text/javascript">showTocToggle("+","-")</script>';
+        $this->doc .= '<div class="tocheader" id="toc__header">';
         $this->doc .= $lang['toc'];
         $this->doc .= '</div>'.DOKU_LF;
-        $this->doc .= '<div id="tocinside">'.DOKU_LF;
+        $this->doc .= '<div id="toc__inside">'.DOKU_LF;
     }
     
     function tocbranch_open($level) {
diff --git a/lib/exe/js.php b/lib/exe/js.php
index ca1d83948..b776f957b 100644
--- a/lib/exe/js.php
+++ b/lib/exe/js.php
@@ -77,6 +77,7 @@ function js_out(){
     // init stuff
     js_runonstart("ajax_qsearch.init('qsearch_in','qsearch_out')");
     js_runonstart("addEvent(document,'click',closePopups)");
+    js_runonstart('addTocToggle()');
 
     if($edit){
         // size controls
@@ -114,6 +115,13 @@ function js_out(){
     // load user script
     @readfile(DOKU_CONF.'userscript.js');
 
+
+    // initialize init pseudo event
+    echo 'if (document.addEventListener) {';
+    echo '    document.addEventListener("DOMContentLoaded", window.fireoninit, null);';
+    echo '}';
+    echo 'addEvent(window,"load",window.fireoninit);';
+
     // end output buffering and get contents
     $js = ob_get_contents();
     ob_end_clean();
@@ -184,7 +192,7 @@ function js_escape($string){
  * @author Andreas Gohr <andi@splitbrain.org>
  */
 function js_runonstart($func){
-    print "addEvent(window,'load',function(){ $func; });";
+    echo "addInitEvent(function(){ $func; });";
 }
 
 /**
diff --git a/lib/scripts/events.js b/lib/scripts/events.js
index f6360219d..fb65b1bd7 100644
--- a/lib/scripts/events.js
+++ b/lib/scripts/events.js
@@ -59,4 +59,54 @@ fixEvent.preventDefault = function() {
 };
 fixEvent.stopPropagation = function() {
 	this.cancelBubble = true;
-};
\ No newline at end of file
+};
+
+
+/**
+ * Pseudo event handler to be fired after the DOM was parsed or
+ * on window load at last.
+ *
+ * @author based upon some code by Dean Edwards
+ * @author Andreas Gohr
+ * @see    http://dean.edwards.name/weblog/2005/09/busted/
+ */
+window.fireoninit = function() {
+  // quit if this function has already been called
+  if (arguments.callee.done) return;
+  // flag this function so we don't do the same thing twice
+  arguments.callee.done = true;
+
+	if (typeof window.oninit == 'function') {
+		window.oninit();
+  }
+}
+
+/**
+ * This is a pseudo Event that will be fired by the above function
+ *
+ * Use addInitEvent to bind to this event!
+ *
+ * @author Andreas Gohr
+ */
+window.oninit = function() {
+}
+
+/**
+ * Bind a function to the window.init pseudo event
+ *
+ * @author Simon Willison
+ * @see http://simon.incutio.com/archive/2004/05/26/addLoadEvent
+ */
+function addInitEvent(func) {
+  var oldoninit = window.oninit;
+  if (typeof window.oninit != 'function') {
+    window.oninit = func;
+  } else {
+    window.oninit = function() {
+      oldoninit();
+      func();
+    };
+  }
+}
+
+
diff --git a/lib/scripts/script.js b/lib/scripts/script.js
index b91859265..d589aa0b4 100644
--- a/lib/scripts/script.js
+++ b/lib/scripts/script.js
@@ -97,6 +97,19 @@ function escapeQuotes(text) {
   return text;
 }
 
+/**
+ * Adds a node as the first childenode to the given parent
+ *
+ * @see appendChild()
+ */
+function prependChild(parent,element) {
+	if(!parent.firstChild){
+		parent.appendChild(element);
+	}else{
+		parent.insertBefore(element,parent.firstChild);
+	}
+}
+
 /**
  * Prints a animated gif to show the search is performed
  *
@@ -143,37 +156,45 @@ function suggestWikiname(){
 }
 
 /**
- * This prints the switch to toggle the Table of Contents
+ * Adds the toggle switch to the TOC
  */
-function showTocToggle(showtxt,hidetxt) {
-  if(document.getElementById) {
-		show = '<img src="'+DOKU_BASE+'lib/images/arrow_down.gif" alt="'+showtxt+'">';
-		hide = '<img src="'+DOKU_BASE+'lib/images/arrow_up.gif" alt="'+hidetxt+'">';
-
-    document.writeln('<div class=\'toctoggle\'><a href="javascript:toggleToc()" class="toc">' +
-    '<span id="showlink" style="display:none;">' + show + '</span>' +
-    '<span id="hidelink">' + hide + '</span>' +
-    '</a></div>');
-  }
+function addTocToggle() {
+	if(!document.getElementById) return;
+	var header = document.getElementById('toc__header');
+  if(!header) return;
+
+  var showimg     = document.createElement('img');
+	showimg.id      = 'toc__show';
+  showimg.src     = DOKU_BASE+'lib/images/arrow_down.gif';
+  showimg.alt     = '+';
+	showimg.onclick = toggleToc;
+  showimg.style.display = 'none';
+
+	var hideimg     = document.createElement('img');
+	hideimg.id      = 'toc__hide';
+  hideimg.src     = DOKU_BASE+'lib/images/arrow_up.gif';
+  hideimg.alt     = '-';
+	hideimg.onclick = toggleToc;
+	
+  prependChild(header,showimg);
+  prependChild(header,hideimg);
 }
 
 /**
  * This toggles the visibility of the Table of Contents
  */
 function toggleToc() {
-  var toc = document.getElementById('tocinside');
-  var showlink=document.getElementById('showlink');
-  var hidelink=document.getElementById('hidelink');
+  var toc = document.getElementById('toc__inside');
+  var showimg = document.getElementById('toc__show');
+  var hideimg = document.getElementById('toc__hide');
   if(toc.style.display == 'none') {
-    toc.style.display = tocWas;
-    hidelink.style.display='';
-    showlink.style.display='none';
+    toc.style.display      = '';
+    hideimg.style.display = '';
+    showimg.style.display = 'none';
   } else {
-    tocWas = toc.style.display;
-    toc.style.display = 'none';
-    hidelink.style.display='none';
-    showlink.style.display='';
-
+    toc.style.display      = 'none';
+    hideimg.style.display = 'none';
+    showimg.style.display = '';
   }
 }
 
diff --git a/lib/tpl/default/design.css b/lib/tpl/default/design.css
index 303bff0f7..229bbc58c 100644
--- a/lib/tpl/default/design.css
+++ b/lib/tpl/default/design.css
@@ -482,17 +482,18 @@ div.tocheader {
 }
 
 div.toctoggle {
-  float:right;
-  margin-top:0.3em;
-  margin-right:3px;
 }
 
-div.toctoggle img {
+div.tocheader img {
   width:0.8em;
   height:0.8em;
+  float:right;
+  margin-top:0.3em;
+  margin-right:3px;
+  cursor: pointer;
 }
 
-#tocinside {
+#toc__inside {
   border: 1px solid __dark__;
   background-color: __white__;
   text-align: left;
-- 
GitLab