Index: /anagram/trunk/anagram.hs
===================================================================
--- /anagram/trunk/anagram.hs	(revision 3)
+++ /anagram/trunk/anagram.hs	(revision 3)
@@ -0,0 +1,38 @@
+-- anagram.hs: Program to find the anagrams of a word in a word list
+
+import Char
+import System
+import IO
+
+-- Remove one occurrence of an element from a list
+deleteOne e [] = []
+deleteOne e (y:ys)
+    | e == y    = ys
+    | otherwise = y : deleteOne e ys
+
+-- Test if the strings is an re-arrangements of each other
+anagram []     [] = True
+anagram []     ys = False
+anagram xs     [] = False
+anagram (x:xs) ys = elem x ys && anagram xs (deleteOne x ys)
+
+printStrList = putStr . concat . map (++ "\n")
+
+main = do
+    args <- getArgs
+    case args of
+        [string,file]  -> do
+            chars <- readFile file
+            printStrList (filter (anagram string') (phrases chars))
+                where string'   = filter isAlpha string
+                      phrases c = map (filter isAlpha) (lines c)
+        [string]    -> do
+            chars <- getContents
+            printStrList (filter (anagram string) (lines chars))
+        _           -> do
+            putStr "Usage: "
+            getProgName >>= putStr
+            putStrLn " WORD [FILE]"
+            putStrLn "List anagrams of WORD from FILE or standard input."
+            exitWith (ExitFailure 1)
+
Index: /tracirclog/trunk/COPYING
===================================================================
--- /tracirclog/trunk/COPYING	(revision 3)
+++ /tracirclog/trunk/COPYING	(revision 3)
@@ -0,0 +1,345 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: /tracirclog/trunk/README
===================================================================
--- /tracirclog/trunk/README	(revision 3)
+++ /tracirclog/trunk/README	(revision 3)
@@ -0,0 +1,90 @@
+trac-irclog
+===========
+
+An IRC log beautifier for the Trac wiki
+
+
+Introduction
+------------
+
+I thought my lovely IRC snippets contained in a Trac
+(http://projects.edgewall.com/trac/) wiki would look a little better with the
+usual splash of colour I have seen in various places. What did I have to go
+and do?  Write one!
+
+Well, not exactly.  I took irclog2html.py (http://mg.pov.lt/irclog2html/),
+written by Marius Gedminas, and modified it for use as a Trac wiki-processor.
+
+
+Changes from irclog2html.py
+---------------------------
+
+I would have used irclog2html.py by simply importing it as a module, but it
+didn't quite fit my needs:
+
+  * Replaced class NickColourizer with NickClassifer, assigning style classes
+    to nicks instead of inline colours.
+
+  * Added nick-colours.css, an example stylesheet (with colours generated from
+    NickColourizer).
+
+  * Removed non-XHTML styles
+
+  * Modified regular expression patterns to also match the peculiar format used
+    by an Xchat (http://www.xchat.org/) client I have.
+
+
+Todo
+----
+
+  * Stylesheet generator for nick colours (or revert to inline styles?)
+
+
+Download
+--------
+
+Get the wiki-processor and stylesheets from:
+
+    http://www.mivok.net/~simon/projects/trac/irclog/
+
+
+Usage
+-----
+
+Simply drop irclog.py into your $TRAC_ENV/wiki-macros/ directory.
+
+In your wiki pages, write:
+
+    {{{
+    #!irclog
+
+    ... IRC snippets ...
+
+    }}}
+
+To make it look nice, include irclog.css and nick-colours.css in your
+$TRAC_ENV/templates/site_css.cs file, for example:
+
+    <?cs
+    ##################################################################
+    # Site CSS - Place custom CSS, including overriding styles here.
+    ?>
+    @import url(/style/irclog.css);
+    @import url(/style/nick-colours.css);
+
+
+Authors and Licensing
+---------------------
+
+Trac wiki-processor written by Simon Ward <simon@mivok.net>
+
+Based on irclog2html.py, written by Marius Gedminas.
+
+Original irclog2html.pl script written by Jeff Waugh with contributions by
+Rick Welykochy and Alexander Else.
+
+irclog.py is released under the terms of the GNU GPL. A copy of the GNU GPL
+can be obtained from:
+
+    http://www.gnu.org/copyleft/gpl.html
+
Index: /tracirclog/trunk/example.html
===================================================================
--- /tracirclog/trunk/example.html	(revision 3)
+++ /tracirclog/trunk/example.html	(revision 3)
@@ -0,0 +1,262 @@
+<!DOCTYPE html
+    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+ <head>
+  <title>SandBox - Angry Panda Game Project - Trac</title>
+  <link rel="start" href="/trac/wiki" />
+  <link rel="search" href="/trac/search" />
+  <link rel="alternate" href="?format=txt" title="Plain Text" type="text/plain" />
+  <link rel="help" href="/trac/wiki/TracGuide" />
+  <style type="text/css">
+   @import url(http://gameproj.mivok.net/tracdocs/css/trac.css);
+   @import url(http://gameproj.mivok.net/tracdocs/css/wiki.css);
+
+@import url(http://gameproj.mivok.net/style/topbar.css);
+@import url(http://gameproj.mivok.net/style/irclog.css);
+@import url(http://gameproj.mivok.net/style/irclog-colours.css);
+
+  </style>
+
+  <script src="http://gameproj.mivok.net/tracdocs/trac.js" type="text/javascript"></script>
+ </head>
+<body>
+
+<div id="gp-topbar">
+  <ul>
+     <li><a href="/forums/"><span>Discussion Forums</span></a></li>
+     <li class="gp-active"><a href="/trac/"><span>Project Management</span></a></li>
+   </ul>
+
+   <span id="gp-home-link">Angry Panda Game Project</span>
+</div>
+
+
+<div id="banner">
+
+<div id="header">
+  <a id="logo" href="http://trac.edgewall.com/"><img src="http://gameproj.mivok.net/tracdocs/trac_banner.png"
+      width="236" height="73"
+      alt="Trac" /></a>
+  <hr />
+</div>
+
+<form id="search" action="/trac/search" method="get">
+ <div>
+
+  <label for="proj-search">Search:</label>
+  <input type="text" id="proj-search" name="q" size="10" value="" />
+  <input type="submit" value="Search" />
+  <input type="hidden" name="wiki" value="on" />
+  <input type="hidden" name="changeset" value="on" />
+  <input type="hidden" name="ticket" value="on" />
+ </div>
+</form>
+
+<div id="metanav" class="nav">
+ <h2>Navigation</h2>
+ <ul>
+  <li class="first">
+    logged in as simon </li>
+    <li><a href="/trac/logout">Logout</a>
+  </li>
+  <li><a href="/trac/settings">Settings</a></li>
+
+  <li><a accesskey="6" href="/trac/wiki/TracGuide">Help/Guide</a></li>
+  <li style="display: none"><a accesskey="5" href="http://projects.edgewall.com/trac/wiki/TracFaq">FAQ</a></li>
+  <li style="display: none"><a accesskey="0" href="/trac/wiki/TracAccessibility">Accessibility</a></li>
+  <li class="last"><a accesskey="9" href="/trac/about_trac">About Trac</a></li>
+ </ul>
+</div>
+
+</div>
+
+
+
+
+
+<div id="mainnav" class="nav">
+ <ul>
+  <li><a href="/trac/wiki" class="active" accesskey="1">Wiki</a></li>
+  <li><a href="/trac/timeline" accesskey="2">Timeline</a></li>
+  <li><a href="/trac/roadmap" accesskey="3">Roadmap</a></li>
+
+  <li><a href="/trac/browser/">Browse Source</a></li>
+  <li><a href="/trac/report">View Tickets</a></li>
+  <li><a href="/trac/newticket" accesskey="7">New Ticket</a></li>
+  <li><a href="/trac/search" accesskey="4">Search</a></li>
+ </ul>
+</div>
+
+<div id="main">
+
+
+<div id="ctxtnav" class="nav">
+
+ <h2>Wiki Navigation</h2>
+ <ul>
+  <li><a href="/trac/wiki">Start Page</a></li>
+  <li><a href="/trac/wiki/TitleIndex">Title Index</a></li>
+  <li><a href="/trac/wiki/RecentChanges">Recent Changes</a></li>
+  
+   <li class="last"><a href="/trac/wiki/SandBox?history=yes">Page History</a></li>
+
+  
+ </ul>
+ <hr />
+</div>
+
+<div id="content" class="wiki">
+ 
+  
+   <div class="wikipage">
+    <div id="searchable"><p>
+<div class='wiki-toc'>
+<h4>Table of Contents</h4>
+<ol>
+<ol><ol><li><a href="/trac/wiki/SandBox#CommentbysimononSun13Feb2005030047GMT">Comment by simon on Sun 13 Feb 2005 03:00:47 GMT</a></li>
+
+</ol>
+</ol>
+</ol>
+</div>
+
+</p>
+<h1 id="TheSandbox">The Sandbox</h1>
+<p>
+This is just a page to practice and learn <a href="/trac/wiki/WikiFormatting">WikiFormatting</a>. 
+</p>
+<p>
+Go ahead, edit it freely.
+</p>
+<table class="irclog"><tr class="nc01" id="tJan 07 15:24:05"><th class="nick">puremourning</th><td class="text"> but i cant gfet gaim to display offline people</td><td class="time"><a href="#tJan 07 15:24:05" class="time">15:24</a></td></tr>
+
+<tr class="nc01" id="tJan 07 15:24:07"><th class="nick">puremourning</th><td class="text"> &gt;:Z</td><td class="time"><a href="#tJan 07 15:24:07" class="time">15:24</a></td></tr>
+<tr class="nc01" id="tJan 07 15:24:09"><th class="nick">puremourning</th><td class="text"> bbl</td><td class="time"><a href="#tJan 07 15:24:09" class="time">15:24</a></td></tr>
+<tr class="nc02" id="tJan 07 15:24:13"><th class="nick">s_wardman</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;why not?</td><td class="time"><a href="#tJan 07 15:24:13" class="time">15:24</a></td></tr>
+<tr id="tJan 07 15:24:22"><td class="action" colspan="2">*       s_wardman fires up gaim</td><td><a href="#tJan 07 15:24:22" class="time">15:24</a></td></tr>
+
+<tr id="tJan 07 15:25:03"><td class="action" colspan="2">*       s_wardman doesn't fire up gaim, since he doesn't have it</td><td><a href="#tJan 07 15:25:03" class="time">15:25</a></td></tr>
+<tr id="tJan 07 15:50:15"><td class="join" colspan="2">--&gt;     fragMonkey (FragMonkey@phrenzy-779764E0.freedom2surf.net) has joined #gameproject</td><td><a href="#tJan 07 15:50:15" class="time">15:50</a></td></tr>
+<tr class="nc03" id="tJan 07 15:53:28"><th class="nick">fragMonkey</th><td class="text">&nbsp;&nbsp; !stats</td><td class="time"><a href="#tJan 07 15:53:28" class="time">15:53</a></td></tr>
+<tr class="nc03" id="tJan 07 15:56:33"><th class="nick">fragMonkey</th><td class="text">&nbsp;&nbsp; fragMonkey spoke of terrible things. 0.2% of his/her language was offensive.</td><td class="time"><a href="#tJan 07 15:56:33" class="time">15:56</a></td></tr>
+
+<tr class="nc03" id="tJan 07 15:56:34"><th class="nick">fragMonkey</th><td class="text">&nbsp;&nbsp; Example:</td><td class="time"><a href="#tJan 07 15:56:34" class="time">15:56</a></td></tr>
+<tr class="nc03" id="tJan 07 15:56:34"><th class="nick">fragMonkey</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;fragMonkey&gt; im thinking of giving them out as christmas presents</td><td class="time"><a href="#tJan 07 15:56:34" class="time">15:56</a></td></tr>
+<tr class="nc03" id="tJan 07 15:56:50"><th class="nick">fragMonkey</th><td class="text">&nbsp;&nbsp; so offensive</td><td class="time"><a href="#tJan 07 15:56:50" class="time">15:56</a></td></tr>
+<tr class="nc02" id="tJan 07 15:57:02"><th class="nick">s_wardman</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;yes, you should wash your mouth out with soap and water</td><td class="time"><a href="#tJan 07 15:57:02" class="time">15:57</a></td></tr>
+
+<tr id="tJan 07 15:58:31"><td class="part" colspan="2">&lt;--     svnbot has quit (Ping timeout)</td><td><a href="#tJan 07 15:58:31" class="time">15:58</a></td></tr>
+<tr class="nc02" id="tJan 07 15:58:57"><th class="nick">s_wardman</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;hey!</td><td class="time"><a href="#tJan 07 15:58:57" class="time">15:58</a></td></tr>
+<tr class="nc02" id="tJan 07 15:59:02"><th class="nick">s_wardman</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;come back!</td><td class="time"><a href="#tJan 07 15:59:02" class="time">15:59</a></td></tr>
+<tr id="tJan 07 16:03:37"><td class="join" colspan="2">--&gt;     svnbot (svnbot@phrenzy-2F826597.net) has joined #gameproject</td><td><a href="#tJan 07 16:03:37" class="time">16:03</a></td></tr>
+<tr class="nc02" id="tJan 07 16:04:25"><th class="nick">s_wardman</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;he took a while</td><td class="time"><a href="#tJan 07 16:04:25" class="time">16:04</a></td></tr>
+
+<tr class="nc02" id="tJan 07 16:04:30"><th class="nick">s_wardman</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;I bet Tainted broke him</td><td class="time"><a href="#tJan 07 16:04:30" class="time">16:04</a></td></tr>
+<tr class="nc02" id="tJan 07 16:04:36"><th class="nick">s_wardman</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;by trying to commit mad stuff</td><td class="time"><a href="#tJan 07 16:04:36" class="time">16:04</a></td></tr>
+<tr id="tJan 07 18:26:03"><td class="nickchange" colspan="2">---     You are now known as s_foodman</td><td><a href="#tJan 07 18:26:03" class="time">18:26</a></td></tr>
+<tr id="tJan 07 19:08:04"><td class="part" colspan="2">&lt;--     svnbot has quit (Ping timeout)</td><td><a href="#tJan 07 19:08:04" class="time">19:08</a></td></tr>
+<tr id="tJan 07 21:18:31"><td class="nickchange" colspan="2">---     You are now known as s_wardman</td><td><a href="#tJan 07 21:18:31" class="time">21:18</a></td></tr>
+
+<tr class="nc02" id="tJan 07 21:30:13"><th class="nick">s_wardman</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;hmm, not reconnecting</td><td class="time"><a href="#tJan 07 21:30:13" class="time">21:30</a></td></tr>
+<tr id="tJan 07 21:35:34"><td class="join" colspan="2">--&gt;     svnbot (svnbot@phrenzy-2F826597.net) has joined #gameproject</td><td><a href="#tJan 07 21:35:34" class="time">21:35</a></td></tr>
+<tr class="nc02" id="tJan 07 21:36:23"><th class="nick">s_wardman</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;hopefully he should reconnect if he gets disconnected</td><td class="time"><a href="#tJan 07 21:36:23" class="time">21:36</a></td></tr>
+<tr class="nc02" id="tJan 07 21:36:35"><th class="nick">s_wardman</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;there's a retry delay I had to reset</td><td class="time"><a href="#tJan 07 21:36:35" class="time">21:36</a></td></tr>
+
+<tr class="nc02" id="tJan 07 21:37:06"><th class="nick">s_wardman</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;it's exponential I think, so he was probably going to wait a while before reconnecting because it hadn't been reset</td><td class="time"><a href="#tJan 07 21:37:06" class="time">21:37</a></td></tr>
+<tr class="nc04" id="tJan 08 00:12:25"><th class="nick">svnbot</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Update: Revision 426 committed by TheTaintedOne</td><td class="time"><a href="#tJan 08 00:12:25" class="time">00:12</a></td></tr>
+<tr class="nc04" id="tJan 08 00:12:26"><th class="nick">svnbot</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="http://gameproj.mivok.net/trac/changeset/426">http://gameproj.mivok.net/trac/changeset/426</a></td><td class="time"><a href="#tJan 08 00:12:26" class="time">00:12</a></td></tr>
+<tr id="tJan 08 00:27:58"><td class="part" colspan="2">&lt;--     NinjaChimp has quit (Exit: fu :O!)</td><td><a href="#tJan 08 00:27:58" class="time">00:27</a></td></tr>
+
+<tr id="tJan 08 00:28:23"><td class="join" colspan="2">--&gt;     NinjaChimp (stefan.mor@phrenzy-31BE1FDD.range81-155.btcentralplus.com) has joined #gameproject</td><td><a href="#tJan 08 00:28:23" class="time">00:28</a></td></tr>
+<tr class="nc02" id="tJan 08 00:32:46"><th class="nick">s_wardman</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;yay!</td><td class="time"><a href="#tJan 08 00:32:46" class="time">00:32</a></td></tr>
+<tr class="nc02" id="tJan 08 00:32:49"><th class="nick">s_wardman</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;It works!</td><td class="time"><a href="#tJan 08 00:32:49" class="time">00:32</a></td></tr>
+<tr class="nc03" id="tJan 08 00:35:36"><th class="nick">fragMonkey</th><td class="text">&nbsp;&nbsp; nice one</td><td class="time"><a href="#tJan 08 00:35:36" class="time">00:35</a></td></tr>
+
+<tr class="nc02" id="tJan 08 00:37:02"><th class="nick">s_wardman</th><td class="text">&nbsp;&nbsp;&nbsp;&nbsp;some day I might add some query stuff to it, so you can get diffs, log message, etc from it, but that'll have to wait</td><td class="time"><a href="#tJan 08 00:37:02" class="time">00:37</a></td></tr>
+<tr class="nc03" id="tJan 08 00:41:36"><th class="nick">fragMonkey</th><td class="text">&nbsp;&nbsp; be happy with what you have</td><td class="time"><a href="#tJan 08 00:41:36" class="time">00:41</a></td></tr>
+</table><h4 id="CommentbysimononSun13Feb2005030047GMT">Comment by simon on Sun 13 Feb 2005 03:00:47 GMT</h4>
+<p>
+sdfsf
+</p>
+<p>
+<form name='addcomment' action='/trac/wiki/SandBox' method='post'>
+<fieldset>
+
+<legend>Add comment</legend>
+<textarea id='addcomment' name='addcomment' cols='80' rows='5' wrap='soft'></textarea>
+<br>
+<input type='submit' name='submitaddcomment' value='Add comment'>
+<input type='submit' name='previewaddcomment' value='Preview comment'>
+<input type='submit' name='canceladdcomment' value='Cancel'>
+<script type='text/javascript'>
+addWikiFormattingToolbar(document.getElementById('addcomment'));
+</script>
+</fieldset>
+</form>
+
+</p>
+</div>
+   </div>
+   
+  
+   <div class="buttons">
+
+    
+     <form method="get" action=""><div>
+      <input type="hidden" name="edit" value="yes" />
+      <input type="submit" value="Edit This Page" />
+     </div></form>
+     <form method="get" action="/trac/attachment/wiki/SandBox"><div>
+      <input type="submit" value="Attach File" />
+     </div></form>
+    
+    
+     <form method="post" action=""><div id="delete">
+      <input type="hidden" name="edit_version" value="8" />
+
+       <input type="submit" name="delete_ver" id="delete_ver" value="Delete This Version" onclick="return confirm('Do you really want to delete version 8 of this page?\nThis is an irreversible operation.')" />
+       <input type="submit" name="delete_page" value="Delete Page" onclick="return confirm('Do you really want to delete all versions of this page?\nThis is an irreversible operation.')" />
+     </div></form>
+    
+   </div>
+  
+ 
+ 
+</div>
+<script type="text/javascript">searchHighlight()</script>
+
+
+<div id="altlinks">
+ <h3>Download in other formats:</h3>
+
+ <ul>
+  <li class="first last">
+   <a href="?format=txt">Plain Text</a>
+  </li>
+ </ul>
+</div>
+
+
+</div>
+
+<div id="footer">
+
+ <hr />
+ <a id="tracpowered" href="http://trac.edgewall.com/"><img src="http://gameproj.mivok.net/tracdocs/trac_logo_mini.png" height="30" width="107"
+     alt="Trac Powered"/></a>
+ <p class="left">
+  Powered by <a href="/trac/about_trac"><strong>Trac 0.8</strong></a><br />
+  By <a href="http://www.edgewall.com/">Edgewall Software</a>.
+ </p>
+ <p class="right">
+
+  Visit the Trac open source project at <a href="http://trac.edgewall.com/">http://trac.edgewall.com/</a><br />Webmaster: <a href="mailto:&#115;&#105;&#109;&#111;&#110;&#64;&#109;&#105;&#118;&#111;&#107;&#46;&#110;&#101;&#116;">Simon</a> &nbsp;|&nbsp; Hosted by <a href="http://www.mivok.net/">mivok.net</a>
+ </p>
+</div>
+
+
+
+ </body>
+</html>
+
Index: /tracirclog/trunk/irclog.css
===================================================================
--- /tracirclog/trunk/irclog.css	(revision 3)
+++ /tracirclog/trunk/irclog.css	(revision 3)
@@ -0,0 +1,71 @@
+/* Alternative 1: text */
+
+div.irclog { font-family: monospace; text-indent: -7em; padding-left: 7em; }
+div.irclog p { margin: 0.2em 0; }
+
+/* Alternative 2: table */
+table.irclog { width: 100%; }
+.irclog th.nick { color: #ffffff; padding: 0.1em 0.2em; }
+.irclog td.text { width: 100%; background: #eeeeee; }
+.irclog td.action { background: #eeeeee; }
+
+/* Common rules */
+.irclog h1 { color: #134d73; margin: 0.5em 0; }
+.irclog a { color: #869ABF; }
+.irclog a:visited { color: purple; }
+.irclog a:active, .irclog a:hover { color: #B22222; }
+.irclog a.time,
+.irclog a.time:visited {
+    text-decoration: none;
+    color: #8888cc;
+}
+.irclog a.time:active,
+.irclog a.time:hover { 
+    text-decoration: none;
+    color: #9999dd;
+}
+.irclog a.time:before { content: "["; }
+.irclog a.time:after { content: "]"; }
+
+.irclog .nick { font-weight: bold; }
+.irclog .part { color: #000099; }
+.irclog .join { color: #009900; }
+.irclog .servermsg { color: #009900; }
+.irclog .nickchange { color: #009900; }
+.irclog .action { color: #cc00cc; }
+.irclog .other { color: #888888; }
+
+/* Make boring messages small */
+.irclog .part, .irclog .join, .irclog .servermsg,
+.irclog .nickchange, .irclog .other { font-size: smaller; }
+.irclog td.part + td,
+.irclog td.join + td,
+.irclog td.servermsg + td,
+.irclog td.nickchange + td,
+.irclog td.other + td { 
+    font-size: smaller;
+    text-align: right;
+}
+
+.irclog div.generatedby {
+    margin-top: 1em;
+    font-size: small;
+    color: #888888;
+}
+.irclog div.generatedby a:link {
+    color: #8888cc;
+    text-decoration: none;
+    border-bottom: 1px dotted #8888cc;
+}
+.irclog div.generatedby a:visited {
+    color: #cc88cc;
+    text-decoration: none;
+    border-bottom: 1px dotted #cc88cc;
+}
+.irclog div.generatedby a:active,
+.irclog div.generatedby a:hover {
+    color: #ff8888;
+    text-decoration: none;
+    border-bottom: 1px dotted #ff8888;
+}
+
Index: /tracirclog/trunk/irclog.py
===================================================================
--- /tracirclog/trunk/irclog.py	(revision 3)
+++ /tracirclog/trunk/irclog.py	(revision 3)
@@ -0,0 +1,369 @@
+"""irclog -- an IRC log beautifier for the Trac wiki""" 
+
+# Copyright (c) 2005, Simon E. Ward
+# Copyright (c) 2005, Marius Gedminas 
+# Copyright (c) 2000, Jeffrey W. Waugh
+
+# Trac Wiki-Processor:
+#   Simon Ward <simon@mivok.net>
+# Python port:
+#   Marius Gedminas <marius@pov.lt>
+# Original Author:
+#   Jeff Waugh <jdub@perkypants.org>
+# Contributors:
+#   Rick Welykochy <rick@praxis.com.au>
+#   Alexander Else <aelse@uu.net>
+#
+# Released under the terms of the GNU GPL
+# http://www.gnu.org/copyleft/gpl.html
+
+import re
+from StringIO import StringIO
+
+IRCLOG2HTML_VERSION = "2.3"
+IRCLOG2HTML_RELEASE = "2005-03-28"
+
+URL_REGEXP = re.compile(r'((http|https|ftp|gopher|news)://[^ \'")>]*)')
+
+def createlinks(text):
+    """Replace possible URLs with links."""
+    return URL_REGEXP.sub(r'<a href="\1">\1</a>', text)
+
+def escape(s):
+    """Replace ampersands, pointies, control characters.
+
+        >>> escape('Hello & <world>')
+        'Hello &amp; &lt;world&gt;'
+        >>> escape('Hello & <world>')
+        'Hello &amp; &lt;world&gt;'
+
+    Control characters (ASCII 0 to 31) are stripped away
+
+        >>> escape(''.join([chr(x) for x in range(32)]))
+        ''
+
+    """
+    s = s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
+    return ''.join([c for c in s if ord(c) > 0x1F])
+
+def shorttime(time):
+    """Strip date and seconds from time.
+
+        >>> shorttime('12:45:17')
+        '12:45'
+        >>> shorttime('12:45')
+        '12:45'
+        >>> shorttime('2005-02-04T12:45')
+        '12:45'
+
+    """
+    if 'T' in time:
+        time = time.split('T')[-1]
+    elif ' ' in time:
+        time = time.split(' ')[-1]
+    if time.count(':') > 1:
+        time = ':'.join(time.split(':')[:2])
+    return time
+
+
+class Enum(object):
+    """Enumerated value."""
+
+    def __init__(self, value):
+        self.value = value
+
+    def __repr__(self):
+        return self.value
+
+
+class LogParser(object):
+    """Parse an IRC log file.
+
+    When iterated, yields the following events:
+
+        time, COMMENT, (nick, text)
+        time, ACTION, text
+        time, JOIN, text
+        time, PART, text,
+        time, NICKCHANGE, (text, oldnick, newnick)
+        time, SERVER, text
+
+    """
+
+    COMMENT = Enum('COMMENT')
+    ACTION = Enum('ACTION')
+    JOIN = Enum('JOIN')
+    PART = Enum('PART')
+    NICKCHANGE = Enum('NICKCHANGE')
+    SERVER = Enum('SERVER')
+    OTHER = Enum('OTHER')
+
+    TIME_REGEXP = re.compile(
+            r'^\[?(' # Optional [
+            r'(?:\d{4}-\d{2}-\d{2}T|\d{2}-\w{3}-\d{4} |\w{3} \d{2} )?'
+                # Optional date
+            r'\d\d:\d\d(:\d\d)?' # Mandatory HH:MM, optional :SS
+            r')\]? +') # Optional ], mandatory space
+    NICK_REGEXP = re.compile(r'^(?:<(.*?)>|([a-zA-Z\[\\\]^_`{|}][a-zA-Z0-9\[\\\]^_`{|}-]+))\s')
+    JOIN_REGEXP = re.compile(r'^(?:\*\*\*|-->)\s.*joined')
+    PART_REGEXP = re.compile(r'^(?:\*\*\*|<--)\s.*(quit|left)')
+    SERVMSG_REGEXP = re.compile(r'^(?:\*\*\*|---)\s')
+    NICK_CHANGE_REGEXP = re.compile(
+            r'^(?:\*\*\*|---)\s+(.*?) (?:are|is) now known as (.*)')
+
+    def __init__(self, infile):
+        self.infile = infile
+
+    def __iter__(self):
+        for line in self.infile:
+            line = line.rstrip('\r\n')
+            if not line:
+                continue
+
+            m = self.TIME_REGEXP.match(line)
+            if m:
+                time = m.group(1)
+                line = line[len(m.group(0)):]
+            else:
+                time = None
+
+            m = self.NICK_REGEXP.match(line)
+            if m:
+                nick = m.group(1) or m.group(2)
+                text = line[len(m.group(0)):]
+                yield time, self.COMMENT, (nick, text)
+            elif line.startswith('* ') or line.startswith('*\t'):
+                yield time, self.ACTION, line
+            elif self.JOIN_REGEXP.match(line):
+                yield time, self.JOIN, line
+            elif self.PART_REGEXP.match(line):
+                yield time, self.PART, line
+            else:
+                m = self.NICK_CHANGE_REGEXP.match(line)
+                if m:
+                    oldnick = m.group(1)
+                    newnick = m.group(2)
+                    yield time, self.NICKCHANGE, (line, oldnick, newnick)
+                elif self.SERVMSG_REGEXP.match(line):
+                    yield time, self.SERVER, line
+                else:
+                    yield time, self.OTHER, line
+
+
+class NickClassifier(object):
+    """Assign style classes to nicknames."""
+
+    def __init__(self, maxclasses=30, default_classes=None):
+        self.nickcount = 0
+        self.maxclasses = maxclasses
+        self.nick_classes = {}
+        if default_classes:
+            self.nick_classes.update(default_classes)
+
+    def __getitem__(self, nick):
+        cls = self.nick_classes.get(nick)
+        if not cls:
+            self.nickcount += 1
+            fieldlen = len(str(self.maxclasses))
+            cls = ('nc%%0%dd' % fieldlen) % (self.nickcount % self.maxclasses)
+            self.nick_classes[nick] = cls
+        return cls
+
+    def change(self, oldnick, newnick):
+        if oldnick in self.nick_classes:
+            self.nick_classes[newnick] = self.nick_classes.pop(oldnick)
+
+
+class AbstractStyle(object):
+    """A style defines the way output is formatted.
+
+    This is not a real class, rather it is an description of how style
+    classes should be written.
+    """
+
+    name = "stylename"
+    description = "Single-line description"
+
+    def __init__(self, outfile, classes=None):
+        """Create a text formatter for writing to outfile.
+
+        `classes` may have the following attributes:
+           part
+           join
+           server
+           nickchange
+           action
+        """
+        self.outfile = outfile
+        self.classes = classes or {}
+
+    def servermsg(self, time, what, line):
+        """Output a generic server message.
+
+        `time` is a string.
+        `line` is not escaped.
+        `what` is one of LogParser event constants (e.g. LogParser.JOIN).
+        """
+
+    def nicktext(self, time, nick, text, htmlclass):
+        """Output a comment uttered by someone.
+
+        `time` is a string.
+        `nick` and `text` are not escaped.
+        `htmlclass` is a string.
+        """
+
+
+class XHTMLStyle(AbstractStyle):
+    """Text style, produces XHTML that can be styled with CSS"""
+
+    name = 'xhtml'
+    description = __doc__
+
+    CLASSMAP = {
+        LogParser.ACTION: 'action',
+        LogParser.JOIN: 'join',
+        LogParser.PART: 'part',
+        LogParser.NICKCHANGE: 'nickchange',
+        LogParser.SERVER: 'servermsg',
+        LogParser.OTHER: 'other',
+    }
+
+    prefix = '<div class="irclog">'
+    suffix = """</div>
+<div class="generatedby">
+<p>Generated by irclog wiki-processor
+ (<a href="mailto:simon@mivok.net">Simon Ward</a>),
+ based on irclog2html.py %(VERSION)s by 
+ <a href="mailto:marius@pov.lt">Marius Gedminas</a>
+ - find it at <a href="http://mg.pov.lt/irclog2html/">mg.pov.lt</a>!</p>
+</div> """ % { 'VERSION': IRCLOG2HTML_VERSION }
+
+    def link(self, url, title):
+        # Intentionally not escaping title so that &entities; work
+        if url:
+            print >> self.outfile, ('<a href="%s">%s</a>'
+                                    % (escape(urllib.quote(url)),
+                                       title or escape(url))),
+        elif title:
+            print >> self.outfile, ('<span class="disabled">%s</span>'
+                                    % title),
+
+    def servermsg(self, time, what, text):
+        """Output a generic server message.
+
+        `time` is a string.
+        `line` is not escaped.
+        `what` is one of LogParser event constants (e.g. LogParser.JOIN).
+        """
+        text = escape(text)
+        text = createlinks(text)
+        if time:
+            displaytime = shorttime(time)
+            print >> self.outfile, ('<p id="t%s" class="%s">'
+                                    '<a href="#t%s" class="time">%s</a> '
+                                    '%s</p>'
+                                    % (time, self.CLASSMAP[what], time,
+                                       displaytime, text))
+        else:
+            print >> self.outfile, ('<p class="%s">%s</p>'
+                                    % (self.CLASSMAP[what], text))
+
+    def nicktext(self, time, nick, text, htmlclass):
+        """Output a comment uttered by someone.
+
+        `time` is a string.
+        `nick` and `text` are not escaped.
+        `htmlclass` is a string.
+        """
+        nick = escape(nick)
+        text = escape(text)
+        text = createlinks(text)
+        text = text.replace('  ', '&nbsp;&nbsp;')
+        if time:
+            displaytime = shorttime(time)
+            print >> self.outfile, ('<p id="t%s" class="comment %s">'
+                                    '<a href="#t%s" class="time">%s</a> '
+                                    '<span class="nick">&lt;%s&gt;</span>'
+                                    ' <span class="text">%s</span></p>'
+                                    % (time, htmlclass, time, displaytime,
+                                       nick, text))
+        else:
+            print >> self.outfile, ('<p class="comment %s">'
+                                    '<span class="nick">&lt;%s&gt;</span>'
+                                    ' <span class="text">%s</span></p>'
+                                    % (htmlclass, nick, text))
+
+
+class XHTMLTableStyle(XHTMLStyle):
+    """Table style, produces XHTML that can be styled with CSS"""
+
+    name = 'xhtmltable'
+    description = __doc__
+
+    prefix = '<table class="irclog">'
+    suffix = '</table>'
+
+    def servermsg(self, time, what, text):
+        text = escape(text)
+        text = createlinks(text)
+        if time:
+            displaytime = shorttime(time)
+            print >> self.outfile, ('<tr id="t%s">'
+                                    '<td class="%s" colspan="2">%s</td>'
+                                    '<td><a href="#t%s" class="time">%s</a></td>'
+                                    '</tr>'
+                                    % (time, self.CLASSMAP[what], text,
+                                       time, displaytime))
+        else:
+            print >> self.outfile, ('<tr>'
+                                    '<td class="%s" colspan="3">%s</td>'
+                                    '</tr>'
+                                    % (self.CLASSMAP[what], text))
+
+    def nicktext(self, time, nick, text, htmlclass):
+        nick = escape(nick)
+        text = escape(text)
+        text = createlinks(text)
+        text = text.replace('  ', '&nbsp;&nbsp;')
+        if time:
+            displaytime = shorttime(time)
+            print >> self.outfile, ('<tr class="%s" id="t%s">'
+                                    '<th class="nick">%s</th>'
+                                    '<td class="text">%s</td>'
+                                    '<td class="time">'
+                                    '<a href="#t%s" class="time">%s</a></td>'
+                                    '</tr>'
+                                    % (htmlclass, time, nick, text,
+                                       time, displaytime))
+        else:
+            print >> self.outfile, ('<tr class="%s">'
+                                    '<th class="nick">%s</th>'
+                                    '<td class="text" colspan="2">%s</td>'
+                                    '</tr>' % (htmlclass, nick, text))
+
+
+def execute(hdf, text, env):
+    textbuf = StringIO(text)
+    htmlbuf = StringIO()
+
+    parser = LogParser(textbuf)
+    formatter = XHTMLTableStyle(htmlbuf)
+    nick_classes = NickClassifier(maxclasses=20)
+    htmlbuf.write(formatter.prefix)
+    for time, what, info in parser:
+        if what == LogParser.COMMENT:
+            nick, text = info
+            nickclass = nick_classes[nick]
+            formatter.nicktext(time, nick, text, nickclass)
+        else:
+            if what == LogParser.NICKCHANGE:
+                text, oldnick, newnick = info
+                nick_classes.change(oldnick, newnick)
+            else:
+                text = info
+            formatter.servermsg(time, what, text)
+
+    # Footer
+    htmlbuf.write(formatter.suffix)
+    return htmlbuf.getvalue()
Index: /tracirclog/trunk/nick-colours.css
===================================================================
--- /tracirclog/trunk/nick-colours.css	(revision 3)
+++ /tracirclog/trunk/nick-colours.css	(revision 3)
@@ -0,0 +1,80 @@
+.irclog .nc01 span.nick { color: #763e3e; }
+.irclog .nc01 th.nick { background-color: #763e3e; color: #ffffff; }
+.irclog .nc01 td.text { color: #763e3e; }
+
+.irclog .nc02 span.nick { color: #417c41; }
+.irclog .nc02 th.nick { background-color: #417c41; color: #ffffff; }
+.irclog .nc02 td.text { color: #417c41; }
+
+.irclog .nc03 span.nick { color: #444481; }
+.irclog .nc03 th.nick { background-color: #444481; color: #ffffff; }
+.irclog .nc03 td.text { color: #444481; }
+
+.irclog .nc04 span.nick { color: #878747; }
+.irclog .nc04 th.nick { background-color: #878747; color: #ffffff; }
+.irclog .nc04 td.text { color: #878747; }
+
+.irclog .nc05 span.nick { color: #8c4a8c; }
+.irclog .nc05 th.nick { background-color: #8c4a8c; color: #ffffff; }
+.irclog .nc05 td.text { color: #8c4a8c; }
+
+.irclog .nc06 span.nick { color: #4c9292; }
+.irclog .nc06 th.nick { background-color: #4c9292; color: #ffffff; }
+.irclog .nc06 td.text { color: #4c9292; }
+
+.irclog .nc07 span.nick { color: #974f4f; }
+.irclog .nc07 th.nick { background-color: #974f4f; color: #ffffff; }
+.irclog .nc07 td.text { color: #974f4f; }
+
+.irclog .nc08 span.nick { color: #529c52; }
+.irclog .nc08 th.nick { background-color: #529c52; color: #ffffff; }
+.irclog .nc08 td.text { color: #529c52; }
+
+.irclog .nc09 span.nick { color: #5555a2; }
+.irclog .nc09 th.nick { background-color: #5555a2; color: #ffffff; }
+.irclog .nc09 td.text { color: #5555a2; }
+
+.irclog .nc10 span.nick { color: #a7a758; }
+.irclog .nc10 th.nick { background-color: #a7a758; color: #ffffff; }
+.irclog .nc10 td.text { color: #a7a758; }
+
+.irclog .nc11 span.nick { color: #ad5bad; }
+.irclog .nc11 th.nick { background-color: #ad5bad; color: #ffffff; }
+.irclog .nc11 td.text { color: #ad5bad; }
+
+.irclog .nc12 span.nick { color: #5eb2b2; }
+.irclog .nc12 th.nick { background-color: #5eb2b2; color: #ffffff; }
+.irclog .nc12 td.text { color: #5eb2b2; }
+
+.irclog .nc13 span.nick { color: #b86161; }
+.irclog .nc13 th.nick { background-color: #b86161; color: #ffffff; }
+.irclog .nc13 td.text { color: #b86161; }
+
+.irclog .nc14 span.nick { color: #63bd63; }
+.irclog .nc14 th.nick { background-color: #63bd63; color: #ffffff; }
+.irclog .nc14 td.text { color: #63bd63; }
+
+.irclog .nc15 span.nick { color: #6666c3; }
+.irclog .nc15 th.nick { background-color: #6666c3; color: #ffffff; }
+.irclog .nc15 td.text { color: #6666c3; }
+
+.irclog .nc16 span.nick { color: #c8c869; }
+.irclog .nc16 th.nick { background-color: #c8c869; color: #ffffff; }
+.irclog .nc16 td.text { color: #c8c869; }
+
+.irclog .nc17 span.nick { color: #ce6cce; }
+.irclog .nc17 th.nick { background-color: #ce6cce; color: #ffffff; }
+.irclog .nc17 td.text { color: #ce6cce; }
+
+.irclog .nc18 span.nick { color: #6fd3d3; }
+.irclog .nc18 th.nick { background-color: #6fd3d3; color: #ffffff; }
+.irclog .nc18 td.text { color: #6fd3d3; }
+
+.irclog .nc19 span.nick { color: #d97272; }
+.irclog .nc19 th.nick { background-color: #d97272; color: #ffffff; }
+.irclog .nc19 td.text { color: #d97272; }
+
+.irclog .nc20 span.nick { color: #75de75; }
+.irclog .nc20 th.nick { background-color: #75de75; color: #ffffff; }
+.irclog .nc20 td.text { color: #75de75; }
+
