<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>COPYING</filename>
    </added>
    <added>
      <filename>README</filename>
    </added>
    <added>
      <filename>www/cnet.php</filename>
    </added>
    <added>
      <filename>www/css/hypertree.css</filename>
    </added>
    <added>
      <filename>www/css/infovis.css</filename>
    </added>
    <added>
      <filename>www/css/style.css</filename>
    </added>
    <added>
      <filename>www/js/excanvas.js</filename>
    </added>
    <added>
      <filename>www/js/infovis.js</filename>
    </added>
    <added>
      <filename>www/js/mootools-1.2.js</filename>
    </added>
    <added>
      <filename>www/org.php</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,7 +1,3 @@
 #!/usr/bin/env python
-# FILE: &quot;/data1/stef/projects/python/pbot/src/__init__.py&quot;
-# LAST MODIFICATION: &quot;Fri, 27 May 2005 00:39:01 +0200 (stef)&quot;
-# (C) 2005 by Marsiske Stefan, &lt;stef@sysdata.siemens.hu&gt;
-# $Id:$
-
+# (C) 2008-2009 by Marsiske Stefan, &lt;stefan.marsiske@gmail.com&gt;
 __all__ = [&quot;objects&quot;,&quot;utils&quot;]</diff>
      <filename>lib/__init__.py</filename>
    </modified>
    <modified>
      <diff>@@ -1,50 +1,72 @@
 #!/usr/bin/python
+&quot;&quot;&quot;
+Maelstrom - visualizing email contacts
 
-from sqlobject import *
+Copyright&#169; 2008-2009 Stefan Marsiske &lt;my name at gmail.com&gt;
+
+database layer for maelstrom
+&quot;&quot;&quot;
+
+import sqlobject
 import psyco, sys, os
 
-class Message(SQLObject):
-   delivered=DateTimeCol()
-   messageid=StringCol()
-   headers=SQLMultipleJoin(&quot;HeaderValue&quot;)
-   sender=ForeignKey(&quot;Email&quot;)
-   path=StringCol()
-   # TODO: if mailindexer
-   # add path to raw
-   # add paths to payloads
-   # add path to mbox where message is stored
-
-class Header(SQLObject):
-   name=StringCol(unique=True)
-
-class Email(SQLObject):
-   username=StringCol()
-   mailserver=StringCol()
-   owner=ForeignKey('Person')
-
-class Person(SQLObject):
-   fullname=StringCol()
-
-class Role(SQLObject):
-   email=ForeignKey('Email')
-   header=ForeignKey('Header')
-   msg=ForeignKey('Message')
-   
-class HeaderValue(SQLObject):
-   value=StringCol()
-   msg=ForeignKey('Message')
-   header=ForeignKey('Header')
+DBPATH = os.path.abspath('db/messages.db')
+sqlobject.sqlhub.processConnection = sqlobject.connectionForURI('sqlite:' + DBPATH)
+
+class Message(sqlobject.SQLObject):
+    &quot;&quot;&quot; represents a message object &quot;&quot;&quot;
+    delivered = sqlobject.col.DateTimeCol()
+    messageid = sqlobject.col.StringCol()
+    headers = sqlobject.SQLMultipleJoin(&quot;HeaderValue&quot;)
+    sender = sqlobject.col.ForeignKey(&quot;Email&quot;)
+    path = sqlobject.col.StringCol()
+    # TODO: if mailindexer
+    # add path to raw
+    # add paths to payloads
+    # add path to mbox where message is stored
+
+class Header(sqlobject.SQLObject):
+    &quot;&quot;&quot; Represents a header object, this is stored uniquely in a
+    separate table, headervalues reference these&quot;&quot;&quot;
+    name = sqlobject.col.StringCol(unique = True)
+
+class Email(sqlobject.SQLObject):
+    &quot;&quot;&quot; represents a email object, it consists of an
+    &lt;username&gt;@&lt;mailserver&gt; and an associated owner&quot;&quot;&quot;
+    username = sqlobject.col.StringCol()
+    mailserver = sqlobject.col.StringCol()
+    owner = sqlobject.col.ForeignKey('Person')
+
+class Person(sqlobject.SQLObject):
+    &quot;&quot;&quot; represents a person, currently only stores the name&quot;&quot;&quot;
+    fullname = sqlobject.col.StringCol()
+
+class Role(sqlobject.SQLObject):
+    &quot;&quot;&quot; represents the role of a person in respect to an email, we
+    link a message, with an email address and the according header
+    (cc, to)&quot;&quot;&quot;
+    email = sqlobject.col.ForeignKey('Email')
+    header = sqlobject.col.ForeignKey('Header')
+    msg = sqlobject.col.ForeignKey('Message')
+
+class HeaderValue(sqlobject.SQLObject):
+    &quot;&quot;&quot; this represents a header set in a message, the msg is linked
+    to the header and a value is associated.&quot;&quot;&quot;
+    value = sqlobject.col.StringCol()
+    msg = sqlobject.col.ForeignKey('Message')
+    header = sqlobject.col.ForeignKey('Header')
 
 def main():
-   Header.createTable(ifNotExists=True)
-   HeaderValue.createTable(ifNotExists=True)
-   Person.createTable(ifNotExists=True)
-   Email.createTable(ifNotExists=True)
-   Role.createTable(ifNotExists=True)
-   Message.createTable(ifNotExists=True)
-
-sqlhub.processConnection = connectionForURI('sqlite:' + os.path.abspath('db/messages.db'))
-
-if __name__=='__main__':
-   psyco.full()
-   sys.exit(main())
+    &quot;&quot;&quot; this function creates a new database&quot;&quot;&quot;
+    Header.createTable(ifNotExists = True)
+    HeaderValue.createTable(ifNotExists = True)
+    Person.createTable(ifNotExists = True)
+    Email.createTable(ifNotExists = True)
+    Role.createTable(ifNotExists = True)
+    Message.createTable(ifNotExists = True)
+
+&quot;&quot;&quot; if being executed instead of loaded as a module, create a new
+database&quot;&quot;&quot;
+if (__name__ == '__main__'):
+    psyco.full()
+    sys.exit(main())</diff>
      <filename>lib/objects.py</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,9 @@
+&quot;&quot;&quot;
+Maelstrom - visualizing email contacts
+
+Copyright&#169; 2008-2009 Stefan Marsiske &lt;my name at gmail.com&gt;
+&quot;&quot;&quot;
+
 import email
 
 def decode_header(text):</diff>
      <filename>lib/utils.py</filename>
    </modified>
    <modified>
      <diff>@@ -1 +1,4 @@
+# Maelstrom - visualizing email contacts
+# Copyright&#169; 2008-2009 Stefan Marsiske &lt;my name at gmail.com&gt;
+
 echo &quot;select username, mailserver, fullname from email, person where owner_id==person.id;&quot; | sqlite3 db/messages.db | sed -s 's/\(.*\)|\(.*\)|/\1@\2 / '</diff>
      <filename>utils/getNamedEmails.sh</filename>
    </modified>
    <modified>
      <diff>@@ -1 +1,3 @@
+# Maelstrom - visualizing email contacts
+# Copyright&#169; 2008-2009 Stefan Marsiske &lt;my name at gmail.com&gt;
 echo &quot;select * from email where owner_id is null;&quot; | sqlite3 db/messages.db | sed -s 's/.*|\(.*\)|\(.*\)|/\1@\2/'</diff>
      <filename>utils/getOrphanEmails.sh</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,8 @@
 #!/usr/bin/python
 
+# Maelstrom - visualizing email contacts
+# Copyright&#169; 2008-2009 Stefan Marsiske &lt;my name at gmail.com&gt;
+
 # BUGS: the mbox parse chockes on messages that have a line starting with From
 # in the body.
 import mailbox, sys, os, psyco, datetime, email, getopt</diff>
      <filename>utils/index.py</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,6 @@
 &lt;?php
