changeset 200:4c121c2aa844

Added candydoc files for docs.
author Anders Johnsen <skabet@gmail.com>
date Sun, 10 Aug 2008 17:09:41 +0200
parents 5e05c03d1558
children 4f94b5adbc8a
files doc/candydoc/CANDYDOC.txt doc/candydoc/candy.ddoc doc/candydoc/explorer.js doc/candydoc/ie56hack.css doc/candydoc/img/bg.gif doc/candydoc/img/candydoc.gif doc/candydoc/img/outline/alias.gif doc/candydoc/img/outline/bg.gif doc/candydoc/img/outline/class.gif doc/candydoc/img/outline/enum.gif doc/candydoc/img/outline/func.gif doc/candydoc/img/outline/module.gif doc/candydoc/img/outline/package.gif doc/candydoc/img/outline/struct.gif doc/candydoc/img/outline/template.gif doc/candydoc/img/outline/var.gif doc/candydoc/img/package/bg.gif doc/candydoc/img/tree/shim.gif doc/candydoc/img/tree/tb.gif doc/candydoc/img/tree/tbr.gif doc/candydoc/img/tree/tbrm.gif doc/candydoc/img/tree/tbrp.gif doc/candydoc/img/tree/tr.gif doc/candydoc/img/tree/trm.gif doc/candydoc/img/tree/trp.gif doc/candydoc/modules.ddoc doc/candydoc/style.css doc/candydoc/tree.js doc/candydoc/util.js
diffstat 29 files changed, 1033 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/candydoc/CANDYDOC.txt	Sun Aug 10 17:09:41 2008 +0200
@@ -0,0 +1,56 @@
+
+CanDyDOC is fileset for creating advanced documentation of programs written
+in D programming language. CanDyDOC adds some eye-candy and advanced navigation
+features to .html documents that are generated by D compiler and known as DDOC.
+Produced result is AJAX web-application that is compatible with all mainstream
+web browsers.
+
+This is a fork of the original candydoc, distributed with DSSS. For more
+information on DSSS and DSSS' fork of candydoc, see
+http://www.dsource.org/projects/dsss/
+
+CanDyDOC includes following files:
+    - candy.ddoc
+        File with DDOC macro definitions. You haven't to touch it.
+
+    - modules.ddoc
+        You should enumerate all modules that would be avaible for navigation
+        here.
+
+    - style.css
+        Cascading style sheet file that defines look of produced documentation.
+        You can leave this file without changes or adjust fonts, colors, etc
+        here. See it for documentation.
+
+    - ie56hack.css
+        CSS file to force Internet Explorer 5/6 browser show documentation
+        as it looks like in standard-compliant browsers.
+
+    - tree.js
+        JavaScript implementing tree control that looks like native one.
+
+    - util.js
+        Common cross-browser routines.
+
+    - explorer.js
+        Heart of every documentation's page. Controls generation, behaviour and
+        navigation of a page.
+
+    - numerous of image files in 'img' folder.
+
+How to use:
+    1) Put 'candydoc' directory in place where documentation will be.
+    2) Modify modules.ddoc file: enumerate all modules that should be avaible
+       for navigation.
+    3) Modify style.css file if you want to change style of documentation. Or
+       leave it unmodified to apply defaul theme.
+    4) Run documentation compilation with candy.ddoc and modules.ddoc specified
+       on command line.
+    5) Enjoy a result :)
+
+Known bugs:
+    - Explorer window doesn't work on Safari browser.
+    - Scroll bar positions are not adjusted after explorer's tab change in Opera
+      browser. So it is posible to see nothing on some tab: solution is to
+      return to a previous tab, scroll it to top and then return back.
+    - Overlapping of some elements when too few horizontal place avaible.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/candydoc/candy.ddoc	Sun Aug 10 17:09:41 2008 +0200
@@ -0,0 +1,51 @@
+DDOC =  
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8">
+<meta content="text/javascript" http-equiv="content-script-type">
+<title>$(TITLE)</title>
+<link rel="stylesheet" type="text/css" href="candydoc/style.css">
+<!--[if lt IE 7]><link rel="stylesheet" type="text/css" href="candydoc/ie56hack.css"><![endif]-->
+<script language="JavaScript" src="candydoc/util.js" type="text/javascript"></script>
+<script language="JavaScript" src="candydoc/tree.js" type="text/javascript"></script>
+<script language="JavaScript" src="candydoc/explorer.js" type="text/javascript"></script>
+</head><body>
+<div id="tabarea"></div><div id="explorerclient"></div>
+<div id="content"><script>explorer.initialize("$(TITLE)");</script>
+	<table class="content">
+		<tr><td id="docbody"><h1>$(TITLE)</h1>$(BODY)</td></tr>
+		<tr><td id="docfooter">
+			Page was generated with
+			<img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px">
+			on $(DATETIME)
+		</td></tr>
+	</table>
+</div>
+$(ADD_MODULES)
+</body></html>
+
+
+DDOC_DECL =
+<script>explorer.outline.writeEnabled = true;</script>
+$(DT <span class="decl">$0</span>)
+<script>explorer.outline.writeEnabled = false;</script>
+
+
+DDOC_PSYMBOL =
+<span class="currsymbol">$0</span>
+<script>explorer.outline.addDecl('$0');</script>
+
+
+DDOC_MEMBERS =
+<script>explorer.outline.incSymbolLevel();</script>
+$(DL $0)
+<script>explorer.outline.decSymbolLevel();</script>
+
+
+DDOC_PARAM_ID =
+<td nowrap valign="top" style="padding-right: 8px">$0</td>
+
+               
+DDOC_PARAM =<span class="funcparam">$0</span>
+ADD_MODULES =<script>$(MODULES)</script>
+MODULE =explorer.packageExplorer.addModule("$0");
+MODULE_FULL =explorer.packageExplorer.addModuleFull("$0");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/candydoc/explorer.js	Sun Aug 10 17:09:41 2008 +0200
@@ -0,0 +1,319 @@
+/* This file is a part of CanDyDOC fileset.
+   File is written by Victor Nakoryakov and placed into the public domain.
+
+   This file is javascript with classes that represents explorer window.
+   And things related to navigation. */
+   
+var explorer = new Explorer();
+
+///////////////////////////////////////////////////////////////////////////////
+// Current symbol marker class constructor
+///////////////////////////////////////////////////////////////////////////////
+function Marker()
+{
+    this.top    = document.createElement("div");
+    this.middle = document.createElement("div");
+    this.bottom = document.createElement("div");
+    this.container = document.createElement("div");
+    
+    this.setTo = function(term)
+    {
+        // find definition related to `term`
+        var def = term.nextSibling;
+        while (def && def.nodeName != "DD")
+            def = def.nextSibling;
+            
+        var defHeight = 0;
+        var childrenHeight = 0; // children of current declaration
+        if (def)
+        {
+            defHeight = def.offsetHeight;
+            var child = def.firstChild;
+            
+            // traverse until DL tag, until children definition
+            while (child && child.nodeName != "DL")
+                child = child.nextSibling;
+                
+            if (child)
+                childrenHeight = child.offsetHeight;
+        }
+        
+        this.top.style.height = term.offsetHeight;
+        this.middle.style.height = defHeight - childrenHeight;
+        this.bottom.style.height = childrenHeight;
+        
+        if (childrenHeight == 0)
+            this.bottom.style.display = "none";
+        else
+            this.bottom.style.display = "";
+        
+        this.container.style.left = getLeft(term) - 8;
+        this.container.style.top = getTop(term);
+        this.container.style.display = "";
+    }
+        
+    ///////////////////////////////////////////////////////////////////////////
+    this.container.style.position = "absolute";
+    this.container.style.display = "none";
+    
+    this.top.className = "markertop";
+    this.middle.className = "markermiddle";
+    this.bottom.className = "markerbottom";
+    
+    this.container.appendChild(this.top);
+    this.container.appendChild(this.middle);
+    this.container.appendChild(this.bottom);
+    
+    //document.body.appendChild( this.container );
+    
+    // Workaround bug in IE 5/6. We can not append anything to document body until
+    // full page load.
+    window.marker = this;
+    if (window.addEventListener)
+        window.addEventListener("load", new Function("document.body.appendChild( window.marker.container );"), false);
+    else if (window.attachEvent)
+        window.attachEvent("onload", new Function("document.body.appendChild( window.marker.container );"));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Outline class constructor
+///////////////////////////////////////////////////////////////////////////////
+function Outline()
+{
+    this.tree           = new TreeView();
+    this.mountPoint     = null;    
+    this.writeEnabled   = false;
+    this.marker         = new Marker();        
+    this.classRegExp    = new RegExp;
+    this.structRegExp   = new RegExp;
+    this.enumRegExp     = new RegExp;
+    this.templateRegExp = new RegExp;
+    this.aliasRegExp    = new RegExp;
+    this.funcRegExp     = new RegExp;
+    
+    this.incSymbolLevel = function()
+    {
+        if (this.mountPoint == null)
+            this.mountPoint = this.tree.children[ 0 ];
+        else
+            this.mountPoint = this.mountPoint.lastChild();
+    }
+
+    this.decSymbolLevel = function()
+    {
+        // place icons near items according to extracted below type
+        for (var i = 0; i < this.mountPoint.children.length; ++i)
+        {
+            child = this.mountPoint.children[i];
+            var term = child.termRef;
+            
+            // find first span node
+            var n = term.firstChild;
+            while (n && n.nodeName != "SPAN")
+                n = n.nextSibling;
+            
+            if (!n) // shouldn't happen
+                continue;
+            
+            var iconSrc;
+            if (n.firstChild.nodeName == "#text")
+            {
+                var text = n.firstChild.data; // text before declaration
+                
+                if ( this.classRegExp.test(text) )
+                    iconSrc = "candydoc/img/outline/class.gif";
+                else if ( this.structRegExp.test(text) )
+                    iconSrc = "candydoc/img/outline/struct.gif";
+                else if ( this.enumRegExp.test(text) )
+                    iconSrc = "candydoc/img/outline/enum.gif";
+                else if ( this.templateRegExp.test(text) )
+                    iconSrc = "candydoc/img/outline/template.gif";
+                else if ( this.aliasRegExp.test(text) )
+                    iconSrc = "candydoc/img/outline/alias.gif";
+                else // function or variable? check whether '(' ')' exists on the right
+                {
+                    var np = n.firstChild;
+                    while (np && np.nodeName != "SCRIPT") // find our script "onDecl"
+                        np = np.nextSibling;
+                        
+                    if (np && np.nextSibling && np.nextSibling.nodeName == "#text" &&
+                        this.funcRegExp.test(np.nextSibling.data))
+                    {
+                        iconSrc = "candydoc/img/outline/func.gif";
+                    }
+                    else
+                        iconSrc = "candydoc/img/outline/var.gif";
+                }
+            }
+            else // enum member ?
+                iconSrc = "candydoc/img/outline/var.gif";
+                    
+            child.icon.src = iconSrc;
+            child.icon.width = 16;
+            child.icon.height = 16;
+        }
+        
+        this.mountPoint = this.mountPoint.parentNode;
+    }
+
+    this.addDecl = function(decl)
+    {
+        function getLastLeaf(elem)
+        {
+            if (elem.childNodes.length > 0)
+                return getLastLeaf(elem.lastChild);
+            else
+                return elem;
+        }
+
+        function getCurrentTerm()
+        {
+            var ret = getLastLeaf( document.getElementById("content") );
+            while (ret && ret.nodeName != "DT")
+                ret = ret.parentNode;
+            
+            return ret;
+        }
+        
+        if (this.writeEnabled)
+        {
+            var node = this.mountPoint.createChild(decl);
+            node.termRef = getCurrentTerm();
+            node.setOnclick( new Function("explorer.outline.mark(this.termRef);") );
+        }
+    }
+
+    this.mark = function(term)
+    {
+        this.marker.setTo(term);
+        window.scrollTo(0, getTop(term) - getWindowHeight() / 6);    
+    }
+        
+    
+    this.classRegExp.compile("(.*\b)?class(\b.*)?");
+    this.structRegExp.compile("(.*\b)?struct(\b.*)?");
+    this.enumRegExp.compile("(.*\b)?enum(\b.*)?");
+    this.templateRegExp.compile("(.*\b)?template(\b.*)?");
+    this.aliasRegExp.compile("(.*\b)?alias(\b.*)?");
+    this.funcRegExp.compile(/.*\(.*/);    
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Package explorer class constructor
+///////////////////////////////////////////////////////////////////////////////
+function PackageExplorer()
+{
+    this.tree = new TreeView(true);    
+    
+    this.addModule2 = function(mod, full)
+    {
+        var moduleIco = "candydoc/img/outline/module.gif";
+        var packageIco = "candydoc/img/outline/package.gif";
+
+        var path = mod.split("\.");
+        var node = this.tree.branch(path[0]);
+        if ( !node )
+            node = this.tree.createBranch(path[0], (path.length == 1) ? moduleIco : packageIco);
+        
+        for (var i = 1; i < path.length; ++i)
+        {
+            var prev = node;
+            node = node.child(path[i]);
+            if (!node)
+                node = prev.createChild(path[i], (path.length == i + 1) ? moduleIco : packageIco);
+                
+            if (path.length == i + 1) {
+                if (full)
+                    node.setRef(mod + ".html");
+                else
+                    node.setRef(path[i] + ".html");
+            }
+        }
+    }
+    
+    this.addModuleFull = function(mod)
+    {
+        this.addModule2(mod, true);
+    }
+    
+    this.addModule = function(mod)
+    {
+        this.addModule2(mod, false);
+    }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Explorer class constructor
+///////////////////////////////////////////////////////////////////////////////
+function Explorer()
+{
+    this.outline         = new Outline();
+    this.packageExplorer = new PackageExplorer();
+    this.tabs            = new Array();
+    this.tabCount        = 0;
+    
+    this.initialize = function(moduleName)
+    {
+        this.tabArea = document.getElementById("tabarea");
+        this.clientArea = document.getElementById("explorerclient");
+        
+        // prevent text selection
+        this.tabArea.onmousedown = new Function("return false;");
+        this.tabArea.onclick = new Function("return true;");
+        this.tabArea.onselectstart = new Function("return false;");
+        this.clientArea.onmousedown = new Function("return false;");
+        this.clientArea.onclick = new Function("return true;");
+        this.clientArea.onselectstart = new Function("return false;");
+        
+        this.outline.tree.createBranch( moduleName, "candydoc/img/outline/module.gif" );
+        
+        // create tabs
+        this.createTab("Outline", this.outline.tree.domEntry);
+        this.createTab("Package", this.packageExplorer.tree.domEntry);
+    }
+    
+    this.createTab = function(name, domEntry)
+    {
+        var tab = new Object();
+        this.tabs[name] = tab;
+        this.tabCount++;
+        
+        tab.domEntry = domEntry;
+        tab.labelSpan = document.createElement("span");
+        
+        if (this.tabCount > 1)
+        {
+            tab.labelSpan.className = "inactivetab";
+            tab.domEntry.style.display = "none";
+        }
+        else
+        {
+            tab.labelSpan.className = "activetab";
+            tab.domEntry.style.display = "";
+        }
+        
+        tab.labelSpan.appendChild( document.createTextNode(name) );
+        tab.labelSpan.owner = this;
+        tab.labelSpan.onclick = new Function("this.owner.setSelection('" + name + "');");
+        
+        this.tabArea.appendChild( tab.labelSpan );
+        this.clientArea.appendChild( domEntry );
+    }
+    
+    this.setSelection = function(tabName)
+    {
+        for (name in this.tabs)
+        {
+            this.tabs[name].labelSpan.className = "inactivetab";
+            this.tabs[name].domEntry.style.display = "none";
+        }
+        
+        this.tabs[tabName].labelSpan.className = "activetab";
+        this.tabs[tabName].domEntry.style.display = "";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/candydoc/ie56hack.css	Sun Aug 10 17:09:41 2008 +0200
@@ -0,0 +1,21 @@
+/* This file is a part of CanDyDOC fileset.
+   File is written by Victor Nakoryakov and placed into the public domain.
+
+   This file is CSS to work around IE6 and earlier bugs. It's included just
+   in these browsers. */
+
+
+/* Some magic to emulate unsupported "position: fixed" style. */
+#tabarea
+{
+	_position: absolute;
+	_top: expression(eval(document.body.scrollTop+8));
+}
+
+/* ditto */
+#explorerclient 
+{
+	_position: absolute;
+	_top: expression(eval(document.body.scrollTop+24));
+	_height: expression(eval(document.body.clientHeight-48));
+}
Binary file doc/candydoc/img/bg.gif has changed
Binary file doc/candydoc/img/candydoc.gif has changed
Binary file doc/candydoc/img/outline/alias.gif has changed
Binary file doc/candydoc/img/outline/bg.gif has changed
Binary file doc/candydoc/img/outline/class.gif has changed
Binary file doc/candydoc/img/outline/enum.gif has changed
Binary file doc/candydoc/img/outline/func.gif has changed
Binary file doc/candydoc/img/outline/module.gif has changed
Binary file doc/candydoc/img/outline/package.gif has changed
Binary file doc/candydoc/img/outline/struct.gif has changed
Binary file doc/candydoc/img/outline/template.gif has changed
Binary file doc/candydoc/img/outline/var.gif has changed
Binary file doc/candydoc/img/package/bg.gif has changed
Binary file doc/candydoc/img/tree/shim.gif has changed
Binary file doc/candydoc/img/tree/tb.gif has changed
Binary file doc/candydoc/img/tree/tbr.gif has changed
Binary file doc/candydoc/img/tree/tbrm.gif has changed
Binary file doc/candydoc/img/tree/tbrp.gif has changed
Binary file doc/candydoc/img/tree/tr.gif has changed
Binary file doc/candydoc/img/tree/trm.gif has changed
Binary file doc/candydoc/img/tree/trp.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/candydoc/modules.ddoc	Sun Aug 10 17:09:41 2008 +0200
@@ -0,0 +1,2 @@
+MODULES =
+	$(MODULE_FULL ast.Decl)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/candydoc/style.css	Sun Aug 10 17:09:41 2008 +0200
@@ -0,0 +1,169 @@
+/* This file is a part of CanDyDOC fileset.
+   File is written by Victor Nakoryakov and placed into the public domain.
+
+   This file is main CSS file of CanDyDOC. You may adjust some part of
+   parameters to control how result documentation would looks like. See
+   further documentation for details. */
+
+
+
+/* This controls how background would looks like and
+   sets some document-scope defaults. */
+body 
+{
+	/* These parameters control default font. */
+	font-family: Verdana, Arial, Helvetica, sans-serif;
+	font-size: 10pt;
+	color: #666666;
+	
+	/* These control look of background. Note that you have to use
+       fixed background to keep documentation good-looking in
+       IE6 and earlier. Otherwise whole *explorer* will jerk while
+       scrolling. If you do not want to use background at all use
+       some invalid url, e.g. url(foo). */
+	background-color: #e6fcea;
+	background: url(img/bg.gif) fixed;
+	
+	/* Don't touch. Necessary for IE6 and earlier. */
+	height: 100%;	
+}
+
+
+
+/* Style applied to all tables. Actualy there are two: one table is
+   that contains contant and footer with CanDyDOC logo, and others
+   are that contains functions' parameters description. */
+table 
+{
+	font-family: Verdana, Arial, Helvetica, sans-serif;
+	font-size: 10pt;
+	color: #666666;
+	text-align: justify;
+}
+
+
+/* Style used for all hyperlinks. */
+a:link {  color: #009900; text-decoration: none }
+a:visited {  color: #009999; text-decoration: none }
+a:hover {  color: #0033cc; text-decoration: none }
+a:active {  color: #0033cc; text-decoration: none }
+
+/*
+table.matrix 
+{
+	border-left: double 3px #666666;
+	border-right: double 3px #666666;
+	margin-left: 3em;
+}
+*/
+
+/* Style appled to declarations. E.g. 'void foo(int a, float b);' */
+span.decl { font-size: 10pt; font-weight: bold; color: #000000; text-align: left }
+/* Style appled to current declaration's symbol. E.g. 'foo' in 'void foo(int a, float b);' */
+span.currsymbol { font-size: 12pt; color: #009900 }
+/* Style appled to function's parameters. E.g. 'a' and 'b' in 'void foo(int a, float b);' */
+span.funcparam  { font-style: italic; font-weight: normal; color: #331200 }
+
+/* Style for div that actualy contains documenation. */
+#content
+{
+	padding-right: 8px;
+	position: absolute;
+	left: 245px;
+	top: 8px;
+	text-align: justify;
+}
+
+/* Style for table that is inside div considered above. Contains documentaton
+   itself and footer with CanDyDOC logo. */
+table.content
+{
+	margin-bottom: 8px;
+	border-spacing: 0px;
+	border-collapse: collapse;
+	background-color: #ffffff;
+}
+
+/* Style for cell of above considered table that contains documentation itself. */
+#docbody
+{
+	padding: 8px 20px 8px 20px;
+	border: solid 1px #009900;
+}
+
+/* Style for cell that contains CanDyDOC logo. */
+#docfooter
+{
+	height: 16px;
+	background-color: #ddeedd;
+	padding: 0px 8px 0px 8px;
+	border: solid 1px #009900;
+}
+
+/* Style applied to currently active tab of explorer window. */
+span.activetab
+{
+	background-color: #0033cc;
+	border-top: solid 2px #009900;
+	color: #ffffff;
+	font-weight: bold;
+	padding-left: 4px;
+	padding-right: 4px;
+	padding-top: 1px;
+	margin-right: 1px;
+}
+
+/* Style applied to currently inactive tab of explorer window. */
+span.inactivetab
+{
+	background-color: #000066;
+	color: #cccccc;
+	font-weight: normal;
+	padding-left: 4px;
+	padding-right: 4px;
+	padding-top: 0px;
+	margin-right: 1px;
+}
+
+/* Style applied to div that contains tabs of explorer. Note that if
+   you want to change it's position you have to change position of
+   #explorerclient, #content and corresponding values in ie56hack.css */
+#tabarea
+{
+	position: fixed;
+	top: 8px;
+	width: 205px;
+	height: 16px;
+	cursor: default;
+}
+
+
+/* Style applied to div that contains tree in explorer. Note that if
+   you want to change it's position you have to change position of
+   #tabarea, #content and corresponding values in ie56hack.css */
+#explorerclient 
+{
+	position: fixed;
+	top: 24px;
+	bottom: 8px;
+	width: 205px;
+	overflow: auto;
+	background-color: #fcfffc;
+	border: solid 2px #0033cc;
+	padding: 4px;
+	cursor: default;
+	color: Black;
+}
+
+/* Following 3 styles control appearance of marker that appears
+   if you click some entity in outline window. */
+div.markertop	{ border-left: solid 2px #0033cc;}
+div.markermiddle{ border-left: dotted 2px #0033cc;}
+div.markerbottom{ border-left: dotted 2px #66cc66;}
+
+/* Style applied to preformated text used to show examples. */
+pre.d_code
+{
+	border: dotted 1px #9c9;
+	background-color: #eeffee;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/candydoc/tree.js	Sun Aug 10 17:09:41 2008 +0200
@@ -0,0 +1,374 @@
+/* This file is a part of CanDyDOC fileset.
+   File is written by Victor Nakoryakov and placed into the public domain.
+
+   This file is javascript with classes that represents native style tree control. */
+   
+var pmNone = 0;
+var pmPlus = 1;
+var pmMinus = 2;
+
+var hlNone = 0;
+var hlGrey = 1;
+var hlSelected = 2;
+
+function TreeView(hrefMode)
+{
+    this.domEntry = document.createElement("div");
+    this.children = new Array();
+    this.selection = null;
+    this.hrefMode = hrefMode;
+    
+    this.createBranch = function(text, iconSrc)
+    {
+        var root = new TreeNode(text, iconSrc, this.hrefMode);
+        root.owner = this;
+        this.children[ this.children.length ] = root;
+        this.domEntry.appendChild( root.domEntry );
+        return root;
+    }
+    
+    this.branch = function(text)
+    {
+        var ret = null;
+        for (var i = 0; i < this.children.length; ++i)
+            if (this.children[i].textElement.data == text)
+            {
+                ret = this.children[i];
+                break;
+            }
+            
+        return ret;
+    }
+    
+    this.domEntry.style.fontSize = "10px";
+    this.domEntry.style.cursor = "default";
+    this.domEntry.style.whiteSpace = "nowrap";
+}
+
+var idCounter = 0;
+function TreeNode(text, iconSrc, hrefMode)
+{
+    this.id             = idCounter++;
+    this.parentNode     = null;
+    this.children       = new Array();
+    this.domEntry       = document.createElement("div");
+    this.icon           = document.createElement("img");
+    this.textElement    = document.createTextNode(text);
+    this.textSpan       = document.createElement("span");
+    this.lineDiv        = document.createElement("div");
+    this.hierarchyImgs  = new Array();
+    this.onclick        = null;
+    
+    function createIcon()
+    {
+        var img = document.createElement("img");
+        img.style.verticalAlign = "middle";
+        img.style.position = "relative";
+        img.style.top = "-1px";
+        img.width = 16;
+        img.height = 16;
+        return img;
+    }
+    
+    function createHierarchyImage()
+    {
+        var img = createIcon();
+        img.pointsTop = false;
+        img.pointsBottom = false;
+        img.pointsRight = false;
+        img.pmState = pmNone;
+        return img;
+    }
+    
+    function genHierarchyImageSrc(hierarchyImg)
+    {
+        var name = "";
+        if (hierarchyImg.pointsTop)
+            name += "t";
+            
+        if (hierarchyImg.pointsBottom)
+            name += "b";
+            
+        if (hierarchyImg.pointsRight)
+            name += "r";
+            
+        if (hierarchyImg.pmState == pmPlus)
+            name += "p";
+        else if (hierarchyImg.pmState == pmMinus)
+            name += "m";
+        
+        if (name == "")
+            name = "shim";
+        
+        return "candydoc/img/tree/" + name + ".gif";
+    }
+    
+    function setSrc(icon, src)
+    {
+        icon.src = src;
+        // After src change width and height are reseted in IE.
+        // Bug workaround:
+        icon.width = 16;
+        icon.height = 16;
+    }
+    
+    this.createChild = function(text, iconSrc)
+    {
+        var child = new TreeNode(text, iconSrc, this.owner.hrefMode);
+        this.children[ this.children.length ] = child;
+        this.domEntry.appendChild( child.domEntry );
+        child.parentNode = this;
+        child.owner = this.owner;
+        
+        // insert hierarchy images according to deepness level
+        // of created child.
+        
+        if (this.children.length > 1)
+        {
+            // there were already added child before. So copy `level-1`
+            // hierarchy images from it.
+            
+            var prevAddedChild = this.children[ this.children.length - 2 ];
+            
+            for (var i = 0; i < prevAddedChild.hierarchyImgs.length - 1; ++i)
+            {
+                var prevAddedChildImg = prevAddedChild.hierarchyImgs[i];
+                var img = createHierarchyImage();
+                setSrc(img, prevAddedChildImg.src);
+                img.pointsTop = prevAddedChildImg.pointsTop;
+                img.pointsBottom = prevAddedChildImg.pointsBottom;
+                img.pointsRight = prevAddedChildImg.pointsRight;
+                img.pmState = prevAddedChildImg.pmState;
+                
+                child.hierarchyImgs[ child.hierarchyImgs.length ] = img;
+                child.lineDiv.insertBefore(img, child.icon);
+            }
+            
+            // change last hierarchy image of prevAddedChild from |_ to |-
+            var lastHierarchyImg = prevAddedChild.hierarchyImgs[ prevAddedChild.hierarchyImgs.length - 1 ];
+            lastHierarchyImg.pointsBottom = true;
+            setSrc(lastHierarchyImg, genHierarchyImageSrc(lastHierarchyImg));
+                        
+            // change hierarchy images of prevAddedChild's children on it's last
+            // level to |
+            prevAddedChild.addHierarchyTBLine(prevAddedChild.hierarchyImgs.length - 1);
+        }
+        else
+        {
+            // this is a first child. So copy `level-2`
+            // hierarchy images from parent, i.e. this.
+            
+            for (var i = 0; i < this.hierarchyImgs.length - 1; ++i)
+            {
+                var parentImg = this.hierarchyImgs[i];
+                var img = createHierarchyImage();
+                setSrc(img, parentImg.src);
+                img.pointsTop = parentImg.pointsTop;
+                img.pointsBottom = parentImg.pointsBottom;
+                img.pointsRight = parentImg.pointsRight;
+                img.pmState = parentImg.pmState;
+                
+                child.hierarchyImgs[ child.hierarchyImgs.length ] = img;
+                child.lineDiv.insertBefore(img, child.icon);
+            }
+            
+            if (this.hierarchyImgs.length > 0) // we are not root
+            {
+                // change last hierarchy image of parent (i.e. this): add minus to it
+                var lastHierarchyImg = this.hierarchyImgs[ this.hierarchyImgs.length - 1];
+                lastHierarchyImg.pmState = pmMinus;
+                setSrc(lastHierarchyImg, genHierarchyImageSrc(lastHierarchyImg));
+                lastHierarchyImg.owner = this;
+                lastHierarchyImg.onclick = new Function("e", "this.owner.processPMClick(e);");
+                
+                // make decision on image on `level-1`. It depends on parent's (ie this)
+                // image on same level.
+                var parentL1HierarchyImg = lastHierarchyImg;
+                var l1HierarchyImg = createHierarchyImage();
+                if (parentL1HierarchyImg.pointsBottom)
+                {
+                    l1HierarchyImg.pointsTop = true;
+                    l1HierarchyImg.pointsBottom = true;
+                }
+                setSrc(l1HierarchyImg, genHierarchyImageSrc(l1HierarchyImg));
+                child.hierarchyImgs[ child.hierarchyImgs.length ] = l1HierarchyImg;
+                child.lineDiv.insertBefore(l1HierarchyImg, child.icon);
+            }
+        }
+        
+        // in any case on last level our child will have icon |_
+        var img = createHierarchyImage();
+        img.pointsTop = true;
+        img.pointsRight = true;
+        setSrc(img, genHierarchyImageSrc(img));
+        
+        child.hierarchyImgs[ child.hierarchyImgs.length ] = img;
+        child.lineDiv.insertBefore(img, child.icon);
+        
+        return child;
+    }
+    
+    this.lastChild = function()
+    {
+        return this.children[ this.children.length - 1 ];
+    }
+    
+    this.child = function(text)
+    {
+        var ret = null;
+        for (var i = 0; i < this.children.length; ++i)
+            if (this.children[i].textElement.data == text)
+            {
+                ret = this.children[i];
+                break;
+            }
+            
+        return ret;
+    }
+    
+    this.addHierarchyTBLine = function(level)
+    {
+        for (var i = 0; i < this.children.length; ++i)
+        {
+            var img = this.children[i].hierarchyImgs[level];
+            img.pointsTop = true;
+            img.pointsBottom = true;
+            setSrc(img, genHierarchyImageSrc(img));
+            this.children[i].addHierarchyTBLine(level);
+        }
+    }
+    
+    this.expand = function()
+    {
+        var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ];
+        
+        if (img.pmState == pmPlus)
+        {
+            img.pmState = pmMinus;
+            setSrc(img, genHierarchyImageSrc(img));
+            
+            for (var i = 0; i < this.children.length; ++i)
+                this.children[i].domEntry.style.display = "";
+        }
+    }
+    
+    this.collapse = function()
+    {
+        var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ];
+        
+        if (img.pmState == pmMinus)
+        {
+            img.pmState = pmPlus;
+            setSrc(img, genHierarchyImageSrc(img));
+            
+            for (var i = 0; i < this.children.length; ++i)
+                this.children[i].domEntry.style.display = "none";
+        }
+    }
+    
+    this.toggle = function()
+    {
+        var img = this.hierarchyImgs[ this.hierarchyImgs.length - 1 ];
+        if (img.pmState == pmMinus)
+            this.collapse();
+        else
+            this.expand();
+    }
+    
+    this.select = function()
+    {
+        if (this.owner.selection != this)
+        {
+            if (this.owner.selection)
+                this.owner.selection.setHighlight(hlNone);
+                
+            this.owner.selection = this;
+            this.setHighlight(hlSelected);
+        }
+    }
+    
+    this.setHighlight = function(mode)
+    {
+        if (mode == hlNone)
+        {
+            this.textSpan.style.backgroundColor = "";
+            this.textSpan.style.color = "";
+            this.textSpan.style.border = "";
+        }
+        else if (mode == hlGrey)
+        {
+            this.textSpan.style.backgroundColor = "#aaaaaa";
+            this.textSpan.style.color = "";
+            this.textSpan.style.border = "";
+        }
+        else if (mode == hlSelected)
+        {
+            this.textSpan.style.backgroundColor = "3399cc";
+            this.textSpan.style.color = "white";
+            this.textSpan.style.border = "dotted 1px red";
+        }
+    }
+    
+    this.setOnclick = function(proc)
+    {
+        this.onclick = proc;
+    }
+    
+    this.setRef = function(url)
+    {
+        if (this.anchor)
+            this.anchor.href = url;
+    }
+    
+    this.processPMClick = function(e)
+    {
+        this.toggle();
+        
+        // prevent this line selection, stop bubbling
+        if (e)
+            e.stopPropagation(); // Mozilla way
+        if (window.event)
+            window.event.cancelBubble = true; // IE way
+    }
+    
+    this.processOnclick = function()
+    {
+        this.select();
+        if (this.onclick instanceof Function)
+            this.onclick();
+    }
+    
+    ///////////////////////////////////////////////////////////////////////////
+    if (iconSrc)
+        this.icon.src = iconSrc;
+    else
+    {
+        this.icon.width = 0;
+        this.icon.height = 0;
+    }
+    
+    this.icon.style.verticalAlign = "middle";
+    this.icon.style.position = "relative";
+    this.icon.style.top = "-1px";
+    this.icon.style.paddingRight = "2px";
+    
+    if (!hrefMode)
+    {
+        this.textSpan.appendChild( this.textElement );
+    }
+    else
+    {
+        this.anchor = document.createElement("a");
+        this.anchor.appendChild( this.textElement );
+        this.textSpan.appendChild( this.anchor );
+    }
+    
+    this.lineDiv.appendChild( this.icon );
+    this.lineDiv.appendChild( this.textSpan );
+    this.domEntry.appendChild( this.lineDiv );
+    
+    this.lineDiv.owner = this;
+    
+    if (!hrefMode)
+        this.lineDiv.onclick = new Function("this.owner.processOnclick();");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/candydoc/util.js	Sun Aug 10 17:09:41 2008 +0200
@@ -0,0 +1,41 @@
+/* This file is a part of CanDyDOC fileset.
+   File is written by Victor Nakoryakov and placed into the public domain.
+
+   This file is javascript with cross-browser utility functions. */
+
+function getLeft(elem)
+{
+    var ret = 0;
+	while (elem.offsetParent)
+	{
+		ret += elem.offsetLeft;
+		elem = elem.offsetParent;
+	}
+
+	return ret;
+}
+
+function getTop(elem)
+{
+    var ret = 0;
+	while (elem.offsetParent)
+	{
+		ret += elem.offsetTop;
+		elem = elem.offsetParent;
+	}
+
+	return ret;
+}
+
+function getWindowHeight()
+{
+    var ret = 0;
+    if (typeof(window.innerHeight) == "number")
+        ret = window.innerHeight;
+    else if (document.documentElement && document.documentElement.clientHeight)
+        ret = document.documentElement.clientHeight;
+    else if (document.body && document.body.clientHeight)
+        ret = document.body.clientHeight;
+    
+    return ret;
+}