Index: /trac/trunk/wiki-macros/TracNav.py
===================================================================
--- /trac/trunk/wiki-macros/TracNav.py	(revision 2863)
+++ /trac/trunk/wiki-macros/TracNav.py	(revision 2863)
@@ -0,0 +1,156 @@
+# -*- coding: iso8859-1 -*-
+"""
+= TracNav: The navigation bar for Trac =
+
+This macro implements a fully customizable navigation bar for the Trac
+wiki engine. The contents of the navigation bar is a wiki page itself
+and can be edited like any other wiki page through the web
+interface. The navigation bar supports hierarchical ordering of
+topics. The design of TracNav mimics the design of the TracGuideToc
+that was originally supplied with Trac. The drawback of TracGuideToc
+is that is is not customizable without editing its source code and
+that it does not support hierarchical ordering.
+"""
+
+import re
+import sys
+
+listRule = re.compile(r"""^(?P<indent> +)\* +(?:(?P<wikilink>\[wiki:(?P<link>(&#34;([^&#34;]*)&#34;|'([^']*)')|([^ \]]+)) +(?P<label>[^\]]*)\])|(?P<text>.*))""", re.M)
+
+def getToc(env, db, name):
+    cursor = db.cursor()
+    cursor.execute('SELECT text FROM wiki WHERE name=%s ORDER BY version DESC LIMIT 1', name)
+    row = cursor.fetchone()
+    if not row:
+        return None
+
+    tocText = row[0]
+
+    # env.log.debug(tocText)
+
+    stack = [(1, [])]
+    nextPos = 0
+    while 1:
+        match = listRule.search(tocText, nextPos)
+        if not match:
+            env.log.debug("No more matches")
+            break
+
+        indent = len(match.group('indent'))
+        if match.group('wikilink'):
+            link = match.group('link')
+            label = match.group('label')
+        else:
+            link = None
+            label = match.group('text')
+
+        if link == None:
+            env.log.debug(label + " ---")
+        else:
+            env.log.debug(label + ": " + link)
+
+        (lastIndent, list) = stack[len(stack) - 1]
+
+        if indent > lastIndent:
+            stack.append((indent, [(link, label, None)]))
+        elif indent == lastIndent:
+            list.append((link, label, None))
+        else:
+            while indent < lastIndent:
+                (_, list) = stack.pop()
+                (lastIndent, topList) = stack[len(stack) - 1]
+                (topLink, topLabel, _) = topList[len(topList) - 1]
+                topList[len(topList) - 1] = (topLink, topLabel, list)
+
+            (lastIndent, list) = stack[len(stack) - 1]
+            list.append((link, label, None))
+                
+        nextPos = match.end()
+
+    while len(stack) > 1:
+        (_, list) = stack.pop()
+        (_, topList) = stack[len(stack) - 1]
+        (topLink, topLabel, _) = topList[len(topList) - 1]
+        topList[len(topList) - 1] = (topLink, topLabel, list)
+
+    (_, list) = stack.pop()
+    return list
+
+
+def execute(hdf, args, env):
+    curpage =  '%s' % hdf.getValue('args.page', '')
+    name = args
+    if not name:
+        name = 'TOC'
+
+    db = env.get_db_cnx()
+    toc = getToc(env, db, name)
+    if not toc:
+        msg = ''
+        msg += '<div class="system-message"><strong>Error: Table of contents does not exist.'
+        if (hdf.getValue('trac.acl.WIKI_MODIFY', '')):
+            msg += ' Click here to <a href="%s?edit=yes">edit</a>.' % env.href.wiki(name)
+        msg += '</strong></div>'
+        return msg
+
+    html = ''
+    html += '<div class="wiki-toc trac-nav">'
+    if (hdf.getValue('trac.acl.WIKI_MODIFY', '')):
+        html += '<div class="edit"><a href="%s?edit=yes">edit</a></div>' % env.href.wiki(name)
+    (found, filtered) = filter(curpage, toc, 0)
+    if found:
+        html += display(env, curpage, filtered)
+    else:
+        html += display(env, curpage, toc)
+    html += '</div>'
+    return html
+
+
+def filter(curpage, toc, level):
+    found = 0
+    result = []
+    for name, title, sub in toc:
+        if sub == None:
+            if name == curpage:
+                found = 1
+            result.append((name, title, None))
+        else:
+            (subfound, subtoc) = filter(curpage, sub, level + 1)
+            if subfound or (name == None):
+                found = 1
+                if level == 0 and name != None:
+                    prepended = [(name, title, subtoc)]
+                    prepended.extend(result)
+                    result = prepended
+                else:
+                    result.append((name, title, subtoc))
+            else:
+                result.append((name, title, []))
+    return (found, result)
+
+
+def display(env, curpage, toc):
+    html = '<ul>'
+    for name, title, sub in toc:
+        if sub == None:
+            if name == curpage:
+                cls = ' class="active"'
+            else:
+                cls = ''
+            html += '<li%s>' % ( cls )
+            if name == None:
+                html += title
+            else:
+                html += '<a href="%s">%s</a>' % (env.href.wiki(name), title)
+            html += '</li>'
+        else:
+            html += '<li>'
+            if name == None or len(sub) > 0:
+                html += '<h4>%s</h4>' % title
+            else:
+                html += '<h4><a href="%s">%s...</a></h4>' % (env.href.wiki(name), title)
+            html += display(env, curpage, sub)
+            html += '</li>'
+    html += '</ul>'
+    return html
+