+/* Maelstrom - visualizing email contacts
+   Copyright&#169; 2008-2009 Stefan Marsiske &lt;my name at gmail.com&gt; */
 include_once(&quot;maelstrom.php&quot;);
 
 if(isset($_GET['c'])) {
@@ -9,21 +11,35 @@ if(isset($_GET['c'])) {
 ?&gt;
 &lt;html&gt;
    &lt;head&gt;
-      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;timecloud/include/jquery.js&quot;&gt;&lt;/script&gt;
-      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;timecloud/include/jquery.sparkline.js&quot; &gt;&lt;/script&gt;
-      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;timecloud/include/ui.core.js&quot;&gt;&lt;/script&gt;
-      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;timecloud/include/ui.draggable.js&quot;&gt;&lt;/script&gt;
-      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;timecloud/include/ui.slider.js&quot;&gt;&lt;/script&gt;
-      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;timecloud/include/tagcloud.js&quot;&gt;&lt;/script&gt;
-      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;timecloud/timecloud.js&quot;&gt;&lt;/script&gt;
+      &lt;!--[if IE]&gt;&lt;script language=&quot;javascript&quot; type=&quot;text/javascript&quot; src=&quot;/timecloud/include/excanvas.js&quot;&gt;&lt;/script&gt;&lt;![endif]--&gt;
+      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;/timecloud/include/jquery.js&quot;&gt;&lt;/script&gt;
+      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;/timecloud/include/jquery.sparkline.js&quot; &gt;&lt;/script&gt;
+      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;/timecloud/include/jquery-ui-p.min.js&quot;&gt;&lt;/script&gt;
+      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;/timecloud/include/jquery.event.wheel.js&quot;&gt;&lt;/script&gt;
+      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;/timecloud/include/tagcloud.js&quot;&gt;&lt;/script&gt;
+      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;/timecloud/timecloud.js&quot;&gt;&lt;/script&gt;
       &lt;script type=&quot;text/javascript&quot;&gt;
          $(document).ready(function() {
             var params=&quot;&amp;c=&lt;?php print urlencode($person)?&gt;&quot;;
+            var query=&quot;maelstrom.php?op=contactOrgs&quot;+params;
+            $.getJSON(query,function(data) {
+               $('#orgs').timecloud({
+                   'timecloud': data,
+                   'winSize': 0,
+                   'urlprefix': 'org.php?o='})});
             var query=&quot;maelstrom.php?op=secondContacts&quot;+params;
-            $.getJSON(query,function(data) { $('#timecloud').timecloud({'timecloud':data})});
+            $.getJSON(query,function(data) {
+                $('#contacts').timecloud({
+                   'timecloud':data,
+                   'winSize': 0,
+                   'urlprefix': 'contact.php?c='})});
          })
       &lt;/script&gt;
-      &lt;link href=&quot;timecloud/style.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; /&gt;
+      &lt;style type=&quot;text/css&quot;&gt;
+         * { margin:  0; padding: 0; font-family: Georgia,Garamond,&quot;Times New Roman&quot;,serif;}
+         #content {width: 90%; margin-left: auto ; margin-right: auto ;}
+      &lt;/style&gt;
+      &lt;link href=&quot;/timecloud/style.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; /&gt;
    &lt;/head&gt;
    &lt;body&gt;
       &lt;div id=&quot;content&quot;&gt;
@@ -31,8 +47,8 @@ if(isset($_GET['c'])) {
             &lt;h1 id=&quot;pagetitle&quot;&gt;&lt;?print $person?&gt;&lt;/h1&gt;
             &lt;a href=&quot;contacts.php&quot;&gt;back to contacts&lt;/a&gt;
          &lt;/div&gt;
-
-            &lt;div id=&quot;timecloud&quot; /&gt;
+            &lt;div id=&quot;orgs&quot;&gt;&lt;/div&gt;
+            &lt;div id=&quot;contacts&quot;&gt;&lt;/div&gt;
          &lt;/div&gt;
       &lt;/div&gt;
    &lt;/body&gt;</diff>
      <filename>www/contact.php</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,6 @@
 &lt;?php
+/* Maelstrom - visualizing email contacts
+   Copyright&#169; 2008-2009 Stefan Marsiske &lt;my name at gmail.com&gt; */
 $timeconstraint='';
 if(isset($_GET['start'])) {
    $timeconstraint=&quot;&amp;start=&quot;.$_GET['start'];
@@ -9,20 +11,26 @@ if(isset($_GET['end'])) {
 ?&gt;
 &lt;html&gt;
    &lt;head&gt;
-      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;timecloud/include/jquery.js&quot;&gt;&lt;/script&gt;
-      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;timecloud/include/jquery.sparkline.js&quot; &gt;&lt;/script&gt;
-      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;timecloud/include/ui.core.js&quot;&gt;&lt;/script&gt;
-      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;timecloud/include/ui.draggable.js&quot;&gt;&lt;/script&gt;
-      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;timecloud/include/ui.slider.js&quot;&gt;&lt;/script&gt;
-      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;timecloud/include/tagcloud.js&quot;&gt;&lt;/script&gt;
-      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;timecloud/timecloud.js&quot;&gt;&lt;/script&gt;
+      &lt;!--[if IE]&gt;&lt;script language=&quot;javascript&quot; type=&quot;text/javascript&quot; src=&quot;/timecloud/include/excanvas.js&quot;&gt;&lt;/script&gt;&lt;![endif]--&gt;
+      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;/timecloud/include/jquery.js&quot;&gt;&lt;/script&gt;
+      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;/timecloud/include/jquery.sparkline.js&quot; &gt;&lt;/script&gt;
+      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;/timecloud/include/jquery-ui-p.min.js&quot;&gt;&lt;/script&gt;
+      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;/timecloud/include/jquery.event.wheel.js&quot;&gt;&lt;/script&gt;
+      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;/timecloud/include/tagcloud.js&quot;&gt;&lt;/script&gt;
+      &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;/timecloud/timecloud.js&quot;&gt;&lt;/script&gt;
+      &lt;style type=&quot;text/css&quot;&gt;
+         * { margin:  0; padding: 0; font-family: Georgia,Garamond,&quot;Times New Roman&quot;,serif;}
+         #content {width: 90%; margin-left: auto ; margin-right: auto ;}
+      &lt;/style&gt;
       &lt;script type=&quot;text/javascript&quot;&gt;
          $(document).ready(function() {
             var query=&quot;maelstrom.php?op=contactTimeCloud&lt;?php print $timeconstraint;?&gt;&quot;;
-            $.getJSON(query,function(data) { $('#timecloud').timecloud({'timecloud':data})});
+            $.getJSON(query,function(data) { $('#timecloud').timecloud({
+                    'timecloud':data,
+                    'urlprefix': 'contact.php?c='});});
          })
       &lt;/script&gt;
-      &lt;link href=&quot;timecloud/style.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; /&gt;
+      &lt;link href=&quot;/timecloud/style.css&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; /&gt;
    &lt;/head&gt;
    &lt;body&gt;
       &lt;div id=&quot;content&quot;&gt;</diff>
      <filename>www/contacts.php</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,393 @@
+/*
+ * File: Core.js
+ * 
+ * Author: Nicolas Garcia Belmonte
+ * 
+ * Copyright: Copyright 2008-2009 by Nicolas Garcia Belmonte.
+ * 
+ * License: BSD License
+ * 
+ * Homepage: &lt;http://thejit.org&gt;
+ * 
+ * Version: 1.0.8a
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the organization nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Nicolas Garcia Belmonte ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Nicolas Garcia Belmonte BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ */
+
+/*
+   Object: $_
+
+   Provides some common utility functions.
+*/
+var $_ = {
+	empty: function() {},
+	
+	fn: function(val) { return function() { return val; }; },
+
+	merge: function(){
+		var mix = {};
+		for (var i = 0, l = arguments.length; i &lt; l; i++){
+			var object = arguments[i];
+			if (typeof object != 'object') continue;
+			for (var key in object){
+				var op = object[key], mp = mix[key];
+				mix[key] = (mp &amp;&amp; typeof op == 'object' &amp;&amp; typeof mp == 'object') ? this.merge(mp, op) : this.unlink(op);
+			}
+		}
+		return mix;
+	},
+
+	unlink: function (object){
+		var unlinked = null;
+		if(this.isArray(object)) {
+				unlinked = [];
+				for (var i = 0, l = object.length; i &lt; l; i++) unlinked[i] = this.unlink(object[i]);
+		} else if(this.isObject(object)) {
+				unlinked = {};
+				for (var p in object) unlinked[p] = this.unlink(object[p]);
+		} else return object;
+
+		return unlinked;
+	},
+	
+	isArray: function(obj) {
+		return obj &amp;&amp; obj.constructor &amp;&amp; obj.constructor.toString().match(/array/i);
+	},
+	
+	isString: function(obj) {
+		return obj &amp;&amp; obj.constructor &amp;&amp; obj.constructor.toString().match(/string/i);
+	},
+	
+	isObject: function(obj) {
+		return obj &amp;&amp; obj.constructor &amp;&amp; obj.constructor.toString().match(/object/i);
+	}
+} ;
+/*
+ * File: Canvas.js
+ * 
+ * Author: Nicolas Garcia Belmonte
+ * 
+ * Copyright: Copyright 2008-2009 by Nicolas Garcia Belmonte.
+ * 
+ * License: BSD License
+ * 
+ * Homepage: &lt;http://thejit.org&gt;
+ * 
+ * Version: 1.0.8a
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the organization nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Nicolas Garcia Belmonte ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Nicolas Garcia Belmonte BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ */
+
+
+/*
+   Class: Canvas
+
+   A multi-purpose Canvas object decorator.
+*/
+var Canvas = (function () {
+	var ctx, bkctx, mainContainer, labelContainer, canvas, bkcanvas;
+	var config = {
+		'injectInto': 'id',
+		
+		'width':200,
+		'height':200,
+		
+		'backgroundColor':'#333333',
+		
+		'styles': {
+			'fillStyle':'#000000',
+			'strokeStyle':'#000000'
+		},
+		
+		'backgroundCanvas': false
+	};
+	
+	function hasCanvas() {
+		hasCanvas.t = hasCanvas.t || typeof(HTMLCanvasElement);
+		return &quot;function&quot; == hasCanvas.t || &quot;object&quot; == hasCanvas.t;
+	};
+	
+	function create(tag, prop, styles) {
+		var elem = document.createElement(tag);
+		(function(obj, prop) {
+			if(prop) for (var p in prop) obj[p] = prop[p];
+			return arguments.callee;
+		})(elem, prop)(elem.style, styles);
+		 //feature check
+		 if(tag == &quot;canvas&quot; &amp;&amp; !hasCanvas() &amp;&amp; G_vmlCanvasManager) {
+			elem = G_vmlCanvasManager.initElement(
+				document.body.appendChild(elem));
+		 }
+		 	
+		return elem;
+	};
+	
+	function get(id) {
+		return document.getElementById(id);
+	};
+	
+	function translateToCenter(canvas, ctx, w, h) {
+		var width = w? (w - canvas.width) : canvas.width;
+		var height = h? (h - canvas.height) : canvas.height;
+		ctx.translate(width / 2, height / 2);
+	};
+	
+	/*
+	   Constructor: Canvas
+	
+	   Canvas constructor.
+	
+	   Parameters:
+	
+	      id - The canvas tag id.
+	      opt - configuration object, possible parameters are:
+	      - *injectInto* id for the container of the canvas.
+	      Canvas object will be appended to the object specified by this id.
+	      - *width* canvas width, default's 200
+	      - *height* canvas height, default's 200
+	      - *backgroundColor* used for background color when clipping in IE
+	      - *styles* an object containing canvas style properties. See &lt;https://developer.mozilla.org/en/Canvas_tutorial/Applying_styles_and_colors&gt;
+		  - *backgroundCanvas* an object containing configuration properties for a background canvas.
+		  
+		  A possible configuration object could be defined as:
+		  (start code)
+			var config = {
+				'injectInto': 'id',
+				
+				'width':200,
+				'height':200,
+				
+				'backgroundColor':'#333333',
+				
+				'styles': {
+					'fillStyle':'#000000',
+					'strokeStyle':'#000000'
+				},
+				
+				'backgroundCanvas': false
+			};
+		  (end code)
+		  
+		  More information in &lt;http://blog.thejit.org&gt;.
+
+	   Returns:
+	
+	      A new Canvas instance.
+	*/
+	return function(id, opt) {
+		if(arguments.length &lt; 1) throw &quot;Arguments missing&quot;;
+		var idLabel = id + &quot;-label&quot;, idCanvas = id + &quot;-canvas&quot;, idBCanvas = id + &quot;-bkcanvas&quot;;
+		opt = $_.merge(config, opt || {});
+		//create elements
+		var dim = { 'width': opt.width, 'height': opt.height };
+		mainContainer = create(&quot;div&quot;, { 'id': id }, $_.merge(dim, { 'position': 'relative' }));
+		labelContainer = create(&quot;div&quot;, { 'id': idLabel }, { 
+			'overflow': 'visible',
+			'position': 'absolute',
+			'top': 0,
+			'left': 0,
+			'width': dim.width + 'px',
+			'height': 0
+		});
+		var dimPos = {
+			'position': 'absolute',
+			'top': 0,
+			'left': 0,
+			'width': dim.width + 'px',
+			'height': dim.height + 'px'
+		};
+		canvas = create(&quot;canvas&quot;, $_.merge({ 'id': idCanvas }, dim), dimPos);
+		var bc = opt.backgroundCanvas;
+		if(bc) {
+			bkcanvas = create(&quot;canvas&quot;, $_.merge({ 'id': idBCanvas }, dim), dimPos);
+			//append elements
+			mainContainer.appendChild(bkcanvas);
+		}
+		mainContainer.appendChild(canvas);
+		mainContainer.appendChild(labelContainer);
+		get(opt.injectInto).appendChild(mainContainer);
+		
+		//create contexts
+		ctx = canvas.getContext('2d');
+		translateToCenter(canvas, ctx);
+		var st = opt.styles;
+		for(var s in st) ctx[s] = st[s];
+		if(bc) {
+			bkctx = bkcanvas.getContext('2d');
+			var st = bc.styles;
+			for(var s in st) bkctx[s] = st[s];
+			translateToCenter(bkcanvas, bkctx);
+			bc.impl.init(bkcanvas, bkctx);
+			bc.impl.plot(bkcanvas, bkctx);
+		}
+		//create methods
+		return {
+			'id': id,
+			/*
+			   Method: getCtx
+			
+			   Returns:
+			
+			      Main canvas context.
+			*/
+			getCtx: function() {
+				return ctx;
+			},
+
+			/*
+			   Method: getElement
+			
+			   Returns:
+			
+			      DOM canvas wrapper generated. More information
+			      about this can be found in the post &lt;http://blog.thejit.org&gt;
+			*/
+			getElement: function() {
+				return mainContainer;
+			},
+			
+			/*
+			   Method: resize
+			
+			   Resizes the canvas.
+			
+			   Parameters:
+			
+			      width - New canvas width.
+			      height - New canvas height.
+			
+			*/
+			resize: function(width, height) {
+				(function(canvas, ctx) {
+					translateToCenter(canvas, ctx, width, height);
+					canvas.width = width;
+					canvas.height = height;
+					return arguments.callee;
+				})(canvas, ctx)(bkcanvas, bkctx);
+			},
+			
+			/*
+			   Method: getSize
+			
+			   Returns canvas dimensions.
+			
+			   Returns:
+			
+			      An object with _width_ and _height_ properties.
+			*/
+			getSize: function() {
+				return { 'width': canvas.width, 'height': canvas.height };
+			},
+			
+			/*
+			   Method: path
+			   
+			  Performs a _beginPath_ executes _action_ doing then a _type_ ('fill' or 'stroke') and closing the path with closePath.
+			*/
+			path: function(type, action) {
+				ctx.beginPath();
+				action(ctx);
+				ctx[type]();
+				ctx.closePath();
+			},
+			
+			/*
+			   Method: clear
+			
+			   Clears the canvas object.
+			*/		
+			clear: function () {
+				var size = this.getSize();
+				ctx.clearRect(-size.width / 2, -size.height / 2, size.width, size.height);
+			},
+			
+			/*
+			   Method: clearReactangle
+			
+			   Same as &lt;clear&gt; but only clears a section of the canvas.
+			   
+			   Parameters:
+			   
+			   	top - An integer specifying the top of the rectangle.
+			   	right -  An integer specifying the right of the rectangle.
+			   	bottom - An integer specifying the bottom of the rectangle.
+			   	left - An integer specifying the left of the rectangle.
+			*/		
+			clearRectangle: function (top, right, bottom, left) {
+				//if using excanvas
+				if(!hasCanvas()) {
+					var f0 = ctx.fillStyle;
+					ctx.fillStyle = opt.backgroundColor;
+					ctx.fillRect(left, top, right - left, bottom - top);
+					ctx.fillStyle = f0;
+				} else {
+					//TODO absolutely arbitraty offsets!
+					ctx.clearRect(left, top -2, right - left +2, Math.abs(bottom - top) +5);
+				}
+			},
+			
+			/*
+			   Method: makeRect
+			
+			   Draws a rectangle in canvas.
+			
+			   Parameters:
+			
+			      mode - A String sepecifying if mode is &quot;fill&quot; or &quot;stroke&quot;.
+			      pos - A set of two coordinates specifying top left and bottom right corners of the rectangle.
+			*/
+			makeRect: function(pos, mode) {
+				if(mode == &quot;fill&quot; || mode == &quot;stroke&quot;) {
+					ctx[mode + &quot;Rect&quot;](pos.x1, pos.y1, pos.x2, pos.y2);
+				} else throw &quot;parameter not recognized &quot; + mode;
+			}
+			
+		};
+	};
+	
+})();
 /*
  * File: Hypertree.js
  * 
@@ -5,9 +395,9 @@
  * 
  * Homepage: &lt;http://thejit.org&gt;
  * 
- * Version: 1.0.7a
+ * Version: 1.0.8a
  *
- * Copyright: Copyright 2008 by Nicolas Garcia Belmonte.
+ * Copyright: Copyright 2008-2009 by Nicolas Garcia Belmonte.
  * 
  * License: BSD License
  * 
@@ -37,205 +427,6 @@
  */
 
 /*
-   Object: $_
-
-   Provides some common utility functions.
-*/
-var $_ = {
-	fn: function() { return function() {}; },
-
-	merge: function(){
-		var mix = {};
-		for (var i = 0, l = arguments.length; i &lt; l; i++){
-			var object = arguments[i];
-			if (typeof object != 'object') continue;
-			for (var key in object){
-				var op = object[key], mp = mix[key];
-				mix[key] = (mp &amp;&amp; typeof op == 'object' &amp;&amp; typeof mp == 'object') ? this.merge(mp, op) : this.unlink(op);
-			}
-		}
-		return mix;
-	},
-
-	unlink: function (object){
-		var unlinked = null;
-		if(this.isArray(object)) {
-				unlinked = [];
-				for (var i = 0, l = object.length; i &lt; l; i++) unlinked[i] = this.unlink(object[i]);
-		} else if(this.isObject(object)) {
-				unlinked = {};
-				for (var p in object) unlinked[p] = this.unlink(object[p]);
-		} else return object;
-
-		return unlinked;
-	},
-	
-	isArray: function(obj) {
-		return obj.constructor.toString().match(/array/i);
-	},
-	
-	isString: function(obj) {
-		return obj.constructor.toString().match(/string/i);
-	},
-	
-	isObject: function(obj) {
-		return obj.constructor.toString().match(/object/i);
-	}
-} ;
-
-/*
-   Class: Canvas
-
-   A multi-purpose Canvas object decorator.
-*/
-
-/*
-   Constructor: Canvas
-
-   Canvas initializer.
-
-   Parameters:
-
-      canvasId - The canvas tag id.
-      fillStyle - (optional) fill color style. Default's to white
-      strokeStyle - (optional) stroke color style. Default's to white
-
-   Returns:
-
-      A new Canvas instance.
-*/
-var Canvas= function (canvasId, fillStyle, strokeStyle) {
-	//browser supports canvas element
-		this.canvasId= canvasId;
-		//canvas element exists
-		if((this.canvas= document.getElementById(this.canvasId)) 
-			&amp;&amp; this.canvas.getContext) {
-		      this.ctx = this.canvas.getContext('2d');
-		      this.ctx.fillStyle = fillStyle || 'white';
-		      this.ctx.strokeStyle = strokeStyle || 'white';
-		      this.setPosition();
-		      this.translateToCenter();
-		} else {
-			throw &quot;Canvas object could not initialize.&quot;;
-		}
-	
-};
-
-Canvas.prototype= {
-	/*
-	   Method: getContext
-
-	   Returns:
-	
-	      Canvas context handler.
-	*/
-	getContext: function () {
-		return this.ctx;
-	},
-
-	/*
-	   Method: setPosition
-	
-	   Calculates canvas absolute position on HTML document.
-	*/	
-	setPosition: function() {
-		var obj= this.canvas;
-		var curleft = curtop = 0;
-		if (obj.offsetParent) {
-			curleft = obj.offsetLeft
-			curtop = obj.offsetTop
-			while (obj = obj.offsetParent) {
-				curleft += obj.offsetLeft
-				curtop += obj.offsetTop
-			}
-		}
-		this.position= { x: curleft, y: curtop };
-	},
-
-	/*
-	   Method: getPosition
-
-	   Returns:
-	
-	      Canvas absolute position to the HTML document.
-	*/
-	getPosition: function() {
-		return this.position;
-	},
-
-	/*
-	   Method: clear
-	
-	   Clears the canvas object.
-	*/		
-	clear: function () {
-		var size = this.getSize();
-		this.ctx.clearRect(-size.x / 2, -size.y / 2, size.x, size.y);
-	},
-
-	/*
-	   Method: drawMainCircle
-	
-	   Draws the boundary circle for the Hyperbolic Tree.
-	*/	
-	drawMainCircle: function () {	
-	  var ctx= this.ctx;
-	  ctx.beginPath();
-	  	ctx.arc(0, 0, this.getSmallerSize() / 2, 0, Math.PI*2, true);
-	  	ctx.stroke();
- 		ctx.closePath();
-	},
-	
-	/*
-	   Method: translateToCenter
-	
-	   Translates canvas coordinates system to the center of the canvas object.
-	*/
-	translateToCenter: function() {
-		this.ctx.translate(this.canvas.width / 2, this.canvas.height / 2);
-	},
-	
-
-	/*
-	   Method: getSize
-
-	   Returns:
-	
-	      An object that contains the canvas width and height.
-	      i.e. { x: canvasWidth, y: canvasHeight }
-	*/
-	getSize: function () {
-		var width = this.canvas.width;
-		var height = this.canvas.height;
-		return { x: width, y: height };
-	},
-
-	/*
-	   Method: path
-	   
-	  Performs a _beginPath_ executes _action_ doing then a _type_ ('fill' or 'stroke') and closing the path with closePath.
-	*/
-	path: function(type, action) {
-		this.ctx.beginPath();
-		action(this.ctx);
-		this.ctx[type]();
-		this.ctx.closePath();
-	},
-	
-	/*
-	   Method: getSmallerSize
-	   
-	  Returns min(width, height) for the canvas object.
-	*/
-	getSmallerSize: function() {
-		var s = this.getSize();
-		return (s.x &lt;= s.y)? s.x : s.y;
-	}
-	
-};
-
-
-/*
    Class: Complex
 	
 	 A multi-purpose Complex Class with common methods.
@@ -1350,8 +1541,8 @@ var GraphPlot = {
 	sequence: function(viz, options) {
 		options = $_.merge({
 			condition: function() { return false; },
-			step: $_.fn(),
-			onComplete: $_.fn(),
+			step: $_.empty,
+			onComplete: $_.empty,
 			duration: 200
 		}, options);
 
@@ -1413,9 +1604,8 @@ var GraphPlot = {
 	*/
 	plot: function(viz, opt) {
 		var aGraph = viz.graph, canvas = viz.canvas, id = viz.root;
-		var that = this, ctx = canvas.getContext(), GUtil = GraphUtil;
+		var that = this, ctx = canvas.getCtx(), GUtil = GraphUtil;
 		canvas.clear();
-		if(Config.drawMainCircle) canvas.drawMainCircle();
 		var T = !!aGraph.getNode(id).visited;
 		GUtil.eachNode(aGraph, function(node) {
 			GUtil.eachAdjacency(node, function(adj) {
@@ -1430,7 +1620,9 @@ var GraphPlot = {
 			});
 			ctx.save();
 			ctx.globalAlpha = node.alpha;
+			opt.onBeforePlotNode(node);
 			that.plotNode(node, canvas);
+			opt.onAfterPlotNode(node);
 	 		if(!that.labelsHidden &amp;&amp; ctx.globalAlpha &gt;= .95) that.plotLabel(canvas, node, opt);
 	 		else if(!that.labelsHidden &amp;&amp; ctx.globalAlpha &lt; .95) that.hideLabel(node);
 			ctx.restore();
@@ -1444,7 +1636,8 @@ var GraphPlot = {
 	   Plots a graph node.
 	*/
 	plotNode: function(node, canvas) {
-		var scale = canvas.getSmallerSize() / 2;
+		var size = canvas.getSize();
+		var scale = Math.min(size.width, size.height)/ 2;
 		var p = node.pos.toComplex(), pos = p.scale(scale);
 		canvas.path('fill', function(context) {
 			var prod = Config.transformNodes?  node._radius * (1 - p.squaredNorm()) : node._radius;
@@ -1462,11 +1655,12 @@ var GraphPlot = {
 		var node = adj.nodeFrom, child = adj.nodeTo, data = adj.data;
 		var pos = node.pos.toComplex(), posChild = child.pos.toComplex();
 		var centerOfCircle = this.computeArcThroughTwoPoints(pos, posChild);
-		var scale = canvas.getSmallerSize()/2;
+		var size = canvas.getSize();
+		var scale = Math.min(size.width, size.height)/2;
 		var angleBegin = Math.atan2(posChild.y - centerOfCircle.y, posChild.x - centerOfCircle.x);
-   		var angleEnd    = Math.atan2(pos.y - centerOfCircle.y, pos.x - centerOfCircle.x);
-		var sense        = this.sense(angleBegin, angleEnd);
-		var context = canvas.getContext();
+   		var angleEnd   = Math.atan2(pos.y - centerOfCircle.y, pos.x - centerOfCircle.x);
+		var sense      = this.sense(angleBegin, angleEnd);
+		var context = canvas.getCtx();
 		context.save();
 		canvas.path('stroke', function(ctx) {
 		 	if(centerOfCircle.a &gt; 1000 || centerOfCircle.b &gt; 1000 || centerOfCircle.ratio &gt; 1000) {
@@ -1517,7 +1711,7 @@ var GraphPlot = {
 	*/
 	plotLabel: function(canvas, node, controller) {
 		var id = node.id, tag = this.getLabel(id);
-		if(!tag &amp;&amp; !(tag = document.getElementById(id))) {
+		if(!tag) {
 			tag = document.createElement('div');
 			var container = this.getLabelContainer();
 			container.appendChild(tag);
@@ -1525,18 +1719,19 @@ var GraphPlot = {
 			tag.className = 'node';
 			tag.style.position = 'absolute';
 			controller.onCreateLabel(tag, node);
+			this.labels[node.id] = tag;
 		}
 		var pos = node.pos.toComplex();
 		var radius= canvas.getSize();
-		var scale = canvas.getSmallerSize() / 2;
-		var canvasPos = canvas.getPosition();
+		var scale = Math.min(radius.width, radius.height) / 2;
 		var labelPos= {
-			x: Math.round(pos.x * scale + canvasPos.x + radius.x/2),
-			y: Math.round(pos.y * scale + canvasPos.y + radius.y/2)
+			x: Math.round(pos.x * scale + radius.width/2),
+			y: Math.round(pos.y * scale + radius.height/2)
 		};
-		tag.style.left = labelPos.x + 'px';
-		tag.style.top = labelPos.y  + 'px';
-		tag.style.display = '';
+		var style = tag.style;
+		style.left = labelPos.x + 'px';
+		style.top = labelPos.y  + 'px';
+		style.display = '';
 		controller.onPlaceLabel(tag, node);
 	},
 
@@ -1583,7 +1778,7 @@ var GraphPlot = {
 	
 	(start code)
 
-	  var canvas= new Canvas('infovis', '#fff', '#fff');
+	  var canvas= new Canvas('infovis', {});
 	  var ht= new Hypertree(canvas);
 	  ht.loadTreeFromJSON(json);
 	  ht.compute();
@@ -1608,14 +1803,16 @@ var GraphPlot = {
 */
 var Hypertree = function(canvas, controller) {
 	var innerController = {
-		onBeforeCompute: $_.fn(),
-		onAfterCompute:  $_.fn(),
-		onCreateLabel:   $_.fn(),
-		onPlaceLabel:    $_.fn(),
-		onCreateElement: $_.fn(),
-		onComplete:      $_.fn(),
-		onBeforePlotLine: $_.fn(),
-		onAfterPlotLine: $_.fn(),
+		onBeforeCompute: $_.empty,
+		onAfterCompute:  $_.empty,
+		onCreateLabel:   $_.empty,
+		onPlaceLabel:    $_.empty,
+		onCreateElement: $_.empty,
+		onComplete:      $_.empty,
+		onBeforePlotLine: $_.empty,
+		onAfterPlotLine: $_.empty,
+		onBeforePlotNode: $_.empty,
+		onAfterPlotNode: $_.empty,
 		request:         false
 	};
 	
@@ -1628,7 +1825,7 @@ var Hypertree = function(canvas, controller) {
 
 	Animation.fps = Config.fps;
 	Animation.duration = Config.animationTime;
-
+	Config.labelContainer = canvas.id + &quot;-label&quot;;
 };
 
 Hypertree.prototype = {
@@ -1927,18 +2124,18 @@ Hypertree.prototype = {
 	
 	 Performs all calculations and animation when clicking on a label specified by _id_. The label id is the same id as its homologue node.
 	*/
-	onClick: function(e) {
-		Mouse.capturePosition(e);
-		var mousePosition = Mouse.getPosition(this.canvas);
-		this.move(mousePosition);
+	onClick: function(id) {
+      var node=this.graph.getNode(id);
+		var pos = node.pos.toComplex();
+		this.move(pos,node);
 	},
 	
-	move: function(mousePosition) {
-		if(this.busy === false &amp;&amp; mousePosition.norm() &lt; 1) {
+	move: function(pos,node) {
+		var versor = new Complex(pos.x, pos.y);
+		if(this.busy === false &amp;&amp; versor.norm() &lt; 1) {
 			this.busy = true;
-			var root = this.graph.getNode(this.root), that = this;
-			this.controller.onBeforeCompute(root);
-			var versor = new Complex(mousePosition.x, mousePosition.y);
+			var that = this;
+			this.controller.onBeforeCompute(node);
 			if(versor.norm() &lt; 1)
 				GraphPlot.animate(this, $_.merge(this.controller, {
 					modes: ['moebius'],
@@ -1948,90 +2145,9 @@ Hypertree.prototype = {
 					}
 				}), versor);
 		}
-	},
-	
-	/*
-	 Method: prepareCanvasEvents
-	
-	 Adds a click handler to the canvas in order to translate the Hypertree when clicking anywhere on the canvas. You could set a translation handler on the labels and not activate this one if you want to. You can perform that by using a controller &lt;http://blog.thejit.org/?p=8&gt;
-	*/
-	prepareCanvasEvents: function() {
-		var that = this;
-		this.canvas.canvas.onclick = function(e) { that.onClick(e); };
-	}
-	
+	}	
 };
 
-
-/*
-   Class: Mouse
-
-   A multi-purpose Mouse class.
-*/
-var Mouse = {
-	  
-	  position: null,
-
-		/*
-		   Method: getPosition
-		
-		   Returns mouse position relative to canvas.
-		
-		   Parameters:
-		
-		      canvas - A canvas object.
-		
-		   Returns:
-		
-		      A Complex instance representing the mouse position on the canvas.
-		*/
-		getPosition: function (canvas) {
-			var posx = this.posx;
-			var posy = this.posy;
-			var position = canvas.getPosition();
-			var s = canvas.getSmallerSize();
-			var size = canvas.getSize();
-			var coordinates= {
-			  x: ((posx - position.x) - size.x / 2) / (s / 2),
-			  y: ((posy - position.y) - size.y / 2) / (s / 2)
-			};
-			
-			this.position= new Complex(coordinates.x, coordinates.y);
-			return this.position;
-		},
-
-
-		/*
-		   Method: capturePosition
-		
-		   Captures mouse position.
-		
-		   Parameters:
-		
-		      e - Triggered event.
-		*/
-	  capturePosition: function(e) {
-			var posx = 0;
-			var posy = 0;
-			if (!e) var e = window.event;
-			if (e.pageX || e.pageY) 	{
-				posx = e.pageX;
-				posy = e.pageY;
-			}
-			else if (e.clientX || e.clientY) 	{
-				posx = e.clientX + document.body.scrollLeft
-					+ document.documentElement.scrollLeft;
-				posy = e.clientY + document.body.scrollTop
-					+ document.documentElement.scrollTop;
-			}
-			
-			this.posx= posx;
-			this.posy= posy;
-	}
-};
-
-
-
 /*
  Class: Graph
 </diff>
      <filename>www/js/Hypertree.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,171 +1,213 @@
-var Log = {
-	elem: $('log'),
-	write: function(text) {
-		if(!this.elem) this.elem = $('log');
-		this.elem.set('html', text);
-	}
-};
-
-function init(q) {
-  //computes page layout (not a library function, used to adjust some css thingys on the page)
-  Infovis.initLayout();
-
-  //Draw main background circle
-  Config.drawMainCircle = false;
-
-  var canvas= new Canvas('infovis', '#ddd', '#ddd');
-
-  var ht= new Hypertree(canvas, {
-	onBeforeCompute: function(node) {
-		Log.write(&quot;centering&quot;);
-                this.nodeId = node.id;
-  		this.nodeName = node.name;
-	},
-
-	getName: function(node1, node2) {
-		for(var i=0; i&lt;node1.data.length; i++) {
-			var dataset = node1.data[i];
-			if(dataset.key == node2.name) return dataset.value;
-		}
-
-		for(var i=0; i&lt;node2.data.length; i++) {
-			var dataset = node2.data[i];
-			if(dataset.key == node1.name) return dataset.value;
-		}
-	},
-
-	onCreateLabel: function(domElement, node) {
-		var d = $(domElement);
-		d.set('tween', { duration: 300 }).set('html', node.name).setOpacity(0.8).addEvents({
-
-	//Call the &quot;onclick&quot; method from the hypertree to move the hypertree correspondingly.
-	//This method takes the native event object. Since Mootools uses a wrapper for this
-	//event, I have to put e.event to get the native event object.
-			'click': function(e) {
-				ht.onClick(e.event);
-			},
-
-			'mouseenter': function() {
-				d.tween('opacity', 1);
-			},
-
-			'mouseleave': function() {
-				d.tween('opacity', 0.8);
-			}
-		});
-	 },
-
-	//Take the left style property and substract half of the label actual width.
-	onPlaceLabel: function(tag, node) {
-		var width = tag.offsetWidth;
-		var intX = tag.style.left.toInt();
-		intX -= width/2;
-		tag.style.left = intX + 'px';
-	},
-
-        //This method searches for nodes that already
-        //existed in the visualization and sets the new node's
-        //id to the previous one. That way, all existing nodes
-        //that exist also in the new data won't be deleted.
-        preprocessTree: function(json) {
-	var ch = json.children;
-	var getNode = function(nodeName) {
-		for(var i=0; i&lt;ch.length; i++) {
-			if(ch[i].name == nodeName) return ch[i];
-		}
-		return false;
-	};
-	json.id = ht.root;
-	var root = ht.graph.getNode(ht.root);
-	GraphUtil.eachAdjacency(root, function(elem) {
-		var nodeTo = elem.nodeTo, jsonNode = getNode(nodeTo.name);
-		if(jsonNode) jsonNode.id = nodeTo.id;
-	});
-        },
-
-	onAfterCompute: function() {
-		Log.write(&quot;done&quot;);
-		var node = GraphUtil.getClosestNodeToOrigin(ht.graph, &quot;pos&quot;);
-		var that = this;
-	        var id = node.id, name = node.name;
-
-	     Log.write(&quot;requesting info...&quot;);
-	     var jsonRequest = new Request.JSON({
-		'url': URLTEMPLATE + encodeURIComponent(name) ,
-
-		onSuccess: function(json) {
-			Log.write(&quot;morphing...&quot;);
-			//Once me received the data
-			//we preprocess the ids of the nodes
-			//received to match existing nodes
-			//in the graph and perform a morphing
-			//operation.
-			that.preprocessTree(json);
-			GraphOp.morph(ht, json, {
-				'id': id,
-				'type': 'fade',
-				'duration':2000,
-				hideLabels:true,
-				onComplete: function() {
-					Log.write('done');
-				},
-				onAfterCompute: $empty,
-				onBeforeCompute: $empty
-			});
-	                //set details for this node.
-	                var html = &quot;&lt;h4&gt;&quot; + node.name + &quot;&lt;/h4&gt;&lt;b&gt;Connections:&lt;/b&gt;&quot;;
-	                html += &quot;&lt;ul&gt;&quot;;
-	                GraphUtil.eachAdjacency(node, function(adj) {
-	                	var child = adj.nodeTo;
-	                	if(child.data &amp;&amp; child.data.length &gt; 0) {
-	                		html += &quot;&lt;li&gt;&quot; + child.name + &quot; &quot; + &quot;&lt;div class=\&quot;relation\&quot;&gt;(relation: &quot; + that.getName(node, child) + &quot;)&lt;/div&gt;&lt;/li&gt;&quot;;
-	                	}
-	                });
-	                html+= &quot;&lt;/ul&gt;&quot;;
-	                $('inner-details').set(&quot;html&quot;, html);
-	                //hide labels that aren't directly connected to the centered node.
-	                var GPlot = GraphPlot;
-	                GraphUtil.eachNode(ht.graph, function(elem) {
-	                	if(elem.id != node.id &amp;&amp; !node.adjacentTo(elem)){
-	                		GPlot.hideLabel(elem);
-	                	}
-	                });
-			Log.write(&quot;done.&quot;);
-		},
-
-		onFailure: function() {
-			Log.write(&quot;sorry, the request failed&quot;);
-		}
-	}).get();
-	}
-  });
-
-        window.addEvent('domready', function() {
-	Log.write(&quot;Loading data...&quot;);
-	new Request.JSON({
-	  	'url':q,
-	  	onSuccess: function(json) {
-	                  Log.write(&quot;calculating graph...&quot;);
-			  //load wine dependency tree.
-			  ht.loadTreeFromJSON(json);
-			  //compute positions
-			  ht.compute();
-			  //make first plot
-			  ht.plot();
-			  Log.write(&quot;done&quot;);
-			  ht.controller.nodeName = name;
-	  	},
-
-	  	onFailure: function() {
-	  		Log.write(&quot;failed!&quot;);
-	  	}
-	}).get();
-
-	});
-	//compute positions then plot.
-	//ht.refresh();
-	//optional: set an &quot;onclick&quot; event handler on the canvas tag to animate the tree.
-	//ht.prepareCanvasEvents();
-	//ht.controller.onAfterCompute();
-}
-
+/* Maelstrom - visualizing email contacts
+   Copyright&#169; 2008-2009 Stefan Marsiske &lt;my name at gmail.com&gt; */
+var Log = {
+	elem: $('log'),
+	write: function(text) {
+		if(!this.elem) this.elem = $('log');
+		this.elem.set('html', text);
+	}
+};
+
+function init(q) {
+  //computes page layout (not a library function, used to adjust some css thingys on the page)
+  Infovis.initLayout();
+
+  //Get width and height properties for the canvas container.
+  var infovis = $('infovis');
+  var w = infovis.offsetWidth, h = infovis.offsetHeight;
+  var fStyle, sStyle, lineWidth;
+  //Create a new canvas instance.
+  var canvas = new Canvas('mycanvas', {
+     //Where to inject the canvas. Any HTML container will do.
+     'injectInto':'infovis',
+     //Width and height of canvas, default's to 200.
+     'width': w,
+     'height':h,
+     //Canvas styles.
+     'styles': {
+     'fillStyle': '#ddd',
+     'strokeStyle': '#ddd'
+     },
+
+     //Add a background canvas to draw the main circle.
+     'backgroundCanvas': {
+         'styles': {
+         'fillStyle': '#ccc',
+         'strokeStyle': '#ccc'
+         },
+
+         'impl': {
+         'init': $empty,
+             'plot': function(canvas, ctx) {
+                ctx.beginPath();
+                ctx.arc(0, 0, ((w &lt; h)? w : h) / 2, 0, Math.PI*2, true);
+                ctx.stroke();
+                ctx.closePath();
+             }
+         }
+     }
+  });
+
+  var ht= new Hypertree(canvas, {
+     nodeId: &quot;&quot;,  
+     nodeName: &quot;&quot;,  
+     onBeforeCompute: function(node) {
+         Log.write(&quot;centering &quot; + node.name + &quot;...&quot;);
+         this.nodeId = node.id;
+         this.nodeName = node.name;
+      },
+
+      getName: function(node1, node2) {
+         for(var i=0; i&lt;node1.data.length; i++) {
+            var dataset = node1.data[i];
+            if(dataset.key == node2.name) return dataset.value;
+         }
+
+         for(var i=0; i&lt;node2.data.length; i++) {
+            var dataset = node2.data[i];
+            if(dataset.key == node1.name) return dataset.value;
+         }
+      },
+
+      onCreateLabel: function(domElement, node) {
+         var d = $(domElement);
+         d.set('tween', { duration: 300 }).set('html', node.name).setOpacity(0.8).addEvents({
+
+            //Call the &quot;onclick&quot; method from the hypertree to move the hypertree correspondingly.
+            //This method takes the native event object. Since Mootools uses a wrapper for this
+            //event, I have to put e.event to get the native event object.
+            'click': function(e) {
+               ht.onClick(d.id);
+            },
+
+            'mouseenter': function() {
+               d.tween('opacity', 1);
+            },
+
+            'mouseleave': function() {
+               d.tween('opacity', 0.8);
+            }
+         });
+       },
+
+      //Take the left style property and substract half of the label actual width.
+      onPlaceLabel: function(tag, node) {
+         var width = tag.offsetWidth;
+         var intX = tag.style.left.toInt();
+         intX -= width/2;
+         tag.style.left = intX + 'px';
+      },
+
+      //This method searches for nodes that already
+      //existed in the visualization and sets the new node's
+      //id to the previous one. That way, all existing nodes
+      //that exist also in the new data won't be deleted.
+      preprocessTree: function(json) {
+         var ch = json.children;
+         var getNode = function(nodeName) {
+            for(var i=0; i&lt;ch.length; i++) {
+               if(ch[i].name == nodeName) return ch[i];
+            }
+            return false;
+         };
+         json.id = ht.graph.getNode(json.name); //ht.root;
+         var root = ht.graph.getNode(ht.root);
+         GraphUtil.eachAdjacency(root, function(elem) {
+            var nodeTo = elem.nodeTo, jsonNode = getNode(nodeTo.name);
+            if(jsonNode) jsonNode.id = nodeTo.id;
+         });
+      },
+
+      reloadGraph: function() {
+         var that = this, id = this.nodeId, name = this.nodeName;
+         Log.write(&quot;requesting info...&quot;);
+         var jsonRequest = new Request.JSON({
+            'url': URLTEMPLATE + encodeURIComponent(name),
+            onSuccess: function(json) {
+               Log.write(&quot;morphing...&quot;);
+               //Once me received the data we preprocess the ids of the nodes
+               //received to match existing nodes in the graph and perform a
+               //morphing operation.
+               that.preprocessTree(json);
+               GraphOp.morph(ht, json, {
+                  'id': id,
+                  'type': 'fade',
+                  'duration':2000,
+                  hideLabels:true,
+                  onComplete: function() {
+                     Log.write('done');
+                  },
+                  onAfterCompute: $empty,
+                  onBeforeCompute: $empty
+               });
+            },
+            onFailure: function() {
+               Log.write(&quot;sorry, the request failed&quot;);
+            }
+         }).get();
+      },
+
+      refreshDetails: function() {
+         var node = GraphUtil.getClosestNodeToOrigin(ht.graph, &quot;pos&quot;);
+         var that = this;
+         //set details for this node.
+         var html = &quot;&lt;h4&gt;&quot; + node.name + &quot;&lt;/h4&gt;&lt;b&gt;Connections:&lt;/b&gt;&quot;;
+         html += &quot;&lt;ul&gt;&quot;;
+         GraphUtil.eachAdjacency(node, function(adj) {
+            var child = adj.nodeTo;
+            if(child.data &amp;&amp; child.data.length &gt; 0) {
+               html += &quot;&lt;li&gt;&quot; + child.name + &quot;&lt;/li&gt;&quot;; //+ &quot; &quot; + &quot;&lt;div class=\&quot;relation\&quot;&gt;(relation: &quot; + that.getName(node, child) + &quot;)&lt;/div&gt;&lt;/li&gt;&quot;;
+            }
+         });
+         html+= &quot;&lt;/ul&gt;&quot;;
+         $('inner-details').set(&quot;html&quot;, html);
+
+         //hide labels that aren't directly connected to the centered node.
+         var GPlot = GraphPlot;
+         GraphUtil.eachNode(ht.graph, function(elem) {
+            if(elem.id != node.id &amp;&amp; !node.adjacentTo(elem)){
+               GPlot.hideLabel(elem);
+            }
+         });
+      },
+      onAfterCompute: function() {
+         var node = GraphUtil.getClosestNodeToOrigin(ht.graph, &quot;pos&quot;);
+         this.nodeName=node.name;
+         this.nodeId=node.id;
+         this.refreshDetails();
+
+         this.reloadGraph();
+      }
+   });
+
+   window.addEvent('domready', function() {
+      // optional: set an &quot;onclick&quot; event handler on the canvas tag to animate the tree.
+      var mycanvas = $('mycanvas');
+      var size = canvas.getSize();
+      mycanvas.addEvent('click', function(e) {
+         var pos = mycanvas.getPosition();
+         var s = Math.min(size.width, size.height) / 2;
+         ht.move({
+            'x':  (e.page.x - pos.x - size.width  / 2) / s,
+            'y':  (e.page.y - pos.y - size.height / 2) / s
+         });
+      });
+      Log.write(&quot;Loading data...&quot;);
+      new Request.JSON({
+         'url':q,
+         onSuccess: function(json) {
+            Log.write(&quot;calculating graph...&quot;);
+            //load data
+            ht.loadTreeFromJSON(json);
+            //compute positions then plot.
+            ht.refresh();
+            ht.controller.refreshDetails();
+            Log.write(&quot;done&quot;);
+         },
+         onFailure: function() {
+            Log.write(&quot;failed!&quot;);
+         }
+      }).get();
+	});
+
+}</diff>
      <filename>www/js/static1-hypertree.js</filename>
    </modified>
    <modified>
      <diff>@@ -1,13 +1,15 @@
 &lt;?php
+/* Maelstrom - visualizing email contacts
+   Copyright&#169; 2008-2009 Stefan Marsiske &lt;my name at gmail.com&gt; */
 
-$MAILBOXOWNER=&quot;Marsiske Stefan&quot;;
+$MAILBOXOWNER=&quot;&quot;; //IMPORTANT CONFIGURE THESE!!!!
 $dburl='sqlite:'.$_SERVER['DOCUMENT_ROOT'].'/maelstrom/db/messages.db';
 
 // get all mails from person
 
 function contactMails($db) {
    global $MAILBOXOWNER;
-   list($start, $time)=mailTimeFrame($db);
+   list($start, $end)=mailTimeFrame($db);
 
    if(isset($_GET['start'])) {
       $start=$_GET['start'];
@@ -23,8 +25,8 @@ function contactMails($db) {
 
    $q=&quot;select count(message.id) as count,
               date(message.delivered) as delivered
-         from message, role, email, person 
-         where message.id=role.msg_id and 
+         from message, role, email, person
+         where message.id=role.msg_id and
                role.email_id=email.id and
                email.owner_id==person.id and
                person.fullname=='$person'
@@ -44,7 +46,7 @@ function contactMails($db) {
 // get all contacts with weights for person
 
 function secondContacts($db) {
-   list($start, $time)=mailTimeFrame($db);
+   list($start, $end)=mailTimeFrame($db);
 
    if(isset($_GET['start'])) {
       $start=$_GET['start'];
@@ -58,7 +60,7 @@ function secondContacts($db) {
       $person=$MAILBOXOWNER;
    }
 
-   // TODO add window handling if needed, should be enough if the subquery 
+   // TODO add window handling if needed, should be enough if the subquery
    // return messages in the interval
    //and date(message.delivered)&gt;='$start' AND
    //date(message.delivered)&lt;'$end'&quot;;
@@ -66,16 +68,15 @@ function secondContacts($db) {
               count(person.id) as count,
               date(message.delivered) as date
          from message, role, email, person
-         where message.id in (select message.id 
-                             from message, role, email, person 
-                             where message.id==role.msg_id and 
+         where message.id in (select message.id
+                             from message, role, email, person
+                             where message.id==role.msg_id and
                                    role.email_id==email.id and
                                    email.owner_id==person.id and
                                    person.fullname like '$person') and
               message.id==role.msg_id and
               role.email_id==email.id and
-              email.owner_id==person.id and 
-              person.fullname!='$MAILBOXOWNER' and
+              email.owner_id==person.id and
               person.fullname not like '$person'
          group by contact
          order by date;&quot;;
@@ -177,6 +178,122 @@ function contactTimeCloud($db) {
    return ($results);
 }
 
+function contactOrgs($db) {
+   list($start, $end)=mailTimeFrame($db);
+
+   if(isset($_GET['c'])) {
+      $user=$_GET['c'];
+   } else {
+      $user=$MAILBOXOWNER;
+   }
+
+   if(isset($_GET['start'])) {
+      $start=$_GET['start'];
+   }
+   if(isset($_GET['end'])) {
+      $end=$_GET['end'];
+   }
+
+   $q=&quot;select mailserver as org,
+             count(role.id) as count,
+             date(message.delivered) as date 
+        from email, person, role, message
+        where role.email_id==email.id and
+             email.owner_id==person.id and
+             role.msg_id==message.id and
+             fullname='$user'
+        group by org, delivered
+        order by delivered;&quot;;
+   $results=array();
+   $curdate=&quot;00-00-00&quot;;
+   foreach ($db-&gt;query($q) as $row) {
+      //print_r($row);
+      //print &quot;&lt;br&gt;&quot;;
+      $date=$row['date'];
+      if($date&gt;$curdate) {
+         // row is a new day
+         if(isset($res)) {
+            // store the list of contact volumes in result vector
+            $results[]=array($curdate, $res);
+         }
+         // create a new list of contact volumes
+         $res=array(array($row['org'], $row['count']));
+         // set curdate to the currently created new day
+         $curdate=$date;
+      } elseif($date==$curdate) {
+         // store the contact in the current days volume list
+         $res[]=array($row['org'], $row['count']);
+      } else {
+         header(&quot;Content-type: text/plain&quot;);
+         print &quot;curdate $curdate&quot;;
+         print &quot;date $date&quot;;
+         print_r($row);
+      }
+   }
+   // append the last day
+   if($res) {
+      $results[]=array($curdate, $res);
+   }
+   return ($results);
+}
+
+function orgContacts($db) {
+   list($start, $end)=mailTimeFrame($db);
+
+   if(!isset($_GET['o'])) {
+     die;
+   }
+   $org=$_GET['o'];
+
+   if(isset($_GET['start'])) {
+      $start=$_GET['start'];
+   }
+   if(isset($_GET['end'])) {
+      $end=$_GET['end'];
+   }
+   $q=&quot;select person.fullname as contact,
+              count(person.id) as count,
+              date(message.delivered) as date
+         from message, role, email, person
+         where mailserver like '$org' and
+              message.id==role.msg_id and
+              role.email_id==email.id and
+              email.owner_id==person.id
+         group by contact
+         order by date;&quot;;
+   $results=array();
+   $curdate=&quot;00-00-00&quot;;
+   foreach ($db-&gt;query($q) as $row) {
+      //$results[]=array($row['contact'],$row['count'],$row['date']);
+      //print_r($row);
+      //print &quot;&lt;br&gt;&quot;;
+      $date=$row['date'];
+      if($date&gt;$curdate) {
+         // row is a new day
+         if(isset($res)) {
+            // store the list of contact volumes in result vector
+            $results[]=array($curdate, $res);
+         }
+         // create a new list of contact volumes
+         $res=array(array($row['contact'], $row['count']));
+         // set curdate to the currently created new day
+         $curdate=$date;
+      } elseif($date==$curdate) {
+         // store the contact in the current days volume list
+         $res[]=array($row['contact'], $row['count']);
+      } else {
+         print &quot;curdate $curdate&quot;;
+         print &quot;date $date&quot;;
+         print_r($row);
+      }
+   }
+   // append the last day
+   if($res) {
+      $results[]=array($curdate, $res);
+   }
+   return ($results);
+}
+
 function mailFrequency($db) {
    list($start, $end)=mailTimeFrame($db);
 
@@ -219,12 +336,20 @@ if(isset($_GET['op'])) {
       print json_encode(mailFrequency($db));
    }
    elseif($_GET['op']==&quot;contactMails&quot;) {
-      //header(&quot;Content-type: text/plain&quot;);
-      print json_encode(contactMails($db));
+     //header(&quot;Content-type: text/plain&quot;);
+     print json_encode(contactMails($db));
    }
    elseif($_GET['op']==&quot;secondContacts&quot;) {
-      //header(&quot;Content-type: text/plain&quot;);
-      print json_encode(secondContacts($db));
+     //header(&quot;Content-type: text/plain&quot;);
+     print json_encode(secondContacts($db));
+   }
+   elseif($_GET['op']==&quot;contactOrgs&quot;) {
+     //header(&quot;Content-type: text/plain&quot;);
+     print json_encode(contactOrgs($db));
+   }
+   elseif($_GET['op']==&quot;orgContacts&quot;) {
+     //header(&quot;Content-type: text/plain&quot;);
+     print json_encode(orgContacts($db));
    }
 }
 ?&gt;</diff>
      <filename>www/maelstrom.php</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,6 @@
 &lt;?php
+/* Maelstrom - visualizing email contacts
+   Copyright&#169; 2008-2009 Stefan Marsiske &lt;my name at gmail.com&gt; */
 
 $dburl='sqlite:'.$_SERVER['DOCUMENT_ROOT'].'/maelstrom/db/messages.db';
 
@@ -14,20 +16,20 @@ function mailTimeFrame($db) {
    return array($start,$end);
 }
 
-function makeNode($name, $weight=Array()) {
-  return Array(&quot;id&quot; =&gt; $name,
-               &quot;name&quot; =&gt; $name,
-               &quot;children&quot; =&gt; array(),
-               &quot;weight&quot; =&gt; $weight,
+function makeNode($name/*, $weight=Array()*/) {
+  return Array(&quot;id&quot; =&gt; $name
+               ,&quot;name&quot; =&gt; $name
+               ,&quot;children&quot; =&gt; array()
+               ,&quot;data&quot; =&gt; array(array(&quot;key&quot; =&gt; &quot;weight&quot;, &quot;value&quot; =&gt; 0))
                );
 }
 
-function makeTree($db,$name,$level=2,$weight=Array()) {
-  $node=makeNode($name,$weight);
+function makeTree($db,$name,$level=2/*,$weight=Array()*/) {
+  $node=makeNode($name/*,$weight*/);
   if($level&gt;0) {
     $cl=getContacts($db,$name);
     foreach($cl as $c) {
-      $node[&quot;children&quot;][]=makeTree($db,$c[&quot;name&quot;],$level-1,$c[&quot;weight&quot;]);
+      $node[&quot;children&quot;][]=makeTree($db,$c[&quot;name&quot;],$level-1/*,$c[&quot;weight&quot;]*/);
     }
   }
   return $node;
@@ -63,8 +65,8 @@ function getContacts($db,$person) {
    $results=array();
    $r=$db-&gt;query($q);
    foreach ($r as $row) {
-      $results[]=Array(&quot;name&quot; =&gt; $row['contact'],
-                       &quot;weight&quot; =&gt; $row['weight']);
+      $results[]=Array(&quot;name&quot; =&gt; $row['contact']
+                       /*,&quot;weight&quot; =&gt; $row['weight']*/);
    }
    return ($results);
 }</diff>
      <filename>www/snet.php</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>lib/__init__.pyc</filename>
    </removed>
    <removed>
      <filename>lib/objects.pyc</filename>
    </removed>
    <removed>
      <filename>lib/utils.pyc</filename>
    </removed>
    <removed>
      <filename>www/timecloud/examples/delicious.php</filename>
    </removed>
    <removed>
      <filename>www/timecloud/include/tagcloud.js</filename>
    </removed>
    <removed>
      <filename>www/timecloud/style.css</filename>
    </removed>
    <removed>
      <filename>www/timecloud/timecloud.js</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>49a133a063e06983da30751fd86678dcd0837149</id>
    </parent>
  </parents>
  <author>
    <name>Stefan Marsiske</name>
    <email>stefan.marsiske@gmail.com</email>
  </author>
  <url>http://github.com/stef/maelstrom/commit/c931f919a817cc22538d3d8dbd5670d5b80e7c06</url>
  <id>c931f919a817cc22538d3d8dbd5670d5b80e7c06</id>
  <committed-date>2009-02-08T13:24:49-08:00</committed-date>
  <authored-date>2009-02-08T13:24:49-08:00</authored-date>
  <message>[enh] v0.2 lot's of improvements, most notably
* added support for cyrus style flat filedirs (use with find)
* added graph visualization of contacts via the jit library [http://thejit.org]
* proper license</message>
  <tree>26865cf8f9c5f68685ae8857fc1bb4f2d61e42d7</tree>
  <committer>
    <name>Stefan Marsiske</name>
    <email>stefan.marsiske@gmail.com</email>
  </committer>
</commit>
