refs bug 359602 - updated to JIT version 2.0.1
diff --git a/bundles/org.eclipse.rap.rwt.visualization.jit.demo/RAP Javascript InfoVis Toolkit Demo.launch b/bundles/org.eclipse.rap.rwt.visualization.jit.demo/RAP Javascript InfoVis Toolkit Demo.launch
index 628e8b5..a04ff0b 100644
--- a/bundles/org.eclipse.rap.rwt.visualization.jit.demo/RAP Javascript InfoVis Toolkit Demo.launch
+++ b/bundles/org.eclipse.rap.rwt.visualization.jit.demo/RAP Javascript InfoVis Toolkit Demo.launch
@@ -1,11 +1,13 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <launchConfiguration type="org.eclipse.rap.ui.launch.RAPLauncher">
 <booleanAttribute key="append.args" value="true"/>
+<booleanAttribute key="askclear" value="false"/>
 <booleanAttribute key="automaticAdd" value="false"/>
 <booleanAttribute key="automaticValidate" value="false"/>
 <stringAttribute key="bootstrap" value=""/>
 <stringAttribute key="checked" value="[NONE]"/>
 <booleanAttribute key="clearConfig" value="true"/>
+<booleanAttribute key="clearws" value="false"/>
 <stringAttribute key="configLocation" value="${system_property:java.io.tmpdir}/org.eclipse.rap.rwt.visualization.jit.demo"/>
 <booleanAttribute key="default" value="true"/>
 <booleanAttribute key="default_auto_start" value="true"/>
@@ -16,6 +18,8 @@
 <stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
 <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Declipse.ignoreApp=true -Dosgi.noShutdown=true&#13;&#10;-Dorg.eclipse.equinox.http.jetty.customizer.class=org.eclipse.rap.jettycustomizer.internal.SessionCookieCustomizer"/>
 <stringAttribute key="org.eclipse.rap.launch.browserMode" value="INTERNAL"/>
+<stringAttribute key="org.eclipse.rap.launch.contextpath" value=""/>
+<stringAttribute key="org.eclipse.rap.launch.dataLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.rap.ui.launch/RAPJavascriptInfoVisToolkitDemo"/>
 <stringAttribute key="org.eclipse.rap.launch.entryPoint" value="jit"/>
 <stringAttribute key="org.eclipse.rap.launch.libraryVariant" value="DEBUG"/>
 <stringAttribute key="org.eclipse.rap.launch.logLevel" value="ALL"/>
@@ -24,13 +28,15 @@
 <stringAttribute key="org.eclipse.rap.launch.servletName" value="visualization"/>
 <intAttribute key="org.eclipse.rap.launch.sessionTimeout" value="0"/>
 <booleanAttribute key="org.eclipse.rap.launch.terminatePrevious" value="true"/>
+<booleanAttribute key="org.eclipse.rap.launch.useDefaultDataLocation" value="true"/>
+<booleanAttribute key="org.eclipse.rap.launch.useManualContextPath" value="false"/>
 <booleanAttribute key="org.eclipse.rap.launch.useManualPort" value="true"/>
 <booleanAttribute key="org.eclipse.rap.launch.useSessionTimeout" value="false"/>
 <stringAttribute key="pde.version" value="3.3"/>
-<booleanAttribute key="show_selected_only" value="false"/>
-<stringAttribute key="target_bundles" value="org.eclipse.rap.rwt@default:default,org.eclipse.core.databinding.property@default:default,org.eclipse.core.contenttype@default:default,org.eclipse.rap.ui.forms@default:default,org.eclipse.equinox.http.servlet@default:default,org.eclipse.rap.ui@default:default,org.eclipse.osgi.services@default:default,org.eclipse.osgi@-1:true,org.eclipse.rap.ui.workbench@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.core.runtime@default:true,org.eclipse.core.jobs@default:default,javax.servlet*2.5.0.v201103041518@default:default,org.eclipse.rap.ui.cheatsheets@default:default,org.mortbay.jetty.server@default:default,org.eclipse.rap.ui.views@default:default,org.eclipse.rap.jface@default:default,org.eclipse.rap.jettycustomizer@default:false,org.eclipse.core.databinding@default:default,org.eclipse.help@default:default,org.eclipse.equinox.app@default:default,org.eclipse.equinox.preferences@default:default,org.eclipse.core.databinding.beans@default:default,org.eclipse.core.commands@default:default,javax.servlet*3.0.0.v201103241727@default:default,org.eclipse.equinox.http.registry@default:default,org.eclipse.equinox.http.jetty@default:default,org.eclipse.rap.rwt.osgi@default:default,org.eclipse.equinox.common@2:true,org.eclipse.rap.jface.databinding@default:default,org.eclipse.core.expressions@default:default,org.mortbay.jetty.util@default:default,org.eclipse.core.databinding.observable@default:default"/>
+<booleanAttribute key="show_selected_only" value="true"/>
+<stringAttribute key="target_bundles" value="com.ibm.icu@default:default,javax.servlet@default:default,org.eclipse.core.commands@default:default,org.eclipse.core.contenttype@default:default,org.eclipse.core.databinding.beans@default:default,org.eclipse.core.databinding.observable@default:default,org.eclipse.core.databinding.property@default:default,org.eclipse.core.databinding@default:default,org.eclipse.core.expressions@default:default,org.eclipse.core.jobs*3.5.100.v20110404@default:default,org.eclipse.core.jobs*3.5.200.v20110721-1714@default:default,org.eclipse.core.runtime*3.7.0.v20110110@default:true,org.eclipse.core.runtime*3.8.0.v20110920-2042@default:true,org.eclipse.equinox.app*1.3.100.v20110321@default:default,org.eclipse.equinox.app*1.3.100.v20111010-1614@default:default,org.eclipse.equinox.common*3.6.0.v20110523@2:true,org.eclipse.equinox.common*3.6.100.v20111010-1614@2:true,org.eclipse.equinox.http.jetty@default:default,org.eclipse.equinox.http.registry@default:default,org.eclipse.equinox.http.servlet@default:default,org.eclipse.equinox.preferences*3.4.0.v20110502@default:default,org.eclipse.equinox.preferences*3.4.100.v20111129-1548@default:default,org.eclipse.equinox.registry*3.5.100.v20110502@default:default,org.eclipse.equinox.registry*3.5.200.v20111010-1614@default:default,org.eclipse.help@default:default,org.eclipse.jetty.continuation@default:default,org.eclipse.jetty.http@default:default,org.eclipse.jetty.io@default:default,org.eclipse.jetty.security@default:default,org.eclipse.jetty.server@default:default,org.eclipse.jetty.servlet@default:default,org.eclipse.jetty.util@default:default,org.eclipse.osgi*3.7.0.v20110613@-1:true,org.eclipse.osgi*3.8.0.v20111111-1618@-1:true,org.eclipse.osgi.services*3.3.0.v20110513@default:default,org.eclipse.osgi.services*3.3.0.v20111117-1210@default:default,org.eclipse.rap.jettycustomizer@default:false,org.eclipse.rap.jface.databinding@default:default,org.eclipse.rap.jface@default:default,org.eclipse.rap.jface@default:default,org.eclipse.rap.rwt.excanvas@default:default,org.eclipse.rap.rwt.osgi@default:default,org.eclipse.rap.rwt.osgi@default:default,org.eclipse.rap.rwt@default:default,org.eclipse.rap.ui.cheatsheets@default:default,org.eclipse.rap.ui.cheatsheets@default:default,org.eclipse.rap.ui.forms@default:default,org.eclipse.rap.ui.forms@default:default,org.eclipse.rap.ui.views@default:default,org.eclipse.rap.ui.views@default:default,org.eclipse.rap.ui.workbench@default:default,org.eclipse.rap.ui.workbench@default:default,org.eclipse.rap.ui@default:default,org.eclipse.rap.ui@default:default"/>
 <booleanAttribute key="tracing" value="false"/>
 <booleanAttribute key="useCustomFeatures" value="false"/>
 <booleanAttribute key="useDefaultConfigArea" value="false"/>
-<stringAttribute key="workspace_bundles" value="org.eclipse.rap.rwt.visualization.jit.demo@default:default,org.eclipse.rap.rwt.visualization.jit@default:default,org.eclipse.rap.rwt.excanvas@default:default"/>
+<stringAttribute key="workspace_bundles" value="org.eclipse.rap.rwt.visualization.jit.demo@default:default,org.eclipse.rap.rwt.visualization.jit@default:default"/>
 </launchConfiguration>
diff --git a/bundles/org.eclipse.rap.rwt.visualization.jit.demo/samples/hypertree-weighted.json b/bundles/org.eclipse.rap.rwt.visualization.jit.demo/samples/hypertree-weighted.json
index a22e9cf..a408f71 100644
--- a/bundles/org.eclipse.rap.rwt.visualization.jit.demo/samples/hypertree-weighted.json
+++ b/bundles/org.eclipse.rap.rwt.visualization.jit.demo/samples/hypertree-weighted.json
@@ -1,43 +1,51 @@
-[{
+[
+    {
         "id": "node0",
         "name": "Large Circle",
         "data": {
             "$dim": 16.759175934208628,
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node1",
-            "data": {
-                "weight": 3,
-                "$lineWidth": 6,
-                "$color": "#990000",
+        "adjacencies": [
+            {
+                "nodeTo": "node1",
+                "data": {
+                    "weight": 3,
+                    "$lineWidth": 6,
+                    "$color": "#990000"
+                }
+            },
+            {
+                "nodeTo": "node2",
+                "data": {
+                    "weight": 3
+                }
+            },
+            {
+                "nodeTo": "node3",
+                "data": {
+                    "weight": 3,
+                    "$lineWidth": 2,
+                    "$color": "#007722"
+                }
+            },
+            {
+                "nodeTo": "node4",
+                "data": {
+                    "weight": 1
+                }
+            },
+            {
+                "nodeTo": "node5",
+                "data": {
+                    "weight": 1,
+                    "$lineWidth": 4,
+                    "$color": "#FFEE11"
+                }
             }
-        }, {
-            "nodeTo": "node2",
-            "data": {
-                "weight": 3
-            }
-        }, {
-            "nodeTo": "node3",
-            "data": {
-                "weight": 3,
-                "$lineWidth": 2,
-                "$color": "#007722"
-            }
-        }, {
-            "nodeTo": "node4",
-            "data": {
-                "weight": 1
-            }
-        }, {
-            "nodeTo": "node5",
-            "data": {
-                "weight": 1,
-                "$lineWidth": 4,
-                "$color": "#FFEE11",
-            }
-        }]
-    }, {
+        ]
+    },
+    {
         "id": "node1",
         "name": "Square",
         "data": {
@@ -46,33 +54,40 @@
             "$color": "#440000",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node0",
-            "data": {
-                "weight": 3
+        "adjacencies": [
+            {
+                "nodeTo": "node0",
+                "data": {
+                    "weight": 3
+                }
+            },
+            {
+                "nodeTo": "node2",
+                "data": {
+                    "weight": 1
+                }
+            },
+            {
+                "nodeTo": "node3",
+                "data": {
+                    "weight": 3
+                }
+            },
+            {
+                "nodeTo": "node4",
+                "data": {
+                    "weight": 1
+                }
+            },
+            {
+                "nodeTo": "node5",
+                "data": {
+                    "weight": 1
+                }
             }
-        }, {
-            "nodeTo": "node2",
-            "data": {
-                "weight": 1
-            }
-        }, {
-            "nodeTo": "node3",
-            "data": {
-                "weight": 3
-            }
-        }, {
-            "nodeTo": "node4",
-            "data": {
-                "weight": 1
-            }
-        }, {
-            "nodeTo": "node5",
-            "data": {
-                "weight": 1
-            }
-        }]
-    }, {
+        ]
+    },
+    {
         "id": "node2",
         "name": "Large Triangle",
         "data": {
@@ -80,33 +95,40 @@
             "$type": "triangle",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node0",
-            "data": {
-                "weight": 3
+        "adjacencies": [
+            {
+                "nodeTo": "node0",
+                "data": {
+                    "weight": 3
+                }
+            },
+            {
+                "nodeTo": "node1",
+                "data": {
+                    "weight": 1
+                }
+            },
+            {
+                "nodeTo": "node3",
+                "data": {
+                    "weight": 3
+                }
+            },
+            {
+                "nodeTo": "node4",
+                "data": {
+                    "weight": 3
+                }
+            },
+            {
+                "nodeTo": "node5",
+                "data": {
+                    "weight": 1
+                }
             }
-        }, {
-            "nodeTo": "node1",
-            "data": {
-                "weight": 1
-            }
-        }, {
-            "nodeTo": "node3",
-            "data": {
-                "weight": 3
-            }
-        }, {
-            "nodeTo": "node4",
-            "data": {
-                "weight": 3
-            }
-        }, {
-            "nodeTo": "node5",
-            "data": {
-                "weight": 1
-            }
-        }]
-    }, {
+        ]
+    },
+    {
         "id": "node3",
         "name": "Small Circle",
         "data": {
@@ -114,67 +136,81 @@
             "$color": "#007722",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node0",
-            "data": {
-                "weight": 3
+        "adjacencies": [
+            {
+                "nodeTo": "node0",
+                "data": {
+                    "weight": 3
+                }
+            },
+            {
+                "nodeTo": "node1",
+                "data": {
+                    "weight": 3
+                }
+            },
+            {
+                "nodeTo": "node2",
+                "data": {
+                    "weight": 3
+                }
+            },
+            {
+                "nodeTo": "node4",
+                "data": {
+                    "weight": 1
+                }
+            },
+            {
+                "nodeTo": "node5",
+                "data": {
+                    "weight": 3
+                }
             }
-        }, {
-            "nodeTo": "node1",
-            "data": {
-                "weight": 3
-            }
-        }, {
-            "nodeTo": "node2",
-            "data": {
-                "weight": 3
-            }
-        }, {
-            "nodeTo": "node4",
-            "data": {
-                "weight": 1
-            }
-        }, {
-            "nodeTo": "node5",
-            "data": {
-                "weight": 3
-            }
-        }]
-    }, {
+        ]
+    },
+    {
         "id": "node4",
         "name": "Small Triangle",
         "data": {
-            "$dim": 5.3754347037767345,
-            "$type":"triangle",
+            "$dim": 5.375434703776735,
+            "$type": "triangle",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node0",
-            "data": {
-                "weight": 1
+        "adjacencies": [
+            {
+                "nodeTo": "node0",
+                "data": {
+                    "weight": 1
+                }
+            },
+            {
+                "nodeTo": "node1",
+                "data": {
+                    "weight": 1
+                }
+            },
+            {
+                "nodeTo": "node2",
+                "data": {
+                    "weight": 3
+                }
+            },
+            {
+                "nodeTo": "node3",
+                "data": {
+                    "weight": 1
+                }
+            },
+            {
+                "nodeTo": "node5",
+                "data": {
+                    "weight": 3
+                }
             }
-        }, {
-            "nodeTo": "node1",
-            "data": {
-                "weight": 1
-            }
-        }, {
-            "nodeTo": "node2",
-            "data": {
-                "weight": 3
-            }
-        }, {
-            "nodeTo": "node3",
-            "data": {
-                "weight": 1
-            }
-        }, {
-            "nodeTo": "node5",
-            "data": {
-                "weight": 3
-            }
-        }]
-    }, {
+        ]
+    },
+    {
         "id": "node5",
         "name": "Star",
         "data": {
@@ -183,227 +219,265 @@
             "$color": "#FF6600",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node0",
-            "data": {
-                "weight": 1
+        "adjacencies": [
+            {
+                "nodeTo": "node0",
+                "data": {
+                    "weight": 1
+                }
+            },
+            {
+                "nodeTo": "node1",
+                "data": {
+                    "weight": 1
+                }
+            },
+            {
+                "nodeTo": "node2",
+                "data": {
+                    "weight": 1
+                }
+            },
+            {
+                "nodeTo": "node3",
+                "data": {
+                    "weight": 3
+                }
+            },
+            {
+                "nodeTo": "node4",
+                "data": {
+                    "weight": 3
+                }
             }
-        }, {
-            "nodeTo": "node1",
-            "data": {
-                "weight": 1
-            }
-        }, {
-            "nodeTo": "node2",
-            "data": {
-                "weight": 1
-            }
-        }, {
-            "nodeTo": "node3",
-            "data": {
-                "weight": 3
-            }
-        }, {
-            "nodeTo": "node4",
-            "data": {
-                "weight": 3
-            }
-        }]
+        ]
     },
     {
         "id": "node6",
         "name": "Small Square 1",
         "data": {
-            "$dim": 5.3754347037767345,
+            "$dim": 5.375434703776735,
             "$type": "square",
             "$color": "#FF0000",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node1",
-            "data": {
-                "weight": 3,
-                "$color": "#990000"
+        "adjacencies": [
+            {
+                "nodeTo": "node1",
+                "data": {
+                    "weight": 3,
+                    "$color": "#990000"
+                }
             }
-        }]
-    }, {
+        ]
+    },
+    {
         "id": "node7",
         "name": "Small Square 2",
         "data": {
-            "$dim": 5.3754347037767345,
+            "$dim": 5.375434703776735,
             "$type": "square",
             "$color": "#FF0000",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node1",
-            "data": {
-                "weight": 3,
-                "$color": "#990000"
+        "adjacencies": [
+            {
+                "nodeTo": "node1",
+                "data": {
+                    "weight": 3,
+                    "$color": "#990000"
+                }
             }
-        }]
-    }, {
+        ]
+    },
+    {
         "id": "node8",
         "name": "Small Square 3",
         "data": {
-            "$dim": 5.3754347037767345,
+            "$dim": 5.375434703776735,
             "$type": "square",
             "$color": "#FF0000",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node1",
-            "data": {
-                "weight": 3,
-                "$color": "#990000"
+        "adjacencies": [
+            {
+                "nodeTo": "node1",
+                "data": {
+                    "weight": 3,
+                    "$color": "#990000"
+                }
             }
-        }]
-    }, {
+        ]
+    },
+    {
         "id": "node9",
         "name": "Small Square 4",
         "data": {
-            "$dim": 5.3754347037767345,
+            "$dim": 5.375434703776735,
             "$type": "square",
             "$color": "#FF0000",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node1",
-            "data": {
-                "weight": 3,
-                "$color": "#990000"
+        "adjacencies": [
+            {
+                "nodeTo": "node1",
+                "data": {
+                    "weight": 3,
+                    "$color": "#990000"
+                }
             }
-        }]
-    }, {
+        ]
+    },
+    {
         "id": "node10",
         "name": "Small Square 5",
         "data": {
-            "$dim": 5.3754347037767345,
+            "$dim": 5.375434703776735,
             "$type": "square",
             "$color": "#FF0000",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node1",
-            "data": {
-                "weight": 3,
-                "$color": "#990000"
+        "adjacencies": [
+            {
+                "nodeTo": "node1",
+                "data": {
+                    "weight": 3,
+                    "$color": "#990000"
+                }
             }
-        }]
-    }, {
+        ]
+    },
+    {
         "id": "node11",
         "name": "Small Square 6",
         "data": {
-            "$dim": 5.3754347037767345,
+            "$dim": 5.375434703776735,
             "$type": "square",
             "$color": "#FF0000",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node1",
-            "data": {
-                "weight": 3,
-                "$color": "#990000"
+        "adjacencies": [
+            {
+                "nodeTo": "node1",
+                "data": {
+                    "weight": 3,
+                    "$color": "#990000"
+                }
             }
-        }]
+        ]
     },
     {
         "id": "node12",
         "name": "Small Star 1",
         "data": {
-            "$dim": 5.3754347037767345,
+            "$dim": 5.375434703776735,
             "$type": "star",
             "$color": "#FFFF11",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node5",
-            "data": {
-                "weight": 3,
-                "$color": "#FFEE11"
+        "adjacencies": [
+            {
+                "nodeTo": "node5",
+                "data": {
+                    "weight": 3,
+                    "$color": "#FFEE11"
+                }
             }
-        }]
-    }, {
+        ]
+    },
+    {
         "id": "node13",
         "name": "Small Star 2",
         "data": {
-            "$dim": 5.3754347037767345,
+            "$dim": 5.375434703776735,
             "$type": "star",
             "$color": "#FFFF11",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node5",
-            "data": {
-                "weight": 3,
-                "$color": "#FFEE11"
+        "adjacencies": [
+            {
+                "nodeTo": "node5",
+                "data": {
+                    "weight": 3,
+                    "$color": "#FFEE11"
+                }
             }
-        }]
-    }, {
+        ]
+    },
+    {
         "id": "node14",
         "name": "Small Star 3",
         "data": {
-            "$dim": 5.3754347037767345,
+            "$dim": 5.375434703776735,
             "$type": "star",
             "$color": "#FFFF11",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node5",
-            "data": {
-                "weight": 3,
-                "$color": "#FFEE11"
+        "adjacencies": [
+            {
+                "nodeTo": "node5",
+                "data": {
+                    "weight": 3,
+                    "$color": "#FFEE11"
+                }
             }
-        }]
+        ]
     },
     {
         "id": "node15",
         "name": "Small Star 4",
         "data": {
-            "$dim": 5.3754347037767345,
+            "$dim": 5.375434703776735,
             "$type": "star",
             "$color": "#FFFF11",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node5",
-            "data": {
-                "weight": 3,
-                "$color": "#FFEE11"
+        "adjacencies": [
+            {
+                "nodeTo": "node5",
+                "data": {
+                    "weight": 3,
+                    "$color": "#FFEE11"
+                }
             }
-        }]
+        ]
     },
     {
         "id": "node16",
         "name": "Small Circle 1",
         "data": {
-            "$dim": 5.3754347037767345,
+            "$dim": 5.375434703776735,
             "$type": "circle",
             "$color": "#007722",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node3",
-            "data": {
-                "weight": 3,
-                "$color": "#007722"
+        "adjacencies": [
+            {
+                "nodeTo": "node3",
+                "data": {
+                    "weight": 3,
+                    "$color": "#007722"
+                }
             }
-        }]
+        ]
     },
     {
         "id": "node17",
         "name": "Small Circle 2",
         "data": {
-            "$dim": 5.3754347037767345,
+            "$dim": 5.375434703776735,
             "$type": "circle",
             "$color": "#007722",
             "some other key": "some other value"
         },
-        "adjacencies": [{
-            "nodeTo": "node3",
-            "data": {
-                "weight": 3,
-                "$color": "#007722"
+        "adjacencies": [
+            {
+                "nodeTo": "node3",
+                "data": {
+                    "weight": 3,
+                    "$color": "#007722"
+                }
             }
-        }]
-    }]
\ No newline at end of file
+        ]
+    }
+]
\ No newline at end of file
diff --git a/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/JITGraphWidget.java b/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/JITGraphWidget.java
index 309eb49..c49eea3 100644
--- a/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/JITGraphWidget.java
+++ b/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/JITGraphWidget.java
@@ -80,6 +80,15 @@
   {
      addCommand("setEdgeProperty", new Object[] {propName, propValue});
   }
+  
+  /**
+   * Sets the zoom level of the graph canvas.
+   * @see <A href="http://thejit.org">JIT API documentation </A>
+   * @param percent - the percentage zoom level
+   */
+  public void setZoom(double percent) {
+     addCommand("setZoom", new Object[] {Double.valueOf(percent)});
+  }
 
   protected String convertRGBToCSSString(RGB color) {
      if (color == null) return "";
diff --git a/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/SpaceTree.java b/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/SpaceTree.java
index ac63e5a..ef3aaf3 100644
--- a/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/SpaceTree.java
+++ b/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/SpaceTree.java
@@ -23,7 +23,17 @@
     {
       super( parent, style );
       setNodeColor(new RGB(255,221,136));
-      setEdgeColor(new RGB(0,0,200));
+      setEdgeColor(new RGB(130,155,205));
     }
 
+     
+//    /**
+//     * Sets the zoom level of the graph canvas.
+//     * @see <A href="http://thejit.org">JIT API documentation </A>
+//     * @param percent - the percentage zoom level
+//     */
+//    public void zoomToFit() {
+//       addCommand("zoomToFit", null);
+//    }
+
 }
diff --git a/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/hypertreekit/HyperTree.js b/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/hypertreekit/HyperTree.js
index 49204a0..6117b04 100644
--- a/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/hypertreekit/HyperTree.js
+++ b/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/hypertreekit/HyperTree.js
@@ -53,136 +53,141 @@
 		},
 		
 		load : function() {
-	      var parent = this;
+		  var parent = this;
 		  try {
-			var vis = this.getVisible();
-			if (vis == "false") {
-				// make invisible
-				return;
-			}
-			qx.ui.core.Widget.flushGlobalQueues();
-			if (this._viz == null) {
-				this.info("Creating hypertree.");
-				
-				var qParent = document.getElementById(this._id);
-				var vizParent = document.createElement("div");
-//				var vizParent = document.createElement("canvas");
-				vizParent.setAttribute("id", "vizParent"+this._id);
-				qParent.appendChild(vizParent);
-				var canvas = new Canvas('vizCanvas'+this._id, {
-			        'injectInto': "vizParent"+this._id,
-			        'width': this.getWidth(),
-			        'height': this.getHeight()
-			    });
-				qParent._canvas = canvas;
-
-				vizParent.width = this.getWidth();
-				vizParent.height = this.getHeight();
-				var widgetId = this._id;
-				var config = {
-				        //Change node and edge styles such as
-				        //color, width and dimensions.
-				        Node: {
-				            dim: 9,
-				            overridable: true,
-				            color: "#f00"
-				        },
-				        
-				        Edge: {
-				            overridable: true,
-				            lineWidth: 2,
-				            color: "#088"
-				        },
-				        
-				        onBeforeCompute: function(node){
-				            //Log.write("centering");
-				        },
-				        //Attach event handlers and add text to the
-				        //labels. This method is only triggered on label
-				        //creation
-				        onCreateLabel: function(domElement, node){
-				            domElement.innerHTML = node.name;
-				            domElement.style.cursor = "pointer";
-				            parent.addTreeEvent(domElement, 'click', function () {
-				                ht.onClick(node.id);
-				            });
-				        },
-				        
-				        //This method is called right before plotting an
-				        //edge. This method is useful for adding individual
-				        //styles to edges.
-				        onBeforePlotLine: function(adj){
-				            //Set lineWidth for edges.
+		    var vis = this.getVisible();
+		    if (vis == "false") {
+		      // make invisible
+		      return;
+		    }
+		    qx.ui.core.Widget.flushGlobalQueues();
+		    if (this._viz == null) {
+		      this.info("Creating hypertree.");
+		      
+		      var qParent = document.getElementById(this._id);
+		      var vizParent = document.createElement("div");
+		      var vizId = "vizParent"+this._id;
+		      vizParent.setAttribute("id", vizId);
+		      qParent.appendChild(vizParent);
+		      vizParent.width = this.getWidth();
+		      vizParent.height = this.getHeight();
+		      var widgetId = this._id;
+		      var config = {
+		          injectInto: vizId,
+		          Navigation: {  
+	              enable:true,  
+	              panning:true,
+	              zooming: 20
+	            },
+		          //Change node and edge styles such as
+		          //color, width and dimensions.
+		          Node: {
+		            dim: 9,
+		            overridable: true,
+		            color: "#f00"
+		          },
+		          
+		          Edge: {
+		            overridable: true,
+		            lineWidth: 2,
+		            color: "#088"
+		          },
+		          
+		          onBeforeCompute: function(node){
+		            //Log.write("centering");
+		          },
+		          //Attach event handlers and add text to the
+		          //labels. This method is only triggered on label
+		          //creation
+		          onCreateLabel: function(domElement, node){
+		            domElement.innerHTML = node.name;
+		            domElement.style.cursor = "pointer";
+		            parent.addTreeEvent(domElement, 'click', function () {
+		              ht.onClick(node.id);
+		            });
+		          },
+		          
+		          //This method is called right before plotting an
+		          //edge. This method is useful for adding individual
+		          //styles to edges.
+		          onBeforePlotLine: function(adj){
+		            //Set lineWidth for edges.
 //				            if (!adj.data.$lineWidth) 
 //				                adj.data.$lineWidth = Math.random() * 5 + 1;
-				        },
-				        //Change node styles when labels are placed
-				        //or moved.
-				        onPlaceLabel: function(domElement, node){
-				            var style = domElement.style;
-				            style.display = '';
-				            style.cursor = 'pointer';
-				            var font = parent.getFont();
-				            style.fontFamily = font.getFamily();
-				            style.fontStyle = font.generateStyle();
-				            var color = parent.getTextColor();
-				            if (node._depth <= 1) {
-				                style.fontSize = font.getSize();
-				                style.color = color;
-
-				            } else if(node._depth == 2){
-				            	style.fontSize = font.getSize()-2;
-				                style.color = "#555";
-
-				            } else {
-				                style.display = 'none';
-				            }
-
-				            var left = parseInt(style.left);
-				            var w = domElement.offsetWidth;
-				            style.left = (left - w / 2) + 'px';
-				        },
-				        
-				        onAfterCompute: function(){
-				        	var node = Graph.Util.getClosestNodeToOrigin(ht.graph, "pos");
-				        	qParent.selection = node;
-			            	//fire selection event
-			            	var req = org.eclipse.swt.Request.getInstance();
-			            	req.addParameter(widgetId + ".selectedNode", node.id);
-			            	req.addEvent( "org.eclipse.swt.events.widgetSelected", widgetId );
-			            	req.send();
-				        }
-				    };
-			    var ht = new Hypertree(canvas, config);
-			    
-			    this.addEventListener("changeWidth", function(e) {
-					if (canvas != null) {
-						canvas.width = e.getValue();
-						vizParent.width = e.getValue();
-						canvas.resize(vizParent.width, vizParent.height);
-						ht.refresh(true);
-					}
-				});
-				this.addEventListener("changeHeight", function(e) {
-					if (canvas != null) {
-						canvas.height = e.getValue();
-						vizParent.height = e.getValue();
-						canvas.resize(vizParent.width, vizParent.height);
-						ht.refresh(true);
-					}
-				});
-			    
-				this._viz = ht;
-				this._vizParent = vizParent;
-			}
+		          },
+		          //Change node styles when labels are placed
+		          //or moved.
+		          onPlaceLabel: function(domElement, node){
+		            var style = domElement.style;
+		            style.display = '';
+		            style.cursor = 'pointer';
+		            var font = parent.getFont();
+		            style.fontFamily = font.getFamily();
+		            try {
+		              style.fontStyle = font.generateStyle();
+		            }
+		            catch (e) {
+		              //ignore..some items are not cross - browser compatible
+		            }
+		            var color = parent.getTextColor();
+		            if (node._depth <= 1) {
+		              style.fontSize = font.getSize();
+		              style.color = color;
+		              
+		            } else if(node._depth == 2){
+		              style.fontSize = font.getSize()-2;
+		              style.color = "#555";
+		              
+		            } else {
+		              style.display = 'none';
+		            }
+		            
+		            var left = parseInt(style.left);
+		            var w = domElement.offsetWidth;
+		            style.left = (left - w / 2) + 'px';
+		          },
+		          
+		          onAfterCompute: function(){
+		            var node = $jit.Graph.Util.getClosestNodeToOrigin(ht.graph, "current");
+		            qParent.selection = node;
+		            //fire selection event
+		            parent.info("Sending selected node: "+node.id);
+		            var req = org.eclipse.swt.Request.getInstance();
+		            req.addParameter(widgetId + ".selectedNode", node.id);
+		            req.addEvent( "org.eclipse.swt.events.widgetSelected", widgetId );
+		            req.send();
+		          }
+		      };
+		      var ht = new $jit.Hypertree(config);
+		      
+		      this.addEventListener("changeWidth", function(e) {
+            if (vizParent != null) {
+              vizParent.width = e.getValue();
+              if (vizParent.height != null && vizParent.width != null) {
+                ht.canvas.resize(vizParent.width, vizParent.height);
+                ht.refresh();
+              }
+            }
+          });
+          this.addEventListener("changeHeight", function(e) {
+            if (vizParent != null) {
+              vizParent.height = e.getValue();
+              if (vizParent.height != null && vizParent.width != null) {
+                ht.canvas.resize(vizParent.width, vizParent.height);
+                ht.refresh();
+              }
+            }
+          });
+		      
+		      this._viz = ht;
+		      this._vizParent = vizParent;
+		    }
 		  }
 		  catch (e) {
-			 this.info(e);
+		    this.info(e);
 		  }
 		},
 		
-		
-		
 		addTreeEvent : function (obj, type, fn) {
 		    if (obj.addEventListener) obj.addEventListener(type, fn, false);
 		    else obj.attachEvent('on' + type, fn);
@@ -244,6 +249,19 @@
         this.info(e);
       }
     },
+    
+    setZoom : function (percent) {
+      try {
+        var st = this._viz;
+        if (st != null) {
+          st.canvas.scale(percent,percent);
+          st.refresh();
+        }
+      }
+      catch (e) {
+        this.info(e);
+      }
+    },
 		
 		selectNode : function (id) {
 			try {
diff --git a/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/jit-yc.js b/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/jit-yc.js
index 764e842..abdcf7a 100644
--- a/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/jit-yc.js
+++ b/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/jit-yc.js
@@ -1 +1,23 @@
-(function(){function b(){}function c(w,u){for(var v in (u||{})){w[v]=u[v]}return w}function m(u){return(typeof u=="function")?u:function(){return u}}var k=Date.now||function(){return +new Date};function j(v){var u=h(v);return(u)?((u!="array")?[v]:v):[]}var h=function(u){return h.s.call(u).match(/^\[object\s(.*)\]$/)[1].toLowerCase()};h.s=Object.prototype.toString;function g(y,x){var w=h(y);if(w=="object"){for(var v in y){x(y[v],v)}}else{for(var u=0;u<y.length;u++){x(y[u],u)}}}function r(){var y={};for(var x=0,u=arguments.length;x<u;x++){var v=arguments[x];if(h(v)!="object"){continue}for(var w in v){var A=v[w],z=y[w];y[w]=(z&&h(A)=="object"&&h(z)=="object")?r(z,A):n(A)}}return y}function n(w){var v;switch(h(w)){case"object":v={};for(var y in w){v[y]=n(w[y])}break;case"array":v=[];for(var x=0,u=w.length;x<u;x++){v[x]=n(w[x])}break;default:return w}return v}function d(y,x){if(y.length<3){return null}if(y.length==4&&y[3]==0&&!x){return"transparent"}var v=[];for(var u=0;u<3;u++){var w=(y[u]-0).toString(16);v.push((w.length==1)?"0"+w:w)}return(x)?v:"#"+v.join("")}function i(u){f(u);if(u.parentNode){u.parentNode.removeChild(u)}if(u.clearAttributes){u.clearAttributes()}}function f(w){for(var v=w.childNodes,u=0;u<v.length;u++){i(v[u])}}function t(w,v,u){if(w.addEventListener){w.addEventListener(v,u,false)}else{w.attachEvent("on"+v,u)}}function s(v,u){return(" "+v.className+" ").indexOf(" "+u+" ")>-1}function p(v,u){if(!s(v,u)){v.className=(v.className+" "+u)}}function a(v,u){v.className=v.className.replace(new RegExp("(^|\\s)"+u+"(?:\\s|$)"),"$1")}function e(u){return document.getElementById(u)}var o=function(v){v=v||{};var u=function(){this.constructor=u;if(o.prototyping){return this}var x=(this.initialize)?this.initialize.apply(this,arguments):this;return x};for(var w in o.Mutators){if(!v[w]){continue}v=o.Mutators[w](v,v[w]);delete v[w]}c(u,this);u.constructor=o;u.prototype=v;return u};o.Mutators={Extends:function(w,u){o.prototyping=u.prototype;var v=new u;delete v.parent;v=o.inherit(v,w);delete o.prototyping;return v},Implements:function(u,v){g(j(v),function(w){o.prototying=w;c(u,(h(w)=="function")?new w:w);delete o.prototyping});return u}};c(o,{inherit:function(v,y){var u=arguments.callee.caller;for(var x in y){var w=y[x];var A=v[x];var z=h(w);if(A&&z=="function"){if(w!=A){if(u){w.__parent=A;v[x]=w}else{o.override(v,x,w)}}}else{if(z=="object"){v[x]=r(A,w)}else{v[x]=w}}}if(u){v.parent=function(){return arguments.callee.caller.__parent.apply(this,arguments)}}return v},override:function(v,u,y){var x=o.prototyping;if(x&&v[u]!=x[u]){x=null}var w=function(){var z=this.parent;this.parent=x?x[u]:v[u];var A=y.apply(this,arguments);this.parent=z;return A};v[u]=w}});o.prototype.implement=function(){var u=this.prototype;g(Array.prototype.slice.call(arguments||[]),function(v){o.inherit(u,v)});return this};this.TreeUtil={prune:function(v,u){this.each(v,function(x,w){if(w==u&&x.children){delete x.children;x.children=[]}})},getParent:function(u,y){if(u.id==y){return false}var x=u.children;if(x&&x.length>0){for(var w=0;w<x.length;w++){if(x[w].id==y){return u}else{var v=this.getParent(x[w],y);if(v){return v}}}}return false},getSubtree:function(u,y){if(u.id==y){return u}for(var w=0,x=u.children;w<x.length;w++){var v=this.getSubtree(x[w],y);if(v!=null){return v}}return null},getLeaves:function(w,u){var x=[],v=u||Number.MAX_VALUE;this.each(w,function(z,y){if(y<v&&(!z.children||z.children.length==0)){x.push({node:z,level:v-y})}});return x},eachLevel:function(u,z,w,y){if(z<=w){y(u,z);for(var v=0,x=u.children;v<x.length;v++){this.eachLevel(x[v],z+1,w,y)}}},each:function(u,v){this.eachLevel(u,0,Number.MAX_VALUE,v)},loadSubtrees:function(D,x){var C=x.request&&x.levelsToShow;var y=this.getLeaves(D,C),A=y.length,z={};if(A==0){x.onComplete()}for(var w=0,u=0;w<A;w++){var B=y[w],v=B.node.id;z[v]=B.node;x.request(v,B.level,{onComplete:function(G,E){var F=E.children;z[G].children=F;if(++u==A){x.onComplete()}}})}}};this.Canvas=(function(){var v={injectInto:"id",width:200,height:200,backgroundColor:"#333333",styles:{fillStyle:"#000000",strokeStyle:"#000000"},backgroundCanvas:false};function x(){x.t=x.t||typeof(HTMLCanvasElement);return"function"==x.t||"object"==x.t}function w(z,C,B){var A=document.createElement(z);(function(E,F){if(F){for(var D in F){E[D]=F[D]}}return arguments.callee})(A,C)(A.style,B);if(z=="canvas"&&!x()&&G_vmlCanvasManager){A=G_vmlCanvasManager.initElement(document.body.appendChild(A))}return A}function u(z){return document.getElementById(z)}function y(C,B,A,E){var D=A?(C.width-A):C.width;var z=E?(C.height-E):C.height;B.translate(D/2,z/2)}return function(B,C){var N,G,z,K,D,J;if(arguments.length<1){throw"Arguments missing"}var A=B+"-label",I=B+"-canvas",E=B+"-bkcanvas";C=r(v,C||{});var F={width:C.width,height:C.height};z=w("div",{id:B},r(F,{position:"relative"}));K=w("div",{id:A},{overflow:"visible",position:"absolute",top:0,left:0,width:F.width+"px",height:0});var L={position:"absolute",top:0,left:0,width:F.width+"px",height:F.height+"px"};D=w("canvas",r({id:I},F),L);var H=C.backgroundCanvas;if(H){J=w("canvas",r({id:E},F),L);z.appendChild(J)}z.appendChild(D);z.appendChild(K);u(C.injectInto).appendChild(z);N=D.getContext("2d");y(D,N);var M=C.styles;var O;for(O in M){N[O]=M[O]}if(H){G=J.getContext("2d");M=H.styles;for(O in M){G[O]=M[O]}y(J,G);H.impl.init(J,G);H.impl.plot(J,G)}return{id:B,getCtx:function(){return N},getElement:function(){return z},resize:function(T,P){var S=D.width,U=D.height;D.width=T;D.height=P;D.style.width=T+"px";D.style.height=P+"px";if(H){J.width=T;J.height=P;J.style.width=T+"px";J.style.height=P+"px"}if(!x()){y(D,N,S,U)}else{y(D,N)}var Q=C.styles;var R;for(R in Q){N[R]=Q[R]}if(H){Q=H.styles;for(R in Q){G[R]=Q[R]}if(!x()){y(J,G,S,U)}else{y(J,G)}H.impl.init(J,G);H.impl.plot(J,G)}},getSize:function(){return{width:D.width,height:D.height}},path:function(P,Q){N.beginPath();Q(N);N[P]();N.closePath()},clear:function(){var P=this.getSize();N.clearRect(-P.width/2,-P.height/2,P.width,P.height)},clearRectangle:function(T,R,Q,S){if(!x()){var P=N.fillStyle;N.fillStyle=C.backgroundColor;N.fillRect(S,T,Math.abs(R-S),Math.abs(Q-T));N.fillStyle=P}else{N.clearRect(S,T,Math.abs(R-S),Math.abs(Q-T))}}}}})();this.Polar=function(v,u){this.theta=v;this.rho=u};Polar.prototype={getc:function(u){return this.toComplex(u)},getp:function(){return this},set:function(u){u=u.getp();this.theta=u.theta;this.rho=u.rho},setc:function(u,v){this.rho=Math.sqrt(u*u+v*v);this.theta=Math.atan2(v,u);if(this.theta<0){this.theta+=Math.PI*2}},setp:function(v,u){this.theta=v;this.rho=u},clone:function(){return new Polar(this.theta,this.rho)},toComplex:function(w){var u=Math.cos(this.theta)*this.rho;var v=Math.sin(this.theta)*this.rho;if(w){return{x:u,y:v}}return new Complex(u,v)},add:function(u){return new Polar(this.theta+u.theta,this.rho+u.rho)},scale:function(u){return new Polar(this.theta,this.rho*u)},equals:function(u){return this.theta==u.theta&&this.rho==u.rho},$add:function(u){this.theta=this.theta+u.theta;this.rho+=u.rho;return this},$madd:function(u){this.theta=(this.theta+u.theta)%(Math.PI*2);this.rho+=u.rho;return this},$scale:function(u){this.rho*=u;return this},interpolate:function(w,C){var x=Math.PI,A=x*2;var v=function(D){return(D<0)?(D%A)+A:D%A};var z=this.theta,B=w.theta;var y;if(Math.abs(z-B)>x){if(z>B){y=v((B+((z-A)-B)*C))}else{y=v((B-A+(z-(B-A))*C))}}else{y=v((B+(z-B)*C))}var u=(this.rho-w.rho)*C+w.rho;return{theta:y,rho:u}}};var l=function(v,u){return new Polar(v,u)};Polar.KER=l(0,0);this.Complex=function(u,v){this.x=u;this.y=v};Complex.prototype={getc:function(){return this},getp:function(u){return this.toPolar(u)},set:function(u){u=u.getc(true);this.x=u.x;this.y=u.y},setc:function(u,v){this.x=u;this.y=v},setp:function(v,u){this.x=Math.cos(v)*u;this.y=Math.sin(v)*u},clone:function(){return new Complex(this.x,this.y)},toPolar:function(w){var u=this.norm();var v=Math.atan2(this.y,this.x);if(v<0){v+=Math.PI*2}if(w){return{theta:v,rho:u}}return new Polar(v,u)},norm:function(){return Math.sqrt(this.squaredNorm())},squaredNorm:function(){return this.x*this.x+this.y*this.y},add:function(u){return new Complex(this.x+u.x,this.y+u.y)},prod:function(u){return new Complex(this.x*u.x-this.y*u.y,this.y*u.x+this.x*u.y)},conjugate:function(){return new Complex(this.x,-this.y)},scale:function(u){return new Complex(this.x*u,this.y*u)},equals:function(u){return this.x==u.x&&this.y==u.y},$add:function(u){this.x+=u.x;this.y+=u.y;return this},$prod:function(w){var u=this.x,v=this.y;this.x=u*w.x-v*w.y;this.y=v*w.x+u*w.y;return this},$conjugate:function(){this.y=-this.y;return this},$scale:function(u){this.x*=u;this.y*=u;return this},$div:function(z){var u=this.x,w=this.y;var v=z.squaredNorm();this.x=u*z.x+w*z.y;this.y=w*z.x-u*z.y;return this.$scale(1/v)}};var q=function(v,u){return new Complex(v,u)};Complex.KER=q(0,0);this.Graph=new o({initialize:function(u){var v={complex:false,Node:{}};this.opt=r(v,u||{});this.nodes={}},getNode:function(u){if(this.hasNode(u)){return this.nodes[u]}return false},getAdjacence:function(w,u){var v=[];if(this.hasNode(w)&&this.hasNode(u)&&this.nodes[w].adjacentTo({id:u})&&this.nodes[u].adjacentTo({id:w})){v.push(this.nodes[w].getAdjacency(u));v.push(this.nodes[u].getAdjacency(w));return v}return false},addNode:function(u){if(!this.nodes[u.id]){this.nodes[u.id]=new Graph.Node(c({id:u.id,name:u.name,data:u.data},this.opt.Node),this.opt.complex)}return this.nodes[u.id]},addAdjacence:function(x,w,v){var y=[];if(!this.hasNode(x.id)){this.addNode(x)}if(!this.hasNode(w.id)){this.addNode(w)}x=this.nodes[x.id];w=this.nodes[w.id];for(var u in this.nodes){if(this.nodes[u].id==x.id){if(!this.nodes[u].adjacentTo(w)){y.push(this.nodes[u].addAdjacency(w,v))}}if(this.nodes[u].id==w.id){if(!this.nodes[u].adjacentTo(x)){y.push(this.nodes[u].addAdjacency(x,v))}}}return y},removeNode:function(w){if(this.hasNode(w)){var v=this.nodes[w];for(var u=0 in v.adjacencies){var adj=v.adjacencies[u];this.removeAdjacence(w,adj.nodeTo.id)}delete this.nodes[w]}},removeAdjacence:function(y,x){if(this.hasNode(y)){this.nodes[y].removeAdjacency(x)}if(this.hasNode(x)){this.nodes[x].removeAdjacency(y)}},hasNode:function(x){return x in this.nodes}});Graph.Node=new o({initialize:function(x,z){var y={id:"",name:"",data:{},adjacencies:{},selected:false,drawn:false,exist:false,angleSpan:{begin:0,end:0},alpha:1,startAlpha:1,endAlpha:1,pos:(z&&q(0,0))||l(0,0),startPos:(z&&q(0,0))||l(0,0),endPos:(z&&q(0,0))||l(0,0)};c(this,c(y,x))},adjacentTo:function(x){return x.id in this.adjacencies},getAdjacency:function(x){return this.adjacencies[x]},addAdjacency:function(y,z){var x=new Graph.Adjacence(this,y,z);return this.adjacencies[y.id]=x},removeAdjacency:function(x){delete this.adjacencies[x]}});Graph.Adjacence=function(x,z,y){this.nodeFrom=x;this.nodeTo=z;this.data=y||{};this.alpha=1;this.startAlpha=1;this.endAlpha=1};Graph.Util={filter:function(y){if(!y||!(h(y)=="string")){return function(){return true}}var x=y.split(" ");return function(A){for(var z=0;z<x.length;z++){if(A[x[z]]){return false}}return true}},getNode:function(x,y){return x.getNode(y)},eachNode:function(B,A,x){var z=this.filter(x);for(var y in B.nodes){if(z(B.nodes[y])){A(B.nodes[y])}}},eachAdjacency:function(A,B,x){var y=A.adjacencies,z=this.filter(x);for(var C in y){if(z(y[C])){B(y[C],C)}}},computeLevels:function(D,E,A,z){A=A||0;var B=this.filter(z);this.eachNode(D,function(F){F._flag=false;F._depth=-1},z);var y=D.getNode(E);y._depth=A;var x=[y];while(x.length!=0){var C=x.pop();C._flag=true;this.eachAdjacency(C,function(F){var G=F.nodeTo;if(G._flag==false&&B(G)){if(G._depth<0){G._depth=C._depth+1+A}x.unshift(G)}},z)}},eachBFS:function(C,D,B,y){var z=this.filter(y);this.clean(C);var x=[C.getNode(D)];while(x.length!=0){var A=x.pop();A._flag=true;B(A,A._depth);this.eachAdjacency(A,function(E){var F=E.nodeTo;if(F._flag==false&&z(F)){F._flag=true;x.unshift(F)}},y)}},eachLevel:function(B,F,y,C,A){var E=B._depth,x=this.filter(A),D=this;y=y===false?Number.MAX_VALUE-E:y;(function z(I,G,H){var J=I._depth;if(J>=G&&J<=H&&x(I)){C(I,J)}if(J<H){D.eachAdjacency(I,function(K){var L=K.nodeTo;if(L._depth>J){z(L,G,H)}})}})(B,F+E,y+E)},eachSubgraph:function(y,z,x){this.eachLevel(y,0,false,z,x)},eachSubnode:function(y,z,x){this.eachLevel(y,1,1,z,x)},anySubnode:function(A,z,y){var x=false;z=z||m(true);var B=h(z)=="string"?function(C){return C[z]}:z;this.eachSubnode(A,function(C){if(B(C)){x=true}},y);return x},getSubnodes:function(C,D,x){var z=[],B=this;D=D||0;var A,y;if(h(D)=="array"){A=D[0];y=D[1]}else{A=D;y=Number.MAX_VALUE-C._depth}this.eachLevel(C,A,y,function(E){z.push(E)},x);return z},getParents:function(y){var x=[];this.eachAdjacency(y,function(z){var A=z.nodeTo;if(A._depth<y._depth){x.push(A)}});return x},isDescendantOf:function(A,B){if(A.id==B){return true}var z=this.getParents(A),x=false;for(var y=0;!x&&y<z.length;y++){x=x||this.isDescendantOf(z[y],B)}return x},clean:function(x){this.eachNode(x,function(y){y._flag=false})}};Graph.Op={options:{type:"nothing",duration:2000,hideLabels:true,fps:30},removeNode:function(C,A){var x=this.viz;var y=r(this.options,x.controller,A);var E=j(C);var z,B,D;switch(y.type){case"nothing":for(z=0;z<E.length;z++){x.graph.removeNode(E[z])}break;case"replot":this.removeNode(E,{type:"nothing"});x.fx.clearLabels();x.refresh(true);break;case"fade:seq":case"fade":B=this;for(z=0;z<E.length;z++){D=x.graph.getNode(E[z]);D.endAlpha=0}x.fx.animate(r(y,{modes:["fade:nodes"],onComplete:function(){B.removeNode(E,{type:"nothing"});x.fx.clearLabels();x.reposition();x.fx.animate(r(y,{modes:["linear"]}))}}));break;case"fade:con":B=this;for(z=0;z<E.length;z++){D=x.graph.getNode(E[z]);D.endAlpha=0;D.ignore=true}x.reposition();x.fx.animate(r(y,{modes:["fade:nodes","linear"],onComplete:function(){B.removeNode(E,{type:"nothing"})}}));break;case"iter":B=this;x.fx.sequence({condition:function(){return E.length!=0},step:function(){B.removeNode(E.shift(),{type:"nothing"});x.fx.clearLabels()},onComplete:function(){y.onComplete()},duration:Math.ceil(y.duration/E.length)});break;default:this.doError()}},removeEdge:function(D,B){var x=this.viz;var z=r(this.options,x.controller,B);var y=(h(D[0])=="string")?[D]:D;var A,C,E;switch(z.type){case"nothing":for(A=0;A<y.length;A++){x.graph.removeAdjacence(y[A][0],y[A][1])}break;case"replot":this.removeEdge(y,{type:"nothing"});x.refresh(true);break;case"fade:seq":case"fade":C=this;for(A=0;A<y.length;A++){E=x.graph.getAdjacence(y[A][0],y[A][1]);if(E){E[0].endAlpha=0;E[1].endAlpha=0}}x.fx.animate(r(z,{modes:["fade:vertex"],onComplete:function(){C.removeEdge(y,{type:"nothing"});x.reposition();x.fx.animate(r(z,{modes:["linear"]}))}}));break;case"fade:con":C=this;for(A=0;A<y.length;A++){E=x.graph.getAdjacence(y[A][0],y[A][1]);if(E){E[0].endAlpha=0;E[0].ignore=true;E[1].endAlpha=0;E[1].ignore=true}}x.reposition();x.fx.animate(r(z,{modes:["fade:vertex","linear"],onComplete:function(){C.removeEdge(y,{type:"nothing"})}}));break;case"iter":C=this;x.fx.sequence({condition:function(){return y.length!=0},step:function(){C.removeEdge(y.shift(),{type:"nothing"});x.fx.clearLabels()},onComplete:function(){z.onComplete()},duration:Math.ceil(z.duration/y.length)});break;default:this.doError()}},sum:function(E,y){var C=this.viz;var F=r(this.options,C.controller,y),B=C.root;var A,D;C.root=y.id||C.root;switch(F.type){case"nothing":D=C.construct(E);A=Graph.Util;A.eachNode(D,function(G){A.eachAdjacency(G,function(H){C.graph.addAdjacence(H.nodeFrom,H.nodeTo,H.data)})});break;case"replot":C.refresh(true);this.sum(E,{type:"nothing"});C.refresh(true);break;case"fade:seq":case"fade":case"fade:con":A=Graph.Util;that=this;D=C.construct(E);var x=this.preprocessSum(D);var z=!x?["fade:nodes"]:["fade:nodes","fade:vertex"];C.reposition();if(F.type!="fade:con"){C.fx.animate(r(F,{modes:["linear"],onComplete:function(){C.fx.animate(r(F,{modes:z,onComplete:function(){F.onComplete()}}))}}))}else{A.eachNode(C.graph,function(G){if(G.id!=B&&G.pos.getp().equals(Polar.KER)){G.pos.set(G.endPos);G.startPos.set(G.endPos)}});C.fx.animate(r(F,{modes:["linear"].concat(z)}))}break;default:this.doError()}},morph:function(E,y){var C=this.viz;var F=r(this.options,C.controller,y),B=C.root;var A,D;C.root=y.id||C.root;switch(F.type){case"nothing":D=C.construct(E);A=Graph.Util;A.eachNode(D,function(G){A.eachAdjacency(G,function(H){C.graph.addAdjacence(H.nodeFrom,H.nodeTo,H.data)})});A.eachNode(C.graph,function(G){A.eachAdjacency(G,function(H){if(!D.getAdjacence(H.nodeFrom.id,H.nodeTo.id)){C.graph.removeAdjacence(H.nodeFrom.id,H.nodeTo.id)}});if(!D.hasNode(G.id)){C.graph.removeNode(G.id)}});break;case"replot":C.fx.clearLabels(true);this.morph(E,{type:"nothing"});C.refresh(true);C.refresh(true);break;case"fade:seq":case"fade":case"fade:con":A=Graph.Util;that=this;D=C.construct(E);var x=this.preprocessSum(D);A.eachNode(C.graph,function(G){if(!D.hasNode(G.id)){G.alpha=1;G.startAlpha=1;G.endAlpha=0;G.ignore=true}});A.eachNode(C.graph,function(G){if(G.ignore){return}A.eachAdjacency(G,function(H){if(H.nodeFrom.ignore||H.nodeTo.ignore){return}var I=D.getNode(H.nodeFrom.id);var J=D.getNode(H.nodeTo.id);if(!I.adjacentTo(J)){var K=C.graph.getAdjacence(I.id,J.id);x=true;K[0].alpha=1;K[0].startAlpha=1;K[0].endAlpha=0;K[0].ignore=true;K[1].alpha=1;K[1].startAlpha=1;K[1].endAlpha=0;K[1].ignore=true}})});var z=!x?["fade:nodes"]:["fade:nodes","fade:vertex"];C.reposition();A.eachNode(C.graph,function(G){if(G.id!=B&&G.pos.getp().equals(Polar.KER)){G.pos.set(G.endPos);G.startPos.set(G.endPos)}});C.fx.animate(r(F,{modes:["polar"].concat(z),onComplete:function(){A.eachNode(C.graph,function(G){if(G.ignore){C.graph.removeNode(G.id)}});A.eachNode(C.graph,function(G){A.eachAdjacency(G,function(H){if(H.ignore){C.graph.removeAdjacence(H.nodeFrom.id,H.nodeTo.id)}})});F.onComplete()}}));break;default:this.doError()}},preprocessSum:function(z){var x=this.viz;var y=Graph.Util;y.eachNode(z,function(B){if(!x.graph.hasNode(B.id)){x.graph.addNode(B);var C=x.graph.getNode(B.id);C.alpha=0;C.startAlpha=0;C.endAlpha=1}});var A=false;y.eachNode(z,function(B){y.eachAdjacency(B,function(C){var D=x.graph.getNode(C.nodeFrom.id);var E=x.graph.getNode(C.nodeTo.id);if(!D.adjacentTo(E)){var F=x.graph.addAdjacence(D,E,C.data);if(D.startAlpha==D.endAlpha&&E.startAlpha==E.endAlpha){A=true;F[0].alpha=0;F[0].startAlpha=0;F[0].endAlpha=1;F[1].alpha=0;F[1].startAlpha=0;F[1].endAlpha=1}}})});return A}};Graph.Plot={Interpolator:{moebius:function(C,E,A){if(E<=1||A.norm()<=1){var z=A.x,D=A.y;var B=C.startPos.getc().moebiusTransformation(A);C.pos.setc(B.x,B.y);A.x=z;A.y=D}},linear:function(x,A){var z=x.startPos.getc(true);var y=x.endPos.getc(true);x.pos.setc((y.x-z.x)*A+z.x,(y.y-z.y)*A+z.y)},"fade:nodes":function(x,A){if(A<=1&&(x.endAlpha!=x.alpha)){var z=x.startAlpha;var y=x.endAlpha;x.alpha=z+(y-z)*A}},"fade:vertex":function(x,A){var z=x.adjacencies;for(var y in z){this["fade:nodes"](z[y],A)}},polar:function(y,B){var A=y.startPos.getp(true);var z=y.endPos.getp();var x=z.interpolate(A,B);y.pos.setp(x.theta,x.rho)}},labelsHidden:false,labelContainer:false,labels:{},getLabelContainer:function(){return this.labelContainer?this.labelContainer:this.labelContainer=document.getElementById(this.viz.config.labelContainer)},getLabel:function(x){return(x in this.labels&&this.labels[x]!=null)?this.labels[x]:this.labels[x]=document.getElementById(x)},hideLabels:function(y){var x=this.getLabelContainer();if(y){x.style.display="none"}else{x.style.display=""}this.labelsHidden=y},clearLabels:function(x){for(var y in this.labels){if(x||!this.viz.graph.hasNode(y)){this.disposeLabel(y);delete this.labels[y]}}},disposeLabel:function(y){var x=this.getLabel(y);if(x&&x.parentNode){x.parentNode.removeChild(x)}},hideLabel:function(B,x){B=j(B);var y=x?"":"none",z,A=this;g(B,function(D){var C=A.getLabel(D.id);if(C){C.style.display=y}})},sequence:function(y){var z=this;y=r({condition:m(false),step:b,onComplete:b,duration:200},y||{});var x=setInterval(function(){if(y.condition()){y.step()}else{clearInterval(x);y.onComplete()}z.viz.refresh(true)},y.duration)},animate:function(z,y){var B=this,x=this.viz,C=x.graph,A=Graph.Util;z=r(x.controller,z||{});if(z.hideLabels){this.hideLabels(true)}this.animation.setOptions(r(z,{$animating:false,compute:function(E){var D=y?y.scale(-E):null;A.eachNode(C,function(G){for(var F=0;F<z.modes.length;F++){B.Interpolator[z.modes[F]](G,E,D)}});B.plot(z,this.$animating);this.$animating=true},complete:function(){A.eachNode(C,function(D){D.startPos.set(D.pos);D.startAlpha=D.alpha});if(z.hideLabels){B.hideLabels(false)}B.plot(z);z.onComplete();z.onAfterCompute()}})).start()},plot:function(y,G){var E=this.viz,B=E.graph,z=E.canvas,x=E.root,C=this,F=z.getCtx(),D=Graph.Util;y=y||this.viz.controller;y.clearCanvas&&z.clear();var A=!!B.getNode(x).visited;D.eachNode(B,function(H){D.eachAdjacency(H,function(I){var J=I.nodeTo;if(!!J.visited===A&&H.drawn&&J.drawn){!G&&y.onBeforePlotLine(I);F.save();F.globalAlpha=Math.min(Math.min(H.alpha,J.alpha),I.alpha);C.plotLine(I,z,G);F.restore();!G&&y.onAfterPlotLine(I)}});F.save();if(H.drawn){F.globalAlpha=H.alpha;!G&&y.onBeforePlotNode(H);C.plotNode(H,z,G);!G&&y.onAfterPlotNode(H)}if(!C.labelsHidden&&y.withLabels){if(H.drawn&&F.globalAlpha>=0.95){C.plotLabel(z,H,y)}else{C.hideLabel(H,false)}}F.restore();H.visited=!A})},plotLabel:function(A,B,z){var C=B.id,x=this.getLabel(C);if(!x&&!(x=document.getElementById(C))){x=document.createElement("div");var y=this.getLabelContainer();y.appendChild(x);x.id=C;x.className="node";x.style.position="absolute";z.onCreateLabel(x,B);this.labels[B.id]=x}this.placeLabel(x,B,z)},plotNode:function(z,y,G){var E=this.node,B=z.data;var D=E.overridable&&B;var x=D&&B.$lineWidth||E.lineWidth;var A=D&&B.$color||E.color;var F=y.getCtx();F.lineWidth=x;F.fillStyle=A;F.strokeStyle=A;var C=z.data&&z.data.$type||E.type;this.nodeTypes[C].call(this,z,y,G)},plotLine:function(E,z,G){var x=this.edge,B=E.data;var D=x.overridable&&B;var y=D&&B.$lineWidth||x.lineWidth;var A=D&&B.$color||x.color;var F=z.getCtx();F.lineWidth=y;F.fillStyle=A;F.strokeStyle=A;var C=E.data&&E.data.$type||x.type;this.edgeTypes[C].call(this,E,z,G)},fitsInCanvas:function(z,x){var y=x.getSize();if(z.x>=y.width||z.x<0||z.y>=y.height||z.y<0){return false}return true}};var Loader={construct:function(y){var z=(h(y)=="array");var x=new Graph(this.graphOptions);if(!z){(function(A,C){A.addNode(C);for(var B=0,D=C.children;B<D.length;B++){A.addAdjacence(C,D[B]);arguments.callee(A,D[B])}})(x,y)}else{(function(B,E){var H=function(J){for(var I=0;I<E.length;I++){if(E[I].id==J){return E[I]}}return undefined};for(var D=0;D<E.length;D++){B.addNode(E[D]);for(var C=0,A=E[D].adjacencies;C<A.length;C++){var F=A[C],G;if(typeof A[C]!="string"){G=F.data;F=F.nodeTo}B.addAdjacence(E[D],H(F),G)}}})(x,y)}return x},loadJSON:function(y,x){this.json=y;this.graph=this.construct(y);if(h(y)!="array"){this.root=y.id}else{this.root=y[x?x:0].id}}};this.Trans={linear:function(x){return x}};(function(){var x=function(A,z){z=j(z);return c(A,{easeIn:function(B){return A(B,z)},easeOut:function(B){return 1-A(1-B,z)},easeInOut:function(B){return(B<=0.5)?A(2*B,z)/2:(2-A(2*(1-B),z))/2}})};var y={Pow:function(A,z){return Math.pow(A,z[0]||6)},Expo:function(z){return Math.pow(2,8*(z-1))},Circ:function(z){return 1-Math.sin(Math.acos(z))},Sine:function(z){return 1-Math.sin((1-z)*Math.PI/2)},Back:function(A,z){z=z[0]||1.618;return Math.pow(A,2)*((z+1)*A-z)},Bounce:function(C){var B;for(var A=0,z=1;1;A+=z,z/=2){if(C>=(7-4*A)/11){B=z*z-Math.pow((11-6*A-11*C)/4,2);break}}return B},Elastic:function(A,z){return Math.pow(2,10*--A)*Math.cos(20*A*Math.PI*(z[0]||1)/3)}};g(y,function(A,z){Trans[z]=x(A)});g(["Quad","Cubic","Quart","Quint"],function(A,z){Trans[A]=x(function(B){return Math.pow(B,[z+2])})})})();var Animation=new o({initalize:function(x){this.setOptions(x)},setOptions:function(x){var y={duration:2500,fps:40,transition:Trans.Quart.easeInOut,compute:b,complete:b};this.opt=r(y,x||{});return this},getTime:function(){return k()},step:function(){var y=this.getTime(),x=this.opt;if(y<this.time+x.duration){var z=x.transition((y-this.time)/x.duration);x.compute(z)}else{this.timer=clearInterval(this.timer);x.compute(1);x.complete()}},start:function(){this.time=0;this.startTimer();return this},startTimer:function(){var y=this,x=this.opt;if(this.timer){return false}this.time=this.getTime()-this.time;this.timer=setInterval((function(){y.step()}),Math.round(1000/x.fps));return true}});(function(){var G=Array.prototype.slice;function E(Q,K,I,O){var M=K.Node,N=Graph.Util;var J=K.multitree;if(M.overridable){var P=-1,L=-1;N.eachNode(Q,function(T){if(T._depth==I&&(!J||("$orn" in T.data)&&T.data.$orn==O)){var R=T.data.$width||M.width;var S=T.data.$height||M.height;P=(P<R)?R:P;L=(L<S)?S:L}});return{width:P<0?M.width:P,height:L<0?M.height:L}}else{return M}}function H(J,M,L,I){var K=(I=="left"||I=="right")?"y":"x";J[M][K]+=L}function C(J,K){var I=[];g(J,function(L){L=G.call(L);L[0]+=K;L[1]+=K;I.push(L)});return I}function F(L,I){if(L.length==0){return I}if(I.length==0){return L}var K=L.shift(),J=I.shift();return[[K[0],J[1]]].concat(F(L,I))}function A(I,J){J=J||[];if(I.length==0){return J}var K=I.pop();return A(I,F(K,J))}function D(L,J,M,I,K){if(L.length<=K||J.length<=K){return 0}var O=L[K][1],N=J[K][0];return Math.max(D(L,J,M,I,++K)+M,O-N+I)}function B(L,J,I){function K(O,Q,N){if(Q.length<=N){return[]}var P=Q[N],M=D(O,P,J,I,0);return[M].concat(K(F(O,C(P,M)),Q,++N))}return K([],L,0)}function y(M,L,K){function I(P,R,O){if(R.length<=O){return[]}var Q=R[O],N=-D(Q,P,L,K,0);return[N].concat(I(F(C(Q,N),P),R,++O))}M=G.call(M);var J=I([],M.reverse(),0);return J.reverse()}function x(O,M,J,P){var K=B(O,M,J),N=y(O,M,J);if(P=="left"){N=K}else{if(P=="right"){K=N}}for(var L=0,I=[];L<K.length;L++){I[L]=(K[L]+N[L])/2}return I}function z(J,T,K,aa,Y){var M=aa.multitree;var S=["x","y"],P=["width","height"];var L=+(Y=="left"||Y=="right");var Q=S[L],Z=S[1-L];var V=aa.Node;var O=P[L],X=P[1-L];var N=aa.siblingOffset;var W=aa.subtreeOffset;var U=aa.align;var I=Graph.Util;function R(ad,ah,al){var ac=(V.overridable&&ad.data["$"+O])||V[O];var ak=ah||((V.overridable&&ad.data["$"+X])||V[X]);var ao=[],am=[],ai=false;var ab=ak+aa.levelDistance;I.eachSubnode(ad,function(aq){if(aq.exist&&(!M||("$orn" in aq.data)&&aq.data.$orn==Y)){if(!ai){ai=E(J,aa,aq._depth,Y)}var ap=R(aq,ai[X],al+ab);ao.push(ap.tree);am.push(ap.extent)}});var ag=x(am,W,N,U);for(var af=0,ae=[],aj=[];af<ao.length;af++){H(ao[af],K,ag[af],Y);aj.push(C(am[af],ag[af]))}var an=[[-ac/2,ac/2]].concat(A(aj));ad[K][Q]=0;if(Y=="top"||Y=="left"){ad[K][Z]=al}else{ad[K][Z]=-al}return{tree:ad,extent:an}}R(T,false,0)}this.ST=(function(){var J=[];function K(P){P=P||this.clickedNode;var M=this.geom,T=Graph.Util;var U=this.graph;var N=this.canvas;var L=P._depth,Q=[];T.eachNode(U,function(V){if(V.exist&&!V.selected){if(T.isDescendantOf(V,P.id)){if(V._depth<=L){Q.push(V)}}else{Q.push(V)}}});var R=M.getRightLevelToShow(P,N);T.eachLevel(P,R,R,function(V){if(V.exist&&!V.selected){Q.push(V)}});for(var S=0;S<J.length;S++){var O=this.graph.getNode(J[S]);if(!T.isDescendantOf(O,P.id)){Q.push(O)}}return Q}function I(O){var N=[],M=Graph.Util,L=this.config;O=O||this.clickedNode;M.eachLevel(this.clickedNode,0,L.levelsToShow,function(P){if(L.multitree&&!("$orn" in P.data)&&M.anySubnode(P,function(Q){return Q.exist&&!Q.drawn})){N.push(P)}else{if(P.drawn&&!M.anySubnode(P,"drawn")){N.push(P)}}});return N}return new o({Implements:Loader,initialize:function(O,L){var M={onBeforeCompute:b,onAfterCompute:b,onCreateLabel:b,onPlaceLabel:b,onComplete:b,onBeforePlotNode:b,onAfterPlotNode:b,onBeforePlotLine:b,onAfterPlotLine:b,request:false};var N={orientation:"left",labelContainer:O.id+"-label",levelsToShow:2,subtreeOffset:8,siblingOffset:5,levelDistance:30,withLabels:true,clearCanvas:true,align:"center",indent:10,multitree:false,constrained:true,Node:{overridable:false,type:"rectangle",color:"#ccb",lineWidth:1,height:20,width:90,dim:15,align:"center"},Edge:{overridable:false,type:"line",color:"#ccc",dim:15,lineWidth:1},duration:700,fps:25,transition:Trans.Quart.easeInOut};this.controller=this.config=r(N,M,L);this.canvas=O;this.graphOptions={complex:true};this.graph=new Graph(this.graphOptions);this.fx=new ST.Plot(this);this.op=new ST.Op(this);this.group=new ST.Group(this);this.geom=new ST.Geom(this);this.clickedNode=null},plot:function(){this.fx.plot(this.controller)},switchPosition:function(Q,P,O){var L=this.geom,M=this.fx,N=this;if(!M.busy){M.busy=true;this.contract({onComplete:function(){L.switchOrientation(Q);N.compute("endPos",false);M.busy=false;if(P=="animate"){N.onClick(N.clickedNode.id,O)}else{if(P=="replot"){N.select(N.clickedNode.id,O)}}}},Q)}},switchAlignment:function(N,M,L){this.config.align=N;if(M=="animate"){this.select(this.clickedNode.id,L)}else{if(M=="replot"){this.onClick(this.clickedNode.id,L)}}},addNodeInPath:function(L){J.push(L);this.select((this.clickedNode&&this.clickedNode.id)||this.root)},clearNodesInPath:function(L){J.length=0;this.select((this.clickedNode&&this.clickedNode.id)||this.root)},refresh:function(){this.reposition();this.select((this.clickedNode&&this.clickedNode.id)||this.root)},reposition:function(){Graph.Util.computeLevels(this.graph,this.root,0,"ignore");this.geom.setRightLevelToShow(this.clickedNode,this.canvas);Graph.Util.eachNode(this.graph,function(L){if(L.exist){L.drawn=true}});this.compute("endPos")},compute:function(N,M){var O=N||"startPos";var L=this.graph.getNode(this.root);c(L,{drawn:true,exist:true,selected:true});if(!!M||!("_depth" in L)){Graph.Util.computeLevels(this.graph,this.root,0,"ignore")}this.computePositions(L,O)},computePositions:function(P,L){var N=this.config;var M=N.multitree;var S=N.align;var O=S!=="center"&&N.indent;var T=N.orientation;var R=M?["top","right","bottom","left"]:[T];var Q=this;g(R,function(U){z(Q.graph,P,L,Q.config,U);var V=["x","y"][+(U=="left"||U=="right")];(function W(X){Graph.Util.eachSubnode(X,function(Y){if(Y.exist&&(!M||("$orn" in Y.data)&&Y.data.$orn==U)){Y[L][V]+=X[L][V];if(O){Y[L][V]+=S=="left"?O:-O}W(Y)}})})(P)})},requestNodes:function(O,P){var M=r(this.controller,P),L=this.config.levelsToShow,N=Graph.Util;if(M.request){var R=[],Q=O._depth;N.eachLevel(O,0,L,function(S){if(S.drawn&&!N.anySubnode(S)){R.push(S);S._level=L-(S._depth-Q)}});this.group.requestNodes(R,M)}else{M.onComplete()}},contract:function(P,Q){var O=this.config.orientation;var L=this.geom,N=this.group;if(Q){L.switchOrientation(Q)}var M=K.call(this);if(Q){L.switchOrientation(O)}N.contract(M,r(this.controller,P))},move:function(M,N){this.compute("endPos",false);var L=N.Move,O={x:L.offsetX,y:L.offsetY};if(L.enable){this.geom.translate(M.endPos.add(O).$scale(-1),"endPos")}this.fx.animate(r(this.controller,{modes:["linear"]},N))},expand:function(M,N){var L=I.call(this,M);this.group.expand(L,r(this.controller,N))},selectPath:function(P){var O=Graph.Util,N=this;O.eachNode(this.graph,function(R){R.selected=false});function Q(S){if(S==null||S.selected){return}S.selected=true;g(N.group.getSiblings([S])[S.id],function(T){T.exist=true;T.drawn=true});var R=O.getParents(S);R=(R.length>0)?R[0]:null;Q(R)}for(var L=0,M=[P.id].concat(J);L<M.length;L++){Q(this.graph.getNode(M[L]))}},setRoot:function(S,R,Q){var P=this,N=this.canvas;var L=this.graph.getNode(this.root);var M=this.graph.getNode(S);function O(){if(this.config.multitree&&M.data.$orn){var U=M.data.$orn;var V={left:"right",right:"left",top:"bottom",bottom:"top"}[U];L.data.$orn=V;(function T(W){Graph.Util.eachSubnode(W,function(X){if(X.id!=S){X.data.$orn=V;T(X)}})})(L);delete M.data.$orn}this.root=S;this.clickedNode=M;Graph.Util.computeLevels(this.graph,this.root,0,"ignore")}delete L.data.$orns;if(R=="animate"){this.onClick(S,{onBeforeMove:function(){O.call(P);P.selectPath(M)}})}else{if(R=="replot"){O.call(this);this.select(this.root)}}},addSubtree:function(L,N,M){if(N=="replot"){this.op.sum(L,c({type:"replot"},M||{}))}else{if(N=="animate"){this.op.sum(L,c({type:"fade:seq"},M||{}))}}},removeSubtree:function(Q,M,P,O){var N=this.graph.getNode(Q),L=[];Graph.Util.eachLevel(N,+!M,false,function(R){L.push(R.id)});if(P=="replot"){this.op.removeNode(L,c({type:"replot"},O||{}))}else{if(P=="animate"){this.op.removeNode(L,c({type:"fade:seq"},O||{}))}}},select:function(L,O){var T=this.group,R=this.geom;var P=this.graph.getNode(L),N=this.canvas;var S=this.graph.getNode(this.root);var M=r(this.controller,O);var Q=this;M.onBeforeCompute(P);this.selectPath(P);this.clickedNode=P;this.requestNodes(P,{onComplete:function(){T.hide(T.prepare(K.call(Q)),M);R.setRightLevelToShow(P,N);Q.compute("pos");Graph.Util.eachNode(Q.graph,function(V){var U=V.pos.getc(true);V.startPos.setc(U.x,U.y);V.endPos.setc(U.x,U.y);V.visited=false});Q.geom.translate(P.endPos.scale(-1),["pos","startPos","endPos"]);T.show(I.call(Q));Q.plot();M.onAfterCompute(Q.clickedNode);M.onComplete()}})},onClick:function(N,U){var O=this.canvas,S=this,R=this.fx,T=Graph.Util,L=this.geom;var Q={Move:{enable:true,offsetX:0,offsetY:0},onBeforeRequest:b,onBeforeContract:b,onBeforeMove:b,onBeforeExpand:b};var M=r(this.controller,Q,U);if(!this.busy){this.busy=true;var P=this.graph.getNode(N);this.selectPath(P,this.clickedNode);this.clickedNode=P;M.onBeforeCompute(P);M.onBeforeRequest(P);this.requestNodes(P,{onComplete:function(){M.onBeforeContract(P);S.contract({onComplete:function(){L.setRightLevelToShow(P,O);M.onBeforeMove(P);S.move(P,{Move:M.Move,onComplete:function(){M.onBeforeExpand(P);S.expand(P,{onComplete:function(){S.busy=false;M.onAfterCompute(N);M.onComplete()}})}})}})}})}}})})();ST.Op=new o({Implements:Graph.Op,initialize:function(I){this.viz=I}});ST.Group=new o({initialize:function(I){this.viz=I;this.canvas=I.canvas;this.config=I.config;this.animation=new Animation;this.nodes=null},requestNodes:function(N,M){var L=0,J=N.length,P={};var K=function(){M.onComplete()};var I=this.viz;if(J==0){K()}for(var O=0;O<J;O++){P[N[O].id]=N[O];M.request(N[O].id,N[O]._level,{onComplete:function(R,Q){if(Q&&Q.children){Q.id=R;I.op.sum(Q,{type:"nothing"})}if(++L==J){Graph.Util.computeLevels(I.graph,I.root,0);K()}}})}},contract:function(K,J){var M=Graph.Util;var I=this.viz;var L=this;K=this.prepare(K);this.animation.setOptions(r(J,{$animating:false,compute:function(N){if(N==1){N=0.99}L.plotStep(1-N,J,this.$animating);this.$animating="contract"},complete:function(){L.hide(K,J)}})).start()},hide:function(K,J){var N=Graph.Util,I=this.viz;for(var L=0;L<K.length;L++){if(true||!J||!J.request){N.eachLevel(K[L],1,false,function(O){if(O.exist){c(O,{drawn:false,exist:false})}})}else{var M=[];N.eachLevel(K[L],1,false,function(O){M.push(O.id)});I.op.removeNode(M,{type:"nothing"});I.fx.clearLabels()}}J.onComplete()},expand:function(J,I){var L=this,K=Graph.Util;this.show(J);this.animation.setOptions(r(I,{$animating:false,compute:function(M){L.plotStep(M,I,this.$animating);this.$animating="expand"},complete:function(){L.plotStep(undefined,I,false);I.onComplete()}})).start()},show:function(I){var K=Graph.Util,J=this.config;this.prepare(I);g(I,function(M){if(J.multitree&&!("$orn" in M.data)){delete M.data.$orns;var L=" ";K.eachSubnode(M,function(N){if(("$orn" in N.data)&&L.indexOf(N.data.$orn)<0&&N.exist&&!N.drawn){L+=N.data.$orn+" "}});M.data.$orns=L}K.eachLevel(M,0,J.levelsToShow,function(N){if(N.exist){N.drawn=true}})})},prepare:function(I){this.nodes=this.getNodesWithChildren(I);return this.nodes},getNodesWithChildren:function(K){var J=[],O=Graph.Util,M=this.config,I=this.viz.root;K.sort(function(R,Q){return(R._depth<=Q._depth)-(R._depth>=Q._depth)});for(var N=0;N<K.length;N++){if(O.anySubnode(K[N],"exist")){for(var L=N+1,P=false;!P&&L<K.length;L++){if(!M.multitree||"$orn" in K[L].data){P=P||O.isDescendantOf(K[N],K[L].id)}}if(!P){J.push(K[N])}}}return J},plotStep:function(T,O,V){var S=this.viz,L=this.config,K=S.canvas,U=K.getCtx(),I=this.nodes,Q=Graph.Util;var N,M;var J={};for(N=0;N<I.length;N++){M=I[N];J[M.id]=[];var R=L.multitree&&!("$orn" in M.data);var P=R&&M.data.$orns;Q.eachSubgraph(M,function(W){if(R&&P&&P.indexOf(W.data.$orn)>0&&W.drawn){W.drawn=false;J[M.id].push(W)}else{if((!R||!P)&&W.drawn){W.drawn=false;J[M.id].push(W)}}});M.drawn=true}if(I.length>0){S.fx.plot()}for(N in J){g(J[N],function(W){W.drawn=true})}for(N=0;N<I.length;N++){M=I[N];U.save();S.fx.plotSubtree(M,O,T,V);U.restore()}},getSiblings:function(I){var K={},J=Graph.Util;g(I,function(N){var M=J.getParents(N);if(M.length==0){K[N.id]=[N]}else{var L=[];J.eachSubnode(M[0],function(O){L.push(O)});K[N.id]=L}});return K}});ST.Geom=new o({initialize:function(I){this.viz=I;this.config=I.config;this.node=I.config.Node;this.edge=I.config.Edge},translate:function(J,I){I=j(I);Graph.Util.eachNode(this.viz.graph,function(K){g(I,function(L){K[L].$add(J)})})},switchOrientation:function(I){this.config.orientation=I},dispatch:function(){var J=Array.prototype.slice.call(arguments);var K=J.shift(),I=J.length;var L=function(M){return typeof M=="function"?M():M};if(I==2){return(K=="top"||K=="bottom")?L(J[0]):L(J[1])}else{if(I==4){switch(K){case"top":return L(J[0]);case"right":return L(J[1]);case"bottom":return L(J[2]);case"left":return L(J[3])}}}return undefined},getSize:function(J,I){var L=this.node,M=J.data,K=this.config;var O=L.overridable,P=K.siblingOffset;var R=(this.config.multitree&&("$orn" in J.data)&&J.data.$orn)||this.config.orientation;var Q=(O&&M.$width||L.width)+P;var N=(O&&M.$height||L.height)+P;if(!I){return this.dispatch(R,N,Q)}else{return this.dispatch(R,Q,N)}},getTreeBaseSize:function(M,N,J){var K=this.getSize(M,true),I=0,L=this;if(J(N,M)){return K}if(N===0){return 0}Graph.Util.eachSubnode(M,function(O){I+=L.getTreeBaseSize(O,N-1,J)});return(K>I?K:I)+this.config.subtreeOffset},getEdge:function(I,N,Q){var M=function(S,R){return function(){return I.pos.add(new Complex(S,R))}};var L=this.node;var O=this.node.overridable,J=I.data;var P=O&&J.$width||L.width;var K=O&&J.$height||L.height;if(N=="begin"){if(L.align=="center"){return this.dispatch(Q,M(0,K/2),M(-P/2,0),M(0,-K/2),M(P/2,0))}else{if(L.align=="left"){return this.dispatch(Q,M(0,K),M(0,0),M(0,0),M(P,0))}else{if(L.align=="right"){return this.dispatch(Q,M(0,0),M(-P,0),M(0,-K),M(0,0))}else{throw"align: not implemented"}}}}else{if(N=="end"){if(L.align=="center"){return this.dispatch(Q,M(0,-K/2),M(P/2,0),M(0,K/2),M(-P/2,0))}else{if(L.align=="left"){return this.dispatch(Q,M(0,0),M(P,0),M(0,K),M(0,0))}else{if(L.align=="right"){return this.dispatch(Q,M(0,-K),M(0,0),M(0,0),M(-P,0))}else{throw"align: not implemented"}}}}}},getScaledTreePosition:function(I,J){var L=this.node;var O=this.node.overridable,K=I.data;var P=(O&&K.$width||L.width);var M=(O&&K.$height||L.height);var Q=(this.config.multitree&&("$orn" in I.data)&&I.data.$orn)||this.config.orientation;var N=function(S,R){return function(){return I.pos.add(new Complex(S,R)).$scale(1-J)}};if(L.align=="left"){return this.dispatch(Q,N(0,M),N(0,0),N(0,0),N(P,0))}else{if(L.align=="center"){return this.dispatch(Q,N(0,M/2),N(-P/2,0),N(0,-M/2),N(P/2,0))}else{if(L.align=="right"){return this.dispatch(Q,N(0,0),N(-P,0),N(0,-M),N(0,0))}else{throw"align: not implemented"}}}},treeFitsInCanvas:function(N,I,O){var K=I.getSize(N);var L=(this.config.multitree&&("$orn" in N.data)&&N.data.$orn)||this.config.orientation;var J=this.dispatch(L,K.width,K.height);var M=this.getTreeBaseSize(N,O,function(Q,P){return Q===0||!Graph.Util.anySubnode(P)});return(M<J)},setRightLevelToShow:function(K,I){var L=this.getRightLevelToShow(K,I),J=this.viz.fx;Graph.Util.eachLevel(K,0,this.config.levelsToShow,function(N){var M=N._depth-K._depth;if(M>L){N.drawn=false;N.exist=false;J.hideLabel(N,false)}else{N.exist=true}});K.drawn=true},getRightLevelToShow:function(L,J){var I=this.config;var M=I.levelsToShow;var K=I.constrained;if(!K){return M}while(!this.treeFitsInCanvas(L,J,M)&&M>1){M--}return M}});ST.Plot=new o({Implements:Graph.Plot,initialize:function(I){this.viz=I;this.config=I.config;this.node=this.config.Node;this.edge=this.config.Edge;this.animation=new Animation;this.nodeTypes=new ST.Plot.NodeTypes;this.edgeTypes=new ST.Plot.EdgeTypes},plotSubtree:function(N,M,P,K){var I=this.viz,L=I.canvas;P=Math.min(Math.max(0.001,P),1);if(P>=0){N.drawn=false;var J=L.getCtx();var O=I.geom.getScaledTreePosition(N,P);J.translate(O.x,O.y);J.scale(P,P)}this.plotTree(N,!P,M,K);if(P>=0){N.drawn=true}},plotTree:function(L,M,I,S){var O=this,Q=this.viz,J=Q.canvas,K=this.config,R=J.getCtx();var P=K.multitree&&!("$orn" in L.data);var N=P&&L.data.$orns;Graph.Util.eachSubnode(L,function(U){if((!P||N.indexOf(U.data.$orn)>0)&&U.exist&&U.drawn){var T=L.getAdjacency(U.id);!S&&I.onBeforePlotLine(T);R.globalAlpha=Math.min(L.alpha,U.alpha);O.plotLine(T,J,S);!S&&I.onAfterPlotLine(T);O.plotTree(U,M,I,S)}});if(L.drawn){R.globalAlpha=L.alpha;!S&&I.onBeforePlotNode(L);this.plotNode(L,J,S);!S&&I.onAfterPlotNode(L);if(M&&R.globalAlpha>=0.95){this.plotLabel(J,L,I)}else{this.hideLabel(L,false)}}else{this.hideLabel(L,true)}},placeLabel:function(T,L,O){var R=L.pos.getc(true),M=this.node,J=this.viz.canvas;var S=M.overridable&&L.data.$width||M.width;var N=M.overridable&&L.data.$height||M.height;var P=J.getSize();var K,Q;if(M.align=="center"){K={x:Math.round(R.x-S/2+P.width/2),y:Math.round(R.y-N/2+P.height/2)}}else{if(M.align=="left"){Q=this.config.orientation;if(Q=="bottom"||Q=="top"){K={x:Math.round(R.x-S/2+P.width/2),y:Math.round(R.y+P.height/2)}}else{K={x:Math.round(R.x+P.width/2),y:Math.round(R.y-N/2+P.height/2)}}}else{if(M.align=="right"){Q=this.config.orientation;if(Q=="bottom"||Q=="top"){K={x:Math.round(R.x-S/2+P.width/2),y:Math.round(R.y-N+P.height/2)}}else{K={x:Math.round(R.x-S+P.width/2),y:Math.round(R.y-N/2+P.height/2)}}}else{throw"align: not implemented"}}}var I=T.style;I.left=K.x+"px";I.top=K.y+"px";I.display=this.fitsInCanvas(K,J)?"":"none";O.onPlaceLabel(T,L)},getAlignedPos:function(N,L,I){var K=this.node;var M,J;if(K.align=="center"){M={x:N.x-L/2,y:N.y-I/2}}else{if(K.align=="left"){J=this.config.orientation;if(J=="bottom"||J=="top"){M={x:N.x-L/2,y:N.y}}else{M={x:N.x,y:N.y-I/2}}}else{if(K.align=="right"){J=this.config.orientation;if(J=="bottom"||J=="top"){M={x:N.x-L/2,y:N.y-I}}else{M={x:N.x-L,y:N.y-I/2}}}else{throw"align: not implemented"}}}return M},getOrientation:function(I){var K=this.config;var J=K.orientation;if(K.multitree){var L=I.nodeFrom;var M=I.nodeTo;J=(("$orn" in L.data)&&L.data.$orn)||(("$orn" in M.data)&&M.data.$orn)}return J}});ST.Plot.NodeTypes=new o({none:function(){},circle:function(M,J){var P=M.pos.getc(true),L=this.node,N=M.data;var K=L.overridable&&N;var O=K&&N.$dim||L.dim;var I=this.getAlignedPos(P,O*2,O*2);J.path("fill",function(Q){Q.arc(I.x+O,I.y+O,O,0,Math.PI*2,true)})},square:function(M,J){var P=M.pos.getc(true),L=this.node,N=M.data;var K=L.overridable&&N;var O=K&&N.$dim||L.dim;var I=this.getAlignedPos(P,O,O);J.getCtx().fillRect(I.x,I.y,O,O)},ellipse:function(K,J){var N=K.pos.getc(true),O=this.node,L=K.data;var M=O.overridable&&L;var I=(M&&L.$width||O.width)/2;var Q=(M&&L.$height||O.height)/2;var P=this.getAlignedPos(N,I*2,Q*2);var R=J.getCtx();R.save();R.scale(I/Q,Q/I);J.path("fill",function(S){S.arc((P.x+I)*(Q/I),(P.y+Q)*(I/Q),Q,0,Math.PI*2,true)});R.restore()},rectangle:function(K,J){var N=K.pos.getc(true),O=this.node,L=K.data;var M=O.overridable&&L;var I=M&&L.$width||O.width;var Q=M&&L.$height||O.height;var P=this.getAlignedPos(N,I,Q);J.getCtx().fillRect(P.x,P.y,I,Q)}});ST.Plot.EdgeTypes=new o({none:function(){},line:function(J,L){var K=this.getOrientation(J);var N=J.nodeFrom,O=J.nodeTo;var M=this.viz.geom.getEdge(N._depth<O._depth?N:O,"begin",K);var I=this.viz.geom.getEdge(N._depth<O._depth?O:N,"end",K);L.path("stroke",function(P){P.moveTo(M.x,M.y);P.lineTo(I.x,I.y)})},"quadratic:begin":function(R,J){var Q=this.getOrientation(R);var M=R.data,I=this.edge;var O=R.nodeFrom,S=R.nodeTo;var K=this.viz.geom.getEdge(O._depth<S._depth?O:S,"begin",Q);var L=this.viz.geom.getEdge(O._depth<S._depth?S:O,"end",Q);var P=I.overridable&&M;var N=P&&M.$dim||I.dim;switch(Q){case"left":J.path("stroke",function(T){T.moveTo(K.x,K.y);T.quadraticCurveTo(K.x+N,K.y,L.x,L.y)});break;case"right":J.path("stroke",function(T){T.moveTo(K.x,K.y);T.quadraticCurveTo(K.x-N,K.y,L.x,L.y)});break;case"top":J.path("stroke",function(T){T.moveTo(K.x,K.y);T.quadraticCurveTo(K.x,K.y+N,L.x,L.y)});break;case"bottom":J.path("stroke",function(T){T.moveTo(K.x,K.y);T.quadraticCurveTo(K.x,K.y-N,L.x,L.y)});break}},"quadratic:end":function(R,J){var Q=this.getOrientation(R);var M=R.data,I=this.edge;var O=R.nodeFrom,S=R.nodeTo;var K=this.viz.geom.getEdge(O._depth<S._depth?O:S,"begin",Q);var L=this.viz.geom.getEdge(O._depth<S._depth?S:O,"end",Q);var P=I.overridable&&M;var N=P&&M.$dim||I.dim;switch(Q){case"left":J.path("stroke",function(T){T.moveTo(K.x,K.y);T.quadraticCurveTo(L.x-N,L.y,L.x,L.y)});break;case"right":J.path("stroke",function(T){T.moveTo(K.x,K.y);T.quadraticCurveTo(L.x+N,L.y,L.x,L.y)});break;case"top":J.path("stroke",function(T){T.moveTo(K.x,K.y);T.quadraticCurveTo(L.x,L.y-N,L.x,L.y)});break;case"bottom":J.path("stroke",function(T){T.moveTo(K.x,K.y);T.quadraticCurveTo(L.x,L.y+N,L.x,L.y)});break}},bezier:function(R,J){var M=R.data,I=this.edge;var Q=this.getOrientation(R);var O=R.nodeFrom,S=R.nodeTo;var K=this.viz.geom.getEdge(O._depth<S._depth?O:S,"begin",Q);var L=this.viz.geom.getEdge(O._depth<S._depth?S:O,"end",Q);var P=I.overridable&&M;var N=P&&M.$dim||I.dim;switch(Q){case"left":J.path("stroke",function(T){T.moveTo(K.x,K.y);T.bezierCurveTo(K.x+N,K.y,L.x-N,L.y,L.x,L.y)});break;case"right":J.path("stroke",function(T){T.moveTo(K.x,K.y);T.bezierCurveTo(K.x-N,K.y,L.x+N,L.y,L.x,L.y)});break;case"top":J.path("stroke",function(T){T.moveTo(K.x,K.y);T.bezierCurveTo(K.x,K.y+N,L.x,L.y-N,L.x,L.y)});break;case"bottom":J.path("stroke",function(T){T.moveTo(K.x,K.y);T.bezierCurveTo(K.x,K.y-N,L.x,L.y+N,L.x,L.y)});break}},arrow:function(Q,L){var W=this.getOrientation(Q);var U=Q.nodeFrom,M=Q.nodeTo;var Z=Q.data,P=this.edge;var R=P.overridable&&Z;var O=R&&Z.$dim||P.dim;if(R&&Z.$direction&&Z.$direction.length>1){var K={};K[U.id]=U;K[M.id]=M;var V=Z.$direction;U=K[V[0]];M=K[V[1]]}var N=this.viz.geom.getEdge(U,"begin",W);var S=this.viz.geom.getEdge(M,"end",W);var T=new Complex(S.x-N.x,S.y-N.y);T.$scale(O/T.norm());var X=new Complex(S.x-T.x,S.y-T.y);var Y=new Complex(-T.y/2,T.x/2);var J=X.add(Y),I=X.$add(Y.$scale(-1));L.path("stroke",function(aa){aa.moveTo(N.x,N.y);aa.lineTo(S.x,S.y)});L.path("fill",function(aa){aa.moveTo(J.x,J.y);aa.lineTo(I.x,I.y);aa.lineTo(S.x,S.y)})}})})();var AngularWidth={setAngularWidthForNodes:function(){var x=this.config.Node;var z=x.overridable;var y=x.dim;Graph.Util.eachBFS(this.graph,this.root,function(C,A){var B=(z&&C.data&&C.data.$aw)||y;C._angularWidth=B/A},"ignore")},setSubtreesAngularWidth:function(){var x=this;Graph.Util.eachNode(this.graph,function(y){x.setSubtreeAngularWidth(y)},"ignore")},setSubtreeAngularWidth:function(A){var z=this,y=A._angularWidth,x=0;Graph.Util.eachSubnode(A,function(B){z.setSubtreeAngularWidth(B);x+=B._treeAngularWidth},"ignore");A._treeAngularWidth=Math.max(y,x)},computeAngularWidths:function(){this.setAngularWidthForNodes();this.setSubtreesAngularWidth()}};this.RGraph=new o({Implements:[Loader,AngularWidth],initialize:function(A,x){var z={labelContainer:A.id+"-label",interpolation:"linear",levelDistance:100,withLabels:true,Node:{overridable:false,type:"circle",dim:3,color:"#ccb",width:5,height:5,lineWidth:1},Edge:{overridable:false,type:"line",color:"#ccb",lineWidth:1},fps:40,duration:2500,transition:Trans.Quart.easeInOut,clearCanvas:true};var y={onBeforeCompute:b,onAfterCompute:b,onCreateLabel:b,onPlaceLabel:b,onComplete:b,onBeforePlotLine:b,onAfterPlotLine:b,onBeforePlotNode:b,onAfterPlotNode:b};this.controller=this.config=r(z,y,x);this.graphOptions={complex:false,Node:{selected:false,exist:true,drawn:true}};this.graph=new Graph(this.graphOptions);this.fx=new RGraph.Plot(this);this.op=new RGraph.Op(this);this.json=null;this.canvas=A;this.root=null;this.busy=false;this.parent=false},refresh:function(){this.compute();this.plot()},reposition:function(){this.compute("endPos")},plot:function(){this.fx.plot()},compute:function(y){var z=y||["pos","startPos","endPos"];var x=this.graph.getNode(this.root);x._depth=0;Graph.Util.computeLevels(this.graph,this.root,0,"ignore");this.computeAngularWidths();this.computePositions(z)},computePositions:function(E){var y=j(E);var D=this.graph;var C=Graph.Util;var x=this.graph.getNode(this.root);var B=this.parent;var z=this.config;for(var A=0;A<y.length;A++){x[y[A]]=l(0,0)}x.angleSpan={begin:0,end:2*Math.PI};x._rel=1;C.eachBFS(this.graph,this.root,function(I){var L=I.angleSpan.end-I.angleSpan.begin;var O=(I._depth+1)*z.levelDistance;var M=I.angleSpan.begin;var N=0,F=[];C.eachSubnode(I,function(Q){N+=Q._treeAngularWidth;F.push(Q)},"ignore");if(B&&B.id==I.id&&F.length>0&&F[0].dist){F.sort(function(R,Q){return(R.dist>=Q.dist)-(R.dist<=Q.dist)})}for(var J=0;J<F.length;J++){var H=F[J];if(!H._flag){H._rel=H._treeAngularWidth/N;var P=H._rel*L;var G=M+P/2;for(var K=0;K<y.length;K++){H[y[K]]=l(G,O)}H.angleSpan={begin:M,end:M+P};M+=P}}},"ignore")},getNodeAndParentAngle:function(E){var z=false;var D=this.graph.getNode(E);var B=Graph.Util.getParents(D);var A=(B.length>0)?B[0]:false;if(A){var x=A.pos.getc(),C=D.pos.getc();var y=x.add(C.scale(-1));z=Math.atan2(y.y,y.x);if(z<0){z+=2*Math.PI}}return{parent:A,theta:z}},tagChildren:function(B,D){if(B.angleSpan){var C=[];Graph.Util.eachAdjacency(B,function(E){C.push(E.nodeTo)},"ignore");var x=C.length;for(var A=0;A<x&&D!=C[A].id;A++){}for(var z=(A+1)%x,y=0;D!=C[z].id;z=(z+1)%x){C[z].dist=y++}}},onClick:function(B,y){if(this.root!=B&&!this.busy){this.busy=true;this.root=B;that=this;this.controller.onBeforeCompute(this.graph.getNode(B));var z=this.getNodeAndParentAngle(B);this.tagChildren(z.parent,B);this.parent=z.parent;this.compute("endPos");var x=z.theta-z.parent.endPos.theta;Graph.Util.eachNode(this.graph,function(C){C.endPos.set(C.endPos.getp().add(l(x,0)))});var A=this.config.interpolation;y=r({onComplete:b},y||{});this.fx.animate(r({hideLabels:true,modes:[A]},y,{onComplete:function(){that.busy=false;y.onComplete()}}))}}});RGraph.Op=new o({Implements:Graph.Op,initialize:function(x){this.viz=x}});RGraph.Plot=new o({Implements:Graph.Plot,initialize:function(x){this.viz=x;this.config=x.config;this.node=x.config.Node;this.edge=x.config.Edge;this.animation=new Animation;this.nodeTypes=new RGraph.Plot.NodeTypes;this.edgeTypes=new RGraph.Plot.EdgeTypes},placeLabel:function(y,C,z){var E=C.pos.getc(true),A=this.viz.canvas;var x=A.getSize();var D={x:Math.round(E.x+x.width/2),y:Math.round(E.y+x.height/2)};var B=y.style;B.left=D.x+"px";B.top=D.y+"px";B.display=this.fitsInCanvas(D,A)?"":"none";z.onPlaceLabel(y,C)}});RGraph.Plot.NodeTypes=new o({none:function(){},circle:function(z,x){var C=z.pos.getc(true),y=this.node,B=z.data;var A=y.overridable&&B&&B.$dim||y.dim;x.path("fill",function(D){D.arc(C.x,C.y,A,0,Math.PI*2,true)})},square:function(A,x){var D=A.pos.getc(true),z=this.node,C=A.data;var B=z.overridable&&C&&C.$dim||z.dim;var y=2*B;x.getCtx().fillRect(D.x-B,D.y-B,y,y)},rectangle:function(B,y){var D=B.pos.getc(true),A=this.node,C=B.data;var z=A.overridable&&C&&C.$width||A.width;var x=A.overridable&&C&&C.$height||A.height;y.getCtx().fillRect(D.x-z/2,D.y-x/2,z,x)},triangle:function(B,y){var F=B.pos.getc(true),G=this.node,C=B.data;var x=G.overridable&&C&&C.$dim||G.dim;var A=F.x,z=F.y-x,I=A-x,H=F.y+x,E=A+x,D=H;y.path("fill",function(J){J.moveTo(A,z);J.lineTo(I,H);J.lineTo(E,D)})},star:function(z,y){var D=z.pos.getc(true),E=this.node,B=z.data;var x=E.overridable&&B&&B.$dim||E.dim;var F=y.getCtx(),C=Math.PI/5;F.save();F.translate(D.x,D.y);F.beginPath();F.moveTo(x,0);for(var A=0;A<9;A++){F.rotate(C);if(A%2==0){F.lineTo((x/0.525731)*0.200811,0)}else{F.lineTo(x,0)}}F.closePath();F.fill();F.restore()}});RGraph.Plot.EdgeTypes=new o({none:function(){},line:function(x,y){var A=x.nodeFrom.pos.getc(true);var z=x.nodeTo.pos.getc(true);y.path("stroke",function(B){B.moveTo(A.x,A.y);B.lineTo(z.x,z.y)})},arrow:function(J,B){var D=J.nodeFrom,A=J.nodeTo;var E=J.data,x=this.edge;var I=x.overridable&&E;var L=I&&E.$dim||14;if(I&&E.$direction&&E.$direction.length>1){var y={};y[D.id]=D;y[A.id]=A;var z=E.$direction;D=y[z[0]];A=y[z[1]]}var N=D.pos.getc(true),C=A.pos.getc(true);var H=new Complex(C.x-N.x,C.y-N.y);H.$scale(L/H.norm());var F=new Complex(C.x-H.x,C.y-H.y);var G=new Complex(-H.y/2,H.x/2);var M=F.add(G),K=F.$add(G.$scale(-1));B.path("stroke",function(O){O.moveTo(N.x,N.y);O.lineTo(C.x,C.y)});B.path("fill",function(O){O.moveTo(M.x,M.y);O.lineTo(K.x,K.y);O.lineTo(C.x,C.y)})}});Complex.prototype.moebiusTransformation=function(z){var x=this.add(z);var y=z.$conjugate().$prod(this);y.x++;return x.$div(y)};Graph.Util.getClosestNodeToOrigin=function(y,z,x){return this.getClosestNodeToPos(y,Polar.KER,z,x)};Graph.Util.getClosestNodeToPos=function(z,C,B,x){var y=null;B=B||"pos";C=C&&C.getc(true)||Complex.KER;var A=function(E,D){var G=E.x-D.x,F=E.y-D.y;return G*G+F*F};this.eachNode(z,function(D){y=(y==null||A(D[B].getc(true),C)<A(y[B].getc(true),C))?D:y},x);return y};Graph.Util.moebiusTransformation=function(z,B,A,y,x){this.eachNode(z,function(D){for(var C=0;C<A.length;C++){var F=B[C].scale(-1),E=y?y:A[C];D[A[C]].set(D[E].getc().moebiusTransformation(F))}},x)};this.Hypertree=new o({Implements:[Loader,AngularWidth],initialize:function(A,x){var z={labelContainer:A.id+"-label",withLabels:true,Node:{overridable:false,type:"circle",dim:7,color:"#ccb",width:5,height:5,lineWidth:1,transform:true},Edge:{overridable:false,type:"hyperline",color:"#ccb",lineWidth:1},clearCanvas:true,fps:40,duration:1500,transition:Trans.Quart.easeInOut};var y={onBeforeCompute:b,onAfterCompute:b,onCreateLabel:b,onPlaceLabel:b,onComplete:b,onBeforePlotLine:b,onAfterPlotLine:b,onBeforePlotNode:b,onAfterPlotNode:b};this.controller=this.config=r(z,y,x);this.graphOptions={complex:false,Node:{selected:false,exist:true,drawn:true}};this.graph=new Graph(this.graphOptions);this.fx=new Hypertree.Plot(this);this.op=new Hypertree.Op(this);this.json=null;this.canvas=A;this.root=null;this.busy=false},refresh:function(x){if(x){this.reposition();Graph.Util.eachNode(this.graph,function(y){y.startPos.rho=y.pos.rho=y.endPos.rho;y.startPos.theta=y.pos.theta=y.endPos.theta})}else{this.compute()}this.plot()},reposition:function(){this.compute("endPos");var x=this.graph.getNode(this.root).pos.getc().scale(-1);Graph.Util.moebiusTransformation(this.graph,[x],["endPos"],"endPos","ignore");Graph.Util.eachNode(this.graph,function(y){if(y.ignore){y.endPos.rho=y.pos.rho;y.endPos.theta=y.pos.theta}})},plot:function(){this.fx.plot()},compute:function(y){var z=y||["pos","startPos"];var x=this.graph.getNode(this.root);x._depth=0;Graph.Util.computeLevels(this.graph,this.root,0,"ignore");this.computeAngularWidths();this.computePositions(z)},computePositions:function(F){var G=j(F);var B=this.graph,D=Graph.Util;var E=this.graph.getNode(this.root),C=this,x=this.config;var H=this.canvas.getSize();var z=Math.min(H.width,H.height)/2;for(var A=0;A<G.length;A++){E[G[A]]=l(0,0)}E.angleSpan={begin:0,end:2*Math.PI};E._rel=1;var y=(function(){var K=0;D.eachNode(B,function(L){K=(L._depth>K)?L._depth:K;L._scale=z},"ignore");for(var J=0.51;J<=1;J+=0.01){var I=(function(L,M){return(1-Math.pow(L,M))/(1-L)})(J,K+1);if(I>=2){return J-0.01}}return 0.5})();D.eachBFS(this.graph,this.root,function(N){var J=N.angleSpan.end-N.angleSpan.begin;var O=N.angleSpan.begin;var M=(function(Q){var R=0;D.eachSubnode(Q,function(S){R+=S._treeAngularWidth},"ignore");return R})(N);for(var L=1,I=0,K=y,P=N._depth;L<=P+1;L++){I+=K;K*=y}D.eachSubnode(N,function(T){if(!T._flag){T._rel=T._treeAngularWidth/M;var S=T._rel*J;var R=O+S/2;for(var Q=0;Q<G.length;Q++){T[G[Q]]=l(R,I)}T.angleSpan={begin:O,end:O+S};O+=S}},"ignore")},"ignore")},onClick:function(z,x){var y=this.graph.getNode(z).pos.getc(true);this.move(y,x)},move:function(C,z){var y=q(C.x,C.y);if(this.busy===false&&y.norm()<1){var B=Graph.Util;this.busy=true;var x=B.getClosestNodeToPos(this.graph,y),A=this;B.computeLevels(this.graph,x.id,0);this.controller.onBeforeCompute(x);if(y.norm()<1){z=r({onComplete:b},z||{});this.fx.animate(r({modes:["moebius"],hideLabels:true},z,{onComplete:function(){A.busy=false;z.onComplete()}}),y)}}}});Hypertree.Op=new o({Implements:Graph.Op,initialize:function(x){this.viz=x}});Hypertree.Plot=new o({Implements:Graph.Plot,initialize:function(x){this.viz=x;this.config=x.config;this.node=this.config.Node;this.edge=this.config.Edge;this.animation=new Animation;this.nodeTypes=new Hypertree.Plot.NodeTypes;this.edgeTypes=new Hypertree.Plot.EdgeTypes},hyperline:function(I,A){var B=I.nodeFrom,z=I.nodeTo,F=I.data;var J=B.pos.getc(),E=z.pos.getc();var D=this.computeArcThroughTwoPoints(J,E);var K=A.getSize();var C=Math.min(K.width,K.height)/2;if(D.a>1000||D.b>1000||D.ratio>1000){A.path("stroke",function(L){L.moveTo(J.x*C,J.y*C);L.lineTo(E.x*C,E.y*C)})}else{var H=Math.atan2(E.y-D.y,E.x-D.x);var G=Math.atan2(J.y-D.y,J.x-D.x);var y=this.sense(H,G);var x=A.getCtx();A.path("stroke",function(L){L.arc(D.x*C,D.y*C,D.ratio*C,H,G,y)})}},computeArcThroughTwoPoints:function(L,K){var D=(L.x*K.y-L.y*K.x),z=D;var C=L.squaredNorm(),B=K.squaredNorm();if(D==0){return{x:0,y:0,ratio:1001}}var J=(L.y*B-K.y*C+L.y-K.y)/D;var H=(K.x*C-L.x*B+K.x-L.x)/z;var I=-J/2;var G=-H/2;var F=(J*J+H*H)/4-1;if(F<0){return{x:0,y:0,ratio:1001}}var E=Math.sqrt(F);var A={x:I,y:G,ratio:E,a:J,b:H};return A},sense:function(x,y){return(x<y)?((x+Math.PI>y)?false:true):((y+Math.PI>x)?true:false)},placeLabel:function(F,A,C){var E=A.pos.getc(true),y=this.viz.canvas;var D=y.getSize();var B=A._scale;var z={x:Math.round(E.x*B+D.width/2),y:Math.round(E.y*B+D.height/2)};var x=F.style;x.left=z.x+"px";x.top=z.y+"px";x.display="";C.onPlaceLabel(F,A)}});Hypertree.Plot.NodeTypes=new o({none:function(){},circle:function(A,y){var z=this.node,C=A.data;var B=z.overridable&&C&&C.$dim||z.dim;var D=A.pos.getc(),E=D.scale(A._scale);var x=z.transform?B*(1-D.squaredNorm()):B;if(x>=B/4){y.path("fill",function(F){F.arc(E.x,E.y,x,0,Math.PI*2,true)})}},square:function(A,z){var F=this.node,C=A.data;var x=F.overridable&&C&&C.$dim||F.dim;var y=A.pos.getc(),E=y.scale(A._scale);var D=F.transform?x*(1-y.squaredNorm()):x;var B=2*D;if(D>=x/4){z.getCtx().fillRect(E.x-D,E.y-D,B,B)}},rectangle:function(A,z){var E=this.node,B=A.data;var y=E.overridable&&B&&B.$width||E.width;var F=E.overridable&&B&&B.$height||E.height;var x=A.pos.getc(),D=x.scale(A._scale);var C=1-x.squaredNorm();y=E.transform?y*C:y;F=E.transform?F*C:F;if(C>=0.25){z.getCtx().fillRect(D.x-y/2,D.y-F/2,y,F)}},triangle:function(C,z){var I=this.node,D=C.data;var x=I.overridable&&D&&D.$dim||I.dim;var y=C.pos.getc(),H=y.scale(C._scale);var G=I.transform?x*(1-y.squaredNorm()):x;if(G>=x/4){var B=H.x,A=H.y-G,K=B-G,J=H.y+G,F=B+G,E=J;z.path("fill",function(L){L.moveTo(B,A);L.lineTo(K,J);L.lineTo(F,E)})}},star:function(A,z){var G=this.node,C=A.data;var x=G.overridable&&C&&C.$dim||G.dim;var y=A.pos.getc(),F=y.scale(A._scale);var E=G.transform?x*(1-y.squaredNorm()):x;if(E>=x/4){var H=z.getCtx(),D=Math.PI/5;H.save();H.translate(F.x,F.y);H.beginPath();H.moveTo(x,0);for(var B=0;B<9;B++){H.rotate(D);if(B%2==0){H.lineTo((E/0.525731)*0.200811,0)}else{H.lineTo(E,0)}}H.closePath();H.fill();H.restore()}}});Hypertree.Plot.EdgeTypes=new o({none:function(){},line:function(x,y){var z=x.nodeFrom._scale;var B=x.nodeFrom.pos.getc(true);var A=x.nodeTo.pos.getc(true);y.path("stroke",function(C){C.moveTo(B.x*z,B.y*z);C.lineTo(A.x*z,A.y*z)})},hyperline:function(x,y){this.hyperline(x,y)}});this.TM={layout:{orientation:"h",vertical:function(){return this.orientation=="v"},horizontal:function(){return this.orientation=="h"},change:function(){this.orientation=this.vertical()?"h":"v"}},innerController:{onBeforeCompute:b,onAfterCompute:b,onComplete:b,onCreateElement:b,onDestroyElement:b,request:false},config:{orientation:"h",titleHeight:13,rootId:"infovis",offset:4,levelsToShow:3,addLeftClickHandler:false,addRightClickHandler:false,selectPathOnHover:false,Color:{allow:false,minValue:-100,maxValue:100,minColorValue:[255,0,50],maxColorValue:[0,255,50]},Tips:{allow:false,offsetX:20,offsetY:20,onShow:b}},initialize:function(x){this.tree=null;this.shownTree=null;this.controller=this.config=r(this.config,this.innerController,x);this.rootId=this.config.rootId;this.layout.orientation=this.config.orientation;if(this.config.Tips.allow&&document.body){var B=document.getElementById("_tooltip")||document.createElement("div");B.id="_tooltip";B.className="tip";var z=B.style;z.position="absolute";z.display="none";z.zIndex=13000;document.body.appendChild(B);this.tip=B}var A=this;var y=function(){A.empty();if(window.CollectGarbage){window.CollectGarbage()}delete y};if(window.addEventListener){window.addEventListener("unload",y,false)}else{window.attachEvent("onunload",y)}},each:function(x){(function y(D){if(!D){return}var C=D.childNodes,z=C.length;if(z>0){x.apply(this,[D,z===1,C[0],C[1]])}if(z>1){for(var A=C[1].childNodes,B=0;B<A.length;B++){y(A[B])}}})(e(this.rootId).firstChild)},toStyle:function(z){var x="";for(var y in z){x+=y+":"+z[y]+";"}return x},leaf:function(x){return x.children==0},createBox:function(y,A,x){var z;if(!this.leaf(y)){z=this.headBox(y,A)+this.bodyBox(x,A)}else{z=this.leafBox(y,A)}return this.contentBox(y,A,z)},plot:function(B){var D=B.coord,A="";if(this.leaf(B)){return this.createBox(B,D,null)}for(var z=0,C=B.children;z<C.length;z++){var y=C[z],x=y.coord;if(x.width*x.height>1){A+=this.plot(y)}}return this.createBox(B,D,A)},headBox:function(y,B){var x=this.config,A=x.offset;var z={height:x.titleHeight+"px",width:(B.width-A)+"px",left:A/2+"px"};return'<div class="head" style="'+this.toStyle(z)+'">'+y.name+"</div>"},bodyBox:function(y,C){var x=this.config,z=x.titleHeight,B=x.offset;var A={width:(C.width-B)+"px",height:(C.height-B-z)+"px",top:(z+B/2)+"px",left:(B/2)+"px"};return'<div class="body" style="'+this.toStyle(A)+'">'+y+"</div>"},contentBox:function(z,B,y){var A={};for(var x in B){A[x]=B[x]+"px"}return'<div class="content" style="'+this.toStyle(A)+'" id="'+z.id+'">'+y+"</div>"},leafBox:function(A,E){var z=this.config;var y=z.Color.allow&&this.setColor(A),D=z.offset,B=E.width-D,x=E.height-D;var C={top:(D/2)+"px",height:x+"px",width:B+"px",left:(D/2)+"px"};if(y){C["background-color"]=y}return'<div class="leaf" style="'+this.toStyle(C)+'">'+A.name+"</div>"},setColor:function(F){var A=this.config.Color,B=A.maxColorValue,y=A.minColorValue,C=A.maxValue,G=A.minValue,E=C-G,D=(F.data.$color-0);var z=function(I,H){return Math.round((((B[I]-y[I])/E)*(H-G)+y[I]))};return d([z(0,D),z(1,D),z(2,D)])},enter:function(x){this.view(x.parentNode.id)},onLeftClick:function(x){this.enter(x)},out:function(){var x=TreeUtil.getParent(this.tree,this.shownTree.id);if(x){if(this.controller.request){TreeUtil.prune(x,this.config.levelsToShow)}this.view(x.id)}},onRightClick:function(){this.out()},view:function(B){var x=this.config,z=this;var y={onComplete:function(){z.loadTree(B);e(x.rootId).focus()}};if(this.controller.request){var A=TreeUtil;A.loadSubtrees(A.getSubtree(this.tree,B),r(this.controller,y))}else{y.onComplete()}},resetPath:function(x){var y=this.rootId,B=this.resetPath.previous;this.resetPath.previous=x||false;function z(D){var C=D.parentNode;return C&&(C.id!=y)&&C}function A(F,C){if(F){var D=e(F.id);if(D){var E=z(D);while(E){F=E.childNodes[0];if(s(F,"in-path")){if(C==undefined||!!C){a(F,"in-path")}}else{if(!C){p(F,"in-path")}}E=z(E)}}}}A(B,true);A(x,false)},initializeElements:function(){var x=this.controller,z=this;var y=m(false),A=x.Tips.allow;this.each(function(F,E,D,C){var B=TreeUtil.getSubtree(z.tree,F.id);x.onCreateElement(F,B,E,D,C);if(x.addRightClickHandler){D.oncontextmenu=y}if(x.addLeftClickHandler||x.addRightClickHandler){t(D,"mouseup",function(G){var H=(G.which==3||G.button==2);if(H){if(x.addRightClickHandler){z.onRightClick()}}else{if(x.addLeftClickHandler){z.onLeftClick(D)}}if(G.preventDefault){G.preventDefault()}else{G.returnValue=false}})}if(x.selectPathOnHover||A){t(D,"mouseover",function(G){if(x.selectPathOnHover){if(E){p(D,"over-leaf")}else{p(D,"over-head");p(F,"over-content")}if(F.id){z.resetPath(B)}}if(A){x.Tips.onShow(z.tip,B,E,D)}});t(D,"mouseout",function(G){if(x.selectPathOnHover){if(E){a(D,"over-leaf")}else{a(D,"over-head");a(F,"over-content")}z.resetPath()}if(A){z.tip.style.display="none"}});if(A){t(D,"mousemove",function(J,I){var O=z.tip;I=I||window;J=J||I.event;var N=I.document;N=N.html||N.body;var K={x:J.pageX||J.clientX+N.scrollLeft,y:J.pageY||J.clientY+N.scrollTop};O.style.display="";I={height:document.body.clientHeight,width:document.body.clientWidth};var H={width:O.offsetWidth,height:O.offsetHeight};var G=O.style,M=x.Tips.offsetX,L=x.Tips.offsetY;G.top=((K.y+L+H.height>I.height)?(K.y-H.height-L):K.y+L)+"px";G.left=((K.x+H.width+M>I.width)?(K.x-H.width-M):K.x+M)+"px"})}}})},destroyElements:function(){if(this.controller.onDestroyElement!=b){var x=this.controller,y=this;this.each(function(C,B,A,z){x.onDestroyElement(C,TreeUtil.getSubtree(y.tree,C.id),B,A,z)})}},empty:function(){this.destroyElements();f(e(this.rootId))},loadTree:function(x){this.empty();this.loadJSON(TreeUtil.getSubtree(this.tree,x))}};TM.SliceAndDice=new o({Implements:TM,loadJSON:function(A){this.controller.onBeforeCompute(A);var y=e(this.rootId),z=this.config,B=y.offsetWidth,x=y.offsetHeight;var C={coord:{top:0,left:0,width:B,height:x+z.titleHeight+z.offset}};if(this.tree==null){this.tree=A}this.shownTree=A;this.compute(C,A,this.layout.orientation);y.innerHTML=this.plot(A);this.initializeElements();this.controller.onAfterCompute(A)},compute:function(D,M,B){var O=this.config,I=D.coord,L=O.offset,H=I.width-L,F=I.height-L-O.titleHeight,y=D.data,x=(y&&("$area" in y))?M.data.$area/y.$area:1;var G,E,K,C,A;var N=(B=="h");if(N){B="v";G=F;E=Math.round(H*x);K="height";C="top";A="left"}else{B="h";G=Math.round(F*x);E=H;K="width";C="left";A="top"}M.coord={width:E,height:G,top:0,left:0};var J=0,z=this;g(M.children,function(P){z.compute(M,P,B);P.coord[C]=J;P.coord[A]=0;J+=Math.floor(P.coord[K])})}});TM.Area=new o({loadJSON:function(z){this.controller.onBeforeCompute(z);var y=e(this.rootId),A=y.offsetWidth,x=y.offsetHeight,E=this.config.offset,C=A-E,B=x-E-this.config.titleHeight;z.coord={height:x,width:A,top:0,left:0};var D=r(z.coord,{width:C,height:B});this.compute(z,D);y.innerHTML=this.plot(z);if(this.tree==null){this.tree=z}this.shownTree=z;this.initializeElements();this.controller.onAfterCompute(z)},computeDim:function(A,E,y,D,z){if(A.length+E.length==1){var x=(A.length==1)?A:E;this.layoutLast(x,y,D);return}if(A.length>=2&&E.length==0){E=[A[0]];A=A.slice(1)}if(A.length==0){if(E.length>0){this.layoutRow(E,y,D)}return}var C=A[0];if(z(E,y)>=z([C].concat(E),y)){this.computeDim(A.slice(1),E.concat([C]),y,D,z)}else{var B=this.layoutRow(E,y,D);this.computeDim(A,[],B.dim,B,z)}},worstAspectRatio:function(x,E){if(!x||x.length==0){return Number.MAX_VALUE}var y=0,F=0,B=Number.MAX_VALUE;for(var C=0;C<x.length;C++){var z=x[C]._area;y+=z;B=(B<z)?B:z;F=(F>z)?F:z}var D=E*E,A=y*y;return Math.max(D*F/A,A/(D*B))},avgAspectRatio:function(A,x){if(!A||A.length==0){return Number.MAX_VALUE}var C=0;for(var y=0;y<A.length;y++){var B=A[y]._area;var z=B/x;C+=(x>z)?x/z:z/x}return C/A.length},layoutLast:function(y,x,z){y[0].coord=z}});TM.Squarified=new o({Implements:[TM,TM.Area],compute:function(F,C){if(!(C.width>=C.height&&this.layout.horizontal())){this.layout.change()}var x=F.children,z=this.config;if(x.length>0){this.processChildrenLayout(F,x,C);for(var B=0;B<x.length;B++){var A=x[B].coord,D=z.offset,E=A.height-(z.titleHeight+D),y=A.width-D;C={width:y,height:E,top:0,left:0};this.compute(x[B],C)}}},processChildrenLayout:function(F,x,B){var y=B.width*B.height;var A,C=0,G=[];for(A=0;A<x.length;A++){G[A]=parseFloat(x[A].data.$area);C+=G[A]}for(A=0;A<G.length;A++){x[A]._area=y*G[A]/C}var z=(this.layout.horizontal())?B.height:B.width;x.sort(function(I,H){return(I._area<=H._area)-(I._area>=H._area)});var E=[x[0]];var D=x.slice(1);this.squarify(D,E,z,B)},squarify:function(y,A,x,z){this.computeDim(y,A,x,z,this.worstAspectRatio)},layoutRow:function(y,x,z){if(this.layout.horizontal()){return this.layoutV(y,x,z)}else{return this.layoutH(y,x,z)}},layoutV:function(x,F,C){var G=0,z=Math.round;g(x,function(H){G+=H._area});var y=z(G/F),D=0;for(var A=0;A<x.length;A++){var B=z(x[A]._area/y);x[A].coord={height:B,width:y,top:C.top+D,left:C.left};D+=B}var E={height:C.height,width:C.width-y,top:C.top,left:C.left+y};E.dim=Math.min(E.width,E.height);if(E.dim!=E.height){this.layout.change()}return E},layoutH:function(x,E,B){var G=0,y=Math.round;g(x,function(H){G+=H._area});var F=y(G/E),C=B.top,z=0;for(var A=0;A<x.length;A++){x[A].coord={height:F,width:y(x[A]._area/F),top:C,left:B.left+z};z+=x[A].coord.width}var D={height:B.height-F,width:B.width,top:B.top+F,left:B.left};D.dim=Math.min(D.width,D.height);if(D.dim!=D.width){this.layout.change()}return D}});TM.Strip=new o({Implements:[TM,TM.Area],compute:function(F,C){var x=F.children,z=this.config;if(x.length>0){this.processChildrenLayout(F,x,C);for(var B=0;B<x.length;B++){var A=x[B].coord,D=z.offset,E=A.height-(z.titleHeight+D),y=A.width-D;C={width:y,height:E,top:0,left:0};this.compute(x[B],C)}}},processChildrenLayout:function(A,z,E){var B=E.width*E.height;var C=parseFloat(A.data.$area);g(z,function(F){F._area=B*parseFloat(F.data.$area)/C});var y=(this.layout.horizontal())?E.width:E.height;var D=[z[0]];var x=z.slice(1);this.stripify(x,D,y,E)},stripify:function(y,A,x,z){this.computeDim(y,A,x,z,this.avgAspectRatio)},layoutRow:function(y,x,z){if(this.layout.horizontal()){return this.layoutH(y,x,z)}else{return this.layoutV(y,x,z)}},layoutV:function(x,F,C){var G=0,z=function(H){return H};g(x,function(H){G+=H._area});var y=z(G/F),D=0;for(var A=0;A<x.length;A++){var B=z(x[A]._area/y);x[A].coord={height:B,width:y,top:C.top+(F-B-D),left:C.left};D+=B}var E={height:C.height,width:C.width-y,top:C.top,left:C.left+y,dim:F};return E},layoutH:function(x,E,B){var G=0,y=function(H){return H};g(x,function(H){G+=H._area});var F=y(G/E),C=B.height-F,z=0;for(var A=0;A<x.length;A++){x[A].coord={height:F,width:y(x[A]._area/F),top:C,left:B.left+z};z+=x[A].coord.width}var D={height:B.height-F,width:B.width,top:B.top,left:B.left,dim:E};return D}})})();
\ No newline at end of file
+/*
+Copyright (c) 2011 Sencha Inc. - Author: Nicolas Garcia Belmonte (http://philogb.github.com/)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+ */
+ (function(){window.$jit=function(x){x=x||window;for(var y in $jit){if($jit[y].$extend){x[y]=$jit[y]}}};$jit.version="2.0.1";var c=function(w){return document.getElementById(w)};c.empty=function(){};c.extend=function(y,w){for(var x in (w||{})){y[x]=w[x]}return y};c.lambda=function(w){return(typeof w=="function")?w:function(){return w}};c.time=Date.now||function(){return +new Date};c.splat=function(x){var w=c.type(x);return w?((w!="array")?[x]:x):[]};c.type=function(x){var w=c.type.s.call(x).match(/^\[object\s(.*)\]$/)[1].toLowerCase();if(w!="object"){return w}if(x&&x.$$family){return x.$$family}return(x&&x.nodeName&&x.nodeType==1)?"element":w};c.type.s=Object.prototype.toString;c.each=function(B,A){var z=c.type(B);if(z=="object"){for(var y in B){A(B[y],y)}}else{for(var x=0,w=B.length;x<w;x++){A(B[x],x)}}};c.indexOf=function(z,y){if(Array.indexOf){return z.indexOf(y)}for(var x=0,w=z.length;x<w;x++){if(z[x]===y){return x}}return -1};c.map=function(y,x){var w=[];c.each(y,function(A,z){w.push(x(A,z))});return w};c.reduce=function(A,y,x){var w=A.length;if(w==0){return x}var z=arguments.length==3?x:A[--w];while(w--){z=y(z,A[w])}return z};c.merge=function(){var A={};for(var z=0,w=arguments.length;z<w;z++){var x=arguments[z];if(c.type(x)!="object"){continue}for(var y in x){var C=x[y],B=A[y];A[y]=(B&&c.type(C)=="object"&&c.type(B)=="object")?c.merge(B,C):c.unlink(C)}}return A};c.unlink=function(y){var x;switch(c.type(y)){case"object":x={};for(var A in y){x[A]=c.unlink(y[A])}break;case"array":x=[];for(var z=0,w=y.length;z<w;z++){x[z]=c.unlink(y[z])}break;default:return y}return x};c.zip=function(){if(arguments.length===0){return[]}for(var y=0,x=[],w=arguments.length,B=arguments[0].length;y<B;y++){for(var z=0,A=[];z<w;z++){A.push(arguments[z][y])}x.push(A)}return x};c.rgbToHex=function(A,z){if(A.length<3){return null}if(A.length==4&&A[3]==0&&!z){return"transparent"}var x=[];for(var w=0;w<3;w++){var y=(A[w]-0).toString(16);x.push(y.length==1?"0"+y:y)}return z?x:"#"+x.join("")};c.hexToRgb=function(y){if(y.length!=7){y=y.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);y.shift();if(y.length!=3){return null}var w=[];for(var x=0;x<3;x++){var z=y[x];if(z.length==1){z+=z}w.push(parseInt(z,16))}return w}else{y=parseInt(y.slice(1),16);return[y>>16,y>>8&255,y&255]}};c.destroy=function(w){c.clean(w);if(w.parentNode){w.parentNode.removeChild(w)}if(w.clearAttributes){w.clearAttributes()}};c.clean=function(z){for(var y=z.childNodes,x=0,w=y.length;x<w;x++){c.destroy(y[x])}};c.addEvent=function(y,x,w){if(y.addEventListener){y.addEventListener(x,w,false)}else{y.attachEvent("on"+x,w)}};c.addEvents=function(x,y){for(var w in y){c.addEvent(x,w,y[w])}};c.hasClass=function(x,w){return(" "+x.className+" ").indexOf(" "+w+" ")>-1};c.addClass=function(x,w){if(!c.hasClass(x,w)){x.className=(x.className+" "+w)}};c.removeClass=function(x,w){x.className=x.className.replace(new RegExp("(^|\\s)"+w+"(?:\\s|$)"),"$1")};c.getPos=function(y){var B=A(y);var w=z(y);return{x:B.x-w.x,y:B.y-w.y};function A(D){var C={x:0,y:0};while(D&&!x(D)){C.x+=D.offsetLeft;C.y+=D.offsetTop;D=D.offsetParent}return C}function z(D){var C={x:0,y:0};while(D&&!x(D)){C.x+=D.scrollLeft;C.y+=D.scrollTop;D=D.parentNode}return C}function x(C){return(/^(?:body|html)$/i).test(C.tagName)}};c.event={get:function(x,w){w=w||window;return x||w.event},getWheel:function(w){return w.wheelDelta?w.wheelDelta/120:-(w.detail||0)/3},isRightClick:function(w){return(w.which==3||w.button==2)},getPos:function(z,y){y=y||window;z=z||y.event;var x=y.document;x=x.documentElement||x.body;if(z.touches&&z.touches.length){z=z.touches[0]}var w={x:z.pageX||(z.clientX+x.scrollLeft),y:z.pageY||(z.clientY+x.scrollTop)};return w},stop:function(w){if(w.stopPropagation){w.stopPropagation()}w.cancelBubble=true;if(w.preventDefault){w.preventDefault()}else{w.returnValue=false}}};$jit.util=$jit.id=c;var q=function(x){x=x||{};var w=function(){for(var A in this){if(typeof this[A]!="function"){this[A]=c.unlink(this[A])}}this.constructor=w;if(q.prototyping){return this}var z=this.initialize?this.initialize.apply(this,arguments):this;this.$$family="class";return z};for(var y in q.Mutators){if(!x[y]){continue}x=q.Mutators[y](x,x[y]);delete x[y]}c.extend(w,this);w.constructor=q;w.prototype=x;return w};q.Mutators={Implements:function(w,x){c.each(c.splat(x),function(z){q.prototyping=z;var y=(typeof z=="function")?new z:z;for(var A in y){if(!(A in w)){w[A]=y[A]}}delete q.prototyping});return w}};c.extend(q,{inherit:function(w,z){for(var y in z){var x=z[y];var B=w[y];var A=c.type(x);if(B&&A=="function"){if(x!=B){q.override(w,y,x)}}else{if(A=="object"){w[y]=c.merge(B,x)}else{w[y]=x}}}return w},override:function(x,w,A){var z=q.prototyping;if(z&&x[w]!=z[w]){z=null}var y=function(){var B=this.parent;this.parent=z?z[w]:x[w];var C=A.apply(this,arguments);this.parent=B;return C};x[w]=y}});q.prototype.implement=function(){var w=this.prototype;c.each(Array.prototype.slice.call(arguments||[]),function(x){q.inherit(w,x)});return this};$jit.Class=q;$jit.json={prune:function(x,w){this.each(x,function(z,y){if(y==w&&z.children){delete z.children;z.children=[]}})},getParent:function(w,A){if(w.id==A){return false}var z=w.children;if(z&&z.length>0){for(var y=0;y<z.length;y++){if(z[y].id==A){return w}else{var x=this.getParent(z[y],A);if(x){return x}}}}return false},getSubtree:function(w,A){if(w.id==A){return w}for(var y=0,z=w.children;z&&y<z.length;y++){var x=this.getSubtree(z[y],A);if(x!=null){return x}}return null},eachLevel:function(w,B,y,A){if(B<=y){A(w,B);if(!w.children){return}for(var x=0,z=w.children;x<z.length;x++){this.eachLevel(z[x],B+1,y,A)}}},each:function(w,x){this.eachLevel(w,0,Number.MAX_VALUE,x)}};$jit.Trans={$extend:true,linear:function(w){return w}};var i=$jit.Trans;(function(){var w=function(z,y){y=c.splat(y);return c.extend(z,{easeIn:function(A){return z(A,y)},easeOut:function(A){return 1-z(1-A,y)},easeInOut:function(A){return(A<=0.5)?z(2*A,y)/2:(2-z(2*(1-A),y))/2}})};var x={Pow:function(z,y){return Math.pow(z,y[0]||6)},Expo:function(y){return Math.pow(2,8*(y-1))},Circ:function(y){return 1-Math.sin(Math.acos(y))},Sine:function(y){return 1-Math.sin((1-y)*Math.PI/2)},Back:function(z,y){y=y[0]||1.618;return Math.pow(z,2)*((y+1)*z-y)},Bounce:function(B){var A;for(var z=0,y=1;1;z+=y,y/=2){if(B>=(7-4*z)/11){A=y*y-Math.pow((11-6*z-11*B)/4,2);break}}return A},Elastic:function(z,y){return Math.pow(2,10*--z)*Math.cos(20*z*Math.PI*(y[0]||1)/3)}};c.each(x,function(z,y){i[y]=w(z)});c.each(["Quad","Cubic","Quart","Quint"],function(z,y){i[z]=w(function(A){return Math.pow(A,[y+2])})})})();var u=new q({initialize:function(w){this.setOptions(w)},setOptions:function(w){var x={duration:2500,fps:40,transition:i.Quart.easeInOut,compute:c.empty,complete:c.empty,link:"ignore"};this.opt=c.merge(x,w||{});return this},step:function(){var x=c.time(),w=this.opt;if(x<this.time+w.duration){var y=w.transition((x-this.time)/w.duration);w.compute(y)}else{this.timer=clearInterval(this.timer);w.compute(1);w.complete()}},start:function(){if(!this.check()){return this}this.time=0;this.startTimer();return this},startTimer:function(){var w=this,x=this.opt.fps;if(this.timer){return false}this.time=c.time()-this.time;this.timer=setInterval((function(){w.step()}),Math.round(1000/x));return true},pause:function(){this.stopTimer();return this},resume:function(){this.startTimer();return this},stopTimer:function(){if(!this.timer){return false}this.time=c.time()-this.time;this.timer=clearInterval(this.timer);return true},check:function(){if(!this.timer){return true}if(this.opt.link=="cancel"){this.stopTimer();return true}return false}});var n=function(){var y=arguments;for(var A=0,w=y.length,x={};A<w;A++){var z=n[y[A]];if(z.$extend){c.extend(x,z)}else{x[y[A]]=z}}return x};n.AreaChart={$extend:true,animate:true,labelOffset:3,type:"stacked",Tips:{enable:false,onShow:c.empty,onHide:c.empty},Events:{enable:false,onClick:c.empty},selectOnHover:true,showAggregates:true,showLabels:true,filterOnClick:false,restoreOnRightClick:false};n.Margin={$extend:false,top:0,left:0,right:0,bottom:0};n.Canvas={$extend:true,injectInto:"id",type:"2D",width:false,height:false,useCanvas:false,withLabels:true,background:false,Scene:{Lighting:{enable:false,ambient:[1,1,1],directional:{direction:{x:-100,y:-100,z:-100},color:[0.5,0.3,0.1]}}}};n.Tree={$extend:true,orientation:"left",subtreeOffset:8,siblingOffset:5,indent:10,multitree:false,align:"center"};n.Node={$extend:false,overridable:false,type:"circle",color:"#ccb",alpha:1,dim:3,height:20,width:90,autoHeight:false,autoWidth:false,lineWidth:1,transform:true,align:"center",angularWidth:1,span:1,CanvasStyles:{}};n.Edge={$extend:false,overridable:false,type:"line",color:"#ccb",lineWidth:1,dim:15,alpha:1,epsilon:7,CanvasStyles:{}};n.Fx={$extend:true,fps:40,duration:2500,transition:$jit.Trans.Quart.easeInOut,clearCanvas:true};n.Label={$extend:false,overridable:false,type:"HTML",style:" ",size:10,family:"sans-serif",textAlign:"center",textBaseline:"alphabetic",color:"#fff"};n.Tips={$extend:false,enable:false,type:"auto",offsetX:20,offsetY:20,force:false,onShow:c.empty,onHide:c.empty};n.NodeStyles={$extend:false,enable:false,type:"auto",stylesHover:false,stylesClick:false};n.Events={$extend:false,enable:false,enableForEdges:false,type:"auto",onClick:c.empty,onRightClick:c.empty,onMouseMove:c.empty,onMouseEnter:c.empty,onMouseLeave:c.empty,onDragStart:c.empty,onDragMove:c.empty,onDragCancel:c.empty,onDragEnd:c.empty,onTouchStart:c.empty,onTouchMove:c.empty,onTouchEnd:c.empty,onMouseWheel:c.empty};n.Navigation={$extend:false,enable:false,type:"auto",panning:false,zooming:false};n.Controller={$extend:true,onBeforeCompute:c.empty,onAfterCompute:c.empty,onCreateLabel:c.empty,onPlaceLabel:c.empty,onComplete:c.empty,onBeforePlotLine:c.empty,onAfterPlotLine:c.empty,onBeforePlotNode:c.empty,onAfterPlotNode:c.empty,request:false};var t={initialize:function(y,w){this.viz=w;this.canvas=w.canvas;this.config=w.config[y];this.nodeTypes=w.fx.nodeTypes;var x=this.config.type;this.dom=x=="auto"?(w.config.Label.type!="Native"):(x!="Native");this.labelContainer=this.dom&&w.labels.getLabelContainer();this.isEnabled()&&this.initializePost()},initializePost:c.empty,setAsProperty:c.lambda(false),isEnabled:function(){return this.config.enable},isLabel:function(B,A,z){B=c.event.get(B,A);var w=this.labelContainer,y=B.target||B.srcElement,x=B.relatedTarget;if(z){return x&&x==this.viz.canvas.getCtx().canvas&&!!y&&this.isDescendantOf(y,w)}else{return this.isDescendantOf(y,w)}},isDescendantOf:function(x,w){while(x&&x.parentNode){if(x.parentNode==w){return x}x=x.parentNode}return false}};var h={onMouseUp:c.empty,onMouseDown:c.empty,onMouseMove:c.empty,onMouseOver:c.empty,onMouseOut:c.empty,onMouseWheel:c.empty,onTouchStart:c.empty,onTouchMove:c.empty,onTouchEnd:c.empty,onTouchCancel:c.empty};var s=new q({initialize:function(w){this.viz=w;this.canvas=w.canvas;this.node=false;this.edge=false;this.registeredObjects=[];this.attachEvents()},attachEvents:function(){var y=this.canvas.getElement(),x=this;y.oncontextmenu=c.lambda(false);c.addEvents(y,{mouseup:function(B,A){var z=c.event.get(B,A);x.handleEvent("MouseUp",B,A,x.makeEventObject(B,A),c.event.isRightClick(z))},mousedown:function(B,A){var z=c.event.get(B,A);x.handleEvent("MouseDown",B,A,x.makeEventObject(B,A),c.event.isRightClick(z))},mousemove:function(A,z){x.handleEvent("MouseMove",A,z,x.makeEventObject(A,z))},mouseover:function(A,z){x.handleEvent("MouseOver",A,z,x.makeEventObject(A,z))},mouseout:function(A,z){x.handleEvent("MouseOut",A,z,x.makeEventObject(A,z))},touchstart:function(A,z){x.handleEvent("TouchStart",A,z,x.makeEventObject(A,z))},touchmove:function(A,z){x.handleEvent("TouchMove",A,z,x.makeEventObject(A,z))},touchend:function(A,z){x.handleEvent("TouchEnd",A,z,x.makeEventObject(A,z))}});var w=function(C,B){var A=c.event.get(C,B);var z=c.event.getWheel(A);x.handleEvent("MouseWheel",C,B,z)};if(!document.getBoxObjectFor&&window.mozInnerScreenX==null){c.addEvent(y,"mousewheel",w)}else{y.addEventListener("DOMMouseScroll",w,false)}},register:function(w){this.registeredObjects.push(w)},handleEvent:function(){var x=Array.prototype.slice.call(arguments),A=x.shift();for(var z=0,y=this.registeredObjects,w=y.length;z<w;z++){y[z]["on"+A].apply(y[z],x)}},makeEventObject:function(C,B){var z=this,A=this.viz.graph,y=this.viz.fx,x=y.nodeTypes,w=y.edgeTypes;return{pos:false,node:false,edge:false,contains:false,getNodeCalled:false,getEdgeCalled:false,getPos:function(){var F=z.viz.canvas,G=F.getSize(),H=F.getPos(),E=F.translateOffsetX,D=F.translateOffsetY,K=F.scaleOffsetX,I=F.scaleOffsetY,J=c.event.getPos(C,B);this.pos={x:(J.x-H.x-G.width/2-E)*1/K,y:(J.y-H.y-G.height/2-D)*1/I};return this.pos},getNode:function(){if(this.getNodeCalled){return this.node}this.getNodeCalled=true;for(var G in A.nodes){var F=A.nodes[G],E=F&&x[F.getData("type")],D=E&&E.contains&&E.contains.call(y,F,this.getPos());if(D){this.contains=D;return z.node=this.node=F}}return z.node=this.node=false},getEdge:function(){if(this.getEdgeCalled){return this.edge}this.getEdgeCalled=true;var F={};for(var J in A.edges){var H=A.edges[J];F[J]=true;for(var I in H){if(I in F){continue}var G=H[I],E=G&&w[G.getData("type")],D=E&&E.contains&&E.contains.call(y,G,this.getPos());if(D){this.contains=D;return z.edge=this.edge=G}}}return z.edge=this.edge=false},getContains:function(){if(this.getNodeCalled){return this.contains}this.getNode();return this.contains}}}});var o={initializeExtras:function(){var x=new s(this),w=this;c.each(["NodeStyles","Tips","Navigation","Events"],function(y){var z=new o.Classes[y](y,w);if(z.isEnabled()){x.register(z)}if(z.setAsProperty()){w[y.toLowerCase()]=z}})}};o.Classes={};o.Classes.Events=new q({Implements:[t,h],initializePost:function(){this.fx=this.viz.fx;this.ntypes=this.viz.fx.nodeTypes;this.etypes=this.viz.fx.edgeTypes;this.hovered=false;this.pressed=false;this.touched=false;this.touchMoved=false;this.moved=false},setAsProperty:c.lambda(true),onMouseUp:function(A,z,x,y){var w=c.event.get(A,z);if(!this.moved){if(y){this.config.onRightClick(this.hovered,x,w)}else{this.config.onClick(this.pressed,x,w)}}if(this.pressed){if(this.moved){this.config.onDragEnd(this.pressed,x,w)}else{this.config.onDragCancel(this.pressed,x,w)}this.pressed=this.moved=false}},onMouseOut:function(B,A,z){var x=c.event.get(B,A),y;if(this.dom&&(y=this.isLabel(B,A,true))){this.config.onMouseLeave(this.viz.graph.getNode(y.id),z,x);this.hovered=false;return}var w=x.relatedTarget,C=this.canvas.getElement();while(w&&w.parentNode){if(C==w.parentNode){return}w=w.parentNode}if(this.hovered){this.config.onMouseLeave(this.hovered,z,x);this.hovered=false}},onMouseOver:function(A,z,y){var w=c.event.get(A,z),x;if(this.dom&&(x=this.isLabel(A,z,true))){this.hovered=this.viz.graph.getNode(x.id);this.config.onMouseEnter(this.hovered,y,w)}},onMouseMove:function(C,B,A){var x,w=c.event.get(C,B);if(this.pressed){this.moved=true;this.config.onDragMove(this.pressed,A,w);return}if(this.dom){this.config.onMouseMove(this.hovered,A,w)}else{if(this.hovered){var D=this.hovered;var z=D.nodeFrom?this.etypes[D.getData("type")]:this.ntypes[D.getData("type")];var y=z&&z.contains&&z.contains.call(this.fx,D,A.getPos());if(y){this.config.onMouseMove(D,A,w);return}else{this.config.onMouseLeave(D,A,w);this.hovered=false}}if(this.hovered=(A.getNode()||(this.config.enableForEdges&&A.getEdge()))){this.config.onMouseEnter(this.hovered,A,w)}else{this.config.onMouseMove(false,A,w)}}},onMouseWheel:function(x,w,y){this.config.onMouseWheel(y,c.event.get(x,w))},onMouseDown:function(A,z,y){var w=c.event.get(A,z),x;if(this.dom){if(x=this.isLabel(A,z)){this.pressed=this.viz.graph.getNode(x.id)}}else{this.pressed=y.getNode()||(this.config.enableForEdges&&y.getEdge())}this.pressed&&this.config.onDragStart(this.pressed,y,w)},onTouchStart:function(A,z,y){var w=c.event.get(A,z),x;if(this.dom&&(x=this.isLabel(A,z))){this.touched=this.viz.graph.getNode(x.id)}else{this.touched=y.getNode()||(this.config.enableForEdges&&y.getEdge())}this.touched&&this.config.onTouchStart(this.touched,y,w)},onTouchMove:function(z,y,x){var w=c.event.get(z,y);if(this.touched){this.touchMoved=true;this.config.onTouchMove(this.touched,x,w)}},onTouchEnd:function(z,y,x){var w=c.event.get(z,y);if(this.touched){if(this.touchMoved){this.config.onTouchEnd(this.touched,x,w)}else{this.config.onTouchCancel(this.touched,x,w)}this.touched=this.touchMoved=false}}});o.Classes.Tips=new q({Implements:[t,h],initializePost:function(){if(document.body){var w=c("_tooltip")||document.createElement("div");w.id="_tooltip";w.className="tip";c.extend(w.style,{position:"absolute",display:"none",zIndex:13000});document.body.appendChild(w);this.tip=w;this.node=false}},setAsProperty:c.lambda(true),onMouseOut:function(z,y){var x=c.event.get(z,y);if(this.dom&&this.isLabel(z,y,true)){this.hide(true);return}var w=z.relatedTarget,A=this.canvas.getElement();while(w&&w.parentNode){if(A==w.parentNode){return}w=w.parentNode}this.hide(false)},onMouseOver:function(y,x){var w;if(this.dom&&(w=this.isLabel(y,x,false))){this.node=this.viz.graph.getNode(w.id);this.config.onShow(this.tip,this.node,w)}},onMouseMove:function(z,y,w){if(this.dom&&this.isLabel(z,y)){this.setTooltipPosition(c.event.getPos(z,y))}if(!this.dom){var x=w.getNode();if(!x){this.hide(true);return}if(this.config.force||!this.node||this.node.id!=x.id){this.node=x;this.config.onShow(this.tip,x,w.getContains())}this.setTooltipPosition(c.event.getPos(z,y))}},setTooltipPosition:function(F){var B=this.tip,A=B.style,z=this.config;A.display="";var D={height:document.body.clientHeight,width:document.body.clientWidth};var C={width:B.offsetWidth,height:B.offsetHeight};var w=z.offsetX,E=z.offsetY;A.top=((F.y+E+C.height>D.height)?(F.y-C.height-E):F.y+E)+"px";A.left=((F.x+C.width+w>D.width)?(F.x-C.width-w):F.x+w)+"px"},hide:function(w){this.tip.style.display="none";w&&this.config.onHide()}});o.Classes.NodeStyles=new q({Implements:[t,h],initializePost:function(){this.fx=this.viz.fx;this.types=this.viz.fx.nodeTypes;this.nStyles=this.config;this.nodeStylesOnHover=this.nStyles.stylesHover;this.nodeStylesOnClick=this.nStyles.stylesClick;this.hoveredNode=false;this.fx.nodeFxAnimation=new u();this.down=false;this.move=false},onMouseOut:function(y,x){this.down=this.move=false;if(!this.hoveredNode){return}if(this.dom&&this.isLabel(y,x,true)){this.toggleStylesOnHover(this.hoveredNode,false)}var w=y.relatedTarget,z=this.canvas.getElement();while(w&&w.parentNode){if(z==w.parentNode){return}w=w.parentNode}this.toggleStylesOnHover(this.hoveredNode,false);this.hoveredNode=false},onMouseOver:function(z,y){var w;if(this.dom&&(w=this.isLabel(z,y,true))){var x=this.viz.graph.getNode(w.id);if(x.selected){return}this.hoveredNode=x;this.toggleStylesOnHover(this.hoveredNode,true)}},onMouseDown:function(A,z,x,y){if(y){return}var w;if(this.dom&&(w=this.isLabel(A,z))){this.down=this.viz.graph.getNode(w.id)}else{if(!this.dom){this.down=x.getNode()}}this.move=false},onMouseUp:function(z,y,w,x){if(x){return}if(!this.move){this.onClick(w.getNode())}this.down=this.move=false},getRestoredStyles:function(x,w){var z={},y=this["nodeStylesOn"+w];for(var A in y){z[A]=x.styles["$"+A]}return z},toggleStylesOnHover:function(w,x){if(this.nodeStylesOnHover){this.toggleStylesOn("Hover",w,x)}},toggleStylesOnClick:function(w,x){if(this.nodeStylesOnClick){this.toggleStylesOn("Click",w,x)}},toggleStylesOn:function(A,w,C){var D=this.viz;var B=this.nStyles;if(C){var z=this;if(!w.styles){w.styles=c.merge(w.data,{})}for(var E in this["nodeStylesOn"+A]){var x="$"+E;if(!(x in w.styles)){w.styles[x]=w.getData(E)}}D.fx.nodeFx(c.extend({elements:{id:w.id,properties:z["nodeStylesOn"+A]},transition:i.Quart.easeOut,duration:300,fps:40},this.config))}else{var y=this.getRestoredStyles(w,A);D.fx.nodeFx(c.extend({elements:{id:w.id,properties:y},transition:i.Quart.easeOut,duration:300,fps:40},this.config))}},onClick:function(w){if(!w){return}var x=this.nodeStylesOnClick;if(!x){return}if(w.selected){this.toggleStylesOnClick(w,false);delete w.selected}else{this.viz.graph.eachNode(function(z){if(z.selected){for(var y in x){z.setData(y,z.styles["$"+y],"end")}delete z.selected}});this.toggleStylesOnClick(w,true);w.selected=true;delete w.hovered;this.hoveredNode=false}},onMouseMove:function(C,B,z){if(this.down){this.move=true}if(this.dom&&this.isLabel(C,B)){return}var A=this.nodeStylesOnHover;if(!A){return}if(!this.dom){if(this.hoveredNode){var x=this.types[this.hoveredNode.getData("type")];var w=x&&x.contains&&x.contains.call(this.fx,this.hoveredNode,z.getPos());if(w){return}}var y=z.getNode();if(!this.hoveredNode&&!y){return}if(y.hovered){return}if(y&&!y.selected){this.fx.nodeFxAnimation.stopTimer();this.viz.graph.eachNode(function(E){if(E.hovered&&!E.selected){for(var D in A){E.setData(D,E.styles["$"+D],"end")}delete E.hovered}});y.hovered=true;this.hoveredNode=y;this.toggleStylesOnHover(y,true)}else{if(this.hoveredNode&&!this.hoveredNode.selected){this.fx.nodeFxAnimation.stopTimer();this.toggleStylesOnHover(this.hoveredNode,false);delete this.hoveredNode.hovered;this.hoveredNode=false}}}}});o.Classes.Navigation=new q({Implements:[t,h],initializePost:function(){this.pos=false;this.pressed=false},onMouseWheel:function(z,y,w){if(!this.config.zooming){return}c.event.stop(c.event.get(z,y));var A=this.config.zooming/1000,x=1+w*A;this.canvas.scale(x,x)},onMouseDown:function(B,A,z){if(!this.config.panning){return}if(this.config.panning=="avoid nodes"&&(this.dom?this.isLabel(B,A):z.getNode())){return}this.pressed=true;this.pos=z.getPos();var y=this.canvas,x=y.translateOffsetX,w=y.translateOffsetY,D=y.scaleOffsetX,C=y.scaleOffsetY;this.pos.x*=D;this.pos.x+=x;this.pos.y*=C;this.pos.y+=w},onMouseMove:function(D,C,F){if(!this.config.panning){return}if(!this.pressed){return}if(this.config.panning=="avoid nodes"&&(this.dom?this.isLabel(D,C):F.getNode())){return}var B=this.pos,E=F.getPos(),z=this.canvas,A=z.translateOffsetX,w=z.translateOffsetY,J=z.scaleOffsetX,H=z.scaleOffsetY;E.x*=J;E.y*=H;E.x+=A;E.y+=w;var I=E.x-B.x,G=E.y-B.y;this.pos=E;this.canvas.translate(I*1/J,G*1/H)},onMouseUp:function(z,y,x,w){if(!this.config.panning){return}this.pressed=false}});var l;(function(){var w=typeof HTMLCanvasElement,y=(w=="object"||w=="function");function x(z,A){var B=document.createElement(z);for(var C in A){if(typeof A[C]=="object"){c.extend(B[C],A[C])}else{B[C]=A[C]}}if(z=="canvas"&&!y&&G_vmlCanvasManager){B=G_vmlCanvasManager.initElement(document.body.appendChild(B))}return B}$jit.Canvas=l=new q({canvases:[],pos:false,element:false,labelContainer:false,translateOffsetX:0,translateOffsetY:0,scaleOffsetX:1,scaleOffsetY:1,initialize:function(L,E){this.viz=L;this.opt=this.config=E;var B=c.type(E.injectInto)=="string"?E.injectInto:E.injectInto.id,K=E.type,C=B+"-label",z=c(B),D=E.width||z.offsetWidth,M=E.height||z.offsetHeight;this.id=B;var F={injectInto:B,width:D,height:M};this.element=x("div",{id:B+"-canvaswidget",style:{position:"relative",width:D+"px",height:M+"px"}});this.labelContainer=this.createLabelContainer(E.Label.type,C,F);this.canvases.push(new l.Base[K]({config:c.extend({idSuffix:"-canvas"},F),plot:function(N){L.fx.plot()},resize:function(){L.refresh()}}));var G=E.background;if(G){var J=new l.Background[G.type](L,c.extend(G,F));this.canvases.push(new l.Base[K](J))}var I=this.canvases.length;while(I--){this.element.appendChild(this.canvases[I].canvas);if(I>0){this.canvases[I].plot()}}this.element.appendChild(this.labelContainer);z.appendChild(this.element);var A=null,H=this;c.addEvent(window,"scroll",function(){clearTimeout(A);A=setTimeout(function(){H.getPos(true)},500)})},getCtx:function(z){return this.canvases[z||0].getCtx()},getConfig:function(){return this.opt},getElement:function(){return this.element},getSize:function(z){return this.canvases[z||0].getSize()},resize:function(D,z){this.getPos(true);this.translateOffsetX=this.translateOffsetY=0;this.scaleOffsetX=this.scaleOffsetY=1;for(var B=0,A=this.canvases.length;B<A;B++){this.canvases[B].resize(D,z)}var C=this.element.style;C.width=D+"px";C.height=z+"px";if(this.labelContainer){this.labelContainer.style.width=D+"px"}},translate:function(z,D,C){this.translateOffsetX+=z*this.scaleOffsetX;this.translateOffsetY+=D*this.scaleOffsetY;for(var B=0,A=this.canvases.length;B<A;B++){this.canvases[B].translate(z,D,C)}},scale:function(E,B,C){var F=this.scaleOffsetX*E,D=this.scaleOffsetY*B;var H=this.translateOffsetX*(E-1)/F,G=this.translateOffsetY*(B-1)/D;this.scaleOffsetX=F;this.scaleOffsetY=D;for(var A=0,z=this.canvases.length;A<z;A++){this.canvases[A].scale(E,B,true)}this.translate(H,G,false)},getPos:function(z){if(z||!this.pos){return this.pos=c.getPos(this.getElement())}return this.pos},clear:function(z){this.canvases[z||0].clear()},path:function(A,B){var z=this.canvases[0].getCtx();z.beginPath();B(z);z[A]();z.closePath()},createLabelContainer:function(B,F,E){var D="http://www.w3.org/2000/svg";if(B=="HTML"||B=="Native"){return x("div",{id:F,style:{overflow:"visible",position:"absolute",top:0,left:0,width:E.width+"px",height:0}})}else{if(B=="SVG"){var C=document.createElementNS(D,"svg:svg");C.setAttribute("width",E.width);C.setAttribute("height",E.height);var A=C.style;A.position="absolute";A.left=A.top="0px";var z=document.createElementNS(D,"svg:g");z.setAttribute("width",E.width);z.setAttribute("height",E.height);z.setAttribute("x",0);z.setAttribute("y",0);z.setAttribute("id",F);C.appendChild(z);return C}}}});l.Base={};l.Base["2D"]=new q({translateOffsetX:0,translateOffsetY:0,scaleOffsetX:1,scaleOffsetY:1,initialize:function(z){this.viz=z;this.opt=z.config;this.size=false;this.createCanvas();this.translateToCenter()},createCanvas:function(){var A=this.opt,B=A.width,z=A.height;this.canvas=x("canvas",{id:A.injectInto+A.idSuffix,width:B,height:z,style:{position:"absolute",top:0,left:0,width:B+"px",height:z+"px"}})},getCtx:function(){if(!this.ctx){return this.ctx=this.canvas.getContext("2d")}return this.ctx},getSize:function(){if(this.size){return this.size}var z=this.canvas;return this.size={width:z.width,height:z.height}},translateToCenter:function(C){var A=this.getSize(),B=C?(A.width-C.width-this.translateOffsetX*2):A.width;height=C?(A.height-C.height-this.translateOffsetY*2):A.height;var z=this.getCtx();C&&z.scale(1/this.scaleOffsetX,1/this.scaleOffsetY);z.translate(B/2,height/2)},resize:function(C,z){var B=this.getSize(),A=this.canvas,D=A.style;this.size=false;A.width=C;A.height=z;D.width=C+"px";D.height=z+"px";if(!y){this.translateToCenter(B)}else{this.translateToCenter()}this.translateOffsetX=this.translateOffsetY=0;this.scaleOffsetX=this.scaleOffsetY=1;this.clear();this.viz.resize(C,z,this)},translate:function(z,D,A){var C=this.scaleOffsetX,B=this.scaleOffsetY;this.translateOffsetX+=z*C;this.translateOffsetY+=D*B;this.getCtx().translate(z,D);!A&&this.plot()},scale:function(z,B,A){this.scaleOffsetX*=z;this.scaleOffsetY*=B;this.getCtx().scale(z,B);!A&&this.plot()},clear:function(){var B=this.getSize(),A=this.translateOffsetX,z=this.translateOffsetY,D=this.scaleOffsetX,C=this.scaleOffsetY;this.getCtx().clearRect((-B.width/2-A)*1/D,(-B.height/2-z)*1/C,B.width*1/D,B.height*1/C)},plot:function(){this.clear();this.viz.plot(this)}});l.Background={};l.Background.Circles=new q({initialize:function(z,A){this.viz=z;this.config=c.merge({idSuffix:"-bkcanvas",levelDistance:100,numberOfCircles:6,CanvasStyles:{},offset:0},A)},resize:function(A,z,B){this.plot(B)},plot:function(z){var A=z.canvas,G=z.getCtx(),D=this.config,F=D.CanvasStyles;for(var H in F){G[H]=F[H]}var B=D.numberOfCircles,E=D.levelDistance;for(var C=1;C<=B;C++){G.beginPath();G.arc(0,0,E*C,0,2*Math.PI,false);G.stroke();G.closePath()}}})})();var b=function(x,w){this.theta=x||0;this.rho=w||0};$jit.Polar=b;b.prototype={getc:function(w){return this.toComplex(w)},getp:function(){return this},set:function(w){w=w.getp();this.theta=w.theta;this.rho=w.rho},setc:function(w,z){this.rho=Math.sqrt(w*w+z*z);this.theta=Math.atan2(z,w);if(this.theta<0){this.theta+=Math.PI*2}},setp:function(x,w){this.theta=x;this.rho=w},clone:function(){return new b(this.theta,this.rho)},toComplex:function(A){var w=Math.cos(this.theta)*this.rho;var z=Math.sin(this.theta)*this.rho;if(A){return{x:w,y:z}}return new p(w,z)},add:function(w){return new b(this.theta+w.theta,this.rho+w.rho)},scale:function(w){return new b(this.theta,this.rho*w)},equals:function(w){return this.theta==w.theta&&this.rho==w.rho},$add:function(w){this.theta=this.theta+w.theta;this.rho+=w.rho;return this},$madd:function(w){this.theta=(this.theta+w.theta)%(Math.PI*2);this.rho+=w.rho;return this},$scale:function(w){this.rho*=w;return this},isZero:function(){var x=0.0001,w=Math.abs;return w(this.theta)<x&&w(this.rho)<x},interpolate:function(y,F){var z=Math.PI,C=z*2;var x=function(H){var G=(H<0)?(H%C)+C:H%C;return G};var B=this.theta,E=y.theta;var A,D=Math.abs(B-E);if(D==z){if(B>E){A=x((E+((B-C)-E)*F))}else{A=x((E-C+(B-(E))*F))}}else{if(D>=z){if(B>E){A=x((E+((B-C)-E)*F))}else{A=x((E-C+(B-(E-C))*F))}}else{A=x((E+(B-E)*F))}}var w=(this.rho-y.rho)*F+y.rho;return{theta:A,rho:w}}};var k=function(x,w){return new b(x,w)};b.KER=k(0,0);var p=function(w,z){this.x=w||0;this.y=z||0};$jit.Complex=p;p.prototype={getc:function(){return this},getp:function(w){return this.toPolar(w)},set:function(w){w=w.getc(true);this.x=w.x;this.y=w.y},setc:function(w,z){this.x=w;this.y=z},setp:function(x,w){this.x=Math.cos(x)*w;this.y=Math.sin(x)*w},clone:function(){return new p(this.x,this.y)},toPolar:function(y){var w=this.norm();var x=Math.atan2(this.y,this.x);if(x<0){x+=Math.PI*2}if(y){return{theta:x,rho:w}}return new b(x,w)},norm:function(){return Math.sqrt(this.squaredNorm())},squaredNorm:function(){return this.x*this.x+this.y*this.y},add:function(w){return new p(this.x+w.x,this.y+w.y)},prod:function(w){return new p(this.x*w.x-this.y*w.y,this.y*w.x+this.x*w.y)},conjugate:function(){return new p(this.x,-this.y)},scale:function(w){return new p(this.x*w,this.y*w)},equals:function(w){return this.x==w.x&&this.y==w.y},$add:function(w){this.x+=w.x;this.y+=w.y;return this},$prod:function(A){var w=this.x,z=this.y;this.x=w*A.x-z*A.y;this.y=z*A.x+w*A.y;return this},$conjugate:function(){this.y=-this.y;return this},$scale:function(w){this.x*=w;this.y*=w;return this},$div:function(B){var w=this.x,A=this.y;var z=B.squaredNorm();this.x=w*B.x+A*B.y;this.y=A*B.x-w*B.y;return this.$scale(1/z)},isZero:function(){var x=0.0001,w=Math.abs;return w(this.x)<x&&w(this.y)<x}};var r=function(x,w){return new p(x,w)};p.KER=r(0,0);$jit.Graph=new q({initialize:function(y,x,w,C){var A={klass:p,Node:{}};this.Node=x;this.Edge=w;this.Label=C;this.opt=c.merge(A,y||{});this.nodes={};this.edges={};var z=this;this.nodeList={};for(var B in j){z.nodeList[B]=(function(D){return function(){var E=Array.prototype.slice.call(arguments);z.eachNode(function(F){F[D].apply(F,E)})}})(B)}},getNode:function(w){if(this.hasNode(w)){return this.nodes[w]}return false},get:function(w){return this.getNode(w)},getByName:function(w){for(var y in this.nodes){var x=this.nodes[y];if(x.name==w){return x}}return false},getAdjacence:function(x,w){if(x in this.edges){return this.edges[x][w]}return false},addNode:function(x){if(!this.nodes[x.id]){var w=this.edges[x.id]={};this.nodes[x.id]=new e.Node(c.extend({id:x.id,name:x.name,data:c.merge(x.data||{},{}),adjacencies:w},this.opt.Node),this.opt.klass,this.Node,this.Edge,this.Label)}return this.nodes[x.id]},addAdjacence:function(z,y,x){if(!this.hasNode(z.id)){this.addNode(z)}if(!this.hasNode(y.id)){this.addNode(y)}z=this.nodes[z.id];y=this.nodes[y.id];if(!z.adjacentTo(y)){var A=this.edges[z.id]=this.edges[z.id]||{};var w=this.edges[y.id]=this.edges[y.id]||{};A[y.id]=w[z.id]=new e.Adjacence(z,y,x,this.Edge,this.Label);return A[y.id]}return this.edges[z.id][y.id]},removeNode:function(y){if(this.hasNode(y)){delete this.nodes[y];var x=this.edges[y];for(var w in x){delete this.edges[w][y]}delete this.edges[y]}},removeAdjacence:function(x,w){delete this.edges[x][w];delete this.edges[w][x]},hasNode:function(w){return w in this.nodes},empty:function(){this.nodes={};this.edges={}}});var e=$jit.Graph;var j;(function(){var w=function(D,F,A,C,E){var B;A=A||"current";D="$"+(D?D+"-":"");if(A=="current"){B=this.data}else{if(A=="start"){B=this.startData}else{if(A=="end"){B=this.endData}}}var z=D+F;if(C){return B[z]}if(!this.Config.overridable){return E[F]||0}return(z in B)?B[z]:((z in this.data)?this.data[z]:(E[F]||0))};var y=function(C,D,B,z){z=z||"current";C="$"+(C?C+"-":"");var A;if(z=="current"){A=this.data}else{if(z=="start"){A=this.startData}else{if(z=="end"){A=this.endData}}}A[C+D]=B};var x=function(B,z){B="$"+(B?B+"-":"");var A=this;c.each(z,function(D){var C=B+D;delete A.data[C];delete A.endData[C];delete A.startData[C]})};j={getData:function(B,z,A){return w.call(this,"",B,z,A,this.Config)},setData:function(B,A,z){y.call(this,"",B,A,z)},setDataset:function(C,D){C=c.splat(C);for(var z in D){for(var B=0,E=c.splat(D[z]),A=C.length;B<A;B++){this.setData(z,E[B],C[B])}}},removeData:function(){x.call(this,"",Array.prototype.slice.call(arguments))},getCanvasStyle:function(B,z,A){return w.call(this,"canvas",B,z,A,this.Config.CanvasStyles)},setCanvasStyle:function(B,A,z){y.call(this,"canvas",B,A,z)},setCanvasStyles:function(C,D){C=c.splat(C);for(var z in D){for(var B=0,E=c.splat(D[z]),A=C.length;B<A;B++){this.setCanvasStyle(z,E[B],C[B])}}},removeCanvasStyle:function(){x.call(this,"canvas",Array.prototype.slice.call(arguments))},getLabelData:function(B,z,A){return w.call(this,"label",B,z,A,this.Label)},setLabelData:function(B,A,z){y.call(this,"label",B,A,z)},setLabelDataset:function(C,D){C=c.splat(C);for(var z in D){for(var B=0,E=c.splat(D[z]),A=C.length;B<A;B++){this.setLabelData(z,E[B],C[B])}}},removeLabelData:function(){x.call(this,"label",Array.prototype.slice.call(arguments))}}})();e.Node=new q({initialize:function(z,w,y,x,B){var A={id:"",name:"",data:{},startData:{},endData:{},adjacencies:{},selected:false,drawn:false,exist:false,angleSpan:{begin:0,end:0},pos:new w,startPos:new w,endPos:new w};c.extend(this,c.extend(A,z));this.Config=this.Node=y;this.Edge=x;this.Label=B},adjacentTo:function(w){return w.id in this.adjacencies},getAdjacency:function(w){return this.adjacencies[w]},getPos:function(w){w=w||"current";if(w=="current"){return this.pos}else{if(w=="end"){return this.endPos}else{if(w=="start"){return this.startPos}}}},setPos:function(x,w){w=w||"current";var y;if(w=="current"){y=this.pos}else{if(w=="end"){y=this.endPos}else{if(w=="start"){y=this.startPos}}}y.set(x)}});e.Node.implement(j);e.Adjacence=new q({initialize:function(x,A,y,w,z){this.nodeFrom=x;this.nodeTo=A;this.data=y||{};this.startData={};this.endData={};this.Config=this.Edge=w;this.Label=z}});e.Adjacence.implement(j);e.Util={filter:function(x){if(!x||!(c.type(x)=="string")){return function(){return true}}var w=x.split(" ");return function(z){for(var y=0;y<w.length;y++){if(z[w[y]]){return false}}return true}},getNode:function(w,x){return w.nodes[x]},eachNode:function(A,z,w){var y=this.filter(w);for(var x in A.nodes){if(y(A.nodes[x])){z(A.nodes[x])}}},each:function(y,x,w){this.eachNode(y,x,w)},eachAdjacency:function(B,C,x){var y=B.adjacencies,A=this.filter(x);for(var D in y){var w=y[D];if(A(w)){if(w.nodeFrom!=B){var z=w.nodeFrom;w.nodeFrom=w.nodeTo;w.nodeTo=z}C(w,D)}}},computeLevels:function(C,D,z,y){z=z||0;var A=this.filter(y);this.eachNode(C,function(E){E._flag=false;E._depth=-1},y);var x=C.getNode(D);x._depth=z;var w=[x];while(w.length!=0){var B=w.pop();B._flag=true;this.eachAdjacency(B,function(E){var F=E.nodeTo;if(F._flag==false&&A(F)){if(F._depth<0){F._depth=B._depth+1+z}w.unshift(F)}},y)}},eachBFS:function(B,C,A,x){var y=this.filter(x);this.clean(B);var w=[B.getNode(C)];while(w.length!=0){var z=w.pop();z._flag=true;A(z,z._depth);this.eachAdjacency(z,function(D){var E=D.nodeTo;if(E._flag==false&&y(E)){E._flag=true;w.unshift(E)}},x)}},eachLevel:function(A,E,x,B,z){var D=A._depth,w=this.filter(z),C=this;x=x===false?Number.MAX_VALUE-D:x;(function y(H,F,G){var I=H._depth;if(I>=F&&I<=G&&w(H)){B(H,I)}if(I<G){C.eachAdjacency(H,function(J){var K=J.nodeTo;if(K._depth>I){y(K,F,G)}})}})(A,E+D,x+D)},eachSubgraph:function(x,y,w){this.eachLevel(x,0,false,y,w)},eachSubnode:function(x,y,w){this.eachLevel(x,1,1,y,w)},anySubnode:function(z,y,x){var w=false;y=y||c.lambda(true);var A=c.type(y)=="string"?function(B){return B[y]}:y;this.eachSubnode(z,function(B){if(A(B)){w=true}},x);return w},getSubnodes:function(B,C,w){var y=[],A=this;C=C||0;var z,x;if(c.type(C)=="array"){z=C[0];x=C[1]}else{z=C;x=Number.MAX_VALUE-B._depth}this.eachLevel(B,z,x,function(D){y.push(D)},w);return y},getParents:function(x){var w=[];this.eachAdjacency(x,function(y){var z=y.nodeTo;if(z._depth<x._depth){w.push(z)}});return w},isDescendantOf:function(z,A){if(z.id==A){return true}var y=this.getParents(z),w=false;for(var x=0;!w&&x<y.length;x++){w=w||this.isDescendantOf(y[x],A)}return w},clean:function(w){this.eachNode(w,function(x){x._flag=false})},getClosestNodeToOrigin:function(x,y,w){return this.getClosestNodeToPos(x,b.KER,y,w)},getClosestNodeToPos:function(y,B,A,w){var x=null;A=A||"current";B=B&&B.getc(true)||p.KER;var z=function(D,C){var F=D.x-C.x,E=D.y-C.y;return F*F+E*E};this.eachNode(y,function(C){x=(x==null||z(C.getPos(A).getc(true),B)<z(x.getPos(A).getc(true),B))?C:x},w);return x}};c.each(["get","getNode","each","eachNode","computeLevels","eachBFS","clean","getClosestNodeToPos","getClosestNodeToOrigin"],function(w){e.prototype[w]=function(){return e.Util[w].apply(e.Util,[this].concat(Array.prototype.slice.call(arguments)))}});c.each(["eachAdjacency","eachLevel","eachSubgraph","eachSubnode","anySubnode","getSubnodes","getParents","isDescendantOf"],function(w){e.Node.prototype[w]=function(){return e.Util[w].apply(e.Util,[this].concat(Array.prototype.slice.call(arguments)))}});e.Op={options:{type:"nothing",duration:2000,hideLabels:true,fps:30},initialize:function(w){this.viz=w},removeNode:function(B,z){var w=this.viz;var x=c.merge(this.options,w.controller,z);var D=c.splat(B);var y,A,C;switch(x.type){case"nothing":for(y=0;y<D.length;y++){w.graph.removeNode(D[y])}break;case"replot":this.removeNode(D,{type:"nothing"});w.labels.clearLabels();w.refresh(true);break;case"fade:seq":case"fade":A=this;for(y=0;y<D.length;y++){C=w.graph.getNode(D[y]);C.setData("alpha",0,"end")}w.fx.animate(c.merge(x,{modes:["node-property:alpha"],onComplete:function(){A.removeNode(D,{type:"nothing"});w.labels.clearLabels();w.reposition();w.fx.animate(c.merge(x,{modes:["linear"]}))}}));break;case"fade:con":A=this;for(y=0;y<D.length;y++){C=w.graph.getNode(D[y]);C.setData("alpha",0,"end");C.ignore=true}w.reposition();w.fx.animate(c.merge(x,{modes:["node-property:alpha","linear"],onComplete:function(){A.removeNode(D,{type:"nothing"});x.onComplete&&x.onComplete()}}));break;case"iter":A=this;w.fx.sequence({condition:function(){return D.length!=0},step:function(){A.removeNode(D.shift(),{type:"nothing"});w.labels.clearLabels()},onComplete:function(){x.onComplete&&x.onComplete()},duration:Math.ceil(x.duration/D.length)});break;default:this.doError()}},removeEdge:function(D,B){var w=this.viz;var z=c.merge(this.options,w.controller,B);var y=(c.type(D[0])=="string")?[D]:D;var A,C,x;switch(z.type){case"nothing":for(A=0;A<y.length;A++){w.graph.removeAdjacence(y[A][0],y[A][1])}break;case"replot":this.removeEdge(y,{type:"nothing"});w.refresh(true);break;case"fade:seq":case"fade":C=this;for(A=0;A<y.length;A++){x=w.graph.getAdjacence(y[A][0],y[A][1]);if(x){x.setData("alpha",0,"end")}}w.fx.animate(c.merge(z,{modes:["edge-property:alpha"],onComplete:function(){C.removeEdge(y,{type:"nothing"});w.reposition();w.fx.animate(c.merge(z,{modes:["linear"]}))}}));break;case"fade:con":C=this;for(A=0;A<y.length;A++){x=w.graph.getAdjacence(y[A][0],y[A][1]);if(x){x.setData("alpha",0,"end");x.ignore=true}}w.reposition();w.fx.animate(c.merge(z,{modes:["edge-property:alpha","linear"],onComplete:function(){C.removeEdge(y,{type:"nothing"});z.onComplete&&z.onComplete()}}));break;case"iter":C=this;w.fx.sequence({condition:function(){return y.length!=0},step:function(){C.removeEdge(y.shift(),{type:"nothing"});w.labels.clearLabels()},onComplete:function(){z.onComplete()},duration:Math.ceil(z.duration/y.length)});break;default:this.doError()}},sum:function(A,z){var w=this.viz;var y=c.merge(this.options,w.controller,z),x=w.root;var C;w.root=z.id||w.root;switch(y.type){case"nothing":C=w.construct(A);C.eachNode(function(E){E.eachAdjacency(function(F){w.graph.addAdjacence(F.nodeFrom,F.nodeTo,F.data)})});break;case"replot":w.refresh(true);this.sum(A,{type:"nothing"});w.refresh(true);break;case"fade:seq":case"fade":case"fade:con":that=this;C=w.construct(A);var D=this.preprocessSum(C);var B=!D?["node-property:alpha"]:["node-property:alpha","edge-property:alpha"];w.reposition();if(y.type!="fade:con"){w.fx.animate(c.merge(y,{modes:["linear"],onComplete:function(){w.fx.animate(c.merge(y,{modes:B,onComplete:function(){y.onComplete()}}))}}))}else{w.graph.eachNode(function(E){if(E.id!=x&&E.pos.isZero()){E.pos.set(E.endPos);E.startPos.set(E.endPos)}});w.fx.animate(c.merge(y,{modes:["linear"].concat(B)}))}break;default:this.doError()}},morph:function(E,x,z){z=z||{};var B=this.viz;var F=c.merge(this.options,B.controller,x),A=B.root;var C;B.root=x.id||B.root;switch(F.type){case"nothing":C=B.construct(E);C.eachNode(function(I){var H=B.graph.hasNode(I.id);I.eachAdjacency(function(M){var L=!!B.graph.getAdjacence(M.nodeFrom.id,M.nodeTo.id);B.graph.addAdjacence(M.nodeFrom,M.nodeTo,M.data);if(L){var K=B.graph.getAdjacence(M.nodeFrom.id,M.nodeTo.id);for(var N in (M.data||{})){K.data[N]=M.data[N]}}});if(H){var G=B.graph.getNode(I.id);for(var J in (I.data||{})){G.data[J]=I.data[J]}}});B.graph.eachNode(function(G){G.eachAdjacency(function(H){if(!C.getAdjacence(H.nodeFrom.id,H.nodeTo.id)){B.graph.removeAdjacence(H.nodeFrom.id,H.nodeTo.id)}});if(!C.hasNode(G.id)){B.graph.removeNode(G.id)}});break;case"replot":B.labels.clearLabels(true);this.morph(E,{type:"nothing"});B.refresh(true);B.refresh(true);break;case"fade:seq":case"fade":case"fade:con":that=this;C=B.construct(E);var D=("node-property" in z)&&c.map(c.splat(z["node-property"]),function(G){return"$"+G});B.graph.eachNode(function(H){var I=C.getNode(H.id);if(!I){H.setData("alpha",1);H.setData("alpha",1,"start");H.setData("alpha",0,"end");H.ignore=true}else{var G=I.data;for(var J in G){if(D&&(c.indexOf(D,J)>-1)){H.endData[J]=G[J]}else{H.data[J]=G[J]}}}});B.graph.eachNode(function(G){if(G.ignore){return}G.eachAdjacency(function(H){if(H.nodeFrom.ignore||H.nodeTo.ignore){return}var I=C.getNode(H.nodeFrom.id);var J=C.getNode(H.nodeTo.id);if(!I.adjacentTo(J)){var H=B.graph.getAdjacence(I.id,J.id);w=true;H.setData("alpha",1);H.setData("alpha",1,"start");H.setData("alpha",0,"end")}})});var w=this.preprocessSum(C);var y=!w?["node-property:alpha"]:["node-property:alpha","edge-property:alpha"];y[0]=y[0]+(("node-property" in z)?(":"+c.splat(z["node-property"]).join(":")):"");y[1]=(y[1]||"edge-property:alpha")+(("edge-property" in z)?(":"+c.splat(z["edge-property"]).join(":")):"");if("label-property" in z){y.push("label-property:"+c.splat(z["label-property"]).join(":"))}if(B.reposition){B.reposition()}else{B.compute("end")}B.graph.eachNode(function(G){if(G.id!=A&&G.pos.getp().equals(b.KER)){G.pos.set(G.endPos);G.startPos.set(G.endPos)}});B.fx.animate(c.merge(F,{modes:[z.position||"polar"].concat(y),onComplete:function(){B.graph.eachNode(function(G){if(G.ignore){B.graph.removeNode(G.id)}});B.graph.eachNode(function(G){G.eachAdjacency(function(H){if(H.ignore){B.graph.removeAdjacence(H.nodeFrom.id,H.nodeTo.id)}})});F.onComplete()}}));break;default:}},contract:function(y,x){var w=this.viz;if(y.collapsed||!y.anySubnode(c.lambda(true))){return}x=c.merge(this.options,w.config,x||{},{modes:["node-property:alpha:span","linear"]});y.collapsed=true;(function z(A){A.eachSubnode(function(B){B.ignore=true;B.setData("alpha",0,x.type=="animate"?"end":"current");z(B)})})(y);if(x.type=="animate"){w.compute("end");if(w.rotated){w.rotate(w.rotated,"none",{property:"end"})}(function z(A){A.eachSubnode(function(B){B.setPos(y.getPos("end"),"end");z(B)})})(y);w.fx.animate(x)}else{if(x.type=="replot"){w.refresh()}}},expand:function(y,x){if(!("collapsed" in y)){return}var w=this.viz;x=c.merge(this.options,w.config,x||{},{modes:["node-property:alpha:span","linear"]});delete y.collapsed;(function z(A){A.eachSubnode(function(B){delete B.ignore;B.setData("alpha",1,x.type=="animate"?"end":"current");z(B)})})(y);if(x.type=="animate"){w.compute("end");if(w.rotated){w.rotate(w.rotated,"none",{property:"end"})}w.fx.animate(x)}else{if(x.type=="replot"){w.refresh()}}},preprocessSum:function(x){var w=this.viz;x.eachNode(function(z){if(!w.graph.hasNode(z.id)){w.graph.addNode(z);var A=w.graph.getNode(z.id);A.setData("alpha",0);A.setData("alpha",0,"start");A.setData("alpha",1,"end")}});var y=false;x.eachNode(function(z){z.eachAdjacency(function(A){var B=w.graph.getNode(A.nodeFrom.id);var C=w.graph.getNode(A.nodeTo.id);if(!B.adjacentTo(C)){var A=w.graph.addAdjacence(B,C,A.data);if(B.startAlpha==B.endAlpha&&C.startAlpha==C.endAlpha){y=true;A.setData("alpha",0);A.setData("alpha",0,"start");A.setData("alpha",1,"end")}}})});return y}};var a={none:{render:c.empty,contains:c.lambda(false)},circle:{render:function(z,A,w,y){var x=y.getCtx();x.beginPath();x.arc(A.x,A.y,w,0,Math.PI*2,true);x.closePath();x[z]()},contains:function(B,A,w){var y=B.x-A.x,x=B.y-A.y,z=y*y+x*x;return z<=w*w}},ellipse:{render:function(C,E,w,F,x){var G=x.getCtx(),z=1,y=1,D=1,B=1,A=0;if(w>F){A=w/2;y=F/w;B=w/F}else{A=F/2;z=w/F;D=F/w}G.save();G.scale(z,y);G.beginPath();G.arc(E.x*D,E.y*B,A,0,Math.PI*2,true);G.closePath();G[C]();G.restore()},contains:function(w,D,x,F){var C=0,B=1,A=1,z=0,y=0,E=0;if(x>F){C=x/2;A=F/x}else{C=F/2;B=x/F}z=(w.x-D.x)*(1/B);y=(w.y-D.y)*(1/A);E=z*z+y*y;return E<=C*C}},square:{render:function(x,z,y,w){w.getCtx()[x+"Rect"](z.x-y,z.y-y,2*y,2*y)},contains:function(y,x,w){return Math.abs(x.x-y.x)<=w&&Math.abs(x.y-y.y)<=w}},rectangle:{render:function(z,A,y,w,x){x.getCtx()[z+"Rect"](A.x-y/2,A.y-w/2,y,w)},contains:function(z,y,x,w){return Math.abs(y.x-z.x)<=x/2&&Math.abs(y.y-z.y)<=w/2}},triangle:{render:function(C,D,z,w){var G=w.getCtx(),y=D.x,x=D.y-z,F=y-z,E=D.y+z,B=y+z,A=E;G.beginPath();G.moveTo(y,x);G.lineTo(F,E);G.lineTo(B,A);G.closePath();G[C]()},contains:function(y,x,w){return a.circle.contains(y,x,w)}},star:{render:function(A,C,B,x){var w=x.getCtx(),z=Math.PI/5;w.save();w.translate(C.x,C.y);w.beginPath();w.moveTo(B,0);for(var y=0;y<9;y++){w.rotate(z);if(y%2==0){w.lineTo((B/0.525731)*0.200811,0)}else{w.lineTo(B,0)}}w.closePath();w[A]();w.restore()},contains:function(y,x,w){return a.circle.contains(y,x,w)}}};var m={line:{render:function(z,y,x){var w=x.getCtx();w.beginPath();w.moveTo(z.x,z.y);w.lineTo(y.x,y.y);w.stroke()},contains:function(G,y,B,E){var z=Math.min,C=Math.max,x=z(G.x,y.x),F=C(G.x,y.x),w=z(G.y,y.y),D=C(G.y,y.y);if(B.x>=x&&B.x<=F&&B.y>=w&&B.y<=D){if(Math.abs(y.x-G.x)<=E){return true}var A=(y.y-G.y)/(y.x-G.x)*(B.x-G.x)+G.y;return Math.abs(A-B.y)<=E}return false}},arrow:{render:function(F,G,z,x,w){var H=w.getCtx();if(x){var y=F;F=G;G=y}var C=new p(G.x-F.x,G.y-F.y);C.$scale(z/C.norm());var A=new p(G.x-C.x,G.y-C.y),B=new p(-C.y/2,C.x/2),E=A.add(B),D=A.$add(B.$scale(-1));H.beginPath();H.moveTo(F.x,F.y);H.lineTo(G.x,G.y);H.stroke();H.beginPath();H.moveTo(E.x,E.y);H.lineTo(D.x,D.y);H.lineTo(G.x,G.y);H.closePath();H.fill()},contains:function(x,w,z,y){return m.line.contains(x,w,z,y)}},hyperline:{render:function(D,E,w,y){var F=y.getCtx();var z=A(D,E);if(z.a>1000||z.b>1000||z.ratio<0){F.beginPath();F.moveTo(D.x*w,D.y*w);F.lineTo(E.x*w,E.y*w);F.stroke()}else{var C=Math.atan2(E.y-z.y,E.x-z.x);var B=Math.atan2(D.y-z.y,D.x-z.x);var x=x(C,B);F.beginPath();F.arc(z.x*w,z.y*w,z.ratio*w,C,B,x);F.stroke()}function A(S,R){var K=(S.x*R.y-S.y*R.x),G=K;var J=S.squaredNorm(),I=R.squaredNorm();if(K==0){return{x:0,y:0,ratio:-1}}var Q=(S.y*I-R.y*J+S.y-R.y)/K;var O=(R.x*J-S.x*I+R.x-S.x)/G;var P=-Q/2;var N=-O/2;var M=(Q*Q+O*O)/4-1;if(M<0){return{x:0,y:0,ratio:-1}}var L=Math.sqrt(M);var H={x:P,y:N,ratio:L>1000?-1:L,a:Q,b:O};return H}function x(G,H){return(G<H)?((G+Math.PI>H)?false:true):((H+Math.PI>G)?true:false)}},contains:c.lambda(false)}};e.Plot={initialize:function(x,w){this.viz=x;this.config=x.config;this.node=x.config.Node;this.edge=x.config.Edge;this.animation=new u;this.nodeTypes=new w.Plot.NodeTypes;this.edgeTypes=new w.Plot.EdgeTypes;this.labels=x.labels},nodeHelper:a,edgeHelper:m,Interpolator:{map:{border:"color",color:"color",width:"number",height:"number",dim:"number",alpha:"number",lineWidth:"number",angularWidth:"number",span:"number",valueArray:"array-number",dimArray:"array-number"},canvas:{globalAlpha:"number",fillStyle:"color",strokeStyle:"color",lineWidth:"number",shadowBlur:"number",shadowColor:"color",shadowOffsetX:"number",shadowOffsetY:"number",miterLimit:"number"},label:{size:"number",color:"color"},compute:function(y,x,w){return y+(x-y)*w},moebius:function(D,C,F,z){var B=z.scale(-F);if(B.norm()<1){var w=B.x,E=B.y;var A=D.startPos.getc().moebiusTransformation(B);D.pos.setc(A.x,A.y);B.x=w;B.y=E}},linear:function(x,w,A){var z=x.startPos.getc(true);var y=x.endPos.getc(true);x.pos.setc(this.compute(z.x,y.x,A),this.compute(z.y,y.y,A))},polar:function(y,x,B){var A=y.startPos.getp(true);var z=y.endPos.getp();var w=z.interpolate(A,B);y.pos.setp(w.theta,w.rho)},number:function(x,C,B,w,A){var z=x[w](C,"start");var y=x[w](C,"end");x[A](C,this.compute(z,y,B))},color:function(y,w,E,B,z){var C=c.hexToRgb(y[B](w,"start"));var D=c.hexToRgb(y[B](w,"end"));var A=this.compute;var x=c.rgbToHex([parseInt(A(C[0],D[0],E)),parseInt(A(C[1],D[1],E)),parseInt(A(C[2],D[2],E))]);y[z](w,x)},"array-number":function(z,y,J,G,B){var H=z[G](y,"start"),I=z[G](y,"end"),K=[];for(var E=0,A=H.length;E<A;E++){var x=H[E],w=I[E];if(x.length){for(var D=0,F=x.length,C=[];D<F;D++){C.push(this.compute(x[D],w[D],J))}K.push(C)}else{K.push(this.compute(x,w,J))}}z[B](y,K)},node:function(x,C,E,w,D,y){w=this[w];if(C){var B=C.length;for(var z=0;z<B;z++){var A=C[z];this[w[A]](x,A,E,D,y)}}else{for(var A in w){this[w[A]](x,A,E,D,y)}}},edge:function(y,x,D,z,w,C){var B=y.adjacencies;for(var A in B){this["node"](B[A],x,D,z,w,C)}},"node-property":function(x,w,y){this["node"](x,w,y,"map","getData","setData")},"edge-property":function(x,w,y){this["edge"](x,w,y,"map","getData","setData")},"label-property":function(x,w,y){this["node"](x,w,y,"label","getLabelData","setLabelData")},"node-style":function(x,w,y){this["node"](x,w,y,"canvas","getCanvasStyle","setCanvasStyle")},"edge-style":function(x,w,y){this["edge"](x,w,y,"canvas","getCanvasStyle","setCanvasStyle")}},sequence:function(x){var y=this;x=c.merge({condition:c.lambda(false),step:c.empty,onComplete:c.empty,duration:200},x||{});var w=setInterval(function(){if(x.condition()){x.step()}else{clearInterval(w);x.onComplete()}y.viz.refresh(true)},x.duration)},prepare:function(C){var B=this.viz.graph,z={"node-property":{getter:"getData",setter:"setData"},"edge-property":{getter:"getData",setter:"setData"},"node-style":{getter:"getCanvasStyle",setter:"setCanvasStyle"},"edge-style":{getter:"getCanvasStyle",setter:"setCanvasStyle"}};var x={};if(c.type(C)=="array"){for(var A=0,w=C.length;A<w;A++){var y=C[A].split(":");x[y.shift()]=y}}else{for(var D in C){if(D=="position"){x[C.position]=[]}else{x[D]=c.splat(C[D])}}}B.eachNode(function(E){E.startPos.set(E.pos);c.each(["node-property","node-style"],function(H){if(H in x){var I=x[H];for(var G=0,F=I.length;G<F;G++){E[z[H].setter](I[G],E[z[H].getter](I[G]),"start")}}});c.each(["edge-property","edge-style"],function(F){if(F in x){var G=x[F];E.eachAdjacency(function(I){for(var J=0,H=G.length;J<H;J++){I[z[F].setter](G[J],I[z[F].getter](G[J]),"start")}})}})});return x},animate:function(z,y){z=c.merge(this.viz.config,z||{});var A=this,x=this.viz,C=x.graph,D=this.Interpolator,B=z.type==="nodefx"?this.nodeFxAnimation:this.animation;var w=this.prepare(z.modes);if(z.hideLabels){this.labels.hideLabels(true)}B.setOptions(c.extend(z,{$animating:false,compute:function(E){C.eachNode(function(F){for(var G in w){D[G](F,w[G],E,y)}});A.plot(z,this.$animating,E);this.$animating=true},complete:function(){if(z.hideLabels){A.labels.hideLabels(false)}A.plot(z);z.onComplete()}})).start()},nodeFx:function(y){var D=this.viz,E=D.graph,B=this.nodeFxAnimation,F=c.merge(this.viz.config,{elements:{id:false,properties:{}},reposition:false});y=c.merge(F,y||{},{onBeforeCompute:c.empty,onAfterCompute:c.empty});B.stopTimer();var C=y.elements.properties;if(!y.elements.id){E.eachNode(function(H){for(var G in C){H.setData(G,C[G],"end")}})}else{var w=c.splat(y.elements.id);c.each(w,function(I){var H=E.getNode(I);if(H){for(var G in C){H.setData(G,C[G],"end")}}})}var A=[];for(var x in C){A.push(x)}var z=["node-property:"+A.join(":")];if(y.reposition){z.push("linear");D.compute("end")}this.animate(c.merge(y,{modes:z,type:"nodefx"}))},plot:function(x,G){var E=this.viz,B=E.graph,y=E.canvas,w=E.root,C=this,F=y.getCtx(),A=Math.min,x=x||this.viz.controller;x.clearCanvas&&y.clear();var D=B.getNode(w);if(!D){return}var z=!!D.visited;B.eachNode(function(I){var H=I.getData("alpha");I.eachAdjacency(function(J){var K=J.nodeTo;if(!!K.visited===z&&I.drawn&&K.drawn){!G&&x.onBeforePlotLine(J);C.plotLine(J,y,G);!G&&x.onAfterPlotLine(J)}});if(I.drawn){!G&&x.onBeforePlotNode(I);C.plotNode(I,y,G);!G&&x.onAfterPlotNode(I)}if(!C.labelsHidden&&x.withLabels){if(I.drawn&&H>=0.95){C.labels.plotLabel(y,I,x)}else{C.labels.hideLabel(I,false)}}I.visited=!z})},plotTree:function(A,x,E){var B=this,C=this.viz,y=C.canvas,z=this.config,D=y.getCtx();var w=A.getData("alpha");A.eachSubnode(function(G){if(x.plotSubtree(A,G)&&G.exist&&G.drawn){var F=A.getAdjacency(G.id);!E&&x.onBeforePlotLine(F);B.plotLine(F,y,E);!E&&x.onAfterPlotLine(F);B.plotTree(G,x,E)}});if(A.drawn){!E&&x.onBeforePlotNode(A);this.plotNode(A,y,E);!E&&x.onAfterPlotNode(A);if(!x.hideLabels&&x.withLabels&&w>=0.95){this.labels.plotLabel(y,A,x)}else{this.labels.hideLabel(A,false)}}else{this.labels.hideLabel(A,true)}},plotNode:function(y,x,F){var C=y.getData("type"),B=this.node.CanvasStyles;if(C!="none"){var w=y.getData("lineWidth"),A=y.getData("color"),z=y.getData("alpha"),D=x.getCtx();D.save();D.lineWidth=w;D.fillStyle=D.strokeStyle=A;D.globalAlpha=z;for(var E in B){D[E]=y.getCanvasStyle(E)}this.nodeTypes[C].render.call(this,y,x,F);D.restore()}},plotLine:function(C,x,G){var B=C.getData("type"),z=this.edge.CanvasStyles;if(B!="none"){var w=C.getData("lineWidth"),y=C.getData("color"),E=x.getCtx(),A=C.nodeFrom,D=C.nodeTo;E.save();E.lineWidth=w;E.fillStyle=E.strokeStyle=y;E.globalAlpha=Math.min(A.getData("alpha"),D.getData("alpha"),C.getData("alpha"));for(var F in z){E[F]=C.getCanvasStyle(F)}this.edgeTypes[B].render.call(this,C,x,G);E.restore()}}};e.Plot3D=c.merge(e.Plot,{Interpolator:{linear:function(x,w,A){var z=x.startPos.getc(true);var y=x.endPos.getc(true);x.pos.setc(this.compute(z.x,y.x,A),this.compute(z.y,y.y,A),this.compute(z.z,y.z,A))}},plotNode:function(x,w){if(x.getData("type")=="none"){return}this.plotElement(x,w,{getAlpha:function(){return x.getData("alpha")}})},plotLine:function(w,x){if(w.getData("type")=="none"){return}this.plotElement(w,x,{getAlpha:function(){return Math.min(w.nodeFrom.getData("alpha"),w.nodeTo.getData("alpha"),w.getData("alpha"))}})},plotElement:function(Y,E,z){var V=E.getCtx(),F=new Matrix4,x=E.config.Scene.Lighting,Z=E.canvases[0],K=Z.program,X=Z.camera;if(!Y.geometry){Y.geometry=new O3D[Y.getData("type")]}Y.geometry.update(Y);if(!Y.webGLVertexBuffer){var J=[],B=[],P=[],N=0,S=Y.geometry;for(var W=0,U=S.vertices,H=S.faces,G=H.length;W<G;W++){var M=H[W],D=U[M.a],C=U[M.b],A=U[M.c],y=M.d?U[M.d]:false,R=M.normal;J.push(D.x,D.y,D.z);J.push(C.x,C.y,C.z);J.push(A.x,A.y,A.z);if(y){J.push(y.x,y.y,y.z)}P.push(R.x,R.y,R.z);P.push(R.x,R.y,R.z);P.push(R.x,R.y,R.z);if(y){P.push(R.x,R.y,R.z)}B.push(N,N+1,N+2);if(y){B.push(N,N+2,N+3);N+=4}else{N+=3}}Y.webGLVertexBuffer=V.createBuffer();V.bindBuffer(V.ARRAY_BUFFER,Y.webGLVertexBuffer);V.bufferData(V.ARRAY_BUFFER,new Float32Array(J),V.STATIC_DRAW);Y.webGLFaceBuffer=V.createBuffer();V.bindBuffer(V.ELEMENT_ARRAY_BUFFER,Y.webGLFaceBuffer);V.bufferData(V.ELEMENT_ARRAY_BUFFER,new Uint16Array(B),V.STATIC_DRAW);Y.webGLFaceCount=B.length;Y.webGLNormalBuffer=V.createBuffer();V.bindBuffer(V.ARRAY_BUFFER,Y.webGLNormalBuffer);V.bufferData(V.ARRAY_BUFFER,new Float32Array(P),V.STATIC_DRAW)}F.multiply(X.matrix,Y.geometry.matrix);V.uniformMatrix4fv(K.viewMatrix,false,F.flatten());V.uniformMatrix4fv(K.projectionMatrix,false,X.projectionMatrix.flatten());var L=Matrix4.makeInvert(F);L.$transpose();V.uniformMatrix4fv(K.normalMatrix,false,L.flatten());var T=c.hexToRgb(Y.getData("color"));T.push(z.getAlpha());V.uniform4f(K.color,T[0]/255,T[1]/255,T[2]/255,T[3]);V.uniform1i(K.enableLighting,x.enable);if(x.enable){if(x.ambient){var O=x.ambient;V.uniform3f(K.ambientColor,O[0],O[1],O[2])}if(x.directional){var Q=x.directional,T=Q.color,I=Q.direction,w=new Vector3(I.x,I.y,I.z).normalize().$scale(-1);V.uniform3f(K.lightingDirection,w.x,w.y,w.z);V.uniform3f(K.directionalColor,T[0],T[1],T[2])}}V.bindBuffer(V.ARRAY_BUFFER,Y.webGLVertexBuffer);V.vertexAttribPointer(K.position,3,V.FLOAT,false,0,0);V.bindBuffer(V.ARRAY_BUFFER,Y.webGLNormalBuffer);V.vertexAttribPointer(K.normal,3,V.FLOAT,false,0,0);V.bindBuffer(V.ELEMENT_ARRAY_BUFFER,Y.webGLFaceBuffer);V.drawElements(V.TRIANGLES,Y.webGLFaceCount,V.UNSIGNED_SHORT,0)}});e.Label={};e.Label.Native=new q({initialize:function(w){this.viz=w},plotLabel:function(y,z,x){var w=y.getCtx();var A=z.pos.getc(true);w.font=z.getLabelData("style")+" "+z.getLabelData("size")+"px "+z.getLabelData("family");w.textAlign=z.getLabelData("textAlign");w.fillStyle=w.strokeStyle=z.getLabelData("color");w.textBaseline=z.getLabelData("textBaseline");this.renderLabel(y,z,x)},renderLabel:function(y,z,x){var w=y.getCtx();var A=z.pos.getc(true);w.fillText(z.name,A.x,A.y+z.getData("height")/2)},hideLabel:c.empty,hideLabels:c.empty});e.Label.DOM=new q({labelsHidden:false,labelContainer:false,labels:{},getLabelContainer:function(){return this.labelContainer?this.labelContainer:this.labelContainer=document.getElementById(this.viz.config.labelContainer)},getLabel:function(w){return(w in this.labels&&this.labels[w]!=null)?this.labels[w]:this.labels[w]=document.getElementById(w)},hideLabels:function(x){var w=this.getLabelContainer();if(x){w.style.display="none"}else{w.style.display=""}this.labelsHidden=x},clearLabels:function(w){for(var x in this.labels){if(w||!this.viz.graph.hasNode(x)){this.disposeLabel(x);delete this.labels[x]}}},disposeLabel:function(x){var w=this.getLabel(x);if(w&&w.parentNode){w.parentNode.removeChild(w)}},hideLabel:function(A,w){A=c.splat(A);var x=w?"":"none",y,z=this;c.each(A,function(C){var B=z.getLabel(C.id);if(B){B.style.display=x}})},fitsInCanvas:function(y,w){var x=w.getSize();if(y.x>=x.width||y.x<0||y.y>=x.height||y.y<0){return false}return true}});e.Label.HTML=new q({Implements:e.Label.DOM,plotLabel:function(z,A,y){var B=A.id,w=this.getLabel(B);if(!w&&!(w=document.getElementById(B))){w=document.createElement("div");var x=this.getLabelContainer();w.id=B;w.className="node";w.style.position="absolute";y.onCreateLabel(w,A);x.appendChild(w);this.labels[A.id]=w}this.placeLabel(w,A,y)}});e.Label.SVG=new q({Implements:e.Label.DOM,plotLabel:function(z,B,y){var D=B.id,w=this.getLabel(D);if(!w&&!(w=document.getElementById(D))){var A="http://www.w3.org/2000/svg";w=document.createElementNS(A,"svg:text");var C=document.createElementNS(A,"svg:tspan");w.appendChild(C);var x=this.getLabelContainer();w.setAttribute("id",D);w.setAttribute("class","node");x.appendChild(w);y.onCreateLabel(w,B);this.labels[B.id]=w}this.placeLabel(w,B,y)}});e.Geom=new q({initialize:function(w){this.viz=w;this.config=w.config;this.node=w.config.Node;this.edge=w.config.Edge},translate:function(x,w){w=c.splat(w);this.viz.graph.eachNode(function(y){c.each(w,function(z){y.getPos(z).$add(x)})})},setRightLevelToShow:function(z,w,B){var A=this.getRightLevelToShow(z,w),y=this.viz.labels,x=c.merge({execShow:true,execHide:true,onHide:c.empty,onShow:c.empty},B||{});z.eachLevel(0,this.config.levelsToShow,function(D){var C=D._depth-z._depth;if(C>A){x.onHide(D);if(x.execHide){D.drawn=false;D.exist=false;y.hideLabel(D,false)}}else{x.onShow(D);if(x.execShow){D.exist=true}}});z.drawn=true},getRightLevelToShow:function(z,x){var w=this.config;var A=w.levelsToShow;var y=w.constrained;if(!y){return A}while(!this.treeFitsInCanvas(z,x,A)&&A>1){A--}return A}});var d={construct:function(x){var y=(c.type(x)=="array");var w=new e(this.graphOptions,this.config.Node,this.config.Edge,this.config.Label);if(!y){(function(z,B){z.addNode(B);if(B.children){for(var A=0,C=B.children;A<C.length;A++){z.addAdjacence(B,C[A]);arguments.callee(z,C[A])}}})(w,x)}else{(function(H,I){var A=function(M){for(var L=0,J=I.length;L<J;L++){if(I[L].id==M){return I[L]}}var K={id:M,name:M};return H.addNode(K)};for(var E=0,B=I.length;E<B;E++){H.addNode(I[E]);var F=I[E].adjacencies;if(F){for(var C=0,G=F.length;C<G;C++){var z=F[C],D={};if(typeof F[C]!="string"){D=c.merge(z.data,{});z=z.nodeTo}H.addAdjacence(I[E],A(z),D)}}}})(w,x)}return w},loadJSON:function(x,w){this.json=x;if(this.labels&&this.labels.clearLabels){this.labels.clearLabels(true)}this.graph=this.construct(x);if(c.type(x)!="array"){this.root=x.id}else{this.root=x[w?w:0].id}},toJSON:function(A){A=A||"tree";if(A=="tree"){var y={};var x=this.graph.getNode(this.root);var y=(function w(D){var B={};B.id=D.id;B.name=D.name;B.data=D.data;var C=[];D.eachSubnode(function(E){C.push(w(E))});B.children=C;return B})(x);return y}else{var y=[];var z=!!this.graph.getNode(this.root).visited;this.graph.eachNode(function(C){var B={};B.id=C.id;B.name=C.name;B.data=C.data;var D=[];C.eachAdjacency(function(E){var G=E.nodeTo;if(!!G.visited===z){var F={};F.nodeTo=G.id;F.data=E.data;D.push(F)}});B.adjacencies=D;y.push(B);C.visited=!z});return y}}};var g=$jit.Layouts={};var f={label:null,compute:function(z,A,x){this.initializeLabel(x);var w=this.label,y=w.style;z.eachNode(function(D){var H=D.getData("autoWidth"),I=D.getData("autoHeight");if(H||I){delete D.data.$width;delete D.data.$height;delete D.data.$dim;var B=D.getData("width"),J=D.getData("height");y.width=H?"auto":B+"px";y.height=I?"auto":J+"px";w.innerHTML=D.name;var F=w.offsetWidth,C=w.offsetHeight;var G=D.getData("type");if(c.indexOf(["circle","square","triangle","star"],G)===-1){D.setData("width",F);D.setData("height",C)}else{var E=F>C?F:C;D.setData("width",E);D.setData("height",E);D.setData("dim",E)}}})},initializeLabel:function(w){if(!this.label){this.label=document.createElement("div");document.body.appendChild(this.label)}this.setLabelStyles(w)},setLabelStyles:function(w){c.extend(this.label.style,{visibility:"hidden",position:"absolute",width:"auto",height:"auto"});this.label.className="jit-autoadjust-label"}};g.Tree=(function(){var F=Array.prototype.slice;function D(P,K,H,N,I){var M=K.Node;var J=K.multitree;if(M.overridable){var O=-1,L=-1;P.eachNode(function(S){if(S._depth==H&&(!J||("$orn" in S.data)&&S.data.$orn==N)){var Q=S.getData("width",I);var R=S.getData("height",I);O=(O<Q)?Q:O;L=(L<R)?R:L}});return{width:O<0?M.width:O,height:L<0?M.height:L}}else{return M}}function G(I,L,K,H){var J=(H=="left"||H=="right")?"y":"x";I.getPos(L)[J]+=K}function B(I,J){var H=[];c.each(I,function(K){K=F.call(K);K[0]+=J;K[1]+=J;H.push(K)});return H}function E(K,H){if(K.length==0){return H}if(H.length==0){return K}var J=K.shift(),I=H.shift();return[[J[0],I[1]]].concat(E(K,H))}function z(H,I){I=I||[];if(H.length==0){return I}var J=H.pop();return z(H,E(J,I))}function C(K,I,L,H,J){if(K.length<=J||I.length<=J){return 0}var N=K[J][1],M=I[J][0];return Math.max(C(K,I,L,H,++J)+L,N-M+H)}function A(K,I,H){function J(N,P,M){if(P.length<=M){return[]}var O=P[M],L=C(N,O,I,H,0);return[L].concat(J(E(N,B(O,L)),P,++M))}return J([],K,0)}function x(L,K,J){function H(O,Q,N){if(Q.length<=N){return[]}var P=Q[N],M=-C(P,O,K,J,0);return[M].concat(H(E(B(P,M),O),Q,++N))}L=F.call(L);var I=H([],L.reverse(),0);return I.reverse()}function w(N,L,I,O){var J=A(N,L,I),M=x(N,L,I);if(O=="left"){M=J}else{if(O=="right"){J=M}}for(var K=0,H=[];K<J.length;K++){H[K]=(J[K]+M[K])/2}return H}function y(H,R,I,Y,W){var K=Y.multitree;var Q=["x","y"],N=["width","height"];var J=+(W=="left"||W=="right");var O=Q[J],X=Q[1-J];var T=Y.Node;var M=N[J],V=N[1-J];var L=Y.siblingOffset;var U=Y.subtreeOffset;var S=Y.align;function P(ab,af,aj){var aa=ab.getData(M,I);var ai=af||(ab.getData(V,I));var am=[],ak=[],ag=false;var Z=ai+Y.levelDistance;ab.eachSubnode(function(ao){if(ao.exist&&(!K||("$orn" in ao.data)&&ao.data.$orn==W)){if(!ag){ag=D(H,Y,ao._depth,W,I)}var an=P(ao,ag[V],aj+Z);am.push(an.tree);ak.push(an.extent)}});var ae=w(ak,U,L,S);for(var ad=0,ac=[],ah=[];ad<am.length;ad++){G(am[ad],I,ae[ad],W);ah.push(B(ak[ad],ae[ad]))}var al=[[-aa/2,aa/2]].concat(z(ah));ab.getPos(I)[O]=0;if(W=="top"||W=="left"){ab.getPos(I)[X]=aj}else{ab.getPos(I)[X]=-aj}return{tree:ab,extent:al}}P(R,false,0)}return new q({compute:function(J,I){var K=J||"start";var H=this.graph.getNode(this.root);c.extend(H,{drawn:true,exist:true,selected:true});f.compute(this.graph,K,this.config);if(!!I||!("_depth" in H)){this.graph.computeLevels(this.root,0,"ignore")}this.computePositions(H,K)},computePositions:function(L,H){var J=this.config;var I=J.multitree;var O=J.align;var K=O!=="center"&&J.indent;var P=J.orientation;var N=I?["top","right","bottom","left"]:[P];var M=this;c.each(N,function(Q){y(M.graph,L,H,M.config,Q,H);var R=["x","y"][+(Q=="left"||Q=="right")];(function S(T){T.eachSubnode(function(U){if(U.exist&&(!I||("$orn" in U.data)&&U.data.$orn==Q)){U.getPos(H)[R]+=T.getPos(H)[R];if(K){U.getPos(H)[R]+=O=="left"?K:-K}S(U)}})})(L)})}})})();$jit.ST=(function(){var x=[];function y(D){D=D||this.clickedNode;if(!this.config.constrained){return[]}var A=this.geom;var H=this.graph;var B=this.canvas;var z=D._depth,E=[];H.eachNode(function(I){if(I.exist&&!I.selected){if(I.isDescendantOf(D.id)){if(I._depth<=z){E.push(I)}}else{E.push(I)}}});var F=A.getRightLevelToShow(D,B);D.eachLevel(F,F,function(I){if(I.exist&&!I.selected){E.push(I)}});for(var G=0;G<x.length;G++){var C=this.graph.getNode(x[G]);if(!C.isDescendantOf(D.id)){E.push(C)}}return E}function w(B){var A=[],z=this.config;B=B||this.clickedNode;this.clickedNode.eachLevel(0,z.levelsToShow,function(C){if(z.multitree&&!("$orn" in C.data)&&C.anySubnode(function(D){return D.exist&&!D.drawn})){A.push(C)}else{if(C.drawn&&!C.anySubnode("drawn")){A.push(C)}}});return A}return new q({Implements:[d,o,g.Tree],initialize:function(z){var B=$jit.ST;var A={levelsToShow:2,levelDistance:30,constrained:true,Node:{type:"rectangle"},duration:700,offsetX:0,offsetY:0};this.controller=this.config=c.merge(n("Canvas","Fx","Tree","Node","Edge","Controller","Tips","NodeStyles","Events","Navigation","Label"),A,z);var C=this.config;if(C.useCanvas){this.canvas=C.useCanvas;this.config.labelContainer=this.canvas.id+"-label"}else{if(C.background){C.background=c.merge({type:"Circles"},C.background)}this.canvas=new l(this,C);this.config.labelContainer=(typeof C.injectInto=="string"?C.injectInto:C.injectInto.id)+"-label"}this.graphOptions={klass:p};this.graph=new e(this.graphOptions,this.config.Node,this.config.Edge);this.labels=new B.Label[C.Label.type](this);this.fx=new B.Plot(this,B);this.op=new B.Op(this);this.group=new B.Group(this);this.geom=new B.Geom(this);this.clickedNode=null;this.initializeExtras()},plot:function(){this.fx.plot(this.controller)},switchPosition:function(E,D,C){var z=this.geom,A=this.fx,B=this;if(!A.busy){A.busy=true;this.contract({onComplete:function(){z.switchOrientation(E);B.compute("end",false);A.busy=false;if(D=="animate"){B.onClick(B.clickedNode.id,C)}else{if(D=="replot"){B.select(B.clickedNode.id,C)}}}},E)}},switchAlignment:function(B,A,z){this.config.align=B;if(A=="animate"){this.select(this.clickedNode.id,z)}else{if(A=="replot"){this.onClick(this.clickedNode.id,z)}}},addNodeInPath:function(z){x.push(z);this.select((this.clickedNode&&this.clickedNode.id)||this.root)},clearNodesInPath:function(z){x.length=0;this.select((this.clickedNode&&this.clickedNode.id)||this.root)},refresh:function(){this.reposition();this.select((this.clickedNode&&this.clickedNode.id)||this.root)},reposition:function(){this.graph.computeLevels(this.root,0,"ignore");this.geom.setRightLevelToShow(this.clickedNode,this.canvas);this.graph.eachNode(function(z){if(z.exist){z.drawn=true}});this.compute("end")},requestNodes:function(B,C){var A=c.merge(this.controller,C),z=this.config.levelsToShow;if(A.request){var E=[],D=B._depth;B.eachLevel(0,z,function(F){if(F.drawn&&!F.anySubnode()){E.push(F);F._level=z-(F._depth-D)}});this.group.requestNodes(E,A)}else{A.onComplete()}},contract:function(D,E){var C=this.config.orientation;var z=this.geom,B=this.group;if(E){z.switchOrientation(E)}var A=y.call(this);if(E){z.switchOrientation(C)}B.contract(A,c.merge(this.controller,D))},move:function(A,B){this.compute("end",false);var z=B.Move,C={x:z.offsetX,y:z.offsetY};if(z.enable){this.geom.translate(A.endPos.add(C).$scale(-1),"end")}this.fx.animate(c.merge(this.controller,{modes:["linear"]},B))},expand:function(A,B){var z=w.call(this,A);this.group.expand(z,c.merge(this.controller,B))},selectPath:function(C){var B=this;this.graph.eachNode(function(E){E.selected=false});function D(F){if(F==null||F.selected){return}F.selected=true;c.each(B.group.getSiblings([F])[F.id],function(G){G.exist=true;G.drawn=true});var E=F.getParents();E=(E.length>0)?E[0]:null;D(E)}for(var z=0,A=[C.id].concat(x);z<A.length;z++){D(this.graph.getNode(A[z]))}},setRoot:function(G,F,E){if(this.busy){return}this.busy=true;var D=this,B=this.canvas;var z=this.graph.getNode(this.root);var A=this.graph.getNode(G);function C(){if(this.config.multitree&&A.data.$orn){var I=A.data.$orn;var J={left:"right",right:"left",top:"bottom",bottom:"top"}[I];z.data.$orn=J;(function H(K){K.eachSubnode(function(L){if(L.id!=G){L.data.$orn=J;H(L)}})})(z);delete A.data.$orn}this.root=G;this.clickedNode=A;this.graph.computeLevels(this.root,0,"ignore");this.geom.setRightLevelToShow(A,B,{execHide:false,onShow:function(K){if(!K.drawn){K.drawn=true;K.setData("alpha",1,"end");K.setData("alpha",0);K.pos.setc(A.pos.x,A.pos.y)}}});this.compute("end");this.busy=true;this.fx.animate({modes:["linear","node-property:alpha"],onComplete:function(){D.busy=false;D.onClick(G,{onComplete:function(){E&&E.onComplete()}})}})}delete z.data.$orns;if(F=="animate"){C.call(this);D.selectPath(A)}else{if(F=="replot"){C.call(this);this.select(this.root)}}},addSubtree:function(z,B,A){if(B=="replot"){this.op.sum(z,c.extend({type:"replot"},A||{}))}else{if(B=="animate"){this.op.sum(z,c.extend({type:"fade:seq"},A||{}))}}},removeSubtree:function(E,A,D,C){var B=this.graph.getNode(E),z=[];B.eachLevel(+!A,false,function(F){z.push(F.id)});if(D=="replot"){this.op.removeNode(z,c.extend({type:"replot"},C||{}))}else{if(D=="animate"){this.op.removeNode(z,c.extend({type:"fade:seq"},C||{}))}}},select:function(z,C){var H=this.group,F=this.geom;var D=this.graph.getNode(z),B=this.canvas;var G=this.graph.getNode(this.root);var A=c.merge(this.controller,C);var E=this;A.onBeforeCompute(D);this.selectPath(D);this.clickedNode=D;this.requestNodes(D,{onComplete:function(){H.hide(H.prepare(y.call(E)),A);F.setRightLevelToShow(D,B);E.compute("current");E.graph.eachNode(function(K){var J=K.pos.getc(true);K.startPos.setc(J.x,J.y);K.endPos.setc(J.x,J.y);K.visited=false});var I={x:A.offsetX,y:A.offsetY};E.geom.translate(D.endPos.add(I).$scale(-1),["start","current","end"]);H.show(w.call(E));E.plot();A.onAfterCompute(E.clickedNode);A.onComplete()}})},onClick:function(A,H){var C=this.canvas,G=this,z=this.geom,D=this.config;var F={Move:{enable:true,offsetX:D.offsetX||0,offsetY:D.offsetY||0},setRightLevelToShowConfig:false,onBeforeRequest:c.empty,onBeforeContract:c.empty,onBeforeMove:c.empty,onBeforeExpand:c.empty};var B=c.merge(this.controller,F,H);if(!this.busy){this.busy=true;var E=this.graph.getNode(A);this.selectPath(E,this.clickedNode);this.clickedNode=E;B.onBeforeCompute(E);B.onBeforeRequest(E);this.requestNodes(E,{onComplete:function(){B.onBeforeContract(E);G.contract({onComplete:function(){z.setRightLevelToShow(E,C,B.setRightLevelToShowConfig);B.onBeforeMove(E);G.move(E,{Move:B.Move,onComplete:function(){B.onBeforeExpand(E);G.expand(E,{onComplete:function(){G.busy=false;B.onAfterCompute(A);B.onComplete()}})}})}})}})}}})})();$jit.ST.$extend=true;$jit.ST.Op=new q({Implements:e.Op});$jit.ST.Group=new q({initialize:function(w){this.viz=w;this.canvas=w.canvas;this.config=w.config;this.animation=new u;this.nodes=null},requestNodes:function(B,A){var z=0,x=B.length,D={};var y=function(){A.onComplete()};var w=this.viz;if(x==0){y()}for(var C=0;C<x;C++){D[B[C].id]=B[C];A.request(B[C].id,B[C]._level,{onComplete:function(F,E){if(E&&E.children){E.id=F;w.op.sum(E,{type:"nothing"})}if(++z==x){w.graph.computeLevels(w.root,0);y()}}})}},contract:function(y,x){var w=this.viz;var z=this;y=this.prepare(y);this.animation.setOptions(c.merge(x,{$animating:false,compute:function(A){if(A==1){A=0.99}z.plotStep(1-A,x,this.$animating);this.$animating="contract"},complete:function(){z.hide(y,x)}})).start()},hide:function(y,x){var w=this.viz;for(var z=0;z<y.length;z++){if(true||!x||!x.request){y[z].eachLevel(1,false,function(B){if(B.exist){c.extend(B,{drawn:false,exist:false})}})}else{var A=[];y[z].eachLevel(1,false,function(B){A.push(B.id)});w.op.removeNode(A,{type:"nothing"});w.labels.clearLabels()}}x.onComplete()},expand:function(x,w){var y=this;this.show(x);this.animation.setOptions(c.merge(w,{$animating:false,compute:function(z){y.plotStep(z,w,this.$animating);this.$animating="expand"},complete:function(){y.plotStep(undefined,w,false);w.onComplete()}})).start()},show:function(w){var x=this.config;this.prepare(w);c.each(w,function(z){if(x.multitree&&!("$orn" in z.data)){delete z.data.$orns;var y=" ";z.eachSubnode(function(A){if(("$orn" in A.data)&&y.indexOf(A.data.$orn)<0&&A.exist&&!A.drawn){y+=A.data.$orn+" "}});z.data.$orns=y}z.eachLevel(0,x.levelsToShow,function(A){if(A.exist){A.drawn=true}})})},prepare:function(w){this.nodes=this.getNodesWithChildren(w);return this.nodes},getNodesWithChildren:function(y){var x=[],A=this.config,w=this.viz.root;y.sort(function(E,D){return(E._depth<=D._depth)-(E._depth>=D._depth)});for(var B=0;B<y.length;B++){if(y[B].anySubnode("exist")){for(var z=B+1,C=false;!C&&z<y.length;z++){if(!A.multitree||"$orn" in y[z].data){C=C||y[B].isDescendantOf(y[z].id)}}if(!C){x.push(y[B])}}}return x},plotStep:function(G,C,I){var F=this.viz,z=this.config,y=F.canvas,H=y.getCtx(),w=this.nodes;var B,A;var x={};for(B=0;B<w.length;B++){A=w[B];x[A.id]=[];var E=z.multitree&&!("$orn" in A.data);var D=E&&A.data.$orns;A.eachSubgraph(function(J){if(E&&D&&D.indexOf(J.data.$orn)>0&&J.drawn){J.drawn=false;x[A.id].push(J)}else{if((!E||!D)&&J.drawn){J.drawn=false;x[A.id].push(J)}}});A.drawn=true}if(w.length>0){F.fx.plot()}for(B in x){c.each(x[B],function(J){J.drawn=true})}for(B=0;B<w.length;B++){A=w[B];H.save();F.fx.plotSubtree(A,C,G,I);H.restore()}},getSiblings:function(w){var x={};c.each(w,function(A){var z=A.getParents();if(z.length==0){x[A.id]=[A]}else{var y=[];z[0].eachSubnode(function(B){y.push(B)});x[A.id]=y}});return x}});$jit.ST.Geom=new q({Implements:e.Geom,switchOrientation:function(w){this.config.orientation=w},dispatch:function(){var x=Array.prototype.slice.call(arguments);var y=x.shift(),w=x.length;var z=function(A){return typeof A=="function"?A():A};if(w==2){return(y=="top"||y=="bottom")?z(x[0]):z(x[1])}else{if(w==4){switch(y){case"top":return z(x[0]);case"right":return z(x[1]);case"bottom":return z(x[2]);case"left":return z(x[3])}}}return undefined},getSize:function(E,D){var C=E.data,z=this.config;var y=z.siblingOffset;var B=(z.multitree&&("$orn" in C)&&C.$orn)||z.orientation;var x=E.getData("width")+y;var A=E.getData("height")+y;if(!D){return this.dispatch(B,A,x)}else{return this.dispatch(B,x,A)}},getTreeBaseSize:function(A,B,x){var y=this.getSize(A,true),w=0,z=this;if(x(B,A)){return y}if(B===0){return 0}A.eachSubnode(function(C){w+=z.getTreeBaseSize(C,B-1,x)});return(y>w?y:w)+this.config.subtreeOffset},getEdge:function(C,B,A){var y=function(E,w){return function(){return C.pos.add(new p(E,w))}};var D=this.node;var x=C.getData("width");var z=C.getData("height");if(B=="begin"){if(D.align=="center"){return this.dispatch(A,y(0,z/2),y(-x/2,0),y(0,-z/2),y(x/2,0))}else{if(D.align=="left"){return this.dispatch(A,y(0,z),y(0,0),y(0,0),y(x,0))}else{if(D.align=="right"){return this.dispatch(A,y(0,0),y(-x,0),y(0,-z),y(0,0))}else{throw"align: not implemented"}}}}else{if(B=="end"){if(D.align=="center"){return this.dispatch(A,y(0,-z/2),y(x/2,0),y(0,z/2),y(-x/2,0))}else{if(D.align=="left"){return this.dispatch(A,y(0,0),y(x,0),y(0,z),y(0,0))}else{if(D.align=="right"){return this.dispatch(A,y(0,-z),y(0,0),y(0,0),y(-x,0))}else{throw"align: not implemented"}}}}}},getScaledTreePosition:function(B,D){var C=this.node;var x=B.getData("width");var A=B.getData("height");var z=(this.config.multitree&&("$orn" in B.data)&&B.data.$orn)||this.config.orientation;var y=function(E,w){return function(){return B.pos.add(new p(E,w)).$scale(1-D)}};if(C.align=="left"){return this.dispatch(z,y(0,A),y(0,0),y(0,0),y(x,0))}else{if(C.align=="center"){return this.dispatch(z,y(0,A/2),y(-x/2,0),y(0,-A/2),y(x/2,0))}else{if(C.align=="right"){return this.dispatch(z,y(0,0),y(-x,0),y(0,-A),y(0,0))}else{throw"align: not implemented"}}}},treeFitsInCanvas:function(B,w,C){var y=w.getSize();var z=(this.config.multitree&&("$orn" in B.data)&&B.data.$orn)||this.config.orientation;var x=this.dispatch(z,y.width,y.height);var A=this.getTreeBaseSize(B,C,function(E,D){return E===0||!D.anySubnode()});return(A<x)}});$jit.ST.Plot=new q({Implements:e.Plot,plotSubtree:function(z,w,A,E){var C=this.viz,x=C.canvas,y=C.config;A=Math.min(Math.max(0.001,A),1);if(A>=0){z.drawn=false;var D=x.getCtx();var B=C.geom.getScaledTreePosition(z,A);D.translate(B.x,B.y);D.scale(A,A)}this.plotTree(z,c.merge(w,{withLabels:true,hideLabels:!!A,plotSubtree:function(I,G){var F=y.multitree&&!("$orn" in z.data);var H=F&&z.getData("orns");return !F||H.indexOf(z.getData("orn"))>-1}}),E);if(A>=0){z.drawn=true}},getAlignedPos:function(B,z,w){var y=this.node;var A,x;if(y.align=="center"){A={x:B.x-z/2,y:B.y-w/2}}else{if(y.align=="left"){x=this.config.orientation;if(x=="bottom"||x=="top"){A={x:B.x-z/2,y:B.y}}else{A={x:B.x,y:B.y-w/2}}}else{if(y.align=="right"){x=this.config.orientation;if(x=="bottom"||x=="top"){A={x:B.x-z/2,y:B.y-w}}else{A={x:B.x-z,y:B.y-w/2}}}else{throw"align: not implemented"}}}return A},getOrientation:function(w){var y=this.config;var x=y.orientation;if(y.multitree){var z=w.nodeFrom;var A=w.nodeTo;x=(("$orn" in z.data)&&z.data.$orn)||(("$orn" in A.data)&&A.data.$orn)}return x}});$jit.ST.Label={};$jit.ST.Label.Native=new q({Implements:e.Label.Native,renderLabel:function(z,B,y){var x=z.getCtx(),D=B.pos.getc(true),A=B.getData("width"),w=B.getData("height"),C=this.viz.fx.getAlignedPos(D,A,w);x.fillText(B.name,C.x+A/2,C.y+w/2)}});$jit.ST.Label.DOM=new q({Implements:e.Label.DOM,placeLabel:function(P,J,F){var B=J.pos.getc(true),O=this.viz.config,K=O.Node,x=this.viz.canvas,C=J.getData("width"),M=J.getData("height"),y=x.getSize(),G,N;var A=x.translateOffsetX,z=x.translateOffsetY,E=x.scaleOffsetX,D=x.scaleOffsetY,I=B.x*E+A,H=B.y*D+z;if(K.align=="center"){G={x:Math.round(I-C/2+y.width/2),y:Math.round(H-M/2+y.height/2)}}else{if(K.align=="left"){N=O.orientation;if(N=="bottom"||N=="top"){G={x:Math.round(I-C/2+y.width/2),y:Math.round(H+y.height/2)}}else{G={x:Math.round(I+y.width/2),y:Math.round(H-M/2+y.height/2)}}}else{if(K.align=="right"){N=O.orientation;if(N=="bottom"||N=="top"){G={x:Math.round(I-C/2+y.width/2),y:Math.round(H-M+y.height/2)}}else{G={x:Math.round(I-C+y.width/2),y:Math.round(H-M/2+y.height/2)}}}else{throw"align: not implemented"}}}var L=P.style;L.left=G.x+"px";L.top=G.y+"px";L.display=this.fitsInCanvas(G,x)?"":"none";F.onPlaceLabel(P,J)}});$jit.ST.Label.SVG=new q({Implements:[$jit.ST.Label.DOM,e.Label.SVG],initialize:function(w){this.viz=w}});$jit.ST.Label.HTML=new q({Implements:[$jit.ST.Label.DOM,e.Label.HTML],initialize:function(w){this.viz=w}});$jit.ST.Plot.NodeTypes=new q({none:{render:c.empty,contains:c.lambda(false)},circle:{render:function(x,w){var z=x.getData("dim"),A=this.getAlignedPos(x.pos.getc(true),z,z),y=z/2;this.nodeHelper.circle.render("fill",{x:A.x+y,y:A.y+y},y,w)},contains:function(w,A){var y=w.getData("dim"),z=this.getAlignedPos(w.pos.getc(true),y,y),x=y/2;this.nodeHelper.circle.contains({x:z.x+x,y:z.y+x},A,x)}},square:{render:function(x,w){var z=x.getData("dim"),y=z/2,A=this.getAlignedPos(x.pos.getc(true),z,z);this.nodeHelper.square.render("fill",{x:A.x+y,y:A.y+y},y,w)},contains:function(w,A){var y=w.getData("dim"),z=this.getAlignedPos(w.pos.getc(true),y,y),x=y/2;this.nodeHelper.square.contains({x:z.x+x,y:z.y+x},A,x)}},ellipse:{render:function(z,x){var y=z.getData("width"),w=z.getData("height"),A=this.getAlignedPos(z.pos.getc(true),y,w);this.nodeHelper.ellipse.render("fill",{x:A.x+y/2,y:A.y+w/2},y,w,x)},contains:function(y,A){var x=y.getData("width"),w=y.getData("height"),z=this.getAlignedPos(y.pos.getc(true),x,w);this.nodeHelper.ellipse.contains({x:z.x+x/2,y:z.y+w/2},A,x,w)}},rectangle:{render:function(z,x){var y=z.getData("width"),w=z.getData("height"),A=this.getAlignedPos(z.pos.getc(true),y,w);this.nodeHelper.rectangle.render("fill",{x:A.x+y/2,y:A.y+w/2},y,w,x)},contains:function(y,A){var x=y.getData("width"),w=y.getData("height"),z=this.getAlignedPos(y.pos.getc(true),x,w);this.nodeHelper.rectangle.contains({x:z.x+x/2,y:z.y+w/2},A,x,w)}}});$jit.ST.Plot.EdgeTypes=new q({none:c.empty,line:{render:function(x,z){var y=this.getOrientation(x),A=x.nodeFrom,B=x.nodeTo,w=A._depth<B._depth,D=this.viz.geom.getEdge(w?A:B,"begin",y),C=this.viz.geom.getEdge(w?B:A,"end",y);this.edgeHelper.line.render(D,C,z)},contains:function(x,D){var y=this.getOrientation(x),z=x.nodeFrom,A=x.nodeTo,w=z._depth<A._depth,C=this.viz.geom.getEdge(w?z:A,"begin",y),B=this.viz.geom.getEdge(w?A:z,"end",y);return this.edgeHelper.line.contains(C,B,D,this.edge.epsilon)}},arrow:{render:function(C,x){var B=this.getOrientation(C),y=C.nodeFrom,w=C.nodeTo,A=C.getData("dim"),E=this.viz.geom.getEdge(y,"begin",B),F=this.viz.geom.getEdge(w,"end",B),D=C.data.$direction,z=(D&&D.length>1&&D[0]!=y.id);this.edgeHelper.arrow.render(E,F,A,z,x)},contains:function(x,D){var y=this.getOrientation(x),z=x.nodeFrom,A=x.nodeTo,w=z._depth<A._depth,C=this.viz.geom.getEdge(w?z:A,"begin",y),B=this.viz.geom.getEdge(w?A:z,"end",y);return this.edgeHelper.arrow.contains(C,B,D,this.edge.epsilon)}},"quadratic:begin":{render:function(C,w){var B=this.getOrientation(C);var A=C.nodeFrom,D=C.nodeTo,F=A._depth<D._depth,x=this.viz.geom.getEdge(F?A:D,"begin",B),y=this.viz.geom.getEdge(F?D:A,"end",B),z=C.getData("dim"),E=w.getCtx();E.beginPath();E.moveTo(x.x,x.y);switch(B){case"left":E.quadraticCurveTo(x.x+z,x.y,y.x,y.y);break;case"right":E.quadraticCurveTo(x.x-z,x.y,y.x,y.y);break;case"top":E.quadraticCurveTo(x.x,x.y+z,y.x,y.y);break;case"bottom":E.quadraticCurveTo(x.x,x.y-z,y.x,y.y);break}E.stroke()}},"quadratic:end":{render:function(C,w){var B=this.getOrientation(C);var A=C.nodeFrom,D=C.nodeTo,F=A._depth<D._depth,x=this.viz.geom.getEdge(F?A:D,"begin",B),y=this.viz.geom.getEdge(F?D:A,"end",B),z=C.getData("dim"),E=w.getCtx();E.beginPath();E.moveTo(x.x,x.y);switch(B){case"left":E.quadraticCurveTo(y.x-z,y.y,y.x,y.y);break;case"right":E.quadraticCurveTo(y.x+z,y.y,y.x,y.y);break;case"top":E.quadraticCurveTo(y.x,y.y-z,y.x,y.y);break;case"bottom":E.quadraticCurveTo(y.x,y.y+z,y.x,y.y);break}E.stroke()}},bezier:{render:function(C,w){var B=this.getOrientation(C),A=C.nodeFrom,D=C.nodeTo,F=A._depth<D._depth,x=this.viz.geom.getEdge(F?A:D,"begin",B),y=this.viz.geom.getEdge(F?D:A,"end",B),z=C.getData("dim"),E=w.getCtx();E.beginPath();E.moveTo(x.x,x.y);switch(B){case"left":E.bezierCurveTo(x.x+z,x.y,y.x-z,y.y,y.x,y.y);break;case"right":E.bezierCurveTo(x.x-z,x.y,y.x+z,y.y,y.x,y.y);break;case"top":E.bezierCurveTo(x.x,x.y+z,y.x,y.y-z,y.x,y.y);break;case"bottom":E.bezierCurveTo(x.x,x.y-z,y.x,y.y+z,y.x,y.y);break}E.stroke()}}});$jit.ST.Plot.NodeTypes.implement({"areachart-stacked":{render:function(W,D){var U=W.pos.getc(true),w=W.getData("width"),A=W.getData("height"),G=this.getAlignedPos(U,w,A),ab=G.x,aa=G.y,L=W.getData("stringArray"),F=W.getData("dimArray"),B=W.getData("valueArray"),ad=c.reduce(B,function(aj,ak){return aj+ak[0]},0),ac=c.reduce(B,function(aj,ak){return aj+ak[1]},0),I=W.getData("colorArray"),C=I.length,Y=W.getData("config"),J=W.getData("gradient"),ai=Y.showLabels,N=Y.showAggregates,ae=Y.Label,T=W.getData("prev");var M=D.getCtx(),H=W.getData("border");if(I&&F&&L){for(var ah=0,af=F.length,K=0,E=0,X=0;ah<af;ah++){M.fillStyle=M.strokeStyle=I[ah%C];M.save();if(J&&(F[ah][0]>0||F[ah][1]>0)){var R=K+F[ah][0],P=E+F[ah][1],ag=Math.atan((P-R)/w),Z=55;var V=M.createLinearGradient(ab+w/2,aa-(R+P)/2,ab+w/2+Z*Math.sin(ag),aa-(R+P)/2+Z*Math.cos(ag));var Q=c.rgbToHex(c.map(c.hexToRgb(I[ah%C].slice(1)),function(x){return(x*0.85)>>0}));V.addColorStop(0,I[ah%C]);V.addColorStop(1,Q);M.fillStyle=V}M.beginPath();M.moveTo(ab,aa-K);M.lineTo(ab+w,aa-E);M.lineTo(ab+w,aa-E-F[ah][1]);M.lineTo(ab,aa-K-F[ah][0]);M.lineTo(ab,aa-K);M.fill();M.restore();if(H){var S=H.name==L[ah];var z=S?0.7:0.8;var Q=c.rgbToHex(c.map(c.hexToRgb(I[ah%C].slice(1)),function(x){return(x*z)>>0}));M.strokeStyle=Q;M.lineWidth=S?4:1;M.save();M.beginPath();if(H.index===0){M.moveTo(ab,aa-K);M.lineTo(ab,aa-K-F[ah][0])}else{M.moveTo(ab+w,aa-E);M.lineTo(ab+w,aa-E-F[ah][1])}M.stroke();M.restore()}K+=(F[ah][0]||0);E+=(F[ah][1]||0);if(F[ah][0]>0){X+=(B[ah][0]||0)}}if(T&&ae.type=="Native"){M.save();M.beginPath();M.fillStyle=M.strokeStyle=ae.color;M.font=ae.style+" "+ae.size+"px "+ae.family;M.textAlign="center";M.textBaseline="middle";var O=N(W.name,ad,ac,W,X);if(O!==false){M.fillText(O!==true?O:X,ab,aa-K-Y.labelOffset-ae.size/2,w)}if(ai(W.name,ad,ac,W)){M.fillText(W.name,ab,aa+ae.size/2+Y.labelOffset)}M.restore()}}},contains:function(C,E){var J=C.pos.getc(true),z=C.getData("width"),N=C.getData("height"),M=this.getAlignedPos(J,z,N),L=M.x,K=M.y,O=C.getData("dimArray"),w=E.x-L;if(E.x<L||E.x>L+z||E.y>K||E.y<K-N){return false}for(var F=0,D=O.length,I=K,A=K;F<D;F++){var B=O[F];I-=B[0];A-=B[1];var G=I+(A-I)*w/z;if(E.y>=G){var H=+(w>z/2);return{name:C.getData("stringArray")[F],color:C.getData("colorArray")[F],value:C.getData("valueArray")[F][H],index:H}}}return false}}});$jit.AreaChart=new q({st:null,colors:["#416D9C","#70A35E","#EBB056","#C74243","#83548B","#909291","#557EAA"],selected:{},busy:false,initialize:function(y){this.controller=this.config=c.merge(n("Canvas","Margin","Label","AreaChart"),{Label:{type:"Native"}},y);var z=this.config.showLabels,x=c.type(z),A=this.config.showAggregates,w=c.type(A);this.config.showLabels=x=="function"?z:c.lambda(z);this.config.showAggregates=w=="function"?A:c.lambda(A);this.initializeViz()},initializeViz:function(){var x=this.config,B=this,w=x.type.split(":")[0],A={};var z=new $jit.ST({injectInto:x.injectInto,width:x.width,height:x.height,orientation:"bottom",levelDistance:0,siblingOffset:0,subtreeOffset:0,withLabels:x.Label.type!="Native",useCanvas:x.useCanvas,Label:{type:x.Label.type},Node:{overridable:true,type:"areachart-"+w,align:"left",width:1,height:1},Edge:{type:"none"},Tips:{enable:x.Tips.enable,type:"Native",force:true,onShow:function(G,F,D){var E=D;x.Tips.onShow(G,E,F)}},Events:{enable:true,type:"Native",onClick:function(F,G,D){if(!x.filterOnClick&&!x.Events.enable){return}var E=G.getContains();if(E){x.filterOnClick&&B.filter(E.name)}x.Events.enable&&x.Events.onClick(E,G,D)},onRightClick:function(E,F,D){if(!x.restoreOnRightClick){return}B.restore()},onMouseMove:function(F,G,D){if(!x.selectOnHover){return}if(F){var E=G.getContains();B.select(F.id,E.name,E.index)}else{B.select(false,false,false)}}},onCreateLabel:function(J,G){var P=x.Label,O=G.getData("valueArray"),H=c.reduce(O,function(Q,R){return Q+R[0]},0),M=c.reduce(O,function(Q,R){return Q+R[1]},0);if(G.getData("prev")){var L={wrapper:document.createElement("div"),aggregate:document.createElement("div"),label:document.createElement("div")};var D=L.wrapper,N=L.label,E=L.aggregate,F=D.style,K=N.style,I=E.style;A[G.id]=L;D.appendChild(N);D.appendChild(E);if(!x.showLabels(G.name,H,M,G)){N.style.display="none"}if(!x.showAggregates(G.name,H,M,G)){E.style.display="none"}F.position="relative";F.overflow="visible";F.fontSize=P.size+"px";F.fontFamily=P.family;F.color=P.color;F.textAlign="center";I.position=K.position="absolute";J.style.width=G.getData("width")+"px";J.style.height=G.getData("height")+"px";N.innerHTML=G.name;J.appendChild(D)}},onPlaceLabel:function(V,P){if(!P.getData("prev")){return}var T=A[P.id],E=T.wrapper.style,D=T.label.style,O=T.aggregate.style,M=P.getData("width"),K=P.getData("height"),J=P.getData("dimArray"),G=P.getData("valueArray"),L=c.reduce(G,function(W,X){return W+X[0]},0),H=c.reduce(G,function(W,X){return W+X[1]},0),I=parseInt(E.fontSize,10),N=V.style;if(J&&G){if(x.showLabels(P.name,L,H,P)){D.display=""}else{D.display="none"}var F=x.showAggregates(P.name,L,H,P);if(F!==false){O.display=""}else{O.display="none"}E.width=O.width=D.width=V.style.width=M+"px";O.left=D.left=-M/2+"px";for(var S=0,Q=G.length,R=0,U=0;S<Q;S++){if(J[S][0]>0){R+=G[S][0];U+=J[S][0]}}O.top=(-I-x.labelOffset)+"px";D.top=(x.labelOffset+U)+"px";V.style.top=parseInt(V.style.top,10)-U+"px";V.style.height=E.height=U+"px";T.aggregate.innerHTML=F!==true?F:R}}});var y=z.canvas.getSize(),C=x.Margin;z.config.offsetY=-y.height/2+C.bottom+(x.showLabels&&(x.labelOffset+x.Label.size));z.config.offsetX=(C.right-C.left)/2;this.delegate=z;this.canvas=this.delegate.canvas},loadJSON:function(N){var J=c.time(),B=[],M=this.delegate,Q=c.splat(N.label),I=c.splat(N.color||this.colors),O=this.config,x=!!O.type.split(":")[1],z=O.animate;for(var K=0,y=N.values,H=y.length;K<H-1;K++){var P=y[K],E=y[K-1],F=y[K+1];var L=c.splat(y[K].values),w=c.splat(y[K+1].values);var A=c.zip(L,w);var D=0,C=0;B.push({id:J+P.label,name:P.label,data:{value:A,"$valueArray":A,"$colorArray":I,"$stringArray":Q,"$next":F.label,"$prev":E?E.label:false,"$config":O,"$gradient":x},children:[]})}var G={id:J+"$root",name:"",data:{"$type":"none","$width":1,"$height":1},children:B};M.loadJSON(G);this.normalizeDims();M.compute();M.select(M.root);if(z){M.fx.animate({modes:["node-property:height:dimArray"],duration:1500})}},updateJSON:function(G,x){if(this.busy){return}this.busy=true;var D=this.delegate,F=D.graph,A=G.label&&c.splat(G.label),E=G.values,w=this.config.animate,C=this,B={};for(var z=0,y=E.length;z<y;z++){B[E[z].label]=E[z]}F.eachNode(function(L){var H=B[L.name],I=L.getData("stringArray"),K=L.getData("valueArray"),J=L.getData("next");if(H){H.values=c.splat(H.values);c.each(K,function(M,N){M[0]=H.values[N];if(A){I[N]=A[N]}});L.setData("valueArray",K)}if(J){H=B[J];if(H){c.each(K,function(M,N){M[1]=H.values[N]})}}});this.normalizeDims();D.compute();D.select(D.root);if(w){D.fx.animate({modes:["node-property:height:dimArray"],duration:1500,onComplete:function(){C.busy=false;x&&x.onComplete()}})}},filter:function(z,A){if(this.busy){return}this.busy=true;if(this.config.Tips.enable){this.delegate.tips.hide()}this.select(false,false,false);var x=c.splat(z);var w=this.delegate.graph.getNode(this.delegate.root);var y=this;this.normalizeDims();w.eachAdjacency(function(B){var E=B.nodeTo,D=E.getData("dimArray","end"),C=E.getData("stringArray");E.setData("dimArray",c.map(D,function(G,F){return(c.indexOf(x,C[F])>-1)?G:[0,0]}),"end")});this.delegate.fx.animate({modes:["node-property:dimArray"],duration:1500,onComplete:function(){y.busy=false;A&&A.onComplete()}})},restore:function(x){if(this.busy){return}this.busy=true;if(this.config.Tips.enable){this.delegate.tips.hide()}this.select(false,false,false);this.normalizeDims();var w=this;this.delegate.fx.animate({modes:["node-property:height:dimArray"],duration:1500,onComplete:function(){w.busy=false;x&&x.onComplete()}})},select:function(B,x,w){if(!this.config.selectOnHover){return}var y=this.selected;if(y.id!=B||y.name!=x||y.index!=w){y.id=B;y.name=x;y.index=w;this.delegate.graph.eachNode(function(C){C.setData("border",false)});if(B){var A=this.delegate.graph.getNode(B);A.setData("border",y);var z=w===0?"prev":"next";z=A.getData(z);if(z){A=this.delegate.graph.getByName(z);if(A){A.setData("border",{name:x,index:1-w})}}}this.delegate.plot()}},getLegend:function(){var y={};var z;this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(A){z=A.nodeTo});var x=z.getData("colorArray"),w=x.length;c.each(z.getData("stringArray"),function(B,A){y[B]=x[A%w]});return y},getMaxValue:function(){var w=0;this.delegate.graph.eachNode(function(B){var y=B.getData("valueArray"),x=0,A=0;c.each(y,function(C){x+=+C[0];A+=+C[1]});var z=A>x?A:x;w=w>z?w:z});return w},normalizeDims:function(){var C=this.delegate.graph.getNode(this.delegate.root),z=0;C.eachAdjacency(function(){z++});var B=this.getMaxValue()||1,F=this.delegate.canvas.getSize(),y=this.config,A=y.Margin,D=y.labelOffset+y.Label.size,w=(F.width-(A.left+A.right))/z,x=y.animate,E=F.height-(A.top+A.bottom)-(y.showAggregates&&D)-(y.showLabels&&D);this.delegate.graph.eachNode(function(L){var I=0,K=0,G=[];c.each(L.getData("valueArray"),function(M){I+=+M[0];K+=+M[1];G.push([0,0])});var J=K>I?K:I;L.setData("width",w);if(x){L.setData("height",J*E/B,"end");L.setData("dimArray",c.map(L.getData("valueArray"),function(M){return[M[0]*E/B,M[1]*E/B]}),"end");var H=L.getData("dimArray");if(!H){L.setData("dimArray",G)}}else{L.setData("height",J*E/B);L.setData("dimArray",c.map(L.getData("valueArray"),function(M){return[M[0]*E/B,M[1]*E/B]}))}})}});n.BarChart={$extend:true,animate:true,type:"stacked",labelOffset:3,barsOffset:0,hoveredColor:"#9fd4ff",orientation:"horizontal",showAggregates:true,showLabels:true,Tips:{enable:false,onShow:c.empty,onHide:c.empty},Events:{enable:false,onClick:c.empty}};$jit.ST.Plot.NodeTypes.implement({"barchart-stacked":{render:function(R,C){var H=R.pos.getc(true),Q=R.getData("width"),O=R.getData("height"),M=this.getAlignedPos(H,Q,O),L=M.x,K=M.y,N=R.getData("dimArray"),F=R.getData("valueArray"),E=R.getData("colorArray"),B=E.length,Y=R.getData("stringArray");var T=C.getCtx(),w={},U=R.getData("border"),z=R.getData("gradient"),aa=R.getData("config"),A=aa.orientation=="horizontal",D=aa.showAggregates,P=aa.showLabels,J=aa.Label;if(E&&N&&Y){for(var X=0,S=N.length,W=0,G=0;X<S;X++){T.fillStyle=T.strokeStyle=E[X%B];if(z){var Z;if(A){Z=T.createLinearGradient(L+W+N[X]/2,K,L+W+N[X]/2,K+O)}else{Z=T.createLinearGradient(L,K-W-N[X]/2,L+Q,K-W-N[X]/2)}var V=c.rgbToHex(c.map(c.hexToRgb(E[X%B].slice(1)),function(x){return(x*0.5)>>0}));Z.addColorStop(0,V);Z.addColorStop(0.5,E[X%B]);Z.addColorStop(1,V);T.fillStyle=Z}if(A){T.fillRect(L+W,K,N[X],O)}else{T.fillRect(L,K-W-N[X],Q,N[X])}if(U&&U.name==Y[X]){w.acum=W;w.dimValue=N[X]}W+=(N[X]||0);G+=(F[X]||0)}if(U){T.save();T.lineWidth=2;T.strokeStyle=U.color;if(A){T.strokeRect(L+w.acum+1,K+1,w.dimValue-2,O-2)}else{T.strokeRect(L+1,K-w.acum-w.dimValue+1,Q-2,w.dimValue-2)}T.restore()}if(J.type=="Native"){T.save();T.fillStyle=T.strokeStyle=J.color;T.font=J.style+" "+J.size+"px "+J.family;T.textBaseline="middle";var I=D(R.name,G,R);if(I!==false){I=I!==true?I:G;if(A){T.textAlign="right";T.fillText(I,L+W-aa.labelOffset,K+O/2)}else{T.textAlign="center";T.fillText(I,L+Q/2,K-O-J.size/2-aa.labelOffset)}}if(P(R.name,G,R)){if(A){T.textAlign="center";T.translate(L-aa.labelOffset-J.size/2,K+O/2);T.rotate(Math.PI/2);T.fillText(R.name,0,0)}else{T.textAlign="center";T.fillText(R.name,L+Q/2,K+J.size/2+aa.labelOffset)}}T.restore()}}},contains:function(D,F){var I=D.pos.getc(true),A=D.getData("width"),N=D.getData("height"),M=this.getAlignedPos(I,A,N),L=M.x,J=M.y,O=D.getData("dimArray"),B=D.getData("config"),z=F.x-L,w=B.orientation=="horizontal";if(w){if(F.x<L||F.x>L+A||F.y>J+N||F.y<J){return false}}else{if(F.x<L||F.x>L+A||F.y>J||F.y<J-N){return false}}for(var G=0,E=O.length,K=(w?L:J);G<E;G++){var C=O[G];if(w){K+=C;var H=K;if(F.x<=H){return{name:D.getData("stringArray")[G],color:D.getData("colorArray")[G],value:D.getData("valueArray")[G],label:D.name}}}else{K-=C;var H=K;if(F.y>=H){return{name:D.getData("stringArray")[G],color:D.getData("colorArray")[G],value:D.getData("valueArray")[G],label:D.name}}}}return false}},"barchart-grouped":{render:function(S,C){var I=S.pos.getc(true),R=S.getData("width"),P=S.getData("height"),N=this.getAlignedPos(I,R,P),M=N.x,L=N.y,O=S.getData("dimArray"),G=S.getData("valueArray"),Y=G.length,F=S.getData("colorArray"),B=F.length,aa=S.getData("stringArray");var U=C.getCtx(),w={},V=S.getData("border"),z=S.getData("gradient"),ac=S.getData("config"),A=ac.orientation=="horizontal",E=ac.showAggregates,Q=ac.showLabels,K=ac.Label,D=(A?P:R)/Y;if(F&&O&&aa){for(var Z=0,T=Y,X=0,H=0;Z<T;Z++){U.fillStyle=U.strokeStyle=F[Z%B];if(z){var ab;if(A){ab=U.createLinearGradient(M+O[Z]/2,L+D*Z,M+O[Z]/2,L+D*(Z+1))}else{ab=U.createLinearGradient(M+D*Z,L-O[Z]/2,M+D*(Z+1),L-O[Z]/2)}var W=c.rgbToHex(c.map(c.hexToRgb(F[Z%B].slice(1)),function(x){return(x*0.5)>>0}));ab.addColorStop(0,W);ab.addColorStop(0.5,F[Z%B]);ab.addColorStop(1,W);U.fillStyle=ab}if(A){U.fillRect(M,L+D*Z,O[Z],D)}else{U.fillRect(M+D*Z,L-O[Z],D,O[Z])}if(V&&V.name==aa[Z]){w.acum=D*Z;w.dimValue=O[Z]}X+=(O[Z]||0);H+=(G[Z]||0)}if(V){U.save();U.lineWidth=2;U.strokeStyle=V.color;if(A){U.strokeRect(M+1,L+w.acum+1,w.dimValue-2,D-2)}else{U.strokeRect(M+w.acum+1,L-w.dimValue+1,D-2,w.dimValue-2)}U.restore()}if(K.type=="Native"){U.save();U.fillStyle=U.strokeStyle=K.color;U.font=K.style+" "+K.size+"px "+K.family;U.textBaseline="middle";var J=E(S.name,H,S);if(J!==false){J=J!==true?J:H;if(A){U.textAlign="right";U.fillText(J,M+Math.max.apply(null,O)-ac.labelOffset,L+P/2)}else{U.textAlign="center";U.fillText(J,M+R/2,L-Math.max.apply(null,O)-K.size/2-ac.labelOffset)}}if(Q(S.name,H,S)){if(A){U.textAlign="center";U.translate(M-ac.labelOffset-K.size/2,L+P/2);U.rotate(Math.PI/2);U.fillText(S.name,0,0)}else{U.textAlign="center";U.fillText(S.name,M+R/2,L+K.size/2+ac.labelOffset)}}U.restore()}}},contains:function(J,F){var B=J.pos.getc(true),I=J.getData("width"),H=J.getData("height"),E=this.getAlignedPos(B,I,H),D=E.x,C=E.y,G=J.getData("dimArray"),M=G.length,P=J.getData("config"),A=F.x-D,w=P.orientation=="horizontal",z=(w?H:I)/M;if(w){if(F.x<D||F.x>D+I||F.y>C+H||F.y<C){return false}}else{if(F.x<D||F.x>D+I||F.y>C||F.y<C-H){return false}}for(var L=0,K=G.length;L<K;L++){var O=G[L];if(w){var N=C+z*L;if(F.x<=D+O&&F.y>=N&&F.y<=N+z){return{name:J.getData("stringArray")[L],color:J.getData("colorArray")[L],value:J.getData("valueArray")[L],label:J.name}}}else{var N=D+z*L;if(F.x>=N&&F.x<=N+z&&F.y>=C-O){return{name:J.getData("stringArray")[L],color:J.getData("colorArray")[L],value:J.getData("valueArray")[L],label:J.name}}}}return false}}});$jit.BarChart=new q({st:null,colors:["#416D9C","#70A35E","#EBB056","#C74243","#83548B","#909291","#557EAA"],selected:{},busy:false,initialize:function(y){this.controller=this.config=c.merge(n("Canvas","Margin","Label","BarChart"),{Label:{type:"Native"}},y);var z=this.config.showLabels,x=c.type(z),A=this.config.showAggregates,w=c.type(A);this.config.showLabels=x=="function"?z:c.lambda(z);this.config.showAggregates=w=="function"?A:c.lambda(A);this.initializeViz()},initializeViz:function(){var x=this.config,B=this;var w=x.type.split(":")[0],D=x.orientation=="horizontal",A={};var z=new $jit.ST({injectInto:x.injectInto,width:x.width,height:x.height,orientation:D?"left":"bottom",levelDistance:0,siblingOffset:x.barsOffset,subtreeOffset:0,withLabels:x.Label.type!="Native",useCanvas:x.useCanvas,Label:{type:x.Label.type},Node:{overridable:true,type:"barchart-"+w,align:"left",width:1,height:1},Edge:{type:"none"},Tips:{enable:x.Tips.enable,type:"Native",force:true,onShow:function(H,G,E){var F=E;x.Tips.onShow(H,F,G)}},Events:{enable:true,type:"Native",onClick:function(G,H,E){if(!x.Events.enable){return}var F=H.getContains();x.Events.onClick(F,H,E)},onMouseMove:function(G,H,E){if(!x.hoveredColor){return}if(G){var F=H.getContains();B.select(G.id,F.name,F.index)}else{B.select(false,false,false)}}},onCreateLabel:function(J,H){var P=x.Label,N=H.getData("valueArray"),M=c.reduce(N,function(Q,R){return Q+R},0);var L={wrapper:document.createElement("div"),aggregate:document.createElement("div"),label:document.createElement("div")};var E=L.wrapper,O=L.label,F=L.aggregate,G=E.style,K=O.style,I=F.style;A[H.id]=L;E.appendChild(O);E.appendChild(F);if(!x.showLabels(H.name,M,H)){K.display="none"}if(!x.showAggregates(H.name,M,H)){I.display="none"}G.position="relative";G.overflow="visible";G.fontSize=P.size+"px";G.fontFamily=P.family;G.color=P.color;G.textAlign="center";I.position=K.position="absolute";J.style.width=H.getData("width")+"px";J.style.height=H.getData("height")+"px";I.left=K.left="0px";O.innerHTML=H.name;J.appendChild(E)},onPlaceLabel:function(U,P){if(!A[P.id]){return}var T=A[P.id],G=T.wrapper.style,E=T.label.style,O=T.aggregate.style,V=x.type.split(":")[0]=="grouped",F=x.orientation=="horizontal",K=P.getData("dimArray"),I=P.getData("valueArray"),M=(V&&F)?Math.max.apply(null,K):P.getData("width"),L=(V&&!F)?Math.max.apply(null,K):P.getData("height"),J=parseInt(G.fontSize,10),N=U.style;if(K&&I){G.width=O.width=E.width=U.style.width=M+"px";for(var S=0,Q=I.length,R=0;S<Q;S++){if(K[S]>0){R+=I[S]}}if(x.showLabels(P.name,R,P)){E.display=""}else{E.display="none"}var H=x.showAggregates(P.name,R,P);if(H!==false){O.display=""}else{O.display="none"}if(x.orientation=="horizontal"){O.textAlign="right";E.textAlign="left";E.textIndex=O.textIndent=x.labelOffset+"px";O.top=E.top=(L-J)/2+"px";U.style.height=G.height=L+"px"}else{O.top=(-J-x.labelOffset)+"px";E.top=(x.labelOffset+L)+"px";U.style.top=parseInt(U.style.top,10)-L+"px";U.style.height=G.height=L+"px"}T.aggregate.innerHTML=H!==true?H:R}}});var y=z.canvas.getSize(),C=x.Margin;if(D){z.config.offsetX=y.width/2-C.left-(x.showLabels&&(x.labelOffset+x.Label.size));z.config.offsetY=(C.bottom-C.top)/2}else{z.config.offsetY=-y.height/2+C.bottom+(x.showLabels&&(x.labelOffset+x.Label.size));z.config.offsetX=(C.right-C.left)/2}this.delegate=z;this.canvas=this.delegate.canvas},loadJSON:function(K){if(this.busy){return}this.busy=true;var H=c.time(),C=[],J=this.delegate,N=c.splat(K.label),G=c.splat(K.color||this.colors),L=this.config,w=!!L.type.split(":")[1],z=L.animate,y=L.orientation=="horizontal",A=this;for(var I=0,x=K.values,E=x.length;I<E;I++){var M=x[I];var B=c.splat(x[I].values);var F=0;C.push({id:H+M.label,name:M.label,data:{value:B,"$valueArray":B,"$colorArray":G,"$stringArray":N,"$gradient":w,"$config":L},children:[]})}var D={id:H+"$root",name:"",data:{"$type":"none","$width":1,"$height":1},children:C};J.loadJSON(D);this.normalizeDims();J.compute();J.select(J.root);if(z){if(y){J.fx.animate({modes:["node-property:width:dimArray"],duration:1500,onComplete:function(){A.busy=false}})}else{J.fx.animate({modes:["node-property:height:dimArray"],duration:1500,onComplete:function(){A.busy=false}})}}else{this.busy=false}},updateJSON:function(y,C){if(this.busy){return}this.busy=true;this.select(false,false,false);var z=this.delegate;var B=z.graph;var x=y.values;var w=this.config.animate;var A=this;var D=this.config.orientation=="horizontal";c.each(x,function(E){var F=B.getByName(E.label);if(F){F.setData("valueArray",c.splat(E.values));if(y.label){F.setData("stringArray",c.splat(y.label))}}});this.normalizeDims();z.compute();z.select(z.root);if(w){if(D){z.fx.animate({modes:["node-property:width:dimArray"],duration:1500,onComplete:function(){A.busy=false;C&&C.onComplete()}})}else{z.fx.animate({modes:["node-property:height:dimArray"],duration:1500,onComplete:function(){A.busy=false;C&&C.onComplete()}})}}},select:function(y,w){if(!this.config.hoveredColor){return}var x=this.selected;if(x.id!=y||x.name!=w){x.id=y;x.name=w;x.color=this.config.hoveredColor;this.delegate.graph.eachNode(function(z){if(y==z.id){z.setData("border",x)}else{z.setData("border",false)}});this.delegate.plot()}},getLegend:function(){var y={};var z;this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(A){z=A.nodeTo});var x=z.getData("colorArray"),w=x.length;c.each(z.getData("stringArray"),function(B,A){y[B]=x[A%w]});return y},getMaxValue:function(){var x=0,w=this.config.type.split(":")[0]=="stacked";this.delegate.graph.eachNode(function(A){var y=A.getData("valueArray"),z=0;if(!y){return}if(w){c.each(y,function(B){z+=+B})}else{z=Math.max.apply(null,y)}x=x>z?x:z});return x},setBarType:function(w){this.config.type=w;this.delegate.config.Node.type="barchart-"+w.split(":")[0]},normalizeDims:function(){var G=this.delegate.graph.getNode(this.delegate.root),B=0;G.eachAdjacency(function(){B++});var D=this.getMaxValue()||1,J=this.delegate.canvas.getSize(),z=this.config,C=z.Margin,H=C.left+C.right,A=C.top+C.bottom,x=z.orientation=="horizontal",w=(J[x?"height":"width"]-(x?A:H)-(B-1)*z.barsOffset)/B,y=z.animate,I=J[x?"width":"height"]-(x?H:A)-(!x&&z.showAggregates&&(z.Label.size+z.labelOffset))-(z.showLabels&&(z.Label.size+z.labelOffset)),F=x?"height":"width",E=x?"width":"height";this.delegate.graph.eachNode(function(N){var M=0,K=[];c.each(N.getData("valueArray"),function(O){M+=+O;K.push(0)});N.setData(F,w);if(y){N.setData(E,M*I/D,"end");N.setData("dimArray",c.map(N.getData("valueArray"),function(O){return O*I/D}),"end");var L=N.getData("dimArray");if(!L){N.setData("dimArray",K)}}else{N.setData(E,M*I/D);N.setData("dimArray",c.map(N.getData("valueArray"),function(O){return O*I/D}))}})}});n.PieChart={$extend:true,animate:true,offset:25,sliceOffset:0,labelOffset:3,type:"stacked",hoveredColor:"#9fd4ff",Events:{enable:false,onClick:c.empty},Tips:{enable:false,onShow:c.empty,onHide:c.empty},showLabels:true,resizeLabels:false,updateHeights:false};g.Radial=new q({compute:function(x){var y=c.splat(x||["current","start","end"]);f.compute(this.graph,y,this.config);this.graph.computeLevels(this.root,0,"ignore");var w=this.createLevelDistanceFunc();this.computeAngularWidths(y);this.computePositions(y,w)},computePositions:function(D,A){var F=D;var E=this.graph;var B=E.getNode(this.root);var C=this.parent;var w=this.config;for(var y=0,x=F.length;y<x;y++){var z=F[y];B.setPos(k(0,0),z);B.setData("span",Math.PI*2,z)}B.angleSpan={begin:0,end:2*Math.PI};E.eachBFS(this.root,function(K){var Q=K.angleSpan.end-K.angleSpan.begin;var S=K.angleSpan.begin;var R=A(K);var T=0,G=[],J={};K.eachSubnode(function(W){T+=W._treeAngularWidth;for(var X=0,V=F.length;X<V;X++){var Z=F[X],Y=W.getData("dim",Z);J[Z]=(Z in J)?(Y>J[Z]?Y:J[Z]):Y}G.push(W)},"ignore");if(C&&C.id==K.id&&G.length>0&&G[0].dist){G.sort(function(W,V){return(W.dist>=V.dist)-(W.dist<=V.dist)})}for(var M=0,O=G.length;M<O;M++){var I=G[M];if(!I._flag){var U=I._treeAngularWidth/T*Q;var H=S+U/2;for(var N=0,L=F.length;N<L;N++){var P=F[N];I.setPos(k(H,R),P);I.setData("span",U,P);I.setData("dim-quotient",I.getData("dim",P)/J[P],P)}I.angleSpan={begin:S,end:S+U};S+=U}}},"ignore")},setAngularWidthForNodes:function(w){this.graph.eachBFS(this.root,function(z,x){var y=z.getData("angularWidth",w[0])||5;z._angularWidth=y/x},"ignore")},setSubtreesAngularWidth:function(){var w=this;this.graph.eachNode(function(x){w.setSubtreeAngularWidth(x)},"ignore")},setSubtreeAngularWidth:function(z){var y=this,x=z._angularWidth,w=0;z.eachSubnode(function(A){y.setSubtreeAngularWidth(A);w+=A._treeAngularWidth},"ignore");z._treeAngularWidth=Math.max(x,w)},computeAngularWidths:function(w){this.setAngularWidthForNodes(w);this.setSubtreesAngularWidth()}});$jit.Sunburst=new q({Implements:[d,o,g.Radial],initialize:function(w){var y=$jit.Sunburst;var x={interpolation:"linear",levelDistance:100,Node:{type:"multipie",height:0},Edge:{type:"none"},Label:{textAlign:"start",textBaseline:"middle"}};this.controller=this.config=c.merge(n("Canvas","Node","Edge","Fx","Tips","NodeStyles","Events","Navigation","Controller","Label"),x,w);var z=this.config;if(z.useCanvas){this.canvas=z.useCanvas;this.config.labelContainer=this.canvas.id+"-label"}else{if(z.background){z.background=c.merge({type:"Circles"},z.background)}this.canvas=new l(this,z);this.config.labelContainer=(typeof z.injectInto=="string"?z.injectInto:z.injectInto.id)+"-label"}this.graphOptions={klass:b,Node:{selected:false,exist:true,drawn:true}};this.graph=new e(this.graphOptions,this.config.Node,this.config.Edge);this.labels=new y.Label[z.Label.type](this);this.fx=new y.Plot(this,y);this.op=new y.Op(this);this.json=null;this.root=null;this.rotated=null;this.busy=false;this.initializeExtras()},createLevelDistanceFunc:function(){var w=this.config.levelDistance;return function(x){return(x._depth+1)*w}},refresh:function(){this.compute();this.plot()},reposition:function(){this.compute("end")},rotate:function(y,z,x){var w=y.getPos(x.property||"current").getp(true).theta;this.rotated=y;this.rotateAngle(-w,z,x)},rotateAngle:function(y,B,x){var z=this;var w=c.merge(this.config,x||{},{modes:["polar"]});var A=x.property||(B==="animate"?"end":"current");if(B==="animate"){this.fx.animation.pause()}this.graph.eachNode(function(D){var C=D.getPos(A);C.theta+=y;if(C.theta<0){C.theta+=Math.PI*2}});if(B=="animate"){this.fx.animate(w)}else{if(B=="replot"){this.fx.plot();this.busy=false}}},plot:function(){this.fx.plot()}});$jit.Sunburst.$extend=true;(function(w){w.Op=new q({Implements:e.Op});w.Plot=new q({Implements:e.Plot});w.Label={};w.Label.Native=new q({Implements:e.Label.Native,initialize:function(x){this.viz=x;this.label=x.config.Label;this.config=x.config},renderLabel:function(C,E,G){var N=E.getData("span");if(N<Math.PI/2&&Math.tan(N)*this.config.levelDistance*E._depth<10){return}var O=C.getCtx();var A=O.measureText(E.name);if(E.id==this.viz.root){var M=-A.width/2,K=0,L=0;var z=0}else{var D=5;var z=G.levelDistance-D;var J=E.pos.clone();J.rho+=D;var B=J.getp(true);var H=J.getc(true);var M=H.x,K=H.y;var F=Math.PI;var I=(B.theta>F/2&&B.theta<3*F/2);var L=I?B.theta+F:B.theta;if(I){M-=Math.abs(Math.cos(B.theta)*A.width);K+=Math.sin(B.theta)*A.width}else{if(E.id==this.viz.root){M-=A.width/2}}}O.save();O.translate(M,K);O.rotate(L);O.fillText(E.name,0,0);O.restore()}});w.Label.SVG=new q({Implements:e.Label.SVG,initialize:function(x){this.viz=x},placeLabel:function(N,C,E){var J=C.pos.getc(true),M=this.viz,A=this.viz.canvas;var F=A.getSize();var B={x:Math.round(J.x+F.width/2),y:Math.round(J.y+F.height/2)};N.setAttribute("x",B.x);N.setAttribute("y",B.y);var G=N.getBBox();if(G){var L=N.getAttribute("x");var I=N.getAttribute("y");var z=C.pos.getp(true);var D=Math.PI;var H=(z.theta>D/2&&z.theta<3*D/2);if(H){N.setAttribute("x",L-G.width);N.setAttribute("y",I-G.height)}else{if(C.id==M.root){N.setAttribute("x",L-G.width/2)}}var K=H?z.theta+D:z.theta;if(C._depth){N.setAttribute("transform","rotate("+K*360/(2*D)+" "+L+" "+I+")")}}E.onPlaceLabel(N,C)}});w.Label.HTML=new q({Implements:e.Label.HTML,initialize:function(x){this.viz=x},placeLabel:function(G,A,C){var E=A.pos.clone(),y=this.viz.canvas,F=A.getData("height"),B=((F||A._depth==0)?F:this.viz.config.levelDistance)/2,D=y.getSize();E.rho+=B;E=E.getc(true);var z={x:Math.round(E.x+D.width/2),y:Math.round(E.y+D.height/2)};var x=G.style;x.left=z.x+"px";x.top=z.y+"px";x.display=this.fitsInCanvas(z,y)?"":"none";C.onPlaceLabel(G,A)}});w.Plot.NodeTypes=new q({none:{render:c.empty,contains:c.lambda(false),anglecontains:function(B,D){var A=B.getData("span")/2,y=B.pos.theta;var z=y-A,x=y+A;if(z<0){z+=Math.PI*2}var C=Math.atan2(D.y,D.x);if(C<0){C+=Math.PI*2}if(z>x){return(C>z&&C<=Math.PI*2)||C<x}else{return C>z&&C<x}}},pie:{render:function(C,A){var G=C.getData("span")/2,z=C.pos.theta;var B=z-G,D=z+G;var F=C.pos.getp(true);var x=new b(F.rho,B);var y=x.getc(true);x.theta=D;var E=x.getc(true);var H=A.getCtx();H.beginPath();H.moveTo(0,0);H.lineTo(y.x,y.y);H.moveTo(0,0);H.lineTo(E.x,E.y);H.moveTo(0,0);H.arc(0,0,F.rho*C.getData("dim-quotient"),B,D,false);H.fill()},contains:function(z,B){if(this.nodeTypes.none.anglecontains.call(this,z,B)){var x=Math.sqrt(B.x*B.x+B.y*B.y);var y=this.config.levelDistance,A=z._depth;return(x<=y*A)}return false}},multipie:{render:function(D,B){var K=D.getData("height");var E=K?K:this.config.levelDistance;var J=D.getData("span")/2,A=D.pos.theta;var C=A-J,G=A+J;var I=D.pos.getp(true);var y=new b(I.rho,C);var z=y.getc(true);y.theta=G;var H=y.getc(true);y.rho+=E;var x=y.getc(true);y.theta=C;var F=y.getc(true);var L=B.getCtx();L.moveTo(0,0);L.beginPath();L.arc(0,0,I.rho,C,G,false);L.arc(0,0,I.rho+E,G,C,true);L.moveTo(z.x,z.y);L.lineTo(F.x,F.y);L.moveTo(H.x,H.y);L.lineTo(x.x,x.y);L.fill();if(D.collapsed){L.save();L.lineWidth=2;L.moveTo(0,0);L.beginPath();L.arc(0,0,I.rho+E+5,G-0.01,C+0.01,true);L.stroke();L.restore()}},contains:function(A,D){if(this.nodeTypes.none.anglecontains.call(this,A,D)){var y=Math.sqrt(D.x*D.x+D.y*D.y);var x=A.getData("height");var B=x?x:this.config.levelDistance;var z=this.config.levelDistance,C=A._depth;return(y>=z*C)&&(y<=(z*C+B))}return false}},"gradient-multipie":{render:function(A,x){var F=x.getCtx();var E=A.getData("height");var B=E?E:this.config.levelDistance;var y=F.createRadialGradient(0,0,A.getPos().rho,0,0,A.getPos().rho+B);var D=c.hexToRgb(A.getData("color")),C=[];c.each(D,function(G){C.push(parseInt(G*0.5,10))});var z=c.rgbToHex(C);y.addColorStop(0,z);y.addColorStop(1,A.getData("color"));F.fillStyle=y;this.nodeTypes.multipie.render.call(this,A,x)},contains:function(x,y){return this.nodeTypes.multipie.contains.call(this,x,y)}},"gradient-pie":{render:function(C,z){var x=z.getCtx();var D=x.createRadialGradient(0,0,0,0,0,C.getPos().rho);var B=c.hexToRgb(C.getData("color")),y=[];c.each(B,function(E){y.push(parseInt(E*0.5,10))});var A=c.rgbToHex(y);D.addColorStop(1,A);D.addColorStop(0,C.getData("color"));x.fillStyle=D;this.nodeTypes.pie.render.call(this,C,z)},contains:function(x,y){return this.nodeTypes.pie.contains.call(this,x,y)}}});w.Plot.EdgeTypes=new q({none:c.empty,line:{render:function(x,y){var A=x.nodeFrom.pos.getc(true),z=x.nodeTo.pos.getc(true);this.edgeHelper.line.render(A,z,y)},contains:function(x,A){var z=x.nodeFrom.pos.getc(true),y=x.nodeTo.pos.getc(true);return this.edgeHelper.line.contains(z,y,A,this.edge.epsilon)}},arrow:{render:function(y,z){var D=y.nodeFrom.pos.getc(true),C=y.nodeTo.pos.getc(true),B=y.getData("dim"),A=y.data.$direction,x=(A&&A.length>1&&A[0]!=y.nodeFrom.id);this.edgeHelper.arrow.render(D,C,B,x,z)},contains:function(x,A){var z=x.nodeFrom.pos.getc(true),y=x.nodeTo.pos.getc(true);return this.edgeHelper.arrow.contains(z,y,A,this.edge.epsilon)}},hyperline:{render:function(x,y){var B=x.nodeFrom.pos.getc(),A=x.nodeTo.pos.getc(),z=Math.max(B.norm(),A.norm());this.edgeHelper.hyperline.render(B.$scale(1/z),A.$scale(1/z),z,y)},contains:c.lambda(false)}})})($jit.Sunburst);$jit.Sunburst.Plot.NodeTypes.implement({"piechart-stacked":{render:function(U,A){var T=U.pos.getp(true),C=U.getData("dimArray"),S=U.getData("valueArray"),G=U.getData("colorArray"),z=G.length,M=U.getData("stringArray"),P=U.getData("span")/2,K=U.pos.theta,F=K-P,J=K+P,R=new b;var N=A.getCtx(),L={},I=U.getData("gradient"),D=U.getData("border"),Z=U.getData("config"),ai=Z.showLabels,Y=Z.resizeLabels,ab=Z.Label;var ae=Z.sliceOffset*Math.cos((F+J)/2);var E=Z.sliceOffset*Math.sin((F+J)/2);if(G&&C&&M){for(var af=0,ac=C.length,w=0,X=0;af<ac;af++){var B=C[af],ag=G[af%z];if(B<=0){continue}N.fillStyle=N.strokeStyle=ag;if(I&&B){var ad=N.createRadialGradient(ae,E,w+Z.sliceOffset,ae,E,w+B+Z.sliceOffset);var x=c.hexToRgb(ag),W=c.map(x,function(al){return(al*0.8)>>0}),y=c.rgbToHex(W);ad.addColorStop(0,ag);ad.addColorStop(0.5,ag);ad.addColorStop(1,y);N.fillStyle=ad}R.rho=w+Z.sliceOffset;R.theta=F;var ah=R.getc(true);R.theta=J;var O=R.getc(true);R.rho+=B;var aj=R.getc(true);R.theta=F;var Q=R.getc(true);N.beginPath();N.arc(ae,E,w+0.01,F,J,false);N.arc(ae,E,w+B+0.01,J,F,true);N.fill();if(D&&D.name==M[af]){L.acum=w;L.dimValue=C[af];L.begin=F;L.end=J}w+=(B||0);X+=(S[af]||0)}if(D){N.save();N.globalCompositeOperation="source-over";N.lineWidth=2;N.strokeStyle=D.color;var aa=F<J?1:-1;N.beginPath();N.arc(ae,E,L.acum+0.01+1,L.begin,L.end,false);N.arc(ae,E,L.acum+L.dimValue+0.01-1,L.end,L.begin,true);N.closePath();N.stroke();N.restore()}if(ai&&ab.type=="Native"){N.save();N.fillStyle=N.strokeStyle=ab.color;var V=Y?U.getData("normalizedDim"):1,H=(ab.size*V)>>0;H=H<+Y?+Y:H;N.font=ab.style+" "+H+"px "+ab.family;N.textBaseline="middle";N.textAlign="center";R.rho=w+Z.labelOffset+Z.sliceOffset;R.theta=U.pos.theta;var ak=R.getc(true);N.fillText(U.name,ak.x,ak.y);N.restore()}}},contains:function(z,D){if(this.nodeTypes.none.anglecontains.call(this,z,D)){var F=Math.sqrt(D.x*D.x+D.y*D.y);var w=this.config.levelDistance,C=z._depth;var x=z.getData("config");if(F<=w*C+x.sliceOffset){var G=z.getData("dimArray");for(var B=0,A=G.length,E=x.sliceOffset;B<A;B++){var y=G[B];if(F>=E&&F<=E+y){return{name:z.getData("stringArray")[B],color:z.getData("colorArray")[B],value:z.getData("valueArray")[B],label:z.name}}E+=y}}return false}return false}}});$jit.PieChart=new q({sb:null,colors:["#416D9C","#70A35E","#EBB056","#C74243","#83548B","#909291","#557EAA"],selected:{},busy:false,initialize:function(w){this.controller=this.config=c.merge(n("Canvas","PieChart","Label"),{Label:{type:"Native"}},w);this.initializeViz()},initializeViz:function(){var x=this.config,B=this;var w=x.type.split(":")[0];var A=new $jit.Sunburst({injectInto:x.injectInto,width:x.width,height:x.height,useCanvas:x.useCanvas,withLabels:x.Label.type!="Native",Label:{type:x.Label.type},Node:{overridable:true,type:"piechart-"+w,width:1,height:1},Edge:{type:"none"},Tips:{enable:x.Tips.enable,type:"Native",force:true,onShow:function(F,E,C){var D=C;x.Tips.onShow(F,D,E)}},Events:{enable:true,type:"Native",onClick:function(E,F,C){if(!x.Events.enable){return}var D=F.getContains();x.Events.onClick(D,F,C)},onMouseMove:function(E,F,C){if(!x.hoveredColor){return}if(E){var D=F.getContains();B.select(E.id,D.name,D.index)}else{B.select(false,false,false)}}},onCreateLabel:function(F,E){var C=x.Label;if(x.showLabels){var D=F.style;D.fontSize=C.size+"px";D.fontFamily=C.family;D.color=C.color;D.textAlign="center";F.innerHTML=E.name}},onPlaceLabel:function(S,M){if(!x.showLabels){return}var G=M.pos.getp(true),J=M.getData("dimArray"),P=M.getData("span")/2,H=M.pos.theta,R=H-P,D=H+P,U=new b;var L=x.showLabels,F=x.resizeLabels,I=x.Label;if(J){for(var Q=0,N=J.length,O=0;Q<N;Q++){O+=J[Q]}var T=F?M.getData("normalizedDim"):1,C=(I.size*T)>>0;C=C<+F?+F:C;S.style.fontSize=C+"px";U.rho=O+x.labelOffset+x.sliceOffset;U.theta=(R+D)/2;var G=U.getc(true);var E=B.canvas.getSize();var K={x:Math.round(G.x+E.width/2),y:Math.round(G.y+E.height/2)};S.style.left=K.x+"px";S.style.top=K.y+"px"}}});var z=A.canvas.getSize(),y=Math.min;A.config.levelDistance=y(z.width,z.height)/2-x.offset-x.sliceOffset;this.delegate=A;this.canvas=this.delegate.canvas;this.canvas.getCtx().globalCompositeOperation="lighter"},loadJSON:function(K){var H=c.time(),B=[],J=this.delegate,N=c.splat(K.label),D=N.length,G=c.splat(K.color||this.colors),y=G.length,L=this.config,w=!!L.type.split(":")[1],z=L.animate,F=D==1;for(var I=0,x=K.values,E=x.length;I<E;I++){var M=x[I];var A=c.splat(M.values);B.push({id:H+M.label,name:M.label,data:{value:A,"$valueArray":A,"$colorArray":F?c.splat(G[I%y]):G,"$stringArray":N,"$gradient":w,"$config":L,"$angularWidth":c.reduce(A,function(O,P){return O+P})},children:[]})}var C={id:H+"$root",name:"",data:{"$type":"none","$width":1,"$height":1},children:B};J.loadJSON(C);this.normalizeDims();J.refresh();if(z){J.fx.animate({modes:["node-property:dimArray"],duration:1500})}},updateJSON:function(y,C){if(this.busy){return}this.busy=true;var z=this.delegate;var B=z.graph;var x=y.values;var w=this.config.animate;var A=this;c.each(x,function(D){var F=B.getByName(D.label),E=c.splat(D.values);if(F){F.setData("valueArray",E);F.setData("angularWidth",c.reduce(E,function(G,H){return G+H}));if(y.label){F.setData("stringArray",c.splat(y.label))}}});this.normalizeDims();if(w){z.compute("end");z.fx.animate({modes:["node-property:dimArray:span","linear"],duration:1500,onComplete:function(){A.busy=false;C&&C.onComplete()}})}else{z.refresh()}},select:function(y,w){if(!this.config.hoveredColor){return}var x=this.selected;if(x.id!=y||x.name!=w){x.id=y;x.name=w;x.color=this.config.hoveredColor;this.delegate.graph.eachNode(function(z){if(y==z.id){z.setData("border",x)}else{z.setData("border",false)}});this.delegate.plot()}},getLegend:function(){var y={};var z;this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(A){z=A.nodeTo});var x=z.getData("colorArray"),w=x.length;c.each(z.getData("stringArray"),function(B,A){y[B]=x[A%w]});return y},getMaxValue:function(){var w=0;this.delegate.graph.eachNode(function(z){var x=z.getData("valueArray"),y=0;c.each(x,function(A){y+=+A});w=w>y?w:y});return w},normalizeDims:function(){var x=this.delegate.graph.getNode(this.delegate.root),w=0;x.eachAdjacency(function(){w++});var B=this.getMaxValue()||1,A=this.config,y=A.animate,z=this.delegate.config.levelDistance;this.delegate.graph.eachNode(function(G){var F=0,C=[];c.each(G.getData("valueArray"),function(H){F+=+H;C.push(1)});var E=(C.length==1)&&!A.updateHeights;if(y){G.setData("dimArray",c.map(G.getData("valueArray"),function(H){return E?z:(H*z/B)}),"end");var D=G.getData("dimArray");if(!D){G.setData("dimArray",C)}}else{G.setData("dimArray",c.map(G.getData("valueArray"),function(H){return E?z:(H*z/B)}))}G.setData("normalizedDim",F/B)})}});g.TM={};g.TM.SliceAndDice=new q({compute:function(B){var x=this.graph.getNode(this.clickedNode&&this.clickedNode.id||this.root);this.controller.onBeforeCompute(x);var z=this.canvas.getSize(),y=this.config,A=z.width,w=z.height;this.graph.computeLevels(this.root,0,"ignore");x.getPos(B).setc(-A/2,-w/2);x.setData("width",A,B);x.setData("height",w+y.titleHeight,B);this.computePositions(x,x,this.layout.orientation,B);this.controller.onAfterCompute(x)},computePositions:function(F,D,P,y){var M=0;F.eachSubnode(function(R){M+=R.getData("area",y)});var Q=this.config,N=Q.offset,J=F.getData("width",y),H=Math.max(F.getData("height",y)-Q.titleHeight,0),x=F==D?1:(D.getData("area",y)/M);var I,G,L,B,A,E,C;var O=(P=="h");if(O){P="v";I=H;G=J*x;L="height";B="y";A="x";E=Q.titleHeight;C=0}else{P="h";I=H*x;G=J;L="width";B="x";A="y";E=0;C=Q.titleHeight}var w=D.getPos(y);D.setData("width",G,y);D.setData("height",I,y);var K=0,z=this;D.eachSubnode(function(S){var R=S.getPos(y);R[B]=K+w[B]+E;R[A]=w[A]+C;z.computePositions(D,S,P,y);K+=S.getData(L,y)})}});g.TM.Area={compute:function(w){w=w||"current";var C=this.graph.getNode(this.clickedNode&&this.clickedNode.id||this.root);this.controller.onBeforeCompute(C);var y=this.config,F=this.canvas.getSize(),x=F.width,E=F.height,D=y.offset,z=x-D,B=E-D;this.graph.computeLevels(this.root,0,"ignore");C.getPos(w).setc(-x/2,-E/2);C.setData("width",x,w);C.setData("height",E,w);var A={top:-E/2+y.titleHeight,left:-x/2,width:z,height:B-y.titleHeight};this.computePositions(C,A,w);this.controller.onAfterCompute(C)},computeDim:function(B,C,E,A,z,x){if(B.length+C.length==1){var y=(B.length==1)?B:C;this.layoutLast(y,E,A,x);return}if(B.length>=2&&C.length==0){C=[B.shift()]}if(B.length==0){if(C.length>0){this.layoutRow(C,E,A,x)}return}var D=B[0];if(z(C,E)>=z([D].concat(C),E)){this.computeDim(B.slice(1),C.concat([D]),E,A,z,x)}else{var F=this.layoutRow(C,E,A,x);this.computeDim(B,[],F.dim,F,z,x)}},worstAspectRatio:function(x,F){if(!x||x.length==0){return Number.MAX_VALUE}var y=0,G=0,B=Number.MAX_VALUE;for(var D=0,C=x.length;D<C;D++){var z=x[D]._area;y+=z;B=B<z?B:z;G=G>z?G:z}var E=F*F,A=y*y;return Math.max(E*G/A,A/(E*B))},avgAspectRatio:function(B,y){if(!B||B.length==0){return Number.MAX_VALUE}var D=0;for(var z=0,x=B.length;z<x;z++){var C=B[z]._area;var A=C/y;D+=y>A?y/A:A/y}return D/x},layoutLast:function(y,x,B,A){var z=y[0];z.getPos(A).setc(B.left,B.top);z.setData("width",B.width,A);z.setData("height",B.height,A)}};g.TM.Squarified=new q({Implements:g.TM.Area,computePositions:function(A,D,x){var z=this.config,F=Math.max;if(D.width>=D.height){this.layout.orientation="h"}else{this.layout.orientation="v"}var w=A.getSubnodes([1,1],"ignore");if(w.length>0){this.processChildrenLayout(A,w,D,x);for(var C=0,B=w.length;C<B;C++){var G=w[C],H=z.offset,I=F(G.getData("height",x)-H-z.titleHeight,0),y=F(G.getData("width",x)-H,0),E=G.getPos(x);D={width:y,height:I,top:E.y+z.titleHeight,left:E.x};this.computePositions(G,D,x)}}},processChildrenLayout:function(G,w,C,x){var A=C.width*C.height;var B,y=w.length,D=0,H=[];for(B=0;B<y;B++){H[B]=parseFloat(w[B].getData("area",x));D+=H[B]}for(B=0;B<y;B++){w[B]._area=A*H[B]/D}var z=this.layout.horizontal()?C.height:C.width;w.sort(function(J,I){var K=I._area-J._area;return K?K:(I.id==J.id?0:(I.id<J.id?1:-1))});var F=[w[0]];var E=w.slice(1);this.squarify(E,F,z,C,x)},squarify:function(y,B,x,A,z){this.computeDim(y,B,x,A,this.worstAspectRatio,z)},layoutRow:function(y,x,A,z){if(this.layout.horizontal()){return this.layoutV(y,x,A,z)}else{return this.layoutH(y,x,A,z)}},layoutV:function(x,I,E,y){var J=0,A=function(w){return w};c.each(x,function(w){J+=w._area});var z=A(J/I),F=0;for(var C=0,B=x.length;C<B;C++){var D=A(x[C]._area/z);var G=x[C];G.getPos(y).setc(E.left,E.top+F);G.setData("width",z,y);G.setData("height",D,y);F+=D}var H={height:E.height,width:E.width-z,top:E.top,left:E.left+z};H.dim=Math.min(H.width,H.height);if(H.dim!=H.height){this.layout.change()}return H},layoutH:function(x,G,C,y){var I=0;c.each(x,function(w){I+=w._area});var H=I/G,D=C.top,z=0;for(var B=0,A=x.length;B<A;B++){var E=x[B];var G=E._area/H;E.getPos(y).setc(C.left+z,D);E.setData("width",G,y);E.setData("height",H,y);z+=G}var F={height:C.height-H,width:C.width,top:C.top+H,left:C.left};F.dim=Math.min(F.width,F.height);if(F.dim!=F.width){this.layout.change()}return F}});g.TM.Strip=new q({Implements:g.TM.Area,computePositions:function(A,D,x){var w=A.getSubnodes([1,1],"ignore"),z=this.config,F=Math.max;if(w.length>0){this.processChildrenLayout(A,w,D,x);for(var C=0,B=w.length;C<B;C++){var G=w[C];var H=z.offset,I=F(G.getData("height",x)-H-z.titleHeight,0),y=F(G.getData("width",x)-H,0);var E=G.getPos(x);D={width:y,height:I,top:E.y+z.titleHeight,left:E.x};this.computePositions(G,D,x)}}},processChildrenLayout:function(G,w,B,x){var z=B.width*B.height;var A,y=w.length,C=0,H=[];for(A=0;A<y;A++){H[A]=+w[A].getData("area",x);C+=H[A]}for(A=0;A<y;A++){w[A]._area=z*H[A]/C}var F=this.layout.horizontal()?B.width:B.height;var E=[w[0]];var D=w.slice(1);this.stripify(D,E,F,B,x)},stripify:function(y,B,x,A,z){this.computeDim(y,B,x,A,this.avgAspectRatio,z)},layoutRow:function(y,x,A,z){if(this.layout.horizontal()){return this.layoutH(y,x,A,z)}else{return this.layoutV(y,x,A,z)}},layoutV:function(x,G,D,y){var H=0;c.each(x,function(w){H+=w._area});var z=H/G,E=0;for(var B=0,A=x.length;B<A;B++){var F=x[B];var C=F._area/z;F.getPos(y).setc(D.left,D.top+(G-C-E));F.setData("width",z,y);F.setData("height",C,y);E+=C}return{height:D.height,width:D.width-z,top:D.top,left:D.left+z,dim:G}},layoutH:function(x,F,C,y){var H=0;c.each(x,function(w){H+=w._area});var G=H/F,D=C.height-G,z=0;for(var B=0,A=x.length;B<A;B++){var E=x[B];var I=E._area/G;E.getPos(y).setc(C.left+z,C.top+D);E.setData("width",I,y);E.setData("height",G,y);z+=I}return{height:C.height-G,width:C.width,top:C.top,left:C.left,dim:F}}});g.Icicle=new q({compute:function(E){E=E||"current";var D=this.graph.getNode(this.root),z=this.config,H=this.canvas.getSize(),w=H.width,G=H.height,A=z.offset,C=z.constrained?z.levelsToShow:Number.MAX_VALUE;this.controller.onBeforeCompute(D);e.Util.computeLevels(this.graph,D.id,0,"ignore");var F=0;e.Util.eachLevel(D,0,false,function(J,I){if(I>F){F=I}});var y=this.graph.getNode(this.clickedNode&&this.clickedNode.id||D.id);var x=Math.min(F,C-1);var B=y._depth;if(this.layout.horizontal()){this.computeSubtree(y,-w/2,-G/2,w/(x+1),G,B,x,E)}else{this.computeSubtree(y,-w/2,-G/2,w,G/(x+1),B,x,E)}},computeSubtree:function(G,I,F,w,L,E,A,H){G.getPos(H).setc(I,F);G.setData("width",w,H);G.setData("height",L,H);var C,K=0,J=0;var z=e.Util.getSubnodes(G,[1,1],"ignore");if(!z.length){return}c.each(z,function(x){J+=x.getData("dim")});for(var D=0,B=z.length;D<B;D++){if(this.layout.horizontal()){C=L*z[D].getData("dim")/J;this.computeSubtree(z[D],I+w,F,w,C,E,A,H);F+=C}else{C=w*z[D].getData("dim")/J;this.computeSubtree(z[D],I,F+L,C,L,E,A,H);I+=C}}}});$jit.Icicle=new q({Implements:[d,o,g.Icicle],layout:{orientation:"h",vertical:function(){return this.orientation=="v"},horizontal:function(){return this.orientation=="h"},change:function(){this.orientation=this.vertical()?"h":"v"}},initialize:function(w){var x={animate:false,orientation:"h",offset:2,levelsToShow:Number.MAX_VALUE,constrained:false,Node:{type:"rectangle",overridable:true},Edge:{type:"none"},Label:{type:"Native"},duration:700,fps:45};var z=n("Canvas","Node","Edge","Fx","Tips","NodeStyles","Events","Navigation","Controller","Label");this.controller=this.config=c.merge(z,x,w);this.layout.orientation=this.config.orientation;var y=this.config;if(y.useCanvas){this.canvas=y.useCanvas;this.config.labelContainer=this.canvas.id+"-label"}else{this.canvas=new l(this,y);this.config.labelContainer=(typeof y.injectInto=="string"?y.injectInto:y.injectInto.id)+"-label"}this.graphOptions={klass:p,Node:{selected:false,exist:true,drawn:true}};this.graph=new e(this.graphOptions,this.config.Node,this.config.Edge,this.config.Label);this.labels=new $jit.Icicle.Label[this.config.Label.type](this);this.fx=new $jit.Icicle.Plot(this,$jit.Icicle);this.op=new $jit.Icicle.Op(this);this.group=new $jit.Icicle.Group(this);this.clickedNode=null;this.initializeExtras()},refresh:function(){var w=this.config.Label.type;if(w!="Native"){var x=this;this.graph.eachNode(function(y){x.labels.hideLabel(y,false)})}this.compute();this.plot()},plot:function(){this.fx.plot(this.config)},enter:function(y){if(this.busy){return}this.busy=true;var x=this,w=this.config;var z={onComplete:function(){if(w.request){x.compute()}if(w.animate){x.graph.nodeList.setDataset(["current","end"],{alpha:[1,0]});e.Util.eachSubgraph(y,function(A){A.setData("alpha",1,"end")},"ignore");x.fx.animate({duration:500,modes:["node-property:alpha"],onComplete:function(){x.clickedNode=y;x.compute("end");x.fx.animate({modes:["linear","node-property:width:height"],duration:1000,onComplete:function(){x.busy=false;x.clickedNode=y}})}})}else{x.clickedNode=y;x.busy=false;x.refresh()}}};if(w.request){this.requestNodes(clickedNode,z)}else{z.onComplete()}},out:function(){if(this.busy){return}var B=this,A=e.Util,y=this.config,D=this.graph,x=A.getParents(D.getNode(this.clickedNode&&this.clickedNode.id||this.root)),z=x[0],w=z,C=this.clickedNode;this.busy=true;this.events.hoveredNode=false;if(!z){this.busy=false;return}callback={onComplete:function(){B.clickedNode=z;if(y.request){B.requestNodes(z,{onComplete:function(){B.compute();B.plot();B.busy=false}})}else{B.compute();B.plot();B.busy=false}}};if(y.animate){this.clickedNode=w;this.compute("end");this.clickedNode=C;this.fx.animate({modes:["linear","node-property:width:height"],duration:1000,onComplete:function(){B.clickedNode=w;D.nodeList.setDataset(["current","end"],{alpha:[0,1]});A.eachSubgraph(C,function(E){E.setData("alpha",1)},"ignore");B.fx.animate({duration:500,modes:["node-property:alpha"],onComplete:function(){callback.onComplete()}})}})}else{callback.onComplete()}},requestNodes:function(y,z){var x=c.merge(this.controller,z),w=this.config.constrained?this.config.levelsToShow:Number.MAX_VALUE;if(x.request){var B=[],A=y._depth;e.Util.eachLevel(y,0,w,function(C){if(C.drawn&&!e.Util.anySubnode(C)){B.push(C);C._level=C._depth-A;if(this.config.constrained){C._level=w-C._level}}});this.group.requestNodes(B,x)}else{x.onComplete()}}});$jit.Icicle.Op=new q({Implements:e.Op});$jit.Icicle.Group=new q({initialize:function(w){this.viz=w;this.canvas=w.canvas;this.config=w.config},requestNodes:function(B,A){var z=0,x=B.length,D={};var y=function(){A.onComplete()};var w=this.viz;if(x==0){y()}for(var C=0;C<x;C++){D[B[C].id]=B[C];A.request(B[C].id,B[C]._level,{onComplete:function(F,E){if(E&&E.children){E.id=F;w.op.sum(E,{type:"nothing"})}if(++z==x){e.Util.computeLevels(w.graph,w.root,0);y()}}})}}});$jit.Icicle.Plot=new q({Implements:e.Plot,plot:function(A,y){A=A||this.viz.controller;var w=this.viz,B=w.graph,x=B.getNode(w.clickedNode&&w.clickedNode.id||w.root),z=x._depth;w.canvas.clear();this.plotTree(x,c.merge(A,{withLabels:true,hideLabels:false,plotSubtree:function(C,D){return !w.config.constrained||(D._depth-z<w.config.levelsToShow)}}),y)}});$jit.Icicle.Label={};$jit.Icicle.Label.Native=new q({Implements:e.Label.Native,renderLabel:function(x,y,A){var D=x.getCtx(),w=y.getData("width"),C=y.getData("height"),E=y.getLabelData("size"),z=D.measureText(y.name);if(C<(E*1.5)||w<z.width){return}var B=y.pos.getc(true);D.fillText(y.name,B.x+w/2,B.y+C/2)}});$jit.Icicle.Label.SVG=new q({Implements:e.Label.SVG,initialize:function(w){this.viz=w},placeLabel:function(x,A,y){var C=A.pos.getc(true),z=this.viz.canvas;var w=z.getSize();var B={x:Math.round(C.x+w.width/2),y:Math.round(C.y+w.height/2)};x.setAttribute("x",B.x);x.setAttribute("y",B.y);y.onPlaceLabel(x,A)}});$jit.Icicle.Label.HTML=new q({Implements:e.Label.HTML,initialize:function(w){this.viz=w},placeLabel:function(x,B,y){var D=B.pos.getc(true),z=this.viz.canvas;var w=z.getSize();var C={x:Math.round(D.x+w.width/2),y:Math.round(D.y+w.height/2)};var A=x.style;A.left=C.x+"px";A.top=C.y+"px";A.display="";y.onPlaceLabel(x,B)}});$jit.Icicle.Plot.NodeTypes=new q({none:{render:c.empty},rectangle:{render:function(z,x,K){var y=this.viz.config;var C=y.offset;var w=z.getData("width");var H=z.getData("height");var B=z.getData("border");var G=z.pos.getc(true);var F=G.x+C/2,D=G.y+C/2;var J=x.getCtx();if(w-C<2||H-C<2){return}if(y.cushion){var A=z.getData("color");var I=J.createRadialGradient(F+(w-C)/2,D+(H-C)/2,1,F+(w-C)/2,D+(H-C)/2,w<H?H:w);var E=c.rgbToHex(c.map(c.hexToRgb(A),function(L){return L*0.3>>0}));I.addColorStop(0,A);I.addColorStop(1,E);J.fillStyle=I}if(B){J.strokeStyle=B;J.lineWidth=3}J.fillRect(F,D,Math.max(0,w-C),Math.max(0,H-C));B&&J.strokeRect(G.x,G.y,w,H)},contains:function(y,A){if(this.viz.clickedNode&&!$jit.Graph.Util.isDescendantOf(y,this.viz.clickedNode.id)){return false}var z=y.pos.getc(true),x=y.getData("width"),w=y.getData("height");return this.nodeHelper.rectangle.contains({x:z.x+x/2,y:z.y+w/2},A,x,w)}}});$jit.Icicle.Plot.EdgeTypes=new q({none:c.empty});g.ForceDirected=new q({getOptions:function(D){var B=this.canvas.getSize();var y=B.width,A=B.height;var C=0;this.graph.eachNode(function(w){C++});var E=y*A/C,z=Math.sqrt(E);var x=this.config.levelDistance;return{width:y,height:A,tstart:y*0.1,nodef:function(w){return E/(w||1)},edgef:function(w){return z*(w-x)}}},compute:function(x,y){var z=c.splat(x||["current","start","end"]);var w=this.getOptions();f.compute(this.graph,z,this.config);this.graph.computeLevels(this.root,0,"ignore");this.graph.eachNode(function(A){c.each(z,function(B){var C=A.getPos(B);if(C.equals(p.KER)){C.x=w.width/5*(Math.random()-0.5);C.y=w.height/5*(Math.random()-0.5)}A.disp={};c.each(z,function(D){A.disp[D]=r(0,0)})})});this.computePositions(z,w,y)},computePositions:function(A,y,B){var C=this.config.iterations,x=0,z=this;if(B){(function w(){for(var E=B.iter,D=0;D<E;D++){y.t=y.tstart;if(C){y.t*=(1-x++/(C-1))}z.computePositionStep(A,y);if(C&&x>=C){B.onComplete();return}}B.onStep(Math.round(x/(C-1)*100));setTimeout(w,1)})()}else{for(;x<C;x++){y.t=y.tstart*(1-x/(C-1));this.computePositionStep(A,y)}}},computePositionStep:function(D,w){var E=this.graph;var y=Math.min,C=Math.max;var B=r(0,0);E.eachNode(function(G){c.each(D,function(H){G.disp[H].x=0;G.disp[H].y=0});E.eachNode(function(H){if(H.id!=G.id){c.each(D,function(L){var J=G.getPos(L),I=H.getPos(L);B.x=J.x-I.x;B.y=J.y-I.y;var K=B.norm()||1;G.disp[L].$add(B.$scale(w.nodef(K)/K))})}})});var x=!!E.getNode(this.root).visited;E.eachNode(function(G){G.eachAdjacency(function(H){var I=H.nodeTo;if(!!I.visited===x){c.each(D,function(M){var K=G.getPos(M),J=I.getPos(M);B.x=K.x-J.x;B.y=K.y-J.y;var L=B.norm()||1;G.disp[M].$add(B.$scale(-w.edgef(L)/L));I.disp[M].$add(B.$scale(-1))})}});G.visited=!x});var F=w.t,z=w.width/2,A=w.height/2;E.eachNode(function(G){c.each(D,function(J){var H=G.disp[J];var I=H.norm()||1;var J=G.getPos(J);J.$add(r(H.x*y(Math.abs(H.x),F)/I,H.y*y(Math.abs(H.y),F)/I));J.x=y(z,C(-z,J.x));J.y=y(A,C(-A,J.y))})})}});$jit.ForceDirected=new q({Implements:[d,o,g.ForceDirected],initialize:function(x){var w=$jit.ForceDirected;var y={iterations:50,levelDistance:50};this.controller=this.config=c.merge(n("Canvas","Node","Edge","Fx","Tips","NodeStyles","Events","Navigation","Controller","Label"),y,x);var z=this.config;if(z.useCanvas){this.canvas=z.useCanvas;this.config.labelContainer=this.canvas.id+"-label"}else{if(z.background){z.background=c.merge({type:"Circles"},z.background)}this.canvas=new l(this,z);this.config.labelContainer=(typeof z.injectInto=="string"?z.injectInto:z.injectInto.id)+"-label"}this.graphOptions={klass:p,Node:{selected:false,exist:true,drawn:true}};this.graph=new e(this.graphOptions,this.config.Node,this.config.Edge);this.labels=new w.Label[z.Label.type](this);this.fx=new w.Plot(this,w);this.op=new w.Op(this);this.json=null;this.busy=false;this.initializeExtras()},refresh:function(){this.compute();this.plot()},reposition:function(){this.compute("end")},computeIncremental:function(w){w=c.merge({iter:20,property:"end",onStep:c.empty,onComplete:c.empty},w||{});this.config.onBeforeCompute(this.graph.getNode(this.root));this.compute(w.property,w)},plot:function(){this.fx.plot()},animate:function(w){this.fx.animate(c.merge({modes:["linear"]},w||{}))}});$jit.ForceDirected.$extend=true;(function(w){w.Op=new q({Implements:e.Op});w.Plot=new q({Implements:e.Plot});w.Label={};w.Label.Native=new q({Implements:e.Label.Native});w.Label.SVG=new q({Implements:e.Label.SVG,initialize:function(x){this.viz=x},placeLabel:function(H,B,C){var F=B.pos.getc(true),y=this.viz.canvas,z=y.translateOffsetX,x=y.translateOffsetY,G=y.scaleOffsetX,E=y.scaleOffsetY,D=y.getSize();var A={x:Math.round(F.x*G+z+D.width/2),y:Math.round(F.y*E+x+D.height/2)};H.setAttribute("x",A.x);H.setAttribute("y",A.y);C.onPlaceLabel(H,B)}});w.Label.HTML=new q({Implements:e.Label.HTML,initialize:function(x){this.viz=x},placeLabel:function(I,C,D){var G=C.pos.getc(true),z=this.viz.canvas,A=z.translateOffsetX,y=z.translateOffsetY,H=z.scaleOffsetX,F=z.scaleOffsetY,E=z.getSize();var B={x:Math.round(G.x*H+A+E.width/2),y:Math.round(G.y*F+y+E.height/2)};var x=I.style;x.left=B.x+"px";x.top=B.y+"px";x.display=this.fitsInCanvas(B,z)?"":"none";D.onPlaceLabel(I,C)}});w.Plot.NodeTypes=new q({none:{render:c.empty,contains:c.lambda(false)},circle:{render:function(y,x){var A=y.pos.getc(true),z=y.getData("dim");this.nodeHelper.circle.render("fill",A,z,x)},contains:function(x,A){var z=x.pos.getc(true),y=x.getData("dim");return this.nodeHelper.circle.contains(z,A,y)}},ellipse:{render:function(A,y){var B=A.pos.getc(true),z=A.getData("width"),x=A.getData("height");this.nodeHelper.ellipse.render("fill",B,z,x,y)},contains:function(z,B){var A=z.pos.getc(true),y=z.getData("width"),x=z.getData("height");return this.nodeHelper.ellipse.contains(A,B,y,x)}},square:{render:function(y,x){var A=y.pos.getc(true),z=y.getData("dim");this.nodeHelper.square.render("fill",A,z,x)},contains:function(x,A){var z=x.pos.getc(true),y=x.getData("dim");return this.nodeHelper.square.contains(z,A,y)}},rectangle:{render:function(A,y){var B=A.pos.getc(true),z=A.getData("width"),x=A.getData("height");this.nodeHelper.rectangle.render("fill",B,z,x,y)},contains:function(z,B){var A=z.pos.getc(true),y=z.getData("width"),x=z.getData("height");return this.nodeHelper.rectangle.contains(A,B,y,x)}},triangle:{render:function(y,x){var A=y.pos.getc(true),z=y.getData("dim");this.nodeHelper.triangle.render("fill",A,z,x)},contains:function(x,A){var z=x.pos.getc(true),y=x.getData("dim");return this.nodeHelper.triangle.contains(z,A,y)}},star:{render:function(y,x){var A=y.pos.getc(true),z=y.getData("dim");this.nodeHelper.star.render("fill",A,z,x)},contains:function(x,A){var z=x.pos.getc(true),y=x.getData("dim");return this.nodeHelper.star.contains(z,A,y)}}});w.Plot.EdgeTypes=new q({none:c.empty,line:{render:function(x,y){var A=x.nodeFrom.pos.getc(true),z=x.nodeTo.pos.getc(true);this.edgeHelper.line.render(A,z,y)},contains:function(x,A){var z=x.nodeFrom.pos.getc(true),y=x.nodeTo.pos.getc(true);return this.edgeHelper.line.contains(z,y,A,this.edge.epsilon)}},arrow:{render:function(y,z){var D=y.nodeFrom.pos.getc(true),C=y.nodeTo.pos.getc(true),B=y.getData("dim"),A=y.data.$direction,x=(A&&A.length>1&&A[0]!=y.nodeFrom.id);this.edgeHelper.arrow.render(D,C,B,x,z)},contains:function(x,A){var z=x.nodeFrom.pos.getc(true),y=x.nodeTo.pos.getc(true);return this.edgeHelper.arrow.contains(z,y,A,this.edge.epsilon)}}})})($jit.ForceDirected);$jit.TM={};var v=$jit.TM;$jit.TM.$extend=true;v.Base={layout:{orientation:"h",vertical:function(){return this.orientation=="v"},horizontal:function(){return this.orientation=="h"},change:function(){this.orientation=this.vertical()?"h":"v"}},initialize:function(w){var x={orientation:"h",titleHeight:13,offset:2,levelsToShow:0,constrained:false,animate:false,Node:{type:"rectangle",overridable:true,width:3,height:3,color:"#444"},Label:{textAlign:"center",textBaseline:"top"},Edge:{type:"none"},duration:700,fps:45};this.controller=this.config=c.merge(n("Canvas","Node","Edge","Fx","Controller","Tips","NodeStyles","Events","Navigation","Label"),x,w);this.layout.orientation=this.config.orientation;var y=this.config;if(y.useCanvas){this.canvas=y.useCanvas;this.config.labelContainer=this.canvas.id+"-label"}else{if(y.background){y.background=c.merge({type:"Circles"},y.background)}this.canvas=new l(this,y);this.config.labelContainer=(typeof y.injectInto=="string"?y.injectInto:y.injectInto.id)+"-label"}this.graphOptions={klass:p,Node:{selected:false,exist:true,drawn:true}};this.graph=new e(this.graphOptions,this.config.Node,this.config.Edge);this.labels=new v.Label[y.Label.type](this);this.fx=new v.Plot(this);this.op=new v.Op(this);this.group=new v.Group(this);this.geom=new v.Geom(this);this.clickedNode=null;this.busy=false;this.initializeExtras()},refresh:function(){if(this.busy){return}this.busy=true;var x=this;if(this.config.animate){this.compute("end");this.config.levelsToShow>0&&this.geom.setRightLevelToShow(this.graph.getNode(this.clickedNode&&this.clickedNode.id||this.root));this.fx.animate(c.merge(this.config,{modes:["linear","node-property:width:height"],onComplete:function(){x.busy=false}}))}else{var w=this.config.Label.type;if(w!="Native"){var x=this;this.graph.eachNode(function(y){x.labels.hideLabel(y,false)})}this.busy=false;this.compute();this.config.levelsToShow>0&&this.geom.setRightLevelToShow(this.graph.getNode(this.clickedNode&&this.clickedNode.id||this.root));this.plot()}},plot:function(){this.fx.plot()},leaf:function(w){return w.getSubnodes([1,1],"ignore").length==0},enter:function(C){if(this.busy){return}this.busy=true;var y=this,x=this.config,A=this.graph,w=C,z=this.clickedNode;var B={onComplete:function(){if(x.levelsToShow>0){y.geom.setRightLevelToShow(C)}if(x.levelsToShow>0||x.request){y.compute()}if(x.animate){A.nodeList.setData("alpha",0,"end");C.eachSubgraph(function(D){D.setData("alpha",1,"end")},"ignore");y.fx.animate({duration:500,modes:["node-property:alpha"],onComplete:function(){y.clickedNode=w;y.compute("end");y.clickedNode=z;y.fx.animate({modes:["linear","node-property:width:height"],duration:1000,onComplete:function(){y.busy=false;y.clickedNode=w}})}})}else{y.busy=false;y.clickedNode=C;y.refresh()}}};if(x.request){this.requestNodes(w,B)}else{B.onComplete()}},out:function(){if(this.busy){return}this.busy=true;this.events.hoveredNode=false;var A=this,y=this.config,C=this.graph,x=C.getNode(this.clickedNode&&this.clickedNode.id||this.root).getParents(),z=x[0],w=z,B=this.clickedNode;if(!z){this.busy=false;return}callback={onComplete:function(){A.clickedNode=z;if(y.request){A.requestNodes(z,{onComplete:function(){A.compute();A.plot();A.busy=false}})}else{A.compute();A.plot();A.busy=false}}};if(y.levelsToShow>0){this.geom.setRightLevelToShow(z)}if(y.animate){this.clickedNode=w;this.compute("end");this.clickedNode=B;this.fx.animate({modes:["linear","node-property:width:height"],duration:1000,onComplete:function(){A.clickedNode=w;C.eachNode(function(D){D.setDataset(["current","end"],{alpha:[0,1]})},"ignore");B.eachSubgraph(function(D){D.setData("alpha",1)},"ignore");A.fx.animate({duration:500,modes:["node-property:alpha"],onComplete:function(){callback.onComplete()}})}})}else{callback.onComplete()}},requestNodes:function(y,z){var x=c.merge(this.controller,z),w=this.config.levelsToShow;if(x.request){var B=[],A=y._depth;y.eachLevel(0,w,function(D){var C=w-(D._depth-A);if(D.drawn&&!D.anySubnode()&&C>0){B.push(D);D._level=C}});this.group.requestNodes(B,x)}else{x.onComplete()}},reposition:function(){this.compute("end")}};v.Op=new q({Implements:e.Op,initialize:function(w){this.viz=w}});v.Geom=new q({Implements:e.Geom,getRightLevelToShow:function(){return this.viz.config.levelsToShow},setRightLevelToShow:function(x){var y=this.getRightLevelToShow(),w=this.viz.labels;x.eachLevel(0,y+1,function(A){var z=A._depth-x._depth;if(z>y){A.drawn=false;A.exist=false;A.ignore=true;w.hideLabel(A,false)}else{A.drawn=true;A.exist=true;delete A.ignore}});x.drawn=true;delete x.ignore}});v.Group=new q({initialize:function(w){this.viz=w;this.canvas=w.canvas;this.config=w.config},requestNodes:function(B,A){var z=0,x=B.length,D={};var y=function(){A.onComplete()};var w=this.viz;if(x==0){y()}for(var C=0;C<x;C++){D[B[C].id]=B[C];A.request(B[C].id,B[C]._level,{onComplete:function(F,E){if(E&&E.children){E.id=F;w.op.sum(E,{type:"nothing"})}if(++z==x){w.graph.computeLevels(w.root,0);y()}}})}}});v.Plot=new q({Implements:e.Plot,initialize:function(w){this.viz=w;this.config=w.config;this.node=this.config.Node;this.edge=this.config.Edge;this.animation=new u;this.nodeTypes=new v.Plot.NodeTypes;this.edgeTypes=new v.Plot.EdgeTypes;this.labels=w.labels},plot:function(y,x){var w=this.viz,z=w.graph;w.canvas.clear();this.plotTree(z.getNode(w.clickedNode&&w.clickedNode.id||w.root),c.merge(w.config,y||{},{withLabels:true,hideLabels:false,plotSubtree:function(B,A){return B.anySubnode("exist")}}),x)}});v.Label={};v.Label.Native=new q({Implements:e.Label.Native,initialize:function(w){this.config=w.config;this.leaf=w.leaf},renderLabel:function(z,A,B){if(!this.leaf(A)&&!this.config.titleHeight){return}var D=A.pos.getc(true),G=z.getCtx(),w=A.getData("width"),F=A.getData("height"),E=D.x+w/2,C=D.y;G.fillText(A.name,E,C,w)}});v.Label.SVG=new q({Implements:e.Label.SVG,initialize:function(w){this.viz=w;this.leaf=w.leaf;this.config=w.config},placeLabel:function(G,A,B){var E=A.pos.getc(true),x=this.viz.canvas,y=x.translateOffsetX,w=x.translateOffsetY,F=x.scaleOffsetX,D=x.scaleOffsetY,C=x.getSize();var z={x:Math.round(E.x*F+y+C.width/2),y:Math.round(E.y*D+w+C.height/2)};G.setAttribute("x",z.x);G.setAttribute("y",z.y);if(!this.leaf(A)&&!this.config.titleHeight){G.style.display="none"}B.onPlaceLabel(G,A)}});v.Label.HTML=new q({Implements:e.Label.HTML,initialize:function(w){this.viz=w;this.leaf=w.leaf;this.config=w.config},placeLabel:function(H,B,C){var F=B.pos.getc(true),y=this.viz.canvas,z=y.translateOffsetX,x=y.translateOffsetY,G=y.scaleOffsetX,E=y.scaleOffsetY,D=y.getSize();var A={x:Math.round(F.x*G+z+D.width/2),y:Math.round(F.y*E+x+D.height/2)};var w=H.style;w.left=A.x+"px";w.top=A.y+"px";w.width=B.getData("width")*G+"px";w.height=B.getData("height")*E+"px";w.zIndex=B._depth*100;w.display="";if(!this.leaf(B)&&!this.config.titleHeight){H.style.display="none"}C.onPlaceLabel(H,B)}});v.Plot.NodeTypes=new q({none:{render:c.empty},rectangle:{render:function(z,x,M){var D=this.viz.leaf(z),y=this.config,I=y.offset,C=y.titleHeight,H=z.pos.getc(true),w=z.getData("width"),J=z.getData("height"),B=z.getData("border"),L=x.getCtx(),G=H.x+I/2,E=H.y+I/2;if(w<=I||J<=I){return}if(D){if(y.cushion){var K=L.createRadialGradient(G+(w-I)/2,E+(J-I)/2,1,G+(w-I)/2,E+(J-I)/2,w<J?J:w);var A=z.getData("color");var F=c.rgbToHex(c.map(c.hexToRgb(A),function(N){return N*0.2>>0}));K.addColorStop(0,A);K.addColorStop(1,F);L.fillStyle=K}L.fillRect(G,E,w-I,J-I);if(B){L.save();L.strokeStyle=B;L.strokeRect(G,E,w-I,J-I);L.restore()}}else{if(C>0){L.fillRect(H.x+I/2,H.y+I/2,w-I,C-I);if(B){L.save();L.strokeStyle=B;L.strokeRect(H.x+I/2,H.y+I/2,w-I,J-I);L.restore()}}}},contains:function(z,B){if(this.viz.clickedNode&&!z.isDescendantOf(this.viz.clickedNode.id)||z.ignore){return false}var A=z.pos.getc(true),y=z.getData("width"),x=this.viz.leaf(z),w=x?z.getData("height"):this.config.titleHeight;return this.nodeHelper.rectangle.contains({x:A.x+y/2,y:A.y+w/2},B,y,w)}}});v.Plot.EdgeTypes=new q({none:c.empty});v.SliceAndDice=new q({Implements:[d,o,v.Base,g.TM.SliceAndDice]});v.Squarified=new q({Implements:[d,o,v.Base,g.TM.Squarified]});v.Strip=new q({Implements:[d,o,v.Base,g.TM.Strip]});$jit.RGraph=new q({Implements:[d,o,g.Radial],initialize:function(w){var x=$jit.RGraph;var y={interpolation:"linear",levelDistance:100};this.controller=this.config=c.merge(n("Canvas","Node","Edge","Fx","Controller","Tips","NodeStyles","Events","Navigation","Label"),y,w);var z=this.config;if(z.useCanvas){this.canvas=z.useCanvas;this.config.labelContainer=this.canvas.id+"-label"}else{if(z.background){z.background=c.merge({type:"Circles"},z.background)}this.canvas=new l(this,z);this.config.labelContainer=(typeof z.injectInto=="string"?z.injectInto:z.injectInto.id)+"-label"}this.graphOptions={klass:b,Node:{selected:false,exist:true,drawn:true}};this.graph=new e(this.graphOptions,this.config.Node,this.config.Edge);this.labels=new x.Label[z.Label.type](this);this.fx=new x.Plot(this,x);this.op=new x.Op(this);this.json=null;this.root=null;this.busy=false;this.parent=false;this.initializeExtras()},createLevelDistanceFunc:function(){var w=this.config.levelDistance;return function(x){return(x._depth+1)*w}},refresh:function(){this.compute();this.plot()},reposition:function(){this.compute("end")},plot:function(){this.fx.plot()},getNodeAndParentAngle:function(D){var y=false;var C=this.graph.getNode(D);var A=C.getParents();var z=(A.length>0)?A[0]:false;if(z){var w=z.pos.getc(),B=C.pos.getc();var x=w.add(B.scale(-1));y=Math.atan2(x.y,x.x);if(y<0){y+=2*Math.PI}}return{parent:z,theta:y}},tagChildren:function(A,C){if(A.angleSpan){var B=[];A.eachAdjacency(function(D){B.push(D.nodeTo)},"ignore");var w=B.length;for(var z=0;z<w&&C!=B[z].id;z++){}for(var y=(z+1)%w,x=0;C!=B[y].id;y=(y+1)%w){B[y].dist=x++}}},onClick:function(B,x){if(this.root!=B&&!this.busy){this.busy=true;this.root=B;var y=this;this.controller.onBeforeCompute(this.graph.getNode(B));var z=this.getNodeAndParentAngle(B);this.tagChildren(z.parent,B);this.parent=z.parent;this.compute("end");var w=z.theta-z.parent.endPos.theta;this.graph.eachNode(function(C){C.endPos.set(C.endPos.getp().add(k(w,0)))});var A=this.config.interpolation;x=c.merge({onComplete:c.empty},x||{});this.fx.animate(c.merge({hideLabels:true,modes:[A]},x,{onComplete:function(){y.busy=false;x.onComplete()}}))}}});$jit.RGraph.$extend=true;(function(w){w.Op=new q({Implements:e.Op});w.Plot=new q({Implements:e.Plot});w.Label={};w.Label.Native=new q({Implements:e.Label.Native});w.Label.SVG=new q({Implements:e.Label.SVG,initialize:function(x){this.viz=x},placeLabel:function(H,B,C){var F=B.pos.getc(true),y=this.viz.canvas,z=y.translateOffsetX,x=y.translateOffsetY,G=y.scaleOffsetX,E=y.scaleOffsetY,D=y.getSize();var A={x:Math.round(F.x*G+z+D.width/2),y:Math.round(F.y*E+x+D.height/2)};H.setAttribute("x",A.x);H.setAttribute("y",A.y);C.onPlaceLabel(H,B)}});w.Label.HTML=new q({Implements:e.Label.HTML,initialize:function(x){this.viz=x},placeLabel:function(I,C,D){var G=C.pos.getc(true),z=this.viz.canvas,A=z.translateOffsetX,y=z.translateOffsetY,H=z.scaleOffsetX,F=z.scaleOffsetY,E=z.getSize();var B={x:Math.round(G.x*H+A+E.width/2),y:Math.round(G.y*F+y+E.height/2)};var x=I.style;x.left=B.x+"px";x.top=B.y+"px";x.display=this.fitsInCanvas(B,z)?"":"none";D.onPlaceLabel(I,C)}});w.Plot.NodeTypes=new q({none:{render:c.empty,contains:c.lambda(false)},circle:{render:function(y,x){var A=y.pos.getc(true),z=y.getData("dim");this.nodeHelper.circle.render("fill",A,z,x)},contains:function(x,A){var z=x.pos.getc(true),y=x.getData("dim");return this.nodeHelper.circle.contains(z,A,y)}},ellipse:{render:function(A,y){var B=A.pos.getc(true),z=A.getData("width"),x=A.getData("height");this.nodeHelper.ellipse.render("fill",B,z,x,y)},contains:function(z,B){var A=z.pos.getc(true),y=z.getData("width"),x=z.getData("height");return this.nodeHelper.ellipse.contains(A,B,y,x)}},square:{render:function(y,x){var A=y.pos.getc(true),z=y.getData("dim");this.nodeHelper.square.render("fill",A,z,x)},contains:function(x,A){var z=x.pos.getc(true),y=x.getData("dim");return this.nodeHelper.square.contains(z,A,y)}},rectangle:{render:function(A,y){var B=A.pos.getc(true),z=A.getData("width"),x=A.getData("height");this.nodeHelper.rectangle.render("fill",B,z,x,y)},contains:function(z,B){var A=z.pos.getc(true),y=z.getData("width"),x=z.getData("height");return this.nodeHelper.rectangle.contains(A,B,y,x)}},triangle:{render:function(y,x){var A=y.pos.getc(true),z=y.getData("dim");this.nodeHelper.triangle.render("fill",A,z,x)},contains:function(x,A){var z=x.pos.getc(true),y=x.getData("dim");return this.nodeHelper.triangle.contains(z,A,y)}},star:{render:function(y,x){var A=y.pos.getc(true),z=y.getData("dim");this.nodeHelper.star.render("fill",A,z,x)},contains:function(x,A){var z=x.pos.getc(true),y=x.getData("dim");return this.nodeHelper.star.contains(z,A,y)}}});w.Plot.EdgeTypes=new q({none:c.empty,line:{render:function(x,y){var A=x.nodeFrom.pos.getc(true),z=x.nodeTo.pos.getc(true);this.edgeHelper.line.render(A,z,y)},contains:function(x,A){var z=x.nodeFrom.pos.getc(true),y=x.nodeTo.pos.getc(true);return this.edgeHelper.line.contains(z,y,A,this.edge.epsilon)}},arrow:{render:function(y,z){var D=y.nodeFrom.pos.getc(true),C=y.nodeTo.pos.getc(true),B=y.getData("dim"),A=y.data.$direction,x=(A&&A.length>1&&A[0]!=y.nodeFrom.id);this.edgeHelper.arrow.render(D,C,B,x,z)},contains:function(x,A){var z=x.nodeFrom.pos.getc(true),y=x.nodeTo.pos.getc(true);return this.edgeHelper.arrow.contains(z,y,A,this.edge.epsilon)}}})})($jit.RGraph);p.prototype.moebiusTransformation=function(y){var w=this.add(y);var x=y.$conjugate().$prod(this);x.x++;return w.$div(x)};e.Util.moebiusTransformation=function(y,A,z,x,w){this.eachNode(y,function(C){for(var B=0;B<z.length;B++){var E=A[B].scale(-1),D=x?x:z[B];C.getPos(z[B]).set(C.getPos(D).getc().moebiusTransformation(E))}},w)};$jit.Hypertree=new q({Implements:[d,o,g.Radial],initialize:function(w){var z=$jit.Hypertree;var x={radius:"auto",offset:0,Edge:{type:"hyperline"},duration:1500,fps:35};this.controller=this.config=c.merge(n("Canvas","Node","Edge","Fx","Tips","NodeStyles","Events","Navigation","Controller","Label"),x,w);var y=this.config;if(y.useCanvas){this.canvas=y.useCanvas;this.config.labelContainer=this.canvas.id+"-label"}else{if(y.background){y.background=c.merge({type:"Circles"},y.background)}this.canvas=new l(this,y);this.config.labelContainer=(typeof y.injectInto=="string"?y.injectInto:y.injectInto.id)+"-label"}this.graphOptions={klass:b,Node:{selected:false,exist:true,drawn:true}};this.graph=new e(this.graphOptions,this.config.Node,this.config.Edge);this.labels=new z.Label[y.Label.type](this);this.fx=new z.Plot(this,z);this.op=new z.Op(this);this.json=null;this.root=null;this.busy=false;this.initializeExtras()},createLevelDistanceFunc:function(){var A=this.getRadius();var C=0,w=Math.max,x=this.config;this.graph.eachNode(function(D){C=w(D._depth,C)},"ignore");C++;var B=function(D){return function(F){F.scale=A;var H=F._depth+1;var G=0,E=Math.pow;while(H){G+=E(D,H--)}return G-x.offset}};for(var z=0.51;z<=1;z+=0.01){var y=(1-Math.pow(z,C))/(1-z);if(y>=2){return B(z-0.01)}}return B(0.75)},getRadius:function(){var w=this.config.radius;if(w!=="auto"){return w}var x=this.canvas.getSize();return Math.min(x.width,x.height)/2},refresh:function(w){if(w){this.reposition();this.graph.eachNode(function(x){x.startPos.rho=x.pos.rho=x.endPos.rho;x.startPos.theta=x.pos.theta=x.endPos.theta})}else{this.compute()}this.plot()},reposition:function(){this.compute("end");var w=this.graph.getNode(this.root).pos.getc().scale(-1);e.Util.moebiusTransformation(this.graph,[w],["end"],"end","ignore");this.graph.eachNode(function(x){if(x.ignore){x.endPos.rho=x.pos.rho;x.endPos.theta=x.pos.theta}})},plot:function(){this.fx.plot()},onClick:function(y,w){var x=this.graph.getNode(y).pos.getc(true);this.move(x,w)},move:function(A,y){var x=r(A.x,A.y);if(this.busy===false&&x.norm()<1){this.busy=true;var w=this.graph.getClosestNodeToPos(x),z=this;this.graph.computeLevels(w.id,0);this.controller.onBeforeCompute(w);y=c.merge({onComplete:c.empty},y||{});this.fx.animate(c.merge({modes:["moebius"],hideLabels:true},y,{onComplete:function(){z.busy=false;y.onComplete()}}),x)}}});$jit.Hypertree.$extend=true;(function(w){w.Op=new q({Implements:e.Op});w.Plot=new q({Implements:e.Plot});w.Label={};w.Label.Native=new q({Implements:e.Label.Native,initialize:function(x){this.viz=x},renderLabel:function(z,B,y){var x=z.getCtx();var C=B.pos.getc(true);var A=this.viz.getRadius();x.fillText(B.name,C.x*A,C.y*A)}});w.Label.SVG=new q({Implements:e.Label.SVG,initialize:function(x){this.viz=x},placeLabel:function(I,C,D){var G=C.pos.getc(true),z=this.viz.canvas,A=z.translateOffsetX,y=z.translateOffsetY,H=z.scaleOffsetX,F=z.scaleOffsetY,E=z.getSize(),x=this.viz.getRadius();var B={x:Math.round((G.x*H)*x+A+E.width/2),y:Math.round((G.y*F)*x+y+E.height/2)};I.setAttribute("x",B.x);I.setAttribute("y",B.y);D.onPlaceLabel(I,C)}});w.Label.HTML=new q({Implements:e.Label.HTML,initialize:function(x){this.viz=x},placeLabel:function(J,D,E){var H=D.pos.getc(true),A=this.viz.canvas,B=A.translateOffsetX,z=A.translateOffsetY,I=A.scaleOffsetX,G=A.scaleOffsetY,F=A.getSize(),x=this.viz.getRadius();var C={x:Math.round((H.x*I)*x+B+F.width/2),y:Math.round((H.y*G)*x+z+F.height/2)};var y=J.style;y.left=C.x+"px";y.top=C.y+"px";y.display=this.fitsInCanvas(C,A)?"":"none";E.onPlaceLabel(J,D)}});w.Plot.NodeTypes=new q({none:{render:c.empty,contains:c.lambda(false)},circle:{render:function(z,x){var y=this.node,B=z.getData("dim"),A=z.pos.getc();B=y.transform?B*(1-A.squaredNorm()):B;A.$scale(z.scale);if(B>0.2){this.nodeHelper.circle.render("fill",A,B,x)}},contains:function(x,A){var y=x.getData("dim"),z=x.pos.getc().$scale(x.scale);return this.nodeHelper.circle.contains(z,A,y)}},ellipse:{render:function(A,y){var B=A.pos.getc().$scale(A.scale),z=A.getData("width"),x=A.getData("height");this.nodeHelper.ellipse.render("fill",B,z,x,y)},contains:function(z,B){var y=z.getData("width"),x=z.getData("height"),A=z.pos.getc().$scale(z.scale);return this.nodeHelper.circle.contains(A,B,y,x)}},square:{render:function(z,x){var y=this.node,B=z.getData("dim"),A=z.pos.getc();B=y.transform?B*(1-A.squaredNorm()):B;A.$scale(z.scale);if(B>0.2){this.nodeHelper.square.render("fill",A,B,x)}},contains:function(x,A){var y=x.getData("dim"),z=x.pos.getc().$scale(x.scale);return this.nodeHelper.square.contains(z,A,y)}},rectangle:{render:function(B,y){var A=this.node,z=B.getData("width"),x=B.getData("height"),C=B.pos.getc();z=A.transform?z*(1-C.squaredNorm()):z;x=A.transform?x*(1-C.squaredNorm()):x;C.$scale(B.scale);if(z>0.2&&x>0.2){this.nodeHelper.rectangle.render("fill",C,z,x,y)}},contains:function(z,B){var y=z.getData("width"),x=z.getData("height"),A=z.pos.getc().$scale(z.scale);return this.nodeHelper.rectangle.contains(A,B,y,x)}},triangle:{render:function(z,x){var y=this.node,B=z.getData("dim"),A=z.pos.getc();B=y.transform?B*(1-A.squaredNorm()):B;A.$scale(z.scale);if(B>0.2){this.nodeHelper.triangle.render("fill",A,B,x)}},contains:function(x,A){var y=x.getData("dim"),z=x.pos.getc().$scale(x.scale);return this.nodeHelper.triangle.contains(z,A,y)}},star:{render:function(z,x){var y=this.node,B=z.getData("dim"),A=z.pos.getc();B=y.transform?B*(1-A.squaredNorm()):B;A.$scale(z.scale);if(B>0.2){this.nodeHelper.star.render("fill",A,B,x)}},contains:function(x,A){var y=x.getData("dim"),z=x.pos.getc().$scale(x.scale);return this.nodeHelper.star.contains(z,A,y)}}});w.Plot.EdgeTypes=new q({none:c.empty,line:{render:function(x,y){var B=x.nodeFrom.pos.getc(true),A=x.nodeTo.pos.getc(true),z=x.nodeFrom.scale;this.edgeHelper.line.render({x:B.x*z,y:B.y*z},{x:A.x*z,y:A.y*z},y)},contains:function(x,B){var A=x.nodeFrom.pos.getc(true),z=x.nodeTo.pos.getc(true),y=x.nodeFrom.scale;this.edgeHelper.line.contains({x:A.x*y,y:A.y*y},{x:z.x*y,y:z.y*y},B,this.edge.epsilon)}},arrow:{render:function(y,z){var E=y.nodeFrom.pos.getc(true),D=y.nodeTo.pos.getc(true),A=y.nodeFrom.scale,C=y.getData("dim"),B=y.data.$direction,x=(B&&B.length>1&&B[0]!=y.nodeFrom.id);this.edgeHelper.arrow.render({x:E.x*A,y:E.y*A},{x:D.x*A,y:D.y*A},C,x,z)},contains:function(x,B){var A=x.nodeFrom.pos.getc(true),z=x.nodeTo.pos.getc(true),y=x.nodeFrom.scale;this.edgeHelper.arrow.contains({x:A.x*y,y:A.y*y},{x:z.x*y,y:z.y*y},B,this.edge.epsilon)}},hyperline:{render:function(x,y){var B=x.nodeFrom.pos.getc(),A=x.nodeTo.pos.getc(),z=this.viz.getRadius();this.edgeHelper.hyperline.render(B,A,z,y)},contains:c.lambda(false)}})})($jit.Hypertree)})();
\ No newline at end of file
diff --git a/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/jit.js b/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/jit.js
index 269fbc7..9bf6e9e 100644
--- a/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/jit.js
+++ b/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/jit.js
@@ -1,191 +1,502 @@
-(function () { 
+/*
+Copyright (c) 2011 Sencha Inc. - Author: Nicolas Garcia Belmonte (http://philogb.github.com/)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+ */
+ (function () { 
 
 /*
   File: Core.js
-  
-  Description:
-  
-  Provides common utility functions and the Class object used internally by the library.
-  
-  Also provides the <TreeUtil> object for manipulating JSON tree structures
-  
-  Some of the Basic utility functions and the Class system are based in the MooTools Framework <http://mootools.net>. Copyright (c) 2006-2009 Valerio Proietti, <http://mad4milk.net/>. MIT license <http://mootools.net/license.txt>.
-  
-  Author: 
-  
-  Nicolas Garcia Belmonte
-  
-  Copyright: 
-  
-  Copyright 2008-2009 by Nicolas Garcia Belmonte.
-  
-  Homepage: 
-  
-  <http://thejit.org>
-  
-  Version: 
-  
-  1.1.3
 
-  License: 
-  
-  BSD License
- 
-> 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.
  */
 
-
-function $empty() {};
-
-function $extend(original, extended){
-    for (var key in (extended || {})) original[key] = extended[key];
-    return original;
-};
-
-function $lambda(value){
-    return (typeof value == 'function') ? value : function(){
-        return value;
-    };
-};
-
-var $time = Date.now || function(){
-    return +new Date;
-};
-
-function $splat(obj){
-    var type = $type(obj);
-    return (type) ? ((type != 'array') ? [obj] : obj) : [];
-};
-
-var $type = function(elem) {
-  return $type.s.call(elem).match(/^\[object\s(.*)\]$/)[1].toLowerCase();
-};
-$type.s = Object.prototype.toString;
-
-function $each(iterable, fn){
-    var type = $type(iterable);
-  if(type == 'object') {
-    for (var key in iterable) fn(iterable[key], key);
-  } else {
-    for(var i=0; i < iterable.length; i++) fn(iterable[i], i);
+/*
+ Object: $jit
+ 
+ Defines the namespace for all library Classes and Objects. 
+ This variable is the *only* global variable defined in the Toolkit. 
+ There are also other interesting properties attached to this variable described below.
+ */
+window.$jit = function(w) {
+  w = w || window;
+  for(var k in $jit) {
+    if($jit[k].$extend) {
+      w[k] = $jit[k];
+    }
   }
 };
 
-function $merge(){
-    var mix = {};
-    for (var i = 0, l = arguments.length; i < l; i++){
-        var object = arguments[i];
-        if ($type(object) != 'object') continue;
-        for (var key in object){
-            var op = object[key], mp = mix[key];
-            mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $merge(mp, op) : $unlink(op);
-        }
-    }
-    return mix;
+$jit.version = '2.0.1';
+/*
+  Object: $jit.id
+  
+  Works just like *document.getElementById*
+  
+  Example:
+  (start code js)
+  var element = $jit.id('elementId');
+  (end code)
+
+*/
+
+/*
+ Object: $jit.util
+ 
+ Contains utility functions.
+ 
+ Some of the utility functions and the Class system were based in the MooTools Framework 
+ <http://mootools.net>. Copyright (c) 2006-2010 Valerio Proietti, <http://mad4milk.net/>. 
+ MIT license <http://mootools.net/license.txt>.
+ 
+ These methods are generally also implemented in DOM manipulation frameworks like JQuery, MooTools and Prototype.
+ I'd suggest you to use the functions from those libraries instead of using these, since their functions 
+ are widely used and tested in many different platforms/browsers. Use these functions only if you have to.
+ 
+ */
+var $ = function(d) {
+  return document.getElementById(d);
 };
 
-function $unlink(object){
-    var unlinked;
-    switch ($type(object)){
-        case 'object':
-            unlinked = {};
-            for (var p in object) unlinked[p] = $unlink(object[p]);
-        break;
-        case 'array':
-            unlinked = [];
-            for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
-        break;
-        default: return object;
-    }
-    return unlinked;
+$.empty = function() {
 };
 
-function $rgbToHex(srcArray, array){
-    if (srcArray.length < 3) return null;
-    if (srcArray.length == 4 && srcArray[3] == 0 && !array) return 'transparent';
-    var hex = [];
-    for (var i = 0; i < 3; i++){
-        var bit = (srcArray[i] - 0).toString(16);
-        hex.push((bit.length == 1) ? '0' + bit : bit);
-    }
-    return (array) ? hex : '#' + hex.join('');
+/*
+  Method: extend
+  
+  Augment an object by appending another object's properties.
+  
+  Parameters:
+  
+  original - (object) The object to be extended.
+  extended - (object) An object which properties are going to be appended to the original object.
+  
+  Example:
+  (start code js)
+  $jit.util.extend({ 'a': 1, 'b': 2 }, { 'b': 3, 'c': 4 }); //{ 'a':1, 'b': 3, 'c': 4 }
+  (end code)
+*/
+$.extend = function(original, extended) {
+  for ( var key in (extended || {}))
+    original[key] = extended[key];
+  return original;
 };
 
-function $destroy(elem) {
-   $clean(elem);
-   if(elem.parentNode) elem.parentNode.removeChild(elem);
-   if(elem.clearAttributes) elem.clearAttributes(); 
-};
-
-function $clean(elem) {
-  for(var ch = elem.childNodes, i=0; i < ch.length; i++) {
-      $destroy(ch[i]);
-  }  
-};
-
-function $addEvent(obj, type, fn) {
-    if (obj.addEventListener) 
-        obj.addEventListener(type, fn, false);
-    else 
-        obj.attachEvent('on' + type, fn);
-};
-
-function $hasClass(obj, klass) {
-    return (' ' + obj.className + ' ').indexOf(' ' + klass + ' ') > -1;
-};
-
-function $addClass(obj, klass) {
-    if(!$hasClass(obj, klass)) obj.className = (obj.className + " " + klass);
-};
-
-function $removeClass(obj, klass) {
-    obj.className = obj.className.replace(new RegExp('(^|\\s)' + klass + '(?:\\s|$)'), '$1');
-};
-
-function $get(id) {
-  return document.getElementById(id);  
-};
-
-var Class = function(properties){
-  properties = properties || {};
-  var klass = function(){
-//      not defining any attributes in Class properties.
-//    for (var key in this){
-//          if (typeof this[key] != 'function') this[key] = $unlink(this[key]);
-//      }
-      this.constructor = klass;
-      if (Class.prototyping) return this;
-      var instance = (this.initialize) ? this.initialize.apply(this, arguments) : this;
-      return instance;
+$.lambda = function(value) {
+  return (typeof value == 'function') ? value : function() {
+    return value;
   };
+};
+
+$.time = Date.now || function() {
+  return +new Date;
+};
+
+/*
+  Method: splat
   
-  for (var mutator in Class.Mutators){
-      if (!properties[mutator]) continue;
-      properties = Class.Mutators[mutator](properties, properties[mutator]);
-      delete properties[mutator];
+  Returns an array wrapping *obj* if *obj* is not an array. Returns *obj* otherwise.
+  
+  Parameters:
+  
+  obj - (mixed) The object to be wrapped in an array.
+  
+  Example:
+  (start code js)
+  $jit.util.splat(3);   //[3]
+  $jit.util.splat([3]); //[3]
+  (end code)
+*/
+$.splat = function(obj) {
+  var type = $.type(obj);
+  return type ? ((type != 'array') ? [ obj ] : obj) : [];
+};
+
+$.type = function(elem) {
+  var type = $.type.s.call(elem).match(/^\[object\s(.*)\]$/)[1].toLowerCase();
+  if(type != 'object') return type;
+  if(elem && elem.$$family) return elem.$$family;
+  return (elem && elem.nodeName && elem.nodeType == 1)? 'element' : type;
+};
+$.type.s = Object.prototype.toString;
+
+/*
+  Method: each
+  
+  Iterates through an iterable applying *f*.
+  
+  Parameters:
+  
+  iterable - (array) The original array.
+  fn - (function) The function to apply to the array elements.
+  
+  Example:
+  (start code js)
+  $jit.util.each([3, 4, 5], function(n) { alert('number ' + n); });
+  (end code)
+*/
+$.each = function(iterable, fn) {
+  var type = $.type(iterable);
+  if (type == 'object') {
+    for ( var key in iterable)
+      fn(iterable[key], key);
+  } else {
+    for ( var i = 0, l = iterable.length; i < l; i++)
+      fn(iterable[i], i);
   }
+};
+
+$.indexOf = function(array, item) {
+  if(Array.indexOf) return array.indexOf(item);
+  for(var i=0,l=array.length; i<l; i++) {
+    if(array[i] === item) return i;
+  }
+  return -1;
+};
+
+/*
+  Method: map
   
-  $extend(klass, this);
+  Maps or collects an array by applying *f*.
+  
+  Parameters:
+  
+  array - (array) The original array.
+  f - (function) The function to apply to the array elements.
+  
+  Example:
+  (start code js)
+  $jit.util.map([3, 4, 5], function(n) { return n*n; }); //[9, 16, 25]
+  (end code)
+*/
+$.map = function(array, f) {
+  var ans = [];
+  $.each(array, function(elem, i) {
+    ans.push(f(elem, i));
+  });
+  return ans;
+};
+
+/*
+  Method: reduce
+  
+  Iteratively applies the binary function *f* storing the result in an accumulator.
+  
+  Parameters:
+  
+  array - (array) The original array.
+  f - (function) The function to apply to the array elements.
+  opt - (optional|mixed) The starting value for the acumulator.
+  
+  Example:
+  (start code js)
+  $jit.util.reduce([3, 4, 5], function(x, y) { return x + y; }, 0); //12
+  (end code)
+*/
+$.reduce = function(array, f, opt) {
+  var l = array.length;
+  if(l==0) return opt;
+  var acum = arguments.length == 3? opt : array[--l];
+  while(l--) {
+    acum = f(acum, array[l]);
+  }
+  return acum;
+};
+
+/*
+  Method: merge
+  
+  Merges n-objects and their sub-objects creating a new, fresh object.
+  
+  Parameters:
+  
+  An arbitrary number of objects.
+  
+  Example:
+  (start code js)
+  $jit.util.merge({ 'a': 1, 'b': 2 }, { 'b': 3, 'c': 4 }); //{ 'a':1, 'b': 3, 'c': 4 }
+  (end code)
+*/
+$.merge = function() {
+  var mix = {};
+  for ( var i = 0, l = arguments.length; i < l; i++) {
+    var object = arguments[i];
+    if ($.type(object) != 'object')
+      continue;
+    for ( var key in object) {
+      var op = object[key], mp = mix[key];
+      mix[key] = (mp && $.type(op) == 'object' && $.type(mp) == 'object') ? $
+          .merge(mp, op) : $.unlink(op);
+    }
+  }
+  return mix;
+};
+
+$.unlink = function(object) {
+  var unlinked;
+  switch ($.type(object)) {
+  case 'object':
+    unlinked = {};
+    for ( var p in object)
+      unlinked[p] = $.unlink(object[p]);
+    break;
+  case 'array':
+    unlinked = [];
+    for ( var i = 0, l = object.length; i < l; i++)
+      unlinked[i] = $.unlink(object[i]);
+    break;
+  default:
+    return object;
+  }
+  return unlinked;
+};
+
+$.zip = function() {
+  if(arguments.length === 0) return [];
+  for(var j=0, ans=[], l=arguments.length, ml=arguments[0].length; j<ml; j++) {
+    for(var i=0, row=[]; i<l; i++) {
+      row.push(arguments[i][j]);
+    }
+    ans.push(row);
+  }
+  return ans;
+};
+
+/*
+  Method: rgbToHex
+  
+  Converts an RGB array into a Hex string.
+  
+  Parameters:
+  
+  srcArray - (array) An array with R, G and B values
+  
+  Example:
+  (start code js)
+  $jit.util.rgbToHex([255, 255, 255]); //'#ffffff'
+  (end code)
+*/
+$.rgbToHex = function(srcArray, array) {
+  if (srcArray.length < 3)
+    return null;
+  if (srcArray.length == 4 && srcArray[3] == 0 && !array)
+    return 'transparent';
+  var hex = [];
+  for ( var i = 0; i < 3; i++) {
+    var bit = (srcArray[i] - 0).toString(16);
+    hex.push(bit.length == 1 ? '0' + bit : bit);
+  }
+  return array ? hex : '#' + hex.join('');
+};
+
+/*
+  Method: hexToRgb
+  
+  Converts an Hex color string into an RGB array.
+  
+  Parameters:
+  
+  hex - (string) A color hex string.
+  
+  Example:
+  (start code js)
+  $jit.util.hexToRgb('#fff'); //[255, 255, 255]
+  (end code)
+*/
+$.hexToRgb = function(hex) {
+  if (hex.length != 7) {
+    hex = hex.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
+    hex.shift();
+    if (hex.length != 3)
+      return null;
+    var rgb = [];
+    for ( var i = 0; i < 3; i++) {
+      var value = hex[i];
+      if (value.length == 1)
+        value += value;
+      rgb.push(parseInt(value, 16));
+    }
+    return rgb;
+  } else {
+    hex = parseInt(hex.slice(1), 16);
+    return [ hex >> 16, hex >> 8 & 0xff, hex & 0xff ];
+  }
+};
+
+$.destroy = function(elem) {
+  $.clean(elem);
+  if (elem.parentNode)
+    elem.parentNode.removeChild(elem);
+  if (elem.clearAttributes)
+    elem.clearAttributes();
+};
+
+$.clean = function(elem) {
+  for (var ch = elem.childNodes, i = 0, l = ch.length; i < l; i++) {
+    $.destroy(ch[i]);
+  }
+};
+
+/*
+  Method: addEvent
+  
+  Cross-browser add event listener.
+  
+  Parameters:
+  
+  obj - (obj) The Element to attach the listener to.
+  type - (string) The listener type. For example 'click', or 'mousemove'.
+  fn - (function) The callback function to be used when the event is fired.
+  
+  Example:
+  (start code js)
+  $jit.util.addEvent(elem, 'click', function(){ alert('hello'); });
+  (end code)
+*/
+$.addEvent = function(obj, type, fn) {
+  if (obj.addEventListener)
+    obj.addEventListener(type, fn, false);
+  else
+    obj.attachEvent('on' + type, fn);
+};
+
+$.addEvents = function(obj, typeObj) {
+  for(var type in typeObj) {
+    $.addEvent(obj, type, typeObj[type]);
+  }
+};
+
+$.hasClass = function(obj, klass) {
+  return (' ' + obj.className + ' ').indexOf(' ' + klass + ' ') > -1;
+};
+
+$.addClass = function(obj, klass) {
+  if (!$.hasClass(obj, klass))
+    obj.className = (obj.className + " " + klass);
+};
+
+$.removeClass = function(obj, klass) {
+  obj.className = obj.className.replace(new RegExp(
+      '(^|\\s)' + klass + '(?:\\s|$)'), '$1');
+};
+
+$.getPos = function(elem) {
+  var offset = getOffsets(elem);
+  var scroll = getScrolls(elem);
+  return {
+    x: offset.x - scroll.x,
+    y: offset.y - scroll.y
+  };
+
+  function getOffsets(elem) {
+    var position = {
+      x: 0,
+      y: 0
+    };
+    while (elem && !isBody(elem)) {
+      position.x += elem.offsetLeft;
+      position.y += elem.offsetTop;
+      elem = elem.offsetParent;
+    }
+    return position;
+  }
+
+  function getScrolls(elem) {
+    var position = {
+      x: 0,
+      y: 0
+    };
+    while (elem && !isBody(elem)) {
+      position.x += elem.scrollLeft;
+      position.y += elem.scrollTop;
+      elem = elem.parentNode;
+    }
+    return position;
+  }
+
+  function isBody(element) {
+    return (/^(?:body|html)$/i).test(element.tagName);
+  }
+};
+
+$.event = {
+  get: function(e, win) {
+    win = win || window;
+    return e || win.event;
+  },
+  getWheel: function(e) {
+    return e.wheelDelta? e.wheelDelta / 120 : -(e.detail || 0) / 3;
+  },
+  isRightClick: function(e) {
+    return (e.which == 3 || e.button == 2);
+  },
+  getPos: function(e, win) {
+    // get mouse position
+    win = win || window;
+    e = e || win.event;
+    var doc = win.document;
+    doc = doc.documentElement || doc.body;
+    //TODO(nico): make touch event handling better
+    if(e.touches && e.touches.length) {
+      e = e.touches[0];
+    }
+    var page = {
+      x: e.pageX || (e.clientX + doc.scrollLeft),
+      y: e.pageY || (e.clientY + doc.scrollTop)
+    };
+    return page;
+  },
+  stop: function(e) {
+    if (e.stopPropagation) e.stopPropagation();
+    e.cancelBubble = true;
+    if (e.preventDefault) e.preventDefault();
+    else e.returnValue = false;
+  }
+};
+
+$jit.util = $jit.id = $;
+
+var Class = function(properties) {
+  properties = properties || {};
+  var klass = function() {
+    for ( var key in this) {
+      if (typeof this[key] != 'function')
+        this[key] = $.unlink(this[key]);
+    }
+    this.constructor = klass;
+    if (Class.prototyping)
+      return this;
+    var instance = this.initialize ? this.initialize.apply(this, arguments)
+        : this;
+    //typize
+    this.$$family = 'class';
+    return instance;
+  };
+
+  for ( var mutator in Class.Mutators) {
+    if (!properties[mutator])
+      continue;
+    properties = Class.Mutators[mutator](properties, properties[mutator]);
+    delete properties[mutator];
+  }
+
+  $.extend(klass, this);
   klass.constructor = Class;
   klass.prototype = properties;
   return klass;
@@ -193,325 +504,2184 @@
 
 Class.Mutators = {
 
-    Extends: function(self, klass){
-        Class.prototyping = klass.prototype;
-        var subclass = new klass;
-        delete subclass.parent;
-        subclass = Class.inherit(subclass, self);
-        delete Class.prototyping;
-        return subclass;
-    },
-
-    Implements: function(self, klasses){
-        $each($splat(klasses), function(klass){
-            Class.prototying = klass;
-            $extend(self, ($type(klass) == 'function') ? new klass : klass);
-            delete Class.prototyping;
-        });
-        return self;
-    }
+  Implements: function(self, klasses) {
+    $.each($.splat(klasses), function(klass) {
+      Class.prototyping = klass;
+      var instance = (typeof klass == 'function') ? new klass : klass;
+      for ( var prop in instance) {
+        if (!(prop in self)) {
+          self[prop] = instance[prop];
+        }
+      }
+      delete Class.prototyping;
+    });
+    return self;
+  }
 
 };
 
-$extend(Class, {
+$.extend(Class, {
 
-    inherit: function(object, properties){
-        var caller = arguments.callee.caller;
-        for (var key in properties){
-            var override = properties[key];
-            var previous = object[key];
-            var type = $type(override);
-            if (previous && type == 'function'){
-                if (override != previous){
-                    if (caller){
-                        override.__parent = previous;
-                        object[key] = override;
-                    } else {
-                        Class.override(object, key, override);
-                    }
-                }
-            } else if(type == 'object'){
-                object[key] = $merge(previous, override);
-            } else {
-                object[key] = override;
-            }
+  inherit: function(object, properties) {
+    for ( var key in properties) {
+      var override = properties[key];
+      var previous = object[key];
+      var type = $.type(override);
+      if (previous && type == 'function') {
+        if (override != previous) {
+          Class.override(object, key, override);
         }
-
-        if (caller) object.parent = function(){
-            return arguments.callee.caller.__parent.apply(this, arguments);
-        };
-
-        return object;
-    },
-
-    override: function(object, name, method){
-        var parent = Class.prototyping;
-        if (parent && object[name] != parent[name]) parent = null;
-        var override = function(){
-            var previous = this.parent;
-            this.parent = parent ? parent[name] : object[name];
-            var value = method.apply(this, arguments);
-            this.parent = previous;
-            return value;
-        };
-        object[name] = override;
+      } else if (type == 'object') {
+        object[key] = $.merge(previous, override);
+      } else {
+        object[key] = override;
+      }
     }
+    return object;
+  },
+
+  override: function(object, name, method) {
+    var parent = Class.prototyping;
+    if (parent && object[name] != parent[name])
+      parent = null;
+    var override = function() {
+      var previous = this.parent;
+      this.parent = parent ? parent[name] : object[name];
+      var value = method.apply(this, arguments);
+      this.parent = previous;
+      return value;
+    };
+    object[name] = override;
+  }
 
 });
 
+Class.prototype.implement = function() {
+  var proto = this.prototype;
+  $.each(Array.prototype.slice.call(arguments || []), function(properties) {
+    Class.inherit(proto, properties);
+  });
+  return this;
+};
 
-Class.prototype.implement = function(){
-    var proto = this.prototype;
-    $each(Array.prototype.slice.call(arguments || []), function(properties){
-        Class.inherit(proto, properties);
+$jit.Class = Class;
+
+/*
+  Object: $jit.json
+  
+  Provides JSON utility functions.
+  
+  Most of these functions are JSON-tree traversal and manipulation functions.
+*/
+$jit.json = {
+  /*
+     Method: prune
+  
+     Clears all tree nodes having depth greater than maxLevel.
+  
+     Parameters:
+  
+        tree - (object) A JSON tree object. For more information please see <Loader.loadJSON>.
+        maxLevel - (number) An integer specifying the maximum level allowed for this tree. All nodes having depth greater than max level will be deleted.
+
+  */
+  prune: function(tree, maxLevel) {
+    this.each(tree, function(elem, i) {
+      if (i == maxLevel && elem.children) {
+        delete elem.children;
+        elem.children = [];
+      }
     });
+  },
+  /*
+     Method: getParent
+  
+     Returns the parent node of the node having _id_ as id.
+  
+     Parameters:
+  
+        tree - (object) A JSON tree object. See also <Loader.loadJSON>.
+        id - (string) The _id_ of the child node whose parent will be returned.
+
+    Returns:
+
+        A tree JSON node if any, or false otherwise.
+  
+  */
+  getParent: function(tree, id) {
+    if (tree.id == id)
+      return false;
+    var ch = tree.children;
+    if (ch && ch.length > 0) {
+      for ( var i = 0; i < ch.length; i++) {
+        if (ch[i].id == id)
+          return tree;
+        else {
+          var ans = this.getParent(ch[i], id);
+          if (ans)
+            return ans;
+        }
+      }
+    }
+    return false;
+  },
+  /*
+     Method: getSubtree
+  
+     Returns the subtree that matches the given id.
+  
+     Parameters:
+  
+        tree - (object) A JSON tree object. See also <Loader.loadJSON>.
+        id - (string) A node *unique* identifier.
+  
+     Returns:
+  
+        A subtree having a root node matching the given id. Returns null if no subtree matching the id is found.
+
+  */
+  getSubtree: function(tree, id) {
+    if (tree.id == id)
+      return tree;
+    for ( var i = 0, ch = tree.children; ch && i < ch.length; i++) {
+      var t = this.getSubtree(ch[i], id);
+      if (t != null)
+        return t;
+    }
+    return null;
+  },
+  /*
+     Method: eachLevel
+  
+      Iterates on tree nodes with relative depth less or equal than a specified level.
+  
+     Parameters:
+  
+        tree - (object) A JSON tree or subtree. See also <Loader.loadJSON>.
+        initLevel - (number) An integer specifying the initial relative level. Usually zero.
+        toLevel - (number) An integer specifying a top level. This method will iterate only through nodes with depth less than or equal this number.
+        action - (function) A function that receives a node and an integer specifying the actual level of the node.
+          
+    Example:
+   (start code js)
+     $jit.json.eachLevel(tree, 0, 3, function(node, depth) {
+        alert(node.name + ' ' + depth);
+     });
+   (end code)
+  */
+  eachLevel: function(tree, initLevel, toLevel, action) {
+    if (initLevel <= toLevel) {
+      action(tree, initLevel);
+      if(!tree.children) return;
+      for ( var i = 0, ch = tree.children; i < ch.length; i++) {
+        this.eachLevel(ch[i], initLevel + 1, toLevel, action);
+      }
+    }
+  },
+  /*
+     Method: each
+  
+      A JSON tree iterator.
+  
+     Parameters:
+  
+        tree - (object) A JSON tree or subtree. See also <Loader.loadJSON>.
+        action - (function) A function that receives a node.
+
+    Example:
+    (start code js)
+      $jit.json.each(tree, function(node) {
+        alert(node.name);
+      });
+    (end code)
+          
+  */
+  each: function(tree, action) {
+    this.eachLevel(tree, 0, Number.MAX_VALUE, action);
+  }
+};
+
+
+/*
+     An object containing multiple type of transformations. 
+*/
+
+$jit.Trans = {
+  $extend: true,
+  
+  linear: function(p){
+    return p;
+  }
+};
+
+var Trans = $jit.Trans;
+
+(function(){
+
+  var makeTrans = function(transition, params){
+    params = $.splat(params);
+    return $.extend(transition, {
+      easeIn: function(pos){
+        return transition(pos, params);
+      },
+      easeOut: function(pos){
+        return 1 - transition(1 - pos, params);
+      },
+      easeInOut: function(pos){
+        return (pos <= 0.5)? transition(2 * pos, params) / 2 : (2 - transition(
+            2 * (1 - pos), params)) / 2;
+      }
+    });
+  };
+
+  var transitions = {
+
+    Pow: function(p, x){
+      return Math.pow(p, x[0] || 6);
+    },
+
+    Expo: function(p){
+      return Math.pow(2, 8 * (p - 1));
+    },
+
+    Circ: function(p){
+      return 1 - Math.sin(Math.acos(p));
+    },
+
+    Sine: function(p){
+      return 1 - Math.sin((1 - p) * Math.PI / 2);
+    },
+
+    Back: function(p, x){
+      x = x[0] || 1.618;
+      return Math.pow(p, 2) * ((x + 1) * p - x);
+    },
+
+    Bounce: function(p){
+      var value;
+      for ( var a = 0, b = 1; 1; a += b, b /= 2) {
+        if (p >= (7 - 4 * a) / 11) {
+          value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
+          break;
+        }
+      }
+      return value;
+    },
+
+    Elastic: function(p, x){
+      return Math.pow(2, 10 * --p)
+          * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
+    }
+
+  };
+
+  $.each(transitions, function(val, key){
+    Trans[key] = makeTrans(val);
+  });
+
+  $.each( [
+      'Quad', 'Cubic', 'Quart', 'Quint'
+  ], function(elem, i){
+    Trans[elem] = makeTrans(function(p){
+      return Math.pow(p, [
+        i + 2
+      ]);
+    });
+  });
+
+})();
+
+/*
+   A Class that can perform animations for generic objects.
+
+   If you are looking for animation transitions please take a look at the <Trans> object.
+
+   Used by:
+
+   <Graph.Plot>
+   
+   Based on:
+   
+   The Animation class is based in the MooTools Framework <http://mootools.net>. Copyright (c) 2006-2009 Valerio Proietti, <http://mad4milk.net/>. MIT license <http://mootools.net/license.txt>.
+
+*/
+
+var Animation = new Class( {
+
+  initialize: function(options){
+    this.setOptions(options);
+  },
+
+  setOptions: function(options){
+    var opt = {
+      duration: 2500,
+      fps: 40,
+      transition: Trans.Quart.easeInOut,
+      compute: $.empty,
+      complete: $.empty,
+      link: 'ignore'
+    };
+    this.opt = $.merge(opt, options || {});
     return this;
+  },
+
+  step: function(){
+    var time = $.time(), opt = this.opt;
+    if (time < this.time + opt.duration) {
+      var delta = opt.transition((time - this.time) / opt.duration);
+      opt.compute(delta);
+    } else {
+      this.timer = clearInterval(this.timer);
+      opt.compute(1);
+      opt.complete();
+    }
+  },
+
+  start: function(){
+    if (!this.check())
+      return this;
+    this.time = 0;
+    this.startTimer();
+    return this;
+  },
+
+  startTimer: function(){
+    var that = this, fps = this.opt.fps;
+    if (this.timer)
+      return false;
+    this.time = $.time() - this.time;
+    this.timer = setInterval((function(){
+      that.step();
+    }), Math.round(1000 / fps));
+    return true;
+  },
+
+  pause: function(){
+    this.stopTimer();
+    return this;
+  },
+
+  resume: function(){
+    this.startTimer();
+    return this;
+  },
+
+  stopTimer: function(){
+    if (!this.timer)
+      return false;
+    this.time = $.time() - this.time;
+    this.timer = clearInterval(this.timer);
+    return true;
+  },
+
+  check: function(){
+    if (!this.timer)
+      return true;
+    if (this.opt.link == 'cancel') {
+      this.stopTimer();
+      return true;
+    }
+    return false;
+  }
+});
+
+
+var Options = function() {
+  var args = arguments;
+  for(var i=0, l=args.length, ans={}; i<l; i++) {
+    var opt = Options[args[i]];
+    if(opt.$extend) {
+      $.extend(ans, opt);
+    } else {
+      ans[args[i]] = opt;  
+    }
+  }
+  return ans;
 };
 
 /*
-   Object: TreeUtil
-
-   Some common JSON tree manipulation methods.
+ * File: Options.AreaChart.js
+ *
 */
-this.TreeUtil = {
 
-    /*
-       Method: prune
-    
-       Clears all tree nodes having depth greater than maxLevel.
-    
-       Parameters:
-    
-          tree - A JSON tree object. For more information please see <Loader.loadJSON>.
-          maxLevel - An integer specifying the maximum level allowed for this tree. All nodes having depth greater than max level will be deleted.
+/*
+  Object: Options.AreaChart
+  
+  <AreaChart> options. 
+  Other options included in the AreaChart are <Options.Canvas>, <Options.Label>, <Options.Margin>, <Options.Tips> and <Options.Events>.
+  
+  Syntax:
+  
+  (start code js)
 
-    */
-    prune: function(tree, maxLevel) {
-        this.each(tree, function(elem, i) {
-            if(i == maxLevel && elem.children) {
-                delete elem.children;
-                elem.children = [];
-            }
-        });
-    },
-    
-    /*
-       Method: getParent
-    
-       Returns the parent node of the node having _id_ as id.
-    
-       Parameters:
-    
-          tree - A JSON tree object. See also <Loader.loadJSON>.
-          id - The _id_ of the child node whose parent will be returned.
+  Options.AreaChart = {
+    animate: true,
+    labelOffset: 3,
+    type: 'stacked',
+    selectOnHover: true,
+    showAggregates: true,
+    showLabels: true,
+    filterOnClick: false,
+    restoreOnRightClick: false
+  };
+  
+  (end code)
+  
+  Example:
+  
+  (start code js)
 
-      Returns:
+  var areaChart = new $jit.AreaChart({
+    animate: true,
+    type: 'stacked:gradient',
+    selectOnHover: true,
+    filterOnClick: true,
+    restoreOnRightClick: true
+  });
+  
+  (end code)
 
-          A tree JSON node if any, or false otherwise.
+  Parameters:
+  
+  animate - (boolean) Default's *true*. Whether to add animated transitions when filtering/restoring stacks.
+  labelOffset - (number) Default's *3*. Adds margin between the label and the default place where it should be drawn.
+  type - (string) Default's *'stacked'*. Stack style. Posible values are 'stacked', 'stacked:gradient' to add gradients.
+  selectOnHover - (boolean) Default's *true*. If true, it will add a mark to the hovered stack.
+  showAggregates - (boolean, function) Default's *true*. Display the values of the stacks. Can also be a function that returns *true* or *false* to display or filter some values. That same function can also return a string with the formatted value.
+  showLabels - (boolean, function) Default's *true*. Display the name of the slots. Can also be a function that returns *true* or *false* to display or not each label.
+  filterOnClick - (boolean) Default's *true*. Select the clicked stack by hiding all other stacks.
+  restoreOnRightClick - (boolean) Default's *true*. Show all stacks by right clicking.
+  
+*/
+  
+Options.AreaChart = {
+  $extend: true,
+
+  animate: true,
+  labelOffset: 3, // label offset
+  type: 'stacked', // gradient
+  Tips: {
+    enable: false,
+    onShow: $.empty,
+    onHide: $.empty
+  },
+  Events: {
+    enable: false,
+    onClick: $.empty
+  },
+  selectOnHover: true,
+  showAggregates: true,
+  showLabels: true,
+  filterOnClick: false,
+  restoreOnRightClick: false
+};
+
+/*
+ * File: Options.Margin.js
+ *
+*/
+
+/*
+  Object: Options.Margin
+  
+  Canvas drawing margins. 
+  
+  Syntax:
+  
+  (start code js)
+
+  Options.Margin = {
+    top: 0,
+    left: 0,
+    right: 0,
+    bottom: 0
+  };
+  
+  (end code)
+  
+  Example:
+  
+  (start code js)
+
+  var viz = new $jit.Viz({
+    Margin: {
+      right: 10,
+      bottom: 20
+    }
+  });
+  
+  (end code)
+
+  Parameters:
+  
+  top - (number) Default's *0*. Top margin.
+  left - (number) Default's *0*. Left margin.
+  right - (number) Default's *0*. Right margin.
+  bottom - (number) Default's *0*. Bottom margin.
+  
+*/
+
+Options.Margin = {
+  $extend: false,
+  
+  top: 0,
+  left: 0,
+  right: 0,
+  bottom: 0
+};
+
+/*
+ * File: Options.Canvas.js
+ *
+*/
+
+/*
+  Object: Options.Canvas
+  
+  These are Canvas general options, like where to append it in the DOM, its dimensions, background, 
+  and other more advanced options.
+  
+  Syntax:
+  
+  (start code js)
+
+  Options.Canvas = {
+    injectInto: 'id',
+    type: '2D', //'3D'
+    width: false,
+    height: false,
+    useCanvas: false,
+    withLabels: true,
+    background: false
+  };  
+  (end code)
+  
+  Example:
+  
+  (start code js)
+  var viz = new $jit.Viz({
+    injectInto: 'someContainerId',
+    width: 500,
+    height: 700
+  });
+  (end code)
+  
+  Parameters:
+  
+  injectInto - *required* (string|element) The id of the DOM container for the visualization. It can also be an Element provided that it has an id.
+  type - (string) Context type. Default's 2D but can be 3D for webGL enabled browsers.
+  width - (number) Default's to the *container's offsetWidth*. The width of the canvas.
+  height - (number) Default's to the *container's offsetHeight*. The height of the canvas.
+  useCanvas - (boolean|object) Default's *false*. You can pass another <Canvas> instance to be used by the visualization.
+  withLabels - (boolean) Default's *true*. Whether to use a label container for the visualization.
+  background - (boolean|object) Default's *false*. An object containing information about the rendering of a background canvas.
+*/
+
+Options.Canvas = {
+    $extend: true,
     
-    */
-    getParent: function(tree, id) {
-        if(tree.id == id) return false;
-        var ch = tree.children;
-        if(ch && ch.length > 0) {
-            for(var i=0; i<ch.length; i++) {
-                if(ch[i].id == id) 
-                    return tree;
-                else {
-                    var ans = this.getParent(ch[i], id);
-                    if(ans) return ans;
-                }
-            }
+    injectInto: 'id',
+    type: '2D',
+    width: false,
+    height: false,
+    useCanvas: false,
+    withLabels: true,
+    background: false,
+    
+    Scene: {
+      Lighting: {
+        enable: false,
+        ambient: [1, 1, 1],
+        directional: {
+          direction: { x: -100, y: -100, z: -100 },
+          color: [0.5, 0.3, 0.1]
         }
-        return false;       
-    },
-
-    /*
-       Method: getSubtree
-    
-       Returns the subtree that matches the given id.
-    
-       Parameters:
-    
-          tree - A JSON tree object. See also <Loader.loadJSON>.
-          id - A node *unique* identifier.
-    
-       Returns:
-    
-          A subtree having a root node matching the given id. Returns null if no subtree matching the id is found.
-
-    */
-    getSubtree: function(tree, id) {
-        if(tree.id == id) return tree;
-        for(var i=0, ch=tree.children; i<ch.length; i++) {
-            var t = this.getSubtree(ch[i], id);
-            if(t != null) return t;
-        }
-        return null;
-    },
-
-    /*
-       Method: getLeaves
-    
-        Returns the leaves of the tree.
-    
-       Parameters:
-    
-          node - A JSON tree node. See also <Loader.loadJSON>.
-          maxLevel - _optional_ A subtree's max level.
-    
-       Returns:
-    
-       An array having objects with two properties. 
-       
-        - The _node_ property contains the leaf node. 
-        - The _level_ property specifies the depth of the node.
-
-    */
-    getLeaves: function (node, maxLevel) {
-        var leaves = [], levelsToShow = maxLevel || Number.MAX_VALUE;
-        this.each(node, function(elem, i) {
-            if(i < levelsToShow && 
-            (!elem.children || elem.children.length == 0 )) {
-                leaves.push({
-                    'node':elem,
-                    'level':levelsToShow - i
-                });
-            }
-        });
-        return leaves;
-    },
-
-
-    /*
-       Method: eachLevel
-    
-        Iterates on tree nodes with relative depth less or equal than a specified level.
-    
-       Parameters:
-    
-          tree - A JSON tree or subtree. See also <Loader.loadJSON>.
-          initLevel - An integer specifying the initial relative level. Usually zero.
-          toLevel - An integer specifying a top level. This method will iterate only through nodes with depth less than or equal this number.
-          action - A function that receives a node and an integer specifying the actual level of the node.
-            
-      Example:
-     (start code js)
-       TreeUtil.eachLevel(tree, 0, 3, function(node, depth) {
-          alert(node.name + ' ' + depth);
-       });
-     (end code)
-    */
-    eachLevel: function(tree, initLevel, toLevel, action) {
-        if(initLevel <= toLevel) {
-            action(tree, initLevel);
-            for(var i=0, ch = tree.children; i<ch.length; i++) {
-                this.eachLevel(ch[i], initLevel +1, toLevel, action);   
-            }
-        }
-    },
-
-    /*
-       Method: each
-    
-        A tree iterator.
-    
-       Parameters:
-    
-          tree - A JSON tree or subtree. See also <Loader.loadJSON>.
-          action - A function that receives a node.
-
-      Example:
-      (start code js)
-        TreeUtil.each(tree, function(node) {
-          alert(node.name);
-        });
-      (end code)
-            
-    */
-    each: function(tree, action) {
-        this.eachLevel(tree, 0, Number.MAX_VALUE, action);
-    },
-    
-    /*
-       Method: loadSubtrees
-    
-        Appends subtrees to leaves by requesting new subtrees
-        with the _request_ method.
-    
-       Parameters:
-    
-          tree - A JSON tree node. <Loader.loadJSON>.
-          controller - An object that implements a request method.
-      
-       Example:
-        (start code js)
-          TreeUtil.loadSubtrees(leafNode, {
-            request: function(nodeId, level, onComplete) {
-              //Pseudo-code to make an ajax request for a new subtree
-              // that has as root id _nodeId_ and depth _level_ ...
-              Ajax.request({
-                'url': 'http://subtreerequesturl/',
-                
-                onSuccess: function(json) {
-                  onComplete.onComplete(nodeId, json);
-                }
-              });
-            }
-          });
-        (end code)
-    */
-    loadSubtrees: function(tree, controller) {
-        var maxLevel = controller.request && controller.levelsToShow;
-        var leaves = this.getLeaves(tree, maxLevel),
-        len = leaves.length,
-        selectedNode = {};
-        if(len == 0) controller.onComplete();
-        for(var i=0, counter=0; i<len; i++) {
-            var leaf = leaves[i], id = leaf.node.id;
-            selectedNode[id] = leaf.node;
-            controller.request(id, leaf.level, {
-                onComplete: function(nodeId, tree) {
-                    var ch = tree.children;
-                    selectedNode[nodeId].children = ch;
-                    if(++counter == len) {
-                        controller.onComplete();
-                    }
-                }
-            });
-        }
+      }
     }
 };
 
+/*
+ * File: Options.Tree.js
+ *
+*/
+
+/*
+  Object: Options.Tree
+  
+  Options related to (strict) Tree layout algorithms. These options are used by the <ST> visualization.
+  
+  Syntax:
+  
+  (start code js)
+  Options.Tree = {
+    orientation: "left",
+    subtreeOffset: 8,
+    siblingOffset: 5,
+    indent:10,
+    multitree: false,
+    align:"center"
+  };
+  (end code)
+  
+  Example:
+  
+  (start code js)
+  var st = new $jit.ST({
+    orientation: 'left',
+    subtreeOffset: 1,
+    siblingOFfset: 5,
+    multitree: true
+  });
+  (end code)
+
+  Parameters:
+    
+  subtreeOffset - (number) Default's 8. Separation offset between subtrees.
+  siblingOffset - (number) Default's 5. Separation offset between siblings.
+  orientation - (string) Default's 'left'. Tree orientation layout. Possible values are 'left', 'top', 'right', 'bottom'.
+  align - (string) Default's *center*. Whether the tree alignment is 'left', 'center' or 'right'.
+  indent - (number) Default's 10. Used when *align* is left or right and shows an indentation between parent and children.
+  multitree - (boolean) Default's *false*. Used with the node $orn data property for creating multitrees.
+     
+*/
+Options.Tree = {
+    $extend: true,
+    
+    orientation: "left",
+    subtreeOffset: 8,
+    siblingOffset: 5,
+    indent:10,
+    multitree: false,
+    align:"center"
+};
+
+
+/*
+ * File: Options.Node.js
+ *
+*/
+
+/*
+  Object: Options.Node
+
+  Provides Node rendering options for Tree and Graph based visualizations.
+
+  Syntax:
+    
+  (start code js)
+  Options.Node = {
+    overridable: false,
+    type: 'circle',
+    color: '#ccb',
+    alpha: 1,
+    dim: 3,
+    height: 20,
+    width: 90,
+    autoHeight: false,
+    autoWidth: false,
+    lineWidth: 1,
+    transform: true,
+    align: "center",
+    angularWidth:1,
+    span:1,
+    CanvasStyles: {}
+  };
+  (end code)
+  
+  Example:
+  
+  (start code js)
+  var viz = new $jit.Viz({
+    Node: {
+      overridable: true,
+      width: 30,
+      autoHeight: true,
+      type: 'rectangle'
+    }
+  });
+  (end code)
+  
+  Parameters:
+
+  overridable - (boolean) Default's *false*. Determine whether or not general node properties can be overridden by a particular <Graph.Node>.
+  type - (string) Default's *circle*. Node's shape. Node built-in types include 'circle', 'rectangle', 'square', 'ellipse', 'triangle', 'star'. The default Node type might vary in each visualization. You can also implement (non built-in) custom Node types into your visualizations.
+  color - (string) Default's *#ccb*. Node color.
+  alpha - (number) Default's *1*. The Node's alpha value. *1* is for full opacity.
+  dim - (number) Default's *3*. An extra parameter used by 'circle', 'square', 'triangle' and 'star' node types. Depending on each shape, this parameter can set the radius of a circle, half the length of the side of a square, half the base and half the height of a triangle or the length of a side of a star (concave decagon).
+  height - (number) Default's *20*. Used by 'rectangle' and 'ellipse' node types. The height of the node shape.
+  width - (number) Default's *90*. Used by 'rectangle' and 'ellipse' node types. The width of the node shape.
+  autoHeight - (boolean) Default's *false*. Whether to set an auto height for the node depending on the content of the Node's label.
+  autoWidth - (boolean) Default's *false*. Whether to set an auto width for the node depending on the content of the Node's label.
+  lineWidth - (number) Default's *1*. Used only by some Node shapes. The line width of the strokes of a node.
+  transform - (boolean) Default's *true*. Only used by the <Hypertree> visualization. Whether to scale the nodes according to the moebius transformation.
+  align - (string) Default's *center*. Possible values are 'center', 'left' or 'right'. Used only by the <ST> visualization, these parameters are used for aligning nodes when some of they dimensions vary.
+  angularWidth - (number) Default's *1*. Used in radial layouts (like <RGraph> or <Sunburst> visualizations). The amount of relative 'space' set for a node.
+  span - (number) Default's *1*. Used in radial layouts (like <RGraph> or <Sunburst> visualizations). The angle span amount set for a node.
+  CanvasStyles - (object) Default's an empty object (i.e. {}). Attach any other canvas specific property that you'd set to the canvas context before plotting a Node.
+
+*/
+Options.Node = {
+  $extend: false,
+  
+  overridable: false,
+  type: 'circle',
+  color: '#ccb',
+  alpha: 1,
+  dim: 3,
+  height: 20,
+  width: 90,
+  autoHeight: false,
+  autoWidth: false,
+  lineWidth: 1,
+  transform: true,
+  align: "center",
+  angularWidth:1,
+  span:1,
+  //Raw canvas styles to be
+  //applied to the context instance
+  //before plotting a node
+  CanvasStyles: {}
+};
+
+
+/*
+ * File: Options.Edge.js
+ *
+*/
+
+/*
+  Object: Options.Edge
+
+  Provides Edge rendering options for Tree and Graph based visualizations.
+
+  Syntax:
+    
+  (start code js)
+  Options.Edge = {
+    overridable: false,
+    type: 'line',
+    color: '#ccb',
+    lineWidth: 1,
+    dim:15,
+    alpha: 1,
+    CanvasStyles: {}
+  };
+  (end code)
+  
+  Example:
+  
+  (start code js)
+  var viz = new $jit.Viz({
+    Edge: {
+      overridable: true,
+      type: 'line',
+      color: '#fff',
+      CanvasStyles: {
+        shadowColor: '#ccc',
+        shadowBlur: 10
+      }
+    }
+  });
+  (end code)
+  
+  Parameters:
+    
+   overridable - (boolean) Default's *false*. Determine whether or not general edges properties can be overridden by a particular <Graph.Adjacence>.
+   type - (string) Default's 'line'. Edge styles include 'line', 'hyperline', 'arrow'. The default Edge type might vary in each visualization. You can also implement custom Edge types.
+   color - (string) Default's '#ccb'. Edge color.
+   lineWidth - (number) Default's *1*. Line/Edge width.
+   alpha - (number) Default's *1*. The Edge's alpha value. *1* is for full opacity.
+   dim - (number) Default's *15*. An extra parameter used by other complex shapes such as quadratic, bezier or arrow, to determine the shape's diameter.
+   epsilon - (number) Default's *7*. Only used when using *enableForEdges* in <Options.Events>. This dimension is used to create an area for the line where the contains method for the edge returns *true*.
+   CanvasStyles - (object) Default's an empty object (i.e. {}). Attach any other canvas specific property that you'd set to the canvas context before plotting an Edge.
+
+  See also:
+   
+   If you want to know more about how to customize Node/Edge data per element, in the JSON or programmatically, take a look at this article.
+*/
+Options.Edge = {
+  $extend: false,
+  
+  overridable: false,
+  type: 'line',
+  color: '#ccb',
+  lineWidth: 1,
+  dim:15,
+  alpha: 1,
+  epsilon: 7,
+
+  //Raw canvas styles to be
+  //applied to the context instance
+  //before plotting an edge
+  CanvasStyles: {}
+};
+
+
+/*
+ * File: Options.Fx.js
+ *
+*/
+
+/*
+  Object: Options.Fx
+
+  Provides animation options like duration of the animations, frames per second and animation transitions.  
+
+  Syntax:
+  
+  (start code js)
+    Options.Fx = {
+      fps:40,
+      duration: 2500,
+      transition: $jit.Trans.Quart.easeInOut,
+      clearCanvas: true
+    };
+  (end code)
+  
+  Example:
+  
+  (start code js)
+  var viz = new $jit.Viz({
+    duration: 1000,
+    fps: 35,
+    transition: $jit.Trans.linear
+  });
+  (end code)
+  
+  Parameters:
+  
+  clearCanvas - (boolean) Default's *true*. Whether to clear the frame/canvas when the viz is plotted or animated.
+  duration - (number) Default's *2500*. Duration of the animation in milliseconds.
+  fps - (number) Default's *40*. Frames per second.
+  transition - (object) Default's *$jit.Trans.Quart.easeInOut*. The transition used for the animations. See below for a more detailed explanation.
+  
+  Object: $jit.Trans
+  
+  This object is used for specifying different animation transitions in all visualizations.
+
+  There are many different type of animation transitions.
+
+  linear:
+
+  Displays a linear transition
+
+  >Trans.linear
+  
+  (see Linear.png)
+
+  Quad:
+
+  Displays a Quadratic transition.
+
+  >Trans.Quad.easeIn
+  >Trans.Quad.easeOut
+  >Trans.Quad.easeInOut
+  
+ (see Quad.png)
+
+ Cubic:
+
+ Displays a Cubic transition.
+
+ >Trans.Cubic.easeIn
+ >Trans.Cubic.easeOut
+ >Trans.Cubic.easeInOut
+
+ (see Cubic.png)
+
+ Quart:
+
+ Displays a Quartetic transition.
+
+ >Trans.Quart.easeIn
+ >Trans.Quart.easeOut
+ >Trans.Quart.easeInOut
+
+ (see Quart.png)
+
+ Quint:
+
+ Displays a Quintic transition.
+
+ >Trans.Quint.easeIn
+ >Trans.Quint.easeOut
+ >Trans.Quint.easeInOut
+
+ (see Quint.png)
+
+ Expo:
+
+ Displays an Exponential transition.
+
+ >Trans.Expo.easeIn
+ >Trans.Expo.easeOut
+ >Trans.Expo.easeInOut
+
+ (see Expo.png)
+
+ Circ:
+
+ Displays a Circular transition.
+
+ >Trans.Circ.easeIn
+ >Trans.Circ.easeOut
+ >Trans.Circ.easeInOut
+
+ (see Circ.png)
+
+ Sine:
+
+ Displays a Sineousidal transition.
+
+ >Trans.Sine.easeIn
+ >Trans.Sine.easeOut
+ >Trans.Sine.easeInOut
+
+ (see Sine.png)
+
+ Back:
+
+ >Trans.Back.easeIn
+ >Trans.Back.easeOut
+ >Trans.Back.easeInOut
+
+ (see Back.png)
+
+ Bounce:
+
+ Bouncy transition.
+
+ >Trans.Bounce.easeIn
+ >Trans.Bounce.easeOut
+ >Trans.Bounce.easeInOut
+
+ (see Bounce.png)
+
+ Elastic:
+
+ Elastic curve.
+
+ >Trans.Elastic.easeIn
+ >Trans.Elastic.easeOut
+ >Trans.Elastic.easeInOut
+
+ (see Elastic.png)
+ 
+ Based on:
+     
+ Easing and Transition animation methods are based in the MooTools Framework <http://mootools.net>. Copyright (c) 2006-2010 Valerio Proietti, <http://mad4milk.net/>. MIT license <http://mootools.net/license.txt>.
+
+
+*/
+Options.Fx = {
+  $extend: true,
+  
+  fps:40,
+  duration: 2500,
+  transition: $jit.Trans.Quart.easeInOut,
+  clearCanvas: true
+};
+
+/*
+ * File: Options.Label.js
+ *
+*/
+/*
+  Object: Options.Label
+
+  Provides styling for Labels such as font size, family, etc. Also sets Node labels as HTML, SVG or Native canvas elements.  
+
+  Syntax:
+  
+  (start code js)
+    Options.Label = {
+      overridable: false,
+      type: 'HTML', //'SVG', 'Native'
+      style: ' ',
+      size: 10,
+      family: 'sans-serif',
+      textAlign: 'center',
+      textBaseline: 'alphabetic',
+      color: '#fff'
+    };
+  (end code)
+  
+  Example:
+  
+  (start code js)
+  var viz = new $jit.Viz({
+    Label: {
+      type: 'Native',
+      size: 11,
+      color: '#ccc'
+    }
+  });
+  (end code)
+  
+  Parameters:
+    
+  overridable - (boolean) Default's *false*. Determine whether or not general label properties can be overridden by a particular <Graph.Node>.
+  type - (string) Default's *HTML*. The type for the labels. Can be 'HTML', 'SVG' or 'Native' canvas labels.
+  style - (string) Default's *empty string*. Can be 'italic' or 'bold'. This parameter is only taken into account when using 'Native' canvas labels. For DOM based labels the className *node* is added to the DOM element for styling via CSS. You can also use <Options.Controller> methods to style individual labels.
+  size - (number) Default's *10*. The font's size. This parameter is only taken into account when using 'Native' canvas labels. For DOM based labels the className *node* is added to the DOM element for styling via CSS. You can also use <Options.Controller> methods to style individual labels.
+  family - (string) Default's *sans-serif*. The font's family. This parameter is only taken into account when using 'Native' canvas labels. For DOM based labels the className *node* is added to the DOM element for styling via CSS. You can also use <Options.Controller> methods to style individual labels.
+  color - (string) Default's *#fff*. The font's color. This parameter is only taken into account when using 'Native' canvas labels. For DOM based labels the className *node* is added to the DOM element for styling via CSS. You can also use <Options.Controller> methods to style individual labels.
+*/
+Options.Label = {
+  $extend: false,
+  
+  overridable: false,
+  type: 'HTML', //'SVG', 'Native'
+  style: ' ',
+  size: 10,
+  family: 'sans-serif',
+  textAlign: 'center',
+  textBaseline: 'alphabetic',
+  color: '#fff'
+};
+
+
+/*
+ * File: Options.Tips.js
+ *
+ */
+
+/*
+  Object: Options.Tips
+  
+  Tips options
+  
+  Syntax:
+    
+  (start code js)
+  Options.Tips = {
+    enable: false,
+    type: 'auto',
+    offsetX: 20,
+    offsetY: 20,
+    onShow: $.empty,
+    onHide: $.empty
+  };
+  (end code)
+  
+  Example:
+  
+  (start code js)
+  var viz = new $jit.Viz({
+    Tips: {
+      enable: true,
+      type: 'Native',
+      offsetX: 10,
+      offsetY: 10,
+      onShow: function(tip, node) {
+        tip.innerHTML = node.name;
+      }
+    }
+  });
+  (end code)
+
+  Parameters:
+
+  enable - (boolean) Default's *false*. If *true*, a tooltip will be shown when a node is hovered. The tooltip is a div DOM element having "tip" as CSS class. 
+  type - (string) Default's *auto*. Defines where to attach the MouseEnter/Leave tooltip events. Possible values are 'Native' to attach them to the canvas or 'HTML' to attach them to DOM label elements (if defined). 'auto' sets this property to the value of <Options.Label>'s *type* property.
+  offsetX - (number) Default's *20*. An offset added to the current tooltip x-position (which is the same as the current mouse position). Default's 20.
+  offsetY - (number) Default's *20*. An offset added to the current tooltip y-position (which is the same as the current mouse position). Default's 20.
+  onShow(tip, node) - This callack is used right before displaying a tooltip. The first formal parameter is the tip itself (which is a DivElement). The second parameter may be a <Graph.Node> for graph based visualizations or an object with label, value properties for charts.
+  onHide() - This callack is used when hiding a tooltip.
+
+*/
+Options.Tips = {
+  $extend: false,
+  
+  enable: false,
+  type: 'auto',
+  offsetX: 20,
+  offsetY: 20,
+  force: false,
+  onShow: $.empty,
+  onHide: $.empty
+};
+
+
+/*
+ * File: Options.NodeStyles.js
+ *
+ */
+
+/*
+  Object: Options.NodeStyles
+  
+  Apply different styles when a node is hovered or selected.
+  
+  Syntax:
+    
+  (start code js)
+  Options.NodeStyles = {
+    enable: false,
+    type: 'auto',
+    stylesHover: false,
+    stylesClick: false
+  };
+  (end code)
+  
+  Example:
+  
+  (start code js)
+  var viz = new $jit.Viz({
+    NodeStyles: {
+      enable: true,
+      type: 'Native',
+      stylesHover: {
+        dim: 30,
+        color: '#fcc'
+      },
+      duration: 600
+    }
+  });
+  (end code)
+
+  Parameters:
+  
+  enable - (boolean) Default's *false*. Whether to enable this option.
+  type - (string) Default's *auto*. Use this to attach the hover/click events in the nodes or the nodes labels (if they have been defined as DOM elements: 'HTML' or 'SVG', see <Options.Label> for more details). The default 'auto' value will set NodeStyles to the same type defined for <Options.Label>.
+  stylesHover - (boolean|object) Default's *false*. An object with node styles just like the ones defined for <Options.Node> or *false* otherwise.
+  stylesClick - (boolean|object) Default's *false*. An object with node styles just like the ones defined for <Options.Node> or *false* otherwise.
+*/
+
+Options.NodeStyles = {
+  $extend: false,
+  
+  enable: false,
+  type: 'auto',
+  stylesHover: false,
+  stylesClick: false
+};
+
+
+/*
+ * File: Options.Events.js
+ *
+*/
+
+/*
+  Object: Options.Events
+  
+  Configuration for adding mouse/touch event handlers to Nodes.
+  
+  Syntax:
+  
+  (start code js)
+  Options.Events = {
+    enable: false,
+    enableForEdges: false,
+    type: 'auto',
+    onClick: $.empty,
+    onRightClick: $.empty,
+    onMouseMove: $.empty,
+    onMouseEnter: $.empty,
+    onMouseLeave: $.empty,
+    onDragStart: $.empty,
+    onDragMove: $.empty,
+    onDragCancel: $.empty,
+    onDragEnd: $.empty,
+    onTouchStart: $.empty,
+    onTouchMove: $.empty,
+    onTouchEnd: $.empty,
+    onTouchCancel: $.empty,
+    onMouseWheel: $.empty
+  };
+  (end code)
+  
+  Example:
+  
+  (start code js)
+  var viz = new $jit.Viz({
+    Events: {
+      enable: true,
+      onClick: function(node, eventInfo, e) {
+        viz.doSomething();
+      },
+      onMouseEnter: function(node, eventInfo, e) {
+        viz.canvas.getElement().style.cursor = 'pointer';
+      },
+      onMouseLeave: function(node, eventInfo, e) {
+        viz.canvas.getElement().style.cursor = '';
+      }
+    }
+  });
+  (end code)
+  
+  Parameters:
+  
+  enable - (boolean) Default's *false*. Whether to enable the Event system.
+  enableForEdges - (boolean) Default's *false*. Whether to track events also in arcs. If *true* the same callbacks -described below- are used for nodes *and* edges. A simple duck type check for edges is to check for *node.nodeFrom*.
+  type - (string) Default's 'auto'. Whether to attach the events onto the HTML labels (via event delegation) or to use the custom 'Native' canvas Event System of the library. 'auto' is set when you let the <Options.Label> *type* parameter decide this.
+  onClick(node, eventInfo, e) - Triggered when a user performs a click in the canvas. *node* is the <Graph.Node> clicked or false if no node has been clicked. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas. 
+  onRightClick(node, eventInfo, e) - Triggered when a user performs a right click in the canvas. *node* is the <Graph.Node> right clicked or false if no node has been clicked. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas. 
+  onMouseMove(node, eventInfo, e) - Triggered when the user moves the mouse. *node* is the <Graph.Node> under the cursor as it's moving over the canvas or false if no node has been clicked. *e* is the grabbed event (should return the native event in a cross-browser manner).  *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas.
+  onMouseEnter(node, eventInfo, e) - Triggered when a user moves the mouse over a node. *node* is the <Graph.Node> that the mouse just entered. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas. 
+  onMouseLeave(node, eventInfo, e) - Triggered when the user mouse-outs a node. *node* is the <Graph.Node> 'mouse-outed'. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas. 
+  onDragStart(node, eventInfo, e) - Triggered when the user mouse-downs over a node. *node* is the <Graph.Node> being pressed. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas. 
+  onDragMove(node, eventInfo, e) - Triggered when a user, after pressing the mouse button over a node, moves the mouse around. *node* is the <Graph.Node> being dragged. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas. 
+  onDragEnd(node, eventInfo, e) - Triggered when a user finished dragging a node. *node* is the <Graph.Node> being dragged. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas. 
+  onDragCancel(node, eventInfo, e) - Triggered when the user releases the mouse button over a <Graph.Node> that wasn't dragged (i.e. the user didn't perform any mouse movement after pressing the mouse button). *node* is the <Graph.Node> being dragged. *e* is the grabbed event (should return the native event in a cross-browser manner). *eventInfo* is an object containing useful methods like *getPos* to get the mouse position relative to the canvas. 
+  onTouchStart(node, eventInfo, e) - Behaves just like onDragStart. 
+  onTouchMove(node, eventInfo, e) - Behaves just like onDragMove. 
+  onTouchEnd(node, eventInfo, e) - Behaves just like onDragEnd. 
+  onTouchCancel(node, eventInfo, e) - Behaves just like onDragCancel.
+  onMouseWheel(delta, e) - Triggered when the user uses the mouse scroll over the canvas. *delta* is 1 or -1 depending on the sense of the mouse scroll.
+*/
+
+Options.Events = {
+  $extend: false,
+  
+  enable: false,
+  enableForEdges: false,
+  type: 'auto',
+  onClick: $.empty,
+  onRightClick: $.empty,
+  onMouseMove: $.empty,
+  onMouseEnter: $.empty,
+  onMouseLeave: $.empty,
+  onDragStart: $.empty,
+  onDragMove: $.empty,
+  onDragCancel: $.empty,
+  onDragEnd: $.empty,
+  onTouchStart: $.empty,
+  onTouchMove: $.empty,
+  onTouchEnd: $.empty,
+  onMouseWheel: $.empty
+};
+
+/*
+ * File: Options.Navigation.js
+ *
+*/
+
+/*
+  Object: Options.Navigation
+  
+  Panning and zooming options for Graph/Tree based visualizations. These options are implemented 
+  by all visualizations except charts (<AreaChart>, <BarChart> and <PieChart>).
+  
+  Syntax:
+  
+  (start code js)
+
+  Options.Navigation = {
+    enable: false,
+    type: 'auto',
+    panning: false, //true, 'avoid nodes'
+    zooming: false
+  };
+  
+  (end code)
+  
+  Example:
+    
+  (start code js)
+  var viz = new $jit.Viz({
+    Navigation: {
+      enable: true,
+      panning: 'avoid nodes',
+      zooming: 20
+    }
+  });
+  (end code)
+  
+  Parameters:
+  
+  enable - (boolean) Default's *false*. Whether to enable Navigation capabilities.
+  type - (string) Default's 'auto'. Whether to attach the navigation events onto the HTML labels (via event delegation) or to use the custom 'Native' canvas Event System of the library. When 'auto' set when you let the <Options.Label> *type* parameter decide this.
+  panning - (boolean|string) Default's *false*. Set this property to *true* if you want to add Drag and Drop panning support to the visualization. You can also set this parameter to 'avoid nodes' to enable DnD panning but disable it if the DnD is taking place over a node. This is useful when some other events like Drag & Drop for nodes are added to <Graph.Nodes>.
+  zooming - (boolean|number) Default's *false*. Set this property to a numeric value to turn mouse-scroll zooming on. The number will be proportional to the mouse-scroll sensitivity.
+  
+*/
+
+Options.Navigation = {
+  $extend: false,
+  
+  enable: false,
+  type: 'auto',
+  panning: false, //true | 'avoid nodes'
+  zooming: false
+};
+
+/*
+ * File: Options.Controller.js
+ *
+*/
+
+/*
+  Object: Options.Controller
+  
+  Provides controller methods. Controller methods are callback functions that get called at different stages 
+  of the animation, computing or plotting of the visualization.
+  
+  Implemented by:
+    
+  All visualizations except charts (<AreaChart>, <BarChart> and <PieChart>).
+  
+  Syntax:
+  
+  (start code js)
+
+  Options.Controller = {
+    onBeforeCompute: $.empty,
+    onAfterCompute:  $.empty,
+    onCreateLabel:   $.empty,
+    onPlaceLabel:    $.empty,
+    onComplete:      $.empty,
+    onBeforePlotLine:$.empty,
+    onAfterPlotLine: $.empty,
+    onBeforePlotNode:$.empty,
+    onAfterPlotNode: $.empty,
+    request:         false
+  };
+  
+  (end code)
+  
+  Example:
+    
+  (start code js)
+  var viz = new $jit.Viz({
+    onBeforePlotNode: function(node) {
+      if(node.selected) {
+        node.setData('color', '#ffc');
+      } else {
+        node.removeData('color');
+      }
+    },
+    onBeforePlotLine: function(adj) {
+      if(adj.nodeFrom.selected && adj.nodeTo.selected) {
+        adj.setData('color', '#ffc');
+      } else {
+        adj.removeData('color');
+      }
+    },
+    onAfterCompute: function() {
+      alert("computed!");
+    }
+  });
+  (end code)
+  
+  Parameters:
+
+   onBeforeCompute(node) - This method is called right before performing all computations and animations. The selected <Graph.Node> is passed as parameter.
+   onAfterCompute() - This method is triggered after all animations or computations ended.
+   onCreateLabel(domElement, node) - This method receives a new label DIV element as first parameter, and the corresponding <Graph.Node> as second parameter. This method will only be called once for each label. This method is useful when adding events or styles to the labels used by the JIT.
+   onPlaceLabel(domElement, node) - This method receives a label DIV element as first parameter and the corresponding <Graph.Node> as second parameter. This method is called each time a label has been placed in the visualization, for example at each step of an animation, and thus it allows you to update the labels properties, such as size or position. Note that onPlaceLabel will be triggered after updating the labels positions. That means that, for example, the left and top css properties are already updated to match the nodes positions. Width and height properties are not set however.
+   onBeforePlotNode(node) - This method is triggered right before plotting each <Graph.Node>. This method is useful for changing a node style right before plotting it.
+   onAfterPlotNode(node) - This method is triggered right after plotting each <Graph.Node>.
+   onBeforePlotLine(adj) - This method is triggered right before plotting a <Graph.Adjacence>. This method is useful for adding some styles to a particular edge before being plotted.
+   onAfterPlotLine(adj) - This method is triggered right after plotting a <Graph.Adjacence>.
+
+    *Used in <ST>, <TM.Base> and <Icicle> visualizations*
+    
+    request(nodeId, level, onComplete) - This method is used for buffering information into the visualization. When clicking on an empty node, the visualization will make a request for this node's subtrees, specifying a given level for this subtree (defined by _levelsToShow_). Once the request is completed, the onComplete callback should be called with the given result. This is useful to provide on-demand information into the visualizations withought having to load the entire information from start. The parameters used by this method are _nodeId_, which is the id of the root of the subtree to request, _level_ which is the depth of the subtree to be requested (0 would mean just the root node). _onComplete_ is an object having the callback method _onComplete.onComplete(json)_ that should be called once the json has been retrieved.  
+ 
+ */
+Options.Controller = {
+  $extend: true,
+  
+  onBeforeCompute: $.empty,
+  onAfterCompute:  $.empty,
+  onCreateLabel:   $.empty,
+  onPlaceLabel:    $.empty,
+  onComplete:      $.empty,
+  onBeforePlotLine:$.empty,
+  onAfterPlotLine: $.empty,
+  onBeforePlotNode:$.empty,
+  onAfterPlotNode: $.empty,
+  request:         false
+};
+
+
+/*
+ * File: Extras.js
+ * 
+ * Provides Extras such as Tips and Style Effects.
+ * 
+ * Description:
+ * 
+ * Provides the <Tips> and <NodeStyles> classes and functions.
+ *
+ */
+
+/*
+ * Manager for mouse events (clicking and mouse moving).
+ * 
+ * This class is used for registering objects implementing onClick
+ * and onMousemove methods. These methods are called when clicking or
+ * moving the mouse around  the Canvas.
+ * For now, <Tips> and <NodeStyles> are classes implementing these methods.
+ * 
+ */
+var ExtrasInitializer = {
+  initialize: function(className, viz) {
+    this.viz = viz;
+    this.canvas = viz.canvas;
+    this.config = viz.config[className];
+    this.nodeTypes = viz.fx.nodeTypes;
+    var type = this.config.type;
+    this.dom = type == 'auto'? (viz.config.Label.type != 'Native') : (type != 'Native');
+    this.labelContainer = this.dom && viz.labels.getLabelContainer();
+    this.isEnabled() && this.initializePost();
+  },
+  initializePost: $.empty,
+  setAsProperty: $.lambda(false),
+  isEnabled: function() {
+    return this.config.enable;
+  },
+  isLabel: function(e, win, group) {
+    e = $.event.get(e, win);
+    var labelContainer = this.labelContainer,
+        target = e.target || e.srcElement,
+        related = e.relatedTarget;
+    if(group) {
+      return related && related == this.viz.canvas.getCtx().canvas 
+          && !!target && this.isDescendantOf(target, labelContainer);
+    } else {
+      return this.isDescendantOf(target, labelContainer);
+    }
+  },
+  isDescendantOf: function(elem, par) {
+    while(elem && elem.parentNode) {
+      if(elem.parentNode == par)
+        return elem;
+      elem = elem.parentNode;
+    }
+    return false;
+  }
+};
+
+var EventsInterface = {
+  onMouseUp: $.empty,
+  onMouseDown: $.empty,
+  onMouseMove: $.empty,
+  onMouseOver: $.empty,
+  onMouseOut: $.empty,
+  onMouseWheel: $.empty,
+  onTouchStart: $.empty,
+  onTouchMove: $.empty,
+  onTouchEnd: $.empty,
+  onTouchCancel: $.empty
+};
+
+var MouseEventsManager = new Class({
+  initialize: function(viz) {
+    this.viz = viz;
+    this.canvas = viz.canvas;
+    this.node = false;
+    this.edge = false;
+    this.registeredObjects = [];
+    this.attachEvents();
+  },
+  
+  attachEvents: function() {
+    var htmlCanvas = this.canvas.getElement(), 
+        that = this;
+    htmlCanvas.oncontextmenu = $.lambda(false);
+    $.addEvents(htmlCanvas, {
+      'mouseup': function(e, win) {
+        var event = $.event.get(e, win);
+        that.handleEvent('MouseUp', e, win, 
+            that.makeEventObject(e, win), 
+            $.event.isRightClick(event));
+      },
+      'mousedown': function(e, win) {
+        var event = $.event.get(e, win);
+        that.handleEvent('MouseDown', e, win, that.makeEventObject(e, win), 
+            $.event.isRightClick(event));
+      },
+      'mousemove': function(e, win) {
+        that.handleEvent('MouseMove', e, win, that.makeEventObject(e, win));
+      },
+      'mouseover': function(e, win) {
+        that.handleEvent('MouseOver', e, win, that.makeEventObject(e, win));
+      },
+      'mouseout': function(e, win) {
+        that.handleEvent('MouseOut', e, win, that.makeEventObject(e, win));
+      },
+      'touchstart': function(e, win) {
+        that.handleEvent('TouchStart', e, win, that.makeEventObject(e, win));
+      },
+      'touchmove': function(e, win) {
+        that.handleEvent('TouchMove', e, win, that.makeEventObject(e, win));
+      },
+      'touchend': function(e, win) {
+        that.handleEvent('TouchEnd', e, win, that.makeEventObject(e, win));
+      }
+    });
+    //attach mousewheel event
+    var handleMouseWheel = function(e, win) {
+      var event = $.event.get(e, win);
+      var wheel = $.event.getWheel(event);
+      that.handleEvent('MouseWheel', e, win, wheel);
+    };
+    //TODO(nico): this is a horrible check for non-gecko browsers!
+    if(!document.getBoxObjectFor && window.mozInnerScreenX == null) {
+      $.addEvent(htmlCanvas, 'mousewheel', handleMouseWheel);
+    } else {
+      htmlCanvas.addEventListener('DOMMouseScroll', handleMouseWheel, false);
+    }
+  },
+  
+  register: function(obj) {
+    this.registeredObjects.push(obj);
+  },
+  
+  handleEvent: function() {
+    var args = Array.prototype.slice.call(arguments),
+        type = args.shift();
+    for(var i=0, regs=this.registeredObjects, l=regs.length; i<l; i++) {
+      regs[i]['on' + type].apply(regs[i], args);
+    }
+  },
+  
+  makeEventObject: function(e, win) {
+    var that = this,
+        graph = this.viz.graph,
+        fx = this.viz.fx,
+        ntypes = fx.nodeTypes,
+        etypes = fx.edgeTypes;
+    return {
+      pos: false,
+      node: false,
+      edge: false,
+      contains: false,
+      getNodeCalled: false,
+      getEdgeCalled: false,
+      getPos: function() {
+        //TODO(nico): check why this can't be cache anymore when using edge detection
+        //if(this.pos) return this.pos;
+        var canvas = that.viz.canvas,
+            s = canvas.getSize(),
+            p = canvas.getPos(),
+            ox = canvas.translateOffsetX,
+            oy = canvas.translateOffsetY,
+            sx = canvas.scaleOffsetX,
+            sy = canvas.scaleOffsetY,
+            pos = $.event.getPos(e, win);
+        this.pos = {
+          x: (pos.x - p.x - s.width/2 - ox) * 1/sx,
+          y: (pos.y - p.y - s.height/2 - oy) * 1/sy
+        };
+        return this.pos;
+      },
+      getNode: function() {
+        if(this.getNodeCalled) return this.node;
+        this.getNodeCalled = true;
+        for(var id in graph.nodes) {
+          var n = graph.nodes[id],
+              geom = n && ntypes[n.getData('type')],
+              contains = geom && geom.contains && geom.contains.call(fx, n, this.getPos());
+          if(contains) {
+            this.contains = contains;
+            return that.node = this.node = n;
+          }
+        }
+        return that.node = this.node = false;
+      },
+      getEdge: function() {
+        if(this.getEdgeCalled) return this.edge;
+        this.getEdgeCalled = true;
+        var hashset = {};
+        for(var id in graph.edges) {
+          var edgeFrom = graph.edges[id];
+          hashset[id] = true;
+          for(var edgeId in edgeFrom) {
+            if(edgeId in hashset) continue;
+            var e = edgeFrom[edgeId],
+                geom = e && etypes[e.getData('type')],
+                contains = geom && geom.contains && geom.contains.call(fx, e, this.getPos());
+            if(contains) {
+              this.contains = contains;
+              return that.edge = this.edge = e;
+            }
+          }
+        }
+        return that.edge = this.edge = false;
+      },
+      getContains: function() {
+        if(this.getNodeCalled) return this.contains;
+        this.getNode();
+        return this.contains;
+      }
+    };
+  }
+});
+
+/* 
+ * Provides the initialization function for <NodeStyles> and <Tips> implemented 
+ * by all main visualizations.
+ *
+ */
+var Extras = {
+  initializeExtras: function() {
+    var mem = new MouseEventsManager(this), that = this;
+    $.each(['NodeStyles', 'Tips', 'Navigation', 'Events'], function(k) {
+      var obj = new Extras.Classes[k](k, that);
+      if(obj.isEnabled()) {
+        mem.register(obj);
+      }
+      if(obj.setAsProperty()) {
+        that[k.toLowerCase()] = obj;
+      }
+    });
+  }   
+};
+
+Extras.Classes = {};
+/*
+  Class: Events
+   
+  This class defines an Event API to be accessed by the user.
+  The methods implemented are the ones defined in the <Options.Events> object.
+*/
+
+Extras.Classes.Events = new Class({
+  Implements: [ExtrasInitializer, EventsInterface],
+  
+  initializePost: function() {
+    this.fx = this.viz.fx;
+    this.ntypes = this.viz.fx.nodeTypes;
+    this.etypes = this.viz.fx.edgeTypes;
+    
+    this.hovered = false;
+    this.pressed = false;
+    this.touched = false;
+
+    this.touchMoved = false;
+    this.moved = false;
+    
+  },
+  
+  setAsProperty: $.lambda(true),
+  
+  onMouseUp: function(e, win, event, isRightClick) {
+    var evt = $.event.get(e, win);
+    if(!this.moved) {
+      if(isRightClick) {
+        this.config.onRightClick(this.hovered, event, evt);
+      } else {
+        this.config.onClick(this.pressed, event, evt);
+      }
+    }
+    if(this.pressed) {
+      if(this.moved) {
+        this.config.onDragEnd(this.pressed, event, evt);
+      } else {
+        this.config.onDragCancel(this.pressed, event, evt);
+      }
+      this.pressed = this.moved = false;
+    }
+  },
+
+  onMouseOut: function(e, win, event) {
+   //mouseout a label
+   var evt = $.event.get(e, win), label;
+   if(this.dom && (label = this.isLabel(e, win, true))) {
+     this.config.onMouseLeave(this.viz.graph.getNode(label.id),
+                              event, evt);
+     this.hovered = false;
+     return;
+   }
+   //mouseout canvas
+   var rt = evt.relatedTarget,
+       canvasWidget = this.canvas.getElement();
+   while(rt && rt.parentNode) {
+     if(canvasWidget == rt.parentNode) return;
+     rt = rt.parentNode;
+   }
+   if(this.hovered) {
+     this.config.onMouseLeave(this.hovered,
+         event, evt);
+     this.hovered = false;
+   }
+  },
+  
+  onMouseOver: function(e, win, event) {
+    //mouseover a label
+    var evt = $.event.get(e, win), label;
+    if(this.dom && (label = this.isLabel(e, win, true))) {
+      this.hovered = this.viz.graph.getNode(label.id);
+      this.config.onMouseEnter(this.hovered,
+                               event, evt);
+    }
+  },
+  
+  onMouseMove: function(e, win, event) {
+   var label, evt = $.event.get(e, win);
+   if(this.pressed) {
+     this.moved = true;
+     this.config.onDragMove(this.pressed, event, evt);
+     return;
+   }
+   if(this.dom) {
+     this.config.onMouseMove(this.hovered,
+         event, evt);
+   } else {
+     if(this.hovered) {
+       var hn = this.hovered;
+       var geom = hn.nodeFrom? this.etypes[hn.getData('type')] : this.ntypes[hn.getData('type')];
+       var contains = geom && geom.contains 
+         && geom.contains.call(this.fx, hn, event.getPos());
+       if(contains) {
+         this.config.onMouseMove(hn, event, evt);
+         return;
+       } else {
+         this.config.onMouseLeave(hn, event, evt);
+         this.hovered = false;
+       }
+     }
+     if(this.hovered = (event.getNode() || (this.config.enableForEdges && event.getEdge()))) {
+       this.config.onMouseEnter(this.hovered, event, evt);
+     } else {
+       this.config.onMouseMove(false, event, evt);
+     }
+   }
+  },
+  
+  onMouseWheel: function(e, win, delta) {
+    this.config.onMouseWheel(delta, $.event.get(e, win));
+  },
+  
+  onMouseDown: function(e, win, event) {
+    var evt = $.event.get(e, win), label;
+    if(this.dom) {
+      if(label = this.isLabel(e, win)) {
+        this.pressed = this.viz.graph.getNode(label.id);
+      }
+    } else {
+      this.pressed = event.getNode() || (this.config.enableForEdges && event.getEdge());
+    }
+    this.pressed && this.config.onDragStart(this.pressed, event, evt);
+  },
+  
+  onTouchStart: function(e, win, event) {
+    var evt = $.event.get(e, win), label;
+    if(this.dom && (label = this.isLabel(e, win))) {
+      this.touched = this.viz.graph.getNode(label.id);
+    } else {
+      this.touched = event.getNode() || (this.config.enableForEdges && event.getEdge());
+    }
+    this.touched && this.config.onTouchStart(this.touched, event, evt);
+  },
+  
+  onTouchMove: function(e, win, event) {
+    var evt = $.event.get(e, win);
+    if(this.touched) {
+      this.touchMoved = true;
+      this.config.onTouchMove(this.touched, event, evt);
+    }
+  },
+  
+  onTouchEnd: function(e, win, event) {
+    var evt = $.event.get(e, win);
+    if(this.touched) {
+      if(this.touchMoved) {
+        this.config.onTouchEnd(this.touched, event, evt);
+      } else {
+        this.config.onTouchCancel(this.touched, event, evt);
+      }
+      this.touched = this.touchMoved = false;
+    }
+  }
+});
+
+/*
+   Class: Tips
+    
+   A class containing tip related functions. This class is used internally.
+   
+   Used by:
+   
+   <ST>, <Sunburst>, <Hypertree>, <RGraph>, <TM>, <ForceDirected>, <Icicle>
+   
+   See also:
+   
+   <Options.Tips>
+*/
+
+Extras.Classes.Tips = new Class({
+  Implements: [ExtrasInitializer, EventsInterface],
+  
+  initializePost: function() {
+    //add DOM tooltip
+    if(document.body) {
+      var tip = $('_tooltip') || document.createElement('div');
+      tip.id = '_tooltip';
+      tip.className = 'tip';
+      $.extend(tip.style, {
+        position: 'absolute',
+        display: 'none',
+        zIndex: 13000
+      });
+      document.body.appendChild(tip);
+      this.tip = tip;
+      this.node = false;
+    }
+  },
+  
+  setAsProperty: $.lambda(true),
+  
+  onMouseOut: function(e, win) {
+    //mouseout a label
+    var evt = $.event.get(e, win);
+    if(this.dom && this.isLabel(e, win, true)) {
+      this.hide(true);
+      return;
+    }
+    //mouseout canvas
+    var rt = e.relatedTarget,
+        canvasWidget = this.canvas.getElement();
+    while(rt && rt.parentNode) {
+      if(canvasWidget == rt.parentNode) return;
+      rt = rt.parentNode;
+    }
+    this.hide(false);
+  },
+  
+  onMouseOver: function(e, win) {
+    //mouseover a label
+    var label;
+    if(this.dom && (label = this.isLabel(e, win, false))) {
+      this.node = this.viz.graph.getNode(label.id);
+      this.config.onShow(this.tip, this.node, label);
+    }
+  },
+  
+  onMouseMove: function(e, win, opt) {
+    if(this.dom && this.isLabel(e, win)) {
+      this.setTooltipPosition($.event.getPos(e, win));
+    }
+    if(!this.dom) {
+      var node = opt.getNode();
+      if(!node) {
+        this.hide(true);
+        return;
+      }
+      if(this.config.force || !this.node || this.node.id != node.id) {
+        this.node = node;
+        this.config.onShow(this.tip, node, opt.getContains());
+      }
+      this.setTooltipPosition($.event.getPos(e, win));
+    }
+  },
+  
+  setTooltipPosition: function(pos) {
+    var tip = this.tip, 
+        style = tip.style, 
+        cont = this.config;
+    style.display = '';
+    //get window dimensions
+    var win = {
+      'height': document.body.clientHeight,
+      'width': document.body.clientWidth
+    };
+    //get tooltip dimensions
+    var obj = {
+      'width': tip.offsetWidth,
+      'height': tip.offsetHeight  
+    };
+    //set tooltip position
+    var x = cont.offsetX, y = cont.offsetY;
+    style.top = ((pos.y + y + obj.height > win.height)?  
+        (pos.y - obj.height - y) : pos.y + y) + 'px';
+    style.left = ((pos.x + obj.width + x > win.width)? 
+        (pos.x - obj.width - x) : pos.x + x) + 'px';
+  },
+  
+  hide: function(triggerCallback) {
+    this.tip.style.display = 'none';
+    triggerCallback && this.config.onHide();
+  }
+});
+
+/*
+  Class: NodeStyles
+   
+  Change node styles when clicking or hovering a node. This class is used internally.
+  
+  Used by:
+  
+  <ST>, <Sunburst>, <Hypertree>, <RGraph>, <TM>, <ForceDirected>, <Icicle>
+  
+  See also:
+  
+  <Options.NodeStyles>
+*/
+Extras.Classes.NodeStyles = new Class({
+  Implements: [ExtrasInitializer, EventsInterface],
+  
+  initializePost: function() {
+    this.fx = this.viz.fx;
+    this.types = this.viz.fx.nodeTypes;
+    this.nStyles = this.config;
+    this.nodeStylesOnHover = this.nStyles.stylesHover;
+    this.nodeStylesOnClick = this.nStyles.stylesClick;
+    this.hoveredNode = false;
+    this.fx.nodeFxAnimation = new Animation();
+    
+    this.down = false;
+    this.move = false;
+  },
+  
+  onMouseOut: function(e, win) {
+    this.down = this.move = false;
+    if(!this.hoveredNode) return;
+    //mouseout a label
+    if(this.dom && this.isLabel(e, win, true)) {
+      this.toggleStylesOnHover(this.hoveredNode, false);
+    }
+    //mouseout canvas
+    var rt = e.relatedTarget,
+        canvasWidget = this.canvas.getElement();
+    while(rt && rt.parentNode) {
+      if(canvasWidget == rt.parentNode) return;
+      rt = rt.parentNode;
+    }
+    this.toggleStylesOnHover(this.hoveredNode, false);
+    this.hoveredNode = false;
+  },
+  
+  onMouseOver: function(e, win) {
+    //mouseover a label
+    var label;
+    if(this.dom && (label = this.isLabel(e, win, true))) {
+      var node = this.viz.graph.getNode(label.id);
+      if(node.selected) return;
+      this.hoveredNode = node;
+      this.toggleStylesOnHover(this.hoveredNode, true);
+    }
+  },
+  
+  onMouseDown: function(e, win, event, isRightClick) {
+    if(isRightClick) return;
+    var label;
+    if(this.dom && (label = this.isLabel(e, win))) {
+      this.down = this.viz.graph.getNode(label.id);
+    } else if(!this.dom) {
+      this.down = event.getNode();
+    }
+    this.move = false;
+  },
+  
+  onMouseUp: function(e, win, event, isRightClick) {
+    if(isRightClick) return;
+    if(!this.move) {
+      this.onClick(event.getNode());
+    }
+    this.down = this.move = false;
+  },
+  
+  getRestoredStyles: function(node, type) {
+    var restoredStyles = {}, 
+        nStyles = this['nodeStylesOn' + type];
+    for(var prop in nStyles) {
+      restoredStyles[prop] = node.styles['$' + prop];
+    }
+    return restoredStyles;
+  },
+  
+  toggleStylesOnHover: function(node, set) {
+    if(this.nodeStylesOnHover) {
+      this.toggleStylesOn('Hover', node, set);
+    }
+  },
+
+  toggleStylesOnClick: function(node, set) {
+    if(this.nodeStylesOnClick) {
+      this.toggleStylesOn('Click', node, set);
+    }
+  },
+  
+  toggleStylesOn: function(type, node, set) {
+    var viz = this.viz;
+    var nStyles = this.nStyles;
+    if(set) {
+      var that = this;
+      if(!node.styles) {
+        node.styles = $.merge(node.data, {});
+      }
+      for(var s in this['nodeStylesOn' + type]) {
+        var $s = '$' + s;
+        if(!($s in node.styles)) {
+            node.styles[$s] = node.getData(s); 
+        }
+      }
+      viz.fx.nodeFx($.extend({
+        'elements': {
+          'id': node.id,
+          'properties': that['nodeStylesOn' + type]
+         },
+         transition: Trans.Quart.easeOut,
+         duration:300,
+         fps:40
+      }, this.config));
+    } else {
+      var restoredStyles = this.getRestoredStyles(node, type);
+      viz.fx.nodeFx($.extend({
+        'elements': {
+          'id': node.id,
+          'properties': restoredStyles
+         },
+         transition: Trans.Quart.easeOut,
+         duration:300,
+         fps:40
+      }, this.config));
+    }
+  },
+
+  onClick: function(node) {
+    if(!node) return;
+    var nStyles = this.nodeStylesOnClick;
+    if(!nStyles) return;
+    //if the node is selected then unselect it
+    if(node.selected) {
+      this.toggleStylesOnClick(node, false);
+      delete node.selected;
+    } else {
+      //unselect all selected nodes...
+      this.viz.graph.eachNode(function(n) {
+        if(n.selected) {
+          for(var s in nStyles) {
+            n.setData(s, n.styles['$' + s], 'end');
+          }
+          delete n.selected;
+        }
+      });
+      //select clicked node
+      this.toggleStylesOnClick(node, true);
+      node.selected = true;
+      delete node.hovered;
+      this.hoveredNode = false;
+    }
+  },
+  
+  onMouseMove: function(e, win, event) {
+    //if mouse button is down and moving set move=true
+    if(this.down) this.move = true;
+    //already handled by mouseover/out
+    if(this.dom && this.isLabel(e, win)) return;
+    var nStyles = this.nodeStylesOnHover;
+    if(!nStyles) return;
+    
+    if(!this.dom) {
+      if(this.hoveredNode) {
+        var geom = this.types[this.hoveredNode.getData('type')];
+        var contains = geom && geom.contains && geom.contains.call(this.fx, 
+            this.hoveredNode, event.getPos());
+        if(contains) return;
+      }
+      var node = event.getNode();
+      //if no node is being hovered then just exit
+      if(!this.hoveredNode && !node) return;
+      //if the node is hovered then exit
+      if(node.hovered) return;
+      //select hovered node
+      if(node && !node.selected) {
+        //check if an animation is running and exit it
+        this.fx.nodeFxAnimation.stopTimer();
+        //unselect all hovered nodes...
+        this.viz.graph.eachNode(function(n) {
+          if(n.hovered && !n.selected) {
+            for(var s in nStyles) {
+              n.setData(s, n.styles['$' + s], 'end');
+            }
+            delete n.hovered;
+          }
+        });
+        //select hovered node
+        node.hovered = true;
+        this.hoveredNode = node;
+        this.toggleStylesOnHover(node, true);
+      } else if(this.hoveredNode && !this.hoveredNode.selected) {
+        //check if an animation is running and exit it
+        this.fx.nodeFxAnimation.stopTimer();
+        //unselect hovered node
+        this.toggleStylesOnHover(this.hoveredNode, false);
+        delete this.hoveredNode.hovered;
+        this.hoveredNode = false;
+      }
+    }
+  }
+});
+
+Extras.Classes.Navigation = new Class({
+  Implements: [ExtrasInitializer, EventsInterface],
+  
+  initializePost: function() {
+    this.pos = false;
+    this.pressed = false;
+  },
+  
+  onMouseWheel: function(e, win, scroll) {
+    if(!this.config.zooming) return;
+    $.event.stop($.event.get(e, win));
+    var val = this.config.zooming / 1000,
+        ans = 1 + scroll * val;
+    this.canvas.scale(ans, ans);
+  },
+  
+  onMouseDown: function(e, win, eventInfo) {
+    if(!this.config.panning) return;
+    if(this.config.panning == 'avoid nodes' && (this.dom? this.isLabel(e, win) : eventInfo.getNode())) return;
+    this.pressed = true;
+    this.pos = eventInfo.getPos();
+    var canvas = this.canvas,
+        ox = canvas.translateOffsetX,
+        oy = canvas.translateOffsetY,
+        sx = canvas.scaleOffsetX,
+        sy = canvas.scaleOffsetY;
+    this.pos.x *= sx;
+    this.pos.x += ox;
+    this.pos.y *= sy;
+    this.pos.y += oy;
+  },
+  
+  onMouseMove: function(e, win, eventInfo) {
+    if(!this.config.panning) return;
+    if(!this.pressed) return;
+    if(this.config.panning == 'avoid nodes' && (this.dom? this.isLabel(e, win) : eventInfo.getNode())) return;
+    var thispos = this.pos, 
+        currentPos = eventInfo.getPos(),
+        canvas = this.canvas,
+        ox = canvas.translateOffsetX,
+        oy = canvas.translateOffsetY,
+        sx = canvas.scaleOffsetX,
+        sy = canvas.scaleOffsetY;
+    currentPos.x *= sx;
+    currentPos.y *= sy;
+    currentPos.x += ox;
+    currentPos.y += oy;
+    var x = currentPos.x - thispos.x,
+        y = currentPos.y - thispos.y;
+    this.pos = currentPos;
+    this.canvas.translate(x * 1/sx, y * 1/sy);
+  },
+  
+  onMouseUp: function(e, win, eventInfo, isRightClick) {
+    if(!this.config.panning) return;
+    this.pressed = false;
+  }
+});
 
 
 /*
  * File: Canvas.js
  *
- * A cross browser Canvas widget.
- *
- * Used By:
- *
- * <ST>, <Hypertree>, <RGraph>
  */
+
 /*
  Class: Canvas
  
- 	A multi-purpose Canvas Class. This Class can be used with the ExCanvas library to provide
- cross browser Canvas based visualizations.
+ 	A canvas widget used by all visualizations. The canvas object can be accessed by doing *viz.canvas*. If you want to 
+ 	know more about <Canvas> options take a look at <Options.Canvas>.
  
- Parameters:
- 
- id - The canvas id. This id will be used as prefix for the canvas widget DOM elements ids.
- options - An object containing multiple options such as
- 
- - _injectInto_ This property is _required_ and it specifies the id of the DOM element
- to which the Canvas widget will be appended
- - _width_ The width of the Canvas widget. Default's to 200px
- - _height_ The height of the Canvas widget. Default's to 200px
- - _backgroundColor_ Used for compatibility with IE. The canvas' background color.
- Default's to '#333'
- - _styles_ A hash containing canvas specific style properties such as _fillStyle_ and _strokeStyle_ among others.
+ A canvas widget is a set of DOM elements that wrap the native canvas DOM Element providing a consistent API and behavior 
+ across all browsers. It can also include Elements to add DOM (SVG or HTML) label support to all visualizations.
  
  Example:
  
@@ -521,21 +2691,16 @@
  	<div id="infovis"></div>
  (end code)
  
- Now we create a new Canvas instance
+ Now we create a new Visualization
  
  (start code js)
- 	//Create a new canvas instance
- 	var canvas = new Canvas('mycanvas', {
+ 	var viz = new $jit.Viz({
  		//Where to inject the canvas. Any div container will do.
  		'injectInto':'infovis',
-		 //width and height for canvas. Default's to 200.
+		 //width and height for canvas. 
+		 //Default's to the container offsetWidth and Height.
 		 'width': 900,
-		 'height':500,
-		 //Canvas styles
-		 'styles': {
-		 'fillStyle': '#ccddee',
-		 'strokeStyle': '#772277'
-		 }
+		 'height':500
 	 });
  (end code)
 
@@ -543,355 +2708,476 @@
  
  (start code xml)
  <div id="infovis">
- 	<div id="mycanvas" style="position:relative;">
- 	<canvas id="mycanvas-canvas" width=900 height=500
+ 	<div id="infovis-canvaswidget" style="position:relative;">
+ 	<canvas id="infovis-canvas" width=900 height=500
  	style="position:absolute; top:0; left:0; width:900px; height:500px;" />
- 	<div id="mycanvas-label"
+ 	<div id="infovis-label"
  	style="overflow:visible; position:absolute; top:0; left:0; width:900px; height:0px">
  	</div>
  	</div>
  </div>
  (end code)
  
- As you can see, the generated HTML consists of a canvas DOM element of id _mycanvas-canvas_ and a div label container
- of id _mycanvas-label_, wrapped in a main div container of id _mycanvas_.
- You can also add a background canvas, for making background drawings.
- This is how the <RGraph> background concentric circles are drawn
- 
- Example:
- 
- (start code js)
- 	//Create a new canvas instance.
- 	var canvas = new Canvas('mycanvas', {
-		//Where to inject the canvas. Any div container will do.
-		'injectInto':'infovis',
-		//width and height for canvas. Default's to 200.
-		'width': 900,
-		'height':500,
-		//Canvas styles
-		'styles': {
-			'fillStyle': '#ccddee',
-			'strokeStyle': '#772277'
-		},
-		//Add a background canvas for plotting
-		//concentric circles.
-		'backgroundCanvas': {
-			//Add Canvas styles for the bck canvas.
-			'styles': {
-				'fillStyle': '#444',
-				'strokeStyle': '#444'
-			},
-			//Add the initialization and plotting functions.
-			'impl': {
-				'init': function() {},
-				'plot': function(canvas, ctx) {
-					var times = 6, d = 100;
-					var pi2 = Math.PI*2;
-					for(var i=1; i<=times; i++) {
-						ctx.beginPath();
-						ctx.arc(0, 0, i * d, 0, pi2, true);
-						ctx.stroke();
-						ctx.closePath();
-					}
-				}
-			}
-		}
-	});
- (end code)
- 
- The _backgroundCanvas_ object contains a canvas _styles_ property and
- an _impl_ key to be used for implementing background canvas specific code.
- 
- The _init_ method is only called once, at the instanciation of the background canvas.
- The _plot_ method is called for plotting a Canvas image.
+ As you can see, the generated HTML consists of a canvas DOM Element of id *infovis-canvas* and a div label container
+ of id *infovis-label*, wrapped in a main div container of id *infovis-canvaswidget*.
  */
-this.Canvas = (function(){
-    var config = {
-        'injectInto': 'id',
-        
-        'width': 200,
-        'height': 200,
-        //deprecated
-        'backgroundColor': '#333333',
-        
-        'styles': {
-            'fillStyle': '#000000',
-            'strokeStyle': '#000000'
-        },
-        
-        'backgroundCanvas': false
-    };
+
+var Canvas;
+(function() {
+  //check for native canvas support
+  var canvasType = typeof HTMLCanvasElement,
+      supportsCanvas = (canvasType == 'object' || canvasType == 'function');
+  //create element function
+  function $E(tag, props) {
+    var elem = document.createElement(tag);
+    for(var p in props) {
+      if(typeof props[p] == "object") {
+        $.extend(elem[p], props[p]);
+      } else {
+        elem[p] = props[p];
+      }
+    }
+    if (tag == "canvas" && !supportsCanvas && G_vmlCanvasManager) {
+      elem = G_vmlCanvasManager.initElement(document.body.appendChild(elem));
+    }
+    return elem;
+  }
+  //canvas widget which we will call just Canvas
+  $jit.Canvas = Canvas = new Class({
+    canvases: [],
+    pos: false,
+    element: false,
+    labelContainer: false,
+    translateOffsetX: 0,
+    translateOffsetY: 0,
+    scaleOffsetX: 1,
+    scaleOffsetY: 1,
     
-    function hasCanvas(){
-        hasCanvas.t = hasCanvas.t || typeof(HTMLCanvasElement);
-        return "function" == hasCanvas.t || "object" == 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 == "canvas" && !hasCanvas() && G_vmlCanvasManager) {
-            elem = G_vmlCanvasManager.initElement(document.body.appendChild(elem));
+    initialize: function(viz, opt) {
+      this.viz = viz;
+      this.opt = this.config = opt;
+      var id = $.type(opt.injectInto) == 'string'? 
+          opt.injectInto:opt.injectInto.id,
+          type = opt.type,
+          idLabel = id + "-label", 
+          wrapper = $(id),
+          width = opt.width || wrapper.offsetWidth,
+          height = opt.height || wrapper.offsetHeight;
+      this.id = id;
+      //canvas options
+      var canvasOptions = {
+        injectInto: id,
+        width: width,
+        height: height
+      };
+      //create main wrapper
+      this.element = $E('div', {
+        'id': id + '-canvaswidget',
+        'style': {
+          'position': 'relative',
+          'width': width + 'px',
+          'height': height + 'px'
         }
-        
-        return elem;
-    };
+      });
+      //create label container
+      this.labelContainer = this.createLabelContainer(opt.Label.type, 
+          idLabel, canvasOptions);
+      //create primary canvas
+      this.canvases.push(new Canvas.Base[type]({
+        config: $.extend({idSuffix: '-canvas'}, canvasOptions),
+        plot: function(base) {
+          viz.fx.plot();
+        },
+        resize: function() {
+          viz.refresh();
+        }
+      }));
+      //create secondary canvas
+      var back = opt.background;
+      if(back) {
+        var backCanvas = new Canvas.Background[back.type](viz, $.extend(back, canvasOptions));
+        this.canvases.push(new Canvas.Base[type](backCanvas));
+      }
+      //insert canvases
+      var len = this.canvases.length;
+      while(len--) {
+        this.element.appendChild(this.canvases[len].canvas);
+        if(len > 0) {
+          this.canvases[len].plot();
+        }
+      }
+      this.element.appendChild(this.labelContainer);
+      wrapper.appendChild(this.element);
+      //Update canvas position when the page is scrolled.
+      var timer = null, that = this;
+      $.addEvent(window, 'scroll', function() {
+        clearTimeout(timer);
+        timer = setTimeout(function() {
+          that.getPos(true); //update canvas position
+        }, 500);
+      });
+    },
+    /*
+      Method: getCtx
+      
+      Returns the main canvas context object
+      
+      Example:
+      
+      (start code js)
+       var ctx = canvas.getCtx();
+       //Now I can use the native canvas context
+       //and for example change some canvas styles
+       ctx.globalAlpha = 1;
+      (end code)
+    */
+    getCtx: function(i) {
+      return this.canvases[i || 0].getCtx();
+    },
+    /*
+      Method: getConfig
+      
+      Returns the current Configuration for this Canvas Widget.
+      
+      Example:
+      
+      (start code js)
+       var config = canvas.getConfig();
+      (end code)
+    */
+    getConfig: function() {
+      return this.opt;
+    },
+    /*
+      Method: getElement
+
+      Returns the main Canvas DOM wrapper
+      
+      Example:
+      
+      (start code js)
+       var wrapper = canvas.getElement();
+       //Returns <div id="infovis-canvaswidget" ... >...</div> as element
+      (end code)
+    */
+    getElement: function() {
+      return this.element;
+    },
+    /*
+      Method: getSize
+      
+      Returns canvas dimensions.
+      
+      Returns:
+      
+      An object with *width* and *height* properties.
+      
+      Example:
+      (start code js)
+      canvas.getSize(); //returns { width: 900, height: 500 }
+      (end code)
+    */
+    getSize: function(i) {
+      return this.canvases[i || 0].getSize();
+    },
+    /*
+      Method: resize
+      
+      Resizes the canvas.
+      
+      Parameters:
+      
+      width - New canvas width.
+      height - New canvas height.
+      
+      Example:
+      
+      (start code js)
+       canvas.resize(width, height);
+      (end code)
     
-    function get(id){
-        return document.getElementById(id);
-    };
+    */
+    resize: function(width, height) {
+      this.getPos(true);
+      this.translateOffsetX = this.translateOffsetY = 0;
+      this.scaleOffsetX = this.scaleOffsetY = 1;
+      for(var i=0, l=this.canvases.length; i<l; i++) {
+        this.canvases[i].resize(width, height);
+      }
+      var style = this.element.style;
+      style.width = width + 'px';
+      style.height = height + 'px';
+      if(this.labelContainer)
+        this.labelContainer.style.width = width + 'px';
+    },
+    /*
+      Method: translate
+      
+      Applies a translation to the canvas.
+      
+      Parameters:
+      
+      x - (number) x offset.
+      y - (number) y offset.
+      disablePlot - (boolean) Default's *false*. Set this to *true* if you don't want to refresh the visualization.
+      
+      Example:
+      
+      (start code js)
+       canvas.translate(30, 30);
+      (end code)
     
-    function translateToCenter(canvas, ctx, w, h){
-        var width = w ? (canvas.width - w) : canvas.width;
-        var height = h ? (canvas.height - h) : canvas.height;
-        ctx.translate(width / 2, height / 2);
-    };
+    */
+    translate: function(x, y, disablePlot) {
+      this.translateOffsetX += x*this.scaleOffsetX;
+      this.translateOffsetY += y*this.scaleOffsetY;
+      for(var i=0, l=this.canvases.length; i<l; i++) {
+        this.canvases[i].translate(x, y, disablePlot);
+      }
+    },
+    /*
+      Method: scale
+      
+      Scales the canvas.
+      
+      Parameters:
+      
+      x - (number) scale value.
+      y - (number) scale value.
+      disablePlot - (boolean) Default's *false*. Set this to *true* if you don't want to refresh the visualization.
+      
+      Example:
+      
+      (start code js)
+       canvas.scale(0.5, 0.5);
+      (end code)
     
-    return function(id, opt){
-        var ctx, bkctx, mainContainer, labelContainer, canvas, bkcanvas;
-        if (arguments.length < 1) 
-            throw "Arguments missing";
-        var idLabel = id + "-label", idCanvas = id + "-canvas", idBCanvas = id + "-bkcanvas";
-        opt = $merge(config, opt || {});
-        //create elements
-        var dim = {
-            'width': opt.width,
-            'height': opt.height
-        };
-        mainContainer = create("div", {
-            'id': id
-        }, $merge(dim, {
-            'position': 'relative'
-        }));
-        labelContainer = create("div", {
-            'id': idLabel
-        }, {
+    */
+    scale: function(x, y, disablePlot) {
+      var px = this.scaleOffsetX * x,
+          py = this.scaleOffsetY * y;
+      var dx = this.translateOffsetX * (x -1) / px,
+          dy = this.translateOffsetY * (y -1) / py;
+      this.scaleOffsetX = px;
+      this.scaleOffsetY = py;
+      for(var i=0, l=this.canvases.length; i<l; i++) {
+        this.canvases[i].scale(x, y, true);
+      }
+      this.translate(dx, dy, false);
+    },
+    /*
+      Method: getPos
+      
+      Returns the canvas position as an *x, y* object.
+      
+      Parameters:
+      
+      force - (boolean) Default's *false*. Set this to *true* if you want to recalculate the position without using any cache information.
+      
+      Returns:
+      
+      An object with *x* and *y* properties.
+      
+      Example:
+      (start code js)
+      canvas.getPos(true); //returns { x: 900, y: 500 }
+      (end code)
+    */
+    getPos: function(force){
+      if(force || !this.pos) {
+        return this.pos = $.getPos(this.getElement());
+      }
+      return this.pos;
+    },
+    /*
+       Method: clear
+       
+       Clears the canvas.
+    */
+    clear: function(i){
+      this.canvases[i||0].clear();
+    },
+    
+    path: function(type, action){
+      var ctx = this.canvases[0].getCtx();
+      ctx.beginPath();
+      action(ctx);
+      ctx[type]();
+      ctx.closePath();
+    },
+    
+    createLabelContainer: function(type, idLabel, dim) {
+      var NS = 'http://www.w3.org/2000/svg';
+      if(type == 'HTML' || type == 'Native') {
+        return $E('div', {
+          'id': idLabel,
+          'style': {
             '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("canvas", $merge({
-            'id': idCanvas
-        }, dim), dimPos);
-        var bc = opt.backgroundCanvas;
-        if (bc) {
-            bkcanvas = create("canvas", $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;
-        var s;
-        for (s in st) 
-            ctx[s] = st[s];
-        if (bc) {
-            bkctx = bkcanvas.getContext('2d');
-            st = bc.styles;
-            for (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 the main canvas context object
-             
-             Returns:
-             
-             Main canvas context
-             
-             Example:
-             
-             (start code js)
-             	var ctx = canvas.getCtx();
-             	//Now I can use the native canvas context
-             	//and for example change some canvas styles
-             	ctx.globalAlpha = 1;
-             (end code)
-             */
-            getCtx: function(){
-                return ctx;
-            },
-            
-            /*
-             Method: getElement
-             Returns the main Canvas DOM wrapper
-             
-             Returns:
-             DOM canvas wrapper generated, (i.e the div wrapper element with id _mycanvas_)
-             
-             Example:
-             (start code js)
-             	var wrapper = canvas.getElement();
-             	//Returns <div id="mycanvas" ... >...</div> as element
-             (end code)
-             */
-            getElement: function(){
-                return mainContainer;
-            },
-            
-            /*
-             Method: resize
-             
-             Resizes the canvas.
-             
-             Parameters:
-             
-             width - New canvas width.
-             height - New canvas height.
-             
-             This method can be used with the <ST>, <Hypertree> or <RGraph> visualizations to resize
-             the visualizations
-             
-             Example:
-             
-             (start code js)
-             	function resizeViz(width, height) {
-             		canvas.resize(width, height);
-             		rgraph.refresh(); //ht.refresh or st.refresh() also work.
-             		rgraph.onAfterCompute();
-             	}
-             (end code)
-             
-             */
-            resize: function(width, height){
-                var pwidth = canvas.width, pheight = canvas.height;
-            	canvas.width = width;
-                canvas.height = height;
-                canvas.style.width = width + "px";
-                canvas.style.height = height + "px";
-                if (bc) {
-                    bkcanvas.width = width;
-                    bkcanvas.height = height;
-                    bkcanvas.style.width = width + "px";
-                    bkcanvas.style.height = height + "px";
-                }
-                //small ExCanvas fix
-                if(!hasCanvas()) {
-                	translateToCenter(canvas, ctx, pwidth, pheight);
-                } else {
-                	translateToCenter(canvas, ctx);
-                }
-                
-                var st = opt.styles;
-                var s;
-                for (s in st) {
-                  ctx[s] = st[s];
-                }
-                if (bc) {
-                    st = bc.styles;
-                    for (s in st) 
-                        bkctx[s] = st[s];
-                    //same ExCanvas fix here
-                    if(!hasCanvas()) {
-                    	translateToCenter(bkcanvas, bkctx, pwidth, pheight);
-                    } else {
-                    	translateToCenter(bkcanvas, bkctx);	
-                    }
-                    
-                    bc.impl.init(bkcanvas, bkctx);
-                    bc.impl.plot(bkcanvas, bkctx);
-                }
-            },
-            
-            /*
-             Method: getSize
-             
-             Returns canvas dimensions.
-             
-             Returns:
-             
-             An object with _width_ and _height_ properties.
-             Example:
-             (start code js)
-             canvas.getSize(); //returns { width: 900, height: 500 }
-             (end code)
-             */
-            getSize: function(){
-                return {
-                    'width': canvas.width,
-                    'height': canvas.height
-                };
-            },
-            
-            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 <Canvas.clear> 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, Math.abs(right - left), Math.abs(bottom - top));
-                    ctx.fillStyle = f0;
-                }
-                else {
-                    ctx.clearRect(left, top, Math.abs(right - left), Math.abs(bottom - top));
-                }
-            }
-        };
-    };
-    
-})();
+      } else if(type == 'SVG') {
+        var svgContainer = document.createElementNS(NS, 'svg:svg');
+        svgContainer.setAttribute("width", dim.width);
+        svgContainer.setAttribute('height', dim.height);
+        var style = svgContainer.style;
+        style.position = 'absolute';
+        style.left = style.top = '0px';
+        var labelContainer = document.createElementNS(NS, 'svg:g');
+        labelContainer.setAttribute('width', dim.width);
+        labelContainer.setAttribute('height', dim.height);
+        labelContainer.setAttribute('x', 0);
+        labelContainer.setAttribute('y', 0);
+        labelContainer.setAttribute('id', idLabel);
+        svgContainer.appendChild(labelContainer);
+        return svgContainer;
+      }
+    }
+  });
+  //base canvas wrapper
+  Canvas.Base = {};
+  Canvas.Base['2D'] = new Class({
+    translateOffsetX: 0,
+    translateOffsetY: 0,
+    scaleOffsetX: 1,
+    scaleOffsetY: 1,
 
+    initialize: function(viz) {
+      this.viz = viz;
+      this.opt = viz.config;
+      this.size = false;
+      this.createCanvas();
+      this.translateToCenter();
+    },
+    createCanvas: function() {
+      var opt = this.opt,
+          width = opt.width,
+          height = opt.height;
+      this.canvas = $E('canvas', {
+        'id': opt.injectInto + opt.idSuffix,
+        'width': width,
+        'height': height,
+        'style': {
+          'position': 'absolute',
+          'top': 0,
+          'left': 0,
+          'width': width + 'px',
+          'height': height + 'px'
+        }
+      });
+    },
+    getCtx: function() {
+      if(!this.ctx) 
+        return this.ctx = this.canvas.getContext('2d');
+      return this.ctx;
+    },
+    getSize: function() {
+      if(this.size) return this.size;
+      var canvas = this.canvas;
+      return this.size = {
+        width: canvas.width,
+        height: canvas.height
+      };
+    },
+    translateToCenter: function(ps) {
+      var size = this.getSize(),
+          width = ps? (size.width - ps.width - this.translateOffsetX*2) : size.width;
+          height = ps? (size.height - ps.height - this.translateOffsetY*2) : size.height;
+      var ctx = this.getCtx();
+      ps && ctx.scale(1/this.scaleOffsetX, 1/this.scaleOffsetY);
+      ctx.translate(width/2, height/2);
+    },
+    resize: function(width, height) {
+      var size = this.getSize(),
+          canvas = this.canvas,
+          styles = canvas.style;
+      this.size = false;
+      canvas.width = width;
+      canvas.height = height;
+      styles.width = width + "px";
+      styles.height = height + "px";
+      //small ExCanvas fix
+      if(!supportsCanvas) {
+        this.translateToCenter(size);
+      } else {
+        this.translateToCenter();
+      }
+      this.translateOffsetX =
+        this.translateOffsetY = 0;
+      this.scaleOffsetX = 
+        this.scaleOffsetY = 1;
+      this.clear();
+      this.viz.resize(width, height, this);
+    },
+    translate: function(x, y, disablePlot) {
+      var sx = this.scaleOffsetX,
+          sy = this.scaleOffsetY;
+      this.translateOffsetX += x*sx;
+      this.translateOffsetY += y*sy;
+      this.getCtx().translate(x, y);
+      !disablePlot && this.plot();
+    },
+    scale: function(x, y, disablePlot) {
+      this.scaleOffsetX *= x;
+      this.scaleOffsetY *= y;
+      this.getCtx().scale(x, y);
+      !disablePlot && this.plot();
+    },
+    clear: function(){
+      var size = this.getSize(),
+          ox = this.translateOffsetX,
+          oy = this.translateOffsetY,
+          sx = this.scaleOffsetX,
+          sy = this.scaleOffsetY;
+      this.getCtx().clearRect((-size.width / 2 - ox) * 1/sx, 
+                              (-size.height / 2 - oy) * 1/sy, 
+                              size.width * 1/sx, size.height * 1/sy);
+    },
+    plot: function() {
+      this.clear();
+      this.viz.plot(this);
+    }
+  });
+  //background canvases
+  //TODO(nico): document this!
+  Canvas.Background = {};
+  Canvas.Background.Circles = new Class({
+    initialize: function(viz, options) {
+      this.viz = viz;
+      this.config = $.merge({
+        idSuffix: '-bkcanvas',
+        levelDistance: 100,
+        numberOfCircles: 6,
+        CanvasStyles: {},
+        offset: 0
+      }, options);
+    },
+    resize: function(width, height, base) {
+      this.plot(base);
+    },
+    plot: function(base) {
+      var canvas = base.canvas,
+          ctx = base.getCtx(),
+          conf = this.config,
+          styles = conf.CanvasStyles;
+      //set canvas styles
+      for(var s in styles) ctx[s] = styles[s];
+      var n = conf.numberOfCircles,
+          rho = conf.levelDistance;
+      for(var i=1; i<=n; i++) {
+        ctx.beginPath();
+        ctx.arc(0, 0, rho * i, 0, 2 * Math.PI, false);
+        ctx.stroke();
+        ctx.closePath();
+      }
+      //TODO(nico): print labels too!
+    }
+  });
+})();
 
 
 /*
@@ -928,11 +3214,13 @@
       rho - The norm.
 */
 
-this.Polar = function(theta, rho) {
-  this.theta = theta;
-  this.rho = rho;
+var Polar = function(theta, rho) {
+  this.theta = theta || 0;
+  this.rho = rho || 0;
 };
 
+$jit.Polar = Polar;
+
 Polar.prototype = {
     /*
        Method: getc
@@ -1155,6 +3443,17 @@
     },
     
     /*
+      Method: isZero
+   
+      Returns *true* if the number is zero.
+   
+   */
+    isZero: function () {
+      var almostZero = 0.0001, abs = Math.abs;
+      return abs(this.theta) < almostZero && abs(this.rho) < almostZero;
+    },
+
+    /*
        Method: interpolate
     
         Calculates a polar interpolation between two points at a given delta moment.
@@ -1171,23 +3470,30 @@
     interpolate: function(elem, delta) {
         var pi = Math.PI, pi2 = pi * 2;
         var ch = function(t) {
-            return (t < 0)? (t % pi2) + pi2 : t % pi2;
+            var a =  (t < 0)? (t % pi2) + pi2 : t % pi2;
+            return a;
         };
         var tt = this.theta, et = elem.theta;
-        var sum;
-        if(Math.abs(tt - et) > pi) {
-            if(tt > et) {
-                sum =ch((et + ((tt - pi2) - et) * delta)) ;
-            } else {
-                sum =ch((et - pi2 + (tt - (et - pi2)) * delta));
-            }
-        } else {
-            sum =ch((et + (tt - et) * delta)) ;
+        var sum, diff = Math.abs(tt - et);
+        if(diff == pi) {
+          if(tt > et) {
+            sum = ch((et + ((tt - pi2) - et) * delta)) ;
+          } else {
+            sum = ch((et - pi2 + (tt - (et)) * delta));
+          }
+        } else if(diff >= pi) {
+          if(tt > et) {
+            sum = ch((et + ((tt - pi2) - et) * delta)) ;
+          } else {
+            sum = ch((et - pi2 + (tt - (et - pi2)) * delta));
+          }
+        } else {  
+          sum = ch((et + (tt - et) * delta)) ;
         }
         var r = (this.rho - elem.rho) * delta + elem.rho;
         return {
-            'theta': sum,
-            'rho': r
+          'theta': sum,
+          'rho': r
         };
     }
 };
@@ -1234,11 +3540,13 @@
  
 */
 
-this.Complex = function(x, y) {
-  this.x = x;
-  this.y = y;
+var Complex = function(x, y) {
+  this.x = x || 0;
+  this.y = y || 0;
 };
 
+$jit.Complex = Complex;
+
 Complex.prototype = {
     /*
        Method: getc
@@ -1282,9 +3590,9 @@
     
     */
     set: function(c) {
-        c = c.getc(true);
-        this.x = c.x; 
-    this.y = c.y;
+      c = c.getc(true);
+      this.x = c.x; 
+      this.y = c.y;
     },
 
     /*
@@ -1569,6 +3877,17 @@
         var sq = pos.squaredNorm();
         this.x = x * pos.x + y * pos.y; this.y = y * pos.x - x * pos.y;
         return this.$scale(1 / sq);
+    },
+
+    /*
+      Method: isZero
+   
+      Returns *true* if the number is zero.
+   
+   */
+    isZero: function () {
+      var almostZero = 0.0001, abs = Math.abs;
+      return abs(this.x) < almostZero && abs(this.y) < almostZero;
     }
 };
 
@@ -1581,79 +3900,83 @@
 /*
  * File: Graph.js
  *
- * Generic <Graph>, <Graph.Node> and <Graph.Adjacence> classes.
- *
- * Used by:
- *
- * <Hypertree>, <RGraph> and <ST>.
- *
 */
 
 /*
  Class: Graph
 
- A generic Graph class.
+ A Graph Class that provides useful manipulation functions. You can find more manipulation methods in the <Graph.Util> object.
 
- Description:
-
- When a json graph/tree structure is loaded by <Loader.loadJSON>, an internal <Graph> representation is created. 
-
- In most cases you'll be dealing with an already created <Graph> structure, so methods like <Graph.addNode> or <Graph.addAdjacence> won't 
- be of many use. However methods like <Graph.getNode> and <Graph.hasNode> are pretty useful.
-
- <Graph.Util> provides also iterators for <Graphs> and advanced and useful graph operations and methods.
-
- Used by:
-
- <Loader.loadJSON>, <Hypertree>, <RGraph> and <ST>.
-
- Access:
-
- An instance of this class can be accessed by using the _graph_ parameter of a <Hypertree>, <RGraph> or <ST> instance
-
+ An instance of this class can be accessed by using the *graph* parameter of any tree or graph visualization.
+ 
  Example:
 
  (start code js)
-   var st = new ST(canvas, config);
-   st.graph.getNode //or any other <Graph> method.
-   
-   var ht = new Hypertree(canvas, config);
-   ht.graph.getNode //or any other <Graph> method.
-   
-   var rg = new RGraph(canvas, config);
-   rg.graph.getNode //or any other <Graph> method.
+   //create new visualization
+   var viz = new $jit.Viz(options);
+   //load JSON data
+   viz.loadJSON(json);
+   //access model
+   viz.graph; //<Graph> instance
  (end code)
  
+ Implements:
+ 
+ The following <Graph.Util> methods are implemented in <Graph>
+ 
+  - <Graph.Util.getNode>
+  - <Graph.Util.eachNode>
+  - <Graph.Util.computeLevels>
+  - <Graph.Util.eachBFS>
+  - <Graph.Util.clean>
+  - <Graph.Util.getClosestNodeToPos>
+  - <Graph.Util.getClosestNodeToOrigin>
+ 
 */  
 
-this.Graph = new Class({
+$jit.Graph = new Class({
 
- initialize: function(opt) {
+  initialize: function(opt, Node, Edge, Label) {
     var innerOptions = {
-    'complex': false,
+    'klass': Complex,
     'Node': {}
-  };
-    this.opt = $merge(innerOptions, opt || {});
-    this.nodes= {};
+    };
+    this.Node = Node;
+    this.Edge = Edge;
+    this.Label = Label;
+    this.opt = $.merge(innerOptions, opt || {});
+    this.nodes = {};
+    this.edges = {};
+    
+    //add nodeList methods
+    var that = this;
+    this.nodeList = {};
+    for(var p in Accessors) {
+      that.nodeList[p] = (function(p) {
+        return function() {
+          var args = Array.prototype.slice.call(arguments);
+          that.eachNode(function(n) {
+            n[p].apply(n, args);
+          });
+        };
+      })(p);
+    }
+
  },
 
 /*
      Method: getNode
     
-     Returns a <Graph.Node> by _id_.
+     Returns a <Graph.Node> by *id*.
 
      Parameters:
 
-     id - A <Graph.Node> id.
-
-     Returns:
-
-     A <Graph.Node> having _id_ as id. Returns *false* otherwise.
+     id - (string) A <Graph.Node> id.
 
      Example:
 
      (start code js)
-       var node = graph.getNode('someid');
+       var node = graph.getNode('nodeId');
      (end code)
 */  
  getNode: function(id) {
@@ -1661,29 +3984,63 @@
     return false;
  },
 
-/*
-     Method: getAdjacence
+ /*
+     Method: get
     
-     Returns an array of <Graph.Adjacence> objects connecting nodes with ids _id_ and _id2_.
-
+     An alias for <Graph.Util.getNode>. Returns a node by *id*.
+    
      Parameters:
+    
+     id - (string) A <Graph.Node> id.
+    
+     Example:
+    
+     (start code js)
+       var node = graph.get('nodeId');
+     (end code)
+*/  
+  get: function(id) {
+    return this.getNode(id);
+  },
 
-     id - A <Graph.Node> id.
-     id2 - A <Graph.Node> id.
+ /*
+   Method: getByName
+  
+   Returns a <Graph.Node> by *name*.
+  
+   Parameters:
+  
+   name - (string) A <Graph.Node> name.
+  
+   Example:
+  
+   (start code js)
+     var node = graph.getByName('someName');
+   (end code)
+  */  
+  getByName: function(name) {
+    for(var id in this.nodes) {
+      var n = this.nodes[id];
+      if(n.name == name) return n;
+    }
+    return false;
+  },
 
-     Returns:
+/*
+   Method: getAdjacence
+  
+   Returns a <Graph.Adjacence> object connecting nodes with ids *id* and *id2*.
 
-     An Array of <Graph.Adjacence> objects. Returns *false* if there's not a <Graph.Adjacence> connecting those two nodes.
+   Parameters:
+
+   id - (string) A <Graph.Node> id.
+   id2 - (string) A <Graph.Node> id.
 */  
   getAdjacence: function (id, id2) {
-    var adjs = [];
-    if(this.hasNode(id)     && this.hasNode(id2) 
-    && this.nodes[id].adjacentTo({ 'id':id2 }) && this.nodes[id2].adjacentTo({ 'id':id })) {
-        adjs.push(this.nodes[id].getAdjacency(id2));
-        adjs.push(this.nodes[id2].getAdjacency(id));
-        return adjs;
+    if(id in this.edges) {
+      return this.edges[id][id2];
     }
-    return false;   
+    return false;
  },
 
     /*
@@ -1693,23 +4050,29 @@
      
      Parameters:
     
-        obj - An object containing as properties
+      obj - An object with the properties described below
 
-        - _id_ node's id
-        - _name_ node's name
-        - _data_ node's data hash
+      id - (string) A node id
+      name - (string) A node's name
+      data - (object) A node's data hash
 
     See also:
     <Graph.Node>
 
   */  
-  addNode: function(obj) {
-    if(!this.nodes[obj.id]) {
-        this.nodes[obj.id] = new Graph.Node($extend({
-      'id': obj.id,
-      'name': obj.name,
-      'data': obj.data
-    }, this.opt.Node), this.opt.complex);
+  addNode: function(obj) { 
+   if(!this.nodes[obj.id]) {  
+     var edges = this.edges[obj.id] = {};
+     this.nodes[obj.id] = new Graph.Node($.extend({
+        'id': obj.id,
+        'name': obj.name,
+        'data': $.merge(obj.data || {}, {}),
+        'adjacencies': edges 
+      }, this.opt.Node), 
+      this.opt.klass, 
+      this.Node, 
+      this.Edge,
+      this.Label);
     }
     return this.nodes[obj.id];
   },
@@ -1717,134 +4080,522 @@
     /*
      Method: addAdjacence
     
-     Connects nodes specified by _obj_ and _obj2_. If not found, nodes are created.
+     Connects nodes specified by *obj* and *obj2*. If not found, nodes are created.
      
      Parameters:
     
-        obj - a <Graph.Node> object.
-        obj2 - Another <Graph.Node> object.
-        data - A DataSet object. Used to store some extra information in the <Graph.Adjacence> object created.
+      obj - (object) A <Graph.Node> object.
+      obj2 - (object) Another <Graph.Node> object.
+      data - (object) A data object. Used to store some extra information in the <Graph.Adjacence> object created.
 
     See also:
 
     <Graph.Node>, <Graph.Adjacence>
     */  
   addAdjacence: function (obj, obj2, data) {
-    var adjs = [];
     if(!this.hasNode(obj.id)) { this.addNode(obj); }
     if(!this.hasNode(obj2.id)) { this.addNode(obj2); }
     obj = this.nodes[obj.id]; obj2 = this.nodes[obj2.id];
-    
-    for(var i in this.nodes) {
-        if(this.nodes[i].id == obj.id) {
-            if(!this.nodes[i].adjacentTo(obj2)) {
-                adjs.push(this.nodes[i].addAdjacency(obj2, data));
-            }
-        }
-        
-        if(this.nodes[i].id == obj2.id) {   
-            if(!this.nodes[i].adjacentTo(obj)) {
-                adjs.push(this.nodes[i].addAdjacency(obj, data));
-            }
-        }
+    if(!obj.adjacentTo(obj2)) {
+      var adjsObj = this.edges[obj.id] = this.edges[obj.id] || {};
+      var adjsObj2 = this.edges[obj2.id] = this.edges[obj2.id] || {};
+      adjsObj[obj2.id] = adjsObj2[obj.id] = new Graph.Adjacence(obj, obj2, data, this.Edge, this.Label);
+      return adjsObj[obj2.id];
     }
-    return adjs;
+    return this.edges[obj.id][obj2.id];
  },
 
     /*
      Method: removeNode
     
-     Removes a <Graph.Node> matching the specified _id_.
+     Removes a <Graph.Node> matching the specified *id*.
 
      Parameters:
 
-     id - A node's id.
+     id - (string) A node's id.
 
     */  
   removeNode: function(id) {
     if(this.hasNode(id)) {
-        var node = this.nodes[id];
-        for(var i=0 in node.adjacencies) {
-            var adj = node.adjacencies[i];
-            this.removeAdjacence(id, adj.nodeTo.id);
-        }
-        delete this.nodes[id];
+      delete this.nodes[id];
+      var adjs = this.edges[id];
+      for(var to in adjs) {
+        delete this.edges[to][id];
+      }
+      delete this.edges[id];
     }
   },
   
 /*
      Method: removeAdjacence
     
-     Removes a <Graph.Adjacence> matching _id1_ and _id2_.
+     Removes a <Graph.Adjacence> matching *id1* and *id2*.
 
      Parameters:
 
-     id1 - A <Graph.Node> id.
-     id2 - A <Graph.Node> id.
+     id1 - (string) A <Graph.Node> id.
+     id2 - (string) A <Graph.Node> id.
 */  
   removeAdjacence: function(id1, id2) {
-    if(this.hasNode(id1)) this.nodes[id1].removeAdjacency(id2);
-    if(this.hasNode(id2)) this.nodes[id2].removeAdjacency(id1);
+    delete this.edges[id1][id2];
+    delete this.edges[id2][id1];
   },
 
-    /*
+   /*
      Method: hasNode
     
-     Returns a Boolean instance indicating if the node belongs to the <Graph> or not.
+     Returns a boolean indicating if the node belongs to the <Graph> or not.
      
      Parameters:
     
-        id - Node id.
-
-     Returns:
-      
-     A Boolean instance indicating if the node belongs to the graph or not.
-    */  
+        id - (string) Node id.
+   */  
   hasNode: function(id) {
     return id in this.nodes;
-  }
+  },
+  
+  /*
+    Method: empty
+
+    Empties the Graph
+
+  */
+  empty: function() { this.nodes = {}; this.edges = {};}
+
 });
 
+var Graph = $jit.Graph;
+
+/*
+ Object: Accessors
+ 
+ Defines a set of methods for data, canvas and label styles manipulation implemented by <Graph.Node> and <Graph.Adjacence> instances.
+ 
+ */
+var Accessors;
+
+(function () {
+  var getDataInternal = function(prefix, prop, type, force, prefixConfig) {
+    var data;
+    type = type || 'current';
+    prefix = "$" + (prefix ? prefix + "-" : "");
+
+    if(type == 'current') {
+      data = this.data;
+    } else if(type == 'start') {
+      data = this.startData;
+    } else if(type == 'end') {
+      data = this.endData;
+    }
+
+    var dollar = prefix + prop;
+
+    if(force) {
+      return data[dollar];
+    }
+
+    if(!this.Config.overridable)
+      return prefixConfig[prop] || 0;
+
+    return (dollar in data) ?
+      data[dollar] : ((dollar in this.data) ? this.data[dollar] : (prefixConfig[prop] || 0));
+  }
+
+  var setDataInternal = function(prefix, prop, value, type) {
+    type = type || 'current';
+    prefix = '$' + (prefix ? prefix + '-' : '');
+
+    var data;
+
+    if(type == 'current') {
+      data = this.data;
+    } else if(type == 'start') {
+      data = this.startData;
+    } else if(type == 'end') {
+      data = this.endData;
+    }
+
+    data[prefix + prop] = value;
+  }
+
+  var removeDataInternal = function(prefix, properties) {
+    prefix = '$' + (prefix ? prefix + '-' : '');
+    var that = this;
+    $.each(properties, function(prop) {
+      var pref = prefix + prop;
+      delete that.data[pref];
+      delete that.endData[pref];
+      delete that.startData[pref];
+    });
+  }
+
+  Accessors = {
+    /*
+    Method: getData
+
+    Returns the specified data value property.
+    This is useful for querying special/reserved <Graph.Node> data properties
+    (i.e dollar prefixed properties).
+
+    Parameters:
+
+      prop  - (string) The name of the property. The dollar sign is not needed. For
+              example *getData(width)* will return *data.$width*.
+      type  - (string) The type of the data property queried. Default's "current". You can access *start* and *end* 
+              data properties also. These properties are used when making animations.
+      force - (boolean) Whether to obtain the true value of the property (equivalent to
+              *data.$prop*) or to check for *node.overridable = true* first.
+
+    Returns:
+
+      The value of the dollar prefixed property or the global Node/Edge property
+      value if *overridable=false*
+
+    Example:
+    (start code js)
+     node.getData('width'); //will return node.data.$width if Node.overridable=true;
+    (end code)
+    */
+    getData: function(prop, type, force) {
+      return getDataInternal.call(this, "", prop, type, force, this.Config);
+    },
+
+
+    /*
+    Method: setData
+
+    Sets the current data property with some specific value.
+    This method is only useful for reserved (dollar prefixed) properties.
+
+    Parameters:
+
+      prop  - (string) The name of the property. The dollar sign is not necessary. For
+              example *setData(width)* will set *data.$width*.
+      value - (mixed) The value to store.
+      type  - (string) The type of the data property to store. Default's "current" but
+              can also be "start" or "end".
+
+    Example:
+    
+    (start code js)
+     node.setData('width', 30);
+    (end code)
+    
+    If we were to make an animation of a node/edge width then we could do
+    
+    (start code js)
+      var node = viz.getNode('nodeId');
+      //set start and end values
+      node.setData('width', 10, 'start');
+      node.setData('width', 30, 'end');
+      //will animate nodes width property
+      viz.fx.animate({
+        modes: ['node-property:width'],
+        duration: 1000
+      });
+    (end code)
+    */
+    setData: function(prop, value, type) {
+      setDataInternal.call(this, "", prop, value, type);
+    },
+
+    /*
+    Method: setDataset
+
+    Convenience method to set multiple data values at once.
+    
+    Parameters:
+    
+    types - (array|string) A set of 'current', 'end' or 'start' values.
+    obj - (object) A hash containing the names and values of the properties to be altered.
+
+    Example:
+    (start code js)
+      node.setDataset(['current', 'end'], {
+        'width': [100, 5],
+        'color': ['#fff', '#ccc']
+      });
+      //...or also
+      node.setDataset('end', {
+        'width': 5,
+        'color': '#ccc'
+      });
+    (end code)
+    
+    See also: 
+    
+    <Accessors.setData>
+    
+    */
+    setDataset: function(types, obj) {
+      types = $.splat(types);
+      for(var attr in obj) {
+        for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {
+          this.setData(attr, val[i], types[i]);
+        }
+      }
+    },
+    
+    /*
+    Method: removeData
+
+    Remove data properties.
+
+    Parameters:
+
+    One or more property names as arguments. The dollar sign is not needed.
+
+    Example:
+    (start code js)
+    node.removeData('width'); //now the default width value is returned
+    (end code)
+    */
+    removeData: function() {
+      removeDataInternal.call(this, "", Array.prototype.slice.call(arguments));
+    },
+
+    /*
+    Method: getCanvasStyle
+
+    Returns the specified canvas style data value property. This is useful for
+    querying special/reserved <Graph.Node> canvas style data properties (i.e.
+    dollar prefixed properties that match with $canvas-<name of canvas style>).
+
+    Parameters:
+
+      prop  - (string) The name of the property. The dollar sign is not needed. For
+              example *getCanvasStyle(shadowBlur)* will return *data[$canvas-shadowBlur]*.
+      type  - (string) The type of the data property queried. Default's *current*. You can access *start* and *end* 
+              data properties also.
+              
+    Example:
+    (start code js)
+      node.getCanvasStyle('shadowBlur');
+    (end code)
+    
+    See also:
+    
+    <Accessors.getData>
+    */
+    getCanvasStyle: function(prop, type, force) {
+      return getDataInternal.call(
+          this, 'canvas', prop, type, force, this.Config.CanvasStyles);
+    },
+
+    /*
+    Method: setCanvasStyle
+
+    Sets the canvas style data property with some specific value.
+    This method is only useful for reserved (dollar prefixed) properties.
+    
+    Parameters:
+    
+    prop - (string) Name of the property. Can be any canvas property like 'shadowBlur', 'shadowColor', 'strokeStyle', etc.
+    value - (mixed) The value to set to the property.
+    type - (string) Default's *current*. Whether to set *start*, *current* or *end* type properties.
+    
+    Example:
+    
+    (start code js)
+     node.setCanvasStyle('shadowBlur', 30);
+    (end code)
+    
+    If we were to make an animation of a node/edge shadowBlur canvas style then we could do
+    
+    (start code js)
+      var node = viz.getNode('nodeId');
+      //set start and end values
+      node.setCanvasStyle('shadowBlur', 10, 'start');
+      node.setCanvasStyle('shadowBlur', 30, 'end');
+      //will animate nodes canvas style property for nodes
+      viz.fx.animate({
+        modes: ['node-style:shadowBlur'],
+        duration: 1000
+      });
+    (end code)
+    
+    See also:
+    
+    <Accessors.setData>.
+    */
+    setCanvasStyle: function(prop, value, type) {
+      setDataInternal.call(this, 'canvas', prop, value, type);
+    },
+
+    /*
+    Method: setCanvasStyles
+
+    Convenience method to set multiple styles at once.
+
+    Parameters:
+    
+    types - (array|string) A set of 'current', 'end' or 'start' values.
+    obj - (object) A hash containing the names and values of the properties to be altered.
+
+    See also:
+    
+    <Accessors.setDataset>.
+    */
+    setCanvasStyles: function(types, obj) {
+      types = $.splat(types);
+      for(var attr in obj) {
+        for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {
+          this.setCanvasStyle(attr, val[i], types[i]);
+        }
+      }
+    },
+
+    /*
+    Method: removeCanvasStyle
+
+    Remove canvas style properties from data.
+
+    Parameters:
+    
+    A variable number of canvas style strings.
+
+    See also:
+    
+    <Accessors.removeData>.
+    */
+    removeCanvasStyle: function() {
+      removeDataInternal.call(this, 'canvas', Array.prototype.slice.call(arguments));
+    },
+
+    /*
+    Method: getLabelData
+
+    Returns the specified label data value property. This is useful for
+    querying special/reserved <Graph.Node> label options (i.e.
+    dollar prefixed properties that match with $label-<name of label style>).
+
+    Parameters:
+
+      prop  - (string) The name of the property. The dollar sign prefix is not needed. For
+              example *getLabelData(size)* will return *data[$label-size]*.
+      type  - (string) The type of the data property queried. Default's *current*. You can access *start* and *end* 
+              data properties also.
+              
+    See also:
+    
+    <Accessors.getData>.
+    */
+    getLabelData: function(prop, type, force) {
+      return getDataInternal.call(
+          this, 'label', prop, type, force, this.Label);
+    },
+
+    /*
+    Method: setLabelData
+
+    Sets the current label data with some specific value.
+    This method is only useful for reserved (dollar prefixed) properties.
+
+    Parameters:
+    
+    prop - (string) Name of the property. Can be any canvas property like 'shadowBlur', 'shadowColor', 'strokeStyle', etc.
+    value - (mixed) The value to set to the property.
+    type - (string) Default's *current*. Whether to set *start*, *current* or *end* type properties.
+    
+    Example:
+    
+    (start code js)
+     node.setLabelData('size', 30);
+    (end code)
+    
+    If we were to make an animation of a node label size then we could do
+    
+    (start code js)
+      var node = viz.getNode('nodeId');
+      //set start and end values
+      node.setLabelData('size', 10, 'start');
+      node.setLabelData('size', 30, 'end');
+      //will animate nodes label size
+      viz.fx.animate({
+        modes: ['label-property:size'],
+        duration: 1000
+      });
+    (end code)
+    
+    See also:
+    
+    <Accessors.setData>.
+    */
+    setLabelData: function(prop, value, type) {
+      setDataInternal.call(this, 'label', prop, value, type);
+    },
+
+    /*
+    Method: setLabelDataset
+
+    Convenience function to set multiple label data at once.
+
+    Parameters:
+    
+    types - (array|string) A set of 'current', 'end' or 'start' values.
+    obj - (object) A hash containing the names and values of the properties to be altered.
+
+    See also:
+    
+    <Accessors.setDataset>.
+    */
+    setLabelDataset: function(types, obj) {
+      types = $.splat(types);
+      for(var attr in obj) {
+        for(var i=0, val = $.splat(obj[attr]), l=types.length; i<l; i++) {
+          this.setLabelData(attr, val[i], types[i]);
+        }
+      }
+    },
+
+    /*
+    Method: removeLabelData
+
+    Remove label properties from data.
+    
+    Parameters:
+    
+    A variable number of label property strings.
+
+    See also:
+    
+    <Accessors.removeData>.
+    */
+    removeLabelData: function() {
+      removeDataInternal.call(this, 'label', Array.prototype.slice.call(arguments));
+    }
+  };
+})();
+
 /*
      Class: Graph.Node
 
      A <Graph> node.
-
-     Parameters:
-
-     obj - An object containing an 'id', 'name' and 'data' properties as described in <Graph.addNode>.
-     complex - Whether node position properties should contain <Complex> or <Polar> instances.
-
-     See also:
-
-     <Graph>
-
-     Description:
-
-     An instance of <Graph.Node> is usually passed as parameter for most configuration/controller methods in the 
-     <Hypertree>, <RGraph> and <ST> classes.
-
-     A <Graph.Node> object has as properties
-
-      id - Node id.
-      name - Node name.
-      data - Node data property containing a hash (i.e {}) with custom options. For more information see <Loader.loadJSON>.
-      selected - Whether the node is selected or not. Used by <ST> for selecting nodes that are between the root node and the selected node.
-      angleSpan - For radial layouts such as the ones performed by the <Hypertree> and the <RGraph>. Contains _begin_ and _end_ properties containing angle values describing the angle span for this subtree.
-      alpha - Current opacity value.
-      startAlpha - Opacity begin value. Used for interpolation.
-      endAlpha - Opacity end value. Used for interpolation.
-      pos - Current position. Can be a <Complex> or <Polar> instance.
-      startPos - Starting position. Used for interpolation.
-      endPos - Ending position. Used for interpolation.
+     
+     Implements:
+     
+     <Accessors> methods.
+     
+     The following <Graph.Util> methods are implemented by <Graph.Node>
+     
+    - <Graph.Util.eachAdjacency>
+    - <Graph.Util.eachLevel>
+    - <Graph.Util.eachSubgraph>
+    - <Graph.Util.eachSubnode>
+    - <Graph.Util.anySubnode>
+    - <Graph.Util.getSubnodes>
+    - <Graph.Util.getParents>
+    - <Graph.Util.isDescendantOf>     
 */
 Graph.Node = new Class({
     
-    initialize: function(opt, complex) {
+  initialize: function(opt, klass, Node, Edge, Label) {
     var innerOptions = {
       'id': '',
       'name': '',
       'data': {},
+      'startData': {},
+      'endData': {},
       'adjacencies': {},
 
       'selected': false,
@@ -1856,16 +4607,15 @@
         'end' : 0
       },
 
-      'alpha': 1,
-      'startAlpha': 1,
-      'endAlpha': 1,
-      
-      'pos': (complex && $C(0, 0)) || $P(0, 0),
-      'startPos': (complex && $C(0, 0)) || $P(0, 0),
-      'endPos': (complex && $C(0, 0)) || $P(0, 0)
+      'pos': new klass,
+      'startPos': new klass,
+      'endPos': new klass
     };
     
-    $extend(this, $extend(innerOptions, opt));
+    $.extend(this, $.extend(innerOptions, opt));
+    this.Config = this.Node = Node;
+    this.Edge = Edge;
+    this.Label = Label;
   },
 
     /*
@@ -1875,15 +4625,11 @@
 
        Parameters:
     
-          id - A node id.
+          id - (string) A node id.
     
-       Returns:
-    
-         A Boolean instance indicating whether this node is adjacent to the specified by id or not.
-
        Example:
        (start code js)
-        node.adjacentTo('mynodeid');
+        node.adjacentTo('nodeId') == true;
        (end code)
     */
     adjacentTo: function(node) {
@@ -1893,90 +4639,117 @@
     /*
        Method: getAdjacency
     
-       Returns a <Graph.Adjacence> object connecting the current <Graph.Node> and the node having _id_ as id.
+       Returns a <Graph.Adjacence> object connecting the current <Graph.Node> and the node having *id* as id.
 
        Parameters:
     
-          id - A node id.
-
-       Returns:
-
-          A <Graph.Adjacence> object or undefined.
+          id - (string) A node id.
     */  
     getAdjacency: function(id) {
         return this.adjacencies[id];
     },
-    /*
-       Method: addAdjacency
-    
-       Connects the current node and the given node.
 
-       Parameters:
-    
-          node - A <Graph.Node>.
-          data - Some custom hash information.
-    */  
-    addAdjacency: function(node, data) {
-        var adj = new Graph.Adjacence(this, node, data);
-        return this.adjacencies[node.id] = adj;
-    },
-    
     /*
-       Method: removeAdjacency
-    
-       Removes a <Graph.Adjacence> by _id_.
-
-       Parameters:
-    
-          id - A node id.
-    */  
-    removeAdjacency: function(id) {
-        delete this.adjacencies[id];
-    }
+      Method: getPos
+   
+      Returns the position of the node.
+  
+      Parameters:
+   
+         type - (string) Default's *current*. Possible values are "start", "end" or "current".
+   
+      Returns:
+   
+        A <Complex> or <Polar> instance.
+  
+      Example:
+      (start code js)
+       var pos = node.getPos('end');
+      (end code)
+   */
+   getPos: function(type) {
+       type = type || "current";
+       if(type == "current") {
+         return this.pos;
+       } else if(type == "end") {
+         return this.endPos;
+       } else if(type == "start") {
+         return this.startPos;
+       }
+   },
+   /*
+     Method: setPos
+  
+     Sets the node's position.
+  
+     Parameters:
+  
+        value - (object) A <Complex> or <Polar> instance.
+        type - (string) Default's *current*. Possible values are "start", "end" or "current".
+  
+     Example:
+     (start code js)
+      node.setPos(new $jit.Complex(0, 0), 'end');
+     (end code)
+  */
+  setPos: function(value, type) {
+      type = type || "current";
+      var pos;
+      if(type == "current") {
+        pos = this.pos;
+      } else if(type == "end") {
+        pos = this.endPos;
+      } else if(type == "start") {
+        pos = this.startPos;
+      }
+      pos.set(value);
+  }
 });
 
+Graph.Node.implement(Accessors);
+
 /*
      Class: Graph.Adjacence
 
-     A <Graph> adjacence (or edge). Connects two <Graph.Nodes>.
-
-     Parameters:
-
-     nodeFrom - A <Graph.Node>.
-     nodeTo - A <Graph.Node>.
-     data - Some custom hash data.
+     A <Graph> adjacence (or edge) connecting two <Graph.Nodes>.
+     
+     Implements:
+     
+     <Accessors> methods.
 
      See also:
 
-     <Graph>
+     <Graph>, <Graph.Node>
 
-     Description:
-
-     An instance of <Graph.Adjacence> is usually passed as parameter for some configuration/controller methods in the 
-     <Hypertree>, <RGraph> and <ST> classes.
-
-     A <Graph.Adjacence> object has as properties
-
+     Properties:
+     
       nodeFrom - A <Graph.Node> connected by this edge.
       nodeTo - Another  <Graph.Node> connected by this edge.
-      data - Node data property containing a hash (i.e {}) with custom options. For more information see <Loader.loadJSON>.
-      alpha - Current opacity value.
-      startAlpha - Opacity begin value. Used for interpolation.
-      endAlpha - Opacity end value. Used for interpolation.
+      data - Node data property containing a hash (i.e {}) with custom options.
 */
-Graph.Adjacence = function(nodeFrom, nodeTo, data) {
+Graph.Adjacence = new Class({
+  
+  initialize: function(nodeFrom, nodeTo, data, Edge, Label) {
     this.nodeFrom = nodeFrom;
     this.nodeTo = nodeTo;
     this.data = data || {};
-    this.alpha = 1;
-    this.startAlpha = 1;
-    this.endAlpha = 1;
-};
+    this.startData = {};
+    this.endData = {};
+    this.Config = this.Edge = Edge;
+    this.Label = Label;
+  }
+});
+
+Graph.Adjacence.implement(Accessors);
 
 /*
    Object: Graph.Util
 
    <Graph> traversal and processing utility object.
+   
+   Note:
+   
+   For your convenience some of these methods have also been appended to <Graph> and <Graph.Node> classes.
 */
 Graph.Util = {
     /*
@@ -1985,7 +4758,7 @@
        For internal use only. Provides a filtering function based on flags.
     */
     filter: function(param) {
-        if(!param || !($type(param) == 'string')) return function() { return true; };
+        if(!param || !($.type(param) == 'string')) return function() { return true; };
         var props = param.split(" ");
         return function(elem) {
             for(var i=0; i<props.length; i++) { 
@@ -1999,42 +4772,52 @@
     /*
        Method: getNode
     
-       Returns a <Graph.Node> by _id_.
+       Returns a <Graph.Node> by *id*.
+       
+       Also implemented by:
+       
+       <Graph>
 
        Parameters:
 
-       graph - A <Graph> instance.
-       id - A <Graph.Node> id.
-
-       Returns:
-
-       A <Graph> node.
+       graph - (object) A <Graph> instance.
+       id - (string) A <Graph.Node> id.
 
        Example:
 
        (start code js)
-         Graph.Util.getNode(graph, 'nodeid');
+         $jit.Graph.Util.getNode(graph, 'nodeid');
+         //or...
+         graph.getNode('nodeid');
        (end code)
     */
     getNode: function(graph, id) {
-        return graph.getNode(id);
+        return graph.nodes[id];
     },
     
     /*
        Method: eachNode
     
-       Iterates over <Graph> nodes performing an _action_.
+       Iterates over <Graph> nodes performing an *action*.
+       
+       Also implemented by:
+       
+       <Graph>.
 
        Parameters:
 
-       graph - A <Graph> instance.
-       action - A callback function having a <Graph.Node> as first formal parameter.
+       graph - (object) A <Graph> instance.
+       action - (function) A callback function having a <Graph.Node> as first formal parameter.
 
        Example:
        (start code js)
-         Graph.Util.each(graph, function(node) {
+         $jit.Graph.Util.eachNode(graph, function(node) {
           alert(node.name);
          });
+         //or...
+         graph.eachNode(function(node) {
+           alert(node.name);
+         });
        (end code)
     */
     eachNode: function(graph, action, flags) {
@@ -2045,27 +4828,70 @@
     },
     
     /*
+      Method: each
+   
+      Iterates over <Graph> nodes performing an *action*. It's an alias for <Graph.Util.eachNode>.
+      
+      Also implemented by:
+      
+      <Graph>.
+  
+      Parameters:
+  
+      graph - (object) A <Graph> instance.
+      action - (function) A callback function having a <Graph.Node> as first formal parameter.
+  
+      Example:
+      (start code js)
+        $jit.Graph.Util.each(graph, function(node) {
+         alert(node.name);
+        });
+        //or...
+        graph.each(function(node) {
+          alert(node.name);
+        });
+      (end code)
+   */
+   each: function(graph, action, flags) {
+      this.eachNode(graph, action, flags); 
+   },
+
+ /*
        Method: eachAdjacency
     
-       Iterates over <Graph.Node> adjacencies applying the _action_ function.
+       Iterates over <Graph.Node> adjacencies applying the *action* function.
+       
+       Also implemented by:
+       
+       <Graph.Node>.
 
        Parameters:
 
-       node - A <Graph.Node>.
-       action - A callback function having <Graph.Adjacence> as first formal parameter.
+       node - (object) A <Graph.Node>.
+       action - (function) A callback function having <Graph.Adjacence> as first formal parameter.
 
        Example:
        (start code js)
-         Graph.Util.eachAdjacency(node, function(adj) {
+         $jit.Graph.Util.eachAdjacency(node, function(adj) {
           alert(adj.nodeTo.name);
          });
+         //or...
+         node.eachAdjacency(function(adj) {
+           alert(adj.nodeTo.name);
+         });
        (end code)
     */
     eachAdjacency: function(node, action, flags) {
         var adj = node.adjacencies, filter = this.filter(flags);
         for(var id in adj) {
-          if(filter(adj[id])) {
-            action(adj[id], id);
+          var a = adj[id];
+          if(filter(a)) {
+            if(a.nodeFrom != node) {
+              var tmp = a.nodeFrom;
+              a.nodeFrom = a.nodeTo;
+              a.nodeTo = tmp;
+            }
+            action(a, id);
           }
         }
     },
@@ -2074,15 +4900,21 @@
        Method: computeLevels
     
        Performs a BFS traversal setting the correct depth for each node.
-
+        
+       Also implemented by:
+       
+       <Graph>.
+       
+       Note:
+       
        The depth of each node can then be accessed by 
        >node._depth
 
        Parameters:
 
-       graph - A <Graph>.
-       id - A starting node id for the BFS traversal.
-       startDepth - _optional_ A minimum depth value. Default's 0.
+       graph - (object) A <Graph>.
+       id - (string) A starting node id for the BFS traversal.
+       startDepth - (optional|number) A minimum depth value. Default's 0.
 
     */
     computeLevels: function(graph, id, startDepth, flags) {
@@ -2092,7 +4924,7 @@
             elem._flag = false;
             elem._depth = -1;
         }, flags);
-    var root = graph.getNode(id);
+        var root = graph.getNode(id);
         root._depth = startDepth;
         var queue = [root];
         while(queue.length != 0) {
@@ -2111,19 +4943,27 @@
     /*
        Method: eachBFS
     
-       Performs a BFS traversal applying _action_ to each <Graph.Node>.
+       Performs a BFS traversal applying *action* to each <Graph.Node>.
+       
+       Also implemented by:
+       
+       <Graph>.
 
        Parameters:
 
-       graph - A <Graph>.
-       id - A starting node id for the BFS traversal.
-       action - A callback function having a <Graph.Node> as first formal parameter.
+       graph - (object) A <Graph>.
+       id - (string) A starting node id for the BFS traversal.
+       action - (function) A callback function having a <Graph.Node> as first formal parameter.
 
        Example:
        (start code js)
-         Graph.Util.eachBFS(graph, 'mynodeid', function(node) {
+         $jit.Graph.Util.eachBFS(graph, 'mynodeid', function(node) {
           alert(node.name);
          });
+         //or...
+         graph.eachBFS('mynodeid', function(node) {
+           alert(node.name);
+         });
        (end code)
     */
     eachBFS: function(graph, id, action, flags) {
@@ -2147,19 +4987,23 @@
     /*
        Method: eachLevel
     
-       Iterates over a node's subgraph applying _action_ to the nodes of relative depth between _levelBegin_ and _levelEnd_.
+       Iterates over a node's subgraph applying *action* to the nodes of relative depth between *levelBegin* and *levelEnd*.
+       
+       Also implemented by:
+       
+       <Graph.Node>.
 
        Parameters:
        
-       node - A <Graph.Node>.
-       levelBegin - A relative level value.
-       levelEnd - A relative level value.
-       action - A callback function having a <Graph.Node> as first formal parameter.
+       node - (object) A <Graph.Node>.
+       levelBegin - (number) A relative level value.
+       levelEnd - (number) A relative level value.
+       action - (function) A callback function having a <Graph.Node> as first formal parameter.
 
     */
     eachLevel: function(node, levelBegin, levelEnd, action, flags) {
         var d = node._depth, filter = this.filter(flags), that = this;
-    levelEnd = levelEnd === false? Number.MAX_VALUE -d : levelEnd;
+        levelEnd = levelEnd === false? Number.MAX_VALUE -d : levelEnd;
         (function loopLevel(node, levelBegin, levelEnd) {
             var d = node._depth;
             if(d >= levelBegin && d <= levelEnd && filter(node)) action(node, d);
@@ -2176,20 +5020,28 @@
        Method: eachSubgraph
     
        Iterates over a node's children recursively.
+       
+       Also implemented by:
+       
+       <Graph.Node>.
 
        Parameters:
-       node - A <Graph.Node>.
-       action - A callback function having a <Graph.Node> as first formal parameter.
+       node - (object) A <Graph.Node>.
+       action - (function) A callback function having a <Graph.Node> as first formal parameter.
 
        Example:
        (start code js)
-         Graph.Util.eachSubgraph(node, function(node) {
-          alert(node.name);
+         $jit.Graph.Util.eachSubgraph(node, function(node) {
+           alert(node.name);
+         });
+         //or...
+         node.eachSubgraph(function(node) {
+           alert(node.name);
          });
        (end code)
     */
     eachSubgraph: function(node, action, flags) {
-    this.eachLevel(node, 0, false, action, flags);
+      this.eachLevel(node, 0, false, action, flags);
     },
 
     /*
@@ -2197,15 +5049,23 @@
     
        Iterates over a node's children (without deeper recursion).
        
+       Also implemented by:
+       
+       <Graph.Node>.
+       
        Parameters:
-       node - A <Graph.Node>.
-       action - A callback function having a <Graph.Node> as first formal parameter.
+       node - (object) A <Graph.Node>.
+       action - (function) A callback function having a <Graph.Node> as first formal parameter.
 
        Example:
        (start code js)
-         Graph.Util.eachSubnode(node, function(node) {
+         $jit.Graph.Util.eachSubnode(node, function(node) {
           alert(node.name);
          });
+         //or...
+         node.eachSubnode(function(node) {
+           alert(node.name);
+         });
        (end code)
     */
     eachSubnode: function(node, action, flags) {
@@ -2216,37 +5076,45 @@
        Method: anySubnode
     
        Returns *true* if any subnode matches the given condition.
+       
+       Also implemented by:
+       
+       <Graph.Node>.
 
        Parameters:
-       node - A <Graph.Node>.
-       cond - A callback function returning a Boolean instance. This function has as first formal parameter a <Graph.Node>.
-
-       Returns:
-       A boolean value.
+       node - (object) A <Graph.Node>.
+       cond - (function) A callback function returning a Boolean instance. This function has as first formal parameter a <Graph.Node>.
 
        Example:
        (start code js)
-         Graph.Util.anySubnode(node, function(node) { return node.name == "mynodename"; });
+         $jit.Graph.Util.anySubnode(node, function(node) { return node.name == "mynodename"; });
+         //or...
+         node.anySubnode(function(node) { return node.name == 'mynodename'; });
        (end code)
     */
     anySubnode: function(node, cond, flags) {
-        var flag = false;
-    cond = cond || $lambda(true);
-    var c = $type(cond) == 'string'? function(n) { return n[cond]; } : cond;
-    this.eachSubnode(node, function(elem) {
-      if(c(elem)) flag = true;
-    }, flags);
-    return flag;
+      var flag = false;
+      cond = cond || $.lambda(true);
+      var c = $.type(cond) == 'string'? function(n) { return n[cond]; } : cond;
+      this.eachSubnode(node, function(elem) {
+        if(c(elem)) flag = true;
+      }, flags);
+      return flag;
     },
   
     /*
        Method: getSubnodes
     
-       Collects all subnodes for a specified node. The _level_ parameter filters nodes having relative depth of _level_ from the root node.
+       Collects all subnodes for a specified node. 
+       The *level* parameter filters nodes having relative depth of *level* from the root node. 
+       
+       Also implemented by:
+       
+       <Graph.Node>.
 
        Parameters:
-       node - A <Graph.Node>.
-       level - _optional_ A starting relative depth for collecting nodes. Default's 0.
+       node - (object) A <Graph.Node>.
+       level - (optional|number) Default's *0*. A starting relative depth for collecting nodes.
 
        Returns:
        An array of nodes.
@@ -2256,7 +5124,7 @@
         var ans = [], that = this;
         level = level || 0;
         var levelStart, levelEnd;
-        if($type(level) == 'array') {
+        if($.type(level) == 'array') {
             levelStart = level[0];
             levelEnd = level[1];
         } else {
@@ -2264,8 +5132,8 @@
             levelEnd = Number.MAX_VALUE - node._depth;
         }
         this.eachLevel(node, levelStart, levelEnd, function(n) {
-      ans.push(n);
-    }, flags);
+            ans.push(n);
+        }, flags);
         return ans;
     },
   
@@ -2273,17 +5141,24 @@
     /*
        Method: getParents
     
-       Returns an Array of <Graph.Nodes> wich are parents of the given node. 
+       Returns an Array of <Graph.Nodes> which are parents of the given node.
+       
+       Also implemented by:
+       
+       <Graph.Node>.
 
        Parameters:
-       node - A <Graph.Node>.
+       node - (object) A <Graph.Node>.
 
        Returns:
        An Array of <Graph.Nodes>.
 
        Example:
        (start code js)
-         var pars = Graph.Util.getParents(node);
+         var pars = $jit.Graph.Util.getParents(node);
+         //or...
+         var pars = node.getParents();
+         
          if(pars.length > 0) {
            //do stuff with parents
          }
@@ -2301,18 +5176,22 @@
     /*
     Method: isDescendantOf
  
-    Returns a Boolean instance indicating if some node is descendant of the node with the given id. 
+    Returns a boolean indicating if some node is descendant of the node with the given id. 
 
+    Also implemented by:
+    
+    <Graph.Node>.
+    
+    
     Parameters:
-    node - A <Graph.Node>.
-    id - A <Graph.Node> id.
-
-    Returns:
-    Ture if _node_ is descendant of the node with the given _id_. False otherwise.
+    node - (object) A <Graph.Node>.
+    id - (string) A <Graph.Node> id.
 
     Example:
     (start code js)
-      var pars = Graph.Util.isDescendantOf(node, "nodeid");
+      $jit.Graph.Util.isDescendantOf(node, "nodeid"); //true|false
+      //or...
+      node.isDescendantOf('nodeid');//true|false
     (end code)
  */
  isDescendantOf: function(node, id) {
@@ -2325,54 +5204,109 @@
  },
 
  /*
-       Method: clean
-    
-       Cleans flags from nodes (by setting the _flag_ property to false).
+     Method: clean
+  
+     Cleans flags from nodes.
 
-       Parameters:
-       graph - A <Graph> instance.
-    */
-    clean: function(graph) { this.eachNode(graph, function(elem) { elem._flag = false; }); }
+     Also implemented by:
+     
+     <Graph>.
+     
+     Parameters:
+     graph - A <Graph> instance.
+  */
+  clean: function(graph) { this.eachNode(graph, function(elem) { elem._flag = false; }); },
+  
+  /* 
+    Method: getClosestNodeToOrigin 
+  
+    Returns the closest node to the center of canvas.
+  
+    Also implemented by:
+    
+    <Graph>.
+    
+    Parameters:
+   
+     graph - (object) A <Graph> instance.
+     prop - (optional|string) Default's 'current'. A <Graph.Node> position property. Possible properties are 'start', 'current' or 'end'.
+  
+  */
+  getClosestNodeToOrigin: function(graph, prop, flags) {
+   return this.getClosestNodeToPos(graph, Polar.KER, prop, flags);
+  },
+  
+  /* 
+    Method: getClosestNodeToPos
+  
+    Returns the closest node to the given position.
+  
+    Also implemented by:
+    
+    <Graph>.
+    
+    Parameters:
+   
+     graph - (object) A <Graph> instance.
+     pos - (object) A <Complex> or <Polar> instance.
+     prop - (optional|string) Default's *current*. A <Graph.Node> position property. Possible properties are 'start', 'current' or 'end'.
+  
+  */
+  getClosestNodeToPos: function(graph, pos, prop, flags) {
+   var node = null;
+   prop = prop || 'current';
+   pos = pos && pos.getc(true) || Complex.KER;
+   var distance = function(a, b) {
+     var d1 = a.x - b.x, d2 = a.y - b.y;
+     return d1 * d1 + d2 * d2;
+   };
+   this.eachNode(graph, function(elem) {
+     node = (node == null || distance(elem.getPos(prop).getc(true), pos) < distance(
+         node.getPos(prop).getc(true), pos)) ? elem : node;
+   }, flags);
+   return node;
+  } 
 };
 
+//Append graph methods to <Graph>
+$.each(['get', 'getNode', 'each', 'eachNode', 'computeLevels', 'eachBFS', 'clean', 'getClosestNodeToPos', 'getClosestNodeToOrigin'], function(m) {
+  Graph.prototype[m] = function() {
+    return Graph.Util[m].apply(Graph.Util, [this].concat(Array.prototype.slice.call(arguments)));
+  };
+});
 
+//Append node methods to <Graph.Node>
+$.each(['eachAdjacency', 'eachLevel', 'eachSubgraph', 'eachSubnode', 'anySubnode', 'getSubnodes', 'getParents', 'isDescendantOf'], function(m) {
+  Graph.Node.prototype[m] = function() {
+    return Graph.Util[m].apply(Graph.Util, [this].concat(Array.prototype.slice.call(arguments)));
+  };
+});
 
 /*
  * File: Graph.Op.js
  *
- * Defines an abstract class for performing <Graph> Operations.
 */
 
 /*
    Object: Graph.Op
 
-   Generic <Graph> Operations.
-   
-   Description:
+   Perform <Graph> operations like adding/removing <Graph.Nodes> or <Graph.Adjacences>, 
+   morphing a <Graph> into another <Graph>, contracting or expanding subtrees, etc.
 
-   An abstract class holding unary and binary powerful graph operations such as removingNodes, removingEdges, adding two graphs and morphing.
-
-   Implemented by:
-
-   <Hypertree.Op>, <RGraph.Op> and <ST.Op>.
-
-   Access:
-
-   The subclasses for this abstract class can be accessed by using the _op_ property of the <Hypertree>, <RGraph> or <ST> instances created.
-
-   See also:
-
-   <Hypertree.Op>, <RGraph.Op>, <ST.Op>, <Hypertree>, <RGraph>, <ST>, <Graph>.
 */
 Graph.Op = {
 
     options: {
-        type: 'nothing',
-        duration: 2000,
-    hideLabels: true,
-        fps:30
+      type: 'nothing',
+      duration: 2000,
+      hideLabels: true,
+      fps:30
     },
-  
+    
+    initialize: function(viz) {
+      this.viz = viz;
+    },
+
     /*
        Method: removeNode
     
@@ -2381,27 +5315,25 @@
 
        Parameters:
     
-          node - The node's id. Can also be an array having many ids.
-          opt - Animation options. It's an object with optional properties
-          
-          - _type_ Type of the animation. Can be "nothing", "replot", "fade:seq",  "fade:con" or "iter". Default's "nothing".
-          - _duration_ Duration of the animation in milliseconds. Default's 2000.
-          - _fps_ Frames per second for the animation. Default's 30.
-          - _hideLabels_ Hide labels during the animation. Default's *true*.
-          - _transition_ Transitions defined in the <Animation> class. Default's the default transition option of the 
-          <RGraph>, <Hypertree> or <ST> instance created.
+        node - (string|array) The node's id. Can also be an array having many ids.
+        opt - (object) Animation options. It's an object with optional properties described below
+        type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:seq",  "fade:con" or "iter".
+        duration - Described in <Options.Fx>.
+        fps - Described in <Options.Fx>.
+        transition - Described in <Options.Fx>.
+        hideLabels - (boolean) Default's *true*. Hide labels during the animation.
    
       Example:
       (start code js)
-        var rg = new RGraph(canvas, config); //could be new ST or new Hypertree also.
-        rg.op.removeNode('nodeid', {
+        var viz = new $jit.Viz(options);
+        viz.op.removeNode('nodeId', {
           type: 'fade:seq',
           duration: 1000,
           hideLabels: false,
-          transition: Trans.Quart.easeOut
+          transition: $jit.Trans.Quart.easeOut
         });
         //or also
-        rg.op.removeNode(['someid', 'otherid'], {
+        viz.op.removeNode(['someId', 'otherId'], {
           type: 'fade:con',
           duration: 1500
         });
@@ -2410,8 +5342,8 @@
   
     removeNode: function(node, opt) {
         var viz = this.viz;
-        var options = $merge(this.options, viz.controller, opt);
-        var n = $splat(node);
+        var options = $.merge(this.options, viz.controller, opt);
+        var n = $.splat(node);
         var i, that, nodeObj;
         switch(options.type) {
             case 'nothing':
@@ -2420,7 +5352,7 @@
             
             case 'replot':
                 this.removeNode(n, { type: 'nothing' });
-                viz.fx.clearLabels();
+                viz.labels.clearLabels();
                 viz.refresh(true);
                 break;
             
@@ -2429,15 +5361,15 @@
                 //set alpha to 0 for nodes to remove.
                 for(i=0; i<n.length; i++) {
                     nodeObj = viz.graph.getNode(n[i]);
-                    nodeObj.endAlpha = 0;
+                    nodeObj.setData('alpha', 0, 'end');
                 }
-                viz.fx.animate($merge(options, {
-                    modes: ['fade:nodes'],
+                viz.fx.animate($.merge(options, {
+                    modes: ['node-property:alpha'],
                     onComplete: function() {
                         that.removeNode(n, { type: 'nothing' });
-                        viz.fx.clearLabels();
+                        viz.labels.clearLabels();
                         viz.reposition();
-                        viz.fx.animate($merge(options, {
+                        viz.fx.animate($.merge(options, {
                             modes: ['linear']
                         }));
                     }
@@ -2449,14 +5381,15 @@
                 //set alpha to 0 for nodes to remove. Tag them for being ignored on computing positions.
                 for(i=0; i<n.length; i++) {
                     nodeObj = viz.graph.getNode(n[i]);
-                    nodeObj.endAlpha = 0;
+                    nodeObj.setData('alpha', 0, 'end');
                     nodeObj.ignore = true;
                 }
                 viz.reposition();
-                viz.fx.animate($merge(options, {
-                    modes: ['fade:nodes', 'linear'],
+                viz.fx.animate($.merge(options, {
+                    modes: ['node-property:alpha', 'linear'],
                     onComplete: function() {
                         that.removeNode(n, { type: 'nothing' });
+                        options.onComplete && options.onComplete();
                     }
                 }));
                 break;
@@ -2465,8 +5398,8 @@
                 that = this;
                 viz.fx.sequence({
                     condition: function() { return n.length != 0; },
-                    step: function() { that.removeNode(n.shift(), { type: 'nothing' });  viz.fx.clearLabels(); },
-                    onComplete: function() { options.onComplete(); },
+                    step: function() { that.removeNode(n.shift(), { type: 'nothing' });  viz.labels.clearLabels(); },
+                    onComplete: function() { options.onComplete && options.onComplete(); },
                     duration: Math.ceil(options.duration / n.length)
                 });
                 break;
@@ -2478,32 +5411,30 @@
     /*
        Method: removeEdge
     
-       Removes one or more edges from the visualization. 
+       Removes one or more <Graph.Adjacences> from the visualization. 
        It can also perform several animations like fading sequentially, fading concurrently, iterating or replotting.
 
        Parameters:
     
-       vertex - An array having two strings which are the ids of the nodes connected by this edge (i.e ['id1', 'id2']). Can also be a two dimensional array holding many edges (i.e [['id1', 'id2'], ['id3', 'id4'], ...]).
-          opt - Animation options. It's an object with optional properties
-          
-          - _type_ Type of the animation. Can be "nothing", "replot", "fade:seq",  "fade:con" or "iter". Default's "nothing".
-          - _duration_ Duration of the animation in milliseconds. Default's 2000.
-          - _fps_ Frames per second for the animation. Default's 30.
-          - _hideLabels_ Hide labels during the animation. Default's *true*.
-          - _transition_ Transitions defined in the <Animation> class. Default's the default transition option of the 
-          <RGraph>, <Hypertree> or <ST> instance created.
+       vertex - (array) An array having two strings which are the ids of the nodes connected by this edge (i.e ['id1', 'id2']). Can also be a two dimensional array holding many edges (i.e [['id1', 'id2'], ['id3', 'id4'], ...]).
+       opt - (object) Animation options. It's an object with optional properties described below
+       type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:seq",  "fade:con" or "iter".
+       duration - Described in <Options.Fx>.
+       fps - Described in <Options.Fx>.
+       transition - Described in <Options.Fx>.
+       hideLabels - (boolean) Default's *true*. Hide labels during the animation.
    
       Example:
       (start code js)
-        var rg = new RGraph(canvas, config); //could be new ST or new Hypertree also.
-        rg.op.removeEdge(['nodeid', 'otherid'], {
+        var viz = new $jit.Viz(options);
+        viz.op.removeEdge(['nodeId', 'otherId'], {
           type: 'fade:seq',
           duration: 1000,
           hideLabels: false,
-          transition: Trans.Quart.easeOut
+          transition: $jit.Trans.Quart.easeOut
         });
         //or also
-        rg.op.removeEdge([['someid', 'otherid'], ['id3', 'id4']], {
+        viz.op.removeEdge([['someId', 'otherId'], ['id3', 'id4']], {
           type: 'fade:con',
           duration: 1500
         });
@@ -2512,9 +5443,9 @@
     */
     removeEdge: function(vertex, opt) {
         var viz = this.viz;
-        var options = $merge(this.options, viz.controller, opt);
-        var v = ($type(vertex[0]) == 'string')? [vertex] : vertex;
-        var i, that, adjs;
+        var options = $.merge(this.options, viz.controller, opt);
+        var v = ($.type(vertex[0]) == 'string')? [vertex] : vertex;
+        var i, that, adj;
         switch(options.type) {
             case 'nothing':
                 for(i=0; i<v.length; i++)   viz.graph.removeAdjacence(v[i][0], v[i][1]);
@@ -2529,18 +5460,17 @@
                 that = this;
                 //set alpha to 0 for edges to remove.
                 for(i=0; i<v.length; i++) {
-                    adjs = viz.graph.getAdjacence(v[i][0], v[i][1]);
-                    if(adjs) {
-                        adjs[0].endAlpha = 0;
-                        adjs[1].endAlpha = 0;
+                    adj = viz.graph.getAdjacence(v[i][0], v[i][1]);
+                    if(adj) {
+                        adj.setData('alpha', 0,'end');
                     }
                 }
-                viz.fx.animate($merge(options, {
-                    modes: ['fade:vertex'],
+                viz.fx.animate($.merge(options, {
+                    modes: ['edge-property:alpha'],
                     onComplete: function() {
                         that.removeEdge(v, { type: 'nothing' });
                         viz.reposition();
-                        viz.fx.animate($merge(options, {
+                        viz.fx.animate($.merge(options, {
                             modes: ['linear']
                         }));
                     }
@@ -2551,19 +5481,18 @@
                 that = this;
                 //set alpha to 0 for nodes to remove. Tag them for being ignored when computing positions.
                 for(i=0; i<v.length; i++) {
-                    adjs = viz.graph.getAdjacence(v[i][0], v[i][1]);
-                    if(adjs) {
-                        adjs[0].endAlpha = 0;
-                        adjs[0].ignore = true;
-                        adjs[1].endAlpha = 0;
-                        adjs[1].ignore = true;
+                    adj = viz.graph.getAdjacence(v[i][0], v[i][1]);
+                    if(adj) {
+                        adj.setData('alpha',0 ,'end');
+                        adj.ignore = true;
                     }
                 }
                 viz.reposition();
-                viz.fx.animate($merge(options, {
-                    modes: ['fade:vertex', 'linear'],
+                viz.fx.animate($.merge(options, {
+                    modes: ['edge-property:alpha', 'linear'],
                     onComplete: function() {
                         that.removeEdge(v, { type: 'nothing' });
+                        options.onComplete && options.onComplete();
                     }
                 }));
                 break;
@@ -2572,7 +5501,7 @@
                 that = this;
                 viz.fx.sequence({
                     condition: function() { return v.length != 0; },
-                    step: function() { that.removeEdge(v.shift(), { type: 'nothing' }); viz.fx.clearLabels(); },
+                    step: function() { that.removeEdge(v.shift(), { type: 'nothing' }); viz.labels.clearLabels(); },
                     onComplete: function() { options.onComplete(); },
                     duration: Math.ceil(options.duration / v.length)
                 });
@@ -2586,36 +5515,32 @@
        Method: sum
     
        Adds a new graph to the visualization. 
-       
-       The json graph (or tree) must at least have a common node with the current graph plotted by the visualization. 
-       
+       The JSON graph (or tree) must at least have a common node with the current graph plotted by the visualization. 
        The resulting graph can be defined as follows <http://mathworld.wolfram.com/GraphSum.html>
 
        Parameters:
     
-          json - A json tree or graph structure. See also <Loader.loadJSON>.
-           opt - Animation options. It's an object with optional properties
-          
-          - _type_ Type of the animation. Can be "nothing", "replot", "fade:seq" or "fade:con". Default's "nothing".
-          - _duration_ Duration of the animation in milliseconds. Default's 2000.
-          - _fps_ Frames per second for the animation. Default's 30.
-          - _hideLabels_ Hide labels during the animation. Default's *true*.
-          - _transition_ Transitions defined in the <Animation> class. Default's the default transition option of the 
-          <RGraph>, <Hypertree> or <ST> instance created.
+       json - (object) A json tree or graph structure. See also <Loader.loadJSON>.
+       opt - (object) Animation options. It's an object with optional properties described below
+       type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:seq",  "fade:con".
+       duration - Described in <Options.Fx>.
+       fps - Described in <Options.Fx>.
+       transition - Described in <Options.Fx>.
+       hideLabels - (boolean) Default's *true*. Hide labels during the animation.
    
       Example:
       (start code js)
-        //json contains a tree or graph structure.
+        //...json contains a tree or graph structure...
 
-        var rg = new RGraph(canvas, config); //could be new ST or new Hypertree also.
-        rg.op.sum(json, {
+        var viz = new $jit.Viz(options);
+        viz.op.sum(json, {
           type: 'fade:seq',
           duration: 1000,
           hideLabels: false,
-          transition: Trans.Quart.easeOut
+          transition: $jit.Trans.Quart.easeOut
         });
         //or also
-        rg.op.sum(json, {
+        viz.op.sum(json, {
           type: 'fade:con',
           duration: 1500
         });
@@ -2624,15 +5549,14 @@
     */
     sum: function(json, opt) {
         var viz = this.viz;
-        var options = $merge(this.options, viz.controller, opt), root = viz.root;
-        var GUtil, graph;
+        var options = $.merge(this.options, viz.controller, opt), root = viz.root;
+        var graph;
         viz.root = opt.id || viz.root;
         switch(options.type) {
             case 'nothing':
                 graph = viz.construct(json);
-                GUtil = Graph.Util;
-                GUtil.eachNode(graph, function(elem) {
-                    GUtil.eachAdjacency(elem, function(adj) {
+                graph.eachNode(function(elem) {
+                    elem.eachAdjacency(function(adj) {
                         viz.graph.addAdjacence(adj.nodeFrom, adj.nodeTo, adj.data);
                     });
                 });
@@ -2645,19 +5569,18 @@
                 break;
             
             case 'fade:seq': case 'fade': case 'fade:con':
-                GUtil = Graph.Util;
                 that = this;
                 graph = viz.construct(json);
 
                 //set alpha to 0 for nodes to add.
                 var fadeEdges = this.preprocessSum(graph);
-                var modes = !fadeEdges? ['fade:nodes'] : ['fade:nodes', 'fade:vertex'];
+                var modes = !fadeEdges? ['node-property:alpha'] : ['node-property:alpha', 'edge-property:alpha'];
                 viz.reposition();
                 if(options.type != 'fade:con') {
-                    viz.fx.animate($merge(options, {
+                    viz.fx.animate($.merge(options, {
                         modes: ['linear'],
                         onComplete: function() {
-                            viz.fx.animate($merge(options, {
+                            viz.fx.animate($.merge(options, {
                                 modes: modes,
                                 onComplete: function() {
                                     options.onComplete();
@@ -2666,12 +5589,13 @@
                         }
                     }));
                 } else {
-                    GUtil.eachNode(viz.graph, function(elem) {
-                        if (elem.id != root && elem.pos.getp().equals(Polar.KER)) {
-                          elem.pos.set(elem.endPos); elem.startPos.set(elem.endPos);
+                    viz.graph.eachNode(function(elem) {
+                        if (elem.id != root && elem.pos.isZero()) {
+                          elem.pos.set(elem.endPos); 
+                          elem.startPos.set(elem.endPos);
                         }
                     });
-                    viz.fx.animate($merge(options, {
+                    viz.fx.animate($.merge(options, {
                         modes: ['linear'].concat(modes)
                     }));
                 }
@@ -2684,57 +5608,86 @@
     /*
        Method: morph
     
-       This method will _morph_ the current visualized graph into the new _json_ representation passed in the method. 
-       
-       Can also perform multiple animations. The _json_ object must at least have the root node in common with the current visualized graph.
+       This method will transform the current visualized graph into the new JSON representation passed in the method. 
+       The JSON object must at least have the root node in common with the current visualized graph.
 
        Parameters:
     
-           json - A json tree or graph structure. See also <Loader.loadJSON>.
-           opt - Animation options. It's an object with optional properties
-          
-          - _type_ Type of the animation. Can be "nothing", "replot", or "fade". Default's "nothing".
-          - _duration_ Duration of the animation in milliseconds. Default's 2000.
-          - _fps_ Frames per second for the animation. Default's 30.
-          - _hideLabels_ Hide labels during the animation. Default's *true*.
-          - _transition_ Transitions defined in the <Animation> class. Default's the default transition option of the 
-          <RGraph>, <Hypertree> or <ST> instance created.
+       json - (object) A json tree or graph structure. See also <Loader.loadJSON>.
+       opt - (object) Animation options. It's an object with optional properties described below
+       type - (string) Default's *nothing*. Type of the animation. Can be "nothing", "replot", "fade:con".
+       duration - Described in <Options.Fx>.
+       fps - Described in <Options.Fx>.
+       transition - Described in <Options.Fx>.
+       hideLabels - (boolean) Default's *true*. Hide labels during the animation.
+       id - (string) The shared <Graph.Node> id between both graphs.
+       
+       extraModes - (optional|object) When morphing with an animation, dollar prefixed data parameters are added to 
+                    *endData* and not *data* itself. This way you can animate dollar prefixed parameters during your morphing operation. 
+                    For animating these extra-parameters you have to specify an object that has animation groups as keys and animation 
+                    properties as values, just like specified in <Graph.Plot.animate>.
    
       Example:
       (start code js)
-        //json contains a tree or graph structure.
+        //...json contains a tree or graph structure...
 
-        var rg = new RGraph(canvas, config); //could be new ST or new Hypertree also.
-        rg.op.morph(json, {
+        var viz = new $jit.Viz(options);
+        viz.op.morph(json, {
           type: 'fade',
           duration: 1000,
           hideLabels: false,
-          transition: Trans.Quart.easeOut
+          transition: $jit.Trans.Quart.easeOut
         });
         //or also
-        rg.op.morph(json, {
+        viz.op.morph(json, {
           type: 'fade',
           duration: 1500
         });
+        //if the json data contains dollar prefixed params
+        //like $width or $height these too can be animated
+        viz.op.morph(json, {
+          type: 'fade',
+          duration: 1500
+        }, {
+          'node-property': ['width', 'height']
+        });
       (end code)
     
     */
-    morph: function(json, opt) {
+    morph: function(json, opt, extraModes) {
+        extraModes = extraModes || {};
         var viz = this.viz;
-        var options = $merge(this.options, viz.controller, opt), root = viz.root;
-        var GUtil, graph;
+        var options = $.merge(this.options, viz.controller, opt), root = viz.root;
+        var graph;
+        //TODO(nico) this hack makes morphing work with the Hypertree. 
+        //Need to check if it has been solved and this can be removed.
         viz.root = opt.id || viz.root;
         switch(options.type) {
             case 'nothing':
                 graph = viz.construct(json);
-                GUtil = Graph.Util;
-                GUtil.eachNode(graph, function(elem) {
-                    GUtil.eachAdjacency(elem, function(adj) {
-                        viz.graph.addAdjacence(adj.nodeFrom, adj.nodeTo, adj.data);
-                    });
+                graph.eachNode(function(elem) {
+                  var nodeExists = viz.graph.hasNode(elem.id);  
+                  elem.eachAdjacency(function(adj) {
+                    var adjExists = !!viz.graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
+                    viz.graph.addAdjacence(adj.nodeFrom, adj.nodeTo, adj.data);
+                    //Update data properties if the node existed
+                    if(adjExists) {
+                      var addedAdj = viz.graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
+                      for(var prop in (adj.data || {})) {
+                        addedAdj.data[prop] = adj.data[prop];
+                      }
+                    }
+                  });
+                  //Update data properties if the node existed
+                  if(nodeExists) {
+                    var addedNode = viz.graph.getNode(elem.id);
+                    for(var prop in (elem.data || {})) {
+                      addedNode.data[prop] = elem.data[prop];
+                    }
+                  }
                 });
-                GUtil.eachNode(viz.graph, function(elem) {
-                    GUtil.eachAdjacency(elem, function(adj) {
+                viz.graph.eachNode(function(elem) {
+                    elem.eachAdjacency(function(adj) {
                         if(!graph.getAdjacence(adj.nodeFrom.id, adj.nodeTo.id)) {
                             viz.graph.removeAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
                         }
@@ -2745,53 +5698,89 @@
                 break;
             
             case 'replot':
-                viz.fx.clearLabels(true);
+                viz.labels.clearLabels(true);
                 this.morph(json, { type: 'nothing' });
                 viz.refresh(true);
                 viz.refresh(true);
                 break;
                 
             case 'fade:seq': case 'fade': case 'fade:con':
-                GUtil = Graph.Util;
                 that = this;
                 graph = viz.construct(json);
-                //preprocessing for adding nodes.
-                var fadeEdges = this.preprocessSum(graph);
                 //preprocessing for nodes to delete.
-                GUtil.eachNode(viz.graph, function(elem) {
-                    if(!graph.hasNode(elem.id)) {
-                        elem.alpha = 1; elem.startAlpha = 1; elem.endAlpha = 0; elem.ignore = true;
+                //get node property modes to interpolate
+                var nodeModes = ('node-property' in extraModes) 
+                  && $.map($.splat(extraModes['node-property']), 
+                      function(n) { return '$' + n; });
+                viz.graph.eachNode(function(elem) {
+                  var graphNode = graph.getNode(elem.id);   
+                  if(!graphNode) {
+                      elem.setData('alpha', 1);
+                      elem.setData('alpha', 1, 'start');
+                      elem.setData('alpha', 0, 'end');
+                      elem.ignore = true;
+                    } else {
+                      //Update node data information
+                      var graphNodeData = graphNode.data;
+                      for(var prop in graphNodeData) {
+                        if(nodeModes && ($.indexOf(nodeModes, prop) > -1)) {
+                          elem.endData[prop] = graphNodeData[prop];
+                        } else {
+                          elem.data[prop] = graphNodeData[prop];
+                        }
+                      }
                     }
                 }); 
-                GUtil.eachNode(viz.graph, function(elem) {
+                viz.graph.eachNode(function(elem) {
                     if(elem.ignore) return;
-                    GUtil.eachAdjacency(elem, function(adj) {
+                    elem.eachAdjacency(function(adj) {
                         if(adj.nodeFrom.ignore || adj.nodeTo.ignore) return;
                         var nodeFrom = graph.getNode(adj.nodeFrom.id);
                         var nodeTo = graph.getNode(adj.nodeTo.id);
                         if(!nodeFrom.adjacentTo(nodeTo)) {
-                            var adjs = viz.graph.getAdjacence(nodeFrom.id, nodeTo.id);
+                            var adj = viz.graph.getAdjacence(nodeFrom.id, nodeTo.id);
                             fadeEdges = true;
-                            adjs[0].alpha = 1; adjs[0].startAlpha = 1; adjs[0].endAlpha = 0; adjs[0].ignore = true;
-                            adjs[1].alpha = 1; adjs[1].startAlpha = 1; adjs[1].endAlpha = 0; adjs[1].ignore = true;
+                            adj.setData('alpha', 1);
+                            adj.setData('alpha', 1, 'start');
+                            adj.setData('alpha', 0, 'end');
                         }
                     });
                 }); 
-                var modes = !fadeEdges? ['fade:nodes'] : ['fade:nodes', 'fade:vertex'];
-                viz.reposition();
-                GUtil.eachNode(viz.graph, function(elem) {
+                //preprocessing for adding nodes.
+                var fadeEdges = this.preprocessSum(graph);
+
+                var modes = !fadeEdges? ['node-property:alpha'] : 
+                                        ['node-property:alpha', 
+                                         'edge-property:alpha'];
+                //Append extra node-property animations (if any)
+                modes[0] = modes[0] + (('node-property' in extraModes)? 
+                    (':' + $.splat(extraModes['node-property']).join(':')) : '');
+                //Append extra edge-property animations (if any)
+                modes[1] = (modes[1] || 'edge-property:alpha') + (('edge-property' in extraModes)? 
+                    (':' + $.splat(extraModes['edge-property']).join(':')) : '');
+                //Add label-property animations (if any)
+                if('label-property' in extraModes) {
+                  modes.push('label-property:' + $.splat(extraModes['label-property']).join(':'))
+                }
+                //only use reposition if its implemented.
+                if (viz.reposition) {
+                  viz.reposition();
+                } else {
+                  viz.compute('end');
+                }
+                viz.graph.eachNode(function(elem) {
                     if (elem.id != root && elem.pos.getp().equals(Polar.KER)) {
                       elem.pos.set(elem.endPos); elem.startPos.set(elem.endPos);
                     }
                 });
-                viz.fx.animate($merge(options, {
-                    modes: ['polar'].concat(modes),
+                viz.fx.animate($.merge(options, {
+                    modes: [extraModes.position || 'polar'].concat(modes),
                     onComplete: function() {
-                        GUtil.eachNode(viz.graph, function(elem) {
+                        viz.graph.eachNode(function(elem) {
                             if(elem.ignore) viz.graph.removeNode(elem.id);
                         });
-                        GUtil.eachNode(viz.graph, function(elem) {
-                            GUtil.eachAdjacency(elem, function(adj) {
+                        viz.graph.eachNode(function(elem) {
+                            elem.eachAdjacency(function(adj) {
                                 if(adj.ignore) viz.graph.removeAdjacence(adj.nodeFrom.id, adj.nodeTo.id);
                             });
                         });
@@ -2800,32 +5789,145 @@
                 }));
                 break;
 
-            default: this.doError();
+            default:;
         }
     },
+
     
+  /*
+    Method: contract
+ 
+    Collapses the subtree of the given node. The node will have a _collapsed=true_ property.
+    
+    Parameters:
+ 
+    node - (object) A <Graph.Node>.
+    opt - (object) An object containing options described below
+    type - (string) Whether to 'replot' or 'animate' the contraction.
+   
+    There are also a number of Animation options. For more information see <Options.Fx>.
+
+    Example:
+    (start code js)
+     var viz = new $jit.Viz(options);
+     viz.op.contract(node, {
+       type: 'animate',
+       duration: 1000,
+       hideLabels: true,
+       transition: $jit.Trans.Quart.easeOut
+     });
+   (end code)
+ 
+   */
+    contract: function(node, opt) {
+      var viz = this.viz;
+      if(node.collapsed || !node.anySubnode($.lambda(true))) return;
+      opt = $.merge(this.options, viz.config, opt || {}, {
+        'modes': ['node-property:alpha:span', 'linear']
+      });
+      node.collapsed = true;
+      (function subn(n) {
+        n.eachSubnode(function(ch) {
+          ch.ignore = true;
+          ch.setData('alpha', 0, opt.type == 'animate'? 'end' : 'current');
+          subn(ch);
+        });
+      })(node);
+      if(opt.type == 'animate') {
+        viz.compute('end');
+        if(viz.rotated) {
+          viz.rotate(viz.rotated, 'none', {
+            'property':'end'
+          });
+        }
+        (function subn(n) {
+          n.eachSubnode(function(ch) {
+            ch.setPos(node.getPos('end'), 'end');
+            subn(ch);
+          });
+        })(node);
+        viz.fx.animate(opt);
+      } else if(opt.type == 'replot'){
+        viz.refresh();
+      }
+    },
+    
+    /*
+    Method: expand
+ 
+    Expands the previously contracted subtree. The given node must have the _collapsed=true_ property.
+    
+    Parameters:
+ 
+    node - (object) A <Graph.Node>.
+    opt - (object) An object containing options described below
+    type - (string) Whether to 'replot' or 'animate'.
+     
+    There are also a number of Animation options. For more information see <Options.Fx>.
+
+    Example:
+    (start code js)
+      var viz = new $jit.Viz(options);
+      viz.op.expand(node, {
+        type: 'animate',
+        duration: 1000,
+        hideLabels: true,
+        transition: $jit.Trans.Quart.easeOut
+      });
+    (end code)
+ 
+   */
+    expand: function(node, opt) {
+      if(!('collapsed' in node)) return;
+      var viz = this.viz;
+      opt = $.merge(this.options, viz.config, opt || {}, {
+        'modes': ['node-property:alpha:span', 'linear']
+      });
+      delete node.collapsed;
+      (function subn(n) {
+        n.eachSubnode(function(ch) {
+          delete ch.ignore;
+          ch.setData('alpha', 1, opt.type == 'animate'? 'end' : 'current');
+          subn(ch);
+        });
+      })(node);
+      if(opt.type == 'animate') {
+        viz.compute('end');
+        if(viz.rotated) {
+          viz.rotate(viz.rotated, 'none', {
+            'property':'end'
+          });
+        }
+        viz.fx.animate(opt);
+      } else if(opt.type == 'replot'){
+        viz.refresh();
+      }
+    },
+
     preprocessSum: function(graph) {
         var viz = this.viz;
-    var GUtil = Graph.Util;
-        GUtil.eachNode(graph, function(elem) {
+        graph.eachNode(function(elem) {
             if(!viz.graph.hasNode(elem.id)) {
                 viz.graph.addNode(elem);
                 var n = viz.graph.getNode(elem.id);
-                n.alpha = 0; n.startAlpha = 0; n.endAlpha = 1;
+                n.setData('alpha', 0);
+                n.setData('alpha', 0, 'start');
+                n.setData('alpha', 1, 'end');
             }
         }); 
         var fadeEdges = false;
-        GUtil.eachNode(graph, function(elem) {
-            GUtil.eachAdjacency(elem, function(adj) {
+        graph.eachNode(function(elem) {
+            elem.eachAdjacency(function(adj) {
                 var nodeFrom = viz.graph.getNode(adj.nodeFrom.id);
                 var nodeTo = viz.graph.getNode(adj.nodeTo.id);
                 if(!nodeFrom.adjacentTo(nodeTo)) {
-                    var adjs = viz.graph.addAdjacence(nodeFrom, nodeTo, adj.data);
+                    var adj = viz.graph.addAdjacence(nodeFrom, nodeTo, adj.data);
                     if(nodeFrom.startAlpha == nodeFrom.endAlpha 
                     && nodeTo.startAlpha == nodeTo.endAlpha) {
                         fadeEdges = true;
-                        adjs[0].alpha = 0; adjs[0].startAlpha = 0; adjs[0].endAlpha = 1;
-                        adjs[1].alpha = 0; adjs[1].startAlpha = 0; adjs[1].endAlpha = 1;
+                        adj.setData('alpha', 0);
+                        adj.setData('alpha', 0, 'start');
+                        adj.setData('alpha', 1, 'end');
                     } 
                 }
             });
@@ -2837,243 +5939,863 @@
 
 
 /*
- * File: Graph.Plot.js
- *
- * Defines an abstract class for performing <Graph> rendering and animation.
- *
- */
+   File: Helpers.js
+ 
+   Helpers are objects that contain rendering primitives (like rectangles, ellipses, etc), for plotting nodes and edges.
+   Helpers also contain implementations of the *contains* method, a method returning a boolean indicating whether the mouse
+   position is over the rendered shape.
+   
+   Helpers are very useful when implementing new NodeTypes, since you can access them through *this.nodeHelper* and 
+   *this.edgeHelper* <Graph.Plot> properties, providing you with simple primitives and mouse-position check functions.
+   
+   Example:
+   (start code js)
+   //implement a new node type
+   $jit.Viz.Plot.NodeTypes.implement({
+     'customNodeType': {
+       'render': function(node, canvas) {
+         this.nodeHelper.circle.render ...
+       },
+       'contains': function(node, pos) {
+         this.nodeHelper.circle.contains ...
+       }
+     }
+   });
+   //implement an edge type
+   $jit.Viz.Plot.EdgeTypes.implement({
+     'customNodeType': {
+       'render': function(node, canvas) {
+         this.edgeHelper.circle.render ...
+       },
+       //optional
+       'contains': function(node, pos) {
+         this.edgeHelper.circle.contains ...
+       }
+     }
+   });
+   (end code)
 
+*/
+
+/*
+   Object: NodeHelper
+   
+   Contains rendering and other type of primitives for simple shapes.
+ */
+var NodeHelper = {
+  'none': {
+    'render': $.empty,
+    'contains': $.lambda(false)
+  },
+  /*
+   Object: NodeHelper.circle
+   */
+  'circle': {
+    /*
+     Method: render
+     
+     Renders a circle into the canvas.
+     
+     Parameters:
+     
+     type - (string) Possible options are 'fill' or 'stroke'.
+     pos - (object) An *x*, *y* object with the position of the center of the circle.
+     radius - (number) The radius of the circle to be rendered.
+     canvas - (object) A <Canvas> instance.
+     
+     Example:
+     (start code js)
+     NodeHelper.circle.render('fill', { x: 10, y: 30 }, 30, viz.canvas);
+     (end code)
+     */
+    'render': function(type, pos, radius, canvas){
+      var ctx = canvas.getCtx();
+      ctx.beginPath();
+      ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2, true);
+      ctx.closePath();
+      ctx[type]();
+    },
+    /*
+    Method: contains
+    
+    Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
+    
+    Parameters:
+    
+    npos - (object) An *x*, *y* object with the <Graph.Node> position.
+    pos - (object) An *x*, *y* object with the position to check.
+    radius - (number) The radius of the rendered circle.
+    
+    Example:
+    (start code js)
+    NodeHelper.circle.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30); //true
+    (end code)
+    */
+    'contains': function(npos, pos, radius){
+      var diffx = npos.x - pos.x, 
+          diffy = npos.y - pos.y, 
+          diff = diffx * diffx + diffy * diffy;
+      return diff <= radius * radius;
+    }
+  },
+  /*
+  Object: NodeHelper.ellipse
+  */
+  'ellipse': {
+    /*
+    Method: render
+    
+    Renders an ellipse into the canvas.
+    
+    Parameters:
+    
+    type - (string) Possible options are 'fill' or 'stroke'.
+    pos - (object) An *x*, *y* object with the position of the center of the ellipse.
+    width - (number) The width of the ellipse.
+    height - (number) The height of the ellipse.
+    canvas - (object) A <Canvas> instance.
+    
+    Example:
+    (start code js)
+    NodeHelper.ellipse.render('fill', { x: 10, y: 30 }, 30, 40, viz.canvas);
+    (end code)
+    */
+    'render': function(type, pos, width, height, canvas){
+      var ctx = canvas.getCtx(),
+          scalex = 1,
+          scaley = 1,
+          scaleposx = 1,
+          scaleposy = 1,
+          radius = 0;
+
+      if (width > height) {
+          radius = width / 2;
+          scaley = height / width;
+          scaleposy = width / height;
+      } else {
+          radius = height / 2;
+          scalex = width / height;
+          scaleposx = height / width;
+      }
+
+      ctx.save();
+      ctx.scale(scalex, scaley);
+      ctx.beginPath();
+      ctx.arc(pos.x * scaleposx, pos.y * scaleposy, radius, 0, Math.PI * 2, true);
+      ctx.closePath();
+      ctx[type]();
+      ctx.restore();
+    },
+    /*
+    Method: contains
+    
+    Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
+    
+    Parameters:
+    
+    npos - (object) An *x*, *y* object with the <Graph.Node> position.
+    pos - (object) An *x*, *y* object with the position to check.
+    width - (number) The width of the rendered ellipse.
+    height - (number) The height of the rendered ellipse.
+    
+    Example:
+    (start code js)
+    NodeHelper.ellipse.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30, 40);
+    (end code)
+    */
+    'contains': function(npos, pos, width, height){
+      var radius = 0,
+          scalex = 1,
+          scaley = 1,
+          diffx = 0,
+          diffy = 0,
+          diff = 0;
+
+      if (width > height) {
+	      radius = width / 2;
+	      scaley = height / width;
+      } else {
+          radius = height / 2;
+          scalex = width / height;
+      }
+
+      diffx = (npos.x - pos.x) * (1 / scalex);
+      diffy = (npos.y - pos.y) * (1 / scaley);
+      diff = diffx * diffx + diffy * diffy;
+      return diff <= radius * radius;
+    }
+  },
+  /*
+  Object: NodeHelper.square
+  */
+  'square': {
+    /*
+    Method: render
+    
+    Renders a square into the canvas.
+    
+    Parameters:
+    
+    type - (string) Possible options are 'fill' or 'stroke'.
+    pos - (object) An *x*, *y* object with the position of the center of the square.
+    dim - (number) The radius (or half-diameter) of the square.
+    canvas - (object) A <Canvas> instance.
+    
+    Example:
+    (start code js)
+    NodeHelper.square.render('stroke', { x: 10, y: 30 }, 40, viz.canvas);
+    (end code)
+    */
+    'render': function(type, pos, dim, canvas){
+      canvas.getCtx()[type + "Rect"](pos.x - dim, pos.y - dim, 2*dim, 2*dim);
+    },
+    /*
+    Method: contains
+    
+    Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
+    
+    Parameters:
+    
+    npos - (object) An *x*, *y* object with the <Graph.Node> position.
+    pos - (object) An *x*, *y* object with the position to check.
+    dim - (number) The radius (or half-diameter) of the square.
+    
+    Example:
+    (start code js)
+    NodeHelper.square.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30);
+    (end code)
+    */
+    'contains': function(npos, pos, dim){
+      return Math.abs(pos.x - npos.x) <= dim && Math.abs(pos.y - npos.y) <= dim;
+    }
+  },
+  /*
+  Object: NodeHelper.rectangle
+  */
+  'rectangle': {
+    /*
+    Method: render
+    
+    Renders a rectangle into the canvas.
+    
+    Parameters:
+    
+    type - (string) Possible options are 'fill' or 'stroke'.
+    pos - (object) An *x*, *y* object with the position of the center of the rectangle.
+    width - (number) The width of the rectangle.
+    height - (number) The height of the rectangle.
+    canvas - (object) A <Canvas> instance.
+    
+    Example:
+    (start code js)
+    NodeHelper.rectangle.render('fill', { x: 10, y: 30 }, 30, 40, viz.canvas);
+    (end code)
+    */
+    'render': function(type, pos, width, height, canvas){
+      canvas.getCtx()[type + "Rect"](pos.x - width / 2, pos.y - height / 2, 
+                                      width, height);
+    },
+    /*
+    Method: contains
+    
+    Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
+    
+    Parameters:
+    
+    npos - (object) An *x*, *y* object with the <Graph.Node> position.
+    pos - (object) An *x*, *y* object with the position to check.
+    width - (number) The width of the rendered rectangle.
+    height - (number) The height of the rendered rectangle.
+    
+    Example:
+    (start code js)
+    NodeHelper.rectangle.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30, 40);
+    (end code)
+    */
+    'contains': function(npos, pos, width, height){
+      return Math.abs(pos.x - npos.x) <= width / 2
+          && Math.abs(pos.y - npos.y) <= height / 2;
+    }
+  },
+  /*
+  Object: NodeHelper.triangle
+  */
+  'triangle': {
+    /*
+    Method: render
+    
+    Renders a triangle into the canvas.
+    
+    Parameters:
+    
+    type - (string) Possible options are 'fill' or 'stroke'.
+    pos - (object) An *x*, *y* object with the position of the center of the triangle.
+    dim - (number) Half the base and half the height of the triangle.
+    canvas - (object) A <Canvas> instance.
+    
+    Example:
+    (start code js)
+    NodeHelper.triangle.render('stroke', { x: 10, y: 30 }, 40, viz.canvas);
+    (end code)
+    */
+    'render': function(type, pos, dim, canvas){
+      var ctx = canvas.getCtx(), 
+          c1x = pos.x, 
+          c1y = pos.y - dim, 
+          c2x = c1x - dim, 
+          c2y = pos.y + dim, 
+          c3x = c1x + dim, 
+          c3y = c2y;
+      ctx.beginPath();
+      ctx.moveTo(c1x, c1y);
+      ctx.lineTo(c2x, c2y);
+      ctx.lineTo(c3x, c3y);
+      ctx.closePath();
+      ctx[type]();
+    },
+    /*
+    Method: contains
+    
+    Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
+    
+    Parameters:
+    
+    npos - (object) An *x*, *y* object with the <Graph.Node> position.
+    pos - (object) An *x*, *y* object with the position to check.
+    dim - (number) Half the base and half the height of the triangle.
+    
+    Example:
+    (start code js)
+    NodeHelper.triangle.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30);
+    (end code)
+    */
+    'contains': function(npos, pos, dim) {
+      return NodeHelper.circle.contains(npos, pos, dim);
+    }
+  },
+  /*
+  Object: NodeHelper.star
+  */
+  'star': {
+    /*
+    Method: render
+    
+    Renders a star (concave decagon) into the canvas.
+    
+    Parameters:
+    
+    type - (string) Possible options are 'fill' or 'stroke'.
+    pos - (object) An *x*, *y* object with the position of the center of the star.
+    dim - (number) The length of a side of a concave decagon.
+    canvas - (object) A <Canvas> instance.
+    
+    Example:
+    (start code js)
+    NodeHelper.star.render('stroke', { x: 10, y: 30 }, 40, viz.canvas);
+    (end code)
+    */
+    'render': function(type, pos, dim, canvas){
+      var ctx = canvas.getCtx(), 
+          pi5 = Math.PI / 5;
+      ctx.save();
+      ctx.translate(pos.x, pos.y);
+      ctx.beginPath();
+      ctx.moveTo(dim, 0);
+      for (var i = 0; i < 9; i++) {
+        ctx.rotate(pi5);
+        if (i % 2 == 0) {
+          ctx.lineTo((dim / 0.525731) * 0.200811, 0);
+        } else {
+          ctx.lineTo(dim, 0);
+        }
+      }
+      ctx.closePath();
+      ctx[type]();
+      ctx.restore();
+    },
+    /*
+    Method: contains
+    
+    Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
+    
+    Parameters:
+    
+    npos - (object) An *x*, *y* object with the <Graph.Node> position.
+    pos - (object) An *x*, *y* object with the position to check.
+    dim - (number) The length of a side of a concave decagon.
+    
+    Example:
+    (start code js)
+    NodeHelper.star.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, 30);
+    (end code)
+    */
+    'contains': function(npos, pos, dim) {
+      return NodeHelper.circle.contains(npos, pos, dim);
+    }
+  }
+};
+
+/*
+  Object: EdgeHelper
+  
+  Contains rendering primitives for simple edge shapes.
+*/
+var EdgeHelper = {
+  /*
+    Object: EdgeHelper.line
+  */
+  'line': {
+      /*
+      Method: render
+      
+      Renders a line into the canvas.
+      
+      Parameters:
+      
+      from - (object) An *x*, *y* object with the starting position of the line.
+      to - (object) An *x*, *y* object with the ending position of the line.
+      canvas - (object) A <Canvas> instance.
+      
+      Example:
+      (start code js)
+      EdgeHelper.line.render({ x: 10, y: 30 }, { x: 10, y: 50 }, viz.canvas);
+      (end code)
+      */
+      'render': function(from, to, canvas){
+        var ctx = canvas.getCtx();
+        ctx.beginPath();
+        ctx.moveTo(from.x, from.y);
+        ctx.lineTo(to.x, to.y);
+        ctx.stroke();
+      },
+      /*
+      Method: contains
+      
+      Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
+      
+      Parameters:
+      
+      posFrom - (object) An *x*, *y* object with a <Graph.Node> position.
+      posTo - (object) An *x*, *y* object with a <Graph.Node> position.
+      pos - (object) An *x*, *y* object with the position to check.
+      epsilon - (number) The dimension of the shape.
+      
+      Example:
+      (start code js)
+      EdgeHelper.line.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, { x: 15, y: 35 }, 30);
+      (end code)
+      */
+      'contains': function(posFrom, posTo, pos, epsilon) {
+        var min = Math.min, 
+            max = Math.max,
+            minPosX = min(posFrom.x, posTo.x),
+            maxPosX = max(posFrom.x, posTo.x),
+            minPosY = min(posFrom.y, posTo.y),
+            maxPosY = max(posFrom.y, posTo.y);
+        
+        if(pos.x >= minPosX && pos.x <= maxPosX 
+            && pos.y >= minPosY && pos.y <= maxPosY) {
+          if(Math.abs(posTo.x - posFrom.x) <= epsilon) {
+            return true;
+          }
+          var dist = (posTo.y - posFrom.y) / (posTo.x - posFrom.x) * (pos.x - posFrom.x) + posFrom.y;
+          return Math.abs(dist - pos.y) <= epsilon;
+        }
+        return false;
+      }
+    },
+  /*
+    Object: EdgeHelper.arrow
+  */
+  'arrow': {
+      /*
+      Method: render
+      
+      Renders an arrow into the canvas.
+      
+      Parameters:
+      
+      from - (object) An *x*, *y* object with the starting position of the arrow.
+      to - (object) An *x*, *y* object with the ending position of the arrow.
+      dim - (number) The dimension of the arrow.
+      swap - (boolean) Whether to set the arrow pointing to the starting position or the ending position.
+      canvas - (object) A <Canvas> instance.
+      
+      Example:
+      (start code js)
+      EdgeHelper.arrow.render({ x: 10, y: 30 }, { x: 10, y: 50 }, 13, false, viz.canvas);
+      (end code)
+      */
+    'render': function(from, to, dim, swap, canvas){
+        var ctx = canvas.getCtx();
+        // invert edge direction
+        if (swap) {
+          var tmp = from;
+          from = to;
+          to = tmp;
+        }
+        var vect = new Complex(to.x - from.x, to.y - from.y);
+        vect.$scale(dim / vect.norm());
+        var intermediatePoint = new Complex(to.x - vect.x, to.y - vect.y),
+            normal = new Complex(-vect.y / 2, vect.x / 2),
+            v1 = intermediatePoint.add(normal), 
+            v2 = intermediatePoint.$add(normal.$scale(-1));
+        
+        ctx.beginPath();
+        ctx.moveTo(from.x, from.y);
+        ctx.lineTo(to.x, to.y);
+        ctx.stroke();
+        ctx.beginPath();
+        ctx.moveTo(v1.x, v1.y);
+        ctx.lineTo(v2.x, v2.y);
+        ctx.lineTo(to.x, to.y);
+        ctx.closePath();
+        ctx.fill();
+    },
+    /*
+    Method: contains
+    
+    Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
+    
+    Parameters:
+    
+    posFrom - (object) An *x*, *y* object with a <Graph.Node> position.
+    posTo - (object) An *x*, *y* object with a <Graph.Node> position.
+    pos - (object) An *x*, *y* object with the position to check.
+    epsilon - (number) The dimension of the shape.
+    
+    Example:
+    (start code js)
+    EdgeHelper.arrow.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, { x: 15, y: 35 }, 30);
+    (end code)
+    */
+    'contains': function(posFrom, posTo, pos, epsilon) {
+      return EdgeHelper.line.contains(posFrom, posTo, pos, epsilon);
+    }
+  },
+  /*
+    Object: EdgeHelper.hyperline
+  */
+  'hyperline': {
+    /*
+    Method: render
+    
+    Renders a hyperline into the canvas. A hyperline are the lines drawn for the <Hypertree> visualization.
+    
+    Parameters:
+    
+    from - (object) An *x*, *y* object with the starting position of the hyperline. *x* and *y* must belong to [0, 1).
+    to - (object) An *x*, *y* object with the ending position of the hyperline. *x* and *y* must belong to [0, 1).
+    r - (number) The scaling factor.
+    canvas - (object) A <Canvas> instance.
+    
+    Example:
+    (start code js)
+    EdgeHelper.hyperline.render({ x: 10, y: 30 }, { x: 10, y: 50 }, 100, viz.canvas);
+    (end code)
+    */
+    'render': function(from, to, r, canvas){
+      var ctx = canvas.getCtx();  
+      var centerOfCircle = computeArcThroughTwoPoints(from, to);
+      if (centerOfCircle.a > 1000 || centerOfCircle.b > 1000
+          || centerOfCircle.ratio < 0) {
+        ctx.beginPath();
+        ctx.moveTo(from.x * r, from.y * r);
+        ctx.lineTo(to.x * r, to.y * r);
+        ctx.stroke();
+      } else {
+        var angleBegin = Math.atan2(to.y - centerOfCircle.y, to.x
+            - centerOfCircle.x);
+        var angleEnd = Math.atan2(from.y - centerOfCircle.y, from.x
+            - centerOfCircle.x);
+        var sense = sense(angleBegin, angleEnd);
+        ctx.beginPath();
+        ctx.arc(centerOfCircle.x * r, centerOfCircle.y * r, centerOfCircle.ratio
+            * r, angleBegin, angleEnd, sense);
+        ctx.stroke();
+      }
+      /*      
+        Calculates the arc parameters through two points.
+        
+        More information in <http://en.wikipedia.org/wiki/Poincar%C3%A9_disc_model#Analytic_geometry_constructions_in_the_hyperbolic_plane> 
+      
+        Parameters:
+      
+        p1 - A <Complex> instance.
+        p2 - A <Complex> instance.
+        scale - The Disk's diameter.
+      
+        Returns:
+      
+        An object containing some arc properties.
+      */
+      function computeArcThroughTwoPoints(p1, p2){
+        var aDen = (p1.x * p2.y - p1.y * p2.x), bDen = aDen;
+        var sq1 = p1.squaredNorm(), sq2 = p2.squaredNorm();
+        // Fall back to a straight line
+        if (aDen == 0)
+          return {
+            x: 0,
+            y: 0,
+            ratio: -1
+          };
+    
+        var a = (p1.y * sq2 - p2.y * sq1 + p1.y - p2.y) / aDen;
+        var b = (p2.x * sq1 - p1.x * sq2 + p2.x - p1.x) / bDen;
+        var x = -a / 2;
+        var y = -b / 2;
+        var squaredRatio = (a * a + b * b) / 4 - 1;
+        // Fall back to a straight line
+        if (squaredRatio < 0)
+          return {
+            x: 0,
+            y: 0,
+            ratio: -1
+          };
+        var ratio = Math.sqrt(squaredRatio);
+        var out = {
+          x: x,
+          y: y,
+          ratio: ratio > 1000? -1 : ratio,
+          a: a,
+          b: b
+        };
+    
+        return out;
+      }
+      /*      
+        Sets angle direction to clockwise (true) or counterclockwise (false). 
+         
+        Parameters: 
+      
+           angleBegin - Starting angle for drawing the arc. 
+           angleEnd - The HyperLine will be drawn from angleBegin to angleEnd. 
+      
+        Returns: 
+      
+           A Boolean instance describing the sense for drawing the HyperLine. 
+      */
+      function sense(angleBegin, angleEnd){
+        return (angleBegin < angleEnd)? ((angleBegin + Math.PI > angleEnd)? false
+            : true) : ((angleEnd + Math.PI > angleBegin)? true : false);
+      }
+    },
+    /*
+    Method: contains
+    
+    Not Implemented
+    
+    Returns *true* if *pos* is contained in the area of the shape. Returns *false* otherwise.
+    
+    Parameters:
+    
+    posFrom - (object) An *x*, *y* object with a <Graph.Node> position.
+    posTo - (object) An *x*, *y* object with a <Graph.Node> position.
+    pos - (object) An *x*, *y* object with the position to check.
+    epsilon - (number) The dimension of the shape.
+    
+    Example:
+    (start code js)
+    EdgeHelper.hyperline.contains({ x: 10, y: 30 }, { x: 15, y: 35 }, { x: 15, y: 35 }, 30);
+    (end code)
+    */
+    'contains': $.lambda(false)
+  }
+};
+
+
+/*
+ * File: Graph.Plot.js
+ */
 
 /*
    Object: Graph.Plot
 
-   Generic <Graph> rendering and animation methods.
+   <Graph> rendering and animation methods.
    
-   Description:
-
-   An abstract class for plotting a generic graph structure.
-
-   Implemented by:
-
-   <Hypertree.Plot>, <RGraph.Plot>, <ST.Plot>.
-
-   Access:
-
-   The subclasses for this abstract class can be accessed by using the _fx_ property of the <Hypertree>, <RGraph>, or <ST> instances created.
-
-   See also:
-
-   <Hypertree.Plot>, <RGraph.Plot>, <ST.Plot>, <Hypertree>, <RGraph>, <ST>, <Graph>.
-
+   Properties:
+   
+   nodeHelper - <NodeHelper> object.
+   edgeHelper - <EdgeHelper> object.
 */
 Graph.Plot = {
+    //Default initializer
+    initialize: function(viz, klass){
+      this.viz = viz;
+      this.config = viz.config;
+      this.node = viz.config.Node;
+      this.edge = viz.config.Edge;
+      this.animation = new Animation;
+      this.nodeTypes = new klass.Plot.NodeTypes;
+      this.edgeTypes = new klass.Plot.EdgeTypes;
+      this.labels = viz.labels;
+   },
+
+    //Add helpers
+    nodeHelper: NodeHelper,
+    edgeHelper: EdgeHelper,
     
     Interpolator: {
-        'moebius': function(elem, delta, vector) {
-            if(delta <= 1 || vector.norm() <= 1) {
-        var x = vector.x, y = vector.y;
-              var ans = elem.startPos.getc().moebiusTransformation(vector);
+        //node/edge property parsers
+        'map': {
+          'border': 'color',
+          'color': 'color',
+          'width': 'number',
+          'height': 'number',
+          'dim': 'number',
+          'alpha': 'number',
+          'lineWidth': 'number',
+          'angularWidth':'number',
+          'span':'number',
+          'valueArray':'array-number',
+          'dimArray':'array-number'
+          //'colorArray':'array-color'
+        },
+        
+        //canvas specific parsers
+        'canvas': {
+          'globalAlpha': 'number',
+          'fillStyle': 'color',
+          'strokeStyle': 'color',
+          'lineWidth': 'number',
+          'shadowBlur': 'number',
+          'shadowColor': 'color',
+          'shadowOffsetX': 'number',
+          'shadowOffsetY': 'number',
+          'miterLimit': 'number'
+        },
+  
+        //label parsers
+        'label': {
+          'size': 'number',
+          'color': 'color'
+        },
+  
+        //Number interpolator
+        'compute': function(from, to, delta) {
+          return from + (to - from) * delta;
+        },
+        
+        //Position interpolators
+        'moebius': function(elem, props, delta, vector) {
+          var v = vector.scale(-delta);  
+          if(v.norm() < 1) {
+              var x = v.x, y = v.y;
+              var ans = elem.startPos
+                .getc().moebiusTransformation(v);
               elem.pos.setc(ans.x, ans.y);
-              vector.x = x; vector.y = y;
+              v.x = x; v.y = y;
             }           
-    },
+        },
 
-        'linear': function(elem, delta) {
+        'linear': function(elem, props, delta) {
             var from = elem.startPos.getc(true);
             var to = elem.endPos.getc(true);
-            elem.pos.setc((to.x - from.x) * delta + from.x, (to.y - from.y) * delta + from.y);
+            elem.pos.setc(this.compute(from.x, to.x, delta), 
+                          this.compute(from.y, to.y, delta));
         },
 
-        'fade:nodes': function(elem, delta) {
-            if(delta <= 1 && (elem.endAlpha != elem.alpha)) {
-                var from = elem.startAlpha;
-                var to   = elem.endAlpha;
-                elem.alpha = from + (to - from) * delta;
-            }
+        'polar': function(elem, props, delta) {
+          var from = elem.startPos.getp(true);
+          var to = elem.endPos.getp();
+          var ans = to.interpolate(from, delta);
+          elem.pos.setp(ans.theta, ans.rho);
         },
         
-        'fade:vertex': function(elem, delta) {
+        //Graph's Node/Edge interpolators
+        'number': function(elem, prop, delta, getter, setter) {
+          var from = elem[getter](prop, 'start');
+          var to = elem[getter](prop, 'end');
+          elem[setter](prop, this.compute(from, to, delta));
+        },
+
+        'color': function(elem, prop, delta, getter, setter) {
+          var from = $.hexToRgb(elem[getter](prop, 'start'));
+          var to = $.hexToRgb(elem[getter](prop, 'end'));
+          var comp = this.compute;
+          var val = $.rgbToHex([parseInt(comp(from[0], to[0], delta)),
+                                parseInt(comp(from[1], to[1], delta)),
+                                parseInt(comp(from[2], to[2], delta))]);
+          
+          elem[setter](prop, val);
+        },
+        
+        'array-number': function(elem, prop, delta, getter, setter) {
+          var from = elem[getter](prop, 'start'),
+              to = elem[getter](prop, 'end'),
+              cur = [];
+          for(var i=0, l=from.length; i<l; i++) {
+            var fromi = from[i], toi = to[i];
+            if(fromi.length) {
+              for(var j=0, len=fromi.length, curi=[]; j<len; j++) {
+                curi.push(this.compute(fromi[j], toi[j], delta));
+              }
+              cur.push(curi);
+            } else {
+              cur.push(this.compute(fromi, toi, delta));
+            }
+          }
+          elem[setter](prop, cur);
+        },
+        
+        'node': function(elem, props, delta, map, getter, setter) {
+          map = this[map];
+          if(props) {
+            var len = props.length;
+            for(var i=0; i<len; i++) {
+              var pi = props[i];
+              this[map[pi]](elem, pi, delta, getter, setter);
+            }
+          } else {
+            for(var pi in map) {
+              this[map[pi]](elem, pi, delta, getter, setter);
+            }
+          }
+        },
+        
+        'edge': function(elem, props, delta, mapKey, getter, setter) {
             var adjs = elem.adjacencies;
-            for(var id in adjs) this['fade:nodes'](adjs[id], delta);
+            for(var id in adjs) this['node'](adjs[id], props, delta, mapKey, getter, setter);
         },
         
-        'polar': function(elem, delta) {
-            var from = elem.startPos.getp(true);
-            var to = elem.endPos.getp();
-      var ans = to.interpolate(from, delta);
-            elem.pos.setp(ans.theta, ans.rho);
-        }
-    },
-    
-    //A flag value indicating if node labels are being displayed or not.
-    labelsHidden: false,
-    //Label DOM element
-    labelContainer: false,
-    //Label DOM elements hash.
-    labels: {},
-
-    /*
-       Method: getLabelContainer
-    
-       Lazy fetcher for the label container.
-
-       Returns:
-
-       The label container DOM element.
-
-       Example:
-
-      (start code js)
-        var rg = new RGraph(canvas, config); //can be also Hypertree or ST
-        var labelContainer = rg.fx.getLabelContainer();
-        alert(labelContainer.innerHTML);
-      (end code)
-    */
-    getLabelContainer: function() {
-        return this.labelContainer? this.labelContainer : this.labelContainer = document.getElementById(this.viz.config.labelContainer);
-    },
-    
-    /*
-       Method: getLabel
-    
-       Lazy fetcher for the label DOM element.
-
-       Parameters:
-
-       id - The label id (which is also a <Graph.Node> id).
-
-       Returns:
-
-       The label DOM element.
-
-       Example:
-
-      (start code js)
-        var rg = new RGraph(canvas, config); //can be also Hypertree or ST
-        var label = rg.fx.getLabel('someid');
-        alert(label.innerHTML);
-      (end code)
-      
-    */
-    getLabel: function(id) {
-        return (id in this.labels && this.labels[id] != null)? this.labels[id] : this.labels[id] = document.getElementById(id);
-    },
-    
-    /*
-       Method: hideLabels
-    
-       Hides all labels (by hiding the label container).
-
-       Parameters:
-
-       hide - A boolean value indicating if the label container must be hidden or not.
-
-       Example:
-       (start code js)
-        var rg = new RGraph(canvas, config); //can be also Hypertree or ST
-        rg.fx.hideLabels(true);
-       (end code)
-       
-    */
-    hideLabels: function (hide) {
-        var container = this.getLabelContainer();
-        if(hide) container.style.display = 'none';
-        else container.style.display = '';
-        this.labelsHidden = hide;
-    },
-    
-    /*
-       Method: clearLabels
-    
-       Clears the label container.
-
-       Useful when using a new visualization with the same canvas element/widget.
-
-       Parameters:
-
-       force - Forces deletion of all labels.
-
-       Example:
-       (start code js)
-        var rg = new RGraph(canvas, config); //can be also Hypertree or ST
-        rg.fx.clearLabels();
-        (end code)
-    */
-    clearLabels: function(force) {
-        for(var id in this.labels) {
-            if (force || !this.viz.graph.hasNode(id)) {
-                this.disposeLabel(id);
-                delete this.labels[id];
-            }
-        }
-    },
-    
-    /*
-       Method: disposeLabel
-    
-       Removes a label.
-
-       Parameters:
-
-       id - A label id (which generally is also a <Graph.Node> id).
-
-       Example:
-       (start code js)
-        var rg = new RGraph(canvas, config); //can be also Hypertree or ST
-        rg.fx.disposeLabel('labelid');
-       (end code)
-    */
-    disposeLabel: function(id) {
-        var elem = this.getLabel(id);
-        if(elem && elem.parentNode) {
-      elem.parentNode.removeChild(elem);
-    }  
-    },
-
-    /*
-       Method: hideLabel
-    
-       Hides the corresponding <Graph.Node> label.
+        'node-property': function(elem, props, delta) {
+          this['node'](elem, props, delta, 'map', 'getData', 'setData');
+        },
         
-       Parameters:
+        'edge-property': function(elem, props, delta) {
+          this['edge'](elem, props, delta, 'map', 'getData', 'setData');  
+        },
 
-       node - A <Graph.Node>. Can also be an array of <Graph.Nodes>.
-       flag - If *true*, nodes will be shown. Otherwise nodes will be hidden.
-
-       Example:
-       (start code js)
-        var rg = new RGraph(canvas, config); //can be also Hypertree or ST
-        rg.fx.hideLabel(rg.graph.getNode('someid'), false);
-       (end code)
-    */
-    hideLabel: function(node, flag) {
-    node = $splat(node);
-    var st = flag? "" : "none", lab, that = this;
-    $each(node, function(n) {
-      var lab = that.getLabel(n.id);
-      if (lab) {
-           lab.style.display = st;
-      } 
-    });
+        'label-property': function(elem, props, delta) {
+          this['node'](elem, props, delta, 'label', 'getLabelData', 'setLabelData');
+        },
+        
+        'node-style': function(elem, props, delta) {
+          this['node'](elem, props, delta, 'canvas', 'getCanvasStyle', 'setCanvasStyle');
+        },
+        
+        'edge-style': function(elem, props, delta) {
+          this['edge'](elem, props, delta, 'canvas', 'getCanvasStyle', 'setCanvasStyle');  
+        }
     },
-
+    
+  
     /*
-       Method: sequence
+       sequence
     
        Iteratively performs an action while refreshing the state of the visualization.
 
        Parameters:
 
-       options - Some sequence options like
-      
-       - _condition_ A function returning a boolean instance in order to stop iterations.
-       - _step_ A function to execute on each step of the iteration.
-       - _onComplete_ A function to execute when the sequence finishes.
-       - _duration_ Duration (in milliseconds) of each step.
+       options - (object) An object containing some sequence options described below
+       condition - (function) A function returning a boolean instance in order to stop iterations.
+       step - (function) A function to execute on each step of the iteration.
+       onComplete - (function) A function to execute when the sequence finishes.
+       duration - (number) Duration (in milliseconds) of each step.
 
       Example:
        (start code js)
-        var rg = new RGraph(canvas, config); //can be also Hypertree or ST
+        var rg = new $jit.RGraph(options);
         var i = 0;
         rg.fx.sequence({
           condition: function() {
@@ -3091,176 +6813,370 @@
     */
     sequence: function(options) {
         var that = this;
-    options = $merge({
-            condition: $lambda(false),
-            step: $empty,
-            onComplete: $empty,
-            duration: 200
+        options = $.merge({
+          condition: $.lambda(false),
+          step: $.empty,
+          onComplete: $.empty,
+          duration: 200
         }, options || {});
 
         var interval = setInterval(function() {
-            if(options.condition()) {
-                options.step();
-            } else {
-                clearInterval(interval);
-                options.onComplete();
-            }
-            that.viz.refresh(true);
+          if(options.condition()) {
+            options.step();
+          } else {
+            clearInterval(interval);
+            options.onComplete();
+          }
+          that.viz.refresh(true);
         }, options.duration);
     },
     
     /*
+      prepare
+ 
+      Prepare graph position and other attribute values before performing an Animation. 
+      This method is used internally by the Toolkit.
+      
+      See also:
+       
+       <Animation>, <Graph.Plot.animate>
+
+    */
+    prepare: function(modes) {
+      var graph = this.viz.graph,
+          accessors = {
+            'node-property': {
+              'getter': 'getData',
+              'setter': 'setData'
+            },
+            'edge-property': {
+              'getter': 'getData',
+              'setter': 'setData'
+            },
+            'node-style': {
+              'getter': 'getCanvasStyle',
+              'setter': 'setCanvasStyle'
+            },
+            'edge-style': {
+              'getter': 'getCanvasStyle',
+              'setter': 'setCanvasStyle'
+            }
+          };
+
+      //parse modes
+      var m = {};
+      if($.type(modes) == 'array') {
+        for(var i=0, len=modes.length; i < len; i++) {
+          var elems = modes[i].split(':');
+          m[elems.shift()] = elems;
+        }
+      } else {
+        for(var p in modes) {
+          if(p == 'position') {
+            m[modes.position] = [];
+          } else {
+            m[p] = $.splat(modes[p]);
+          }
+        }
+      }
+      
+      graph.eachNode(function(node) { 
+        node.startPos.set(node.pos);
+        $.each(['node-property', 'node-style'], function(p) {
+          if(p in m) {
+            var prop = m[p];
+            for(var i=0, l=prop.length; i < l; i++) {
+              node[accessors[p].setter](prop[i], node[accessors[p].getter](prop[i]), 'start');
+            }
+          }
+        });
+        $.each(['edge-property', 'edge-style'], function(p) {
+          if(p in m) {
+            var prop = m[p];
+            node.eachAdjacency(function(adj) {
+              for(var i=0, l=prop.length; i < l; i++) {
+                adj[accessors[p].setter](prop[i], adj[accessors[p].getter](prop[i]), 'start');
+              }
+            });
+          }
+        });
+      });
+      return m;
+    },
+    
+    /*
        Method: animate
     
-       Animates a <Graph> by interpolating some <Graph.Nodes> properties.
+       Animates a <Graph> by interpolating some <Graph.Node>, <Graph.Adjacence> or <Graph.Label> properties.
 
        Parameters:
 
-       opt - Animation options. This object contains as properties
+       opt - (object) Animation options. The object properties are described below
+       duration - (optional) Described in <Options.Fx>.
+       fps - (optional) Described in <Options.Fx>.
+       hideLabels - (optional|boolean) Whether to hide labels during the animation.
+       modes - (required|object) An object with animation modes (described below).
 
-       - _modes_ (required) An Array of animation types. Possible values are "linear", "polar", "moebius", "fade:nodes" and "fade:vertex".
-
-       "linear", "polar" and "moebius" animation options will interpolate <Graph.Nodes> "startPos" and "endPos" properties, storing the result in "pos".
+       Animation modes:
        
-       "fade:nodes" and "fade:vertex" animation options will interpolate <Graph.Nodes> and/or <Graph.Adjacence> "startAlpha" and "endAlpha" properties, storing the result in "alpha".
-
-       - _duration_ Duration (in milliseconds) of the Animation.
-       - _fps_ Frames per second.
-       - _hideLabels_ hide labels or not during the animation.
-
-       ...and other <Hypertree>, <RGraph> or <ST> controller methods.
+       Animation modes are strings representing different node/edge and graph properties that you'd like to animate. 
+       They are represented by an object that has as keys main categories of properties to animate and as values a list 
+       of these specific properties. The properties are described below
+       
+       position - Describes the way nodes' positions must be interpolated. Possible values are 'linear', 'polar' or 'moebius'.
+       node-property - Describes which Node properties will be interpolated. These properties can be any of the ones defined in <Options.Node>.
+       edge-property - Describes which Edge properties will be interpolated. These properties can be any the ones defined in <Options.Edge>.
+       label-property - Describes which Label properties will be interpolated. These properties can be any of the ones defined in <Options.Label> like color or size.
+       node-style - Describes which Node Canvas Styles will be interpolated. These are specific canvas properties like fillStyle, strokeStyle, lineWidth, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, etc.
+       edge-style - Describes which Edge Canvas Styles will be interpolated. These are specific canvas properties like fillStyle, strokeStyle, lineWidth, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, etc.
 
        Example:
        (start code js)
-        var rg = new RGraph(canvas, config); //can be also Hypertree or ST
-        rg.fx.animate({
-          modes: ['linear'],
-          hideLabels: false
-        }); 
+       var viz = new $jit.Viz(options);
+       //...tweak some Data, CanvasStyles or LabelData properties...
+       viz.fx.animate({
+         modes: {
+           'position': 'linear',
+           'node-property': ['width', 'height'],
+           'node-style': 'shadowColor',
+           'label-property': 'size'
+         },
+         hideLabels: false
+       });
+       //...can also be written like this...
+       viz.fx.animate({
+         modes: ['linear',
+                 'node-property:width:height',
+                 'node-style:shadowColor',
+                 'label-property:size'],
+         hideLabels: false
+       });
        (end code)
-       
-       
     */
     animate: function(opt, versor) {
-        var that = this,
-    viz = this.viz,
-    graph  = viz.graph,
-    GUtil = Graph.Util;
-    opt = $merge(viz.controller, opt || {}); 
-    
-        if(opt.hideLabels) this.hideLabels(true);
-        this.animation.setOptions($merge(opt, {
-            $animating: false,
-            compute: function(delta) {
-        var vector = versor? versor.scale(-delta) : null;
-        GUtil.eachNode(graph, function(node) { 
-                    for(var i=0; i<opt.modes.length; i++) {
-            that.Interpolator[opt.modes[i]](node, delta, vector);
-          } 
-                });
-                that.plot(opt, this.$animating);
-                this.$animating = true;
-            },
-            complete: function() {
-                GUtil.eachNode(graph, function(node) { 
-                    node.startPos.set(node.pos);
-                    node.startAlpha = node.alpha;
-                });
-                if(opt.hideLabels) that.hideLabels(false);
-                that.plot(opt);
-                opt.onComplete();
-                opt.onAfterCompute();
-            }       
-    })).start();
+      opt = $.merge(this.viz.config, opt || {});
+      var that = this,
+          viz = this.viz,
+          graph  = viz.graph,
+          interp = this.Interpolator,
+          animation =  opt.type === 'nodefx'? this.nodeFxAnimation : this.animation;
+      //prepare graph values
+      var m = this.prepare(opt.modes);
+      
+      //animate
+      if(opt.hideLabels) this.labels.hideLabels(true);
+      animation.setOptions($.extend(opt, {
+        $animating: false,
+        compute: function(delta) {
+          graph.eachNode(function(node) { 
+            for(var p in m) {
+              interp[p](node, m[p], delta, versor);
+            }
+          });
+          that.plot(opt, this.$animating, delta);
+          this.$animating = true;
+        },
+        complete: function() {
+          if(opt.hideLabels) that.labels.hideLabels(false);
+          that.plot(opt);
+          opt.onComplete();
+          //TODO(nico): This shouldn't be here!
+          //opt.onAfterCompute();
+        }       
+      })).start();
     },
     
     /*
+      nodeFx
+   
+      Apply animation to node properties like color, width, height, dim, etc.
+  
+      Parameters:
+  
+      options - Animation options. This object properties is described below
+      elements - The Elements to be transformed. This is an object that has a properties
+      
+      (start code js)
+      'elements': {
+        //can also be an array of ids
+        'id': 'id-of-node-to-transform',
+        //properties to be modified. All properties are optional.
+        'properties': {
+          'color': '#ccc', //some color
+          'width': 10, //some width
+          'height': 10, //some height
+          'dim': 20, //some dim
+          'lineWidth': 10 //some line width
+        } 
+      }
+      (end code)
+      
+      - _reposition_ Whether to recalculate positions and add a motion animation. 
+      This might be used when changing _width_ or _height_ properties in a <Layouts.Tree> like layout. Default's *false*.
+      
+      - _onComplete_ A method that is called when the animation completes.
+      
+      ...and all other <Graph.Plot.animate> options like _duration_, _fps_, _transition_, etc.
+  
+      Example:
+      (start code js)
+       var rg = new RGraph(canvas, config); //can be also Hypertree or ST
+       rg.fx.nodeFx({
+         'elements': {
+           'id':'mynodeid',
+           'properties': {
+             'color':'#ccf'
+           },
+           'transition': Trans.Quart.easeOut
+         }
+       });
+      (end code)    
+   */
+   nodeFx: function(opt) {
+     var viz = this.viz,
+         graph  = viz.graph,
+         animation = this.nodeFxAnimation,
+         options = $.merge(this.viz.config, {
+           'elements': {
+             'id': false,
+             'properties': {}
+           },
+           'reposition': false
+         });
+     opt = $.merge(options, opt || {}, {
+       onBeforeCompute: $.empty,
+       onAfterCompute: $.empty
+     });
+     //check if an animation is running
+     animation.stopTimer();
+     var props = opt.elements.properties;
+     //set end values for nodes
+     if(!opt.elements.id) {
+       graph.eachNode(function(n) {
+         for(var prop in props) {
+           n.setData(prop, props[prop], 'end');
+         }
+       });
+     } else {
+       var ids = $.splat(opt.elements.id);
+       $.each(ids, function(id) {
+         var n = graph.getNode(id);
+         if(n) {
+           for(var prop in props) {
+             n.setData(prop, props[prop], 'end');
+           }
+         }
+       });
+     }
+     //get keys
+     var propnames = [];
+     for(var prop in props) propnames.push(prop);
+     //add node properties modes
+     var modes = ['node-property:' + propnames.join(':')];
+     //set new node positions
+     if(opt.reposition) {
+       modes.push('linear');
+       viz.compute('end');
+     }
+     //animate
+     this.animate($.merge(opt, {
+       modes: modes,
+       type: 'nodefx'
+     }));
+   },
+
+    
+    /*
        Method: plot
     
        Plots a <Graph>.
 
        Parameters:
 
-       opt - _optional_ Plotting options.
+       opt - (optional) Plotting options. Most of them are described in <Options.Fx>.
 
        Example:
 
        (start code js)
-       var rg = new RGraph(canvas, config); //can be also Hypertree or ST
-       rg.fx.plot(); 
+       var viz = new $jit.Viz(options);
+       viz.fx.plot(); 
        (end code)
 
     */
-    plot: function(opt, animating) {
-        var viz = this.viz, 
-    aGraph = viz.graph, 
-    canvas = viz.canvas, 
-    id = viz.root, 
-    that = this, 
-    ctx = canvas.getCtx(), 
-    GUtil = Graph.Util;
-        opt = opt || this.viz.controller;
-    opt.clearCanvas && canvas.clear();
-        
-        var T = !!aGraph.getNode(id).visited;
-        GUtil.eachNode(aGraph, function(node) {
-            GUtil.eachAdjacency(node, function(adj) {
-        var nodeTo = adj.nodeTo;
-                if(!!nodeTo.visited === T && node.drawn && nodeTo.drawn) {
-                    !animating && opt.onBeforePlotLine(adj);
-                    ctx.save();
-                    ctx.globalAlpha = Math.min(Math.min(node.alpha, nodeTo.alpha), adj.alpha);
-                    that.plotLine(adj, canvas, animating);
-                    ctx.restore();
-                    !animating && opt.onAfterPlotLine(adj);
-                }
-            });
-            ctx.save();
-      if(node.drawn) {
-              ctx.globalAlpha = node.alpha;
-              !animating && opt.onBeforePlotNode(node);
-              that.plotNode(node, canvas, animating);
-              !animating && opt.onAfterPlotNode(node);
-      }
-            if(!that.labelsHidden && opt.withLabels) {
-        if(node.drawn && ctx.globalAlpha >= 0.95) {
-          that.plotLabel(canvas, node, opt);
-        } else {
-          that.hideLabel(node, false);
-        }
-      }
-            ctx.restore();
-            node.visited = !T;
-        });
+   plot: function(opt, animating) {
+     var viz = this.viz, 
+         aGraph = viz.graph, 
+         canvas = viz.canvas, 
+         id = viz.root, 
+         that = this, 
+         ctx = canvas.getCtx(), 
+         min = Math.min,
+         opt = opt || this.viz.controller;
+     
+     opt.clearCanvas && canvas.clear();
+       
+     var root = aGraph.getNode(id);
+     if(!root) return;
+     
+     var T = !!root.visited;
+     aGraph.eachNode(function(node) {
+       var nodeAlpha = node.getData('alpha');
+       node.eachAdjacency(function(adj) {
+         var nodeTo = adj.nodeTo;
+         if(!!nodeTo.visited === T && node.drawn && nodeTo.drawn) {
+           !animating && opt.onBeforePlotLine(adj);
+           that.plotLine(adj, canvas, animating);
+           !animating && opt.onAfterPlotLine(adj);
+         }
+       });
+       if(node.drawn) {
+         !animating && opt.onBeforePlotNode(node);
+         that.plotNode(node, canvas, animating);
+         !animating && opt.onAfterPlotNode(node);
+       }
+       if(!that.labelsHidden && opt.withLabels) {
+         if(node.drawn && nodeAlpha >= 0.95) {
+           that.labels.plotLabel(canvas, node, opt);
+         } else {
+           that.labels.hideLabel(node, false);
+         }
+       }
+       node.visited = !T;
+     });
     },
 
-    /*
-       Method: plotLabel
-    
-       Plots a label for a given node.
+  /*
+      Plots a Subtree.
+   */
+   plotTree: function(node, opt, animating) {
+       var that = this, 
+       viz = this.viz, 
+       canvas = viz.canvas,
+       config = this.config,
+       ctx = canvas.getCtx();
+       var nodeAlpha = node.getData('alpha');
+       node.eachSubnode(function(elem) {
+         if(opt.plotSubtree(node, elem) && elem.exist && elem.drawn) {
+             var adj = node.getAdjacency(elem.id);
+             !animating && opt.onBeforePlotLine(adj);
+             that.plotLine(adj, canvas, animating);
+             !animating && opt.onAfterPlotLine(adj);
+             that.plotTree(elem, opt, animating);
+         }
+       });
+       if(node.drawn) {
+           !animating && opt.onBeforePlotNode(node);
+           this.plotNode(node, canvas, animating);
+           !animating && opt.onAfterPlotNode(node);
+           if(!opt.hideLabels && opt.withLabels && nodeAlpha >= 0.95) 
+               this.labels.plotLabel(canvas, node, opt);
+           else 
+               this.labels.hideLabel(node, false);
+       } else {
+           this.labels.hideLabel(node, true);
+       }
+   },
 
-       Parameters:
-
-       canvas - A <Canvas> instance.
-       node - A <Graph.Node>.
-       controller - A configuration object. See also <Hypertree>, <RGraph>, <ST>.
-
-    */
-    plotLabel: function(canvas, node, controller) {
-    var id = node.id, tag = this.getLabel(id);
-        if(!tag && !(tag = document.getElementById(id))) {
-            tag = document.createElement('div');
-            var container = this.getLabelContainer();
-            container.appendChild(tag);
-            tag.id = id;
-            tag.className = 'node';
-            tag.style.position = 'absolute';
-            controller.onCreateLabel(tag, node);
-            this.labels[node.id] = tag;
-        }
-    this.placeLabel(tag, node, controller);
-    },
-  
   /*
        Method: plotNode
     
@@ -3268,137 +7184,728 @@
 
        Parameters:
        
-       node - A <Graph.Node>.
-       canvas - A <Canvas> element.
+       node - (object) A <Graph.Node>.
+       canvas - (object) A <Canvas> element.
 
     */
     plotNode: function(node, canvas, animating) {
-        var nconfig = this.node, data = node.data;
-        var cond = nconfig.overridable && data;
-        var width = cond && data.$lineWidth || nconfig.lineWidth;
-        var color = cond && data.$color || nconfig.color;
-        var ctx = canvas.getCtx();
-        
-        ctx.lineWidth = width;
-    ctx.fillStyle = color;
-    ctx.strokeStyle = color; 
+        var f = node.getData('type'), 
+            ctxObj = this.node.CanvasStyles;
+        if(f != 'none') {
+          var width = node.getData('lineWidth'),
+              color = node.getData('color'),
+              alpha = node.getData('alpha'),
+              ctx = canvas.getCtx();
+          ctx.save();
+          ctx.lineWidth = width;
+          ctx.fillStyle = ctx.strokeStyle = color;
+          ctx.globalAlpha = alpha;
+          
+          for(var s in ctxObj) {
+            ctx[s] = node.getCanvasStyle(s);
+          }
 
-        var f = node.data && node.data.$type || nconfig.type;
-        this.nodeTypes[f].call(this, node, canvas, animating);
+          this.nodeTypes[f].render.call(this, node, canvas, animating);
+          ctx.restore();
+        }
     },
     
     /*
        Method: plotLine
     
-       Plots a line.
+       Plots a <Graph.Adjacence>.
 
        Parameters:
 
-       adj - A <Graph.Adjacence>.
-       canvas - A <Canvas> instance.
+       adj - (object) A <Graph.Adjacence>.
+       canvas - (object) A <Canvas> instance.
 
     */
     plotLine: function(adj, canvas, animating) {
-        var econfig = this.edge, data = adj.data;
-        var cond = econfig.overridable && data;
-        var width = cond && data.$lineWidth || econfig.lineWidth;
-        var color = cond && data.$color || econfig.color;
-        var ctx = canvas.getCtx();
+      var f = adj.getData('type'),
+          ctxObj = this.edge.CanvasStyles;
+      if(f != 'none') {
+        var width = adj.getData('lineWidth'),
+            color = adj.getData('color'),
+            ctx = canvas.getCtx(),
+            nodeFrom = adj.nodeFrom,
+            nodeTo = adj.nodeTo;
         
+        ctx.save();
         ctx.lineWidth = width;
-        ctx.fillStyle = color;
-        ctx.strokeStyle = color; 
+        ctx.fillStyle = ctx.strokeStyle = color;
+        ctx.globalAlpha = Math.min(nodeFrom.getData('alpha'), 
+            nodeTo.getData('alpha'), 
+            adj.getData('alpha'));
+        
+        for(var s in ctxObj) {
+          ctx[s] = adj.getCanvasStyle(s);
+        }
 
-        var f = adj.data && adj.data.$type || econfig.type;
-        this.edgeTypes[f].call(this, adj, canvas, animating);
-    },    
+        this.edgeTypes[f].render.call(this, adj, canvas, animating);
+        ctx.restore();
+      }
+    }    
   
-  /*
-       Method: fitsInCanvas
+};
+
+/*
+  Object: Graph.Plot3D
+  
+  <Graph> 3D rendering and animation methods.
+  
+  Properties:
+  
+  nodeHelper - <NodeHelper> object.
+  edgeHelper - <EdgeHelper> object.
+
+*/
+Graph.Plot3D = $.merge(Graph.Plot, {
+  Interpolator: {
+    'linear': function(elem, props, delta) {
+      var from = elem.startPos.getc(true);
+      var to = elem.endPos.getc(true);
+      elem.pos.setc(this.compute(from.x, to.x, delta), 
+                    this.compute(from.y, to.y, delta),
+                    this.compute(from.z, to.z, delta));
+    }
+  },
+  
+  plotNode: function(node, canvas) {
+    if(node.getData('type') == 'none') return;
+    this.plotElement(node, canvas, {
+      getAlpha: function() {
+        return node.getData('alpha');
+      }
+    });
+  },
+  
+  plotLine: function(adj, canvas) {
+    if(adj.getData('type') == 'none') return;
+    this.plotElement(adj, canvas, {
+      getAlpha: function() {
+        return Math.min(adj.nodeFrom.getData('alpha'),
+                        adj.nodeTo.getData('alpha'),
+                        adj.getData('alpha'));
+      }
+    });
+  },
+  
+  plotElement: function(elem, canvas, opt) {
+    var gl = canvas.getCtx(),
+        viewMatrix = new Matrix4,
+        lighting = canvas.config.Scene.Lighting,
+        wcanvas = canvas.canvases[0],
+        program = wcanvas.program,
+        camera = wcanvas.camera;
     
+    if(!elem.geometry) {
+      elem.geometry = new O3D[elem.getData('type')];
+    }
+    elem.geometry.update(elem);
+    if(!elem.webGLVertexBuffer) {
+      var vertices = [],
+          faces = [],
+          normals = [],
+          vertexIndex = 0,
+          geom = elem.geometry;
+      
+      for(var i=0, vs=geom.vertices, fs=geom.faces, fsl=fs.length; i<fsl; i++) {
+        var face = fs[i],
+            v1 = vs[face.a],
+            v2 = vs[face.b],
+            v3 = vs[face.c],
+            v4 = face.d? vs[face.d] : false,
+            n = face.normal;
+        
+        vertices.push(v1.x, v1.y, v1.z);
+        vertices.push(v2.x, v2.y, v2.z);
+        vertices.push(v3.x, v3.y, v3.z);
+        if(v4) vertices.push(v4.x, v4.y, v4.z);
+            
+        normals.push(n.x, n.y, n.z);
+        normals.push(n.x, n.y, n.z);
+        normals.push(n.x, n.y, n.z);
+        if(v4) normals.push(n.x, n.y, n.z);
+            
+        faces.push(vertexIndex, vertexIndex +1, vertexIndex +2);
+        if(v4) {
+          faces.push(vertexIndex, vertexIndex +2, vertexIndex +3);
+          vertexIndex += 4;
+        } else {
+          vertexIndex += 3;
+        }
+      }
+      //create and store vertex data
+      elem.webGLVertexBuffer = gl.createBuffer();
+      gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLVertexBuffer);
+      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
+      //create and store faces index data
+      elem.webGLFaceBuffer = gl.createBuffer();
+      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elem.webGLFaceBuffer);
+      gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(faces), gl.STATIC_DRAW);
+      elem.webGLFaceCount = faces.length;
+      //calculate vertex normals and store them
+      elem.webGLNormalBuffer = gl.createBuffer();
+      gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLNormalBuffer);
+      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
+    }
+    viewMatrix.multiply(camera.matrix, elem.geometry.matrix);
+    //send matrix data
+    gl.uniformMatrix4fv(program.viewMatrix, false, viewMatrix.flatten());
+    gl.uniformMatrix4fv(program.projectionMatrix, false, camera.projectionMatrix.flatten());
+    //send normal matrix for lighting
+    var normalMatrix = Matrix4.makeInvert(viewMatrix);
+    normalMatrix.$transpose();
+    gl.uniformMatrix4fv(program.normalMatrix, false, normalMatrix.flatten());
+    //send color data
+    var color = $.hexToRgb(elem.getData('color'));
+    color.push(opt.getAlpha());
+    gl.uniform4f(program.color, color[0] / 255, color[1] / 255, color[2] / 255, color[3]);
+    //send lighting data
+    gl.uniform1i(program.enableLighting, lighting.enable);
+    if(lighting.enable) {
+      //set ambient light color
+      if(lighting.ambient) {
+        var acolor = lighting.ambient;
+        gl.uniform3f(program.ambientColor, acolor[0], acolor[1], acolor[2]);
+      }
+      //set directional light
+      if(lighting.directional) {
+        var dir = lighting.directional,
+            color = dir.color,
+            pos = dir.direction,
+            vd = new Vector3(pos.x, pos.y, pos.z).normalize().$scale(-1);
+        gl.uniform3f(program.lightingDirection, vd.x, vd.y, vd.z);
+        gl.uniform3f(program.directionalColor, color[0], color[1], color[2]);
+      }
+    }
+    //send vertices data
+    gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLVertexBuffer);
+    gl.vertexAttribPointer(program.position, 3, gl.FLOAT, false, 0, 0);
+    //send normals data
+    gl.bindBuffer(gl.ARRAY_BUFFER, elem.webGLNormalBuffer);
+    gl.vertexAttribPointer(program.normal, 3, gl.FLOAT, false, 0, 0);
+    //draw!
+    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elem.webGLFaceBuffer );
+    gl.drawElements(gl.TRIANGLES, elem.webGLFaceCount, gl.UNSIGNED_SHORT, 0);
+  }
+});
+
+
+/*
+ * File: Graph.Label.js
+ *
+*/
+
+/*
+   Object: Graph.Label
+
+   An interface for plotting/hiding/showing labels.
+
+   Description:
+
+   This is a generic interface for plotting/hiding/showing labels.
+   The <Graph.Label> interface is implemented in multiple ways to provide
+   different label types.
+
+   For example, the Graph.Label interface is implemented as <Graph.Label.HTML> to provide
+   HTML label elements. Also we provide the <Graph.Label.SVG> interface for SVG type labels. 
+   The <Graph.Label.Native> interface implements these methods with the native Canvas text rendering functions.
+   
+   All subclasses (<Graph.Label.HTML>, <Graph.Label.SVG> and <Graph.Label.Native>) implement the method plotLabel.
+*/
+
+Graph.Label = {};
+
+/*
+   Class: Graph.Label.Native
+
+   Implements labels natively, using the Canvas text API.
+*/
+Graph.Label.Native = new Class({
+    initialize: function(viz) {
+      this.viz = viz;
+    },
+
+    /*
+       Method: plotLabel
+
+       Plots a label for a given node.
+
+       Parameters:
+
+       canvas - (object) A <Canvas> instance.
+       node - (object) A <Graph.Node>.
+       controller - (object) A configuration object.
+       
+       Example:
+       
+       (start code js)
+       var viz = new $jit.Viz(options);
+       var node = viz.graph.getNode('nodeId');
+       viz.labels.plotLabel(viz.canvas, node, viz.config);
+       (end code)
+    */
+    plotLabel: function(canvas, node, controller) {
+      var ctx = canvas.getCtx();
+      var pos = node.pos.getc(true);
+
+      ctx.font = node.getLabelData('style') + ' ' + node.getLabelData('size') + 'px ' + node.getLabelData('family');
+      ctx.textAlign = node.getLabelData('textAlign');
+      ctx.fillStyle = ctx.strokeStyle = node.getLabelData('color');
+      ctx.textBaseline = node.getLabelData('textBaseline');
+
+      this.renderLabel(canvas, node, controller);
+    },
+
+    /*
+       renderLabel
+
+       Does the actual rendering of the label in the canvas. The default
+       implementation renders the label close to the position of the node, this
+       method should be overriden to position the labels differently.
+
+       Parameters:
+
+       canvas - A <Canvas> instance.
+       node - A <Graph.Node>.
+       controller - A configuration object. See also <Hypertree>, <RGraph>, <ST>.
+    */
+    renderLabel: function(canvas, node, controller) {
+      var ctx = canvas.getCtx();
+      var pos = node.pos.getc(true);
+      ctx.fillText(node.name, pos.x, pos.y + node.getData("height") / 2);
+    },
+
+    hideLabel: $.empty,
+    hideLabels: $.empty
+});
+
+/*
+   Class: Graph.Label.DOM
+
+   Abstract Class implementing some DOM label methods.
+
+   Implemented by:
+
+   <Graph.Label.HTML> and <Graph.Label.SVG>.
+
+*/
+Graph.Label.DOM = new Class({
+    //A flag value indicating if node labels are being displayed or not.
+    labelsHidden: false,
+    //Label container
+    labelContainer: false,
+    //Label elements hash.
+    labels: {},
+
+    /*
+       Method: getLabelContainer
+
+       Lazy fetcher for the label container.
+
+       Returns:
+
+       The label container DOM element.
+
+       Example:
+
+      (start code js)
+        var viz = new $jit.Viz(options);
+        var labelContainer = viz.labels.getLabelContainer();
+        alert(labelContainer.innerHTML);
+      (end code)
+    */
+    getLabelContainer: function() {
+      return this.labelContainer ?
+        this.labelContainer :
+        this.labelContainer = document.getElementById(this.viz.config.labelContainer);
+    },
+
+    /*
+       Method: getLabel
+
+       Lazy fetcher for the label element.
+
+       Parameters:
+
+       id - (string) The label id (which is also a <Graph.Node> id).
+
+       Returns:
+
+       The label element.
+
+       Example:
+
+      (start code js)
+        var viz = new $jit.Viz(options);
+        var label = viz.labels.getLabel('someid');
+        alert(label.innerHTML);
+      (end code)
+
+    */
+    getLabel: function(id) {
+      return (id in this.labels && this.labels[id] != null) ?
+        this.labels[id] :
+        this.labels[id] = document.getElementById(id);
+    },
+
+    /*
+       Method: hideLabels
+
+       Hides all labels (by hiding the label container).
+
+       Parameters:
+
+       hide - (boolean) A boolean value indicating if the label container must be hidden or not.
+
+       Example:
+       (start code js)
+        var viz = new $jit.Viz(options);
+        rg.labels.hideLabels(true);
+       (end code)
+
+    */
+    hideLabels: function (hide) {
+      var container = this.getLabelContainer();
+      if(hide)
+        container.style.display = 'none';
+      else
+        container.style.display = '';
+      this.labelsHidden = hide;
+    },
+
+    /*
+       Method: clearLabels
+
+       Clears the label container.
+
+       Useful when using a new visualization with the same canvas element/widget.
+
+       Parameters:
+
+       force - (boolean) Forces deletion of all labels.
+
+       Example:
+       (start code js)
+        var viz = new $jit.Viz(options);
+        viz.labels.clearLabels();
+        (end code)
+    */
+    clearLabels: function(force) {
+      for(var id in this.labels) {
+        if (force || !this.viz.graph.hasNode(id)) {
+          this.disposeLabel(id);
+          delete this.labels[id];
+        }
+      }
+    },
+
+    /*
+       Method: disposeLabel
+
+       Removes a label.
+
+       Parameters:
+
+       id - (string) A label id (which generally is also a <Graph.Node> id).
+
+       Example:
+       (start code js)
+        var viz = new $jit.Viz(options);
+        viz.labels.disposeLabel('labelid');
+       (end code)
+    */
+    disposeLabel: function(id) {
+      var elem = this.getLabel(id);
+      if(elem && elem.parentNode) {
+        elem.parentNode.removeChild(elem);
+      }
+    },
+
+    /*
+       Method: hideLabel
+
+       Hides the corresponding <Graph.Node> label.
+
+       Parameters:
+
+       node - (object) A <Graph.Node>. Can also be an array of <Graph.Nodes>.
+       show - (boolean) If *true*, nodes will be shown. Otherwise nodes will be hidden.
+
+       Example:
+       (start code js)
+        var rg = new $jit.Viz(options);
+        viz.labels.hideLabel(viz.graph.getNode('someid'), false);
+       (end code)
+    */
+    hideLabel: function(node, show) {
+      node = $.splat(node);
+      var st = show ? "" : "none", lab, that = this;
+      $.each(node, function(n) {
+        var lab = that.getLabel(n.id);
+        if (lab) {
+          lab.style.display = st;
+        }
+      });
+    },
+
+    /*
+       fitsInCanvas
+
        Returns _true_ or _false_ if the label for the node is contained in the canvas dom element or not.
 
        Parameters:
 
        pos - A <Complex> instance (I'm doing duck typing here so any object with _x_ and _y_ parameters will do).
        canvas - A <Canvas> instance.
-       
+
        Returns:
 
        A boolean value specifying if the label is contained in the <Canvas> DOM element or not.
 
     */
     fitsInCanvas: function(pos, canvas) {
-        var size = canvas.getSize();
-        if(pos.x >= size.width || pos.x < 0 
-            || pos.y >= size.height || pos.y < 0) return false;
-        return true;                    
+      var size = canvas.getSize();
+      if(pos.x >= size.width || pos.x < 0
+         || pos.y >= size.height || pos.y < 0) return false;
+       return true;
     }
-};
+});
 
+/*
+   Class: Graph.Label.HTML
+
+   Implements HTML labels.
+
+   Extends:
+
+   All <Graph.Label.DOM> methods.
+
+*/
+Graph.Label.HTML = new Class({
+    Implements: Graph.Label.DOM,
+
+    /*
+       Method: plotLabel
+
+       Plots a label for a given node.
+
+       Parameters:
+
+       canvas - (object) A <Canvas> instance.
+       node - (object) A <Graph.Node>.
+       controller - (object) A configuration object.
+       
+      Example:
+       
+       (start code js)
+       var viz = new $jit.Viz(options);
+       var node = viz.graph.getNode('nodeId');
+       viz.labels.plotLabel(viz.canvas, node, viz.config);
+       (end code)
+
+
+    */
+    plotLabel: function(canvas, node, controller) {
+      var id = node.id, tag = this.getLabel(id);
+
+      if(!tag && !(tag = document.getElementById(id))) {
+        tag = document.createElement('div');
+        var container = this.getLabelContainer();
+        tag.id = id;
+        tag.className = 'node';
+        tag.style.position = 'absolute';
+        controller.onCreateLabel(tag, node);
+        container.appendChild(tag);
+        this.labels[node.id] = tag;
+      }
+
+      this.placeLabel(tag, node, controller);
+    }
+});
+
+/*
+   Class: Graph.Label.SVG
+
+   Implements SVG labels.
+
+   Extends:
+
+   All <Graph.Label.DOM> methods.
+*/
+Graph.Label.SVG = new Class({
+    Implements: Graph.Label.DOM,
+
+    /*
+       Method: plotLabel
+
+       Plots a label for a given node.
+
+       Parameters:
+
+       canvas - (object) A <Canvas> instance.
+       node - (object) A <Graph.Node>.
+       controller - (object) A configuration object.
+       
+       Example:
+       
+       (start code js)
+       var viz = new $jit.Viz(options);
+       var node = viz.graph.getNode('nodeId');
+       viz.labels.plotLabel(viz.canvas, node, viz.config);
+       (end code)
+
+
+    */
+    plotLabel: function(canvas, node, controller) {
+      var id = node.id, tag = this.getLabel(id);
+      if(!tag && !(tag = document.getElementById(id))) {
+        var ns = 'http://www.w3.org/2000/svg';
+          tag = document.createElementNS(ns, 'svg:text');
+        var tspan = document.createElementNS(ns, 'svg:tspan');
+        tag.appendChild(tspan);
+        var container = this.getLabelContainer();
+        tag.setAttribute('id', id);
+        tag.setAttribute('class', 'node');
+        container.appendChild(tag);
+        controller.onCreateLabel(tag, node);
+        this.labels[node.id] = tag;
+      }
+      this.placeLabel(tag, node, controller);
+    }
+});
+
+
+
+Graph.Geom = new Class({
+
+  initialize: function(viz) {
+    this.viz = viz;
+    this.config = viz.config;
+    this.node = viz.config.Node;
+    this.edge = viz.config.Edge;
+  },
+  /*
+    Applies a translation to the tree.
+  
+    Parameters:
+  
+    pos - A <Complex> number specifying translation vector.
+    prop - A <Graph.Node> position property ('pos', 'start' or 'end').
+  
+    Example:
+  
+    (start code js)
+      st.geom.translate(new Complex(300, 100), 'end');
+    (end code)
+  */  
+  translate: function(pos, prop) {
+     prop = $.splat(prop);
+     this.viz.graph.eachNode(function(elem) {
+         $.each(prop, function(p) { elem.getPos(p).$add(pos); });
+     });
+  },
+  /*
+    Hides levels of the tree until it properly fits in canvas.
+  */  
+  setRightLevelToShow: function(node, canvas, callback) {
+     var level = this.getRightLevelToShow(node, canvas), 
+         fx = this.viz.labels,
+         opt = $.merge({
+           execShow:true,
+           execHide:true,
+           onHide: $.empty,
+           onShow: $.empty
+         }, callback || {});
+     node.eachLevel(0, this.config.levelsToShow, function(n) {
+         var d = n._depth - node._depth;
+         if(d > level) {
+             opt.onHide(n);
+             if(opt.execHide) {
+               n.drawn = false; 
+               n.exist = false;
+               fx.hideLabel(n, false);
+             }
+         } else {
+             opt.onShow(n);
+             if(opt.execShow) {
+               n.exist = true;
+             }
+         }
+     });
+     node.drawn= true;
+  },
+  /*
+    Returns the right level to show for the current tree in order to fit in canvas.
+  */  
+  getRightLevelToShow: function(node, canvas) {
+     var config = this.config;
+     var level = config.levelsToShow;
+     var constrained = config.constrained;
+     if(!constrained) return level;
+     while(!this.treeFitsInCanvas(node, canvas, level) && level > 1) { level-- ; }
+     return level;
+  }
+});
 
 /*
  * File: Loader.js
  * 
- * Provides methods for loading JSON data.
- *
- * Description:
- *
- * Provides the <Loader.loadJSON> method implemented by the main visualization classes to load JSON Trees and Graphs.
- * 
- * Implemented By: 
- * 
- * <ST>, <TM>, <Hypertree> and <RGraph> classes
- * 
  */
 
 /*
    Object: Loader
 
-   Provides static methods for loading JSON data.
-
-   Description:
-   
-   This object is extended by the main Visualization classes (<ST>, <Hypertree>, <TM> and <RGraph>)
-   in order to implement the <Loader.loadJSON> method. 
-   
-   The <Loader.loadJSON> method accepts JSON Trees and Graph objects. This will be explained in detail in the <Loader.loadJSON> method definition.
+   Provides methods for loading and serving JSON data.
 */
 var Loader = {
      construct: function(json) {
-        var isGraph = ($type(json) == 'array');
-        var ans = new Graph(this.graphOptions);
+        var isGraph = ($.type(json) == 'array');
+        var ans = new Graph(this.graphOptions, this.config.Node, this.config.Edge, this.config.Label);
         if(!isGraph) 
             //make tree
             (function (ans, json) {
                 ans.addNode(json);
-                for(var i=0, ch = json.children; i<ch.length; i++) {
+                if(json.children) {
+                  for(var i=0, ch = json.children; i<ch.length; i++) {
                     ans.addAdjacence(json, ch[i]);
                     arguments.callee(ans, ch[i]);
+                  }
                 }
             })(ans, json);
         else
             //make graph
             (function (ans, json) {
                 var getNode = function(id) {
-                    for(var w=0; w<json.length; w++) { 
-                      if(json[w].id == id) {
-                        return json[w];
-                      }
+                  for(var i=0, l=json.length; i<l; i++) {
+                    if(json[i].id == id) {
+                      return json[i];
                     }
-                    return undefined;
+                  }
+                  // The node was not defined in the JSON
+                  // Let's create it
+                  var newNode = {
+                		"id" : id,
+                		"name" : id
+                	};
+                  return ans.addNode(newNode);
                 };
 
-                for(var i=0; i<json.length; i++) {
-                    ans.addNode(json[i]);
-                    for(var j=0, adj = json[i].adjacencies; j<adj.length; j++) {
-                        var node = adj[j], data;
-                        if(typeof adj[j] != 'string') {
-                            data = node.data;
-                            node = node.nodeTo;
-                        }
-                        ans.addAdjacence(json[i], getNode(node), data);
+                for(var i=0, l=json.length; i<l; i++) {
+                  ans.addNode(json[i]);
+                  var adj = json[i].adjacencies;
+                  if (adj) {
+                    for(var j=0, lj=adj.length; j<lj; j++) {
+                      var node = adj[j], data = {};
+                      if(typeof adj[j] != 'string') {
+                        data = $.merge(node.data, {});
+                        node = node.nodeTo;
+                      }
+                      ans.addAdjacence(json[i], getNode(node), data);
                     }
+                  }
                 }
             })(ans, json);
 
@@ -3408,879 +7915,688 @@
     /*
      Method: loadJSON
     
-     Loads a JSON structure to the visualization. The JSON structure can be a JSON tree or graph structure.
+     Loads a JSON structure to the visualization. The JSON structure can be a JSON *tree* or *graph* structure.
      
-        A JSON tree or graph structure consists of nodes, each having as properties
-       - _id_ A unique identifier for the node
-       - _name_ A node's name
-       - _data_ The data property contains a hash (i.e {}) where you can store all 
-       the information you want about this node.
+      A JSON tree or graph structure consists of nodes, each having as properties
+       
+       id - (string) A unique identifier for the node
+       name - (string) A node's name
+       data - (object) The data optional property contains a hash (i.e {}) 
+       where you can store all the information you want about this node.
         
-        Hash keys prefixed with a dollar sign (i.e $) have special meaning. I will detail those properties below.
-      
-        For JSON tree structures, there's an extra property _children_ of type Array which contains the node's children.
+      For JSON *Tree* structures, there's an extra optional property *children* of type Array which contains the node's children.
       
       Example:
 
       (start code js)
         var json = {  
-            "id": "aUniqueIdentifier",  
-            "name": "usually a nodes name",  
-            "data": {
-                "some key": "some value",
-                "some other key": "some other value"
-             },  
-            "children": [ 'other nodes or empty' ]  
+          "id": "aUniqueIdentifier",  
+          "name": "usually a nodes name",  
+          "data": {
+            "some key": "some value",
+            "some other key": "some other value"
+           },  
+          "children": [ *other nodes or empty* ]  
         };  
       (end code)
         
-        JSON Graph structures consist of an array of nodes, each specifying the nodes to which the current node is connected.
+        JSON *Graph* structures consist of an array of nodes, each specifying the nodes to which the current node is connected. 
+        For JSON *Graph* structures, the *children* property is replaced by the *adjacencies* property.
         
-        For JSON Graph structures, the _children_ property is replaced by the _adjacencies_ property.
+        There are two types of *Graph* structures, *simple* and *extended* graph structures.
         
-        There are two types of Graph structures, _simple_ and _extended_ graph structures.
-        
-        For _simple_ Graph structures, the adjacencies property contains an array of strings, each specifying the 
+        For *simple* Graph structures, the adjacencies property contains an array of strings, each specifying the 
         id of the node connected to the main node.
         
         Example:
         
         (start code js)
         var json = [  
-            {  
-                "id": "aUniqueIdentifier",  
-                "name": "usually a nodes name",  
-                "data": {
-                    "some key": "some value",
-                    "some other key": "some other value"
-                 },  
-                "adjacencies": ["anotherUniqueIdentifier", "yetAnotherUniqueIdentifier", 'etc']  
-            },
+          {  
+            "id": "aUniqueIdentifier",  
+            "name": "usually a nodes name",  
+            "data": {
+              "some key": "some value",
+              "some other key": "some other value"
+             },  
+            "adjacencies": ["anotherUniqueIdentifier", "yetAnotherUniqueIdentifier", 'etc']  
+          },
 
-            'other nodes go here...' 
+          'other nodes go here...' 
         ];          
         (end code)
         
-        For _extended_ Graph structures, the adjacencies property contains an array of Adjacency objects that have as properties
-        - nodeTo The other node connected by this adjacency.
-        - data A data property, where we can store custom key/value information.
+        For *extended Graph structures*, the adjacencies property contains an array of Adjacency objects that have as properties
+        
+        nodeTo - (string) The other node connected by this adjacency.
+        data - (object) A data property, where we can store custom key/value information.
         
         Example:
         
         (start code js)
         var json = [  
+          {  
+            "id": "aUniqueIdentifier",  
+            "name": "usually a nodes name",  
+            "data": {
+              "some key": "some value",
+              "some other key": "some other value"
+             },  
+            "adjacencies": [  
             {  
-                "id": "aUniqueIdentifier",  
-                "name": "usually a nodes name",  
-                "data": {
-                    "some key": "some value",
-                    "some other key": "some other value"
-                 },  
-                "adjacencies": [  
-                {  
-                    nodeTo:"aNodeId",  
-                    data: {} //put whatever you want here  
-                },
-                'other adjacencies go here...'  
+              nodeTo:"aNodeId",  
+              data: {} //put whatever you want here  
             },
+            'other adjacencies go here...'  
+          },
 
-            'other nodes go here...' 
+          'other nodes go here...' 
         ];          
         (end code)
-        
-        Since all visualizations implement this method, this will be the entry point for JSON data for all visualizations. This method could be called like this
-        
-        Example:
-        
-        (start code js)
-        var ht = new Hypertree(canvas, config);
-        ht.loadJSON(json);
-        
-        var tm = new TM.Squarified(config);
-        tm.loadJSON(json);
-        
-        var st = new ST(canvas, config);
-        st.loadJSON(json);
-        
-        var rg = new RGraph(canvas, config);
-        rg.loadJSON(json);
-        
-        (end code)
-        
-       Parameters:
+       
+       About the data property:
+       
+       As described before, you can store custom data in the *data* property of JSON *nodes* and *adjacencies*. 
+       You can use almost any string as key for the data object. Some keys though are reserved by the toolkit, and 
+       have special meanings. This is the case for keys starting with a dollar sign, for example, *$width*.
+       
+       For JSON *node* objects, adding dollar prefixed properties that match the names of the options defined in 
+       <Options.Node> will override the general value for that option with that particular value. For this to work 
+       however, you do have to set *overridable = true* in <Options.Node>.
+       
+       The same thing is true for JSON adjacencies. Dollar prefixed data properties will alter values set in <Options.Edge> 
+       if <Options.Edge> has *overridable = true*.
+       
+       When loading JSON data into TreeMaps, the *data* property must contain a value for the *$area* key, 
+       since this is the value which will be taken into account when creating the layout. 
+       The same thing goes for the *$color* parameter.
+       
+       In JSON Nodes you can use also *$label-* prefixed properties to refer to <Options.Label> properties. For example, 
+       *$label-size* will refer to <Options.Label> size property. Also, in JSON nodes and adjacencies you can set 
+       canvas specific properties individually by using the *$canvas-* prefix. For example, *$canvas-shadowBlur* will refer 
+       to the *shadowBlur* property.
+       
+       These properties can also be accessed after loading the JSON data from <Graph.Nodes> and <Graph.Adjacences> 
+       by using <Accessors>. For more information take a look at the <Graph> and <Accessors> documentation.
+       
+       Finally, these properties can also be used to create advanced animations like with <Options.NodeStyles>. For more 
+       information about creating animations please take a look at the <Graph.Plot> and <Graph.Plot.animate> documentation.
+       
+       loadJSON Parameters:
     
-          json - A JSON Tree or Graph structure.
-          i - For Graph structures only. Sets the indexed node as root for the visualization.
+        json - A JSON Tree or Graph structure.
+        i - For Graph structures only. Sets the indexed node as root for the visualization.
 
     */
     loadJSON: function(json, i) {
-    this.json = json;
-    this.graph = this.construct(json);
-    if($type(json) != 'array'){
-      this.root = json.id;
-    } else {
-      this.root = json[i? i : 0].id;
+      this.json = json;
+      //if they're canvas labels erase them.
+      if(this.labels && this.labels.clearLabels) {
+        this.labels.clearLabels(true);
+      }
+      this.graph = this.construct(json);
+      if($.type(json) != 'array'){
+        this.root = json.id;
+      } else {
+        this.root = json[i? i : 0].id;
+      }
+    },
+    
+    /*
+      Method: toJSON
+   
+      Returns a JSON tree/graph structure from the visualization's <Graph>. 
+      See <Loader.loadJSON> for the graph formats available.
+      
+      See also:
+      
+      <Loader.loadJSON>
+      
+      Parameters:
+      
+      type - (string) Default's "tree". The type of the JSON structure to be returned. 
+      Possible options are "tree" or "graph".
+    */    
+    toJSON: function(type) {
+      type = type || "tree";
+      if(type == 'tree') {
+        var ans = {};
+        var rootNode = this.graph.getNode(this.root);
+        var ans = (function recTree(node) {
+          var ans = {};
+          ans.id = node.id;
+          ans.name = node.name;
+          ans.data = node.data;
+          var ch =[];
+          node.eachSubnode(function(n) {
+            ch.push(recTree(n));
+          });
+          ans.children = ch;
+          return ans;
+        })(rootNode);
+        return ans;
+      } else {
+        var ans = [];
+        var T = !!this.graph.getNode(this.root).visited;
+        this.graph.eachNode(function(node) {
+          var ansNode = {};
+          ansNode.id = node.id;
+          ansNode.name = node.name;
+          ansNode.data = node.data;
+          var adjs = [];
+          node.eachAdjacency(function(adj) {
+            var nodeTo = adj.nodeTo;
+            if(!!nodeTo.visited === T) {
+              var ansAdj = {};
+              ansAdj.nodeTo = nodeTo.id;
+              ansAdj.data = adj.data;
+              adjs.push(ansAdj);
+            }
+          });
+          ansNode.adjacencies = adjs;
+          ans.push(ansNode);
+          node.visited = !T;
+        });
+        return ans;
+      }
     }
+};
+
+
+
+/*
+ * File: Layouts.js
+ * 
+ * Implements base Tree and Graph layouts.
+ *
+ * Description:
+ *
+ * Implements base Tree and Graph layouts like Radial, Tree, etc.
+ * 
+ */
+
+/*
+ * Object: Layouts
+ * 
+ * Parent object for common layouts.
+ *
+ */
+var Layouts = $jit.Layouts = {};
+
+
+//Some util shared layout functions are defined here.
+var NodeDim = {
+  label: null,
+  
+  compute: function(graph, prop, opt) {
+    this.initializeLabel(opt);
+    var label = this.label, style = label.style;
+    graph.eachNode(function(n) {
+      var autoWidth  = n.getData('autoWidth'),
+          autoHeight = n.getData('autoHeight');
+      if(autoWidth || autoHeight) {
+        //delete dimensions since these are
+        //going to be overridden now.
+        delete n.data.$width;
+        delete n.data.$height;
+        delete n.data.$dim;
+        
+        var width  = n.getData('width'),
+            height = n.getData('height');
+        //reset label dimensions
+        style.width  = autoWidth? 'auto' : width + 'px';
+        style.height = autoHeight? 'auto' : height + 'px';
+        
+        //TODO(nico) should let the user choose what to insert here.
+        label.innerHTML = n.name;
+        
+        var offsetWidth  = label.offsetWidth,
+            offsetHeight = label.offsetHeight;
+        var type = n.getData('type');
+        if($.indexOf(['circle', 'square', 'triangle', 'star'], type) === -1) {
+          n.setData('width', offsetWidth);
+          n.setData('height', offsetHeight);
+        } else {
+          var dim = offsetWidth > offsetHeight? offsetWidth : offsetHeight;
+          n.setData('width', dim);
+          n.setData('height', dim);
+          n.setData('dim', dim); 
+        }
+      }
+    });
+  },
+  
+  initializeLabel: function(opt) {
+    if(!this.label) {
+      this.label = document.createElement('div');
+      document.body.appendChild(this.label);
+    }
+    this.setLabelStyles(opt);
+  },
+  
+  setLabelStyles: function(opt) {
+    $.extend(this.label.style, {
+      'visibility': 'hidden',
+      'position': 'absolute',
+      'width': 'auto',
+      'height': 'auto'
+    });
+    this.label.className = 'jit-autoadjust-label';
   }
 };
 
 
-
 /*
- * File: Animation.js
+ * Class: Layouts.Tree
  * 
- * Core <Animation> and <Trans> transition classes.
- *
-*/
+ * Implements a Tree Layout.
+ * 
+ * Implemented By:
+ * 
+ * <ST>
+ * 
+ * Inspired by:
+ * 
+ * Drawing Trees (Andrew J. Kennedy) <http://research.microsoft.com/en-us/um/people/akenn/fun/drawingtrees.pdf>
+ * 
+ */
+Layouts.Tree = (function() {
+  //Layout functions
+  var slice = Array.prototype.slice;
 
-/*
-   Object: Trans
-    
-     An object containing multiple type of transformations. 
-     
-     Based on:
-         
-     Easing and Transition animation methods are based in the MooTools Framework <http://mootools.net>. Copyright (c) 2006-2009 Valerio Proietti, <http://mad4milk.net/>. MIT license <http://mootools.net/license.txt>.
-
-     Used by:
-
-     <RGraph>, <Hypertree> and <ST> classes.
-
-     Description:
-
-     This object is used for specifying different animation transitions in the <RGraph>, <Hypertree> and <ST> classes.
-
-     There are many different type of animation transitions.
-
-     linear:
-
-     Displays a linear transition
-
-     >Trans.linear
-     
-     (see Linear.png)
-
-     Quad:
-
-     Displays a Quadratic transition.
-  
-     >Trans.Quad.easeIn
-     >Trans.Quad.easeOut
-     >Trans.Quad.easeInOut
-     
-    (see Quad.png)
-
-    Cubic:
-
-    Displays a Cubic transition.
-
-    >Trans.Cubic.easeIn
-    >Trans.Cubic.easeOut
-    >Trans.Cubic.easeInOut
-
-    (see Cubic.png)
-
-    Quart:
-
-    Displays a Quartetic transition.
-
-    >Trans.Quart.easeIn
-    >Trans.Quart.easeOut
-    >Trans.Quart.easeInOut
-
-    (see Quart.png)
-
-    Quint:
-
-    Displays a Quintic transition.
-
-    >Trans.Quint.easeIn
-    >Trans.Quint.easeOut
-    >Trans.Quint.easeInOut
-
-    (see Quint.png)
-
-    Expo:
-
-    Displays an Exponential transition.
-
-    >Trans.Expo.easeIn
-    >Trans.Expo.easeOut
-    >Trans.Expo.easeInOut
-
-    (see Expo.png)
-
-    Circ:
-
-    Displays a Circular transition.
-
-    >Trans.Circ.easeIn
-    >Trans.Circ.easeOut
-    >Trans.Circ.easeInOut
-
-    (see Circ.png)
-
-    Sine:
-
-    Displays a Sineousidal transition.
-
-    >Trans.Sine.easeIn
-    >Trans.Sine.easeOut
-    >Trans.Sine.easeInOut
-
-    (see Sine.png)
-
-    Back:
-
-    >Trans.Back.easeIn
-    >Trans.Back.easeOut
-    >Trans.Back.easeInOut
-
-    (see Back.png)
-
-    Bounce:
-
-    Bouncy transition.
-
-    >Trans.Bounce.easeIn
-    >Trans.Bounce.easeOut
-    >Trans.Bounce.easeInOut
-
-    (see Bounce.png)
-
-    Elastic:
-
-    Elastic curve.
-
-    >Trans.Elastic.easeIn
-    >Trans.Elastic.easeOut
-    >Trans.Elastic.easeInOut
-
-    (see Elastic.png)
-
-
-
-*/
-this.Trans = {
-    linear: function(p) { return p; }
-};
-
-(function() {
-
-  var makeTrans = function(transition, params){
-      params = $splat(params);
-      return $extend(transition, {
-          easeIn: function(pos){
-              return transition(pos, params);
-          },
-          easeOut: function(pos){
-              return 1 - transition(1 - pos, params);
-          },
-          easeInOut: function(pos){
-              return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2;
-          }
+  /*
+     Calculates the max width and height nodes for a tree level
+  */  
+  function getBoundaries(graph, config, level, orn, prop) {
+    var dim = config.Node;
+    var multitree = config.multitree;
+    if (dim.overridable) {
+      var w = -1, h = -1;
+      graph.eachNode(function(n) {
+        if (n._depth == level
+            && (!multitree || ('$orn' in n.data) && n.data.$orn == orn)) {
+          var dw = n.getData('width', prop);
+          var dh = n.getData('height', prop);
+          w = (w < dw) ? dw : w;
+          h = (h < dh) ? dh : h;
+        }
       });
-  };
-  
-  var transitions = {
+      return {
+        'width' : w < 0 ? dim.width : w,
+        'height' : h < 0 ? dim.height : h
+      };
+    } else {
+      return dim;
+    }
+  }
 
-      Pow: function(p, x){
-          return Math.pow(p, x[0] || 6);
-      },
-  
-      Expo: function(p){
-          return Math.pow(2, 8 * (p - 1));
-      },
-  
-      Circ: function(p){
-          return 1 - Math.sin(Math.acos(p));
-      },
-  
-      Sine: function(p){
-          return 1 - Math.sin((1 - p) * Math.PI / 2);
-      },
-  
-      Back: function(p, x){
-          x = x[0] || 1.618;
-          return Math.pow(p, 2) * ((x + 1) * p - x);
-      },
-  
-      Bounce: function(p){
-          var value;
-          for (var a = 0, b = 1; 1; a += b, b /= 2){
-              if (p >= (7 - 4 * a) / 11){
-                  value = b * b - Math.pow((11 - 6 * a - 11 * p) / 4, 2);
-                  break;
-              }
-          }
-          return value;
-      },
-  
-      Elastic: function(p, x){
-          return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
+
+  function movetree(node, prop, val, orn) {
+    var p = (orn == "left" || orn == "right") ? "y" : "x";
+    node.getPos(prop)[p] += val;
+  }
+
+
+  function moveextent(extent, val) {
+    var ans = [];
+    $.each(extent, function(elem) {
+      elem = slice.call(elem);
+      elem[0] += val;
+      elem[1] += val;
+      ans.push(elem);
+    });
+    return ans;
+  }
+
+
+  function merge(ps, qs) {
+    if (ps.length == 0)
+      return qs;
+    if (qs.length == 0)
+      return ps;
+    var p = ps.shift(), q = qs.shift();
+    return [ [ p[0], q[1] ] ].concat(merge(ps, qs));
+  }
+
+
+  function mergelist(ls, def) {
+    def = def || [];
+    if (ls.length == 0)
+      return def;
+    var ps = ls.pop();
+    return mergelist(ls, merge(ps, def));
+  }
+
+
+  function fit(ext1, ext2, subtreeOffset, siblingOffset, i) {
+    if (ext1.length <= i || ext2.length <= i)
+      return 0;
+
+    var p = ext1[i][1], q = ext2[i][0];
+    return Math.max(fit(ext1, ext2, subtreeOffset, siblingOffset, ++i)
+        + subtreeOffset, p - q + siblingOffset);
+  }
+
+
+  function fitlistl(es, subtreeOffset, siblingOffset) {
+    function $fitlistl(acc, es, i) {
+      if (es.length <= i)
+        return [];
+      var e = es[i], ans = fit(acc, e, subtreeOffset, siblingOffset, 0);
+      return [ ans ].concat($fitlistl(merge(acc, moveextent(e, ans)), es, ++i));
+    }
+    ;
+    return $fitlistl( [], es, 0);
+  }
+
+
+  function fitlistr(es, subtreeOffset, siblingOffset) {
+    function $fitlistr(acc, es, i) {
+      if (es.length <= i)
+        return [];
+      var e = es[i], ans = -fit(e, acc, subtreeOffset, siblingOffset, 0);
+      return [ ans ].concat($fitlistr(merge(moveextent(e, ans), acc), es, ++i));
+    }
+    ;
+    es = slice.call(es);
+    var ans = $fitlistr( [], es.reverse(), 0);
+    return ans.reverse();
+  }
+
+
+  function fitlist(es, subtreeOffset, siblingOffset, align) {
+    var esl = fitlistl(es, subtreeOffset, siblingOffset), esr = fitlistr(es,
+        subtreeOffset, siblingOffset);
+
+    if (align == "left")
+      esr = esl;
+    else if (align == "right")
+      esl = esr;
+
+    for ( var i = 0, ans = []; i < esl.length; i++) {
+      ans[i] = (esl[i] + esr[i]) / 2;
+    }
+    return ans;
+  }
+
+
+  function design(graph, node, prop, config, orn) {
+    var multitree = config.multitree;
+    var auxp = [ 'x', 'y' ], auxs = [ 'width', 'height' ];
+    var ind = +(orn == "left" || orn == "right");
+    var p = auxp[ind], notp = auxp[1 - ind];
+
+    var cnode = config.Node;
+    var s = auxs[ind], nots = auxs[1 - ind];
+
+    var siblingOffset = config.siblingOffset;
+    var subtreeOffset = config.subtreeOffset;
+    var align = config.align;
+
+    function $design(node, maxsize, acum) {
+      var sval = node.getData(s, prop);
+      var notsval = maxsize
+          || (node.getData(nots, prop));
+
+      var trees = [], extents = [], chmaxsize = false;
+      var chacum = notsval + config.levelDistance;
+      node.eachSubnode(function(n) {
+            if (n.exist
+                && (!multitree || ('$orn' in n.data) && n.data.$orn == orn)) {
+
+              if (!chmaxsize)
+                chmaxsize = getBoundaries(graph, config, n._depth, orn, prop);
+
+              var s = $design(n, chmaxsize[nots], acum + chacum);
+              trees.push(s.tree);
+              extents.push(s.extent);
+            }
+          });
+      var positions = fitlist(extents, subtreeOffset, siblingOffset, align);
+      for ( var i = 0, ptrees = [], pextents = []; i < trees.length; i++) {
+        movetree(trees[i], prop, positions[i], orn);
+        pextents.push(moveextent(extents[i], positions[i]));
       }
-  
-  };
-  
-  $each(transitions, function(val, key) {
-    Trans[key] = makeTrans(val);
-  });
+      var resultextent = [ [ -sval / 2, sval / 2 ] ]
+          .concat(mergelist(pextents));
+      node.getPos(prop)[p] = 0;
 
-  $each(['Quad', 'Cubic', 'Quart', 'Quint'], function(elem, i) {
-    Trans[elem] = makeTrans(function(p){
-            return Math.pow(p, [i + 2]);
+      if (orn == "top" || orn == "left") {
+        node.getPos(prop)[notp] = acum;
+      } else {
+        node.getPos(prop)[notp] = -acum;
+      }
+
+      return {
+        tree : node,
+        extent : resultextent
+      };
+    }
+
+    $design(node, false, 0);
+  }
+
+
+  return new Class({
+    /*
+    Method: compute
+    
+    Computes nodes' positions.
+
+     */
+    compute : function(property, computeLevels) {
+      var prop = property || 'start';
+      var node = this.graph.getNode(this.root);
+      $.extend(node, {
+        'drawn' : true,
+        'exist' : true,
+        'selected' : true
+      });
+      NodeDim.compute(this.graph, prop, this.config);
+      if (!!computeLevels || !("_depth" in node)) {
+        this.graph.computeLevels(this.root, 0, "ignore");
+      }
+      
+      this.computePositions(node, prop);
+    },
+
+    computePositions : function(node, prop) {
+      var config = this.config;
+      var multitree = config.multitree;
+      var align = config.align;
+      var indent = align !== 'center' && config.indent;
+      var orn = config.orientation;
+      var orns = multitree ? [ 'top', 'right', 'bottom', 'left' ] : [ orn ];
+      var that = this;
+      $.each(orns, function(orn) {
+        //calculate layout
+          design(that.graph, node, prop, that.config, orn, prop);
+          var i = [ 'x', 'y' ][+(orn == "left" || orn == "right")];
+          //absolutize
+          (function red(node) {
+            node.eachSubnode(function(n) {
+              if (n.exist
+                  && (!multitree || ('$orn' in n.data) && n.data.$orn == orn)) {
+
+                n.getPos(prop)[i] += node.getPos(prop)[i];
+                if (indent) {
+                  n.getPos(prop)[i] += align == 'left' ? indent : -indent;
+                }
+                red(n);
+              }
+            });
+          })(node);
         });
+    }
   });
   
 })();
 
 /*
-   Class: Animation
-    
-   A Class that can perform animations for generic objects.
-
-   If you are looking for animation transitions please take a look at the <Trans> object.
-
-   Used by:
-
-   <Graph.Plot>
-   
-   Based on:
-   
-   The Animation class is based in the MooTools Framework <http://mootools.net>. Copyright (c) 2006-2009 Valerio Proietti, <http://mad4milk.net/>. MIT license <http://mootools.net/license.txt>.
-
-*/
-
-var Animation = new Class({
-
-    initalize: function(options) {
-     this.setOptions(options);
-  },
-  
-  setOptions: function(options) {
-        var opt = {
-            duration: 2500,
-            fps: 40,
-            transition: Trans.Quart.easeInOut,
-            compute: $empty,
-            complete: $empty
-        };
-        this.opt = $merge(opt, options || {});
-    return this;
-  },
-  
-  getTime: function() {
-        return $time();
-    },
-    
-    step: function(){
-        var time = this.getTime(), opt = this.opt;
-        if (time < this.time + opt.duration){
-            var delta = opt.transition((time - this.time) / opt.duration);
-            opt.compute(delta);
-        } else {
-            this.timer = clearInterval(this.timer);
-            opt.compute(1);
-            opt.complete();
-        }
-    },
-
-    start: function(){
-        this.time = 0;
-        this.startTimer();
-        return this;
-    },
-
-    startTimer: function(){
-    var that = this, opt = this.opt;
-        if (this.timer) return false;
-        this.time = this.getTime() - this.time;
-        this.timer = setInterval((function () { that.step(); }), Math.round(1000 / opt.fps));
-        return true;
-    }
-});
-
-
-
-/*
  * File: Spacetree.js
- * 
- * Implements the <ST> class and other derived classes.
- *
- * Description:
- *
- * The main idea of the spacetree algorithm is to take the most common tree layout, and to expand nodes that are "context-related" .i.e lying on the path between the root node and a selected node. Useful animations to contract and expand nodes are also included.
- *
- * Inspired by:
- *
- * SpaceTree: Supporting Exploration in Large Node Link Tree, Design Evolution and Empirical Evaluation (Catherine Plaisant, Jesse Grosjean, Benjamin B. Bederson)
- *
- * <http://hcil.cs.umd.edu/trs/2002-05/2002-05.pdf>
- *
- * Disclaimer:
- *
- * This visualization was built from scratch, taking only the paper as inspiration, and only shares some features with the Spacetree.
- *
  */
 
 /*
-     Class: ST
+   Class: ST
+   
+  A Tree layout with advanced contraction and expansion animations.
      
-     The main ST class
+  Inspired by:
+ 
+  SpaceTree: Supporting Exploration in Large Node Link Tree, Design Evolution and Empirical Evaluation (Catherine Plaisant, Jesse Grosjean, Benjamin B. Bederson) 
+  <http://hcil.cs.umd.edu/trs/2002-05/2002-05.pdf>
+  
+  Drawing Trees (Andrew J. Kennedy) <http://research.microsoft.com/en-us/um/people/akenn/fun/drawingtrees.pdf>
+  
+  Note:
+ 
+  This visualization was built and engineered from scratch, taking only the papers as inspiration, and only shares some features with the visualization described in those papers.
+ 
+  Implements:
+  
+  All <Loader> methods
+  
+  Constructor Options:
+  
+  Inherits options from
+  
+  - <Options.Canvas>
+  - <Options.Controller>
+  - <Options.Tree>
+  - <Options.Node>
+  - <Options.Edge>
+  - <Options.Label>
+  - <Options.Events>
+  - <Options.Tips>
+  - <Options.NodeStyles>
+  - <Options.Navigation>
+  
+  Additionally, there are other parameters and some default values changed
+  
+  constrained - (boolean) Default's *true*. Whether to show the entire tree when loaded or just the number of levels specified by _levelsToShow_.
+  levelsToShow - (number) Default's *2*. The number of levels to show for a subtree. This number is relative to the selected node.
+  levelDistance - (number) Default's *30*. The distance between two consecutive levels of the tree.
+  Node.type - Described in <Options.Node>. Default's set to *rectangle*.
+  offsetX - (number) Default's *0*. The x-offset distance from the selected node to the center of the canvas.
+  offsetY - (number) Default's *0*. The y-offset distance from the selected node to the center of the canvas.
+  duration - Described in <Options.Fx>. It's default value has been changed to *700*.
+  
+  Instance Properties:
+  
+  canvas - Access a <Canvas> instance.
+  graph - Access a <Graph> instance.
+  op - Access a <ST.Op> instance.
+  fx - Access a <ST.Plot> instance.
+  labels - Access a <ST.Label> interface implementation.
 
-     Extends:
-
-     <Loader>
-
-     Parameters:
-
-     canvas - A <Canvas> Class
-     config - A configuration/controller object.
-
-     Configuration:
-    
-     The configuration object can have the following properties (all properties are optional and have a default value)
-      
-     *General*
-
-     - _levelsToShow_ Depth of the plotted tree. The plotted tree will be pruned in order to fit the specified depth if constrained=true. Default's 2.
-     - _constrained_ If true, the algorithm will try to plot only the part of the tree that fits the Canvas.
-     - _subtreeOffset_ Separation offset between subtrees. Default's 8.
-     - _siblingOffset_ Separation offset between siblings. Default's 5.
-     - _levelDistance_ Distance between levels. Default's 30.
-     - _orientation_ Sets the orientation layout. Implemented orientations are _left_ (the root node will be placed on the left side of the screen), _top_ (the root node will be placed on top of the screen), _bottom_ and _right_. Default's "left".
-     - _align_ Whether the tree alignment is left, center or right.
-     - _indent_ Used when alignment is left or right and shows an indentation between parent and children. Default's 10.
-     - _withLabels_ Whether the visualization should use/create labels or not. Default's *true*.
-
-     *Node*
-     
-     Customize the visualization nodes' shape, color, and other style properties.
-
-     - _Node_
-
-     This object has as properties
-
-     - _overridable_ Determine whether or not nodes properties can be overriden by a particular node. Default's false.
-
-     If given a JSON tree or graph, a node _data_ property contains properties which are the same as defined here but prefixed with 
-     a dollar sign (i.e $), the node properties will override the global node properties.
-
-     - _type_ Node type (shape). Possible options are "none", "square", "rectangle", "ellipse" and "circle". Default's "rectangle".
-     - _color_ Node color. Default's '#ccb'.
-     - _lineWidth_ Line width. If nodes aren't drawn with strokes then this property won't be of any use. Default's 1.
-     - _height_ Node height. Used for plotting rectangular nodes. _Width_ and _height_ properties are also used as bounding box for nodes of different shapes. 
-     This means that for all shapes you'd have to make sure that the node's shape is contained in the bounding box defined by _width_ and _height_ parameters. Default's 20.
-     - _width_ Node width. Used for plotting rectangular nodes and for calculating a node's bounding box. Default's 90.
-     - _dim_ An extra parameter used by other complex shapes such as square and circle to determine the shape's diameter. 
-     Please notice that even if these complex shapes (square, circle) only use _dim_ for calculating it's diameter property, the complex shape must be 
-     contained in the bounding box calculated with the _width_ and _height_ parameters. Default's 15.
-     - _align_ Defines a node's alignment. Possible values are "center", "left", "right". Default's "center".
-
-     *Edge*
-
-     Customize the visualization edges' shape, color, and other style properties.
-
-     - _Edge_
-
-     This object has as properties
-
-     - _overridable_ Determine whether or not edges properties can be overriden by a particular edge object. Default's false.
-
-     If given a JSON _complex_ graph (defined in <Loader.loadJSON>), an adjacency _data_ property contains properties which are the same as defined here but prefixed with 
-     a dollar sign (i.e $), the adjacency properties will override the global edge properties.
-
-     - _type_ Edge type (shape). Possible options are "none", "line", "quadratic:begin", "quadratic:end", "bezier" and "arrow". Default's "line".
-     - _color_ Edge color. Default's '#ccb'.
-     - _lineWidth_ Line width. If edges aren't drawn with strokes then this property won't be of any use. Default's 1.
-     - _dim_ An extra parameter used by other complex shapes such as quadratic, arrow and bezier to determine the shape's diameter. 
-
-     *Animations*
-
-     - _duration_ Duration of the animation in milliseconds. Default's 700.
-     - _fps_ Frames per second. Default's 25.
-     - _transition_ One of the transitions defined in the <Animation> class. Default's Quart.easeInOut.
-     - _clearCanvas_ Whether to clear canvas on each animation frame or not. Default's true.
-     
-    *Controller options*
-
-    You can also implement controller functions inside the configuration object. This functions are
-    
-    - _onBeforeCompute(node)_ This method is called right before performing all computation and animations to the JIT visualization.
-    - _onAfterCompute()_ This method is triggered right after all animations or computations for the JIT visualizations ended.
-    - _onCreateLabel(domElement, node)_ This method receives the label dom element as first parameter, and the corresponding <Graph.Node> as second parameter. This method will only be called on label creation. Note that a <Graph.Node> is a node from the input tree/graph you provided to the visualization. If you want to know more about what kind of JSON tree/graph format is used to feed the visualizations, you can take a look at <Loader.loadJSON>. This method proves useful when adding events to the labels used by the JIT.
-    - _onPlaceLabel(domElement, node)_ This method receives the label dom element as first parameter and the corresponding <Graph.Node> as second parameter. This method is called each time a label has been placed on the visualization, and thus it allows you to update the labels properties, such as size or position. Note that onPlaceLabel will be triggered after updating the labels positions. That means that, for example, the left and top css properties are already updated to match the nodes positions.
-    - _onBeforePlotNode(node)_ This method is triggered right before plotting a given node. The _node_ parameter is the <Graph.Node> to be plotted. 
-This method is useful for changing a node style right before plotting it.
-    - _onAfterPlotNode(node)_ This method is triggered right after plotting a given node. The _node_ parameter is the <Graph.Node> plotted.
-    - _onBeforePlotLine(adj)_ This method is triggered right before plotting an edge. The _adj_ parameter is a <Graph.Adjacence> object. 
-This method is useful for adding some styles to a particular edge before being plotted.
-    - _onAfterPlotLine(adj)_ This method is triggered right after plotting an edge. The _adj_ parameter is the <Graph.Adjacence> plotted.
-    - _request(nodeId, level, onComplete)_ This method is used for buffering information into the visualization. When clicking on an empty node,
- the visualization will make a request for this node's subtree, specifying a given level for this subtree. Once this request is completed the _onComplete_ 
-object must be called with the given result.
-
-    Example:
-
-    Here goes a complete example. In most cases you won't be forced to implement all properties and methods. In fact, 
-    all configuration properties  have the default value assigned.
-
-    I won't be instanciating a <Canvas> class here. If you want to know more about instanciating a <Canvas> class 
-    please take a look at the <Canvas> class documentation.
-
-    (start code js)
-      var st = new ST(canvas, {
-        orientation: "left",
-        levelsToShow: 2,
-        subtreeOffset: 8,
-        siblingOffset: 5,
-        levelDistance: 30,
-        withLabels: true,
-        align: "center",
-        multitree: false,
-        indent: 10,
-        Node: {
-          overridable: false,
-          type: 'rectangle',
-          color: '#ccb',
-          lineWidth: 1,
-          height: 20,
-          width: 90,
-          dim: 15,
-          align: "center"
-        },
-        Edge: {
-          overridable: false,
-          type: 'line',
-          color: '#ccc',
-          dim: 15,
-          lineWidth: 1
-        },
-        duration: 700,
-        fps: 25,
-        transition: Trans.Quart.easeInOut,
-        clearCanvas: true,
-        
-        onBeforeCompute: function(node) {
-          //do something onBeforeCompute
-        },
-        onAfterCompute:  function () {
-          //do something onAfterCompute
-        },
-        onCreateLabel:   function(domElement, node) {
-          //do something onCreateLabel
-        },
-        onPlaceLabel:    function(domElement, node) {
-          //do something onPlaceLabel
-        },
-        onBeforePlotNode:function(node) {
-          //do something onBeforePlotNode
-        },
-        onAfterPlotNode: function(node) {
-          //do something onAfterPlotNode
-        },
-        onBeforePlotLine:function(adj) {
-          //do something onBeforePlotLine
-        },
-        onAfterPlotLine: function(adj) {
-          //do something onAfterPlotLine
-        },
-        request:         false
-
-      });
-    (end code)
-
-    Instance Properties:
-
-    - _graph_ Access a <Graph> instance.
-    - _op_ Access a <ST.Op> instance.
-    - _fx_ Access a  <ST.Plot> instance.
  */
 
-(function () {
-
-//Layout functions
-var slice = Array.prototype.slice;
-
-/*
-   Calculates the max width and height nodes for a tree level
-*/  
-function getBoundaries(graph, config, level, orn) {
-    var dim = config.Node, GUtil = Graph.Util;
-    var multitree = config.multitree;
-    if(dim.overridable) {
-        var w = -1, h = -1;
-        GUtil.eachNode(graph, function(n) {
-            if(n._depth == level && (!multitree || 
-             		('$orn' in n.data) && 
-             		n.data.$orn == orn)) {
-                var dw = n.data.$width || dim.width;
-                var dh = n.data.$height || dim.height;
-                w = (w < dw)? dw : w;
-                h = (h < dh)? dh : h;
-            }
-        });
-        return {
-            'width':  w < 0? dim.width : w,
-            'height': h < 0? dim.height : h
-        };
-    } else {
-        return dim;
-    }
-};
-
- function movetree(node, prop, val, orn) {
-   var p = (orn == "left" || orn == "right")? "y" : "x";
-   node[prop][p] += val;
- };
- 
- function moveextent(extent, val) {
-     var ans = [];
-     $each(extent, function(elem) {
-         elem = slice.call(elem);
-         elem[0] += val;
-         elem[1] += val;
-         ans.push(elem);
-     });
-     return ans;
- };
- 
- function merge(ps, qs) {
-   if(ps.length == 0) return qs;
-   if(qs.length == 0) return ps;
-   var p = ps.shift(), q = qs.shift();
-   return [[p[0], q[1]]].concat(merge(ps, qs));  
- };
- 
- function mergelist(ls, def) {
-     def = def || [];
-     if(ls.length == 0) return def; 
-     var ps = ls.pop();
-     return mergelist(ls, merge(ps, def));
- };
- 
- function fit(ext1, ext2, subtreeOffset, siblingOffset, i) {
-     if(ext1.length <= i || 
-        ext2.length <= i) return 0;
-     
-     var p = ext1[i][1], q = ext2[i][0];
-     return  Math.max(fit(ext1, ext2, subtreeOffset, siblingOffset, ++i) 
-    		 + subtreeOffset, p - q + siblingOffset);
- };
- 
- function fitlistl(es, subtreeOffset, siblingOffset) {
-   function $fitlistl(acc, es, i) {
-       if(es.length <= i) return [];
-       var e = es[i], ans = fit(acc, e, subtreeOffset, siblingOffset, 0);
-       return [ans].concat($fitlistl(merge(acc, moveextent(e, ans)), es, ++i));
-   };
-   return $fitlistl([], es, 0);
- };
- 
- function fitlistr(es, subtreeOffset, siblingOffset) {
-   function $fitlistr(acc, es, i) {
-       if(es.length <= i) return [];
-       var e = es[i], ans = -fit(e, acc, subtreeOffset, siblingOffset, 0);
-       return [ans].concat($fitlistr(merge(moveextent(e, ans), acc), es, ++i));
-   };
-   es = slice.call(es);
-   var ans = $fitlistr([], es.reverse(), 0);
-   return ans.reverse();
- };
- 
- function fitlist(es, subtreeOffset, siblingOffset, align) {
-    var esl = fitlistl(es, subtreeOffset, siblingOffset),
-        esr = fitlistr(es, subtreeOffset, siblingOffset);
-    
-    if(align == "left")
-    	esr = esl;
-    else if(align == "right")
-    	esl = esr;
-    
-    for(var i = 0, ans = []; i < esl.length; i++) {
-        ans[i] = (esl[i] + esr[i]) / 2;
-    }
-    return ans;
- };
- 
- function design(graph, node, prop, config, orn) {
-     var multitree = config.multitree;
-     var auxp = ['x', 'y'], auxs = ['width', 'height'];
-     var ind = +(orn == "left" || orn == "right");
-     var p = auxp[ind], notp = auxp[1 - ind];
-     
-     var cnode = config.Node;
-     var s = auxs[ind], nots = auxs[1 - ind];
-
-     var siblingOffset = config.siblingOffset;
-     var subtreeOffset = config.subtreeOffset;
-     var align = config.align;
-     
-     var GUtil = Graph.Util;
-
-     function $design(node, maxsize, acum) {
-         var sval = (cnode.overridable && node.data["$" + s]) || cnode[s];
-         var notsval = maxsize || ((cnode.overridable && node.data["$" + nots]) || cnode[nots]);
-         
-         var trees = [], extents = [], chmaxsize = false;
-         var chacum = notsval + config.levelDistance;
-         GUtil.eachSubnode(node, function(n) {
-             if(n.exist && (!multitree ||  
-             		('$orn' in n.data) && 
-             		n.data.$orn == orn)) {
-            	 
-                 if(!chmaxsize) 
-                    chmaxsize = getBoundaries(graph, config, n._depth, orn);
-                 
-                 var s = $design(n, chmaxsize[nots], acum + chacum);
-                 trees.push(s.tree);
-                 extents.push(s.extent);
-             }
-         });
-         var positions = fitlist(extents, subtreeOffset, siblingOffset, align);
-         for(var i=0, ptrees = [], pextents = []; i < trees.length; i++) {
-             movetree(trees[i], prop, positions[i], orn);
-             pextents.push(moveextent(extents[i], positions[i]));
-         }
-         var resultextent = [[-sval/2, sval/2]].concat(mergelist(pextents));
-         node[prop][p] = 0;
-
-         if (orn == "top" || orn == "left") {
-            node[prop][notp] = acum;
-         } else {
-            node[prop][notp] = -acum;
-         }
-
-         return {
-           tree: node,
-           extent: resultextent  
-         };
-     };
-     $design(node, false, 0);
- };
-
-
-
-this.ST= (function() {
-    //Define some private methods first...
-    //Nodes in path
+$jit.ST= (function() {
+    // Define some private methods first...
+    // Nodes in path
     var nodesInPath = [];
-    //Nodes to contract
+    // Nodes to contract
     function getNodesToHide(node) {
       node = node || this.clickedNode;
-      var Geom = this.geom, GUtil = Graph.Util;
+      if(!this.config.constrained) {
+        return [];
+      }
+      var Geom = this.geom;
       var graph = this.graph;
       var canvas = this.canvas;
       var level = node._depth, nodeArray = [];
-	  GUtil.eachNode(graph, function(n) {
-        if(n.exist && !n.selected) {
-            if(GUtil.isDescendantOf(n, node.id)) {
-              if(n._depth <= level) nodeArray.push(n);
-            } else {
-              nodeArray.push(n);
-            }
-        }
-	  });
-	  var leafLevel = Geom.getRightLevelToShow(node, canvas);
-	  GUtil.eachLevel(node, leafLevel, leafLevel, function(n) {
-        if(n.exist && !n.selected) nodeArray.push(n);
-	  });
-	    
-	  for (var i = 0; i < nodesInPath.length; i++) {
-	    var n = this.graph.getNode(nodesInPath[i]);
-	    if(!GUtil.isDescendantOf(n, node.id)) {
-	      nodeArray.push(n);
-	    }
-	  } 
-	  return nodeArray;       
+  	  graph.eachNode(function(n) {
+          if(n.exist && !n.selected) {
+              if(n.isDescendantOf(node.id)) {
+                if(n._depth <= level) nodeArray.push(n);
+              } else {
+                nodeArray.push(n);
+              }
+          }
+  	  });
+  	  var leafLevel = Geom.getRightLevelToShow(node, canvas);
+  	  node.eachLevel(leafLevel, leafLevel, function(n) {
+          if(n.exist && !n.selected) nodeArray.push(n);
+  	  });
+  	    
+  	  for (var i = 0; i < nodesInPath.length; i++) {
+  	    var n = this.graph.getNode(nodesInPath[i]);
+  	    if(!n.isDescendantOf(node.id)) {
+  	      nodeArray.push(n);
+  	    }
+  	  } 
+  	  return nodeArray;       
     };
-    //Nodes to expand
+    // Nodes to expand
      function getNodesToShow(node) {
-        var nodeArray= [], GUtil = Graph.Util, config = this.config;
+        var nodeArray = [], config = this.config;
         node = node || this.clickedNode;
-        GUtil.eachLevel(this.clickedNode, 0, config.levelsToShow, function(n) {
+        this.clickedNode.eachLevel(0, config.levelsToShow, function(n) {
             if(config.multitree && !('$orn' in n.data) 
-            		&& GUtil.anySubnode(n, function(ch){ return ch.exist && !ch.drawn; })) {
+            		&& n.anySubnode(function(ch){ return ch.exist && !ch.drawn; })) {
             	nodeArray.push(n);
-            } else if(n.drawn && !GUtil.anySubnode(n, "drawn")) {
-        		nodeArray.push(n);
+            } else if(n.drawn && !n.anySubnode("drawn")) {
+              nodeArray.push(n);
             }
         });
         return nodeArray;
      };
-    //Now define the actual class.    
+    // Now define the actual class.
     return new Class({
     
-        Implements: Loader,
+        Implements: [Loader, Extras, Layouts.Tree],
         
-        initialize: function(canvas, controller) {
-            var innerController = {
-                onBeforeCompute: $empty,
-                onAfterCompute:  $empty,
-                onCreateLabel:   $empty,
-                onPlaceLabel:    $empty,
-                onComplete:      $empty,
-                onBeforePlotNode:$empty,
-                onAfterPlotNode: $empty,
-                onBeforePlotLine:$empty,
-                onAfterPlotLine: $empty,
-                request:         false
-            };
-            
-            var config= {
-                orientation: "left",
-                labelContainer: canvas.id + '-label',
+        initialize: function(controller) {            
+          var $ST = $jit.ST;
+          
+          var config= {
                 levelsToShow: 2,
-                subtreeOffset: 8,
-                siblingOffset: 5,
                 levelDistance: 30,
-                withLabels: true,
-                clearCanvas: true,
-                align: "center",
-                indent:10,
-                multitree: false,
-                constrained: true,
-                
+                constrained: true,                
                 Node: {
-                    overridable: false,
-                    type: 'rectangle',
-                    color: '#ccb',
-                    lineWidth: 1,
-                    height: 20,
-                    width: 90,
-                    dim: 15,
-                    align: "center"
-                },
-                Edge: {
-                    overridable: false,
-                    type: 'line',
-                    color: '#ccc',
-                    dim: 15,
-                    lineWidth: 1
+                  type: 'rectangle'
                 },
                 duration: 700,
-                fps: 25,
-                transition: Trans.Quart.easeInOut
+                offsetX: 0,
+                offsetY: 0
             };
             
-            this.controller = this.config = $merge(config, innerController, controller);
-            this.canvas = canvas;
+            this.controller = this.config = $.merge(
+                Options("Canvas", "Fx", "Tree", "Node", "Edge", "Controller", 
+                    "Tips", "NodeStyles", "Events", "Navigation", "Label"), config, controller);
+
+            var canvasConfig = this.config;
+            if(canvasConfig.useCanvas) {
+              this.canvas = canvasConfig.useCanvas;
+              this.config.labelContainer = this.canvas.id + '-label';
+            } else {
+              if(canvasConfig.background) {
+                canvasConfig.background = $.merge({
+                  type: 'Circles'
+                }, canvasConfig.background);
+              }
+              this.canvas = new Canvas(this, canvasConfig);
+              this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
+            }
+
             this.graphOptions = {
-                'complex': true
+                'klass': Complex
             };
-            this.graph = new Graph(this.graphOptions);
-            this.fx = new ST.Plot(this);
-            this.op = new ST.Op(this);
-            this.group = new ST.Group(this);
-            this.geom = new ST.Geom(this);
+            this.graph = new Graph(this.graphOptions, this.config.Node, this.config.Edge);
+            this.labels = new $ST.Label[canvasConfig.Label.type](this);
+            this.fx = new $ST.Plot(this, $ST);
+            this.op = new $ST.Op(this);
+            this.group = new $ST.Group(this);
+            this.geom = new $ST.Geom(this);
             this.clickedNode=  null;
-            
+            // initialize extras
+            this.initializeExtras();
         },
     
         /*
          Method: plot
         
-         Plots the tree. Usually this method is called right after computing nodes' positions.
+         Plots the <ST>. This is a shortcut to *fx.plot*.
 
         */  
         plot: function() { this.fx.plot(this.controller); },
@@ -4293,9 +8609,9 @@
 
          Parameters:
 
-        pos - The new tree orientation. Possible values are "top", "left", "right" and "bottom".
-        method - Set this to "animate" if you want to animate the tree when switching its position. You can also set this parameter to "replot" to just replot the subtree.
-        onComplete - _optional_ This callback is called once the "switching" animation is complete.
+        pos - (string) The new tree orientation. Possible values are "top", "left", "right" and "bottom".
+        method - (string) Set this to "animate" if you want to animate the tree when switching its position. You can also set this parameter to "replot" to just replot the subtree.
+        onComplete - (optional|object) This callback is called once the "switching" animation is complete.
 
          Example:
 
@@ -4314,7 +8630,7 @@
               this.contract({
                   onComplete: function() {
                       Geom.switchOrientation(pos);
-                      that.compute('endPos', false);
+                      that.compute('end', false);
                       Plot.busy = false;
                       if(method == 'animate') {
                     	  that.onClick(that.clickedNode.id, onComplete);  
@@ -4333,9 +8649,9 @@
 
         Parameters:
 
-       align - The new tree alignment. Possible values are "left", "center" and "right".
-       method - Set this to "animate" if you want to animate the tree after aligning its position. You can also set this parameter to "replot" to just replot the subtree.
-       onComplete - _optional_ This callback is called once the "switching" animation is complete.
+       align - (string) The new tree alignment. Possible values are "left", "center" and "right".
+       method - (string) Set this to "animate" if you want to animate the tree after aligning its position. You can also set this parameter to "replot" to just replot the subtree.
+       onComplete - (optional|object) This callback is called once the "switching" animation is complete.
 
         Example:
 
@@ -4359,17 +8675,17 @@
        /*
         Method: addNodeInPath
        
-        Adds a node to the current path as selected node. This node will be visible (as in non-collapsed) at all times.
+        Adds a node to the current path as selected node. The selected node will be visible (as in non-collapsed) at all times.
         
 
         Parameters:
 
-       id - A <Graph.Node> id.
+       id - (string) A <Graph.Node> id.
 
         Example:
 
         (start code js)
-          st.addNodeInPath("somenodeid");
+          st.addNodeInPath("nodeId");
         (end code)
        */  
        addNodeInPath: function(id) {
@@ -4382,9 +8698,9 @@
       
        Removes all nodes tagged as selected by the <ST.addNodeInPath> method.
        
-     See also:
-     
-     <ST.addNodeInPath>
+       See also:
+       
+       <ST.addNodeInPath>
      
        Example:
 
@@ -4400,7 +8716,7 @@
        /*
          Method: refresh
         
-         Computes nodes' positions and replots the tree.
+         Computes positions and plots the tree.
          
        */
        refresh: function() {
@@ -4409,122 +8725,70 @@
        },    
 
        reposition: function() {
-            Graph.Util.computeLevels(this.graph, this.root, 0, "ignore");
+            this.graph.computeLevels(this.root, 0, "ignore");
              this.geom.setRightLevelToShow(this.clickedNode, this.canvas);
-            Graph.Util.eachNode(this.graph, function(n) {
+            this.graph.eachNode(function(n) {
                 if(n.exist) n.drawn = true;
             });
-            this.compute('endPos');
-        },
-    
-        /*
-         Method: compute
-        
-         Computes nodes' positions.
-
-        */  
-        compute: function (property, computeLevels) {
-            var prop = property || 'startPos';
-            var node = this.graph.getNode(this.root);
-            $extend(node, {
-                'drawn':true,
-                'exist':true,
-                'selected':true
-            });
-            if(!!computeLevels || !("_depth" in node))
-                Graph.Util.computeLevels(this.graph, this.root, 0, "ignore");
-            this.computePositions(node, prop);
+            this.compute('end');
         },
         
-        computePositions: function(node, prop) {
-        	var config = this.config;
-        	var multitree = config.multitree;
-        	var align = config.align;
-        	var indent = align !== 'center' && config.indent;
-        	var orn = config.orientation;
-        	var orns = multitree? ['top', 'right', 'bottom', 'left'] : [orn];
-        	
-        	var that = this;
-        	$each(orns, function(orn) {
-        		//calculate layout
-        		design(that.graph, node, prop, that.config, orn);
-        		var i = ['x', 'y'][+(orn == "left" || orn == "right")];
-        		//absolutize
-                (function red(node) {
-                    Graph.Util.eachSubnode(node, function(n) {
-                        if(n.exist && (!multitree || 
-                        		('$orn' in n.data) && 
-                        		n.data.$orn == orn)) {
-
-                        	n[prop][i] += node[prop][i];
-                        	if(indent) {
-                        		n[prop][i] += align == 'left'? indent : -indent; 
-                        	}
-                            red(n);
-                        }
-                    });
-                })(node);
-        	}); 
+        requestNodes: function(node, onComplete) {
+          var handler = $.merge(this.controller, onComplete), 
+          lev = this.config.levelsToShow;
+          if(handler.request) {
+              var leaves = [], d = node._depth;
+              node.eachLevel(0, lev, function(n) {
+                  if(n.drawn && 
+                   !n.anySubnode()) {
+                   leaves.push(n);
+                   n._level = lev - (n._depth - d);
+                  }
+              });
+              this.group.requestNodes(leaves, handler);
+          }
+            else
+              handler.onComplete();
         },
-        
-          requestNodes: function(node, onComplete) {
-            var handler = $merge(this.controller, onComplete), 
-            lev = this.config.levelsToShow, GUtil = Graph.Util;
-            if(handler.request) {
-                var leaves = [], d = node._depth;
-                GUtil.eachLevel(node, 0, lev, function(n) {
-                    if(n.drawn && 
-                     !GUtil.anySubnode(n)) {
-                     leaves.push(n);
-                     n._level = lev - (n._depth - d);
-                    }
-                });
-                this.group.requestNodes(leaves, handler);
-            }
-              else
-                handler.onComplete();
-          },
      
-          contract: function(onComplete, switched) {
-            var orn  = this.config.orientation;
-            var Geom = this.geom, Group = this.group;
-            if(switched) Geom.switchOrientation(switched);
-            var nodes = getNodesToHide.call(this);
-            if(switched) Geom.switchOrientation(orn);
-            Group.contract(nodes, $merge(this.controller, onComplete));
-          },
+        contract: function(onComplete, switched) {
+          var orn  = this.config.orientation;
+          var Geom = this.geom, Group = this.group;
+          if(switched) Geom.switchOrientation(switched);
+          var nodes = getNodesToHide.call(this);
+          if(switched) Geom.switchOrientation(orn);
+          Group.contract(nodes, $.merge(this.controller, onComplete));
+        },
       
          move: function(node, onComplete) {
-            this.compute('endPos', false);
+            this.compute('end', false);
             var move = onComplete.Move, offset = {
                 'x': move.offsetX,
                 'y': move.offsetY 
             };
             if(move.enable) {
-                this.geom.translate(node.endPos.add(offset).$scale(-1), "endPos");
+                this.geom.translate(node.endPos.add(offset).$scale(-1), "end");
             }
-            this.fx.animate($merge(this.controller, { modes: ['linear'] }, onComplete));
+            this.fx.animate($.merge(this.controller, { modes: ['linear'] }, onComplete));
          },
       
-      
         expand: function (node, onComplete) {
             var nodeArray = getNodesToShow.call(this, node);
-            this.group.expand(nodeArray, $merge(this.controller, onComplete));
+            this.group.expand(nodeArray, $.merge(this.controller, onComplete));
         },
     
-    
         selectPath: function(node) {
-          var GUtil = Graph.Util, that = this;
-          GUtil.eachNode(this.graph, function(n) { n.selected = false; }); 
+          var that = this;
+          this.graph.eachNode(function(n) { n.selected = false; }); 
           function path(node) {
               if(node == null || node.selected) return;
               node.selected = true;
-              $each(that.group.getSiblings([node])[node.id], 
+              $.each(that.group.getSiblings([node])[node.id], 
               function(n) { 
                    n.exist = true; 
                    n.drawn = true; 
               });    
-              var parents = GUtil.getParents(node);
+              var parents = node.getParents();
               parents = (parents.length > 0)? parents[0] : null;
               path(parents);
           };
@@ -4536,17 +8800,17 @@
         /*
         Method: setRoot
      
-         Switches the current root node.
+         Switches the current root node. Changes the topology of the Tree.
      
         Parameters:
-           id - The id of the node to be set as root.
-           method - Set this to "animate" if you want to animate the tree after adding the subtree. You can also set this parameter to "replot" to just replot the subtree.
-           onComplete - _optional_ An action to perform after the animation (if any).
+           id - (string) The id of the node to be set as root.
+           method - (string) Set this to "animate" if you want to animate the tree after adding the subtree. You can also set this parameter to "replot" to just replot the subtree.
+           onComplete - (optional|object) An action to perform after the animation (if any).
  
         Example:
 
         (start code js)
-          st.setRoot('my_node_id', 'animate', {
+          st.setRoot('nodeId', 'animate', {
              onComplete: function() {
                alert('complete!');
              }
@@ -4554,7 +8818,9 @@
         (end code)
      */
      setRoot: function(id, method, onComplete) {
-        	var that = this, canvas = this.canvas;
+        	if(this.busy) return;
+        	this.busy = true;
+          var that = this, canvas = this.canvas;
         	var rootNode = this.graph.getNode(this.root);
         	var clickedNode = this.graph.getNode(id);
         	function $setRoot() {
@@ -4568,7 +8834,7 @@
             		}[orn];
             		rootNode.data.$orn = opp;
             		(function tag(rootNode) {
-                		Graph.Util.eachSubnode(rootNode, function(n) {
+                		rootNode.eachSubnode(function(n) {
                 			if(n.id != id) {
                 				n.data.$orn = opp;
                 				tag(n);
@@ -4579,19 +8845,39 @@
             	}
             	this.root = id;
             	this.clickedNode = clickedNode;
-            	Graph.Util.computeLevels(this.graph, this.root, 0, "ignore");
+            	this.graph.computeLevels(this.root, 0, "ignore");
+            	this.geom.setRightLevelToShow(clickedNode, canvas, {
+            	  execHide: false,
+            	  onShow: function(node) {
+            	    if(!node.drawn) {
+                    node.drawn = true;
+                    node.setData('alpha', 1, 'end');
+                    node.setData('alpha', 0);
+                    node.pos.setc(clickedNode.pos.x, clickedNode.pos.y);
+            	    }
+            	  }
+            	});
+              this.compute('end');
+              this.busy = true;
+              this.fx.animate({
+                modes: ['linear', 'node-property:alpha'],
+                onComplete: function() {
+                  that.busy = false;
+                  that.onClick(id, {
+                    onComplete: function() {
+                      onComplete && onComplete.onComplete();
+                    }
+                  });
+                }
+              });
         	}
 
-        	//delete previous orientations (if any)
+        	// delete previous orientations (if any)
         	delete rootNode.data.$orns;
 
         	if(method == 'animate') {
-        		this.onClick(id, {
-        			onBeforeMove: function() {
-        				$setRoot.call(that);
-        				that.selectPath(clickedNode);
-        			}
-        		});
+        	  $setRoot.call(this);
+        	  that.selectPath(clickedNode);
         	} else if(method == 'replot') {
         		$setRoot.call(this);
         		this.select(this.root);
@@ -4601,12 +8887,12 @@
      /*
            Method: addSubtree
         
-            Adds a subtree, performing optionally an animation.
+            Adds a subtree.
         
            Parameters:
-              subtree - A JSON Tree object. See also <Loader.loadJSON>.
-              method - Set this to "animate" if you want to animate the tree after adding the subtree. You can also set this parameter to "replot" to just replot the subtree.
-              onComplete - _optional_ An action to perform after the animation (if any).
+              subtree - (object) A JSON Tree object. See also <Loader.loadJSON>.
+              method - (string) Set this to "animate" if you want to animate the tree after adding the subtree. You can also set this parameter to "replot" to just replot the subtree.
+              onComplete - (optional|object) An action to perform after the animation (if any).
     
            Example:
 
@@ -4620,22 +8906,22 @@
         */
         addSubtree: function(subtree, method, onComplete) {
             if(method == 'replot') {
-                this.op.sum(subtree, $extend({ type: 'replot' }, onComplete || {}));
+                this.op.sum(subtree, $.extend({ type: 'replot' }, onComplete || {}));
             } else if (method == 'animate') {
-                this.op.sum(subtree, $extend({ type: 'fade:seq' }, onComplete || {}));
+                this.op.sum(subtree, $.extend({ type: 'fade:seq' }, onComplete || {}));
             }
         },
     
         /*
            Method: removeSubtree
         
-            Removes a subtree, performing optionally an animation.
+            Removes a subtree.
         
            Parameters:
-              id - The _id_ of the subtree to be removed.
-              removeRoot - Remove the root of the subtree or only its subnodes.
-              method - Set this to "animate" if you want to animate the tree after removing the subtree. You can also set this parameter to "replot" to just replot the subtree.
-              onComplete - _optional_ An action to perform after the animation (if any).
+              id - (string) The _id_ of the subtree to be removed.
+              removeRoot - (boolean) Default's *false*. Remove the root of the subtree or only its subnodes.
+              method - (string) Set this to "animate" if you want to animate the tree after removing the subtree. You can also set this parameter to "replot" to just replot the subtree.
+              onComplete - (optional|object) An action to perform after the animation (if any).
 
           Example:
 
@@ -4650,25 +8936,25 @@
         */
         removeSubtree: function(id, removeRoot, method, onComplete) {
             var node = this.graph.getNode(id), subids = [];
-            Graph.Util.eachLevel(node, +!removeRoot, false, function(n) {
+            node.eachLevel(+!removeRoot, false, function(n) {
                 subids.push(n.id);
             });
             if(method == 'replot') {
-                this.op.removeNode(subids, $extend({ type: 'replot' }, onComplete || {}));
+                this.op.removeNode(subids, $.extend({ type: 'replot' }, onComplete || {}));
             } else if (method == 'animate') {
-                this.op.removeNode(subids, $extend({ type: 'fade:seq'}, onComplete || {}));
+                this.op.removeNode(subids, $.extend({ type: 'fade:seq'}, onComplete || {}));
             }
         },
     
         /*
            Method: select
         
-            Selects a sepecific node in the Spacetree without performing an animation. Useful when selecting 
+            Selects a node in the <ST> without performing an animation. Useful when selecting 
             nodes which are currently hidden or deep inside the tree.
 
           Parameters:
-            id - The id of the node to select.
-            onComplete - _optional_ onComplete callback.
+            id - (string) The id of the node to select.
+            onComplete - (optional|object) an onComplete callback.
 
           Example:
           (start code js)
@@ -4683,7 +8969,7 @@
             var group = this.group, geom = this.geom;
             var node=  this.graph.getNode(id), canvas = this.canvas;
             var root  = this.graph.getNode(this.root);
-            var complete = $merge(this.controller, onComplete);
+            var complete = $.merge(this.controller, onComplete);
             var that = this;
     
             complete.onBeforeCompute(node);
@@ -4693,14 +8979,15 @@
                 onComplete: function(){
                     group.hide(group.prepare(getNodesToHide.call(that)), complete);
                     geom.setRightLevelToShow(node, canvas);
-                    that.compute("pos");
-                    Graph.Util.eachNode(that.graph, function(n) { 
+                    that.compute("current");
+                    that.graph.eachNode(function(n) { 
                         var pos = n.pos.getc(true);
                         n.startPos.setc(pos.x, pos.y);
                         n.endPos.setc(pos.x, pos.y);
                         n.visited = false; 
                     });
-                    that.geom.translate(node.endPos.scale(-1), ["pos", "startPos", "endPos"]);
+                    var offset = { x: complete.offsetX, y: complete.offsetY };
+                    that.geom.translate(node.endPos.add(offset).$scale(-1), ["start", "current", "end"]);
                     group.show(getNodesToShow.call(that));              
                     that.plot();
                     complete.onAfterCompute(that.clickedNode);
@@ -4712,16 +8999,14 @@
       /*
          Method: onClick
     
-        This method is called when clicking on a tree node. It mainly performs all calculations and the animation of contracting, translating and expanding pertinent nodes.
-        
+        Animates the <ST> to center the node specified by *id*.
             
-         Parameters:
+        Parameters:
         
-            id - A node id.
-            options - A group of options and callbacks such as
-
-            - _onComplete_ an object callback called when the animation finishes.
-            - _Move_ an object that has as properties _offsetX_ or _offsetY_ for adding some offset position to the centered node.
+        id - (string) A node id.
+        options - (optional|object) A group of options and callbacks described below.
+        onComplete - (object) An object callback called when the animation finishes.
+        Move - (object) An object that has as properties _offsetX_ or _offsetY_ for adding some offset position to the centered node.
 
         Example:
 
@@ -4740,25 +9025,26 @@
     
         */    
       onClick: function (id, options) {
-        var canvas = this.canvas, that = this, Fx = this.fx, Util = Graph.Util, Geom = this.geom;
+        var canvas = this.canvas, that = this, Geom = this.geom, config = this.config;
         var innerController = {
             Move: {
-        	  enable: true,
-              offsetX: 0,
-              offsetY: 0  
+        	    enable: true,
+              offsetX: config.offsetX || 0,
+              offsetY: config.offsetY || 0  
             },
-            onBeforeRequest: $empty,
-            onBeforeContract: $empty,
-            onBeforeMove: $empty,
-            onBeforeExpand: $empty
+            setRightLevelToShowConfig: false,
+            onBeforeRequest: $.empty,
+            onBeforeContract: $.empty,
+            onBeforeMove: $.empty,
+            onBeforeExpand: $.empty
         };
-        var complete = $merge(this.controller, innerController, options);
+        var complete = $.merge(this.controller, innerController, options);
         
         if(!this.busy) {
-            this.busy= true;
-            var node=  this.graph.getNode(id);
+            this.busy = true;
+            var node = this.graph.getNode(id);
             this.selectPath(node, this.clickedNode);
-            this.clickedNode= node;
+           	this.clickedNode = node;
             complete.onBeforeCompute(node);
             complete.onBeforeRequest(node);
             this.requestNodes(node, {
@@ -4766,7 +9052,7 @@
                     complete.onBeforeContract(node);
                     that.contract({
                         onComplete: function() {
-                            Geom.setRightLevelToShow(node, canvas);
+                            Geom.setRightLevelToShow(node, canvas, complete.setRightLevelToShowConfig);
                             complete.onBeforeMove(node);
                             that.move(node, {
                                 Move: complete.Move,
@@ -4778,46 +9064,39 @@
                                             complete.onAfterCompute(id);
                                             complete.onComplete();
                                         }
-                                    }); //expand
+                                    }); // expand
                                 }
-                            }); //move
+                            }); // move
                         }
-                    });//contract
+                    });// contract
                 }
-            });//request
+            });// request
         }
       }
     });
 
 })();
 
+$jit.ST.$extend = true;
+
 /*
    Class: ST.Op
     
-   Performs advanced operations on trees and graphs.
+   Custom extension of <Graph.Op>.
 
    Extends:
 
    All <Graph.Op> methods
-
-   Access:
-
-   This instance can be accessed with the _op_ parameter of the st instance created.
-
-   Example:
-
-   (start code js)
-    var st = new ST(canvas, config);
-    st.op.morph //or can also call any other <Graph.Op> method
-   (end code)
+   
+   See also:
+   
+   <Graph.Op>
 
 */
-ST.Op = new Class({
-    Implements: Graph.Op,
+$jit.ST.Op = new Class({
+
+  Implements: Graph.Op
     
-    initialize: function(viz) {
-        this.viz = viz;
-    }
 });
 
 /*
@@ -4825,7 +9104,7 @@
      Performs operations on group of nodes.
 
 */
-ST.Group = new Class({
+$jit.ST.Group = new Class({
     
     initialize: function(viz) {
         this.viz = viz;
@@ -4853,7 +9132,7 @@
                         viz.op.sum(data, { type: 'nothing' });
                     }
                     if(++counter == len) {
-                        Graph.Util.computeLevels(viz.graph, viz.root, 0);
+                        viz.graph.computeLevels(viz.root, 0);
                         complete();
                     }
                 }
@@ -4866,12 +9145,11 @@
        Collapses group of nodes. 
     */
     contract: function(nodes, controller) {
-        var GUtil = Graph.Util;
         var viz = this.viz;
         var that = this;
 
         nodes = this.prepare(nodes);
-        this.animation.setOptions($merge(controller, {
+        this.animation.setOptions($.merge(controller, {
             $animating: false,
             compute: function(delta) {
               if(delta == 1) delta = 0.99;
@@ -4886,17 +9164,17 @@
     },
     
     hide: function(nodes, controller) {
-        var GUtil = Graph.Util, viz = this.viz;
+        var viz = this.viz;
         for(var i=0; i<nodes.length; i++) {
-            //TODO nodes are requested on demand, but not
-            //deleted when hidden. Would that be a good feature? 
-            //Currently that feature is buggy, so I'll turn it off
-            //Actually this feature is buggy because trimming should take
-            //place onAfterCompute and not right after collapsing nodes.
+            // TODO nodes are requested on demand, but not
+            // deleted when hidden. Would that be a good feature?
+            // Currently that feature is buggy, so I'll turn it off
+            // Actually this feature is buggy because trimming should take
+            // place onAfterCompute and not right after collapsing nodes.
             if (true || !controller || !controller.request) {
-                GUtil.eachLevel(nodes[i], 1, false, function(elem){
+                nodes[i].eachLevel(1, false, function(elem){
                     if (elem.exist) {
-                        $extend(elem, {
+                        $.extend(elem, {
                             'drawn': false,
                             'exist': false
                         });
@@ -4904,11 +9182,11 @@
                 });
             } else {
                 var ids = [];
-                GUtil.eachLevel(nodes[i], 1, false, function(n) {
+                nodes[i].eachLevel(1, false, function(n) {
                     ids.push(n.id);
                 });
                 viz.op.removeNode(ids, { 'type': 'nothing' });
-                viz.fx.clearLabels();
+                viz.labels.clearLabels();
             }
         }
         controller.onComplete();
@@ -4916,13 +9194,12 @@
     
 
     /*
-    
        Expands group of nodes. 
     */
     expand: function(nodes, controller) {
-        var that = this, GUtil = Graph.Util;
+        var that = this;
         this.show(nodes);
-        this.animation.setOptions($merge(controller, {
+        this.animation.setOptions($.merge(controller, {
             $animating: false,
             compute: function(delta) {
                 that.plotStep(delta, controller, this.$animating);
@@ -4938,14 +9215,14 @@
     },
     
     show: function(nodes) {
-        var GUtil = Graph.Util, config = this.config;
+        var config = this.config;
         this.prepare(nodes);
-        $each(nodes, function(n) {
-        	//check for root nodes if multitree
+        $.each(nodes, function(n) {
+        	// check for root nodes if multitree
         	if(config.multitree && !('$orn' in n.data)) {
         		delete n.data.$orns;
         		var orns = ' ';
-        		GUtil.eachSubnode(n, function(ch) {
+        		n.eachSubnode(function(ch) {
         			if(('$orn' in ch.data) 
         					&& orns.indexOf(ch.data.$orn) < 0 
         					&& ch.exist && !ch.drawn) {
@@ -4954,7 +9231,7 @@
         		});
         		n.data.$orns = orns;
         	}
-            GUtil.eachLevel(n, 0, config.levelsToShow, function(n) {
+            n.eachLevel(0, config.levelsToShow, function(n) {
             	if(n.exist) n.drawn = true;
             });     
         });
@@ -4969,13 +9246,13 @@
        Filters an array of nodes leaving only nodes with children.
     */
     getNodesWithChildren: function(nodes) {
-        var ans = [], GUtil = Graph.Util, config = this.config, root = this.viz.root;
+        var ans = [], config = this.config, root = this.viz.root;
         nodes.sort(function(a, b) { return (a._depth <= b._depth) - (a._depth >= b._depth); });
         for(var i=0; i<nodes.length; i++) {
-            if(GUtil.anySubnode(nodes[i], "exist")) {
+            if(nodes[i].anySubnode("exist")) {
             	for (var j = i+1, desc = false; !desc && j < nodes.length; j++) {
                     if(!config.multitree || '$orn' in nodes[j].data) {
-                		desc = desc || GUtil.isDescendantOf(nodes[i], nodes[j].id);                    	
+                		desc = desc || nodes[i].isDescendantOf(nodes[j].id);                    	
                     }
                 }
                 if(!desc) ans.push(nodes[i]);
@@ -4989,20 +9266,19 @@
         config = this.config,
         canvas = viz.canvas, 
         ctx = canvas.getCtx(),
-        nodes = this.nodes,
-        GUtil = Graph.Util;
+        nodes = this.nodes;
         var i, node;
-        //hide nodes that are meant to be collapsed/expanded
+        // hide nodes that are meant to be collapsed/expanded
         var nds = {};
         for(i=0; i<nodes.length; i++) {
           node = nodes[i];
           nds[node.id] = [];
           var root = config.multitree && !('$orn' in node.data);
           var orns = root && node.data.$orns;
-          GUtil.eachSubgraph(node, function(n) { 
-              //TODO(nico): Cleanup
-        	  //special check for root node subnodes when
-        	  //multitree is checked.
+          node.eachSubgraph(function(n) { 
+            // TODO(nico): Cleanup
+        	  // special check for root node subnodes when
+        	  // multitree is checked.
         	  if(root && orns && orns.indexOf(n.data.$orn) > 0 
         			  && n.drawn) {
         		  n.drawn = false;
@@ -5014,13 +9290,13 @@
             });	
             node.drawn = true;
         }
-        //plot the whole (non-scaled) tree
+        // plot the whole (non-scaled) tree
         if(nodes.length > 0) viz.fx.plot();
-        //show nodes that were previously hidden
+        // show nodes that were previously hidden
         for(i in nds) {
-          $each(nds[i], function(n) { n.drawn = true; });
+          $.each(nds[i], function(n) { n.drawn = true; });
         }
-        //plot each scaled subtree
+        // plot each scaled subtree
         for(i=0; i<nodes.length; i++) {
           node = nodes[i];
           ctx.save();
@@ -5030,14 +9306,14 @@
       },
 
       getSiblings: function(nodes) {
-        var siblings = {}, GUtil = Graph.Util;
-        $each(nodes, function(n) {
-            var par = GUtil.getParents(n);
+        var siblings = {};
+        $.each(nodes, function(n) {
+            var par = n.getParents();
             if (par.length == 0) {
                 siblings[n.id] = [n];
             } else {
                 var ans = [];
-                GUtil.eachSubnode(par[0], function(sn) {
+                par[0].eachSubnode(function(sn) {
                     ans.push(sn);
                 });
                 siblings[n.id] = ans;
@@ -5048,9 +9324,9 @@
 });
 
 /*
-   Class: ST.Geom
+   ST.Geom
 
-    Performs low level geometrical computations.
+   Performs low level geometrical computations.
 
    Access:
 
@@ -5065,38 +9341,8 @@
 
 */
 
-ST.Geom = new Class({
-    
-    initialize: function(viz) {
-        this.viz = viz;
-        this.config = viz.config;
-        this.node = viz.config.Node;
-        this.edge = viz.config.Edge;
-    },
-    
-    /*
-       Method: translate
-       
-       Applies a translation to the tree.
-
-       Parameters:
-
-       pos - A <Complex> number specifying translation vector.
-       prop - A <Graph.Node> position property ('pos', 'startPos' or 'endPos').
-
-       Example:
-
-       (start code js)
-         st.geom.translate(new Complex(300, 100), 'endPos');
-       (end code)
-    */  
-    translate: function(pos, prop) {
-        prop = $splat(prop);
-        Graph.Util.eachNode(this.viz.graph, function(elem) {
-            $each(prop, function(p) { elem[p].$add(pos); });
-        });
-    },
-
+$jit.ST.Geom = new Class({
+    Implements: Graph.Geom,
     /*
        Changes the tree current orientation to the one specified.
 
@@ -5111,7 +9357,7 @@
        Works like a CSS property, either _top-right-bottom-left_ or _top|bottom - left|right_.
      */
     dispatch: function() {
-    	//TODO(nico) should store Array.prototype.slice.call somewhere.
+    	  // TODO(nico) should store Array.prototype.slice.call somewhere.
         var args = Array.prototype.slice.call(arguments);
         var s = args.shift(), len = args.length;
         var val = function(a) { return typeof a == 'function'? a() : a; };
@@ -5132,13 +9378,13 @@
        Returns label height or with, depending on the tree current orientation.
     */  
     getSize: function(n, invert) {
-        var node = this.node, data = n.data, config = this.config;
-        var cond = node.overridable, siblingOffset = config.siblingOffset;
-        var s = (this.config.multitree 
-        		&& ('$orn' in n.data) 
-        		&& n.data.$orn) || this.config.orientation;
-        var w = (cond && data.$width || node.width) + siblingOffset;
-        var h = (cond && data.$height || node.height) + siblingOffset;
+        var data = n.data, config = this.config;
+        var siblingOffset = config.siblingOffset;
+        var s = (config.multitree 
+        		&& ('$orn' in data) 
+        		&& data.$orn) || config.orientation;
+        var w = n.getData('width') + siblingOffset;
+        var h = n.getData('height') + siblingOffset;
         if(!invert)
             return this.dispatch(s, h, w);
         else
@@ -5152,7 +9398,7 @@
         var size = this.getSize(node, true), baseHeight = 0, that = this;
         if(leaf(level, node)) return size;
         if(level === 0) return 0;
-        Graph.Util.eachSubnode(node, function(elem) {
+        node.eachSubnode(function(elem) {
             baseHeight += that.getTreeBaseSize(elem, level -1, leaf);
         });
         return (size > baseHeight? size : baseHeight) + this.config.subtreeOffset;
@@ -5160,7 +9406,7 @@
 
 
     /*
-       Method: getEdge
+       getEdge
        
        Returns a Complex instance with the begin or end position of the edge to be plotted.
 
@@ -5180,9 +9426,8 @@
           }; 
         };
         var dim = this.node;
-        var cond = this.node.overridable, data = node.data;
-        var w = cond && data.$width || dim.width;
-        var h = cond && data.$height || dim.height;
+        var w = node.getData('width');
+        var h = node.getData('height');
 
         if(type == 'begin') {
             if(dim.align == "center") {
@@ -5216,9 +9461,8 @@
     */  
     getScaledTreePosition: function(node, scale) {
         var dim = this.node;
-        var cond = this.node.overridable, data = node.data;
-        var w = (cond && data.$width || dim.width);
-        var h = (cond && data.$height || dim.height);
+        var w = node.getData('width');
+        var h = node.getData('height');
         var s = (this.config.multitree 
         		&& ('$orn' in node.data) 
         		&& node.data.$orn) || this.config.orientation;
@@ -5241,7 +9485,7 @@
     },
 
     /*
-       Method: treeFitsInCanvas
+       treeFitsInCanvas
        
        Returns a Boolean if the current subtree fits in canvas.
 
@@ -5252,91 +9496,42 @@
        level - The depth of the subtree to be considered.
     */  
     treeFitsInCanvas: function(node, canvas, level) {
-        var csize = canvas.getSize(node);
+        var csize = canvas.getSize();
         var s = (this.config.multitree 
         		&& ('$orn' in node.data) 
         		&& node.data.$orn) || this.config.orientation;
 
         var size = this.dispatch(s, csize.width, csize.height);
         var baseSize = this.getTreeBaseSize(node, level, function(level, node) { 
-          return level === 0 || !Graph.Util.anySubnode(node);
+          return level === 0 || !node.anySubnode();
         });
         return (baseSize < size);
-    },
-    
-    /*
-       Hides levels of the tree until it properly fits in canvas.
-    */  
-    setRightLevelToShow: function(node, canvas) {
-        var level = this.getRightLevelToShow(node, canvas), fx = this.viz.fx;
-        Graph.Util.eachLevel(node, 0, this.config.levelsToShow, function(n) {
-            var d = n._depth - node._depth;
-            if(d > level) {
-                n.drawn = false; 
-                n.exist = false; 
-                fx.hideLabel(n, false);
-            } else {
-                n.exist = true;
-            }
-        });
-        node.drawn= true;
-    },
-    
-    /*
-       Returns the right level to show for the current tree in order to fit in canvas.
-    */  
-    getRightLevelToShow: function(node, canvas) {
-        var config = this.config;
-    	var level = config.levelsToShow;
-    	var constrained = config.constrained;
-        if(!constrained) return level;
-        while(!this.treeFitsInCanvas(node, canvas, level) && level > 1) { level-- ; }
-        return level;
     }
-
 });
 
 /*
-   Object: ST.Plot
-    
-   Performs plotting operations.
+  Class: ST.Plot
+  
+  Custom extension of <Graph.Plot>.
 
-   Extends:
+  Extends:
 
-   All <Graph.Plot> methods
-
-   Access:
-
-   This instance can be accessed with the _fx_ parameter of the st instance created.
-
-   Example:
-
-   (start code js)
-    var st = new ST(canvas, config);
-    st.fx.placeLabel //or can also call any other <ST.Plot> method
-   (end code)
-
+  All <Graph.Plot> methods
+  
+  See also:
+  
+  <Graph.Plot>
 
 */
-ST.Plot = new Class({
+$jit.ST.Plot = new Class({
     
     Implements: Graph.Plot,
     
-    initialize: function(viz) {
-        this.viz = viz;
-        this.config = viz.config;
-        this.node = this.config.Node;
-        this.edge = this.config.Edge;
-        this.animation = new Animation;
-        this.nodeTypes = new ST.Plot.NodeTypes;
-        this.edgeTypes = new ST.Plot.EdgeTypes;        
-    },
-    
     /*
        Plots a subtree from the spacetree.
     */
     plotSubtree: function(node, opt, scale, animating) {
-        var viz = this.viz, canvas = viz.canvas;
+        var viz = this.viz, canvas = viz.canvas, config = viz.config;
         scale = Math.min(Math.max(0.001, scale), 1);
         if(scale >= 0) {
             node.drawn = false;     
@@ -5345,106 +9540,30 @@
             ctx.translate(diff.x, diff.y);
             ctx.scale(scale, scale);
         }
-        this.plotTree(node, !scale, opt, animating);
+        this.plotTree(node, $.merge(opt, {
+          'withLabels': true,
+          'hideLabels': !!scale,
+          'plotSubtree': function(n, ch) {
+            var root = config.multitree && !('$orn' in node.data);
+            var orns = root && node.getData('orns');
+            return !root || orns.indexOf(node.getData('orn')) > -1;
+          }
+        }), animating);
         if(scale >= 0) node.drawn = true;
-    },
+    },   
+   
     /*
-       Plots a Subtree.
-    */
-    plotTree: function(node, plotLabel, opt, animating) {
-        var that = this, 
-        viz = this.viz, 
-        canvas = viz.canvas,
-        config = this.config,
-        ctx = canvas.getCtx();
-        var root = config.multitree && !('$orn' in node.data);
-        var orns = root && node.data.$orns;
-        Graph.Util.eachSubnode(node, function(elem) {
-            //multitree root node check
-        	if((!root || orns.indexOf(elem.data.$orn) > 0)
-        			&& elem.exist && elem.drawn) {
-	            var adj = node.getAdjacency(elem.id);
-	            !animating && opt.onBeforePlotLine(adj);
-	            ctx.globalAlpha = Math.min(node.alpha, elem.alpha);
-	            that.plotLine(adj, canvas, animating);
-	            !animating && opt.onAfterPlotLine(adj);
-	            that.plotTree(elem, plotLabel, opt, animating);
-        	}
-        });
-
-        if(node.drawn) {
-            ctx.globalAlpha = node.alpha;
-            !animating && opt.onBeforePlotNode(node);
-            this.plotNode(node, canvas, animating);
-            !animating && opt.onAfterPlotNode(node);
-            if(plotLabel && ctx.globalAlpha >= 0.95) 
-                this.plotLabel(canvas, node, opt);
-            else 
-                this.hideLabel(node, false);
-        } else {
-            this.hideLabel(node, true);
-        }
-    },
-    
-    /* 
-      Method: placeLabel
-
-      Overrides abstract method placeLabel in <Graph.Plot>.
-
-      Parameters:
-
-      tag - A DOM label element.
-      node - A <Graph.Node>.
-      controller - A configuration/controller object passed to the visualization.
-     
-    */
-    placeLabel: function(tag, node, controller) {
-        var pos = node.pos.getc(true), dim = this.node, canvas = this.viz.canvas;
-        var w = dim.overridable && node.data.$width || dim.width;
-        var h = dim.overridable && node.data.$height || dim.height;
-        var radius = canvas.getSize();
-        var labelPos, orn;
-        if(dim.align == "center") {
-            labelPos= {
-                x: Math.round(pos.x - w / 2 + radius.width/2),
-                y: Math.round(pos.y - h / 2 + radius.height/2)
-            };
-        } else if (dim.align == "left") {
-            orn = this.config.orientation;
-            if(orn == "bottom" || orn == "top") {
-                labelPos= {
-                    x: Math.round(pos.x - w / 2 + radius.width/2),
-                    y: Math.round(pos.y + radius.height/2)
-                };
-            } else {
-                labelPos= {
-                    x: Math.round(pos.x + radius.width/2),
-                    y: Math.round(pos.y - h / 2 + radius.height/2)
-                };
-            }
-        } else if(dim.align == "right") {
-            orn = this.config.orientation;
-            if(orn == "bottom" || orn == "top") {
-                labelPos= {
-                    x: Math.round(pos.x - w / 2 + radius.width/2),
-                    y: Math.round(pos.y - h + radius.height/2)
-                };
-            } else {
-                labelPos= {
-                    x: Math.round(pos.x - w + radius.width/2),
-                    y: Math.round(pos.y - h / 2 + radius.height/2)
-                };
-            }
-        } else throw "align: not implemented";
-
-        var style = tag.style;
-        style.left = labelPos.x + 'px';
-        style.top  = labelPos.y + 'px';
-        style.display = this.fitsInCanvas(labelPos, canvas)? '' : 'none';
-        controller.onPlaceLabel(tag, node);
-    },
-    
-    
+        Method: getAlignedPos
+        
+        Returns a *x, y* object with the position of the top/left corner of a <ST> node.
+        
+        Parameters:
+        
+        pos - (object) A <Graph.Node> position.
+        width - (number) The width of the node.
+        height - (number) The height of the node.
+        
+     */
     getAlignedPos: function(pos, width, height) {
         var nconfig = this.node;
         var square, orn;
@@ -5502,837 +9621,52 @@
 });
 
 /*
-  Class: ST.Plot.NodeTypes
+  Class: ST.Label
 
-  Here are implemented all kinds of node rendering functions. 
-  Rendering functions implemented are 'none', 'circle', 'ellipse', 'rectangle' and 'square'.
+  Custom extension of <Graph.Label>. 
+  Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
 
-  You can add new Node types by implementing a new method in this class
+  Extends:
 
-  Example:
+  All <Graph.Label> methods and subclasses.
 
-  (start code js)
-    ST.Plot.NodeTypes.implement({
-      'newnodetypename': function(node, canvas) {
-        //Render my node here.
-      }
-    });
-  (end code)
+  See also:
 
-*/
-ST.Plot.NodeTypes = new Class({
-    'none': function() {},
-    
-    'circle': function(node, canvas) {
-        var pos = node.pos.getc(true), nconfig = this.node, data = node.data;
-        var cond = nconfig.overridable && data;
-        var dim  = cond && data.$dim || nconfig.dim;
-        var algnPos = this.getAlignedPos(pos, dim * 2, dim * 2);
-        canvas.path('fill', function(context) {
-            context.arc(algnPos.x + dim, algnPos.y + dim, dim, 0, Math.PI * 2, true);            
-        });
-    },
-
-    'square': function(node, canvas) {
-        var pos = node.pos.getc(true), nconfig = this.node, data = node.data;
-        var cond = nconfig.overridable && data;
-        var dim  = cond && data.$dim || nconfig.dim;
-        var algnPos = this.getAlignedPos(pos, dim, dim);
-        canvas.getCtx().fillRect(algnPos.x, algnPos.y, dim, dim);
-    },
-
-    'ellipse': function(node, canvas) {
-        var pos = node.pos.getc(true), nconfig = this.node, data = node.data;
-        var cond = nconfig.overridable && data;
-        var width  = (cond && data.$width || nconfig.width) / 2;
-        var height = (cond && data.$height || nconfig.height) / 2;
-        var algnPos = this.getAlignedPos(pos, width * 2, height * 2);
-        var ctx = canvas.getCtx();
-        ctx.save();
-        ctx.scale(width / height, height / width);
-        canvas.path('fill', function(context) {
-            context.arc((algnPos.x + width) * (height / width), (algnPos.y + height) * (width / height), height, 0, Math.PI * 2, true);            
-        });
-        ctx.restore();
-    },
-
-    'rectangle': function(node, canvas) {
-        var pos = node.pos.getc(true), nconfig = this.node, data = node.data;
-        var cond = nconfig.overridable && data;
-        var width  = cond && data.$width || nconfig.width;
-        var height = cond && data.$height || nconfig.height;
-        var algnPos = this.getAlignedPos(pos, width, height);
-        canvas.getCtx().fillRect(algnPos.x, algnPos.y, width, height);
-    }
-});
+  <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
+ */ 
+$jit.ST.Label = {};
 
 /*
-  Class: ST.Plot.EdgeTypes
+   ST.Label.Native
 
-  Here are implemented all kinds of edge rendering functions. 
-  Rendering functions implemented are 'none', 'line', 'quadratic:begin', 'quadratic:end', 'bezier' and 'arrow'.
-
-  You can add new Edge types by implementing a new method in this class
-
-  Example:
-
-  (start code js)
-    ST.Plot.EdgeTypes.implement({
-      'newedgetypename': function(adj, canvas) {
-        //Render my edge here.
-      }
-    });
-  (end code)
-
-*/
-ST.Plot.EdgeTypes = new Class({
-    'none': function() {},
-    
-    'line': function(adj, canvas) {
-    	var orn = this.getOrientation(adj);
-    	var nodeFrom = adj.nodeFrom, nodeTo = adj.nodeTo;
-        var begin = this.viz.geom.getEdge(nodeFrom._depth < nodeTo._depth? nodeFrom:nodeTo, 'begin', orn);
-        var end =  this.viz.geom.getEdge(nodeFrom._depth < nodeTo._depth? nodeTo:nodeFrom, 'end', orn);
-        canvas.path('stroke', function(ctx) {
-            ctx.moveTo(begin.x, begin.y);
-            ctx.lineTo(end.x, end.y);
-        });
-    },
-    
-    'quadratic:begin': function(adj, canvas) {
-    	var orn = this.getOrientation(adj);
-        var data = adj.data, econfig = this.edge;
-    	var nodeFrom = adj.nodeFrom, nodeTo = adj.nodeTo;
-        var begin = this.viz.geom.getEdge(nodeFrom._depth < nodeTo._depth? nodeFrom:nodeTo, 'begin', orn);
-        var end =  this.viz.geom.getEdge(nodeFrom._depth < nodeTo._depth? nodeTo:nodeFrom, 'end', orn);
-        var cond = econfig.overridable && data;
-        var dim = cond && data.$dim || econfig.dim;
-        switch(orn) {
-            case "left":
-                canvas.path('stroke', function(ctx){
-                    ctx.moveTo(begin.x, begin.y);
-                    ctx.quadraticCurveTo(begin.x + dim, begin.y, end.x, end.y);
-                });
-                break;
-            case "right":
-                canvas.path('stroke', function(ctx){
-                    ctx.moveTo(begin.x, begin.y);
-                    ctx.quadraticCurveTo(begin.x - dim, begin.y, end.x, end.y);
-                });
-                break;
-            case "top":
-                canvas.path('stroke', function(ctx){
-                    ctx.moveTo(begin.x, begin.y);
-                    ctx.quadraticCurveTo(begin.x, begin.y + dim, end.x, end.y);
-                });
-                break;
-            case "bottom":
-                canvas.path('stroke', function(ctx){
-                    ctx.moveTo(begin.x, begin.y);
-                    ctx.quadraticCurveTo(begin.x, begin.y - dim, end.x, end.y);
-                });
-                break;
-        }
-    },
-
-    'quadratic:end': function(adj, canvas) {
-    	var orn = this.getOrientation(adj);
-        var data = adj.data, econfig = this.edge;
-    	var nodeFrom = adj.nodeFrom, nodeTo = adj.nodeTo;
-        var begin = this.viz.geom.getEdge(nodeFrom._depth < nodeTo._depth? nodeFrom:nodeTo, 'begin', orn);
-        var end =  this.viz.geom.getEdge(nodeFrom._depth < nodeTo._depth? nodeTo:nodeFrom, 'end', orn);
-        var cond = econfig.overridable && data;
-        var dim = cond && data.$dim || econfig.dim;
-        switch(orn) {
-            case "left":
-                canvas.path('stroke', function(ctx){
-                    ctx.moveTo(begin.x, begin.y);
-                    ctx.quadraticCurveTo(end.x - dim, end.y, end.x, end.y);
-                });
-                break;
-            case "right":
-                canvas.path('stroke', function(ctx){
-                    ctx.moveTo(begin.x, begin.y);
-                    ctx.quadraticCurveTo(end.x + dim, end.y, end.x, end.y);
-                });
-                break;
-            case "top":
-                canvas.path('stroke', function(ctx){
-                    ctx.moveTo(begin.x, begin.y);
-                    ctx.quadraticCurveTo(end.x, end.y - dim, end.x, end.y);
-                });
-                break;
-            case "bottom":
-                canvas.path('stroke', function(ctx){
-                    ctx.moveTo(begin.x, begin.y);
-                    ctx.quadraticCurveTo(end.x, end.y + dim, end.x, end.y);
-                });
-                break;
-        }
-    },
-
-    'bezier': function(adj, canvas) {
-        var data = adj.data, econfig = this.edge;
-    	var orn = this.getOrientation(adj);
-    	var nodeFrom = adj.nodeFrom, nodeTo = adj.nodeTo;
-        var begin = this.viz.geom.getEdge(nodeFrom._depth < nodeTo._depth? nodeFrom:nodeTo, 'begin', orn);
-        var end =  this.viz.geom.getEdge(nodeFrom._depth < nodeTo._depth? nodeTo:nodeFrom, 'end', orn);
-        var cond = econfig.overridable && data;
-        var dim = cond && data.$dim || econfig.dim;
-        switch(orn) {
-            case "left":
-                canvas.path('stroke', function(ctx) {
-                    ctx.moveTo(begin.x, begin.y);
-                    ctx.bezierCurveTo(begin.x + dim, begin.y, end.x - dim, end.y, end.x, end.y);
-                });
-                break;
-            case "right":
-                canvas.path('stroke', function(ctx) {
-                    ctx.moveTo(begin.x, begin.y);
-                    ctx.bezierCurveTo(begin.x - dim, begin.y, end.x + dim, end.y, end.x, end.y);
-                });
-                break;
-            case "top":
-                canvas.path('stroke', function(ctx) {
-                    ctx.moveTo(begin.x, begin.y);
-                    ctx.bezierCurveTo(begin.x, begin.y + dim, end.x, end.y - dim, end.x, end.y);
-                });
-                break;
-            case "bottom":
-                canvas.path('stroke', function(ctx) {
-                    ctx.moveTo(begin.x, begin.y);
-                    ctx.bezierCurveTo(begin.x, begin.y - dim, end.x, end.y + dim, end.x, end.y);
-                });
-                break;
-        }
-    },
-
-    'arrow': function(adj, canvas) {
-    	var orn = this.getOrientation(adj);
-    	var node = adj.nodeFrom, child = adj.nodeTo;
-        var data = adj.data, econfig = this.edge;
-        //get edge dim
-        var cond = econfig.overridable && data;
-        var edgeDim = cond && data.$dim || econfig.dim;
-        //get edge direction
-        if(cond && data.$direction && data.$direction.length > 1) {
-            var nodeHash = {};
-            nodeHash[node.id] = node;
-            nodeHash[child.id] = child;
-            var sense = data.$direction;
-            node = nodeHash[sense[0]];
-            child = nodeHash[sense[1]];
-        }
-        var posFrom = this.viz.geom.getEdge(node, 'begin', orn);
-        var posTo =  this.viz.geom.getEdge(child, 'end', orn);
-        var vect = new Complex(posTo.x - posFrom.x, posTo.y - posFrom.y);
-        vect.$scale(edgeDim / vect.norm());
-        var intermediatePoint = new Complex(posTo.x - vect.x, posTo.y - vect.y);
-        var normal = new Complex(-vect.y / 2, vect.x / 2);
-        var v1 = intermediatePoint.add(normal), v2 = intermediatePoint.$add(normal.$scale(-1));
-        canvas.path('stroke', function(context) {
-            context.moveTo(posFrom.x, posFrom.y);
-            context.lineTo(posTo.x, posTo.y);
-        });
-        canvas.path('fill', function(context) {
-            context.moveTo(v1.x, v1.y);
-            context.lineTo(v2.x, v2.y);
-            context.lineTo(posTo.x, posTo.y);
-        });
-    }
-});
-
-    
-})();
-
-
-
-/*
- * File: AngularWidth.js
- * 
- * Provides utility methods for calculating angular widths.
- *
- * Implemented by:
- *
- * <RGraph>, <Hypertree>
- *
- */
-
-/*
-   Object: AngularWidth
-
-   Provides utility methods for calculating angular widths.
-*/
-var AngularWidth = {
-    /*
-     Method: setAngularWidthForNodes
-    
-     Sets nodes angular widths.
-    */
-    setAngularWidthForNodes: function() {
-        var config = this.config.Node;
-    var overridable = config.overridable;
-    var dim = config.dim;
-        
-    Graph.Util.eachBFS(this.graph, this.root, function(elem, i) {
-            var diamValue = (overridable 
-       && elem.data 
-       && elem.data.$aw) || dim;
-            elem._angularWidth = diamValue / i;
-        }, "ignore");
-    },
-    
-    /*
-     Method: setSubtreesAngularWidth
-    
-     Sets subtrees angular widths.
-    */
-    setSubtreesAngularWidth: function() {
-        var that = this;
-        Graph.Util.eachNode(this.graph, function(elem) {
-            that.setSubtreeAngularWidth(elem);
-        }, "ignore");
-    },
-    
-    /*
-     Method: setSubtreeAngularWidth
-    
-     Sets the angular width for a subtree.
-    */
-    setSubtreeAngularWidth: function(elem) {
-        var that = this, nodeAW = elem._angularWidth, sumAW = 0;
-        Graph.Util.eachSubnode(elem, function(child) {
-            that.setSubtreeAngularWidth(child);
-            sumAW += child._treeAngularWidth;
-        }, "ignore");
-        elem._treeAngularWidth = Math.max(nodeAW, sumAW);
-    },
-    
-    /*
-     Method: computeAngularWidths
-    
-     Computes nodes and subtrees angular widths.
-    */
-    computeAngularWidths: function () {
-        this.setAngularWidthForNodes();
-        this.setSubtreesAngularWidth();
-    }
-  
-};
-
-
-/*
- * File: RGraph.js
- * 
- * Implements the <RGraph> class and other derived classes.
- *
- * Description:
- *
- * A radial layout of a tree puts the root node on the center of the canvas, places its children on the first concentric ring away from the root node, its grandchildren on a second concentric ring, and so on...
- *
- * Ka-Ping Yee, Danyel Fisher, Rachna Dhamija and Marti Hearst introduced a very interesting paper called "Animated Exploration of Dynamic Graphs with Radial Layout". In this paper they describe a way to animate a radial layout of a tree with ease-in and ease-out transitions, which make transitions from a graph's state to another easier to understand for the viewer.
- *
- * Inspired by:
- *
- * Animated Exploration of Dynamic Graphs with Radial Layout (Ka-Ping Yee, Danyel Fisher, Rachna Dhamija, Marti Hearst)
- *
- * <http://bailando.sims.berkeley.edu/papers/infovis01.htm>
- *
- * Disclaimer:
- *
- * This visualization was built from scratch, taking only the paper as inspiration, and only shares some features with this paper.
- *
- * 
- */
-
-/*
-   Class: RGraph
-      
-     The main RGraph class
-
-     Extends:
-
-     <Loader>, <AngularWidth>
-
-     Parameters:
-
-     canvas - A <Canvas> Class
-     config - A configuration/controller object.
-
-     Configuration:
-    
-     The configuration object can have the following properties (all properties are optional and have a default value)
-     
-     *General*
-
-     - _interpolation_ Interpolation type used for animations. Possible options are 'polar' and 'linear'. Default's 'linear'.
-     - _levelDistance_ Distance between a parent node and its children. Default's 100.
-     - _withLabels_ Whether the visualization should use/create labels or not. Default's *true*.
-
-     *Node*
-     
-     Customize the visualization nodes' shape, color, and other style properties.
-
-     - _Node_
-
-     This object has as properties
-
-     - _overridable_ Determine whether or not nodes properties can be overriden by a particular node. Default's false.
-
-     If given a JSON tree or graph, a node _data_ property contains properties which are the same as defined here but prefixed with 
-     a dollar sign (i.e $), the node properties will override the global node properties.
-
-     - _type_ Node type (shape). Possible options are "none", "square", "rectangle", "circle", "triangle", "star". Default's "circle".
-     - _color_ Node color. Default's '#ccb'.
-     - _lineWidth_ Line width. If nodes aren't drawn with strokes then this property won't be of any use. Default's 1.
-     - _height_ Node height. Used for plotting rectangular nodes. Default's 5.
-     - _width_ Node width. Used for plotting rectangular nodes. Default's 5.
-     - _dim_ An extra parameter used by other complex shapes such as square and circle to determine the shape's diameter. Default's 3.
-
-     *Edge*
-
-     Customize the visualization edges' shape, color, and other style properties.
-
-     - _Edge_
-
-     This object has as properties
-
-     - _overridable_ Determine whether or not edges properties can be overriden by a particular edge object. Default's false.
-
-     If given a JSON _complex_ graph (defined in <Loader.loadJSON>), an adjacency _data_ property contains properties which are the same as defined here but prefixed with 
-     a dollar sign (i.e $), the adjacency properties will override the global edge properties.
-
-     - _type_ Edge type (shape). Possible options are "none", "line" and "arrow". Default's "line".
-     - _color_ Edge color. Default's '#ccb'.
-     - _lineWidth_ Line width. If edges aren't drawn with strokes then this property won't be of any use. Default's 1.
-
-     *Animations*
-
-     - _duration_ Duration of the animation in milliseconds. Default's 2500.
-     - _fps_ Frames per second. Default's 40.
-     - _transition_ One of the transitions defined in the <Animation> class. Default's Quart.easeInOut.
-     - _clearCanvas_ Whether to clear canvas on each animation frame or not. Default's true.
-     
-    *Controller options*
-
-    You can also implement controller functions inside the configuration object. This functions are
-    
-    - _onBeforeCompute(node)_ This method is called right before performing all computation and animations to the JIT visualization.
-    - _onAfterCompute()_ This method is triggered right after all animations or computations for the JIT visualizations ended.
-    - _onCreateLabel(domElement, node)_ This method receives the label dom element as first parameter, and the corresponding <Graph.Node> as second parameter. This method will only be called on label creation. Note that a <Graph.Node> is a node from the input tree/graph you provided to the visualization. If you want to know more about what kind of JSON tree/graph format is used to feed the visualizations, you can take a look at <Loader.loadJSON>. This method proves useful when adding events to the labels used by the JIT.
-    - _onPlaceLabel(domElement, node)_ This method receives the label dom element as first parameter and the corresponding <Graph.Node> as second parameter. This method is called each time a label has been placed on the visualization, and thus it allows you to update the labels properties, such as size or position. Note that onPlaceLabel will be triggered after updating the labels positions. That means that, for example, the left and top css properties are already updated to match the nodes positions.
-    - _onBeforePlotNode(node)_ This method is triggered right before plotting a given node. The _node_ parameter is the <Graph.Node> to be plotted. 
-This method is useful for changing a node style right before plotting it.
-    - _onAfterPlotNode(node)_ This method is triggered right after plotting a given node. The _node_ parameter is the <Graph.Node> plotted.
-    - _onBeforePlotLine(adj)_ This method is triggered right before plotting an edge. The _adj_ parameter is a <Graph.Adjacence> object. 
-This method is useful for adding some styles to a particular edge before being plotted.
-    - _onAfterPlotLine(adj)_ This method is triggered right after plotting an edge. The _adj_ parameter is the <Graph.Adjacence> plotted.
-
-    Example:
-
-    Here goes a complete example. In most cases you won't be forced to implement all properties and methods. In fact, 
-    all configuration properties  have the default value assigned.
-
-    I won't be instanciating a <Canvas> class here. If you want to know more about instanciating a <Canvas> class 
-    please take a look at the <Canvas> class documentation.
-
-    (start code js)
-      var rgraph = new RGraph(canvas, {
-        interpolation: 'linear',
-        levelDistance: 100,
-        withLabels: true,
-        Node: {
-          overridable: false,
-          type: 'circle',
-          color: '#ccb',
-          lineWidth: 1,
-          height: 5,
-          width: 5,
-          dim: 3
-        },
-        Edge: {
-          overridable: false,
-          type: 'line',
-          color: '#ccb',
-          lineWidth: 1
-        },
-        duration: 2500,
-        fps: 40,
-        transition: Trans.Quart.easeInOut,
-        clearCanvas: true,
-        onBeforeCompute: function(node) {
-          //do something onBeforeCompute
-        },
-        onAfterCompute:  function () {
-          //do something onAfterCompute
-        },
-        onCreateLabel:   function(domElement, node) {
-          //do something onCreateLabel
-        },
-        onPlaceLabel:    function(domElement, node) {
-          //do something onPlaceLabel
-        },
-        onBeforePlotNode:function(node) {
-          //do something onBeforePlotNode
-        },
-        onAfterPlotNode: function(node) {
-          //do something onAfterPlotNode
-        },
-        onBeforePlotLine:function(adj) {
-          //do something onBeforePlotLine
-        },
-        onAfterPlotLine: function(adj) {
-          //do something onAfterPlotLine
-        }
-      });
-    (end code)
-
-  Instance Properties:
-
-   - _graph_ Access a <Graph> instance.
-   - _op_ Access a <RGraph.Op> instance.
-   - _fx_ Access a <RGraph.Plot> instance.
-*/
-
-this.RGraph = new Class({
-  
-    Implements: [Loader, AngularWidth],
-    
-  initialize: function(canvas, controller) {
-    var config= {
-            labelContainer: canvas.id + '-label',
-
-                interpolation: 'linear',
-            levelDistance: 100,
-            withLabels: true,
-                
-        Node: {
-          overridable: false,
-            type: 'circle',
-          dim: 3,
-          color: '#ccb',
-                    width: 5,
-                    height: 5,   
-          lineWidth: 1
-        },
-        
-        Edge: {
-          overridable: false,
-            type: 'line',
-          color: '#ccb',
-          lineWidth: 1
-        },
-
-            fps:40,
-            duration: 2500,
-                transition: Trans.Quart.easeInOut,
-                clearCanvas: true
-    };
-
-      var innerController = {
-          onBeforeCompute: $empty,
-          onAfterCompute:  $empty,
-          onCreateLabel:   $empty,
-          onPlaceLabel:    $empty,
-          onComplete:      $empty,
-          onBeforePlotLine:$empty,
-          onAfterPlotLine: $empty,
-          onBeforePlotNode:$empty,
-          onAfterPlotNode: $empty
-      };
-    
-      this.controller = this.config = $merge(config, innerController, controller);
-      this.graphOptions = {
-            'complex': false,
-            'Node': {
-                'selected': false,
-                'exist': true,
-                'drawn': true
-            }
-        };
-    this.graph = new Graph(this.graphOptions);
-      this.fx = new RGraph.Plot(this);
-    this.op = new RGraph.Op(this);
-    this.json = null;
-      this.canvas = canvas;
-      this.root = null;
-      this.busy = false;
-      this.parent = false;
-  },
-    /* 
-     Method: refresh 
-     
-     Computes nodes' positions and replots the tree.
-
-    */ 
-    refresh: function() {
-        this.compute();
-        this.plot();
-    },
-    
-    /*
-     Method: reposition
-    
-     An alias for computing new positions to _endPos_
-
-     See also:
-
-     <RGraph.compute>
-     
-    */
-    reposition: function() {
-        this.compute('endPos');
-    },
-
-
-    /*
-     Method: plot
-    
-     Plots the RGraph
-    */
-    plot: function() {
-        this.fx.plot();
-    },
-    /* 
-     Method: compute 
-     
-     Computes nodes' positions. 
-
-     Parameters:
-
-     property - _optional_ A <Graph.Node> position property to store the new positions. Possible values are 'pos', 'endPos' or 'startPos'.
-
-    */ 
-    compute: function(property) {
-        var prop = property || ['pos', 'startPos', 'endPos'];
-        var node = this.graph.getNode(this.root);
-        node._depth = 0;
-        Graph.Util.computeLevels(this.graph, this.root, 0, "ignore");
-        this.computeAngularWidths();
-        this.computePositions(prop);
-    },
-    
-    /*
-     computePositions
-    
-     Performs the main algorithm for computing node positions.
-    */
-    computePositions: function(property) {
-        var propArray = $splat(property);
-        var aGraph = this.graph;
-        var GUtil = Graph.Util;
-        var root = this.graph.getNode(this.root);
-        var parent = this.parent;
-    var config = this.config;
-
-        for(var i=0; i<propArray.length; i++)
-            root[propArray[i]] = $P(0, 0);
-        
-        root.angleSpan = {
-            begin: 0,
-            end: 2 * Math.PI
-        };
-        root._rel = 1;
-        
-        GUtil.eachBFS(this.graph, this.root, function (elem) {
-            var angleSpan = elem.angleSpan.end - elem.angleSpan.begin;
-            var rho = (elem._depth + 1) * config.levelDistance;
-            var angleInit = elem.angleSpan.begin;
-            
-      var totalAngularWidths = 0, subnodes = [];
-            GUtil.eachSubnode(elem, function(sib) {
-                totalAngularWidths += sib._treeAngularWidth;
-        subnodes.push(sib);
-            }, "ignore");
-            
-            if(parent && parent.id == elem.id && subnodes.length > 0 && subnodes[0].dist) {
-                subnodes.sort(function(a, b) {
-                    return  (a.dist >= b.dist) - (a.dist <= b.dist);
-                });
-            }
-            for(var k=0; k < subnodes.length; k++) {
-                var child = subnodes[k];
-                if(!child._flag) {
-                    child._rel = child._treeAngularWidth / totalAngularWidths;
-                    var angleProportion = child._rel * angleSpan;
-                    var theta = angleInit + angleProportion / 2;
-
-                    for(var i=0; i<propArray.length; i++)
-                        child[propArray[i]] = $P(theta, rho);
-
-                    child.angleSpan = {
-                        begin: angleInit,
-                        end: angleInit + angleProportion
-                    };
-                    angleInit += angleProportion;
-                }
-            }
-        }, "ignore");
-    },
-
-    /*
-     getNodeAndParentAngle
-    
-     Returns the _parent_ of the given node, also calculating its angle span.
-    */
-    getNodeAndParentAngle: function(id) {
-        var theta = false;
-        var n  = this.graph.getNode(id);
-        var ps = Graph.Util.getParents(n);
-        var p  = (ps.length > 0)? ps[0] : false;
-        if(p) {
-            var posParent = p.pos.getc(), posChild = n.pos.getc();
-            var newPos    = posParent.add(posChild.scale(-1));
-            theta = Math.atan2(newPos.y, newPos.x);
-            if(theta < 0) theta += 2 * Math.PI;
-        }
-        return {parent: p, theta: theta};
-    },
-    
-    /*
-     tagChildren
-    
-     Enumerates the children in order to mantain child ordering (second constraint of the paper).
-    */
-    tagChildren: function(par, id) {
-        if(par.angleSpan) {
-          var adjs = [];
-          Graph.Util.eachAdjacency(par, function(elem) {
-            adjs.push(elem.nodeTo);
-          }, "ignore");
-          var len = adjs.length;
-          for(var i=0; i < len && id != adjs[i].id; i++);
-          for(var j= (i+1) % len, k = 0; id !=  adjs[j].id; j = (j+1) % len) {
-            adjs[j].dist = k++;
-          }
-        }
-    },
-    
-     /* 
-     Method: onClick 
-     
-     Performs all calculations and animations to center the node specified by _id_.
-
-     Parameters:
-
-     id - A <Graph.Node> id.
-     opt - _optional_ An object containing some extra properties like
-
-     - _hideLabels_ Hide labels when performing the animation. Default's *true*.
-
-     Example:
-
-     (start code js)
-       rgraph.onClick('someid');
-       //or also...
-       rgraph.onClick('someid', {
-        hideLabels: false
-       });
-      (end code)
-      
-    */ 
-    onClick: function(id, opt) {
-        if(this.root != id && !this.busy) {
-            this.busy = true;
-            this.root = id; 
-            that = this;
-            this.controller.onBeforeCompute(this.graph.getNode(id));
-            var obj = this.getNodeAndParentAngle(id);
-            
-      //second constraint
-      this.tagChildren(obj.parent, id);
-            this.parent = obj.parent;
-            this.compute('endPos');
-            
-            //first constraint
-            var thetaDiff = obj.theta - obj.parent.endPos.theta;
-            Graph.Util.eachNode(this.graph, function(elem) {
-                elem.endPos.set(elem.endPos.getp().add($P(thetaDiff, 0)));
-            });
-
-            var mode = this.config.interpolation;
-            opt = $merge({ onComplete: $empty }, opt || {});
-
-      this.fx.animate($merge({
-                hideLabels: true,
-                modes: [mode]
-            }, opt, {
-                onComplete: function() {
-                    that.busy = false;
-                    opt.onComplete();
-                }
-            }));
-        }       
-    }
-});
-
-/*
-   Class: RGraph.Op
-
-   Performs advanced operations on trees and graphs.
+   Custom extension of <Graph.Label.Native>.
 
    Extends:
 
-   All <Graph.Op> methods
+   All <Graph.Label.Native> methods
 
-   Access:
+   See also:
 
-   This instance can be accessed with the _op_ parameter of the <RGraph> instance created.
-
-   Example:
-
-   (start code js)
-    var rgraph = new RGraph(canvas, config);
-    rgraph.op.morph //or can also call any other <Graph.Op> method
-   (end code)
-   
+   <Graph.Label.Native>
 */
-RGraph.Op = new Class({
+$jit.ST.Label.Native = new Class({
+  Implements: Graph.Label.Native,
 
-    Implements: Graph.Op,
-
-    initialize: function(viz) {
-        this.viz = viz;
-    }
+  renderLabel: function(canvas, node, controller) {
+    var ctx = canvas.getCtx(),
+        coord = node.pos.getc(true),
+        width = node.getData('width'),
+        height = node.getData('height'),
+        pos = this.viz.fx.getAlignedPos(coord, width, height);
+    ctx.fillText(node.name, pos.x + width / 2, pos.y + height / 2);
+  }
 });
 
-/*
-   Class: RGraph.Plot
+$jit.ST.Label.DOM = new Class({
+  Implements: Graph.Label.DOM,
 
-   Performs plotting operations.
-
-   Extends:
-
-   All <Graph.Plot> methods
-
-   Access:
-
-   This instance can be accessed with the _fx_ parameter of the <RGraph> instance created.
-
-   Example:
-
-   (start code js)
-    var rgraph = new RGraph(canvas, config);
-    rgraph.fx.placeLabel //or can also call any other <RGraph.Plot> method
-   (end code)
-
-*/
-RGraph.Plot = new Class({
-  
-  Implements: Graph.Plot,
-  
-    initialize: function(viz) {
-        this.viz = viz;
-    this.config = viz.config;
-    this.node = viz.config.Node;
-    this.edge = viz.config.Edge;
-    this.animation = new Animation;
-      this.nodeTypes = new RGraph.Plot.NodeTypes;
-    this.edgeTypes = new RGraph.Plot.EdgeTypes;
-    },
-
-    /* 
-      Method: placeLabel
+  /* 
+      placeLabel
 
       Overrides abstract method placeLabel in <Graph.Plot>.
 
@@ -6342,196 +9676,6758 @@
       node - A <Graph.Node>.
       controller - A configuration/controller object passed to the visualization.
      
-     */
+    */
     placeLabel: function(tag, node, controller) {
-        var pos = node.pos.getc(true), canvas = this.viz.canvas; 
-        var radius= canvas.getSize();
-        var labelPos= {
-            x: Math.round(pos.x + radius.width/2),
-            y: Math.round(pos.y + radius.height/2)
-        };
+        var pos = node.pos.getc(true), 
+            config = this.viz.config, 
+            dim = config.Node, 
+            canvas = this.viz.canvas,
+            w = node.getData('width'),
+            h = node.getData('height'),
+            radius = canvas.getSize(),
+            labelPos, orn;
+        
+        var ox = canvas.translateOffsetX,
+            oy = canvas.translateOffsetY,
+            sx = canvas.scaleOffsetX,
+            sy = canvas.scaleOffsetY,
+            posx = pos.x * sx + ox,
+            posy = pos.y * sy + oy;
+
+        if(dim.align == "center") {
+            labelPos= {
+                x: Math.round(posx - w / 2 + radius.width/2),
+                y: Math.round(posy - h / 2 + radius.height/2)
+            };
+        } else if (dim.align == "left") {
+            orn = config.orientation;
+            if(orn == "bottom" || orn == "top") {
+                labelPos= {
+                    x: Math.round(posx - w / 2 + radius.width/2),
+                    y: Math.round(posy + radius.height/2)
+                };
+            } else {
+                labelPos= {
+                    x: Math.round(posx + radius.width/2),
+                    y: Math.round(posy - h / 2 + radius.height/2)
+                };
+            }
+        } else if(dim.align == "right") {
+            orn = config.orientation;
+            if(orn == "bottom" || orn == "top") {
+                labelPos= {
+                    x: Math.round(posx - w / 2 + radius.width/2),
+                    y: Math.round(posy - h + radius.height/2)
+                };
+            } else {
+                labelPos= {
+                    x: Math.round(posx - w + radius.width/2),
+                    y: Math.round(posy - h / 2 + radius.height/2)
+                };
+            }
+        } else throw "align: not implemented";
+
         var style = tag.style;
         style.left = labelPos.x + 'px';
         style.top  = labelPos.y + 'px';
         style.display = this.fitsInCanvas(labelPos, canvas)? '' : 'none';
         controller.onPlaceLabel(tag, node);
-  }
-});
-
-/*
-  Class: RGraph.Plot.NodeTypes
-
-  Here are implemented all kinds of node rendering functions. 
-  Rendering functions implemented are 'none', 'circle', 'triangle', 'rectangle', 'star' and 'square'.
-
-  You can add new Node types by implementing a new method in this class
-
-  Example:
-
-  (start code js)
-    RGraph.Plot.NodeTypes.implement({
-      'newnodetypename': function(node, canvas) {
-        //Render my node here.
-      }
-    });
-  (end code)
-
-*/
-RGraph.Plot.NodeTypes = new Class({
-    'none': function() {},
-    
-    'circle': function(node, canvas) {
-        var pos = node.pos.getc(true), nconfig = this.node, data = node.data;
-        var nodeDim = nconfig.overridable && data && data.$dim || nconfig.dim;
-        canvas.path('fill', function(context) {
-            context.arc(pos.x, pos.y, nodeDim, 0, Math.PI*2, true);            
-        });
-    },
-    
-    'square': function(node, canvas) {
-        var pos = node.pos.getc(true), nconfig = this.node, data = node.data;
-        var nodeDim = nconfig.overridable && data && data.$dim || nconfig.dim;
-    var nodeDim2 = 2 * nodeDim;
-        canvas.getCtx().fillRect(pos.x - nodeDim, pos.y - nodeDim, nodeDim2, nodeDim2);
-    },
-    
-    'rectangle': function(node, canvas) {
-        var pos = node.pos.getc(true), nconfig = this.node, data = node.data;
-        var width = nconfig.overridable && data && data.$width || nconfig.width;
-    var height = nconfig.overridable && data && data.$height || nconfig.height;
-        canvas.getCtx().fillRect(pos.x - width / 2, pos.y - height / 2, width, height);
-    },
-    
-    'triangle': function(node, canvas) {
-        var pos = node.pos.getc(true), nconfig = this.node, data = node.data;
-        var nodeDim = nconfig.overridable && data && data.$dim || nconfig.dim;
-        var c1x = pos.x, c1y = pos.y - nodeDim,
-        c2x = c1x - nodeDim, c2y = pos.y + nodeDim,
-        c3x = c1x + nodeDim, c3y = c2y;
-        canvas.path('fill', function(ctx) {
-            ctx.moveTo(c1x, c1y);
-            ctx.lineTo(c2x, c2y);
-            ctx.lineTo(c3x, c3y);
-        });
-    },
-    
-    'star': function(node, canvas) {
-        var pos = node.pos.getc(true), nconfig = this.node, data = node.data;
-        var nodeDim = nconfig.overridable && data && data.$dim || nconfig.dim;
-        var ctx = canvas.getCtx(), pi5 = Math.PI / 5;
-        ctx.save();
-        ctx.translate(pos.x, pos.y);
-        ctx.beginPath();
-        ctx.moveTo(nodeDim, 0);
-        for (var i=0; i<9; i++){
-          ctx.rotate(pi5);
-          if(i % 2 == 0) {
-            ctx.lineTo((nodeDim / 0.525731) * 0.200811, 0);
-          } else {
-            ctx.lineTo(nodeDim, 0);
-          }
-        }
-        ctx.closePath();
-        ctx.fill();
-        ctx.restore();
     }
 });
 
 /*
-  Class: RGraph.Plot.EdgeTypes
+  ST.Label.SVG
 
-  Here are implemented all kinds of edge rendering functions. 
-  Rendering functions implemented are 'none', 'line' and 'arrow'.
+  Custom extension of <Graph.Label.SVG>.
 
-  You can add new Edge types by implementing a new method in this class
+  Extends:
+
+  All <Graph.Label.SVG> methods
+
+  See also:
+
+  <Graph.Label.SVG>
+*/
+$jit.ST.Label.SVG = new Class({
+  Implements: [$jit.ST.Label.DOM, Graph.Label.SVG],
+
+  initialize: function(viz) {
+    this.viz = viz;
+  }
+});
+
+/*
+   ST.Label.HTML
+
+   Custom extension of <Graph.Label.HTML>.
+
+   Extends:
+
+   All <Graph.Label.HTML> methods.
+
+   See also:
+
+   <Graph.Label.HTML>
+
+*/
+$jit.ST.Label.HTML = new Class({
+  Implements: [$jit.ST.Label.DOM, Graph.Label.HTML],
+
+  initialize: function(viz) {
+    this.viz = viz;
+  }
+});
+
+
+/*
+  Class: ST.Plot.NodeTypes
+
+  This class contains a list of <Graph.Node> built-in types. 
+  Node types implemented are 'none', 'circle', 'rectangle', 'ellipse' and 'square'.
+
+  You can add your custom node types, customizing your visualization to the extreme.
 
   Example:
 
   (start code js)
-    RGraph.Plot.EdgeTypes.implement({
-      'newedgetypename': function(adj, canvas) {
-        //Render my edge here.
+    ST.Plot.NodeTypes.implement({
+      'mySpecialType': {
+        'render': function(node, canvas) {
+          //print your custom node to canvas
+        },
+        //optional
+        'contains': function(node, pos) {
+          //return true if pos is inside the node or false otherwise
+        }
       }
     });
   (end code)
 
 */
-RGraph.Plot.EdgeTypes = new Class({
-    'none': function() {},
-    
-    'line': function(adj, canvas) {
-        var pos = adj.nodeFrom.pos.getc(true);
-    var posChild = adj.nodeTo.pos.getc(true);
-        canvas.path('stroke', function(context) {
-            context.moveTo(pos.x, pos.y);
-            context.lineTo(posChild.x, posChild.y);
-        });
+$jit.ST.Plot.NodeTypes = new Class({
+  'none': {
+    'render': $.empty,
+    'contains': $.lambda(false)
+  },
+  'circle': {
+    'render': function(node, canvas) {
+      var dim  = node.getData('dim'),
+          pos = this.getAlignedPos(node.pos.getc(true), dim, dim),
+          dim2 = dim/2;
+      this.nodeHelper.circle.render('fill', {x:pos.x+dim2, y:pos.y+dim2}, dim2, canvas);
     },
-    
-    'arrow': function(adj, canvas) {
-        var node = adj.nodeFrom, child = adj.nodeTo;
-    var data = adj.data, econfig = this.edge;
-        //get edge dim
-    var cond = econfig.overridable && data;
-    var edgeDim = cond && data.$dim || 14;
-        //get edge direction
-        if(cond && data.$direction && data.$direction.length > 1) {
-            var nodeHash = {};
-            nodeHash[node.id] = node;
-            nodeHash[child.id] = child;
-            var sense = data.$direction;
-            node = nodeHash[sense[0]];
-            child = nodeHash[sense[1]];
-        }
-        var posFrom = node.pos.getc(true), posTo = child.pos.getc(true);
-        var vect = new Complex(posTo.x - posFrom.x, posTo.y - posFrom.y);
-        vect.$scale(edgeDim / vect.norm());
-        var intermediatePoint = new Complex(posTo.x - vect.x, posTo.y - vect.y);
-        var normal = new Complex(-vect.y / 2, vect.x / 2);
-        var v1 = intermediatePoint.add(normal), v2 = intermediatePoint.$add(normal.$scale(-1));
-        canvas.path('stroke', function(context) {
-            context.moveTo(posFrom.x, posFrom.y);
-            context.lineTo(posTo.x, posTo.y);
-        });
-    canvas.path('fill', function(context) {
-            context.moveTo(v1.x, v1.y);
-            context.lineTo(v2.x, v2.y);
-            context.lineTo(posTo.x, posTo.y);
-        });
+    'contains': function(node, pos) {
+      var dim  = node.getData('dim'),
+          npos = this.getAlignedPos(node.pos.getc(true), dim, dim),
+          dim2 = dim/2;
+      this.nodeHelper.circle.contains({x:npos.x+dim2, y:npos.y+dim2}, pos, dim2);
+    }
+  },
+  'square': {
+    'render': function(node, canvas) {
+      var dim  = node.getData('dim'),
+          dim2 = dim/2,
+          pos = this.getAlignedPos(node.pos.getc(true), dim, dim);
+      this.nodeHelper.square.render('fill', {x:pos.x+dim2, y:pos.y+dim2}, dim2, canvas);
+    },
+    'contains': function(node, pos) {
+      var dim  = node.getData('dim'),
+          npos = this.getAlignedPos(node.pos.getc(true), dim, dim),
+          dim2 = dim/2;
+      this.nodeHelper.square.contains({x:npos.x+dim2, y:npos.y+dim2}, pos, dim2);
+    }
+  },
+  'ellipse': {
+    'render': function(node, canvas) {
+      var width = node.getData('width'),
+          height = node.getData('height'),
+          pos = this.getAlignedPos(node.pos.getc(true), width, height);
+      this.nodeHelper.ellipse.render('fill', {x:pos.x+width/2, y:pos.y+height/2}, width, height, canvas);
+    },
+    'contains': function(node, pos) {
+      var width = node.getData('width'),
+          height = node.getData('height'),
+          npos = this.getAlignedPos(node.pos.getc(true), width, height);
+      this.nodeHelper.ellipse.contains({x:npos.x+width/2, y:npos.y+height/2}, pos, width, height);
+    }
+  },
+  'rectangle': {
+    'render': function(node, canvas) {
+      var width = node.getData('width'),
+          height = node.getData('height'),
+          pos = this.getAlignedPos(node.pos.getc(true), width, height);
+      this.nodeHelper.rectangle.render('fill', {x:pos.x+width/2, y:pos.y+height/2}, width, height, canvas);
+    },
+    'contains': function(node, pos) {
+      var width = node.getData('width'),
+          height = node.getData('height'),
+          npos = this.getAlignedPos(node.pos.getc(true), width, height);
+      this.nodeHelper.rectangle.contains({x:npos.x+width/2, y:npos.y+height/2}, pos, width, height);
+    }
   }
 });
 
+/*
+  Class: ST.Plot.EdgeTypes
+
+  This class contains a list of <Graph.Adjacence> built-in types. 
+  Edge types implemented are 'none', 'line', 'arrow', 'quadratic:begin', 'quadratic:end', 'bezier'.
+
+  You can add your custom edge types, customizing your visualization to the extreme.
+
+  Example:
+
+  (start code js)
+    ST.Plot.EdgeTypes.implement({
+      'mySpecialType': {
+        'render': function(adj, canvas) {
+          //print your custom edge to canvas
+        },
+        //optional
+        'contains': function(adj, pos) {
+          //return true if pos is inside the arc or false otherwise
+        }
+      }
+    });
+  (end code)
+
+*/
+$jit.ST.Plot.EdgeTypes = new Class({
+    'none': $.empty,
+    'line': {
+      'render': function(adj, canvas) {
+        var orn = this.getOrientation(adj),
+            nodeFrom = adj.nodeFrom, 
+            nodeTo = adj.nodeTo,
+            rel = nodeFrom._depth < nodeTo._depth,
+            from = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
+            to =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn);
+        this.edgeHelper.line.render(from, to, canvas);
+      },
+      'contains': function(adj, pos) {
+        var orn = this.getOrientation(adj),
+            nodeFrom = adj.nodeFrom, 
+            nodeTo = adj.nodeTo,
+            rel = nodeFrom._depth < nodeTo._depth,
+            from = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
+            to =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn);
+        return this.edgeHelper.line.contains(from, to, pos, this.edge.epsilon);
+      }
+    },
+     'arrow': {
+       'render': function(adj, canvas) {
+         var orn = this.getOrientation(adj),
+             node = adj.nodeFrom, 
+             child = adj.nodeTo,
+             dim = adj.getData('dim'),
+             from = this.viz.geom.getEdge(node, 'begin', orn),
+             to = this.viz.geom.getEdge(child, 'end', orn),
+             direction = adj.data.$direction,
+             inv = (direction && direction.length>1 && direction[0] != node.id);
+         this.edgeHelper.arrow.render(from, to, dim, inv, canvas);
+       },
+       'contains': function(adj, pos) {
+         var orn = this.getOrientation(adj),
+             nodeFrom = adj.nodeFrom, 
+             nodeTo = adj.nodeTo,
+             rel = nodeFrom._depth < nodeTo._depth,
+             from = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
+             to =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn);
+         return this.edgeHelper.arrow.contains(from, to, pos, this.edge.epsilon);
+       }
+     },
+    'quadratic:begin': {
+       'render': function(adj, canvas) {
+          var orn = this.getOrientation(adj);
+          var nodeFrom = adj.nodeFrom, 
+              nodeTo = adj.nodeTo,
+              rel = nodeFrom._depth < nodeTo._depth,
+              begin = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
+              end =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn),
+              dim = adj.getData('dim'),
+              ctx = canvas.getCtx();
+          ctx.beginPath();
+          ctx.moveTo(begin.x, begin.y);
+          switch(orn) {
+            case "left":
+              ctx.quadraticCurveTo(begin.x + dim, begin.y, end.x, end.y);
+              break;
+            case "right":
+              ctx.quadraticCurveTo(begin.x - dim, begin.y, end.x, end.y);
+              break;
+            case "top":
+              ctx.quadraticCurveTo(begin.x, begin.y + dim, end.x, end.y);
+              break;
+            case "bottom":
+              ctx.quadraticCurveTo(begin.x, begin.y - dim, end.x, end.y);
+              break;
+          }
+          ctx.stroke();
+        }
+     },
+    'quadratic:end': {
+       'render': function(adj, canvas) {
+          var orn = this.getOrientation(adj);
+          var nodeFrom = adj.nodeFrom, 
+              nodeTo = adj.nodeTo,
+              rel = nodeFrom._depth < nodeTo._depth,
+              begin = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
+              end =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn),
+              dim = adj.getData('dim'),
+              ctx = canvas.getCtx();
+          ctx.beginPath();
+          ctx.moveTo(begin.x, begin.y);
+          switch(orn) {
+            case "left":
+              ctx.quadraticCurveTo(end.x - dim, end.y, end.x, end.y);
+              break;
+            case "right":
+              ctx.quadraticCurveTo(end.x + dim, end.y, end.x, end.y);
+              break;
+            case "top":
+              ctx.quadraticCurveTo(end.x, end.y - dim, end.x, end.y);
+              break;
+            case "bottom":
+              ctx.quadraticCurveTo(end.x, end.y + dim, end.x, end.y);
+              break;
+          }
+          ctx.stroke();
+       }
+     },
+    'bezier': {
+       'render': function(adj, canvas) {
+         var orn = this.getOrientation(adj),
+             nodeFrom = adj.nodeFrom, 
+             nodeTo = adj.nodeTo,
+             rel = nodeFrom._depth < nodeTo._depth,
+             begin = this.viz.geom.getEdge(rel? nodeFrom:nodeTo, 'begin', orn),
+             end =  this.viz.geom.getEdge(rel? nodeTo:nodeFrom, 'end', orn),
+             dim = adj.getData('dim'),
+             ctx = canvas.getCtx();
+         ctx.beginPath();
+         ctx.moveTo(begin.x, begin.y);
+         switch(orn) {
+           case "left":
+             ctx.bezierCurveTo(begin.x + dim, begin.y, end.x - dim, end.y, end.x, end.y);
+             break;
+           case "right":
+             ctx.bezierCurveTo(begin.x - dim, begin.y, end.x + dim, end.y, end.x, end.y);
+             break;
+           case "top":
+             ctx.bezierCurveTo(begin.x, begin.y + dim, end.x, end.y - dim, end.x, end.y);
+             break;
+           case "bottom":
+             ctx.bezierCurveTo(begin.x, begin.y - dim, end.x, end.y + dim, end.x, end.y);
+             break;
+         }
+         ctx.stroke();
+       }
+    }
+});
+
+
+
+/*
+ * File: AreaChart.js
+ *
+*/
+
+$jit.ST.Plot.NodeTypes.implement({
+  'areachart-stacked' : {
+    'render' : function(node, canvas) {
+      var pos = node.pos.getc(true), 
+          width = node.getData('width'),
+          height = node.getData('height'),
+          algnPos = this.getAlignedPos(pos, width, height),
+          x = algnPos.x, y = algnPos.y,
+          stringArray = node.getData('stringArray'),
+          dimArray = node.getData('dimArray'),
+          valArray = node.getData('valueArray'),
+          valLeft = $.reduce(valArray, function(x, y) { return x + y[0]; }, 0),
+          valRight = $.reduce(valArray, function(x, y) { return x + y[1]; }, 0),
+          colorArray = node.getData('colorArray'),
+          colorLength = colorArray.length,
+          config = node.getData('config'),
+          gradient = node.getData('gradient'),
+          showLabels = config.showLabels,
+          aggregates = config.showAggregates,
+          label = config.Label,
+          prev = node.getData('prev');
+
+      var ctx = canvas.getCtx(), border = node.getData('border');
+      if (colorArray && dimArray && stringArray) {
+        for (var i=0, l=dimArray.length, acumLeft=0, acumRight=0, valAcum=0; i<l; i++) {
+          ctx.fillStyle = ctx.strokeStyle = colorArray[i % colorLength];
+          ctx.save();
+          if(gradient && (dimArray[i][0] > 0 || dimArray[i][1] > 0)) {
+            var h1 = acumLeft + dimArray[i][0],
+                h2 = acumRight + dimArray[i][1],
+                alpha = Math.atan((h2 - h1) / width),
+                delta = 55;
+            var linear = ctx.createLinearGradient(x + width/2, 
+                y - (h1 + h2)/2,
+                x + width/2 + delta * Math.sin(alpha),
+                y - (h1 + h2)/2 + delta * Math.cos(alpha));
+            var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)), 
+                function(v) { return (v * 0.85) >> 0; }));
+            linear.addColorStop(0, colorArray[i % colorLength]);
+            linear.addColorStop(1, color);
+            ctx.fillStyle = linear;
+          }
+          ctx.beginPath();
+          ctx.moveTo(x, y - acumLeft);
+          ctx.lineTo(x + width, y - acumRight);
+          ctx.lineTo(x + width, y - acumRight - dimArray[i][1]);
+          ctx.lineTo(x, y - acumLeft - dimArray[i][0]);
+          ctx.lineTo(x, y - acumLeft);
+          ctx.fill();
+          ctx.restore();
+          if(border) {
+            var strong = border.name == stringArray[i];
+            var perc = strong? 0.7 : 0.8;
+            var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)), 
+                function(v) { return (v * perc) >> 0; }));
+            ctx.strokeStyle = color;
+            ctx.lineWidth = strong? 4 : 1;
+            ctx.save();
+            ctx.beginPath();
+            if(border.index === 0) {
+              ctx.moveTo(x, y - acumLeft);
+              ctx.lineTo(x, y - acumLeft - dimArray[i][0]);
+            } else {
+              ctx.moveTo(x + width, y - acumRight);
+              ctx.lineTo(x + width, y - acumRight - dimArray[i][1]);
+            }
+            ctx.stroke();
+            ctx.restore();
+          }
+          acumLeft += (dimArray[i][0] || 0);
+          acumRight += (dimArray[i][1] || 0);
+          
+          if(dimArray[i][0] > 0)
+            valAcum += (valArray[i][0] || 0);
+        }
+        if(prev && label.type == 'Native') {
+          ctx.save();
+          ctx.beginPath();
+          ctx.fillStyle = ctx.strokeStyle = label.color;
+          ctx.font = label.style + ' ' + label.size + 'px ' + label.family;
+          ctx.textAlign = 'center';
+          ctx.textBaseline = 'middle';
+          var aggValue = aggregates(node.name, valLeft, valRight, node, valAcum);
+          if(aggValue !== false) {
+            ctx.fillText(aggValue !== true? aggValue : valAcum, x, y - acumLeft - config.labelOffset - label.size/2, width);
+          }
+          if(showLabels(node.name, valLeft, valRight, node)) {
+            ctx.fillText(node.name, x, y + label.size/2 + config.labelOffset);
+          }
+          ctx.restore();
+        }
+      }
+    },
+    'contains': function(node, mpos) {
+      var pos = node.pos.getc(true), 
+          width = node.getData('width'),
+          height = node.getData('height'),
+          algnPos = this.getAlignedPos(pos, width, height),
+          x = algnPos.x, y = algnPos.y,
+          dimArray = node.getData('dimArray'),
+          rx = mpos.x - x;
+      //bounding box check
+      if(mpos.x < x || mpos.x > x + width
+        || mpos.y > y || mpos.y < y - height) {
+        return false;
+      }
+      //deep check
+      for(var i=0, l=dimArray.length, lAcum=y, rAcum=y; i<l; i++) {
+        var dimi = dimArray[i];
+        lAcum -= dimi[0];
+        rAcum -= dimi[1];
+        var intersec = lAcum + (rAcum - lAcum) * rx / width;
+        if(mpos.y >= intersec) {
+          var index = +(rx > width/2);
+          return {
+            'name': node.getData('stringArray')[i],
+            'color': node.getData('colorArray')[i],
+            'value': node.getData('valueArray')[i][index],
+            'index': index
+          };
+        }
+      }
+      return false;
+    }
+  }
+});
+
+/*
+  Class: AreaChart
+  
+  A visualization that displays stacked area charts.
+  
+  Constructor Options:
+  
+  See <Options.AreaChart>.
+
+*/
+$jit.AreaChart = new Class({
+  st: null,
+  colors: ["#416D9C", "#70A35E", "#EBB056", "#C74243", "#83548B", "#909291", "#557EAA"],
+  selected: {},
+  busy: false,
+  
+  initialize: function(opt) {
+    this.controller = this.config = 
+      $.merge(Options("Canvas", "Margin", "Label", "AreaChart"), {
+        Label: { type: 'Native' }
+      }, opt);
+    //set functions for showLabels and showAggregates
+    var showLabels = this.config.showLabels,
+        typeLabels = $.type(showLabels),
+        showAggregates = this.config.showAggregates,
+        typeAggregates = $.type(showAggregates);
+    this.config.showLabels = typeLabels == 'function'? showLabels : $.lambda(showLabels);
+    this.config.showAggregates = typeAggregates == 'function'? showAggregates : $.lambda(showAggregates);
+    
+    this.initializeViz();
+  },
+  
+  initializeViz: function() {
+    var config = this.config,
+        that = this,
+        nodeType = config.type.split(":")[0],
+        nodeLabels = {};
+
+    var delegate = new $jit.ST({
+      injectInto: config.injectInto,
+      width: config.width,
+      height: config.height,
+      orientation: "bottom",
+      levelDistance: 0,
+      siblingOffset: 0,
+      subtreeOffset: 0,
+      withLabels: config.Label.type != 'Native',
+      useCanvas: config.useCanvas,
+      Label: {
+        type: config.Label.type
+      },
+      Node: {
+        overridable: true,
+        type: 'areachart-' + nodeType,
+        align: 'left',
+        width: 1,
+        height: 1
+      },
+      Edge: {
+        type: 'none'
+      },
+      Tips: {
+        enable: config.Tips.enable,
+        type: 'Native',
+        force: true,
+        onShow: function(tip, node, contains) {
+          var elem = contains;
+          config.Tips.onShow(tip, elem, node);
+        }
+      },
+      Events: {
+        enable: true,
+        type: 'Native',
+        onClick: function(node, eventInfo, evt) {
+          if(!config.filterOnClick && !config.Events.enable) return;
+          var elem = eventInfo.getContains();
+          if(elem) config.filterOnClick && that.filter(elem.name);
+          config.Events.enable && config.Events.onClick(elem, eventInfo, evt);
+        },
+        onRightClick: function(node, eventInfo, evt) {
+          if(!config.restoreOnRightClick) return;
+          that.restore();
+        },
+        onMouseMove: function(node, eventInfo, evt) {
+          if(!config.selectOnHover) return;
+          if(node) {
+            var elem = eventInfo.getContains();
+            that.select(node.id, elem.name, elem.index);
+          } else {
+            that.select(false, false, false);
+          }
+        }
+      },
+      onCreateLabel: function(domElement, node) {
+        var labelConf = config.Label,
+            valueArray = node.getData('valueArray'),
+            acumLeft = $.reduce(valueArray, function(x, y) { return x + y[0]; }, 0),
+            acumRight = $.reduce(valueArray, function(x, y) { return x + y[1]; }, 0);
+        if(node.getData('prev')) {
+          var nlbs = {
+            wrapper: document.createElement('div'),
+            aggregate: document.createElement('div'),
+            label: document.createElement('div')
+          };
+          var wrapper = nlbs.wrapper,
+              label = nlbs.label,
+              aggregate = nlbs.aggregate,
+              wrapperStyle = wrapper.style,
+              labelStyle = label.style,
+              aggregateStyle = aggregate.style;
+          //store node labels
+          nodeLabels[node.id] = nlbs;
+          //append labels
+          wrapper.appendChild(label);
+          wrapper.appendChild(aggregate);
+          if(!config.showLabels(node.name, acumLeft, acumRight, node)) {
+            label.style.display = 'none';
+          }
+          if(!config.showAggregates(node.name, acumLeft, acumRight, node)) {
+            aggregate.style.display = 'none';
+          }
+          wrapperStyle.position = 'relative';
+          wrapperStyle.overflow = 'visible';
+          wrapperStyle.fontSize = labelConf.size + 'px';
+          wrapperStyle.fontFamily = labelConf.family;
+          wrapperStyle.color = labelConf.color;
+          wrapperStyle.textAlign = 'center';
+          aggregateStyle.position = labelStyle.position = 'absolute';
+          
+          domElement.style.width = node.getData('width') + 'px';
+          domElement.style.height = node.getData('height') + 'px';
+          label.innerHTML = node.name;
+          
+          domElement.appendChild(wrapper);
+        }
+      },
+      onPlaceLabel: function(domElement, node) {
+        if(!node.getData('prev')) return;
+        var labels = nodeLabels[node.id],
+            wrapperStyle = labels.wrapper.style,
+            labelStyle = labels.label.style,
+            aggregateStyle = labels.aggregate.style,
+            width = node.getData('width'),
+            height = node.getData('height'),
+            dimArray = node.getData('dimArray'),
+            valArray = node.getData('valueArray'),
+            acumLeft = $.reduce(valArray, function(x, y) { return x + y[0]; }, 0),
+            acumRight = $.reduce(valArray, function(x, y) { return x + y[1]; }, 0),
+            font = parseInt(wrapperStyle.fontSize, 10),
+            domStyle = domElement.style;
+        
+        if(dimArray && valArray) {
+          if(config.showLabels(node.name, acumLeft, acumRight, node)) {
+            labelStyle.display = '';
+          } else {
+            labelStyle.display = 'none';
+          }
+          var aggValue = config.showAggregates(node.name, acumLeft, acumRight, node);
+          if(aggValue !== false) {
+            aggregateStyle.display = '';
+          } else {
+            aggregateStyle.display = 'none';
+          }
+          wrapperStyle.width = aggregateStyle.width = labelStyle.width = domElement.style.width = width + 'px';
+          aggregateStyle.left = labelStyle.left = -width/2 + 'px';
+          for(var i=0, l=valArray.length, acum=0, leftAcum=0; i<l; i++) {
+            if(dimArray[i][0] > 0) {
+              acum+= valArray[i][0];
+              leftAcum+= dimArray[i][0];
+            }
+          }
+          aggregateStyle.top = (-font - config.labelOffset) + 'px';
+          labelStyle.top = (config.labelOffset + leftAcum) + 'px';
+          domElement.style.top = parseInt(domElement.style.top, 10) - leftAcum + 'px';
+          domElement.style.height = wrapperStyle.height = leftAcum + 'px';
+          labels.aggregate.innerHTML = aggValue !== true? aggValue : acum;
+        }
+      }
+    });
+    
+    var size = delegate.canvas.getSize(),
+        margin = config.Margin;
+    delegate.config.offsetY = -size.height/2 + margin.bottom 
+      + (config.showLabels && (config.labelOffset + config.Label.size));
+    delegate.config.offsetX = (margin.right - margin.left)/2;
+    this.delegate = delegate;
+    this.canvas = this.delegate.canvas;
+  },
+  
+ /*
+  Method: loadJSON
+ 
+  Loads JSON data into the visualization. 
+  
+  Parameters:
+  
+  json - The JSON data format. This format is described in <http://blog.thejit.org/2010/04/24/new-javascript-infovis-toolkit-visualizations/#json-data-format>.
+  
+  Example:
+  (start code js)
+  var areaChart = new $jit.AreaChart(options);
+  areaChart.loadJSON(json);
+  (end code)
+ */  
+  loadJSON: function(json) {
+    var prefix = $.time(), 
+        ch = [], 
+        delegate = this.delegate,
+        name = $.splat(json.label), 
+        color = $.splat(json.color || this.colors),
+        config = this.config,
+        gradient = !!config.type.split(":")[1],
+        animate = config.animate;
+    
+    for(var i=0, values=json.values, l=values.length; i<l-1; i++) {
+      var val = values[i], prev = values[i-1], next = values[i+1];
+      var valLeft = $.splat(values[i].values), valRight = $.splat(values[i+1].values);
+      var valArray = $.zip(valLeft, valRight);
+      var acumLeft = 0, acumRight = 0;
+      ch.push({
+        'id': prefix + val.label,
+        'name': val.label,
+        'data': {
+          'value': valArray,
+          '$valueArray': valArray,
+          '$colorArray': color,
+          '$stringArray': name,
+          '$next': next.label,
+          '$prev': prev? prev.label:false,
+          '$config': config,
+          '$gradient': gradient
+        },
+        'children': []
+      });
+    }
+    var root = {
+      'id': prefix + '$root',
+      'name': '',
+      'data': {
+        '$type': 'none',
+        '$width': 1,
+        '$height': 1
+      },
+      'children': ch
+    };
+    delegate.loadJSON(root);
+    
+    this.normalizeDims();
+    delegate.compute();
+    delegate.select(delegate.root);
+    if(animate) {
+      delegate.fx.animate({
+        modes: ['node-property:height:dimArray'],
+        duration:1500
+      });
+    }
+  },
+  
+ /*
+  Method: updateJSON
+ 
+  Use this method when updating values for the current JSON data. If the items specified by the JSON data already exist in the graph then their values will be updated.
+  
+  Parameters:
+  
+  json - (object) JSON data to be updated. The JSON format corresponds to the one described in <AreaChart.loadJSON>.
+  onComplete - (object) A callback object to be called when the animation transition when updating the data end.
+  
+  Example:
+  
+  (start code js)
+  areaChart.updateJSON(json, {
+    onComplete: function() {
+      alert('update complete!');
+    }
+  });
+  (end code)
+ */  
+  updateJSON: function(json, onComplete) {
+    if(this.busy) return;
+    this.busy = true;
+    
+    var delegate = this.delegate,
+        graph = delegate.graph,
+        labels = json.label && $.splat(json.label),
+        values = json.values,
+        animate = this.config.animate,
+        that = this,
+        hashValues = {};
+
+    //convert the whole thing into a hash
+    for (var i = 0, l = values.length; i < l; i++) {
+      hashValues[values[i].label] = values[i];
+    }
+  
+    graph.eachNode(function(n) {
+      var v = hashValues[n.name],
+          stringArray = n.getData('stringArray'),
+          valArray = n.getData('valueArray'),
+          next = n.getData('next');
+      
+      if (v) {
+        v.values = $.splat(v.values);
+        $.each(valArray, function(a, i) {
+          a[0] = v.values[i];
+          if(labels) stringArray[i] = labels[i];
+        });
+        n.setData('valueArray', valArray);
+      }
+     
+      if(next) {
+        v = hashValues[next];
+        if(v) {
+          $.each(valArray, function(a, i) {
+            a[1] = v.values[i];
+          });
+        }
+      }
+    });
+    this.normalizeDims();
+    delegate.compute();
+    delegate.select(delegate.root);
+    if(animate) {
+      delegate.fx.animate({
+        modes: ['node-property:height:dimArray'],
+        duration:1500,
+        onComplete: function() {
+          that.busy = false;
+          onComplete && onComplete.onComplete();
+        }
+      });
+    }
+  },
+  
+/*
+  Method: filter
+ 
+  Filter selected stacks, collapsing all other stacks. You can filter multiple stacks at the same time.
+  
+  Parameters:
+  
+  filters - (array) An array of strings with the name of the stacks to be filtered.
+  callback - (object) An object with an *onComplete* callback method. 
+  
+  Example:
+  
+  (start code js)
+  areaChart.filter(['label A', 'label C'], {
+      onComplete: function() {
+          console.log('done!');
+      }
+  });
+  (end code)
+  
+  See also:
+  
+  <AreaChart.restore>.
+ */  
+  filter: function(filters, callback) {
+    if(this.busy) return;
+    this.busy = true;
+    if(this.config.Tips.enable) this.delegate.tips.hide();
+    this.select(false, false, false);
+    var args = $.splat(filters);
+    var rt = this.delegate.graph.getNode(this.delegate.root);
+    var that = this;
+    this.normalizeDims();
+    rt.eachAdjacency(function(adj) {
+      var n = adj.nodeTo, 
+          dimArray = n.getData('dimArray', 'end'),
+          stringArray = n.getData('stringArray');
+      n.setData('dimArray', $.map(dimArray, function(d, i) {
+        return ($.indexOf(args, stringArray[i]) > -1)? d:[0, 0];
+      }), 'end');
+    });
+    this.delegate.fx.animate({
+      modes: ['node-property:dimArray'],
+      duration:1500,
+      onComplete: function() {
+        that.busy = false;
+        callback && callback.onComplete();
+      }
+    });
+  },
+  
+  /*
+  Method: restore
+ 
+  Sets all stacks that could have been filtered visible.
+  
+  Example:
+  
+  (start code js)
+  areaChart.restore();
+  (end code)
+  
+  See also:
+  
+  <AreaChart.filter>.
+ */  
+  restore: function(callback) {
+    if(this.busy) return;
+    this.busy = true;
+    if(this.config.Tips.enable) this.delegate.tips.hide();
+    this.select(false, false, false);
+    this.normalizeDims();
+    var that = this;
+    this.delegate.fx.animate({
+      modes: ['node-property:height:dimArray'],
+      duration:1500,
+      onComplete: function() {
+        that.busy = false;
+        callback && callback.onComplete();
+      }
+    });
+  },
+  //adds the little brown bar when hovering the node
+  select: function(id, name, index) {
+    if(!this.config.selectOnHover) return;
+    var s = this.selected;
+    if(s.id != id || s.name != name 
+        || s.index != index) {
+      s.id = id;
+      s.name = name;
+      s.index = index;
+      this.delegate.graph.eachNode(function(n) {
+        n.setData('border', false);
+      });
+      if(id) {
+        var n = this.delegate.graph.getNode(id);
+        n.setData('border', s);
+        var link = index === 0? 'prev':'next';
+        link = n.getData(link);
+        if(link) {
+          n = this.delegate.graph.getByName(link);
+          if(n) {
+            n.setData('border', {
+              name: name,
+              index: 1-index
+            });
+          }
+        }
+      }
+      this.delegate.plot();
+    }
+  },
+  
+  /*
+    Method: getLegend
+   
+    Returns an object containing as keys the legend names and as values hex strings with color values.
+    
+    Example:
+    
+    (start code js)
+    var legend = areaChart.getLegend();
+    (end code)
+ */  
+  getLegend: function() {
+    var legend = {};
+    var n;
+    this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(adj) {
+      n = adj.nodeTo;
+    });
+    var colors = n.getData('colorArray'),
+        len = colors.length;
+    $.each(n.getData('stringArray'), function(s, i) {
+      legend[s] = colors[i % len];
+    });
+    return legend;
+  },
+  
+  /*
+    Method: getMaxValue
+   
+    Returns the maximum accumulated value for the stacks. This method is used for normalizing the graph heights according to the canvas height.
+    
+    Example:
+    
+    (start code js)
+    var ans = areaChart.getMaxValue();
+    (end code)
+    
+    In some cases it could be useful to override this method to normalize heights for a group of AreaCharts, like when doing small multiples.
+    
+    Example:
+    
+    (start code js)
+    //will return 100 for all AreaChart instances,
+    //displaying all of them with the same scale
+    $jit.AreaChart.implement({
+      'getMaxValue': function() {
+        return 100;
+      }
+    });
+    (end code)
+    
+*/  
+  getMaxValue: function() {
+    var maxValue = 0;
+    this.delegate.graph.eachNode(function(n) {
+      var valArray = n.getData('valueArray'),
+          acumLeft = 0, acumRight = 0;
+      $.each(valArray, function(v) { 
+        acumLeft += +v[0];
+        acumRight += +v[1];
+      });
+      var acum = acumRight>acumLeft? acumRight:acumLeft;
+      maxValue = maxValue>acum? maxValue:acum;
+    });
+    return maxValue;
+  },
+  
+  normalizeDims: function() {
+    //number of elements
+    var root = this.delegate.graph.getNode(this.delegate.root), l=0;
+    root.eachAdjacency(function() {
+      l++;
+    });
+    var maxValue = this.getMaxValue() || 1,
+        size = this.delegate.canvas.getSize(),
+        config = this.config,
+        margin = config.Margin,
+        labelOffset = config.labelOffset + config.Label.size,
+        fixedDim = (size.width - (margin.left + margin.right)) / l,
+        animate = config.animate,
+        height = size.height - (margin.top + margin.bottom) - (config.showAggregates && labelOffset) 
+          - (config.showLabels && labelOffset);
+    this.delegate.graph.eachNode(function(n) {
+      var acumLeft = 0, acumRight = 0, animateValue = [];
+      $.each(n.getData('valueArray'), function(v) {
+        acumLeft += +v[0];
+        acumRight += +v[1];
+        animateValue.push([0, 0]);
+      });
+      var acum = acumRight>acumLeft? acumRight:acumLeft;
+      n.setData('width', fixedDim);
+      if(animate) {
+        n.setData('height', acum * height / maxValue, 'end');
+        n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
+          return [n[0] * height / maxValue, n[1] * height / maxValue]; 
+        }), 'end');
+        var dimArray = n.getData('dimArray');
+        if(!dimArray) {
+          n.setData('dimArray', animateValue);
+        }
+      } else {
+        n.setData('height', acum * height / maxValue);
+        n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
+          return [n[0] * height / maxValue, n[1] * height / maxValue]; 
+        }));
+      }
+    });
+  }
+});
+
+
+/*
+ * File: Options.BarChart.js
+ *
+*/
+
+/*
+  Object: Options.BarChart
+  
+  <BarChart> options. 
+  Other options included in the BarChart are <Options.Canvas>, <Options.Label>, <Options.Margin>, <Options.Tips> and <Options.Events>.
+  
+  Syntax:
+  
+  (start code js)
+
+  Options.BarChart = {
+    animate: true,
+    labelOffset: 3,
+    barsOffset: 0,
+    type: 'stacked',
+    hoveredColor: '#9fd4ff',
+    orientation: 'horizontal',
+    showAggregates: true,
+    showLabels: true
+  };
+  
+  (end code)
+  
+  Example:
+  
+  (start code js)
+
+  var barChart = new $jit.BarChart({
+    animate: true,
+    barsOffset: 10,
+    type: 'stacked:gradient'
+  });
+  
+  (end code)
+
+  Parameters:
+  
+  animate - (boolean) Default's *true*. Whether to add animated transitions when filtering/restoring stacks.
+  offset - (number) Default's *25*. Adds margin between the visualization and the canvas.
+  labelOffset - (number) Default's *3*. Adds margin between the label and the default place where it should be drawn.
+  barsOffset - (number) Default's *0*. Separation between bars.
+  type - (string) Default's *'stacked'*. Stack or grouped styles. Posible values are 'stacked', 'grouped', 'stacked:gradient', 'grouped:gradient' to add gradients.
+  hoveredColor - (boolean|string) Default's *'#9fd4ff'*. Sets the selected color for a hovered bar stack.
+  orientation - (string) Default's 'horizontal'. Sets the direction of the bars. Possible options are 'vertical' or 'horizontal'.
+  showAggregates - (boolean, function) Default's *true*. Display the sum the values of each bar. Can also be a function that returns *true* or *false* to display the value of the bar or not. That same function can also return a string with the formatted data to be added.
+  showLabels - (boolean, function) Default's *true*. Display the name of the slots. Can also be a function that returns *true* or *false* for each bar to decide whether to show the label or not.
+  
+*/
+
+Options.BarChart = {
+  $extend: true,
+  
+  animate: true,
+  type: 'stacked', //stacked, grouped, : gradient
+  labelOffset: 3, //label offset
+  barsOffset: 0, //distance between bars
+  hoveredColor: '#9fd4ff',
+  orientation: 'horizontal',
+  showAggregates: true,
+  showLabels: true,
+  Tips: {
+    enable: false,
+    onShow: $.empty,
+    onHide: $.empty
+  },
+  Events: {
+    enable: false,
+    onClick: $.empty
+  }
+};
+
+/*
+ * File: BarChart.js
+ *
+*/
+
+$jit.ST.Plot.NodeTypes.implement({
+  'barchart-stacked' : {
+    'render' : function(node, canvas) {
+      var pos = node.pos.getc(true), 
+          width = node.getData('width'),
+          height = node.getData('height'),
+          algnPos = this.getAlignedPos(pos, width, height),
+          x = algnPos.x, y = algnPos.y,
+          dimArray = node.getData('dimArray'),
+          valueArray = node.getData('valueArray'),
+          colorArray = node.getData('colorArray'),
+          colorLength = colorArray.length,
+          stringArray = node.getData('stringArray');
+
+      var ctx = canvas.getCtx(),
+          opt = {},
+          border = node.getData('border'),
+          gradient = node.getData('gradient'),
+          config = node.getData('config'),
+          horz = config.orientation == 'horizontal',
+          aggregates = config.showAggregates,
+          showLabels = config.showLabels,
+          label = config.Label;
+      
+      if (colorArray && dimArray && stringArray) {
+        for (var i=0, l=dimArray.length, acum=0, valAcum=0; i<l; i++) {
+          ctx.fillStyle = ctx.strokeStyle = colorArray[i % colorLength];
+          if(gradient) {
+            var linear;
+            if(horz) {
+              linear = ctx.createLinearGradient(x + acum + dimArray[i]/2, y, 
+                  x + acum + dimArray[i]/2, y + height);
+            } else {
+              linear = ctx.createLinearGradient(x, y - acum - dimArray[i]/2, 
+                  x + width, y - acum- dimArray[i]/2);
+            }
+            var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)), 
+                function(v) { return (v * 0.5) >> 0; }));
+            linear.addColorStop(0, color);
+            linear.addColorStop(0.5, colorArray[i % colorLength]);
+            linear.addColorStop(1, color);
+            ctx.fillStyle = linear;
+          }
+          if(horz) {
+            ctx.fillRect(x + acum, y, dimArray[i], height);
+          } else {
+            ctx.fillRect(x, y - acum - dimArray[i], width, dimArray[i]);
+          }
+          if(border && border.name == stringArray[i]) {
+            opt.acum = acum;
+            opt.dimValue = dimArray[i];
+          }
+          acum += (dimArray[i] || 0);
+          valAcum += (valueArray[i] || 0);
+        }
+        if(border) {
+          ctx.save();
+          ctx.lineWidth = 2;
+          ctx.strokeStyle = border.color;
+          if(horz) {
+            ctx.strokeRect(x + opt.acum + 1, y + 1, opt.dimValue -2, height - 2);
+          } else {
+            ctx.strokeRect(x + 1, y - opt.acum - opt.dimValue + 1, width -2, opt.dimValue -2);
+          }
+          ctx.restore();
+        }
+        if(label.type == 'Native') {
+          ctx.save();
+          ctx.fillStyle = ctx.strokeStyle = label.color;
+          ctx.font = label.style + ' ' + label.size + 'px ' + label.family;
+          ctx.textBaseline = 'middle';
+          var aggValue = aggregates(node.name, valAcum, node);
+          if(aggValue !== false) {
+            aggValue = aggValue !== true? aggValue : valAcum;
+            if(horz) {
+              ctx.textAlign = 'right';
+              ctx.fillText(aggValue, x + acum - config.labelOffset, y + height/2);
+            } else {
+              ctx.textAlign = 'center';
+              ctx.fillText(aggValue, x + width/2, y - height - label.size/2 - config.labelOffset);
+            }
+          }
+          if(showLabels(node.name, valAcum, node)) {
+            if(horz) {
+              ctx.textAlign = 'center';
+              ctx.translate(x - config.labelOffset - label.size/2, y + height/2);
+              ctx.rotate(Math.PI / 2);
+              ctx.fillText(node.name, 0, 0);
+            } else {
+              ctx.textAlign = 'center';
+              ctx.fillText(node.name, x + width/2, y + label.size/2 + config.labelOffset);
+            }
+          }
+          ctx.restore();
+        }
+      }
+    },
+    'contains': function(node, mpos) {
+      var pos = node.pos.getc(true), 
+          width = node.getData('width'),
+          height = node.getData('height'),
+          algnPos = this.getAlignedPos(pos, width, height),
+          x = algnPos.x, y = algnPos.y,
+          dimArray = node.getData('dimArray'),
+          config = node.getData('config'),
+          rx = mpos.x - x,
+          horz = config.orientation == 'horizontal';
+      //bounding box check
+      if(horz) {
+        if(mpos.x < x || mpos.x > x + width
+            || mpos.y > y + height || mpos.y < y) {
+            return false;
+          }
+      } else {
+        if(mpos.x < x || mpos.x > x + width
+            || mpos.y > y || mpos.y < y - height) {
+            return false;
+          }
+      }
+      //deep check
+      for(var i=0, l=dimArray.length, acum=(horz? x:y); i<l; i++) {
+        var dimi = dimArray[i];
+        if(horz) {
+          acum += dimi;
+          var intersec = acum;
+          if(mpos.x <= intersec) {
+            return {
+              'name': node.getData('stringArray')[i],
+              'color': node.getData('colorArray')[i],
+              'value': node.getData('valueArray')[i],
+              'label': node.name
+            };
+          }
+        } else {
+          acum -= dimi;
+          var intersec = acum;
+          if(mpos.y >= intersec) {
+            return {
+              'name': node.getData('stringArray')[i],
+              'color': node.getData('colorArray')[i],
+              'value': node.getData('valueArray')[i],
+              'label': node.name
+            };
+          }
+        }
+      }
+      return false;
+    }
+  },
+  'barchart-grouped' : {
+    'render' : function(node, canvas) {
+      var pos = node.pos.getc(true), 
+          width = node.getData('width'),
+          height = node.getData('height'),
+          algnPos = this.getAlignedPos(pos, width, height),
+          x = algnPos.x, y = algnPos.y,
+          dimArray = node.getData('dimArray'),
+          valueArray = node.getData('valueArray'),
+          valueLength = valueArray.length,
+          colorArray = node.getData('colorArray'),
+          colorLength = colorArray.length,
+          stringArray = node.getData('stringArray'); 
+
+      var ctx = canvas.getCtx(),
+          opt = {},
+          border = node.getData('border'),
+          gradient = node.getData('gradient'),
+          config = node.getData('config'),
+          horz = config.orientation == 'horizontal',
+          aggregates = config.showAggregates,
+          showLabels = config.showLabels,
+          label = config.Label,
+          fixedDim = (horz? height : width) / valueLength;
+      
+      if (colorArray && dimArray && stringArray) {
+        for (var i=0, l=valueLength, acum=0, valAcum=0; i<l; i++) {
+          ctx.fillStyle = ctx.strokeStyle = colorArray[i % colorLength];
+          if(gradient) {
+            var linear;
+            if(horz) {
+              linear = ctx.createLinearGradient(x + dimArray[i]/2, y + fixedDim * i, 
+                  x + dimArray[i]/2, y + fixedDim * (i + 1));
+            } else {
+              linear = ctx.createLinearGradient(x + fixedDim * i, y - dimArray[i]/2, 
+                  x + fixedDim * (i + 1), y - dimArray[i]/2);
+            }
+            var color = $.rgbToHex($.map($.hexToRgb(colorArray[i % colorLength].slice(1)), 
+                function(v) { return (v * 0.5) >> 0; }));
+            linear.addColorStop(0, color);
+            linear.addColorStop(0.5, colorArray[i % colorLength]);
+            linear.addColorStop(1, color);
+            ctx.fillStyle = linear;
+          }
+          if(horz) {
+            ctx.fillRect(x, y + fixedDim * i, dimArray[i], fixedDim);
+          } else {
+            ctx.fillRect(x + fixedDim * i, y - dimArray[i], fixedDim, dimArray[i]);
+          }
+          if(border && border.name == stringArray[i]) {
+            opt.acum = fixedDim * i;
+            opt.dimValue = dimArray[i];
+          }
+          acum += (dimArray[i] || 0);
+          valAcum += (valueArray[i] || 0);
+        }
+        if(border) {
+          ctx.save();
+          ctx.lineWidth = 2;
+          ctx.strokeStyle = border.color;
+          if(horz) {
+            ctx.strokeRect(x + 1, y + opt.acum + 1, opt.dimValue -2, fixedDim - 2);
+          } else {
+            ctx.strokeRect(x + opt.acum + 1, y - opt.dimValue + 1, fixedDim -2, opt.dimValue -2);
+          }
+          ctx.restore();
+        }
+        if(label.type == 'Native') {
+          ctx.save();
+          ctx.fillStyle = ctx.strokeStyle = label.color;
+          ctx.font = label.style + ' ' + label.size + 'px ' + label.family;
+          ctx.textBaseline = 'middle';
+          var aggValue = aggregates(node.name, valAcum, node);
+          if(aggValue !== false) {
+            aggValue = aggValue !== true? aggValue : valAcum;
+            if(horz) {
+              ctx.textAlign = 'right';
+              ctx.fillText(aggValue, x + Math.max.apply(null, dimArray) - config.labelOffset, y + height/2);
+            } else {
+              ctx.textAlign = 'center';
+              ctx.fillText(aggValue, x + width/2, y - Math.max.apply(null, dimArray) - label.size/2 - config.labelOffset);
+            }
+          }
+          if(showLabels(node.name, valAcum, node)) {
+            if(horz) {
+              ctx.textAlign = 'center';
+              ctx.translate(x - config.labelOffset - label.size/2, y + height/2);
+              ctx.rotate(Math.PI / 2);
+              ctx.fillText(node.name, 0, 0);
+            } else {
+              ctx.textAlign = 'center';
+              ctx.fillText(node.name, x + width/2, y + label.size/2 + config.labelOffset);
+            }
+          }
+          ctx.restore();
+        }
+      }
+    },
+    'contains': function(node, mpos) {
+      var pos = node.pos.getc(true), 
+          width = node.getData('width'),
+          height = node.getData('height'),
+          algnPos = this.getAlignedPos(pos, width, height),
+          x = algnPos.x, y = algnPos.y,
+          dimArray = node.getData('dimArray'),
+          len = dimArray.length,
+          config = node.getData('config'),
+          rx = mpos.x - x,
+          horz = config.orientation == 'horizontal',
+          fixedDim = (horz? height : width) / len;
+      //bounding box check
+      if(horz) {
+        if(mpos.x < x || mpos.x > x + width
+            || mpos.y > y + height || mpos.y < y) {
+            return false;
+          }
+      } else {
+        if(mpos.x < x || mpos.x > x + width
+            || mpos.y > y || mpos.y < y - height) {
+            return false;
+          }
+      }
+      //deep check
+      for(var i=0, l=dimArray.length; i<l; i++) {
+        var dimi = dimArray[i];
+        if(horz) {
+          var limit = y + fixedDim * i;
+          if(mpos.x <= x+ dimi && mpos.y >= limit && mpos.y <= limit + fixedDim) {
+            return {
+              'name': node.getData('stringArray')[i],
+              'color': node.getData('colorArray')[i],
+              'value': node.getData('valueArray')[i],
+              'label': node.name
+            };
+          }
+        } else {
+          var limit = x + fixedDim * i;
+          if(mpos.x >= limit && mpos.x <= limit + fixedDim && mpos.y >= y - dimi) {
+            return {
+              'name': node.getData('stringArray')[i],
+              'color': node.getData('colorArray')[i],
+              'value': node.getData('valueArray')[i],
+              'label': node.name
+            };
+          }
+        }
+      }
+      return false;
+    }
+  }
+});
+
+/*
+  Class: BarChart
+  
+  A visualization that displays stacked bar charts.
+  
+  Constructor Options:
+  
+  See <Options.BarChart>.
+
+*/
+$jit.BarChart = new Class({
+  st: null,
+  colors: ["#416D9C", "#70A35E", "#EBB056", "#C74243", "#83548B", "#909291", "#557EAA"],
+  selected: {},
+  busy: false,
+  
+  initialize: function(opt) {
+    this.controller = this.config = 
+      $.merge(Options("Canvas", "Margin", "Label", "BarChart"), {
+        Label: { type: 'Native' }
+      }, opt);
+    //set functions for showLabels and showAggregates
+    var showLabels = this.config.showLabels,
+        typeLabels = $.type(showLabels),
+        showAggregates = this.config.showAggregates,
+        typeAggregates = $.type(showAggregates);
+    this.config.showLabels = typeLabels == 'function'? showLabels : $.lambda(showLabels);
+    this.config.showAggregates = typeAggregates == 'function'? showAggregates : $.lambda(showAggregates);
+    
+    this.initializeViz();
+  },
+  
+  initializeViz: function() {
+    var config = this.config, that = this;
+    var nodeType = config.type.split(":")[0],
+        horz = config.orientation == 'horizontal',
+        nodeLabels = {};
+    
+    var delegate = new $jit.ST({
+      injectInto: config.injectInto,
+      width: config.width,
+      height: config.height,
+      orientation: horz? 'left' : 'bottom',
+      levelDistance: 0,
+      siblingOffset: config.barsOffset,
+      subtreeOffset: 0,
+      withLabels: config.Label.type != 'Native',      
+      useCanvas: config.useCanvas,
+      Label: {
+        type: config.Label.type
+      },
+      Node: {
+        overridable: true,
+        type: 'barchart-' + nodeType,
+        align: 'left',
+        width: 1,
+        height: 1
+      },
+      Edge: {
+        type: 'none'
+      },
+      Tips: {
+        enable: config.Tips.enable,
+        type: 'Native',
+        force: true,
+        onShow: function(tip, node, contains) {
+          var elem = contains;
+          config.Tips.onShow(tip, elem, node);
+        }
+      },
+      Events: {
+        enable: true,
+        type: 'Native',
+        onClick: function(node, eventInfo, evt) {
+          if(!config.Events.enable) return;
+          var elem = eventInfo.getContains();
+          config.Events.onClick(elem, eventInfo, evt);
+        },
+        onMouseMove: function(node, eventInfo, evt) {
+          if(!config.hoveredColor) return;
+          if(node) {
+            var elem = eventInfo.getContains();
+            that.select(node.id, elem.name, elem.index);
+          } else {
+            that.select(false, false, false);
+          }
+        }
+      },
+      onCreateLabel: function(domElement, node) {
+        var labelConf = config.Label,
+            valueArray = node.getData('valueArray'),
+            acum = $.reduce(valueArray, function(x, y) { return x + y; }, 0);
+        var nlbs = {
+          wrapper: document.createElement('div'),
+          aggregate: document.createElement('div'),
+          label: document.createElement('div')
+        };
+        var wrapper = nlbs.wrapper,
+            label = nlbs.label,
+            aggregate = nlbs.aggregate,
+            wrapperStyle = wrapper.style,
+            labelStyle = label.style,
+            aggregateStyle = aggregate.style;
+        //store node labels
+        nodeLabels[node.id] = nlbs;
+        //append labels
+        wrapper.appendChild(label);
+        wrapper.appendChild(aggregate);
+        if(!config.showLabels(node.name, acum, node)) {
+          labelStyle.display = 'none';
+        }
+        if(!config.showAggregates(node.name, acum, node)) {
+          aggregateStyle.display = 'none';
+        }
+        wrapperStyle.position = 'relative';
+        wrapperStyle.overflow = 'visible';
+        wrapperStyle.fontSize = labelConf.size + 'px';
+        wrapperStyle.fontFamily = labelConf.family;
+        wrapperStyle.color = labelConf.color;
+        wrapperStyle.textAlign = 'center';
+        aggregateStyle.position = labelStyle.position = 'absolute';
+        
+        domElement.style.width = node.getData('width') + 'px';
+        domElement.style.height = node.getData('height') + 'px';
+        aggregateStyle.left = labelStyle.left =  '0px';
+
+        label.innerHTML = node.name;
+        
+        domElement.appendChild(wrapper);
+      },
+      onPlaceLabel: function(domElement, node) {
+        if(!nodeLabels[node.id]) return;
+        var labels = nodeLabels[node.id],
+            wrapperStyle = labels.wrapper.style,
+            labelStyle = labels.label.style,
+            aggregateStyle = labels.aggregate.style,
+            grouped = config.type.split(':')[0] == 'grouped',
+            horz = config.orientation == 'horizontal',
+            dimArray = node.getData('dimArray'),
+            valArray = node.getData('valueArray'),
+            width = (grouped && horz)? Math.max.apply(null, dimArray) : node.getData('width'),
+            height = (grouped && !horz)? Math.max.apply(null, dimArray) : node.getData('height'),
+            font = parseInt(wrapperStyle.fontSize, 10),
+            domStyle = domElement.style;
+            
+        
+        if(dimArray && valArray) {
+          wrapperStyle.width = aggregateStyle.width = labelStyle.width = domElement.style.width = width + 'px';
+          for(var i=0, l=valArray.length, acum=0; i<l; i++) {
+            if(dimArray[i] > 0) {
+              acum+= valArray[i];
+            }
+          }
+          if(config.showLabels(node.name, acum, node)) {
+            labelStyle.display = '';
+          } else {
+            labelStyle.display = 'none';
+          }
+          var aggValue = config.showAggregates(node.name, acum, node);
+          if(aggValue !== false) {
+            aggregateStyle.display = '';
+          } else {
+            aggregateStyle.display = 'none';
+          }
+          if(config.orientation == 'horizontal') {
+            aggregateStyle.textAlign = 'right';
+            labelStyle.textAlign = 'left';
+            labelStyle.textIndex = aggregateStyle.textIndent = config.labelOffset + 'px';
+            aggregateStyle.top = labelStyle.top = (height-font)/2 + 'px';
+            domElement.style.height = wrapperStyle.height = height + 'px';
+          } else {
+            aggregateStyle.top = (-font - config.labelOffset) + 'px';
+            labelStyle.top = (config.labelOffset + height) + 'px';
+            domElement.style.top = parseInt(domElement.style.top, 10) - height + 'px';
+            domElement.style.height = wrapperStyle.height = height + 'px';
+          }
+          labels.aggregate.innerHTML = aggValue !== true? aggValue : acum;
+        }
+      }
+    });
+    
+    var size = delegate.canvas.getSize(),
+        margin = config.Margin;
+    if(horz) {
+      delegate.config.offsetX = size.width/2 - margin.left
+        - (config.showLabels && (config.labelOffset + config.Label.size));    
+      delegate.config.offsetY = (margin.bottom - margin.top)/2;
+    } else {
+      delegate.config.offsetY = -size.height/2 + margin.bottom 
+        + (config.showLabels && (config.labelOffset + config.Label.size));
+      delegate.config.offsetX = (margin.right - margin.left)/2;
+    }
+    this.delegate = delegate;
+    this.canvas = this.delegate.canvas;
+  },
+  
+  /*
+    Method: loadJSON
+   
+    Loads JSON data into the visualization. 
+    
+    Parameters:
+    
+    json - The JSON data format. This format is described in <http://blog.thejit.org/2010/04/24/new-javascript-infovis-toolkit-visualizations/#json-data-format>.
+    
+    Example:
+    (start code js)
+    var barChart = new $jit.BarChart(options);
+    barChart.loadJSON(json);
+    (end code)
+ */  
+  loadJSON: function(json) {
+    if(this.busy) return;
+    this.busy = true;
+    
+    var prefix = $.time(), 
+        ch = [], 
+        delegate = this.delegate,
+        name = $.splat(json.label), 
+        color = $.splat(json.color || this.colors),
+        config = this.config,
+        gradient = !!config.type.split(":")[1],
+        animate = config.animate,
+        horz = config.orientation == 'horizontal',
+        that = this;
+    
+    for(var i=0, values=json.values, l=values.length; i<l; i++) {
+      var val = values[i]
+      var valArray = $.splat(values[i].values);
+      var acum = 0;
+      ch.push({
+        'id': prefix + val.label,
+        'name': val.label,
+        'data': {
+          'value': valArray,
+          '$valueArray': valArray,
+          '$colorArray': color,
+          '$stringArray': name,
+          '$gradient': gradient,
+          '$config': config
+        },
+        'children': []
+      });
+    }
+    var root = {
+      'id': prefix + '$root',
+      'name': '',
+      'data': {
+        '$type': 'none',
+        '$width': 1,
+        '$height': 1
+      },
+      'children': ch
+    };
+    delegate.loadJSON(root);
+    
+    this.normalizeDims();
+    delegate.compute();
+    delegate.select(delegate.root);
+    if(animate) {
+      if(horz) {
+        delegate.fx.animate({
+          modes: ['node-property:width:dimArray'],
+          duration:1500,
+          onComplete: function() {
+            that.busy = false;
+          }
+        });
+      } else {
+        delegate.fx.animate({
+          modes: ['node-property:height:dimArray'],
+          duration:1500,
+          onComplete: function() {
+            that.busy = false;
+          }
+        });
+      }
+    } else {
+      this.busy = false;
+    }
+  },
+  
+  /*
+    Method: updateJSON
+   
+    Use this method when updating values for the current JSON data. If the items specified by the JSON data already exist in the graph then their values will be updated.
+    
+    Parameters:
+    
+    json - (object) JSON data to be updated. The JSON format corresponds to the one described in <BarChart.loadJSON>.
+    onComplete - (object) A callback object to be called when the animation transition when updating the data end.
+    
+    Example:
+    
+    (start code js)
+    barChart.updateJSON(json, {
+      onComplete: function() {
+        alert('update complete!');
+      }
+    });
+    (end code)
+ */  
+  updateJSON: function(json, onComplete) {
+    if(this.busy) return;
+    this.busy = true;
+    this.select(false, false, false);
+    var delegate = this.delegate;
+    var graph = delegate.graph;
+    var values = json.values;
+    var animate = this.config.animate;
+    var that = this;
+    var horz = this.config.orientation == 'horizontal';
+    $.each(values, function(v) {
+      var n = graph.getByName(v.label);
+      if(n) {
+        n.setData('valueArray', $.splat(v.values));
+        if(json.label) {
+          n.setData('stringArray', $.splat(json.label));
+        }
+      }
+    });
+    this.normalizeDims();
+    delegate.compute();
+    delegate.select(delegate.root);
+    if(animate) {
+      if(horz) {
+        delegate.fx.animate({
+          modes: ['node-property:width:dimArray'],
+          duration:1500,
+          onComplete: function() {
+            that.busy = false;
+            onComplete && onComplete.onComplete();
+          }
+        });
+      } else {
+        delegate.fx.animate({
+          modes: ['node-property:height:dimArray'],
+          duration:1500,
+          onComplete: function() {
+            that.busy = false;
+            onComplete && onComplete.onComplete();
+          }
+        });
+      }
+    }
+  },
+  
+  //adds the little brown bar when hovering the node
+  select: function(id, name) {
+    if(!this.config.hoveredColor) return;
+    var s = this.selected;
+    if(s.id != id || s.name != name) {
+      s.id = id;
+      s.name = name;
+      s.color = this.config.hoveredColor;
+      this.delegate.graph.eachNode(function(n) {
+        if(id == n.id) {
+          n.setData('border', s);
+        } else {
+          n.setData('border', false);
+        }
+      });
+      this.delegate.plot();
+    }
+  },
+  
+  /*
+    Method: getLegend
+   
+    Returns an object containing as keys the legend names and as values hex strings with color values.
+    
+    Example:
+    
+    (start code js)
+    var legend = barChart.getLegend();
+    (end code)
+  */  
+  getLegend: function() {
+    var legend = {};
+    var n;
+    this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(adj) {
+      n = adj.nodeTo;
+    });
+    var colors = n.getData('colorArray'),
+        len = colors.length;
+    $.each(n.getData('stringArray'), function(s, i) {
+      legend[s] = colors[i % len];
+    });
+    return legend;
+  },
+  
+  /*
+    Method: getMaxValue
+   
+    Returns the maximum accumulated value for the stacks. This method is used for normalizing the graph heights according to the canvas height.
+    
+    Example:
+    
+    (start code js)
+    var ans = barChart.getMaxValue();
+    (end code)
+    
+    In some cases it could be useful to override this method to normalize heights for a group of BarCharts, like when doing small multiples.
+    
+    Example:
+    
+    (start code js)
+    //will return 100 for all BarChart instances,
+    //displaying all of them with the same scale
+    $jit.BarChart.implement({
+      'getMaxValue': function() {
+        return 100;
+      }
+    });
+    (end code)
+    
+  */  
+  getMaxValue: function() {
+    var maxValue = 0, stacked = this.config.type.split(':')[0] == 'stacked';
+    this.delegate.graph.eachNode(function(n) {
+      var valArray = n.getData('valueArray'),
+          acum = 0;
+      if(!valArray) return;
+      if(stacked) {
+        $.each(valArray, function(v) { 
+          acum += +v;
+        });
+      } else {
+        acum = Math.max.apply(null, valArray);
+      }
+      maxValue = maxValue>acum? maxValue:acum;
+    });
+    return maxValue;
+  },
+  
+  setBarType: function(type) {
+    this.config.type = type;
+    this.delegate.config.Node.type = 'barchart-' + type.split(':')[0];
+  },
+  
+  normalizeDims: function() {
+    //number of elements
+    var root = this.delegate.graph.getNode(this.delegate.root), l=0;
+    root.eachAdjacency(function() {
+      l++;
+    });
+    var maxValue = this.getMaxValue() || 1,
+        size = this.delegate.canvas.getSize(),
+        config = this.config,
+        margin = config.Margin,
+        marginWidth = margin.left + margin.right,
+        marginHeight = margin.top + margin.bottom,
+        horz = config.orientation == 'horizontal',
+        fixedDim = (size[horz? 'height':'width'] - (horz? marginHeight:marginWidth) - (l -1) * config.barsOffset) / l,
+        animate = config.animate,
+        height = size[horz? 'width':'height'] - (horz? marginWidth:marginHeight) 
+          - (!horz && config.showAggregates && (config.Label.size + config.labelOffset))
+          - (config.showLabels && (config.Label.size + config.labelOffset)),
+        dim1 = horz? 'height':'width',
+        dim2 = horz? 'width':'height';
+    this.delegate.graph.eachNode(function(n) {
+      var acum = 0, animateValue = [];
+      $.each(n.getData('valueArray'), function(v) {
+        acum += +v;
+        animateValue.push(0);
+      });
+      n.setData(dim1, fixedDim);
+      if(animate) {
+        n.setData(dim2, acum * height / maxValue, 'end');
+        n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
+          return n * height / maxValue; 
+        }), 'end');
+        var dimArray = n.getData('dimArray');
+        if(!dimArray) {
+          n.setData('dimArray', animateValue);
+        }
+      } else {
+        n.setData(dim2, acum * height / maxValue);
+        n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
+          return n * height / maxValue; 
+        }));
+      }
+    });
+  }
+});
+
+
+/*
+ * File: Options.PieChart.js
+ *
+*/
+/*
+  Object: Options.PieChart
+  
+  <PieChart> options. 
+  Other options included in the PieChart are <Options.Canvas>, <Options.Label>, <Options.Tips> and <Options.Events>.
+  
+  Syntax:
+  
+  (start code js)
+
+  Options.PieChart = {
+    animate: true,
+    offset: 25,
+    sliceOffset:0,
+    labelOffset: 3,
+    type: 'stacked',
+    hoveredColor: '#9fd4ff',
+    showLabels: true,
+    resizeLabels: false,
+    updateHeights: false
+  };  
+
+  (end code)
+  
+  Example:
+  
+  (start code js)
+
+  var pie = new $jit.PieChart({
+    animate: true,
+    sliceOffset: 5,
+    type: 'stacked:gradient'
+  });  
+
+  (end code)
+  
+  Parameters:
+  
+  animate - (boolean) Default's *true*. Whether to add animated transitions when plotting/updating the visualization.
+  offset - (number) Default's *25*. Adds margin between the visualization and the canvas.
+  sliceOffset - (number) Default's *0*. Separation between the center of the canvas and each pie slice.
+  labelOffset - (number) Default's *3*. Adds margin between the label and the default place where it should be drawn.
+  type - (string) Default's *'stacked'*. Stack style. Posible values are 'stacked', 'stacked:gradient' to add gradients.
+  hoveredColor - (boolean|string) Default's *'#9fd4ff'*. Sets the selected color for a hovered pie stack.
+  showLabels - (boolean) Default's *true*. Display the name of the slots.
+  resizeLabels - (boolean|number) Default's *false*. Resize the pie labels according to their stacked values. Set a number for *resizeLabels* to set a font size minimum.
+  updateHeights - (boolean) Default's *false*. Only for mono-valued (most common) pie charts. Resize the height of the pie slices according to their current values.
+
+*/
+Options.PieChart = {
+  $extend: true,
+
+  animate: true,
+  offset: 25, // page offset
+  sliceOffset:0,
+  labelOffset: 3, // label offset
+  type: 'stacked', // gradient
+  hoveredColor: '#9fd4ff',
+  Events: {
+    enable: false,
+    onClick: $.empty
+  },
+  Tips: {
+    enable: false,
+    onShow: $.empty,
+    onHide: $.empty
+  },
+  showLabels: true,
+  resizeLabels: false,
+  
+  //only valid for mono-valued datasets
+  updateHeights: false
+};
+
+/*
+ * Class: Layouts.Radial
+ * 
+ * Implements a Radial Layout.
+ * 
+ * Implemented By:
+ * 
+ * <RGraph>, <Hypertree>
+ * 
+ */
+Layouts.Radial = new Class({
+
+  /*
+   * Method: compute
+   * 
+   * Computes nodes' positions.
+   * 
+   * Parameters:
+   * 
+   * property - _optional_ A <Graph.Node> position property to store the new
+   * positions. Possible values are 'pos', 'end' or 'start'.
+   * 
+   */
+  compute : function(property) {
+    var prop = $.splat(property || [ 'current', 'start', 'end' ]);
+    NodeDim.compute(this.graph, prop, this.config);
+    this.graph.computeLevels(this.root, 0, "ignore");
+    var lengthFunc = this.createLevelDistanceFunc(); 
+    this.computeAngularWidths(prop);
+    this.computePositions(prop, lengthFunc);
+  },
+
+  /*
+   * computePositions
+   * 
+   * Performs the main algorithm for computing node positions.
+   */
+  computePositions : function(property, getLength) {
+    var propArray = property;
+    var graph = this.graph;
+    var root = graph.getNode(this.root);
+    var parent = this.parent;
+    var config = this.config;
+
+    for ( var i=0, l=propArray.length; i < l; i++) {
+      var pi = propArray[i];
+      root.setPos($P(0, 0), pi);
+      root.setData('span', Math.PI * 2, pi);
+    }
+
+    root.angleSpan = {
+      begin : 0,
+      end : 2 * Math.PI
+    };
+
+    graph.eachBFS(this.root, function(elem) {
+      var angleSpan = elem.angleSpan.end - elem.angleSpan.begin;
+      var angleInit = elem.angleSpan.begin;
+      var len = getLength(elem);
+      //Calculate the sum of all angular widths
+      var totalAngularWidths = 0, subnodes = [], maxDim = {};
+      elem.eachSubnode(function(sib) {
+        totalAngularWidths += sib._treeAngularWidth;
+        //get max dim
+        for ( var i=0, l=propArray.length; i < l; i++) {
+          var pi = propArray[i], dim = sib.getData('dim', pi);
+          maxDim[pi] = (pi in maxDim)? (dim > maxDim[pi]? dim : maxDim[pi]) : dim;
+        }
+        subnodes.push(sib);
+      }, "ignore");
+      //Maintain children order
+      //Second constraint for <http://bailando.sims.berkeley.edu/papers/infovis01.htm>
+      if (parent && parent.id == elem.id && subnodes.length > 0
+          && subnodes[0].dist) {
+        subnodes.sort(function(a, b) {
+          return (a.dist >= b.dist) - (a.dist <= b.dist);
+        });
+      }
+      //Calculate nodes positions.
+      for (var k = 0, ls=subnodes.length; k < ls; k++) {
+        var child = subnodes[k];
+        if (!child._flag) {
+          var angleProportion = child._treeAngularWidth / totalAngularWidths * angleSpan;
+          var theta = angleInit + angleProportion / 2;
+
+          for ( var i=0, l=propArray.length; i < l; i++) {
+            var pi = propArray[i];
+            child.setPos($P(theta, len), pi);
+            child.setData('span', angleProportion, pi);
+            child.setData('dim-quotient', child.getData('dim', pi) / maxDim[pi], pi);
+          }
+
+          child.angleSpan = {
+            begin : angleInit,
+            end : angleInit + angleProportion
+          };
+          angleInit += angleProportion;
+        }
+      }
+    }, "ignore");
+  },
+
+  /*
+   * Method: setAngularWidthForNodes
+   * 
+   * Sets nodes angular widths.
+   */
+  setAngularWidthForNodes : function(prop) {
+    this.graph.eachBFS(this.root, function(elem, i) {
+      var diamValue = elem.getData('angularWidth', prop[0]) || 5;
+      elem._angularWidth = diamValue / i;
+    }, "ignore");
+  },
+
+  /*
+   * Method: setSubtreesAngularWidth
+   * 
+   * Sets subtrees angular widths.
+   */
+  setSubtreesAngularWidth : function() {
+    var that = this;
+    this.graph.eachNode(function(elem) {
+      that.setSubtreeAngularWidth(elem);
+    }, "ignore");
+  },
+
+  /*
+   * Method: setSubtreeAngularWidth
+   * 
+   * Sets the angular width for a subtree.
+   */
+  setSubtreeAngularWidth : function(elem) {
+    var that = this, nodeAW = elem._angularWidth, sumAW = 0;
+    elem.eachSubnode(function(child) {
+      that.setSubtreeAngularWidth(child);
+      sumAW += child._treeAngularWidth;
+    }, "ignore");
+    elem._treeAngularWidth = Math.max(nodeAW, sumAW);
+  },
+
+  /*
+   * Method: computeAngularWidths
+   * 
+   * Computes nodes and subtrees angular widths.
+   */
+  computeAngularWidths : function(prop) {
+    this.setAngularWidthForNodes(prop);
+    this.setSubtreesAngularWidth();
+  }
+
+});
+
+
+/*
+ * File: Sunburst.js
+ */
+
+/*
+   Class: Sunburst
+      
+   A radial space filling tree visualization.
+   
+   Inspired by:
+ 
+   Sunburst <http://www.cc.gatech.edu/gvu/ii/sunburst/>.
+   
+   Note:
+   
+   This visualization was built and engineered from scratch, taking only the paper as inspiration, and only shares some features with the visualization described in the paper.
+   
+  Implements:
+  
+  All <Loader> methods
+  
+   Constructor Options:
+   
+   Inherits options from
+   
+   - <Options.Canvas>
+   - <Options.Controller>
+   - <Options.Node>
+   - <Options.Edge>
+   - <Options.Label>
+   - <Options.Events>
+   - <Options.Tips>
+   - <Options.NodeStyles>
+   - <Options.Navigation>
+   
+   Additionally, there are other parameters and some default values changed
+   
+   interpolation - (string) Default's *linear*. Describes the way nodes are interpolated. Possible values are 'linear' and 'polar'.
+   levelDistance - (number) Default's *100*. The distance between levels of the tree. 
+   Node.type - Described in <Options.Node>. Default's to *multipie*.
+   Node.height - Described in <Options.Node>. Default's *0*.
+   Edge.type - Described in <Options.Edge>. Default's *none*.
+   Label.textAlign - Described in <Options.Label>. Default's *start*.
+   Label.textBaseline - Described in <Options.Label>. Default's *middle*.
+     
+   Instance Properties:
+
+   canvas - Access a <Canvas> instance.
+   graph - Access a <Graph> instance.
+   op - Access a <Sunburst.Op> instance.
+   fx - Access a <Sunburst.Plot> instance.
+   labels - Access a <Sunburst.Label> interface implementation.   
+
+*/
+
+$jit.Sunburst = new Class({
+
+  Implements: [ Loader, Extras, Layouts.Radial ],
+
+  initialize: function(controller) {
+    var $Sunburst = $jit.Sunburst;
+
+    var config = {
+      interpolation: 'linear',
+      levelDistance: 100,
+      Node: {
+        'type': 'multipie',
+        'height':0
+      },
+      Edge: {
+        'type': 'none'
+      },
+      Label: {
+        textAlign: 'start',
+        textBaseline: 'middle'
+      }
+    };
+
+    this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",
+        "Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label"), config, controller);
+
+    var canvasConfig = this.config;
+    if(canvasConfig.useCanvas) {
+      this.canvas = canvasConfig.useCanvas;
+      this.config.labelContainer = this.canvas.id + '-label';
+    } else {
+      if(canvasConfig.background) {
+        canvasConfig.background = $.merge({
+          type: 'Circles'
+        }, canvasConfig.background);
+      }
+      this.canvas = new Canvas(this, canvasConfig);
+      this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
+    }
+
+    this.graphOptions = {
+      'klass': Polar,
+      'Node': {
+        'selected': false,
+        'exist': true,
+        'drawn': true
+      }
+    };
+    this.graph = new Graph(this.graphOptions, this.config.Node,
+        this.config.Edge);
+    this.labels = new $Sunburst.Label[canvasConfig.Label.type](this);
+    this.fx = new $Sunburst.Plot(this, $Sunburst);
+    this.op = new $Sunburst.Op(this);
+    this.json = null;
+    this.root = null;
+    this.rotated = null;
+    this.busy = false;
+    // initialize extras
+    this.initializeExtras();
+  },
+
+  /* 
+  
+    createLevelDistanceFunc 
+  
+    Returns the levelDistance function used for calculating a node distance 
+    to its origin. This function returns a function that is computed 
+    per level and not per node, such that all nodes with the same depth will have the 
+    same distance to the origin. The resulting function gets the 
+    parent node as parameter and returns a float.
+
+   */
+  createLevelDistanceFunc: function() {
+    var ld = this.config.levelDistance;
+    return function(elem) {
+      return (elem._depth + 1) * ld;
+    };
+  },
+
+  /* 
+     Method: refresh 
+     
+     Computes positions and plots the tree.
+
+   */
+  refresh: function() {
+    this.compute();
+    this.plot();
+  },
+
+  /*
+   reposition
+  
+   An alias for computing new positions to _endPos_
+
+   See also:
+
+   <Sunburst.compute>
+   
+  */
+  reposition: function() {
+    this.compute('end');
+  },
+
+  /*
+  Method: rotate
+  
+  Rotates the graph so that the selected node is horizontal on the right.
+
+  Parameters:
+  
+  node - (object) A <Graph.Node>.
+  method - (string) Whether to perform an animation or just replot the graph. Possible values are "replot" or "animate".
+  opt - (object) Configuration options merged with this visualization configuration options.
+  
+  See also:
+
+  <Sunburst.rotateAngle>
+  
+  */
+  rotate: function(node, method, opt) {
+    var theta = node.getPos(opt.property || 'current').getp(true).theta;
+    this.rotated = node;
+    this.rotateAngle(-theta, method, opt);
+  },
+
+  /*
+  Method: rotateAngle
+  
+  Rotates the graph of an angle theta.
+  
+   Parameters:
+   
+   node - (object) A <Graph.Node>.
+   method - (string) Whether to perform an animation or just replot the graph. Possible values are "replot" or "animate".
+   opt - (object) Configuration options merged with this visualization configuration options.
+   
+   See also:
+
+   <Sunburst.rotate>
+  
+  */
+  rotateAngle: function(theta, method, opt) {
+    var that = this;
+    var options = $.merge(this.config, opt || {}, {
+      modes: [ 'polar' ]
+    });
+    var prop = opt.property || (method === "animate" ? 'end' : 'current');
+    if(method === 'animate') {
+      this.fx.animation.pause();
+    }
+    this.graph.eachNode(function(n) {
+      var p = n.getPos(prop);
+      p.theta += theta;
+      if (p.theta < 0) {
+        p.theta += Math.PI * 2;
+      }
+    });
+    if (method == 'animate') {
+      this.fx.animate(options);
+    } else if (method == 'replot') {
+      this.fx.plot();
+      this.busy = false;
+    }
+  },
+
+  /*
+   Method: plot
+  
+   Plots the Sunburst. This is a shortcut to *fx.plot*.
+  */
+  plot: function() {
+    this.fx.plot();
+  }
+});
+
+$jit.Sunburst.$extend = true;
+
+(function(Sunburst) {
+
+  /*
+     Class: Sunburst.Op
+
+     Custom extension of <Graph.Op>.
+
+     Extends:
+
+     All <Graph.Op> methods
+     
+     See also:
+     
+     <Graph.Op>
+
+  */
+  Sunburst.Op = new Class( {
+
+    Implements: Graph.Op
+
+  });
+
+  /*
+     Class: Sunburst.Plot
+
+    Custom extension of <Graph.Plot>.
+  
+    Extends:
+  
+    All <Graph.Plot> methods
+    
+    See also:
+    
+    <Graph.Plot>
+  
+  */
+  Sunburst.Plot = new Class( {
+
+    Implements: Graph.Plot
+
+  });
+
+  /*
+    Class: Sunburst.Label
+
+    Custom extension of <Graph.Label>. 
+    Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
+  
+    Extends:
+  
+    All <Graph.Label> methods and subclasses.
+  
+    See also:
+  
+    <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
+  
+   */
+  Sunburst.Label = {};
+
+  /*
+     Sunburst.Label.Native
+
+     Custom extension of <Graph.Label.Native>.
+
+     Extends:
+
+     All <Graph.Label.Native> methods
+
+     See also:
+
+     <Graph.Label.Native>
+  */
+  Sunburst.Label.Native = new Class( {
+    Implements: Graph.Label.Native,
+
+    initialize: function(viz) {
+      this.viz = viz;
+      this.label = viz.config.Label;
+      this.config = viz.config;
+    },
+
+    renderLabel: function(canvas, node, controller) {
+      var span = node.getData('span');
+      if(span < Math.PI /2 && Math.tan(span) * 
+          this.config.levelDistance * node._depth < 10) {
+        return;
+      }
+      var ctx = canvas.getCtx();
+      var measure = ctx.measureText(node.name);
+      if (node.id == this.viz.root) {
+        var x = -measure.width / 2, y = 0, thetap = 0;
+        var ld = 0;
+      } else {
+        var indent = 5;
+        var ld = controller.levelDistance - indent;
+        var clone = node.pos.clone();
+        clone.rho += indent;
+        var p = clone.getp(true);
+        var ct = clone.getc(true);
+        var x = ct.x, y = ct.y;
+        // get angle in degrees
+        var pi = Math.PI;
+        var cond = (p.theta > pi / 2 && p.theta < 3 * pi / 2);
+        var thetap = cond ? p.theta + pi : p.theta;
+        if (cond) {
+          x -= Math.abs(Math.cos(p.theta) * measure.width);
+          y += Math.sin(p.theta) * measure.width;
+        } else if (node.id == this.viz.root) {
+          x -= measure.width / 2;
+        }
+      }
+      ctx.save();
+      ctx.translate(x, y);
+      ctx.rotate(thetap);
+      ctx.fillText(node.name, 0, 0);
+      ctx.restore();
+    }
+  });
+
+  /*
+     Sunburst.Label.SVG
+
+    Custom extension of <Graph.Label.SVG>.
+  
+    Extends:
+  
+    All <Graph.Label.SVG> methods
+  
+    See also:
+  
+    <Graph.Label.SVG>
+  
+  */
+  Sunburst.Label.SVG = new Class( {
+    Implements: Graph.Label.SVG,
+
+    initialize: function(viz) {
+      this.viz = viz;
+    },
+
+    /* 
+       placeLabel
+
+       Overrides abstract method placeLabel in <Graph.Plot>.
+
+       Parameters:
+
+       tag - A DOM label element.
+       node - A <Graph.Node>.
+       controller - A configuration/controller object passed to the visualization.
+      
+     */
+    placeLabel: function(tag, node, controller) {
+      var pos = node.pos.getc(true), viz = this.viz, canvas = this.viz.canvas;
+      var radius = canvas.getSize();
+      var labelPos = {
+        x: Math.round(pos.x + radius.width / 2),
+        y: Math.round(pos.y + radius.height / 2)
+      };
+      tag.setAttribute('x', labelPos.x);
+      tag.setAttribute('y', labelPos.y);
+
+      var bb = tag.getBBox();
+      if (bb) {
+        // center the label
+    var x = tag.getAttribute('x');
+    var y = tag.getAttribute('y');
+    // get polar coordinates
+    var p = node.pos.getp(true);
+    // get angle in degrees
+    var pi = Math.PI;
+    var cond = (p.theta > pi / 2 && p.theta < 3 * pi / 2);
+    if (cond) {
+      tag.setAttribute('x', x - bb.width);
+      tag.setAttribute('y', y - bb.height);
+    } else if (node.id == viz.root) {
+      tag.setAttribute('x', x - bb.width / 2);
+    }
+
+    var thetap = cond ? p.theta + pi : p.theta;
+    if(node._depth)
+      tag.setAttribute('transform', 'rotate(' + thetap * 360 / (2 * pi) + ' ' + x
+          + ' ' + y + ')');
+  }
+
+  controller.onPlaceLabel(tag, node);
+}
+  });
+
+  /*
+     Sunburst.Label.HTML
+
+     Custom extension of <Graph.Label.HTML>.
+
+     Extends:
+
+     All <Graph.Label.HTML> methods.
+
+     See also:
+
+     <Graph.Label.HTML>
+
+  */
+  Sunburst.Label.HTML = new Class( {
+    Implements: Graph.Label.HTML,
+
+    initialize: function(viz) {
+      this.viz = viz;
+    },
+    /* 
+       placeLabel
+
+       Overrides abstract method placeLabel in <Graph.Plot>.
+
+       Parameters:
+
+       tag - A DOM label element.
+       node - A <Graph.Node>.
+       controller - A configuration/controller object passed to the visualization.
+      
+     */
+    placeLabel: function(tag, node, controller) {
+      var pos = node.pos.clone(), 
+          canvas = this.viz.canvas,
+          height = node.getData('height'),
+          ldist = ((height || node._depth == 0)? height : this.viz.config.levelDistance) /2,
+          radius = canvas.getSize();
+      pos.rho += ldist;
+      pos = pos.getc(true);
+      
+      var labelPos = {
+        x: Math.round(pos.x + radius.width / 2),
+        y: Math.round(pos.y + radius.height / 2)
+      };
+
+      var style = tag.style;
+      style.left = labelPos.x + 'px';
+      style.top = labelPos.y + 'px';
+      style.display = this.fitsInCanvas(labelPos, canvas) ? '' : 'none';
+
+      controller.onPlaceLabel(tag, node);
+    }
+  });
+
+  /*
+    Class: Sunburst.Plot.NodeTypes
+
+    This class contains a list of <Graph.Node> built-in types. 
+    Node types implemented are 'none', 'pie', 'multipie', 'gradient-pie' and 'gradient-multipie'.
+
+    You can add your custom node types, customizing your visualization to the extreme.
+
+    Example:
+
+    (start code js)
+      Sunburst.Plot.NodeTypes.implement({
+        'mySpecialType': {
+          'render': function(node, canvas) {
+            //print your custom node to canvas
+          },
+          //optional
+          'contains': function(node, pos) {
+            //return true if pos is inside the node or false otherwise
+          }
+        }
+      });
+    (end code)
+
+  */
+  Sunburst.Plot.NodeTypes = new Class( {
+    'none': {
+      'render': $.empty,
+      'contains': $.lambda(false),
+      'anglecontains': function(node, pos) {
+        var span = node.getData('span') / 2, theta = node.pos.theta;
+        var begin = theta - span, end = theta + span;
+        if (begin < 0)
+          begin += Math.PI * 2;
+        var atan = Math.atan2(pos.y, pos.x);
+        if (atan < 0)
+          atan += Math.PI * 2;
+        if (begin > end) {
+          return (atan > begin && atan <= Math.PI * 2) || atan < end;
+        } else {
+          return atan > begin && atan < end;
+        }
+      }
+    },
+
+    'pie': {
+      'render': function(node, canvas) {
+        var span = node.getData('span') / 2, theta = node.pos.theta;
+        var begin = theta - span, end = theta + span;
+        var polarNode = node.pos.getp(true);
+        var polar = new Polar(polarNode.rho, begin);
+        var p1coord = polar.getc(true);
+        polar.theta = end;
+        var p2coord = polar.getc(true);
+
+        var ctx = canvas.getCtx();
+        ctx.beginPath();
+        ctx.moveTo(0, 0);
+        ctx.lineTo(p1coord.x, p1coord.y);
+        ctx.moveTo(0, 0);
+        ctx.lineTo(p2coord.x, p2coord.y);
+        ctx.moveTo(0, 0);
+        ctx.arc(0, 0, polarNode.rho * node.getData('dim-quotient'), begin, end,
+            false);
+        ctx.fill();
+      },
+      'contains': function(node, pos) {
+        if (this.nodeTypes['none'].anglecontains.call(this, node, pos)) {
+          var rho = Math.sqrt(pos.x * pos.x + pos.y * pos.y);
+          var ld = this.config.levelDistance, d = node._depth;
+          return (rho <= ld * d);
+        }
+        return false;
+      }
+    },
+    'multipie': {
+      'render': function(node, canvas) {
+        var height = node.getData('height');
+        var ldist = height? height : this.config.levelDistance;
+        var span = node.getData('span') / 2, theta = node.pos.theta;
+        var begin = theta - span, end = theta + span;
+        var polarNode = node.pos.getp(true);
+
+        var polar = new Polar(polarNode.rho, begin);
+        var p1coord = polar.getc(true);
+
+        polar.theta = end;
+        var p2coord = polar.getc(true);
+
+        polar.rho += ldist;
+        var p3coord = polar.getc(true);
+
+        polar.theta = begin;
+        var p4coord = polar.getc(true);
+
+        var ctx = canvas.getCtx();
+        ctx.moveTo(0, 0);
+        ctx.beginPath();
+        ctx.arc(0, 0, polarNode.rho, begin, end, false);
+        ctx.arc(0, 0, polarNode.rho + ldist, end, begin, true);
+        ctx.moveTo(p1coord.x, p1coord.y);
+        ctx.lineTo(p4coord.x, p4coord.y);
+        ctx.moveTo(p2coord.x, p2coord.y);
+        ctx.lineTo(p3coord.x, p3coord.y);
+        ctx.fill();
+
+        if (node.collapsed) {
+          ctx.save();
+          ctx.lineWidth = 2;
+          ctx.moveTo(0, 0);
+          ctx.beginPath();
+          ctx.arc(0, 0, polarNode.rho + ldist + 5, end - 0.01, begin + 0.01,
+              true);
+          ctx.stroke();
+          ctx.restore();
+        }
+      },
+      'contains': function(node, pos) {
+        if (this.nodeTypes['none'].anglecontains.call(this, node, pos)) {
+          var rho = Math.sqrt(pos.x * pos.x + pos.y * pos.y);
+          var height = node.getData('height');
+          var ldist = height? height : this.config.levelDistance;
+          var ld = this.config.levelDistance, d = node._depth;
+          return (rho >= ld * d) && (rho <= (ld * d + ldist));
+        }
+        return false;
+      }
+    },
+
+    'gradient-multipie': {
+      'render': function(node, canvas) {
+        var ctx = canvas.getCtx();
+        var height = node.getData('height');
+        var ldist = height? height : this.config.levelDistance;
+        var radialGradient = ctx.createRadialGradient(0, 0, node.getPos().rho,
+            0, 0, node.getPos().rho + ldist);
+
+        var colorArray = $.hexToRgb(node.getData('color')), ans = [];
+        $.each(colorArray, function(i) {
+          ans.push(parseInt(i * 0.5, 10));
+        });
+        var endColor = $.rgbToHex(ans);
+        radialGradient.addColorStop(0, endColor);
+        radialGradient.addColorStop(1, node.getData('color'));
+        ctx.fillStyle = radialGradient;
+        this.nodeTypes['multipie'].render.call(this, node, canvas);
+      },
+      'contains': function(node, pos) {
+        return this.nodeTypes['multipie'].contains.call(this, node, pos);
+      }
+    },
+
+    'gradient-pie': {
+      'render': function(node, canvas) {
+        var ctx = canvas.getCtx();
+        var radialGradient = ctx.createRadialGradient(0, 0, 0, 0, 0, node
+            .getPos().rho);
+
+        var colorArray = $.hexToRgb(node.getData('color')), ans = [];
+        $.each(colorArray, function(i) {
+          ans.push(parseInt(i * 0.5, 10));
+        });
+        var endColor = $.rgbToHex(ans);
+        radialGradient.addColorStop(1, endColor);
+        radialGradient.addColorStop(0, node.getData('color'));
+        ctx.fillStyle = radialGradient;
+        this.nodeTypes['pie'].render.call(this, node, canvas);
+      },
+      'contains': function(node, pos) {
+        return this.nodeTypes['pie'].contains.call(this, node, pos);
+      }
+    }
+  });
+
+  /*
+    Class: Sunburst.Plot.EdgeTypes
+
+    This class contains a list of <Graph.Adjacence> built-in types. 
+    Edge types implemented are 'none', 'line' and 'arrow'.
+  
+    You can add your custom edge types, customizing your visualization to the extreme.
+  
+    Example:
+  
+    (start code js)
+      Sunburst.Plot.EdgeTypes.implement({
+        'mySpecialType': {
+          'render': function(adj, canvas) {
+            //print your custom edge to canvas
+          },
+          //optional
+          'contains': function(adj, pos) {
+            //return true if pos is inside the arc or false otherwise
+          }
+        }
+      });
+    (end code)
+  
+  */
+  Sunburst.Plot.EdgeTypes = new Class({
+    'none': $.empty,
+    'line': {
+      'render': function(adj, canvas) {
+        var from = adj.nodeFrom.pos.getc(true),
+            to = adj.nodeTo.pos.getc(true);
+        this.edgeHelper.line.render(from, to, canvas);
+      },
+      'contains': function(adj, pos) {
+        var from = adj.nodeFrom.pos.getc(true),
+            to = adj.nodeTo.pos.getc(true);
+        return this.edgeHelper.line.contains(from, to, pos, this.edge.epsilon);
+      }
+    },
+    'arrow': {
+      'render': function(adj, canvas) {
+        var from = adj.nodeFrom.pos.getc(true),
+            to = adj.nodeTo.pos.getc(true),
+            dim = adj.getData('dim'),
+            direction = adj.data.$direction,
+            inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);
+        this.edgeHelper.arrow.render(from, to, dim, inv, canvas);
+      },
+      'contains': function(adj, pos) {
+        var from = adj.nodeFrom.pos.getc(true),
+            to = adj.nodeTo.pos.getc(true);
+        return this.edgeHelper.arrow.contains(from, to, pos, this.edge.epsilon);
+      }
+    },
+    'hyperline': {
+      'render': function(adj, canvas) {
+        var from = adj.nodeFrom.pos.getc(),
+            to = adj.nodeTo.pos.getc(),
+            dim = Math.max(from.norm(), to.norm());
+        this.edgeHelper.hyperline.render(from.$scale(1/dim), to.$scale(1/dim), dim, canvas);
+      },
+      'contains': $.lambda(false) //TODO(nico): Implement this!
+    }
+  });
+
+})($jit.Sunburst);
+
+
+/*
+ * File: PieChart.js
+ *
+*/
+
+$jit.Sunburst.Plot.NodeTypes.implement({
+  'piechart-stacked' : {
+    'render' : function(node, canvas) {
+      var pos = node.pos.getp(true),
+          dimArray = node.getData('dimArray'),
+          valueArray = node.getData('valueArray'),
+          colorArray = node.getData('colorArray'),
+          colorLength = colorArray.length,
+          stringArray = node.getData('stringArray'),
+          span = node.getData('span') / 2,
+          theta = node.pos.theta,
+          begin = theta - span,
+          end = theta + span,
+          polar = new Polar;
+    
+      var ctx = canvas.getCtx(), 
+          opt = {},
+          gradient = node.getData('gradient'),
+          border = node.getData('border'),
+          config = node.getData('config'),
+          showLabels = config.showLabels,
+          resizeLabels = config.resizeLabels,
+          label = config.Label;
+
+      var xpos = config.sliceOffset * Math.cos((begin + end) /2);
+      var ypos = config.sliceOffset * Math.sin((begin + end) /2);
+
+      if (colorArray && dimArray && stringArray) {
+        for (var i=0, l=dimArray.length, acum=0, valAcum=0; i<l; i++) {
+          var dimi = dimArray[i], colori = colorArray[i % colorLength];
+          if(dimi <= 0) continue;
+          ctx.fillStyle = ctx.strokeStyle = colori;
+          if(gradient && dimi) {
+            var radialGradient = ctx.createRadialGradient(xpos, ypos, acum + config.sliceOffset,
+                xpos, ypos, acum + dimi + config.sliceOffset);
+            var colorRgb = $.hexToRgb(colori), 
+                ans = $.map(colorRgb, function(i) { return (i * 0.8) >> 0; }),
+                endColor = $.rgbToHex(ans);
+
+            radialGradient.addColorStop(0, colori);
+            radialGradient.addColorStop(0.5, colori);
+            radialGradient.addColorStop(1, endColor);
+            ctx.fillStyle = radialGradient;
+          }
+          
+          polar.rho = acum + config.sliceOffset;
+          polar.theta = begin;
+          var p1coord = polar.getc(true);
+          polar.theta = end;
+          var p2coord = polar.getc(true);
+          polar.rho += dimi;
+          var p3coord = polar.getc(true);
+          polar.theta = begin;
+          var p4coord = polar.getc(true);
+
+          ctx.beginPath();
+          //fixing FF arc method + fill
+          ctx.arc(xpos, ypos, acum + .01, begin, end, false);
+          ctx.arc(xpos, ypos, acum + dimi + .01, end, begin, true);
+          ctx.fill();
+          if(border && border.name == stringArray[i]) {
+            opt.acum = acum;
+            opt.dimValue = dimArray[i];
+            opt.begin = begin;
+            opt.end = end;
+          }
+          acum += (dimi || 0);
+          valAcum += (valueArray[i] || 0);
+        }
+        if(border) {
+          ctx.save();
+          ctx.globalCompositeOperation = "source-over";
+          ctx.lineWidth = 2;
+          ctx.strokeStyle = border.color;
+          var s = begin < end? 1 : -1;
+          ctx.beginPath();
+          //fixing FF arc method + fill
+          ctx.arc(xpos, ypos, opt.acum + .01 + 1, opt.begin, opt.end, false);
+          ctx.arc(xpos, ypos, opt.acum + opt.dimValue + .01 - 1, opt.end, opt.begin, true);
+          ctx.closePath();
+          ctx.stroke();
+          ctx.restore();
+        }
+        if(showLabels && label.type == 'Native') {
+          ctx.save();
+          ctx.fillStyle = ctx.strokeStyle = label.color;
+          var scale = resizeLabels? node.getData('normalizedDim') : 1,
+              fontSize = (label.size * scale) >> 0;
+          fontSize = fontSize < +resizeLabels? +resizeLabels : fontSize;
+          
+          ctx.font = label.style + ' ' + fontSize + 'px ' + label.family;
+          ctx.textBaseline = 'middle';
+          ctx.textAlign = 'center';
+          
+          polar.rho = acum + config.labelOffset + config.sliceOffset;
+          polar.theta = node.pos.theta;
+          var cart = polar.getc(true);
+          
+          ctx.fillText(node.name, cart.x, cart.y);
+          ctx.restore();
+        }
+      }
+    },
+    'contains': function(node, pos) {
+      if (this.nodeTypes['none'].anglecontains.call(this, node, pos)) {
+        var rho = Math.sqrt(pos.x * pos.x + pos.y * pos.y);
+        var ld = this.config.levelDistance, d = node._depth;
+        var config = node.getData('config');
+        if(rho <=ld * d + config.sliceOffset) {
+          var dimArray = node.getData('dimArray');
+          for(var i=0,l=dimArray.length,acum=config.sliceOffset; i<l; i++) {
+            var dimi = dimArray[i];
+            if(rho >= acum && rho <= acum + dimi) {
+              return {
+                name: node.getData('stringArray')[i],
+                color: node.getData('colorArray')[i],
+                value: node.getData('valueArray')[i],
+                label: node.name
+              };
+            }
+            acum += dimi;
+          }
+        }
+        return false;
+        
+      }
+      return false;
+    }
+  }
+});
+
+/*
+  Class: PieChart
+  
+  A visualization that displays stacked bar charts.
+  
+  Constructor Options:
+  
+  See <Options.PieChart>.
+
+*/
+$jit.PieChart = new Class({
+  sb: null,
+  colors: ["#416D9C", "#70A35E", "#EBB056", "#C74243", "#83548B", "#909291", "#557EAA"],
+  selected: {},
+  busy: false,
+  
+  initialize: function(opt) {
+    this.controller = this.config = 
+      $.merge(Options("Canvas", "PieChart", "Label"), {
+        Label: { type: 'Native' }
+      }, opt);
+    this.initializeViz();
+  },
+  
+  initializeViz: function() {
+    var config = this.config, that = this;
+    var nodeType = config.type.split(":")[0];
+    var delegate = new $jit.Sunburst({
+      injectInto: config.injectInto,
+      width: config.width,
+      height: config.height,
+      useCanvas: config.useCanvas,
+      withLabels: config.Label.type != 'Native',
+      Label: {
+        type: config.Label.type
+      },
+      Node: {
+        overridable: true,
+        type: 'piechart-' + nodeType,
+        width: 1,
+        height: 1
+      },
+      Edge: {
+        type: 'none'
+      },
+      Tips: {
+        enable: config.Tips.enable,
+        type: 'Native',
+        force: true,
+        onShow: function(tip, node, contains) {
+          var elem = contains;
+          config.Tips.onShow(tip, elem, node);
+        }
+      },
+      Events: {
+        enable: true,
+        type: 'Native',
+        onClick: function(node, eventInfo, evt) {
+          if(!config.Events.enable) return;
+          var elem = eventInfo.getContains();
+          config.Events.onClick(elem, eventInfo, evt);
+        },
+        onMouseMove: function(node, eventInfo, evt) {
+          if(!config.hoveredColor) return;
+          if(node) {
+            var elem = eventInfo.getContains();
+            that.select(node.id, elem.name, elem.index);
+          } else {
+            that.select(false, false, false);
+          }
+        }
+      },
+      onCreateLabel: function(domElement, node) {
+        var labelConf = config.Label;
+        if(config.showLabels) {
+          var style = domElement.style;
+          style.fontSize = labelConf.size + 'px';
+          style.fontFamily = labelConf.family;
+          style.color = labelConf.color;
+          style.textAlign = 'center';
+          domElement.innerHTML = node.name;
+        }
+      },
+      onPlaceLabel: function(domElement, node) {
+        if(!config.showLabels) return;
+        var pos = node.pos.getp(true),
+            dimArray = node.getData('dimArray'),
+            span = node.getData('span') / 2,
+            theta = node.pos.theta,
+            begin = theta - span,
+            end = theta + span,
+            polar = new Polar;
+      
+        var showLabels = config.showLabels,
+            resizeLabels = config.resizeLabels,
+            label = config.Label;
+        
+        if (dimArray) {
+          for (var i=0, l=dimArray.length, acum=0; i<l; i++) {
+            acum += dimArray[i];
+          }
+          var scale = resizeLabels? node.getData('normalizedDim') : 1,
+              fontSize = (label.size * scale) >> 0;
+          fontSize = fontSize < +resizeLabels? +resizeLabels : fontSize;
+          domElement.style.fontSize = fontSize + 'px';
+          polar.rho = acum + config.labelOffset + config.sliceOffset;
+          polar.theta = (begin + end) / 2;
+          var pos = polar.getc(true);
+          var radius = that.canvas.getSize();
+          var labelPos = {
+            x: Math.round(pos.x + radius.width / 2),
+            y: Math.round(pos.y + radius.height / 2)
+          };
+          domElement.style.left = labelPos.x + 'px';
+          domElement.style.top = labelPos.y + 'px';
+        }
+      }
+    });
+    
+    var size = delegate.canvas.getSize(),
+        min = Math.min;
+    delegate.config.levelDistance = min(size.width, size.height)/2 
+      - config.offset - config.sliceOffset;
+    this.delegate = delegate;
+    this.canvas = this.delegate.canvas;
+    this.canvas.getCtx().globalCompositeOperation = 'lighter';
+  },
+  
+  /*
+    Method: loadJSON
+   
+    Loads JSON data into the visualization. 
+    
+    Parameters:
+    
+    json - The JSON data format. This format is described in <http://blog.thejit.org/2010/04/24/new-javascript-infovis-toolkit-visualizations/#json-data-format>.
+    
+    Example:
+    (start code js)
+    var pieChart = new $jit.PieChart(options);
+    pieChart.loadJSON(json);
+    (end code)
+  */  
+  loadJSON: function(json) {
+    var prefix = $.time(), 
+        ch = [], 
+        delegate = this.delegate,
+        name = $.splat(json.label),
+        nameLength = name.length,
+        color = $.splat(json.color || this.colors),
+        colorLength = color.length,
+        config = this.config,
+        gradient = !!config.type.split(":")[1],
+        animate = config.animate,
+        mono = nameLength == 1;
+    
+    for(var i=0, values=json.values, l=values.length; i<l; i++) {
+      var val = values[i];
+      var valArray = $.splat(val.values);
+      ch.push({
+        'id': prefix + val.label,
+        'name': val.label,
+        'data': {
+          'value': valArray,
+          '$valueArray': valArray,
+          '$colorArray': mono? $.splat(color[i % colorLength]) : color,
+          '$stringArray': name,
+          '$gradient': gradient,
+          '$config': config,
+          '$angularWidth': $.reduce(valArray, function(x,y){return x+y;})
+        },
+        'children': []
+      });
+    }
+    var root = {
+      'id': prefix + '$root',
+      'name': '',
+      'data': {
+        '$type': 'none',
+        '$width': 1,
+        '$height': 1
+      },
+      'children': ch
+    };
+    delegate.loadJSON(root);
+    
+    this.normalizeDims();
+    delegate.refresh();
+    if(animate) {
+      delegate.fx.animate({
+        modes: ['node-property:dimArray'],
+        duration:1500
+      });
+    }
+  },
+  
+  /*
+    Method: updateJSON
+   
+    Use this method when updating values for the current JSON data. If the items specified by the JSON data already exist in the graph then their values will be updated.
+    
+    Parameters:
+    
+    json - (object) JSON data to be updated. The JSON format corresponds to the one described in <PieChart.loadJSON>.
+    onComplete - (object) A callback object to be called when the animation transition when updating the data end.
+    
+    Example:
+    
+    (start code js)
+    pieChart.updateJSON(json, {
+      onComplete: function() {
+        alert('update complete!');
+      }
+    });
+    (end code)
+  */  
+  updateJSON: function(json, onComplete) {
+    if(this.busy) return;
+    this.busy = true;
+    
+    var delegate = this.delegate;
+    var graph = delegate.graph;
+    var values = json.values;
+    var animate = this.config.animate;
+    var that = this;
+    $.each(values, function(v) {
+      var n = graph.getByName(v.label),
+          vals = $.splat(v.values);
+      if(n) {
+        n.setData('valueArray', vals);
+        n.setData('angularWidth', $.reduce(vals, function(x,y){return x+y;}));
+        if(json.label) {
+          n.setData('stringArray', $.splat(json.label));
+        }
+      }
+    });
+    this.normalizeDims();
+    if(animate) {
+      delegate.compute('end');
+      delegate.fx.animate({
+        modes: ['node-property:dimArray:span', 'linear'],
+        duration:1500,
+        onComplete: function() {
+          that.busy = false;
+          onComplete && onComplete.onComplete();
+        }
+      });
+    } else {
+      delegate.refresh();
+    }
+  },
+    
+  //adds the little brown bar when hovering the node
+  select: function(id, name) {
+    if(!this.config.hoveredColor) return;
+    var s = this.selected;
+    if(s.id != id || s.name != name) {
+      s.id = id;
+      s.name = name;
+      s.color = this.config.hoveredColor;
+      this.delegate.graph.eachNode(function(n) {
+        if(id == n.id) {
+          n.setData('border', s);
+        } else {
+          n.setData('border', false);
+        }
+      });
+      this.delegate.plot();
+    }
+  },
+  
+  /*
+    Method: getLegend
+   
+    Returns an object containing as keys the legend names and as values hex strings with color values.
+    
+    Example:
+    
+    (start code js)
+    var legend = pieChart.getLegend();
+    (end code)
+  */  
+  getLegend: function() {
+    var legend = {};
+    var n;
+    this.delegate.graph.getNode(this.delegate.root).eachAdjacency(function(adj) {
+      n = adj.nodeTo;
+    });
+    var colors = n.getData('colorArray'),
+        len = colors.length;
+    $.each(n.getData('stringArray'), function(s, i) {
+      legend[s] = colors[i % len];
+    });
+    return legend;
+  },
+  
+  /*
+    Method: getMaxValue
+   
+    Returns the maximum accumulated value for the stacks. This method is used for normalizing the graph heights according to the canvas height.
+    
+    Example:
+    
+    (start code js)
+    var ans = pieChart.getMaxValue();
+    (end code)
+    
+    In some cases it could be useful to override this method to normalize heights for a group of PieCharts, like when doing small multiples.
+    
+    Example:
+    
+    (start code js)
+    //will return 100 for all PieChart instances,
+    //displaying all of them with the same scale
+    $jit.PieChart.implement({
+      'getMaxValue': function() {
+        return 100;
+      }
+    });
+    (end code)
+    
+  */  
+  getMaxValue: function() {
+    var maxValue = 0;
+    this.delegate.graph.eachNode(function(n) {
+      var valArray = n.getData('valueArray'),
+          acum = 0;
+      $.each(valArray, function(v) { 
+        acum += +v;
+      });
+      maxValue = maxValue>acum? maxValue:acum;
+    });
+    return maxValue;
+  },
+  
+  normalizeDims: function() {
+    //number of elements
+    var root = this.delegate.graph.getNode(this.delegate.root), l=0;
+    root.eachAdjacency(function() {
+      l++;
+    });
+    var maxValue = this.getMaxValue() || 1,
+        config = this.config,
+        animate = config.animate,
+        rho = this.delegate.config.levelDistance;
+    this.delegate.graph.eachNode(function(n) {
+      var acum = 0, animateValue = [];
+      $.each(n.getData('valueArray'), function(v) {
+        acum += +v;
+        animateValue.push(1);
+      });
+      var stat = (animateValue.length == 1) && !config.updateHeights;
+      if(animate) {
+        n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
+          return stat? rho: (n * rho / maxValue); 
+        }), 'end');
+        var dimArray = n.getData('dimArray');
+        if(!dimArray) {
+          n.setData('dimArray', animateValue);
+        }
+      } else {
+        n.setData('dimArray', $.map(n.getData('valueArray'), function(n) { 
+          return stat? rho : (n * rho / maxValue); 
+        }));
+      }
+      n.setData('normalizedDim', acum / maxValue);
+    });
+  }
+});
+
+
+/*
+ * Class: Layouts.TM
+ * 
+ * Implements TreeMaps layouts (SliceAndDice, Squarified, Strip).
+ * 
+ * Implemented By:
+ * 
+ * <TM>
+ * 
+ */
+Layouts.TM = {};
+
+Layouts.TM.SliceAndDice = new Class({
+  compute: function(prop) {
+    var root = this.graph.getNode(this.clickedNode && this.clickedNode.id || this.root);
+    this.controller.onBeforeCompute(root);
+    var size = this.canvas.getSize(),
+        config = this.config,
+        width = size.width,
+        height = size.height;
+    this.graph.computeLevels(this.root, 0, "ignore");
+    //set root position and dimensions
+    root.getPos(prop).setc(-width/2, -height/2);
+    root.setData('width', width, prop);
+    root.setData('height', height + config.titleHeight, prop);
+    this.computePositions(root, root, this.layout.orientation, prop);
+    this.controller.onAfterCompute(root);
+  },
+  
+  computePositions: function(par, ch, orn, prop) {
+    //compute children areas
+    var totalArea = 0;
+    par.eachSubnode(function(n) {
+      totalArea += n.getData('area', prop);
+    });
+    
+    var config = this.config,
+        offst = config.offset,
+        width  = par.getData('width', prop),
+        height = Math.max(par.getData('height', prop) - config.titleHeight, 0),
+        fact = par == ch? 1 : (ch.getData('area', prop) / totalArea);
+
+    var otherSize, size, dim, pos, pos2, posth, pos2th;
+    var horizontal = (orn == "h");
+    if(horizontal) {
+      orn = 'v';
+      otherSize = height;
+      size = width * fact;
+      dim = 'height';
+      pos = 'y';
+      pos2 = 'x';
+      posth = config.titleHeight;
+      pos2th = 0;
+    } else {
+      orn = 'h';    
+      otherSize = height * fact;
+      size = width;
+      dim = 'width';
+      pos = 'x';
+      pos2 = 'y';
+      posth = 0;
+      pos2th = config.titleHeight;
+    }
+    var cpos = ch.getPos(prop);
+    ch.setData('width', size, prop);
+    ch.setData('height', otherSize, prop);
+    var offsetSize = 0, tm = this;
+    ch.eachSubnode(function(n) {
+      var p = n.getPos(prop);
+      p[pos] = offsetSize + cpos[pos] + posth;
+      p[pos2] = cpos[pos2] + pos2th;
+      tm.computePositions(ch, n, orn, prop);
+      offsetSize += n.getData(dim, prop);
+    });
+  }
+
+});
+
+Layouts.TM.Area = {
+ /*
+    Method: compute
+ 
+   Called by loadJSON to calculate recursively all node positions and lay out the tree.
+ 
+    Parameters:
+
+       json - A JSON tree. See also <Loader.loadJSON>.
+       coord - A coordinates object specifying width, height, left and top style properties.
+ */
+ compute: function(prop) {
+    prop = prop || "current";
+    var root = this.graph.getNode(this.clickedNode && this.clickedNode.id || this.root);
+    this.controller.onBeforeCompute(root);
+    var config = this.config,
+        size = this.canvas.getSize(),
+        width = size.width,
+        height = size.height,
+        offst = config.offset,
+        offwdth = width - offst,
+        offhght = height - offst;
+    this.graph.computeLevels(this.root, 0, "ignore");
+    //set root position and dimensions
+    root.getPos(prop).setc(-width/2, -height/2);
+    root.setData('width', width, prop);
+    root.setData('height', height, prop);
+    //create a coordinates object
+    var coord = {
+        'top': -height/2 + config.titleHeight,
+        'left': -width/2,
+        'width': offwdth,
+        'height': offhght - config.titleHeight
+    };
+    this.computePositions(root, coord, prop);
+    this.controller.onAfterCompute(root);
+ }, 
+ 
+ /*
+    Method: computeDim
+ 
+   Computes dimensions and positions of a group of nodes
+   according to a custom layout row condition. 
+ 
+    Parameters:
+
+       tail - An array of nodes.  
+       initElem - An array of nodes (containing the initial node to be laid).
+       w - A fixed dimension where nodes will be layed out.
+       coord - A coordinates object specifying width, height, left and top style properties.
+       comp - A custom comparison function
+ */
+ computeDim: function(tail, initElem, w, coord, comp, prop) {
+   if(tail.length + initElem.length == 1) {
+     var l = (tail.length == 1)? tail : initElem;
+     this.layoutLast(l, w, coord, prop);
+     return;
+   }
+   if(tail.length >= 2 && initElem.length == 0) {
+     initElem = [tail.shift()];
+   }
+   if(tail.length == 0) {
+     if(initElem.length > 0) this.layoutRow(initElem, w, coord, prop);
+     return;
+   }
+   var c = tail[0];
+   if(comp(initElem, w) >= comp([c].concat(initElem), w)) {
+     this.computeDim(tail.slice(1), initElem.concat([c]), w, coord, comp, prop);
+   } else {
+     var newCoords = this.layoutRow(initElem, w, coord, prop);
+     this.computeDim(tail, [], newCoords.dim, newCoords, comp, prop);
+   }
+ },
+
+ 
+ /*
+    Method: worstAspectRatio
+ 
+   Calculates the worst aspect ratio of a group of rectangles. 
+       
+    See also:
+       
+       <http://en.wikipedia.org/wiki/Aspect_ratio>
+   
+    Parameters:
+
+     ch - An array of nodes.  
+     w  - The fixed dimension where rectangles are being laid out.
+
+    Returns:
+ 
+        The worst aspect ratio.
+
+
+ */
+ worstAspectRatio: function(ch, w) {
+   if(!ch || ch.length == 0) return Number.MAX_VALUE;
+   var areaSum = 0, maxArea = 0, minArea = Number.MAX_VALUE;
+   for(var i=0, l=ch.length; i<l; i++) {
+     var area = ch[i]._area;
+     areaSum += area; 
+     minArea = minArea < area? minArea : area;
+     maxArea = maxArea > area? maxArea : area; 
+   }
+   var sqw = w * w, sqAreaSum = areaSum * areaSum;
+   return Math.max(sqw * maxArea / sqAreaSum,
+           sqAreaSum / (sqw * minArea));
+ },
+ 
+ /*
+    Method: avgAspectRatio
+ 
+   Calculates the average aspect ratio of a group of rectangles. 
+       
+       See also:
+       
+       <http://en.wikipedia.org/wiki/Aspect_ratio>
+   
+    Parameters:
+
+     ch - An array of nodes.  
+       w - The fixed dimension where rectangles are being laid out.
+
+    Returns:
+ 
+        The average aspect ratio.
+
+
+ */
+ avgAspectRatio: function(ch, w) {
+   if(!ch || ch.length == 0) return Number.MAX_VALUE;
+   var arSum = 0;
+   for(var i=0, l=ch.length; i<l; i++) {
+     var area = ch[i]._area;
+     var h = area / w;
+     arSum += w > h? w / h : h / w;
+   }
+   return arSum / l;
+ },
+
+ /*
+    layoutLast
+ 
+   Performs the layout of the last computed sibling.
+ 
+    Parameters:
+
+       ch - An array of nodes.  
+       w - A fixed dimension where nodes will be layed out.
+     coord - A coordinates object specifying width, height, left and top style properties.
+ */
+ layoutLast: function(ch, w, coord, prop) {
+   var child = ch[0];
+   child.getPos(prop).setc(coord.left, coord.top);
+   child.setData('width', coord.width, prop);
+   child.setData('height', coord.height, prop);
+ }
+};
+
+
+Layouts.TM.Squarified = new Class({
+ Implements: Layouts.TM.Area,
+ 
+ computePositions: function(node, coord, prop) {
+   var config = this.config, 
+       max = Math.max;
+   
+   if (coord.width >= coord.height) 
+     this.layout.orientation = 'h';
+   else
+     this.layout.orientation = 'v';
+   
+   var ch = node.getSubnodes([1, 1], "ignore");
+   if(ch.length > 0) {
+     this.processChildrenLayout(node, ch, coord, prop);
+     for(var i=0, l=ch.length; i<l; i++) {
+       var chi = ch[i], 
+           offst = config.offset,
+           height = max(chi.getData('height', prop) - offst - config.titleHeight, 0),
+           width = max(chi.getData('width', prop) - offst, 0),
+           chipos = chi.getPos(prop);
+
+       coord = {
+         'width': width,
+         'height': height,
+         'top': chipos.y + config.titleHeight,
+         'left': chipos.x
+       };
+       this.computePositions(chi, coord, prop);
+     }
+   }
+ },
+
+ /*
+    Method: processChildrenLayout
+ 
+   Computes children real areas and other useful parameters for performing the Squarified algorithm.
+ 
+    Parameters:
+
+       par - The parent node of the json subtree.  
+       ch - An Array of nodes
+     coord - A coordinates object specifying width, height, left and top style properties.
+ */
+ processChildrenLayout: function(par, ch, coord, prop) {
+   //compute children real areas
+   var parentArea = coord.width * coord.height;
+   var i, l=ch.length, totalChArea=0, chArea = [];
+   for(i=0; i<l; i++) {
+     chArea[i] = parseFloat(ch[i].getData('area', prop));
+     totalChArea += chArea[i];
+   }
+   for(i=0; i<l; i++) {
+     ch[i]._area = parentArea * chArea[i] / totalChArea;
+   }
+   var minimumSideValue = this.layout.horizontal()? coord.height : coord.width;
+   ch.sort(function(a, b) { 
+     var diff = b._area - a._area; 
+     return diff? diff : (b.id == a.id? 0 : (b.id < a.id? 1 : -1)); 
+   });
+   var initElem = [ch[0]];
+   var tail = ch.slice(1);
+   this.squarify(tail, initElem, minimumSideValue, coord, prop);
+ },
+
+ /*
+   Method: squarify
+ 
+   Performs an heuristic method to calculate div elements sizes in order to have a good aspect ratio.
+ 
+    Parameters:
+
+       tail - An array of nodes.  
+       initElem - An array of nodes, containing the initial node to be laid out.
+       w - A fixed dimension where nodes will be laid out.
+       coord - A coordinates object specifying width, height, left and top style properties.
+ */
+ squarify: function(tail, initElem, w, coord, prop) {
+   this.computeDim(tail, initElem, w, coord, this.worstAspectRatio, prop);
+ },
+ 
+ /*
+    Method: layoutRow
+ 
+   Performs the layout of an array of nodes.
+ 
+    Parameters:
+
+       ch - An array of nodes.  
+       w - A fixed dimension where nodes will be laid out.
+       coord - A coordinates object specifying width, height, left and top style properties.
+ */
+ layoutRow: function(ch, w, coord, prop) {
+   if(this.layout.horizontal()) {
+     return this.layoutV(ch, w, coord, prop);
+   } else {
+     return this.layoutH(ch, w, coord, prop);
+   }
+ },
+ 
+ layoutV: function(ch, w, coord, prop) {
+   var totalArea = 0, rnd = function(x) { return x; }; 
+   $.each(ch, function(elem) { totalArea += elem._area; });
+   var width = rnd(totalArea / w), top =  0; 
+   for(var i=0, l=ch.length; i<l; i++) {
+     var h = rnd(ch[i]._area / width);
+     var chi = ch[i];
+     chi.getPos(prop).setc(coord.left, coord.top + top);
+     chi.setData('width', width, prop);
+     chi.setData('height', h, prop);
+     top += h;
+   }
+   var ans = {
+     'height': coord.height,
+     'width': coord.width - width,
+     'top': coord.top,
+     'left': coord.left + width
+   };
+   //take minimum side value.
+   ans.dim = Math.min(ans.width, ans.height);
+   if(ans.dim != ans.height) this.layout.change();
+   return ans;
+ },
+ 
+ layoutH: function(ch, w, coord, prop) {
+   var totalArea = 0; 
+   $.each(ch, function(elem) { totalArea += elem._area; });
+   var height = totalArea / w,
+       top = coord.top, 
+       left = 0;
+   
+   for(var i=0, l=ch.length; i<l; i++) {
+     var chi = ch[i];
+     var w = chi._area / height;
+     chi.getPos(prop).setc(coord.left + left, top);
+     chi.setData('width', w, prop);
+     chi.setData('height', height, prop);
+     left += w;
+   }
+   var ans = {
+     'height': coord.height - height,
+     'width': coord.width,
+     'top': coord.top + height,
+     'left': coord.left
+   };
+   ans.dim = Math.min(ans.width, ans.height);
+   if(ans.dim != ans.width) this.layout.change();
+   return ans;
+ }
+});
+
+Layouts.TM.Strip = new Class({
+  Implements: Layouts.TM.Area,
+
+    /*
+      Method: compute
+    
+     Called by loadJSON to calculate recursively all node positions and lay out the tree.
+    
+      Parameters:
+    
+         json - A JSON subtree. See also <Loader.loadJSON>. 
+       coord - A coordinates object specifying width, height, left and top style properties.
+    */
+    computePositions: function(node, coord, prop) {
+     var  ch = node.getSubnodes([1, 1], "ignore"), 
+          config = this.config,
+          max = Math.max;
+     if(ch.length > 0) {
+       this.processChildrenLayout(node, ch, coord, prop);
+       for(var i=0, l=ch.length; i<l; i++) {
+         var chi = ch[i];
+         var offst = config.offset,
+             height = max(chi.getData('height', prop) - offst - config.titleHeight, 0),
+             width  = max(chi.getData('width', prop)  - offst, 0);
+         var chipos = chi.getPos(prop);
+         coord = {
+           'width': width,
+           'height': height,
+           'top': chipos.y + config.titleHeight,
+           'left': chipos.x
+         };
+         this.computePositions(chi, coord, prop);
+       }
+     }
+    },
+    
+    /*
+      Method: processChildrenLayout
+    
+     Computes children real areas and other useful parameters for performing the Strip algorithm.
+    
+      Parameters:
+    
+         par - The parent node of the json subtree.  
+         ch - An Array of nodes
+         coord - A coordinates object specifying width, height, left and top style properties.
+    */
+    processChildrenLayout: function(par, ch, coord, prop) {
+     //compute children real areas
+      var parentArea = coord.width * coord.height;
+      var i, l=ch.length, totalChArea=0, chArea = [];
+      for(i=0; i<l; i++) {
+        chArea[i] = +ch[i].getData('area', prop);
+        totalChArea += chArea[i];
+      }
+      for(i=0; i<l; i++) {
+        ch[i]._area = parentArea * chArea[i] / totalChArea;
+      }
+     var side = this.layout.horizontal()? coord.width : coord.height;
+     var initElem = [ch[0]];
+     var tail = ch.slice(1);
+     this.stripify(tail, initElem, side, coord, prop);
+    },
+    
+    /*
+      Method: stripify
+    
+     Performs an heuristic method to calculate div elements sizes in order to have 
+     a good compromise between aspect ratio and order.
+    
+      Parameters:
+    
+         tail - An array of nodes.  
+         initElem - An array of nodes.
+         w - A fixed dimension where nodes will be layed out.
+       coord - A coordinates object specifying width, height, left and top style properties.
+    */
+    stripify: function(tail, initElem, w, coord, prop) {
+     this.computeDim(tail, initElem, w, coord, this.avgAspectRatio, prop);
+    },
+    
+    /*
+      Method: layoutRow
+    
+     Performs the layout of an array of nodes.
+    
+      Parameters:
+    
+         ch - An array of nodes.  
+         w - A fixed dimension where nodes will be laid out.
+         coord - A coordinates object specifying width, height, left and top style properties.
+    */
+    layoutRow: function(ch, w, coord, prop) {
+     if(this.layout.horizontal()) {
+       return this.layoutH(ch, w, coord, prop);
+     } else {
+       return this.layoutV(ch, w, coord, prop);
+     }
+    },
+    
+    layoutV: function(ch, w, coord, prop) {
+     var totalArea = 0; 
+     $.each(ch, function(elem) { totalArea += elem._area; });
+     var width = totalArea / w, top =  0; 
+     for(var i=0, l=ch.length; i<l; i++) {
+       var chi = ch[i];
+       var h = chi._area / width;
+       chi.getPos(prop).setc(coord.left, 
+           coord.top + (w - h - top));
+       chi.setData('width', width, prop);
+       chi.setData('height', h, prop);
+       top += h;
+     }
+    
+     return {
+       'height': coord.height,
+       'width': coord.width - width,
+       'top': coord.top,
+       'left': coord.left + width,
+       'dim': w
+     };
+    },
+    
+    layoutH: function(ch, w, coord, prop) {
+     var totalArea = 0; 
+     $.each(ch, function(elem) { totalArea += elem._area; });
+     var height = totalArea / w,
+         top = coord.height - height, 
+         left = 0;
+     
+     for(var i=0, l=ch.length; i<l; i++) {
+       var chi = ch[i];
+       var s = chi._area / height;
+       chi.getPos(prop).setc(coord.left + left, coord.top + top);
+       chi.setData('width', s, prop);
+       chi.setData('height', height, prop);
+       left += s;
+     }
+     return {
+       'height': coord.height - height,
+       'width': coord.width,
+       'top': coord.top,
+       'left': coord.left,
+       'dim': w
+     };
+    }
+ });
+
+
+/*
+ * Class: Layouts.Icicle
+ *
+ * Implements the icicle tree layout.
+ *
+ * Implemented By:
+ *
+ * <Icicle>
+ *
+ */
+
+Layouts.Icicle = new Class({
+ /*
+  * Method: compute
+  *
+  * Called by loadJSON to calculate all node positions.
+  *
+  * Parameters:
+  *
+  * posType - The nodes' position to compute. Either "start", "end" or
+  *            "current". Defaults to "current".
+  */
+  compute: function(posType) {
+    posType = posType || "current";
+
+    var root = this.graph.getNode(this.root),
+        config = this.config,
+        size = this.canvas.getSize(),
+        width = size.width,
+        height = size.height,
+        offset = config.offset,
+        levelsToShow = config.constrained ? config.levelsToShow : Number.MAX_VALUE;
+
+    this.controller.onBeforeCompute(root);
+
+    Graph.Util.computeLevels(this.graph, root.id, 0, "ignore");
+
+    var treeDepth = 0;
+
+    Graph.Util.eachLevel(root, 0, false, function (n, d) { if(d > treeDepth) treeDepth = d; });
+
+    var startNode = this.graph.getNode(this.clickedNode && this.clickedNode.id || root.id);
+    var maxDepth = Math.min(treeDepth, levelsToShow-1);
+    var initialDepth = startNode._depth;
+    if(this.layout.horizontal()) {
+      this.computeSubtree(startNode, -width/2, -height/2, width/(maxDepth+1), height, initialDepth, maxDepth, posType);
+    } else {
+      this.computeSubtree(startNode, -width/2, -height/2, width, height/(maxDepth+1), initialDepth, maxDepth, posType);
+    }
+  },
+
+  computeSubtree: function (root, x, y, width, height, initialDepth, maxDepth, posType) {
+    root.getPos(posType).setc(x, y);
+    root.setData('width', width, posType);
+    root.setData('height', height, posType);
+
+    var nodeLength, prevNodeLength = 0, totalDim = 0;
+    var children = Graph.Util.getSubnodes(root, [1, 1], 'ignore'); // next level from this node
+
+    if(!children.length)
+      return;
+
+    $.each(children, function(e) { totalDim += e.getData('dim'); });
+
+    for(var i=0, l=children.length; i < l; i++) {
+      if(this.layout.horizontal()) {
+        nodeLength = height * children[i].getData('dim') / totalDim;
+        this.computeSubtree(children[i], x+width, y, width, nodeLength, initialDepth, maxDepth, posType);
+        y += nodeLength;
+      } else {
+        nodeLength = width * children[i].getData('dim') / totalDim;
+        this.computeSubtree(children[i], x, y+height, nodeLength, height, initialDepth, maxDepth, posType);
+        x += nodeLength;
+      }
+    }
+  }
+});
+
+
+
+/*
+ * File: Icicle.js
+ *
+*/
+
+/*
+  Class: Icicle
+  
+  Icicle space filling visualization.
+  
+  Implements:
+  
+  All <Loader> methods
+  
+  Constructor Options:
+  
+  Inherits options from
+  
+  - <Options.Canvas>
+  - <Options.Controller>
+  - <Options.Node>
+  - <Options.Edge>
+  - <Options.Label>
+  - <Options.Events>
+  - <Options.Tips>
+  - <Options.NodeStyles>
+  - <Options.Navigation>
+  
+  Additionally, there are other parameters and some default values changed
+
+  orientation - (string) Default's *h*. Whether to set horizontal or vertical layouts. Possible values are 'h' and 'v'.
+  offset - (number) Default's *2*. Boxes offset.
+  constrained - (boolean) Default's *false*. Whether to show the entire tree when loaded or just the number of levels specified by _levelsToShow_.
+  levelsToShow - (number) Default's *3*. The number of levels to show for a subtree. This number is relative to the selected node.
+  animate - (boolean) Default's *false*. Whether to animate transitions.
+  Node.type - Described in <Options.Node>. Default's *rectangle*.
+  Label.type - Described in <Options.Label>. Default's *Native*.
+  duration - Described in <Options.Fx>. Default's *700*.
+  fps - Described in <Options.Fx>. Default's *45*.
+  
+  Instance Properties:
+  
+  canvas - Access a <Canvas> instance.
+  graph - Access a <Graph> instance.
+  op - Access a <Icicle.Op> instance.
+  fx - Access a <Icicle.Plot> instance.
+  labels - Access a <Icicle.Label> interface implementation.
+
+*/
+
+$jit.Icicle = new Class({
+  Implements: [ Loader, Extras, Layouts.Icicle ],
+
+  layout: {
+    orientation: "h",
+    vertical: function(){
+      return this.orientation == "v";
+    },
+    horizontal: function(){
+      return this.orientation == "h";
+    },
+    change: function(){
+      this.orientation = this.vertical()? "h" : "v";
+    }
+  },
+
+  initialize: function(controller) {
+    var config = {
+      animate: false,
+      orientation: "h",
+      offset: 2,
+      levelsToShow: Number.MAX_VALUE,
+      constrained: false,
+      Node: {
+        type: 'rectangle',
+        overridable: true
+      },
+      Edge: {
+        type: 'none'
+      },
+      Label: {
+        type: 'Native'
+      },
+      duration: 700,
+      fps: 45
+    };
+
+    var opts = Options("Canvas", "Node", "Edge", "Fx", "Tips", "NodeStyles",
+                       "Events", "Navigation", "Controller", "Label");
+    this.controller = this.config = $.merge(opts, config, controller);
+    this.layout.orientation = this.config.orientation;
+
+    var canvasConfig = this.config;
+    if (canvasConfig.useCanvas) {
+      this.canvas = canvasConfig.useCanvas;
+      this.config.labelContainer = this.canvas.id + '-label';
+    } else {
+      this.canvas = new Canvas(this, canvasConfig);
+      this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
+    }
+
+    this.graphOptions = {
+      'klass': Complex,
+      'Node': {
+        'selected': false,
+        'exist': true,
+        'drawn': true
+      }
+    };
+
+    this.graph = new Graph(
+      this.graphOptions, this.config.Node, this.config.Edge, this.config.Label);
+
+    this.labels = new $jit.Icicle.Label[this.config.Label.type](this);
+    this.fx = new $jit.Icicle.Plot(this, $jit.Icicle);
+    this.op = new $jit.Icicle.Op(this);
+    this.group = new $jit.Icicle.Group(this);
+    this.clickedNode = null;
+
+    this.initializeExtras();
+  },
+
+  /* 
+    Method: refresh 
+    
+    Computes positions and plots the tree.
+  */
+  refresh: function(){
+    var labelType = this.config.Label.type;
+    if(labelType != 'Native') {
+      var that = this;
+      this.graph.eachNode(function(n) { that.labels.hideLabel(n, false); });
+    }
+    this.compute();
+    this.plot();
+  },
+
+  /* 
+    Method: plot 
+    
+    Plots the Icicle visualization. This is a shortcut to *fx.plot*. 
+  
+   */
+  plot: function(){
+    this.fx.plot(this.config);
+  },
+
+  /* 
+    Method: enter 
+    
+    Sets the node as root.
+    
+     Parameters:
+     
+     node - (object) A <Graph.Node>.
+  
+   */
+  enter: function (node) {
+    if (this.busy)
+      return;
+    this.busy = true;
+
+    var that = this,
+        config = this.config;
+
+    var callback = {
+      onComplete: function() {
+        //compute positions of newly inserted nodes
+        if(config.request)
+          that.compute();
+
+        if(config.animate) {
+          that.graph.nodeList.setDataset(['current', 'end'], {
+            'alpha': [1, 0] //fade nodes
+          });
+
+          Graph.Util.eachSubgraph(node, function(n) {
+            n.setData('alpha', 1, 'end');
+          }, "ignore");
+
+          that.fx.animate({
+            duration: 500,
+            modes:['node-property:alpha'],
+            onComplete: function() {
+              that.clickedNode = node;
+              that.compute('end');
+
+              that.fx.animate({
+                modes:['linear', 'node-property:width:height'],
+                duration: 1000,
+                onComplete: function() {
+                  that.busy = false;
+                  that.clickedNode = node;
+                }
+              });
+            }
+          });
+        } else {
+          that.clickedNode = node;
+          that.busy = false;
+          that.refresh();
+        }
+      }
+    };
+
+    if(config.request) {
+      this.requestNodes(clickedNode, callback);
+    } else {
+      callback.onComplete();
+    }
+  },
+
+  /* 
+    Method: out 
+    
+    Sets the parent node of the current selected node as root.
+  
+   */
+  out: function(){
+    if(this.busy)
+      return;
+
+    var that = this,
+        GUtil = Graph.Util,
+        config = this.config,
+        graph = this.graph,
+        parents = GUtil.getParents(graph.getNode(this.clickedNode && this.clickedNode.id || this.root)),
+        parent = parents[0],
+        clickedNode = parent,
+        previousClickedNode = this.clickedNode;
+
+    this.busy = true;
+    this.events.hoveredNode = false;
+
+    if(!parent) {
+      this.busy = false;
+      return;
+    }
+
+    //final plot callback
+    callback = {
+      onComplete: function() {
+        that.clickedNode = parent;
+        if(config.request) {
+          that.requestNodes(parent, {
+            onComplete: function() {
+              that.compute();
+              that.plot();
+              that.busy = false;
+            }
+          });
+        } else {
+          that.compute();
+          that.plot();
+          that.busy = false;
+        }
+      }
+    };
+
+    //animate node positions
+    if(config.animate) {
+      this.clickedNode = clickedNode;
+      this.compute('end');
+      //animate the visible subtree only
+      this.clickedNode = previousClickedNode;
+      this.fx.animate({
+        modes:['linear', 'node-property:width:height'],
+        duration: 1000,
+        onComplete: function() {
+          //animate the parent subtree
+          that.clickedNode = clickedNode;
+          //change nodes alpha
+          graph.nodeList.setDataset(['current', 'end'], {
+            'alpha': [0, 1]
+          });
+          GUtil.eachSubgraph(previousClickedNode, function(node) {
+            node.setData('alpha', 1);
+          }, "ignore");
+          that.fx.animate({
+            duration: 500,
+            modes:['node-property:alpha'],
+            onComplete: function() {
+              callback.onComplete();
+            }
+          });
+        }
+      });
+    } else {
+      callback.onComplete();
+    }
+  },
+  requestNodes: function(node, onComplete){
+    var handler = $.merge(this.controller, onComplete),
+        levelsToShow = this.config.constrained ? this.config.levelsToShow : Number.MAX_VALUE;
+
+    if (handler.request) {
+      var leaves = [], d = node._depth;
+      Graph.Util.eachLevel(node, 0, levelsToShow, function(n){
+        if (n.drawn && !Graph.Util.anySubnode(n)) {
+          leaves.push(n);
+          n._level = n._depth - d;
+          if (this.config.constrained)
+            n._level = levelsToShow - n._level;
+
+        }
+      });
+      this.group.requestNodes(leaves, handler);
+    } else {
+      handler.onComplete();
+    }
+  }
+});
+
+/*
+  Class: Icicle.Op
+  
+  Custom extension of <Graph.Op>.
+  
+  Extends:
+  
+  All <Graph.Op> methods
+  
+  See also:
+  
+  <Graph.Op>
+  
+  */
+$jit.Icicle.Op = new Class({
+
+  Implements: Graph.Op
+
+});
+
+/*
+ * Performs operations on group of nodes.
+ */
+$jit.Icicle.Group = new Class({
+
+  initialize: function(viz){
+    this.viz = viz;
+    this.canvas = viz.canvas;
+    this.config = viz.config;
+  },
+
+  /*
+   * Calls the request method on the controller to request a subtree for each node.
+   */
+  requestNodes: function(nodes, controller){
+    var counter = 0, len = nodes.length, nodeSelected = {};
+    var complete = function(){
+      controller.onComplete();
+    };
+    var viz = this.viz;
+    if (len == 0)
+      complete();
+    for(var i = 0; i < len; i++) {
+      nodeSelected[nodes[i].id] = nodes[i];
+      controller.request(nodes[i].id, nodes[i]._level, {
+        onComplete: function(nodeId, data){
+          if (data && data.children) {
+            data.id = nodeId;
+            viz.op.sum(data, {
+              type: 'nothing'
+            });
+          }
+          if (++counter == len) {
+            Graph.Util.computeLevels(viz.graph, viz.root, 0);
+            complete();
+          }
+        }
+      });
+    }
+  }
+});
+
+/*
+  Class: Icicle.Plot
+  
+  Custom extension of <Graph.Plot>.
+  
+  Extends:
+  
+  All <Graph.Plot> methods
+  
+  See also:
+  
+  <Graph.Plot>
+  
+  */
+$jit.Icicle.Plot = new Class({
+  Implements: Graph.Plot,
+
+  plot: function(opt, animating){
+    opt = opt || this.viz.controller;
+    var viz = this.viz,
+        graph = viz.graph,
+        root = graph.getNode(viz.clickedNode && viz.clickedNode.id || viz.root),
+        initialDepth = root._depth;
+
+    viz.canvas.clear();
+    this.plotTree(root, $.merge(opt, {
+      'withLabels': true,
+      'hideLabels': false,
+      'plotSubtree': function(root, node) {
+        return !viz.config.constrained ||
+               (node._depth - initialDepth < viz.config.levelsToShow);
+      }
+    }), animating);
+  }
+});
+
+/*
+  Class: Icicle.Label
+  
+  Custom extension of <Graph.Label>. 
+  Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
+  
+  Extends:
+  
+  All <Graph.Label> methods and subclasses.
+  
+  See also:
+  
+  <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
+  
+  */
+$jit.Icicle.Label = {};
+
+/*
+  Icicle.Label.Native
+  
+  Custom extension of <Graph.Label.Native>.
+  
+  Extends:
+  
+  All <Graph.Label.Native> methods
+  
+  See also:
+  
+  <Graph.Label.Native>
+
+  */
+$jit.Icicle.Label.Native = new Class({
+  Implements: Graph.Label.Native,
+
+  renderLabel: function(canvas, node, controller) {
+    var ctx = canvas.getCtx(),
+        width = node.getData('width'),
+        height = node.getData('height'),
+        size = node.getLabelData('size'),
+        m = ctx.measureText(node.name);
+
+    // Guess as much as possible if the label will fit in the node
+    if(height < (size * 1.5) || width < m.width)
+      return;
+
+    var pos = node.pos.getc(true);
+    ctx.fillText(node.name,
+                 pos.x + width / 2,
+                 pos.y + height / 2);
+  }
+});
+
+/*
+  Icicle.Label.SVG
+  
+  Custom extension of <Graph.Label.SVG>.
+  
+  Extends:
+  
+  All <Graph.Label.SVG> methods
+  
+  See also:
+  
+  <Graph.Label.SVG>
+*/
+$jit.Icicle.Label.SVG = new Class( {
+  Implements: Graph.Label.SVG,
+
+  initialize: function(viz){
+    this.viz = viz;
+  },
+
+  /*
+    placeLabel
+   
+    Overrides abstract method placeLabel in <Graph.Plot>.
+   
+    Parameters:
+   
+    tag - A DOM label element.
+    node - A <Graph.Node>.
+    controller - A configuration/controller object passed to the visualization.
+   */
+  placeLabel: function(tag, node, controller){
+    var pos = node.pos.getc(true), canvas = this.viz.canvas;
+    var radius = canvas.getSize();
+    var labelPos = {
+      x: Math.round(pos.x + radius.width / 2),
+      y: Math.round(pos.y + radius.height / 2)
+    };
+    tag.setAttribute('x', labelPos.x);
+    tag.setAttribute('y', labelPos.y);
+
+    controller.onPlaceLabel(tag, node);
+  }
+});
+
+/*
+  Icicle.Label.HTML
+  
+  Custom extension of <Graph.Label.HTML>.
+  
+  Extends:
+  
+  All <Graph.Label.HTML> methods.
+  
+  See also:
+  
+  <Graph.Label.HTML>
+  
+  */
+$jit.Icicle.Label.HTML = new Class( {
+  Implements: Graph.Label.HTML,
+
+  initialize: function(viz){
+    this.viz = viz;
+  },
+
+  /*
+    placeLabel
+   
+    Overrides abstract method placeLabel in <Graph.Plot>.
+   
+    Parameters:
+   
+    tag - A DOM label element.
+    node - A <Graph.Node>.
+    controller - A configuration/controller object passed to the visualization.
+   */
+  placeLabel: function(tag, node, controller){
+    var pos = node.pos.getc(true), canvas = this.viz.canvas;
+    var radius = canvas.getSize();
+    var labelPos = {
+      x: Math.round(pos.x + radius.width / 2),
+      y: Math.round(pos.y + radius.height / 2)
+    };
+
+    var style = tag.style;
+    style.left = labelPos.x + 'px';
+    style.top = labelPos.y + 'px';
+    style.display = '';
+
+    controller.onPlaceLabel(tag, node);
+  }
+});
+
+/*
+  Class: Icicle.Plot.NodeTypes
+  
+  This class contains a list of <Graph.Node> built-in types. 
+  Node types implemented are 'none', 'rectangle'.
+  
+  You can add your custom node types, customizing your visualization to the extreme.
+  
+  Example:
+  
+  (start code js)
+    Icicle.Plot.NodeTypes.implement({
+      'mySpecialType': {
+        'render': function(node, canvas) {
+          //print your custom node to canvas
+        },
+        //optional
+        'contains': function(node, pos) {
+          //return true if pos is inside the node or false otherwise
+        }
+      }
+    });
+  (end code)
+  
+  */
+$jit.Icicle.Plot.NodeTypes = new Class( {
+  'none': {
+    'render': $.empty
+  },
+
+  'rectangle': {
+    'render': function(node, canvas, animating) {
+      var config = this.viz.config;
+      var offset = config.offset;
+      var width = node.getData('width');
+      var height = node.getData('height');
+      var border = node.getData('border');
+      var pos = node.pos.getc(true);
+      var posx = pos.x + offset / 2, posy = pos.y + offset / 2;
+      var ctx = canvas.getCtx();
+      
+      if(width - offset < 2 || height - offset < 2) return;
+      
+      if(config.cushion) {
+        var color = node.getData('color');
+        var lg = ctx.createRadialGradient(posx + (width - offset)/2, 
+                                          posy + (height - offset)/2, 1, 
+                                          posx + (width-offset)/2, posy + (height-offset)/2, 
+                                          width < height? height : width);
+        var colorGrad = $.rgbToHex($.map($.hexToRgb(color), 
+            function(r) { return r * 0.3 >> 0; }));
+        lg.addColorStop(0, color);
+        lg.addColorStop(1, colorGrad);
+        ctx.fillStyle = lg;
+      }
+
+      if (border) {
+        ctx.strokeStyle = border;
+        ctx.lineWidth = 3;
+      }
+
+      ctx.fillRect(posx, posy, Math.max(0, width - offset), Math.max(0, height - offset));
+      border && ctx.strokeRect(pos.x, pos.y, width, height);
+    },
+
+    'contains': function(node, pos) {
+      if(this.viz.clickedNode && !$jit.Graph.Util.isDescendantOf(node, this.viz.clickedNode.id)) return false;
+      var npos = node.pos.getc(true),
+          width = node.getData('width'),
+          height = node.getData('height');
+      return this.nodeHelper.rectangle.contains({x: npos.x + width/2, y: npos.y + height/2}, pos, width, height);
+    }
+  }
+});
+
+$jit.Icicle.Plot.EdgeTypes = new Class( {
+  'none': $.empty
+});
+
+
+
+/*
+ * File: Layouts.ForceDirected.js
+ *
+*/
+
+/*
+ * Class: Layouts.ForceDirected
+ * 
+ * Implements a Force Directed Layout.
+ * 
+ * Implemented By:
+ * 
+ * <ForceDirected>
+ * 
+ * Credits:
+ * 
+ * Marcus Cobden <http://marcuscobden.co.uk>
+ * 
+ */
+Layouts.ForceDirected = new Class({
+
+  getOptions: function(random) {
+    var s = this.canvas.getSize();
+    var w = s.width, h = s.height;
+    //count nodes
+    var count = 0;
+    this.graph.eachNode(function(n) { 
+      count++;
+    });
+    var k2 = w * h / count, k = Math.sqrt(k2);
+    var l = this.config.levelDistance;
+    
+    return {
+      width: w,
+      height: h,
+      tstart: w * 0.1,
+      nodef: function(x) { return k2 / (x || 1); },
+      edgef: function(x) { return /* x * x / k; */ k * (x - l); }
+    };
+  },
+  
+  compute: function(property, incremental) {
+    var prop = $.splat(property || ['current', 'start', 'end']);
+    var opt = this.getOptions();
+    NodeDim.compute(this.graph, prop, this.config);
+    this.graph.computeLevels(this.root, 0, "ignore");
+    this.graph.eachNode(function(n) {
+      $.each(prop, function(p) {
+        var pos = n.getPos(p);
+        if(pos.equals(Complex.KER)) {
+          pos.x = opt.width/5 * (Math.random() - 0.5);
+          pos.y = opt.height/5 * (Math.random() - 0.5);
+        }
+        //initialize disp vector
+        n.disp = {};
+        $.each(prop, function(p) {
+          n.disp[p] = $C(0, 0);
+        });
+      });
+    });
+    this.computePositions(prop, opt, incremental);
+  },
+  
+  computePositions: function(property, opt, incremental) {
+    var times = this.config.iterations, i = 0, that = this;
+    if(incremental) {
+      (function iter() {
+        for(var total=incremental.iter, j=0; j<total; j++) {
+          opt.t = opt.tstart;
+          if(times) opt.t *= (1 - i++/(times -1));
+          that.computePositionStep(property, opt);
+          if(times && i >= times) {
+            incremental.onComplete();
+            return;
+          }
+        }
+        incremental.onStep(Math.round(i / (times -1) * 100));
+        setTimeout(iter, 1);
+      })();
+    } else {
+      for(; i < times; i++) {
+        opt.t = opt.tstart * (1 - i/(times -1));
+        this.computePositionStep(property, opt);
+      }
+    }
+  },
+  
+  computePositionStep: function(property, opt) {
+    var graph = this.graph;
+    var min = Math.min, max = Math.max;
+    var dpos = $C(0, 0);
+    //calculate repulsive forces
+    graph.eachNode(function(v) {
+      //initialize disp
+      $.each(property, function(p) {
+        v.disp[p].x = 0; v.disp[p].y = 0;
+      });
+      graph.eachNode(function(u) {
+        if(u.id != v.id) {
+          $.each(property, function(p) {
+            var vp = v.getPos(p), up = u.getPos(p);
+            dpos.x = vp.x - up.x;
+            dpos.y = vp.y - up.y;
+            var norm = dpos.norm() || 1;
+            v.disp[p].$add(dpos
+                .$scale(opt.nodef(norm) / norm));
+          });
+        }
+      });
+    });
+    //calculate attractive forces
+    var T = !!graph.getNode(this.root).visited;
+    graph.eachNode(function(node) {
+      node.eachAdjacency(function(adj) {
+        var nodeTo = adj.nodeTo;
+        if(!!nodeTo.visited === T) {
+          $.each(property, function(p) {
+            var vp = node.getPos(p), up = nodeTo.getPos(p);
+            dpos.x = vp.x - up.x;
+            dpos.y = vp.y - up.y;
+            var norm = dpos.norm() || 1;
+            node.disp[p].$add(dpos.$scale(-opt.edgef(norm) / norm));
+            nodeTo.disp[p].$add(dpos.$scale(-1));
+          });
+        }
+      });
+      node.visited = !T;
+    });
+    //arrange positions to fit the canvas
+    var t = opt.t, w2 = opt.width / 2, h2 = opt.height / 2;
+    graph.eachNode(function(u) {
+      $.each(property, function(p) {
+        var disp = u.disp[p];
+        var norm = disp.norm() || 1;
+        var p = u.getPos(p);
+        p.$add($C(disp.x * min(Math.abs(disp.x), t) / norm, 
+            disp.y * min(Math.abs(disp.y), t) / norm));
+        p.x = min(w2, max(-w2, p.x));
+        p.y = min(h2, max(-h2, p.y));
+      });
+    });
+  }
+});
+
+/*
+ * File: ForceDirected.js
+ */
+
+/*
+   Class: ForceDirected
+      
+   A visualization that lays graphs using a Force-Directed layout algorithm.
+   
+   Inspired by:
+  
+   Force-Directed Drawing Algorithms (Stephen G. Kobourov) <http://www.cs.brown.edu/~rt/gdhandbook/chapters/force-directed.pdf>
+   
+  Implements:
+  
+  All <Loader> methods
+  
+   Constructor Options:
+   
+   Inherits options from
+   
+   - <Options.Canvas>
+   - <Options.Controller>
+   - <Options.Node>
+   - <Options.Edge>
+   - <Options.Label>
+   - <Options.Events>
+   - <Options.Tips>
+   - <Options.NodeStyles>
+   - <Options.Navigation>
+   
+   Additionally, there are two parameters
+   
+   levelDistance - (number) Default's *50*. The natural length desired for the edges.
+   iterations - (number) Default's *50*. The number of iterations for the spring layout simulation. Depending on the browser's speed you could set this to a more 'interesting' number, like *200*. 
+     
+   Instance Properties:
+
+   canvas - Access a <Canvas> instance.
+   graph - Access a <Graph> instance.
+   op - Access a <ForceDirected.Op> instance.
+   fx - Access a <ForceDirected.Plot> instance.
+   labels - Access a <ForceDirected.Label> interface implementation.
+
+*/
+
+$jit.ForceDirected = new Class( {
+
+  Implements: [ Loader, Extras, Layouts.ForceDirected ],
+
+  initialize: function(controller) {
+    var $ForceDirected = $jit.ForceDirected;
+
+    var config = {
+      iterations: 50,
+      levelDistance: 50
+    };
+
+    this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",
+        "Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label"), config, controller);
+
+    var canvasConfig = this.config;
+    if(canvasConfig.useCanvas) {
+      this.canvas = canvasConfig.useCanvas;
+      this.config.labelContainer = this.canvas.id + '-label';
+    } else {
+      if(canvasConfig.background) {
+        canvasConfig.background = $.merge({
+          type: 'Circles'
+        }, canvasConfig.background);
+      }
+      this.canvas = new Canvas(this, canvasConfig);
+      this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
+    }
+
+    this.graphOptions = {
+      'klass': Complex,
+      'Node': {
+        'selected': false,
+        'exist': true,
+        'drawn': true
+      }
+    };
+    this.graph = new Graph(this.graphOptions, this.config.Node,
+        this.config.Edge);
+    this.labels = new $ForceDirected.Label[canvasConfig.Label.type](this);
+    this.fx = new $ForceDirected.Plot(this, $ForceDirected);
+    this.op = new $ForceDirected.Op(this);
+    this.json = null;
+    this.busy = false;
+    // initialize extras
+    this.initializeExtras();
+  },
+
+  /* 
+    Method: refresh 
+    
+    Computes positions and plots the tree.
+  */
+  refresh: function() {
+    this.compute();
+    this.plot();
+  },
+
+  reposition: function() {
+    this.compute('end');
+  },
+
+/*
+  Method: computeIncremental
+  
+  Performs the Force Directed algorithm incrementally.
+  
+  Description:
+  
+  ForceDirected algorithms can perform many computations and lead to JavaScript taking too much time to complete. 
+  This method splits the algorithm into smaller parts allowing the user to track the evolution of the algorithm and 
+  avoiding browser messages such as "This script is taking too long to complete".
+  
+  Parameters:
+  
+  opt - (object) The object properties are described below
+  
+  iter - (number) Default's *20*. Split the algorithm into pieces of _iter_ iterations. For example, if the _iterations_ configuration property 
+  of your <ForceDirected> class is 100, then you could set _iter_ to 20 to split the main algorithm into 5 smaller pieces.
+  
+  property - (string) Default's *end*. Whether to update starting, current or ending node positions. Possible values are 'end', 'start', 'current'. 
+  You can also set an array of these properties. If you'd like to keep the current node positions but to perform these 
+  computations for final animation positions then you can just choose 'end'.
+  
+  onStep - (function) A callback function called when each "small part" of the algorithm completed. This function gets as first formal 
+  parameter a percentage value.
+  
+  onComplete - A callback function called when the algorithm completed.
+  
+  Example:
+  
+  In this example I calculate the end positions and then animate the graph to those positions
+  
+  (start code js)
+  var fd = new $jit.ForceDirected(...);
+  fd.computeIncremental({
+    iter: 20,
+    property: 'end',
+    onStep: function(perc) {
+      Log.write("loading " + perc + "%");
+    },
+    onComplete: function() {
+      Log.write("done");
+      fd.animate();
+    }
+  });
+  (end code)
+  
+  In this example I calculate all positions and (re)plot the graph
+  
+  (start code js)
+  var fd = new ForceDirected(...);
+  fd.computeIncremental({
+    iter: 20,
+    property: ['end', 'start', 'current'],
+    onStep: function(perc) {
+      Log.write("loading " + perc + "%");
+    },
+    onComplete: function() {
+      Log.write("done");
+      fd.plot();
+    }
+  });
+  (end code)
+  
+  */
+  computeIncremental: function(opt) {
+    opt = $.merge( {
+      iter: 20,
+      property: 'end',
+      onStep: $.empty,
+      onComplete: $.empty
+    }, opt || {});
+
+    this.config.onBeforeCompute(this.graph.getNode(this.root));
+    this.compute(opt.property, opt);
+  },
+
+  /*
+    Method: plot
+   
+    Plots the ForceDirected graph. This is a shortcut to *fx.plot*.
+   */
+  plot: function() {
+    this.fx.plot();
+  },
+
+  /*
+     Method: animate
+    
+     Animates the graph from the current positions to the 'end' node positions.
+  */
+  animate: function(opt) {
+    this.fx.animate($.merge( {
+      modes: [ 'linear' ]
+    }, opt || {}));
+  }
+});
+
+$jit.ForceDirected.$extend = true;
+
+(function(ForceDirected) {
+
+  /*
+     Class: ForceDirected.Op
+     
+     Custom extension of <Graph.Op>.
+
+     Extends:
+
+     All <Graph.Op> methods
+     
+     See also:
+     
+     <Graph.Op>
+
+  */
+  ForceDirected.Op = new Class( {
+
+    Implements: Graph.Op
+
+  });
+
+  /*
+    Class: ForceDirected.Plot
+    
+    Custom extension of <Graph.Plot>.
+  
+    Extends:
+  
+    All <Graph.Plot> methods
+    
+    See also:
+    
+    <Graph.Plot>
+  
+  */
+  ForceDirected.Plot = new Class( {
+
+    Implements: Graph.Plot
+
+  });
+
+  /*
+    Class: ForceDirected.Label
+    
+    Custom extension of <Graph.Label>. 
+    Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
+  
+    Extends:
+  
+    All <Graph.Label> methods and subclasses.
+  
+    See also:
+  
+    <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
+  
+  */
+  ForceDirected.Label = {};
+
+  /*
+     ForceDirected.Label.Native
+     
+     Custom extension of <Graph.Label.Native>.
+
+     Extends:
+
+     All <Graph.Label.Native> methods
+
+     See also:
+
+     <Graph.Label.Native>
+
+  */
+  ForceDirected.Label.Native = new Class( {
+    Implements: Graph.Label.Native
+  });
+
+  /*
+    ForceDirected.Label.SVG
+    
+    Custom extension of <Graph.Label.SVG>.
+  
+    Extends:
+  
+    All <Graph.Label.SVG> methods
+  
+    See also:
+  
+    <Graph.Label.SVG>
+  
+  */
+  ForceDirected.Label.SVG = new Class( {
+    Implements: Graph.Label.SVG,
+
+    initialize: function(viz) {
+      this.viz = viz;
+    },
+
+    /* 
+       placeLabel
+
+       Overrides abstract method placeLabel in <Graph.Label>.
+
+       Parameters:
+
+       tag - A DOM label element.
+       node - A <Graph.Node>.
+       controller - A configuration/controller object passed to the visualization.
+      
+     */
+    placeLabel: function(tag, node, controller) {
+      var pos = node.pos.getc(true), 
+          canvas = this.viz.canvas,
+          ox = canvas.translateOffsetX,
+          oy = canvas.translateOffsetY,
+          sx = canvas.scaleOffsetX,
+          sy = canvas.scaleOffsetY,
+          radius = canvas.getSize();
+      var labelPos = {
+        x: Math.round(pos.x * sx + ox + radius.width / 2),
+        y: Math.round(pos.y * sy + oy + radius.height / 2)
+      };
+      tag.setAttribute('x', labelPos.x);
+      tag.setAttribute('y', labelPos.y);
+
+      controller.onPlaceLabel(tag, node);
+    }
+  });
+
+  /*
+     ForceDirected.Label.HTML
+     
+     Custom extension of <Graph.Label.HTML>.
+
+     Extends:
+
+     All <Graph.Label.HTML> methods.
+
+     See also:
+
+     <Graph.Label.HTML>
+
+  */
+  ForceDirected.Label.HTML = new Class( {
+    Implements: Graph.Label.HTML,
+
+    initialize: function(viz) {
+      this.viz = viz;
+    },
+    /* 
+       placeLabel
+
+       Overrides abstract method placeLabel in <Graph.Plot>.
+
+       Parameters:
+
+       tag - A DOM label element.
+       node - A <Graph.Node>.
+       controller - A configuration/controller object passed to the visualization.
+      
+     */
+    placeLabel: function(tag, node, controller) {
+      var pos = node.pos.getc(true), 
+          canvas = this.viz.canvas,
+          ox = canvas.translateOffsetX,
+          oy = canvas.translateOffsetY,
+          sx = canvas.scaleOffsetX,
+          sy = canvas.scaleOffsetY,
+          radius = canvas.getSize();
+      var labelPos = {
+        x: Math.round(pos.x * sx + ox + radius.width / 2),
+        y: Math.round(pos.y * sy + oy + radius.height / 2)
+      };
+      var style = tag.style;
+      style.left = labelPos.x + 'px';
+      style.top = labelPos.y + 'px';
+      style.display = this.fitsInCanvas(labelPos, canvas) ? '' : 'none';
+
+      controller.onPlaceLabel(tag, node);
+    }
+  });
+
+  /*
+    Class: ForceDirected.Plot.NodeTypes
+
+    This class contains a list of <Graph.Node> built-in types. 
+    Node types implemented are 'none', 'circle', 'triangle', 'rectangle', 'star', 'ellipse' and 'square'.
+
+    You can add your custom node types, customizing your visualization to the extreme.
+
+    Example:
+
+    (start code js)
+      ForceDirected.Plot.NodeTypes.implement({
+        'mySpecialType': {
+          'render': function(node, canvas) {
+            //print your custom node to canvas
+          },
+          //optional
+          'contains': function(node, pos) {
+            //return true if pos is inside the node or false otherwise
+          }
+        }
+      });
+    (end code)
+
+  */
+  ForceDirected.Plot.NodeTypes = new Class({
+    'none': {
+      'render': $.empty,
+      'contains': $.lambda(false)
+    },
+    'circle': {
+      'render': function(node, canvas){
+        var pos = node.pos.getc(true), 
+            dim = node.getData('dim');
+        this.nodeHelper.circle.render('fill', pos, dim, canvas);
+      },
+      'contains': function(node, pos){
+        var npos = node.pos.getc(true), 
+            dim = node.getData('dim');
+        return this.nodeHelper.circle.contains(npos, pos, dim);
+      }
+    },
+    'ellipse': {
+      'render': function(node, canvas){
+        var pos = node.pos.getc(true), 
+            width = node.getData('width'), 
+            height = node.getData('height');
+        this.nodeHelper.ellipse.render('fill', pos, width, height, canvas);
+        },
+      'contains': function(node, pos){
+        var npos = node.pos.getc(true), 
+            width = node.getData('width'), 
+            height = node.getData('height');
+        return this.nodeHelper.ellipse.contains(npos, pos, width, height);
+      }
+    },
+    'square': {
+      'render': function(node, canvas){
+        var pos = node.pos.getc(true), 
+            dim = node.getData('dim');
+        this.nodeHelper.square.render('fill', pos, dim, canvas);
+      },
+      'contains': function(node, pos){
+        var npos = node.pos.getc(true), 
+            dim = node.getData('dim');
+        return this.nodeHelper.square.contains(npos, pos, dim);
+      }
+    },
+    'rectangle': {
+      'render': function(node, canvas){
+        var pos = node.pos.getc(true), 
+            width = node.getData('width'), 
+            height = node.getData('height');
+        this.nodeHelper.rectangle.render('fill', pos, width, height, canvas);
+      },
+      'contains': function(node, pos){
+        var npos = node.pos.getc(true), 
+            width = node.getData('width'), 
+            height = node.getData('height');
+        return this.nodeHelper.rectangle.contains(npos, pos, width, height);
+      }
+    },
+    'triangle': {
+      'render': function(node, canvas){
+        var pos = node.pos.getc(true), 
+            dim = node.getData('dim');
+        this.nodeHelper.triangle.render('fill', pos, dim, canvas);
+      },
+      'contains': function(node, pos) {
+        var npos = node.pos.getc(true), 
+            dim = node.getData('dim');
+        return this.nodeHelper.triangle.contains(npos, pos, dim);
+      }
+    },
+    'star': {
+      'render': function(node, canvas){
+        var pos = node.pos.getc(true),
+            dim = node.getData('dim');
+        this.nodeHelper.star.render('fill', pos, dim, canvas);
+      },
+      'contains': function(node, pos) {
+        var npos = node.pos.getc(true),
+            dim = node.getData('dim');
+        return this.nodeHelper.star.contains(npos, pos, dim);
+      }
+    }
+  });
+
+  /*
+    Class: ForceDirected.Plot.EdgeTypes
+  
+    This class contains a list of <Graph.Adjacence> built-in types. 
+    Edge types implemented are 'none', 'line' and 'arrow'.
+  
+    You can add your custom edge types, customizing your visualization to the extreme.
+  
+    Example:
+  
+    (start code js)
+      ForceDirected.Plot.EdgeTypes.implement({
+        'mySpecialType': {
+          'render': function(adj, canvas) {
+            //print your custom edge to canvas
+          },
+          //optional
+          'contains': function(adj, pos) {
+            //return true if pos is inside the arc or false otherwise
+          }
+        }
+      });
+    (end code)
+  
+  */
+  ForceDirected.Plot.EdgeTypes = new Class({
+    'none': $.empty,
+    'line': {
+      'render': function(adj, canvas) {
+        var from = adj.nodeFrom.pos.getc(true),
+            to = adj.nodeTo.pos.getc(true);
+        this.edgeHelper.line.render(from, to, canvas);
+      },
+      'contains': function(adj, pos) {
+        var from = adj.nodeFrom.pos.getc(true),
+            to = adj.nodeTo.pos.getc(true);
+        return this.edgeHelper.line.contains(from, to, pos, this.edge.epsilon);
+      }
+    },
+    'arrow': {
+      'render': function(adj, canvas) {
+        var from = adj.nodeFrom.pos.getc(true),
+            to = adj.nodeTo.pos.getc(true),
+            dim = adj.getData('dim'),
+            direction = adj.data.$direction,
+            inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);
+        this.edgeHelper.arrow.render(from, to, dim, inv, canvas);
+      },
+      'contains': function(adj, pos) {
+        var from = adj.nodeFrom.pos.getc(true),
+            to = adj.nodeTo.pos.getc(true);
+        return this.edgeHelper.arrow.contains(from, to, pos, this.edge.epsilon);
+      }
+    }
+  });
+
+})($jit.ForceDirected);
+
+
+/*
+ * File: Treemap.js
+ *
+*/
+
+$jit.TM = {};
+
+var TM = $jit.TM;
+
+$jit.TM.$extend = true;
+
+/*
+  Class: TM.Base
+  
+  Abstract class providing base functionality for <TM.Squarified>, <TM.Strip> and <TM.SliceAndDice> visualizations.
+  
+  Implements:
+  
+  All <Loader> methods
+  
+  Constructor Options:
+  
+  Inherits options from
+  
+  - <Options.Canvas>
+  - <Options.Controller>
+  - <Options.Node>
+  - <Options.Edge>
+  - <Options.Label>
+  - <Options.Events>
+  - <Options.Tips>
+  - <Options.NodeStyles>
+  - <Options.Navigation>
+  
+  Additionally, there are other parameters and some default values changed
+
+  orientation - (string) Default's *h*. Whether to set horizontal or vertical layouts. Possible values are 'h' and 'v'.
+  titleHeight - (number) Default's *13*. The height of the title rectangle for inner (non-leaf) nodes.
+  offset - (number) Default's *2*. Boxes offset.
+  constrained - (boolean) Default's *false*. Whether to show the entire tree when loaded or just the number of levels specified by _levelsToShow_.
+  levelsToShow - (number) Default's *3*. The number of levels to show for a subtree. This number is relative to the selected node.
+  animate - (boolean) Default's *false*. Whether to animate transitions.
+  Node.type - Described in <Options.Node>. Default's *rectangle*.
+  duration - Described in <Options.Fx>. Default's *700*.
+  fps - Described in <Options.Fx>. Default's *45*.
+  
+  Instance Properties:
+  
+  canvas - Access a <Canvas> instance.
+  graph - Access a <Graph> instance.
+  op - Access a <TM.Op> instance.
+  fx - Access a <TM.Plot> instance.
+  labels - Access a <TM.Label> interface implementation.
+
+  Inspired by:
+  
+  Squarified Treemaps (Mark Bruls, Kees Huizing, and Jarke J. van Wijk) <http://www.win.tue.nl/~vanwijk/stm.pdf>
+  
+  Tree visualization with tree-maps: 2-d space-filling approach (Ben Shneiderman) <http://hcil.cs.umd.edu/trs/91-03/91-03.html>
+  
+   Note:
+   
+   This visualization was built and engineered from scratch, taking only the paper as inspiration, and only shares some features with the visualization described in the paper.
+
+*/
+TM.Base = {
+  layout: {
+    orientation: "h",
+    vertical: function(){
+      return this.orientation == "v";
+    },
+    horizontal: function(){
+      return this.orientation == "h";
+    },
+    change: function(){
+      this.orientation = this.vertical()? "h" : "v";
+    }
+  },
+
+  initialize: function(controller){
+    var config = {
+      orientation: "h",
+      titleHeight: 13,
+      offset: 2,
+      levelsToShow: 0,
+      constrained: false,
+      animate: false,
+      Node: {
+        type: 'rectangle',
+        overridable: true,
+        //we all know why this is not zero,
+        //right, Firefox?
+        width: 3,
+        height: 3,
+        color: '#444'
+      },
+      Label: {
+        textAlign: 'center',
+        textBaseline: 'top'
+      },
+      Edge: {
+        type: 'none'
+      },
+      duration: 700,
+      fps: 45
+    };
+
+    this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",
+        "Fx", "Controller", "Tips", "NodeStyles", "Events", "Navigation", "Label"), config, controller);
+    this.layout.orientation = this.config.orientation;
+
+    var canvasConfig = this.config;
+    if (canvasConfig.useCanvas) {
+      this.canvas = canvasConfig.useCanvas;
+      this.config.labelContainer = this.canvas.id + '-label';
+    } else {
+      if(canvasConfig.background) {
+        canvasConfig.background = $.merge({
+          type: 'Circles'
+        }, canvasConfig.background);
+      }
+      this.canvas = new Canvas(this, canvasConfig);
+      this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
+    }
+
+    this.graphOptions = {
+      'klass': Complex,
+      'Node': {
+        'selected': false,
+        'exist': true,
+        'drawn': true
+      }
+    };
+    this.graph = new Graph(this.graphOptions, this.config.Node,
+        this.config.Edge);
+    this.labels = new TM.Label[canvasConfig.Label.type](this);
+    this.fx = new TM.Plot(this);
+    this.op = new TM.Op(this);
+    this.group = new TM.Group(this);
+    this.geom = new TM.Geom(this);
+    this.clickedNode = null;
+    this.busy = false;
+    // initialize extras
+    this.initializeExtras();
+  },
+
+  /* 
+    Method: refresh 
+    
+    Computes positions and plots the tree.
+  */
+  refresh: function(){
+    if(this.busy) return;
+    this.busy = true;
+    var that = this;
+    if(this.config.animate) {
+      this.compute('end');
+      this.config.levelsToShow > 0 && this.geom.setRightLevelToShow(this.graph.getNode(this.clickedNode 
+          && this.clickedNode.id || this.root));
+      this.fx.animate($.merge(this.config, {
+        modes: ['linear', 'node-property:width:height'],
+        onComplete: function() {
+          that.busy = false;
+        }
+      }));
+    } else {
+      var labelType = this.config.Label.type;
+      if(labelType != 'Native') {
+        var that = this;
+        this.graph.eachNode(function(n) { that.labels.hideLabel(n, false); });
+      }
+      this.busy = false;
+      this.compute();
+      this.config.levelsToShow > 0 && this.geom.setRightLevelToShow(this.graph.getNode(this.clickedNode 
+          && this.clickedNode.id || this.root));
+      this.plot();
+    }
+  },
+
+  /* 
+    Method: plot 
+    
+    Plots the TreeMap. This is a shortcut to *fx.plot*. 
+  
+   */
+  plot: function(){
+    this.fx.plot();
+  },
+
+  /* 
+  Method: leaf 
+  
+  Returns whether the node is a leaf.
+  
+   Parameters:
+   
+   n - (object) A <Graph.Node>.
+
+ */
+  leaf: function(n){
+    return n.getSubnodes([
+        1, 1
+    ], "ignore").length == 0;
+  },
+  
+  /* 
+  Method: enter 
+  
+  Sets the node as root.
+  
+   Parameters:
+   
+   n - (object) A <Graph.Node>.
+
+ */
+  enter: function(n){
+    if(this.busy) return;
+    this.busy = true;
+    
+    var that = this,
+        config = this.config,
+        graph = this.graph,
+        clickedNode = n,
+        previousClickedNode = this.clickedNode;
+
+    var callback = {
+      onComplete: function() {
+        //ensure that nodes are shown for that level
+        if(config.levelsToShow > 0) {
+          that.geom.setRightLevelToShow(n);
+        }
+        //compute positions of newly inserted nodes
+        if(config.levelsToShow > 0 || config.request) that.compute();
+        if(config.animate) {
+          //fade nodes
+          graph.nodeList.setData('alpha', 0, 'end');
+          n.eachSubgraph(function(n) {
+            n.setData('alpha', 1, 'end');
+          }, "ignore");
+          that.fx.animate({
+            duration: 500,
+            modes:['node-property:alpha'],
+            onComplete: function() {
+              //compute end positions
+              that.clickedNode = clickedNode;
+              that.compute('end');
+              //animate positions
+              //TODO(nico) commenting this line didn't seem to throw errors...
+              that.clickedNode = previousClickedNode;
+              that.fx.animate({
+                modes:['linear', 'node-property:width:height'],
+                duration: 1000,
+                onComplete: function() { 
+                  that.busy = false;
+                  //TODO(nico) check comment above
+                  that.clickedNode = clickedNode;
+                }
+              });
+            }
+          });
+        } else {
+          that.busy = false;
+          that.clickedNode = n;
+          that.refresh();
+        }
+      }
+    };
+    if(config.request) {
+      this.requestNodes(clickedNode, callback);
+    } else {
+      callback.onComplete();
+    }
+  },
+
+  /* 
+  Method: out 
+  
+  Sets the parent node of the current selected node as root.
+
+ */
+  out: function(){
+    if(this.busy) return;
+    this.busy = true;
+    this.events.hoveredNode = false;
+    var that = this,
+        config = this.config,
+        graph = this.graph,
+        parents = graph.getNode(this.clickedNode 
+            && this.clickedNode.id || this.root).getParents(),
+        parent = parents[0],
+        clickedNode = parent,
+        previousClickedNode = this.clickedNode;
+    
+    //if no parents return
+    if(!parent) {
+      this.busy = false;
+      return;
+    }
+    //final plot callback
+    callback = {
+      onComplete: function() {
+        that.clickedNode = parent;
+        if(config.request) {
+          that.requestNodes(parent, {
+            onComplete: function() {
+              that.compute();
+              that.plot();
+              that.busy = false;
+            }
+          });
+        } else {
+          that.compute();
+          that.plot();
+          that.busy = false;
+        }
+      }
+    };
+    //prune tree
+    if (config.levelsToShow > 0)
+      this.geom.setRightLevelToShow(parent);
+    //animate node positions
+    if(config.animate) {
+      this.clickedNode = clickedNode;
+      this.compute('end');
+      //animate the visible subtree only
+      this.clickedNode = previousClickedNode;
+      this.fx.animate({
+        modes:['linear', 'node-property:width:height'],
+        duration: 1000,
+        onComplete: function() {
+          //animate the parent subtree
+          that.clickedNode = clickedNode;
+          //change nodes alpha
+          graph.eachNode(function(n) {
+            n.setDataset(['current', 'end'], {
+              'alpha': [0, 1]
+            });
+          }, "ignore");
+          previousClickedNode.eachSubgraph(function(node) {
+            node.setData('alpha', 1);
+          }, "ignore");
+          that.fx.animate({
+            duration: 500,
+            modes:['node-property:alpha'],
+            onComplete: function() {
+              callback.onComplete();
+            }
+          });
+        }
+      });
+    } else {
+      callback.onComplete();
+    }
+  },
+
+  requestNodes: function(node, onComplete){
+    var handler = $.merge(this.controller, onComplete), 
+        lev = this.config.levelsToShow;
+    if (handler.request) {
+      var leaves = [], d = node._depth;
+      node.eachLevel(0, lev, function(n){
+        var nodeLevel = lev - (n._depth - d);
+        if (n.drawn && !n.anySubnode() && nodeLevel > 0) {
+          leaves.push(n);
+          n._level = nodeLevel;
+        }
+      });
+      this.group.requestNodes(leaves, handler);
+    } else {
+      handler.onComplete();
+    }
+  },
+  
+  reposition: function() {
+    this.compute('end');
+  }
+};
+
+/*
+  Class: TM.Op
+  
+  Custom extension of <Graph.Op>.
+  
+  Extends:
+  
+  All <Graph.Op> methods
+  
+  See also:
+  
+  <Graph.Op>
+  
+  */
+TM.Op = new Class({
+  Implements: Graph.Op,
+
+  initialize: function(viz){
+    this.viz = viz;
+  }
+});
+
+//extend level methods of Graph.Geom
+TM.Geom = new Class({
+  Implements: Graph.Geom,
+  
+  getRightLevelToShow: function() {
+    return this.viz.config.levelsToShow;
+  },
+  
+  setRightLevelToShow: function(node) {
+    var level = this.getRightLevelToShow(), 
+        fx = this.viz.labels;
+    node.eachLevel(0, level+1, function(n) {
+      var d = n._depth - node._depth;
+      if(d > level) {
+        n.drawn = false; 
+        n.exist = false;
+        n.ignore = true;
+        fx.hideLabel(n, false);
+      } else {
+        n.drawn = true;
+        n.exist = true;
+        delete n.ignore;
+      }
+    });
+    node.drawn = true;
+    delete node.ignore;
+  }
+});
+
+/*
+
+Performs operations on group of nodes.
+
+*/
+TM.Group = new Class( {
+
+  initialize: function(viz){
+    this.viz = viz;
+    this.canvas = viz.canvas;
+    this.config = viz.config;
+  },
+
+  /*
+  
+    Calls the request method on the controller to request a subtree for each node. 
+  */
+  requestNodes: function(nodes, controller){
+    var counter = 0, len = nodes.length, nodeSelected = {};
+    var complete = function(){
+      controller.onComplete();
+    };
+    var viz = this.viz;
+    if (len == 0)
+      complete();
+    for ( var i = 0; i < len; i++) {
+      nodeSelected[nodes[i].id] = nodes[i];
+      controller.request(nodes[i].id, nodes[i]._level, {
+        onComplete: function(nodeId, data){
+          if (data && data.children) {
+            data.id = nodeId;
+            viz.op.sum(data, {
+              type: 'nothing'
+            });
+          }
+          if (++counter == len) {
+            viz.graph.computeLevels(viz.root, 0);
+            complete();
+          }
+        }
+      });
+    }
+  }
+});
+
+/*
+  Class: TM.Plot
+  
+  Custom extension of <Graph.Plot>.
+  
+  Extends:
+  
+  All <Graph.Plot> methods
+  
+  See also:
+  
+  <Graph.Plot>
+  
+  */
+TM.Plot = new Class({
+
+  Implements: Graph.Plot,
+
+  initialize: function(viz){
+    this.viz = viz;
+    this.config = viz.config;
+    this.node = this.config.Node;
+    this.edge = this.config.Edge;
+    this.animation = new Animation;
+    this.nodeTypes = new TM.Plot.NodeTypes;
+    this.edgeTypes = new TM.Plot.EdgeTypes;
+    this.labels = viz.labels;
+  },
+
+  plot: function(opt, animating){
+    var viz = this.viz, 
+        graph = viz.graph;
+    viz.canvas.clear();
+    this.plotTree(graph.getNode(viz.clickedNode && viz.clickedNode.id || viz.root), $.merge(viz.config, opt || {}, {
+      'withLabels': true,
+      'hideLabels': false,
+      'plotSubtree': function(n, ch){
+        return n.anySubnode("exist");
+      }
+    }), animating);
+  }
+});
+
+/*
+  Class: TM.Label
+  
+  Custom extension of <Graph.Label>. 
+  Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
+
+  Extends:
+
+  All <Graph.Label> methods and subclasses.
+
+  See also:
+
+  <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
+  
+*/
+TM.Label = {};
+
+/*
+ TM.Label.Native
+
+ Custom extension of <Graph.Label.Native>.
+
+ Extends:
+
+ All <Graph.Label.Native> methods
+
+ See also:
+
+ <Graph.Label.Native>
+*/
+TM.Label.Native = new Class({
+  Implements: Graph.Label.Native,
+
+  initialize: function(viz) {
+    this.config = viz.config;
+    this.leaf = viz.leaf;
+  },
+  
+  renderLabel: function(canvas, node, controller){
+    if(!this.leaf(node) && !this.config.titleHeight) return;
+    var pos = node.pos.getc(true), 
+        ctx = canvas.getCtx(),
+        width = node.getData('width'),
+        height = node.getData('height'),
+        x = pos.x + width/2,
+        y = pos.y;
+        
+    ctx.fillText(node.name, x, y, width);
+  }
+});
+
+/*
+ TM.Label.SVG
+
+  Custom extension of <Graph.Label.SVG>.
+
+  Extends:
+
+  All <Graph.Label.SVG> methods
+
+  See also:
+
+  <Graph.Label.SVG>
+*/
+TM.Label.SVG = new Class( {
+  Implements: Graph.Label.SVG,
+
+  initialize: function(viz){
+    this.viz = viz;
+    this.leaf = viz.leaf;
+    this.config = viz.config;
+  },
+
+  /* 
+  placeLabel
+
+  Overrides abstract method placeLabel in <Graph.Plot>.
+
+  Parameters:
+
+  tag - A DOM label element.
+  node - A <Graph.Node>.
+  controller - A configuration/controller object passed to the visualization.
+  
+  */
+  placeLabel: function(tag, node, controller){
+    var pos = node.pos.getc(true), 
+        canvas = this.viz.canvas,
+        ox = canvas.translateOffsetX,
+        oy = canvas.translateOffsetY,
+        sx = canvas.scaleOffsetX,
+        sy = canvas.scaleOffsetY,
+        radius = canvas.getSize();
+    var labelPos = {
+      x: Math.round(pos.x * sx + ox + radius.width / 2),
+      y: Math.round(pos.y * sy + oy + radius.height / 2)
+    };
+    tag.setAttribute('x', labelPos.x);
+    tag.setAttribute('y', labelPos.y);
+
+    if(!this.leaf(node) && !this.config.titleHeight) {
+      tag.style.display = 'none';
+    }
+    controller.onPlaceLabel(tag, node);
+  }
+});
+
+/*
+ TM.Label.HTML
+
+ Custom extension of <Graph.Label.HTML>.
+
+ Extends:
+
+ All <Graph.Label.HTML> methods.
+
+ See also:
+
+ <Graph.Label.HTML>
+
+*/
+TM.Label.HTML = new Class( {
+  Implements: Graph.Label.HTML,
+
+  initialize: function(viz){
+    this.viz = viz;
+    this.leaf = viz.leaf;
+    this.config = viz.config;
+  },
+
+  /* 
+    placeLabel
+  
+    Overrides abstract method placeLabel in <Graph.Plot>.
+  
+    Parameters:
+  
+    tag - A DOM label element.
+    node - A <Graph.Node>.
+    controller - A configuration/controller object passed to the visualization.
+  
+  */
+  placeLabel: function(tag, node, controller){
+    var pos = node.pos.getc(true), 
+        canvas = this.viz.canvas,
+        ox = canvas.translateOffsetX,
+        oy = canvas.translateOffsetY,
+        sx = canvas.scaleOffsetX,
+        sy = canvas.scaleOffsetY,
+        radius = canvas.getSize();
+    var labelPos = {
+      x: Math.round(pos.x * sx + ox + radius.width / 2),
+      y: Math.round(pos.y * sy + oy + radius.height / 2)
+    };
+
+    var style = tag.style;
+    style.left = labelPos.x + 'px';
+    style.top = labelPos.y + 'px';
+    style.width = node.getData('width') * sx + 'px';
+    style.height = node.getData('height') * sy + 'px';
+    style.zIndex = node._depth * 100;
+    style.display = '';
+
+    if(!this.leaf(node) && !this.config.titleHeight) {
+      tag.style.display = 'none';
+    }
+    controller.onPlaceLabel(tag, node);
+  }
+});
+
+/*
+  Class: TM.Plot.NodeTypes
+
+  This class contains a list of <Graph.Node> built-in types. 
+  Node types implemented are 'none', 'rectangle'.
+
+  You can add your custom node types, customizing your visualization to the extreme.
+
+  Example:
+
+  (start code js)
+    TM.Plot.NodeTypes.implement({
+      'mySpecialType': {
+        'render': function(node, canvas) {
+          //print your custom node to canvas
+        },
+        //optional
+        'contains': function(node, pos) {
+          //return true if pos is inside the node or false otherwise
+        }
+      }
+    });
+  (end code)
+
+*/
+TM.Plot.NodeTypes = new Class( {
+  'none': {
+    'render': $.empty
+  },
+
+  'rectangle': {
+    'render': function(node, canvas, animating){
+      var leaf = this.viz.leaf(node),
+          config = this.config,
+          offst = config.offset,
+          titleHeight = config.titleHeight,
+          pos = node.pos.getc(true),
+          width = node.getData('width'),
+          height = node.getData('height'),
+          border = node.getData('border'),
+          ctx = canvas.getCtx(),
+          posx = pos.x + offst / 2, 
+          posy = pos.y + offst / 2;
+      if(width <= offst || height <= offst) return;
+      if (leaf) {
+        if(config.cushion) {
+          var lg = ctx.createRadialGradient(posx + (width-offst)/2, posy + (height-offst)/2, 1, 
+              posx + (width-offst)/2, posy + (height-offst)/2, width < height? height : width);
+          var color = node.getData('color');
+          var colorGrad = $.rgbToHex($.map($.hexToRgb(color), 
+              function(r) { return r * 0.2 >> 0; }));
+          lg.addColorStop(0, color);
+          lg.addColorStop(1, colorGrad);
+          ctx.fillStyle = lg;
+        }
+        ctx.fillRect(posx, posy, width - offst, height - offst);
+        if(border) {
+          ctx.save();
+          ctx.strokeStyle = border;
+          ctx.strokeRect(posx, posy, width - offst, height - offst);
+          ctx.restore();
+        }
+      } else if(titleHeight > 0){
+        ctx.fillRect(pos.x + offst / 2, pos.y + offst / 2, width - offst,
+            titleHeight - offst);
+        if(border) {
+          ctx.save();
+          ctx.strokeStyle = border;
+          ctx.strokeRect(pos.x + offst / 2, pos.y + offst / 2, width - offst,
+              height - offst);
+          ctx.restore();
+        }
+      }
+    },
+    'contains': function(node, pos) {
+      if(this.viz.clickedNode && !node.isDescendantOf(this.viz.clickedNode.id) || node.ignore) return false;
+      var npos = node.pos.getc(true),
+          width = node.getData('width'), 
+          leaf = this.viz.leaf(node),
+          height = leaf? node.getData('height') : this.config.titleHeight;
+      return this.nodeHelper.rectangle.contains({x: npos.x + width/2, y: npos.y + height/2}, pos, width, height);
+    }
+  }
+});
+
+TM.Plot.EdgeTypes = new Class( {
+  'none': $.empty
+});
+
+/*
+  Class: TM.SliceAndDice
+  
+  A slice and dice TreeMap visualization.
+  
+  Implements:
+  
+  All <TM.Base> methods and properties.
+*/
+TM.SliceAndDice = new Class( {
+  Implements: [
+      Loader, Extras, TM.Base, Layouts.TM.SliceAndDice
+  ]
+});
+
+/*
+  Class: TM.Squarified
+  
+  A squarified TreeMap visualization.
+
+  Implements:
+  
+  All <TM.Base> methods and properties.
+*/
+TM.Squarified = new Class( {
+  Implements: [
+      Loader, Extras, TM.Base, Layouts.TM.Squarified
+  ]
+});
+
+/*
+  Class: TM.Strip
+  
+  A strip TreeMap visualization.
+
+  Implements:
+  
+  All <TM.Base> methods and properties.
+*/
+TM.Strip = new Class( {
+  Implements: [
+      Loader, Extras, TM.Base, Layouts.TM.Strip
+  ]
+});
+
+
+/*
+ * File: RGraph.js
+ *
+ */
+
+/*
+   Class: RGraph
+   
+   A radial graph visualization with advanced animations.
+   
+   Inspired by:
+ 
+   Animated Exploration of Dynamic Graphs with Radial Layout (Ka-Ping Yee, Danyel Fisher, Rachna Dhamija, Marti Hearst) <http://bailando.sims.berkeley.edu/papers/infovis01.htm>
+   
+   Note:
+   
+   This visualization was built and engineered from scratch, taking only the paper as inspiration, and only shares some features with the visualization described in the paper.
+   
+  Implements:
+  
+  All <Loader> methods
+  
+   Constructor Options:
+   
+   Inherits options from
+   
+   - <Options.Canvas>
+   - <Options.Controller>
+   - <Options.Node>
+   - <Options.Edge>
+   - <Options.Label>
+   - <Options.Events>
+   - <Options.Tips>
+   - <Options.NodeStyles>
+   - <Options.Navigation>
+   
+   Additionally, there are other parameters and some default values changed
+   
+   interpolation - (string) Default's *linear*. Describes the way nodes are interpolated. Possible values are 'linear' and 'polar'.
+   levelDistance - (number) Default's *100*. The distance between levels of the tree. 
+     
+   Instance Properties:
+
+   canvas - Access a <Canvas> instance.
+   graph - Access a <Graph> instance.
+   op - Access a <RGraph.Op> instance.
+   fx - Access a <RGraph.Plot> instance.
+   labels - Access a <RGraph.Label> interface implementation.   
+*/
+
+$jit.RGraph = new Class( {
+
+  Implements: [
+      Loader, Extras, Layouts.Radial
+  ],
+
+  initialize: function(controller){
+    var $RGraph = $jit.RGraph;
+
+    var config = {
+      interpolation: 'linear',
+      levelDistance: 100
+    };
+
+    this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",
+        "Fx", "Controller", "Tips", "NodeStyles", "Events", "Navigation", "Label"), config, controller);
+
+    var canvasConfig = this.config;
+    if(canvasConfig.useCanvas) {
+      this.canvas = canvasConfig.useCanvas;
+      this.config.labelContainer = this.canvas.id + '-label';
+    } else {
+      if(canvasConfig.background) {
+        canvasConfig.background = $.merge({
+          type: 'Circles'
+        }, canvasConfig.background);
+      }
+      this.canvas = new Canvas(this, canvasConfig);
+      this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
+    }
+
+    this.graphOptions = {
+      'klass': Polar,
+      'Node': {
+        'selected': false,
+        'exist': true,
+        'drawn': true
+      }
+    };
+    this.graph = new Graph(this.graphOptions, this.config.Node,
+        this.config.Edge);
+    this.labels = new $RGraph.Label[canvasConfig.Label.type](this);
+    this.fx = new $RGraph.Plot(this, $RGraph);
+    this.op = new $RGraph.Op(this);
+    this.json = null;
+    this.root = null;
+    this.busy = false;
+    this.parent = false;
+    // initialize extras
+    this.initializeExtras();
+  },
+
+  /* 
+  
+    createLevelDistanceFunc 
+  
+    Returns the levelDistance function used for calculating a node distance 
+    to its origin. This function returns a function that is computed 
+    per level and not per node, such that all nodes with the same depth will have the 
+    same distance to the origin. The resulting function gets the 
+    parent node as parameter and returns a float.
+
+   */
+  createLevelDistanceFunc: function(){
+    var ld = this.config.levelDistance;
+    return function(elem){
+      return (elem._depth + 1) * ld;
+    };
+  },
+
+  /* 
+     Method: refresh 
+     
+     Computes positions and plots the tree.
+
+   */
+  refresh: function(){
+    this.compute();
+    this.plot();
+  },
+
+  reposition: function(){
+    this.compute('end');
+  },
+
+  /*
+   Method: plot
+  
+   Plots the RGraph. This is a shortcut to *fx.plot*.
+  */
+  plot: function(){
+    this.fx.plot();
+  },
+  /*
+   getNodeAndParentAngle
+  
+   Returns the _parent_ of the given node, also calculating its angle span.
+  */
+  getNodeAndParentAngle: function(id){
+    var theta = false;
+    var n = this.graph.getNode(id);
+    var ps = n.getParents();
+    var p = (ps.length > 0)? ps[0] : false;
+    if (p) {
+      var posParent = p.pos.getc(), posChild = n.pos.getc();
+      var newPos = posParent.add(posChild.scale(-1));
+      theta = Math.atan2(newPos.y, newPos.x);
+      if (theta < 0)
+        theta += 2 * Math.PI;
+    }
+    return {
+      parent: p,
+      theta: theta
+    };
+  },
+  /*
+   tagChildren
+  
+   Enumerates the children in order to maintain child ordering (second constraint of the paper).
+  */
+  tagChildren: function(par, id){
+    if (par.angleSpan) {
+      var adjs = [];
+      par.eachAdjacency(function(elem){
+        adjs.push(elem.nodeTo);
+      }, "ignore");
+      var len = adjs.length;
+      for ( var i = 0; i < len && id != adjs[i].id; i++)
+        ;
+      for ( var j = (i + 1) % len, k = 0; id != adjs[j].id; j = (j + 1) % len) {
+        adjs[j].dist = k++;
+      }
+    }
+  },
+  /* 
+  Method: onClick 
+  
+  Animates the <RGraph> to center the node specified by *id*.
+
+   Parameters:
+
+   id - A <Graph.Node> id.
+   opt - (optional|object) An object containing some extra properties described below
+   hideLabels - (boolean) Default's *true*. Hide labels when performing the animation.
+
+   Example:
+
+   (start code js)
+     rgraph.onClick('someid');
+     //or also...
+     rgraph.onClick('someid', {
+      hideLabels: false
+     });
+    (end code)
+    
+  */
+  onClick: function(id, opt){
+    if (this.root != id && !this.busy) {
+      this.busy = true;
+      this.root = id;
+      var that = this;
+      this.controller.onBeforeCompute(this.graph.getNode(id));
+      var obj = this.getNodeAndParentAngle(id);
+
+      // second constraint
+      this.tagChildren(obj.parent, id);
+      this.parent = obj.parent;
+      this.compute('end');
+
+      // first constraint
+      var thetaDiff = obj.theta - obj.parent.endPos.theta;
+      this.graph.eachNode(function(elem){
+        elem.endPos.set(elem.endPos.getp().add($P(thetaDiff, 0)));
+      });
+
+      var mode = this.config.interpolation;
+      opt = $.merge( {
+        onComplete: $.empty
+      }, opt || {});
+
+      this.fx.animate($.merge( {
+        hideLabels: true,
+        modes: [
+          mode
+        ]
+      }, opt, {
+        onComplete: function(){
+          that.busy = false;
+          opt.onComplete();
+        }
+      }));
+    }
+  }
+});
+
+$jit.RGraph.$extend = true;
+
+(function(RGraph){
+
+  /*
+     Class: RGraph.Op
+     
+     Custom extension of <Graph.Op>.
+
+     Extends:
+
+     All <Graph.Op> methods
+     
+     See also:
+     
+     <Graph.Op>
+
+  */
+  RGraph.Op = new Class( {
+
+    Implements: Graph.Op
+
+  });
+
+  /*
+     Class: RGraph.Plot
+    
+    Custom extension of <Graph.Plot>.
+  
+    Extends:
+  
+    All <Graph.Plot> methods
+    
+    See also:
+    
+    <Graph.Plot>
+  
+  */
+  RGraph.Plot = new Class( {
+
+    Implements: Graph.Plot
+
+  });
+
+  /*
+    Object: RGraph.Label
+
+    Custom extension of <Graph.Label>. 
+    Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
+  
+    Extends:
+  
+    All <Graph.Label> methods and subclasses.
+  
+    See also:
+  
+    <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
+  
+   */
+  RGraph.Label = {};
+
+  /*
+     RGraph.Label.Native
+
+     Custom extension of <Graph.Label.Native>.
+
+     Extends:
+
+     All <Graph.Label.Native> methods
+
+     See also:
+
+     <Graph.Label.Native>
+
+  */
+  RGraph.Label.Native = new Class( {
+    Implements: Graph.Label.Native
+  });
+
+  /*
+     RGraph.Label.SVG
+    
+    Custom extension of <Graph.Label.SVG>.
+  
+    Extends:
+  
+    All <Graph.Label.SVG> methods
+  
+    See also:
+  
+    <Graph.Label.SVG>
+  
+  */
+  RGraph.Label.SVG = new Class( {
+    Implements: Graph.Label.SVG,
+
+    initialize: function(viz){
+      this.viz = viz;
+    },
+
+    /* 
+       placeLabel
+
+       Overrides abstract method placeLabel in <Graph.Plot>.
+
+       Parameters:
+
+       tag - A DOM label element.
+       node - A <Graph.Node>.
+       controller - A configuration/controller object passed to the visualization.
+      
+     */
+    placeLabel: function(tag, node, controller){
+      var pos = node.pos.getc(true), 
+          canvas = this.viz.canvas,
+          ox = canvas.translateOffsetX,
+          oy = canvas.translateOffsetY,
+          sx = canvas.scaleOffsetX,
+          sy = canvas.scaleOffsetY,
+          radius = canvas.getSize();
+      var labelPos = {
+        x: Math.round(pos.x * sx + ox + radius.width / 2),
+        y: Math.round(pos.y * sy + oy + radius.height / 2)
+      };
+      tag.setAttribute('x', labelPos.x);
+      tag.setAttribute('y', labelPos.y);
+
+      controller.onPlaceLabel(tag, node);
+    }
+  });
+
+  /*
+     RGraph.Label.HTML
+
+     Custom extension of <Graph.Label.HTML>.
+
+     Extends:
+
+     All <Graph.Label.HTML> methods.
+
+     See also:
+
+     <Graph.Label.HTML>
+
+  */
+  RGraph.Label.HTML = new Class( {
+    Implements: Graph.Label.HTML,
+
+    initialize: function(viz){
+      this.viz = viz;
+    },
+    /* 
+       placeLabel
+
+       Overrides abstract method placeLabel in <Graph.Plot>.
+
+       Parameters:
+
+       tag - A DOM label element.
+       node - A <Graph.Node>.
+       controller - A configuration/controller object passed to the visualization.
+      
+     */
+    placeLabel: function(tag, node, controller){
+      var pos = node.pos.getc(true), 
+          canvas = this.viz.canvas,
+          ox = canvas.translateOffsetX,
+          oy = canvas.translateOffsetY,
+          sx = canvas.scaleOffsetX,
+          sy = canvas.scaleOffsetY,
+          radius = canvas.getSize();
+      var labelPos = {
+        x: Math.round(pos.x * sx + ox + radius.width / 2),
+        y: Math.round(pos.y * sy + oy + radius.height / 2)
+      };
+
+      var style = tag.style;
+      style.left = labelPos.x + 'px';
+      style.top = labelPos.y + 'px';
+      style.display = this.fitsInCanvas(labelPos, canvas)? '' : 'none';
+
+      controller.onPlaceLabel(tag, node);
+    }
+  });
+
+  /*
+    Class: RGraph.Plot.NodeTypes
+
+    This class contains a list of <Graph.Node> built-in types. 
+    Node types implemented are 'none', 'circle', 'triangle', 'rectangle', 'star', 'ellipse' and 'square'.
+
+    You can add your custom node types, customizing your visualization to the extreme.
+
+    Example:
+
+    (start code js)
+      RGraph.Plot.NodeTypes.implement({
+        'mySpecialType': {
+          'render': function(node, canvas) {
+            //print your custom node to canvas
+          },
+          //optional
+          'contains': function(node, pos) {
+            //return true if pos is inside the node or false otherwise
+          }
+        }
+      });
+    (end code)
+
+  */
+  RGraph.Plot.NodeTypes = new Class({
+    'none': {
+      'render': $.empty,
+      'contains': $.lambda(false)
+    },
+    'circle': {
+      'render': function(node, canvas){
+        var pos = node.pos.getc(true), 
+            dim = node.getData('dim');
+        this.nodeHelper.circle.render('fill', pos, dim, canvas);
+      },
+      'contains': function(node, pos){
+        var npos = node.pos.getc(true), 
+            dim = node.getData('dim');
+        return this.nodeHelper.circle.contains(npos, pos, dim);
+      }
+    },
+    'ellipse': {
+      'render': function(node, canvas){
+        var pos = node.pos.getc(true), 
+            width = node.getData('width'), 
+            height = node.getData('height');
+        this.nodeHelper.ellipse.render('fill', pos, width, height, canvas);
+        },
+      'contains': function(node, pos){
+        var npos = node.pos.getc(true), 
+            width = node.getData('width'), 
+            height = node.getData('height');
+        return this.nodeHelper.ellipse.contains(npos, pos, width, height);
+      }
+    },
+    'square': {
+      'render': function(node, canvas){
+        var pos = node.pos.getc(true), 
+            dim = node.getData('dim');
+        this.nodeHelper.square.render('fill', pos, dim, canvas);
+      },
+      'contains': function(node, pos){
+        var npos = node.pos.getc(true), 
+            dim = node.getData('dim');
+        return this.nodeHelper.square.contains(npos, pos, dim);
+      }
+    },
+    'rectangle': {
+      'render': function(node, canvas){
+        var pos = node.pos.getc(true), 
+            width = node.getData('width'), 
+            height = node.getData('height');
+        this.nodeHelper.rectangle.render('fill', pos, width, height, canvas);
+      },
+      'contains': function(node, pos){
+        var npos = node.pos.getc(true), 
+            width = node.getData('width'), 
+            height = node.getData('height');
+        return this.nodeHelper.rectangle.contains(npos, pos, width, height);
+      }
+    },
+    'triangle': {
+      'render': function(node, canvas){
+        var pos = node.pos.getc(true), 
+            dim = node.getData('dim');
+        this.nodeHelper.triangle.render('fill', pos, dim, canvas);
+      },
+      'contains': function(node, pos) {
+        var npos = node.pos.getc(true), 
+            dim = node.getData('dim');
+        return this.nodeHelper.triangle.contains(npos, pos, dim);
+      }
+    },
+    'star': {
+      'render': function(node, canvas){
+        var pos = node.pos.getc(true),
+            dim = node.getData('dim');
+        this.nodeHelper.star.render('fill', pos, dim, canvas);
+      },
+      'contains': function(node, pos) {
+        var npos = node.pos.getc(true),
+            dim = node.getData('dim');
+        return this.nodeHelper.star.contains(npos, pos, dim);
+      }
+    }
+  });
+
+  /*
+    Class: RGraph.Plot.EdgeTypes
+
+    This class contains a list of <Graph.Adjacence> built-in types. 
+    Edge types implemented are 'none', 'line' and 'arrow'.
+  
+    You can add your custom edge types, customizing your visualization to the extreme.
+  
+    Example:
+  
+    (start code js)
+      RGraph.Plot.EdgeTypes.implement({
+        'mySpecialType': {
+          'render': function(adj, canvas) {
+            //print your custom edge to canvas
+          },
+          //optional
+          'contains': function(adj, pos) {
+            //return true if pos is inside the arc or false otherwise
+          }
+        }
+      });
+    (end code)
+  
+  */
+  RGraph.Plot.EdgeTypes = new Class({
+    'none': $.empty,
+    'line': {
+      'render': function(adj, canvas) {
+        var from = adj.nodeFrom.pos.getc(true),
+            to = adj.nodeTo.pos.getc(true);
+        this.edgeHelper.line.render(from, to, canvas);
+      },
+      'contains': function(adj, pos) {
+        var from = adj.nodeFrom.pos.getc(true),
+            to = adj.nodeTo.pos.getc(true);
+        return this.edgeHelper.line.contains(from, to, pos, this.edge.epsilon);
+      }
+    },
+    'arrow': {
+      'render': function(adj, canvas) {
+        var from = adj.nodeFrom.pos.getc(true),
+            to = adj.nodeTo.pos.getc(true),
+            dim = adj.getData('dim'),
+            direction = adj.data.$direction,
+            inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);
+        this.edgeHelper.arrow.render(from, to, dim, inv, canvas);
+      },
+      'contains': function(adj, pos) {
+        var from = adj.nodeFrom.pos.getc(true),
+            to = adj.nodeTo.pos.getc(true);
+        return this.edgeHelper.arrow.contains(from, to, pos, this.edge.epsilon);
+      }
+    }
+  });
+
+})($jit.RGraph);
+
 
 /*
  * File: Hypertree.js
  * 
- * Implements the <Hypertree> class and other derived classes.
- *
- * Description:
- *
- * A Hyperbolic Tree (HT) is a focus+context information visualization technique used to display large amount of inter-related data. This technique was originally developed at Xerox PARC.
- *
- * The HT algorithm plots a tree in what's called the Poincare Disk model of Hyperbolic Geometry, a kind of non-Euclidean geometry. By doing this, the HT algorithm applies a moebius transformation to the tree in order to display it with a magnifying glass effect.
- *
- * Inspired by:
- *
- * A Focus+Context Technique Based on Hyperbolic Geometry for Visualizing Large Hierarchies (John Lamping, Ramana Rao, and Peter Pirolli).
- *
- * <http://www.cs.tau.ac.il/~asharf/shrek/Projects/HypBrowser/startree-chi95.pdf>
- *
- * Disclaimer:
- *
- * This visualization was built from scratch, taking only the paper as inspiration, and only shares some features with the Hypertree.
- *
-
 */
 
 /* 
      Complex 
      
-     A multi-purpose Complex Class with common methods. Exetended for the Hypertree. 
+     A multi-purpose Complex Class with common methods. Extended for the Hypertree. 
  
-*/ 
+*/
 /* 
    moebiusTransformation 
  
@@ -6542,60 +16438,14 @@
    Parameters: 
  
       c - An initialized Complex instance representing a translation Vector. 
-*/ 
- 
-Complex.prototype.moebiusTransformation = function(c) { 
-    var num = this.add(c); 
-    var den = c.$conjugate().$prod(this); den.x++; 
-    return num.$div(den); 
-}; 
- 
-/* 
-   Method: getClosestNodeToOrigin 
- 
-   Extends <Graph.Util>. Returns the closest node to the center of canvas.
+*/
 
-   Parameters:
-  
-    graph - A <Graph> instance.
-    prop - _optional_ a <Graph.Node> position property. Possible properties are 'startPos', 'pos' or 'endPos'. Default's 'pos'.
-
-   Returns:
-
-    Closest node to origin. Returns *null* otherwise.
-  
-*/ 
-Graph.Util.getClosestNodeToOrigin = function(graph, prop, flags) { 
-    return this.getClosestNodeToPos(graph, Polar.KER, prop, flags);
-}; 
-
-/* 
-   Method: getClosestNodeToPos
- 
-   Extends <Graph.Util>. Returns the closest node to the given position.
-
-   Parameters:
-  
-    graph - A <Graph> instance.
-    p[os - A <Complex> or <Polar> instance.
-    prop - _optional_ a <Graph.Node> position property. Possible properties are 'startPos', 'pos' or 'endPos'. Default's 'pos'.
-
-   Returns:
-
-    Closest node to the given position. Returns *null* otherwise.
-  
-*/ 
-Graph.Util.getClosestNodeToPos = function(graph, pos, prop, flags) { 
-  var node = null; prop = prop || 'pos'; pos = pos && pos.getc(true) || Complex.KER;
-  var distance = function(a, b) { 
-    var d1 = a.x - b.x, d2 = a.y - b.y;
-    return d1 * d1 + d2 * d2;
-  };
-  this.eachNode(graph, function(elem) { 
-    node = (node == null || distance(elem[prop].getc(true), pos) < distance(node[prop].getc(true), pos))? elem : node; 
-  }, flags); 
-  return node; 
-}; 
+Complex.prototype.moebiusTransformation = function(c) {
+  var num = this.add(c);
+  var den = c.$conjugate().$prod(this);
+  den.x++;
+  return num.$div(den);
+};
 
 /* 
     moebiusTransformation 
@@ -6611,2437 +16461,701 @@
         prop - A property array.
         theta - Rotation angle. 
         startPos - _optional_ start position. 
-*/   
-Graph.Util.moebiusTransformation = function(graph, pos, prop, startPos, flags) { 
-    this.eachNode(graph, function(elem) { 
-        for(var i=0; i<prop.length; i++) { 
-            var p = pos[i].scale(-1), property = startPos? startPos : prop[i];  
-            elem[prop[i]].set(elem[property].getc().moebiusTransformation(p)); 
-        } 
-    }, flags); 
-}; 
- 
+*/
+Graph.Util.moebiusTransformation = function(graph, pos, prop, startPos, flags) {
+  this.eachNode(graph, function(elem) {
+    for ( var i = 0; i < prop.length; i++) {
+      var p = pos[i].scale(-1), property = startPos ? startPos : prop[i];
+      elem.getPos(prop[i]).set(elem.getPos(property).getc().moebiusTransformation(p));
+    }
+  }, flags);
+};
+
 /* 
    Class: Hypertree 
-      
-     The main Hypertree class
+   
+   A Hyperbolic Tree/Graph visualization.
+   
+   Inspired by:
+ 
+   A Focus+Context Technique Based on Hyperbolic Geometry for Visualizing Large Hierarchies (John Lamping, Ramana Rao, and Peter Pirolli). 
+   <http://www.cs.tau.ac.il/~asharf/shrek/Projects/HypBrowser/startree-chi95.pdf>
+ 
+  Note:
+ 
+  This visualization was built and engineered from scratch, taking only the paper as inspiration, and only shares some features with the Hypertree described in the paper.
+
+  Implements:
+  
+  All <Loader> methods
+  
+  Constructor Options:
+  
+  Inherits options from
+  
+  - <Options.Canvas>
+  - <Options.Controller>
+  - <Options.Node>
+  - <Options.Edge>
+  - <Options.Label>
+  - <Options.Events>
+  - <Options.Tips>
+  - <Options.NodeStyles>
+  - <Options.Navigation>
+  
+  Additionally, there are other parameters and some default values changed
+  
+  radius - (string|number) Default's *auto*. The radius of the disc to plot the <Hypertree> in. 'auto' will take the smaller value from the width and height canvas dimensions. You can also set this to a custom value, for example *250*.
+  offset - (number) Default's *0*. A number in the range [0, 1) that will be substracted to each node position to make a more compact <Hypertree>. This will avoid placing nodes too far from each other when a there's a selected node.
+  fps - Described in <Options.Fx>. It's default value has been changed to *35*.
+  duration - Described in <Options.Fx>. It's default value has been changed to *1500*.
+  Edge.type - Described in <Options.Edge>. It's default value has been changed to *hyperline*. 
+  
+  Instance Properties:
+  
+  canvas - Access a <Canvas> instance.
+  graph - Access a <Graph> instance.
+  op - Access a <Hypertree.Op> instance.
+  fx - Access a <Hypertree.Plot> instance.
+  labels - Access a <Hypertree.Label> interface implementation.
+
+*/
+
+$jit.Hypertree = new Class( {
+
+  Implements: [ Loader, Extras, Layouts.Radial ],
+
+  initialize: function(controller) {
+    var $Hypertree = $jit.Hypertree;
+
+    var config = {
+      radius: "auto",
+      offset: 0,
+      Edge: {
+        type: 'hyperline'
+      },
+      duration: 1500,
+      fps: 35
+    };
+    this.controller = this.config = $.merge(Options("Canvas", "Node", "Edge",
+        "Fx", "Tips", "NodeStyles", "Events", "Navigation", "Controller", "Label"), config, controller);
+
+    var canvasConfig = this.config;
+    if(canvasConfig.useCanvas) {
+      this.canvas = canvasConfig.useCanvas;
+      this.config.labelContainer = this.canvas.id + '-label';
+    } else {
+      if(canvasConfig.background) {
+        canvasConfig.background = $.merge({
+          type: 'Circles'
+        }, canvasConfig.background);
+      }
+      this.canvas = new Canvas(this, canvasConfig);
+      this.config.labelContainer = (typeof canvasConfig.injectInto == 'string'? canvasConfig.injectInto : canvasConfig.injectInto.id) + '-label';
+    }
+
+    this.graphOptions = {
+      'klass': Polar,
+      'Node': {
+        'selected': false,
+        'exist': true,
+        'drawn': true
+      }
+    };
+    this.graph = new Graph(this.graphOptions, this.config.Node,
+        this.config.Edge);
+    this.labels = new $Hypertree.Label[canvasConfig.Label.type](this);
+    this.fx = new $Hypertree.Plot(this, $Hypertree);
+    this.op = new $Hypertree.Op(this);
+    this.json = null;
+    this.root = null;
+    this.busy = false;
+    // initialize extras
+    this.initializeExtras();
+  },
+
+  /* 
+  
+  createLevelDistanceFunc 
+
+  Returns the levelDistance function used for calculating a node distance 
+  to its origin. This function returns a function that is computed 
+  per level and not per node, such that all nodes with the same depth will have the 
+  same distance to the origin. The resulting function gets the 
+  parent node as parameter and returns a float.
+
+  */
+  createLevelDistanceFunc: function() {
+    // get max viz. length.
+    var r = this.getRadius();
+    // get max depth.
+    var depth = 0, max = Math.max, config = this.config;
+    this.graph.eachNode(function(node) {
+      depth = max(node._depth, depth);
+    }, "ignore");
+    depth++;
+    // node distance generator
+    var genDistFunc = function(a) {
+      return function(node) {
+        node.scale = r;
+        var d = node._depth + 1;
+        var acum = 0, pow = Math.pow;
+        while (d) {
+          acum += pow(a, d--);
+        }
+        return acum - config.offset;
+      };
+    };
+    // estimate better edge length.
+    for ( var i = 0.51; i <= 1; i += 0.01) {
+      var valSeries = (1 - Math.pow(i, depth)) / (1 - i);
+      if (valSeries >= 2) { return genDistFunc(i - 0.01); }
+    }
+    return genDistFunc(0.75);
+  },
+
+  /* 
+    Method: getRadius 
+    
+    Returns the current radius of the visualization. If *config.radius* is *auto* then it 
+    calculates the radius by taking the smaller size of the <Canvas> widget.
+    
+    See also:
+    
+    <Canvas.getSize>
+   
+  */
+  getRadius: function() {
+    var rad = this.config.radius;
+    if (rad !== "auto") { return rad; }
+    var s = this.canvas.getSize();
+    return Math.min(s.width, s.height) / 2;
+  },
+
+  /* 
+    Method: refresh 
+    
+    Computes positions and plots the tree.
+
+    Parameters:
+
+    reposition - (optional|boolean) Set this to *true* to force all positions (current, start, end) to match.
+
+   */
+  refresh: function(reposition) {
+    if (reposition) {
+      this.reposition();
+      this.graph.eachNode(function(node) {
+        node.startPos.rho = node.pos.rho = node.endPos.rho;
+        node.startPos.theta = node.pos.theta = node.endPos.theta;
+      });
+    } else {
+      this.compute();
+    }
+    this.plot();
+  },
+
+  /* 
+   reposition 
+   
+   Computes nodes' positions and restores the tree to its previous position.
+
+   For calculating nodes' positions the root must be placed on its origin. This method does this 
+     and then attemps to restore the hypertree to its previous position.
+    
+  */
+  reposition: function() {
+    this.compute('end');
+    var vector = this.graph.getNode(this.root).pos.getc().scale(-1);
+    Graph.Util.moebiusTransformation(this.graph, [ vector ], [ 'end' ],
+        'end', "ignore");
+    this.graph.eachNode(function(node) {
+      if (node.ignore) {
+        node.endPos.rho = node.pos.rho;
+        node.endPos.theta = node.pos.theta;
+      }
+    });
+  },
+
+  /* 
+   Method: plot 
+   
+   Plots the <Hypertree>. This is a shortcut to *fx.plot*. 
+
+  */
+  plot: function() {
+    this.fx.plot();
+  },
+
+  /* 
+   Method: onClick 
+   
+   Animates the <Hypertree> to center the node specified by *id*.
+
+   Parameters:
+
+   id - A <Graph.Node> id.
+   opt - (optional|object) An object containing some extra properties described below
+   hideLabels - (boolean) Default's *true*. Hide labels when performing the animation.
+
+   Example:
+
+   (start code js)
+     ht.onClick('someid');
+     //or also...
+     ht.onClick('someid', {
+      hideLabels: false
+     });
+    (end code)
+    
+  */
+  onClick: function(id, opt) {
+    var pos = this.graph.getNode(id).pos.getc(true);
+    this.move(pos, opt);
+  },
+
+  /* 
+   Method: move 
+
+   Translates the tree to the given position. 
+
+   Parameters:
+
+   pos - (object) A *x, y* coordinate object where x, y in [0, 1), to move the tree to.
+   opt - This object has been defined in <Hypertree.onClick>
+   
+   Example:
+   
+   (start code js)
+     ht.move({ x: 0, y: 0.7 }, {
+       hideLabels: false
+     });
+   (end code)
+
+  */
+  move: function(pos, opt) {
+    var versor = $C(pos.x, pos.y);
+    if (this.busy === false && versor.norm() < 1) {
+      this.busy = true;
+      var root = this.graph.getClosestNodeToPos(versor), that = this;
+      this.graph.computeLevels(root.id, 0);
+      this.controller.onBeforeCompute(root);
+      opt = $.merge( {
+        onComplete: $.empty
+      }, opt || {});
+      this.fx.animate($.merge( {
+        modes: [ 'moebius' ],
+        hideLabels: true
+      }, opt, {
+        onComplete: function() {
+          that.busy = false;
+          opt.onComplete();
+        }
+      }), versor);
+    }
+  }
+});
+
+$jit.Hypertree.$extend = true;
+
+(function(Hypertree) {
+
+  /* 
+     Class: Hypertree.Op 
+   
+     Custom extension of <Graph.Op>.
 
      Extends:
 
-     <Loader>, <AngularWidth>
-
-     Parameters:
-
-     canvas - A <Canvas> Class
-     config - A configuration/controller object.
-
-     Configuration:
-    
-     The configuration object can have the following properties (all properties are optional and have a default value)
-      
-     *General*
-     - _withLabels_ Whether the visualization should use/create labels or not. Default's *true*.
+     All <Graph.Op> methods
      
-     *Node*
+     See also:
      
-     Customize the visualization nodes' shape, color, and other style properties.
+     <Graph.Op>
 
-     - _Node_
+  */
+  Hypertree.Op = new Class( {
 
-     This object has as properties
+    Implements: Graph.Op
 
-     - _overridable_ Determine whether or not nodes properties can be overriden by a particular node. Default's false.
+  });
 
-     If given a JSON tree or graph, a node _data_ property contains properties which are the same as defined here but prefixed with 
-     a dollar sign (i.e $), the node properties will override the global node properties.
-
-     - _type_ Node type (shape). Possible options are "none", "square", "rectangle", "circle", "triangle", "star". Default's "circle".
-     - _color_ Node color. Default's '#ccb'.
-     - _lineWidth_ Line width. If nodes aren't drawn with strokes then this property won't be of any use. Default's 1.
-     - _height_ Node height. Used for plotting rectangular nodes. Default's 5.
-     - _width_ Node width. Used for plotting rectangular nodes. Default's 5.
-     - _dim_ An extra parameter used by other complex shapes such as square and circle to determine the shape's diameter. Default's 7.
-     - _transform_ Whether to apply the moebius transformation to the nodes or not. Default's true.
-
-     *Edge*
-
-     Customize the visualization edges' shape, color, and other style properties.
-
-     - _Edge_
-
-     This object has as properties
-
-     - _overridable_ Determine whether or not edges properties can be overriden by a particular edge object. Default's false.
-
-     If given a JSON _complex_ graph (defined in <Loader.loadJSON>), an adjacency _data_ property contains properties which are the same as defined here but prefixed with 
-     a dollar sign (i.e $), the adjacency properties will override the global edge properties.
-
-     - _type_ Edge type (shape). Possible options are "none", "line" and "hyperline". Default's "hyperline".
-     - _color_ Edge color. Default's '#ccb'.
-     - _lineWidth_ Line width. If edges aren't drawn with strokes then this property won't be of any use. Default's 1.
-
-     *Animations*
-
-     - _duration_ Duration of the animation in milliseconds. Default's 1500.
-     - _fps_ Frames per second. Default's 40.
-     - _transition_ One of the transitions defined in the <Animation> class. Default's Quart.easeInOut.
-     - _clearCanvas_ Whether to clear canvas on each animation frame or not. Default's true.
-
-    *Controller options*
-
-    You can also implement controller functions inside the configuration object. This functions are
-    
-    - _onBeforeCompute(node)_ This method is called right before performing all computation and animations to the JIT visualization.
-    - _onAfterCompute()_ This method is triggered right after all animations or computations for the JIT visualizations ended.
-    - _onCreateLabel(domElement, node)_ This method receives the label dom element as first parameter, and the corresponding <Graph.Node> as second parameter. This method will only be called on label creation. Note that a <Graph.Node> is a node from the input tree/graph you provided to the visualization. If you want to know more about what kind of JSON tree/graph format is used to feed the visualizations, you can take a look at <Loader.loadJSON>. This method proves useful when adding events to the labels used by the JIT.
-    - _onPlaceLabel(domElement, node)_ This method receives the label dom element as first parameter and the corresponding <Graph.Node> as second parameter. This method is called each time a label has been placed on the visualization, and thus it allows you to update the labels properties, such as size or position. Note that onPlaceLabel will be triggered after updating the labels positions. That means that, for example, the left and top css properties are already updated to match the nodes positions.
-    - _onBeforePlotNode(node)_ This method is triggered right before plotting a given node. The _node_ parameter is the <Graph.Node> to be plotted. 
-This method is useful for changing a node style right before plotting it.
-    - _onAfterPlotNode(node)_ This method is triggered right after plotting a given node. The _node_ parameter is the <Graph.Node> plotted.
-    - _onBeforePlotLine(adj)_ This method is triggered right before plotting an edge. The _adj_ parameter is a <Graph.Adjacence> object. 
-This method is useful for adding some styles to a particular edge before being plotted.
-    - _onAfterPlotLine(adj)_ This method is triggered right after plotting an edge. The _adj_ parameter is the <Graph.Adjacence> plotted.
-
-    Example:
-
-    Here goes a complete example. In most cases you won't be forced to implement all properties and methods. In fact, 
-    all configuration properties  have the default value assigned.
-
-    I won't be instanciating a <Canvas> class here. If you want to know more about instanciating a <Canvas> class 
-    please take a look at the <Canvas> class documentation.
-
-    (start code js)
-      var ht = new Hypertree(canvas, {
-        
-        Node: {
-          overridable: false,
-          type: 'circle',
-          color: '#ccb',
-          lineWidth: 1,
-          height: 5,
-          width: 5,
-          dim: 7,
-          transform: true
-        },
-        Edge: {
-          overridable: false,
-          type: 'hyperline',
-          color: '#ccb',
-          lineWidth: 1
-        },
-        duration: 1500,
-        fps: 40,
-        transition: Trans.Quart.easeInOut,
-        clearCanvas: true,
-        withLabels: true,
-        
-        onBeforeCompute: function(node) {
-          //do something onBeforeCompute
-        },
-        onAfterCompute:  function () {
-          //do something onAfterCompute
-        },
-        onCreateLabel:   function(domElement, node) {
-          //do something onCreateLabel
-        },
-        onPlaceLabel:    function(domElement, node) {
-          //do something onPlaceLabel
-        },
-        onBeforePlotNode:function(node) {
-          //do something onBeforePlotNode
-        },
-        onAfterPlotNode: function(node) {
-          //do something onAfterPlotNode
-        },
-        onBeforePlotLine:function(adj) {
-          //do something onBeforePlotLine
-        },
-        onAfterPlotLine: function(adj) {
-          //do something onAfterPlotLine
-        }
-      });
-    (end code)
-
-    Instance Properties:
-
-    - _graph_ Access a <Graph> instance.
-    - _op_ Access a <Hypertree.Op> instance.
-    - _fx_ Access a <Hypertree.Plot> instance.
-*/ 
- 
-this.Hypertree = new Class({ 
+  /* 
+     Class: Hypertree.Plot 
    
-  Implements: [Loader, AngularWidth], 
-   
-  initialize: function(canvas, controller) { 
- 
-    var config = { 
-                labelContainer: canvas.id + '-label', 
-             
-                withLabels: true,
-                
-                Node: { 
-                    overridable: false, 
-                    type: 'circle', 
-                    dim: 7, 
-                    color: '#ccb', 
-                    width: 5, 
-                    height: 5,    
-                    lineWidth: 1, 
-                    transform: true 
-                }, 
-                 
-                Edge: { 
-                    overridable: false, 
-                    type: 'hyperline', 
-                    color: '#ccb', 
-                    lineWidth: 1 
-                }, 
-                clearCanvas: true,
-            fps:40, 
-            duration: 1500, 
-                transition: Trans.Quart.easeInOut 
-    }; 
- 
-      var innerController = { 
-          onBeforeCompute: $empty, 
-          onAfterCompute:  $empty, 
-          onCreateLabel:   $empty, 
-          onPlaceLabel:    $empty, 
-          onComplete:      $empty, 
-          onBeforePlotLine:$empty, 
-          onAfterPlotLine: $empty, 
-          onBeforePlotNode:$empty, 
-          onAfterPlotNode: $empty 
-      }; 
-       
-      this.controller = this.config = $merge(config, innerController, controller); 
-        this.graphOptions = { 
-            'complex': false, 
-            'Node': { 
-                'selected': false, 
-                'exist': true, 
-                'drawn': true 
-            } 
-        }; 
-    this.graph = new Graph(this.graphOptions); 
-    this.fx = new Hypertree.Plot(this); 
-    this.op = new Hypertree.Op(this); 
-      this.json = null; 
-      this.canvas = canvas; 
- 
-      this.root = null; 
-      this.busy = false; 
-    }, 
- 
-    /* 
-     Method: refresh 
-     
-     Computes nodes' positions and replots the tree.
+    Custom extension of <Graph.Plot>.
+  
+    Extends:
+  
+    All <Graph.Plot> methods
+    
+    See also:
+    
+    <Graph.Plot>
+  
+  */
+  Hypertree.Plot = new Class( {
 
-     Parameters:
+    Implements: Graph.Plot
 
-     reposition - _optional_ Set this to *true* to force repositioning.
+  });
+
+  /*
+    Object: Hypertree.Label
+
+    Custom extension of <Graph.Label>. 
+    Contains custom <Graph.Label.SVG>, <Graph.Label.HTML> and <Graph.Label.Native> extensions.
+  
+    Extends:
+  
+    All <Graph.Label> methods and subclasses.
+  
+    See also:
+  
+    <Graph.Label>, <Graph.Label.Native>, <Graph.Label.HTML>, <Graph.Label.SVG>.
+
+   */
+  Hypertree.Label = {};
+
+  /*
+     Hypertree.Label.Native
+
+     Custom extension of <Graph.Label.Native>.
+
+     Extends:
+
+     All <Graph.Label.Native> methods
 
      See also:
 
-     <Hypertree.reposition>
-      
-    */ 
-    refresh: function(reposition) { 
-        if(reposition) { 
-            this.reposition(); 
-            Graph.Util.eachNode(this.graph, function(node) { 
-                node.startPos.rho = node.pos.rho = node.endPos.rho;
-                node.startPos.theta = node.pos.theta = node.endPos.theta; 
-            }); 
-        } else { 
-            this.compute(); 
-        } 
-        this.plot(); 
-    }, 
-     
+     <Graph.Label.Native>
+
+  */
+  Hypertree.Label.Native = new Class( {
+    Implements: Graph.Label.Native,
+
+    initialize: function(viz) {
+      this.viz = viz;
+    },
+
+    renderLabel: function(canvas, node, controller) {
+      var ctx = canvas.getCtx();
+      var coord = node.pos.getc(true);
+      var s = this.viz.getRadius();
+      ctx.fillText(node.name, coord.x * s, coord.y * s);
+    }
+  });
+
+  /*
+     Hypertree.Label.SVG
+
+    Custom extension of <Graph.Label.SVG>.
+  
+    Extends:
+  
+    All <Graph.Label.SVG> methods
+  
+    See also:
+  
+    <Graph.Label.SVG>
+  
+  */
+  Hypertree.Label.SVG = new Class( {
+    Implements: Graph.Label.SVG,
+
+    initialize: function(viz) {
+      this.viz = viz;
+    },
+
     /* 
-     Method: reposition 
-     
-     Computes nodes' positions and restores the tree to its previous position.
+       placeLabel
 
-     For calculating nodes' positions the root must be placed on its origin. This method does this 
-       and then attemps to restore the hypertree to its previous position.
-      
-    */ 
-    reposition: function() { 
-        this.compute('endPos'); 
-        var vector = this.graph.getNode(this.root).pos.getc().scale(-1); 
-        Graph.Util.moebiusTransformation(this.graph, [vector], ['endPos'], 'endPos', "ignore"); 
-        Graph.Util.eachNode(this.graph, function(node) { 
-            if (node.ignore) {
-                node.endPos.rho = node.pos.rho;
-                node.endPos.theta = node.pos.theta;
-            } 
-        }); 
-    }, 
- 
-    /* 
-     Method: plot 
-     
-     Plots the Hypertree 
-
-    */ 
-    plot: function() { 
-        this.fx.plot(); 
-    }, 
-     
-    /* 
-     Method: compute 
-     
-     Computes nodes' positions. 
-
-     Parameters:
-
-     property - _optional_ A <Graph.Node> position property to store the new positions. Possible values are 'pos', 'endPos' or 'startPos'.
-
-
-    */ 
-    compute: function(property) { 
-        var prop = property || ['pos', 'startPos']; 
-        var node = this.graph.getNode(this.root); 
-        node._depth = 0; 
-        Graph.Util.computeLevels(this.graph, this.root, 0, "ignore"); 
-        this.computeAngularWidths(); 
-        this.computePositions(prop); 
-    }, 
-     
-    /* 
-     computePositions 
-     
-     Performs the main algorithm for computing node positions.
-
-     Parameters:
-
-     property - A <Graph.Node> position property to store the new positions. Possible values are 'pos', 'endPos' or 'startPos'.
-
-  */ 
-    computePositions: function(property) { 
-        var propArray = $splat(property); 
-        var aGraph = this.graph, GUtil = Graph.Util; 
-        var root = this.graph.getNode(this.root), that = this, config = this.config; 
-        var size = this.canvas.getSize(); 
-        var scale = Math.min(size.width, size.height)/ 2; 
- 
- 
-        //Set default values for the root node 
-        for(var i=0; i<propArray.length; i++)  
-            root[propArray[i]] = $P(0, 0); 
-        root.angleSpan = { 
-            begin: 0, 
-            end: 2 * Math.PI 
-        }; 
-    root._rel = 1; 
-     
-        //Estimate better edge length. 
-        var edgeLength = (function() { 
-            var depth = 0; 
-            GUtil.eachNode(aGraph, function(node) { 
-                depth = (node._depth > depth)? node._depth : depth; 
-        node._scale = scale; 
-            }, "ignore"); 
-            for(var i=0.51; i<=1; i+=0.01) { 
-                var valSeries = (function(a, n) { 
-                    return (1 - Math.pow(a, n)) / (1 - a); 
-                })(i, depth + 1); 
-                if(valSeries >= 2) return i - 0.01; 
-            } 
-            return 0.5; 
-        })(); 
-         
-        GUtil.eachBFS(this.graph, this.root, function (elem) { 
-            var angleSpan = elem.angleSpan.end - elem.angleSpan.begin; 
-            var angleInit = elem.angleSpan.begin; 
-            var totalAngularWidths = (function (element){ 
-                var total = 0; 
-                GUtil.eachSubnode(element, function(sib) { 
-                    total += sib._treeAngularWidth; 
-                }, "ignore"); 
-                return total; 
-            })(elem); 
- 
-            for(var i=1, rho = 0, lenAcum = edgeLength, depth = elem._depth; i<=depth+1; i++) { 
-                rho += lenAcum; 
-                lenAcum *= edgeLength; 
-            } 
-             
-            GUtil.eachSubnode(elem, function(child) { 
-                if(!child._flag) { 
-                    child._rel = child._treeAngularWidth / totalAngularWidths; 
-                    var angleProportion = child._rel * angleSpan; 
-                    var theta = angleInit + angleProportion / 2; 
- 
-                    for(var i=0; i<propArray.length; i++) 
-                        child[propArray[i]] = $P(theta, rho); 
- 
-                    child.angleSpan = { 
-                        begin: angleInit, 
-                        end: angleInit + angleProportion 
-                    }; 
-                    angleInit += angleProportion; 
-                } 
-            }, "ignore"); 
- 
-        }, "ignore"); 
-    }, 
-     
-    /* 
-     Method: onClick 
-     
-     Performs all calculations and animations to center the node specified by _id_.
-
-     Parameters:
-
-     id - A <Graph.Node> id.
-     opt - _optional_ An object containing some extra properties like
-
-     - _hideLabels_ Hide labels when performing the animation. Default's *true*.
-
-     Example:
-
-     (start code js)
-       ht.onClick('someid');
-       //or also...
-       ht.onClick('someid', {
-        hideLabels: false
-       });
-      (end code)
-      
-    */ 
-    onClick: function(id, opt) { 
-        var pos = this.graph.getNode(id).pos.getc(true); 
-        this.move(pos, opt); 
-    }, 
-     
-    /* 
-     Method: move 
-
-     Translates the tree to the given position. 
-
-     Parameters:
-
-     pos - A <Complex> number determining the position to move the tree to.
-     opt - _optional_ An object containing some extra properties defined in <Hypertree.onClick>
-
-
-    */ 
-    move: function(pos, opt) { 
-        var versor = $C(pos.x, pos.y); 
-        if(this.busy === false && versor.norm() < 1) { 
-            var GUtil = Graph.Util;
-            this.busy = true; 
-            var root = GUtil.getClosestNodeToPos(this.graph, versor), that = this;
-            GUtil.computeLevels(this.graph, root.id, 0);
-            this.controller.onBeforeCompute(root); 
-            if (versor.norm() < 1) { 
-                opt = $merge({ onComplete: $empty }, opt || {}); 
-                this.fx.animate($merge({ 
-                    modes: ['moebius'], 
-                    hideLabels: true 
-                }, opt, { 
-                    onComplete: function(){ 
-                        that.busy = false; 
-                        opt.onComplete(); 
-                    } 
-                }), versor); 
-            } 
-        } 
-    }    
-}); 
- 
-/* 
-   Class: Hypertree.Op 
- 
-   Performs advanced operations on trees and graphs.
-
-   Extends:
-
-   All <Graph.Op> methods
-
-   Access:
-
-   This instance can be accessed with the _op_ parameter of the hypertree instance created.
-
-   Example:
-
-   (start code js)
-    var ht = new Hypertree(canvas, config);
-    ht.op.morph //or can also call any other <Graph.Op> method
-   (end code)
-    
-*/ 
-Hypertree.Op = new Class({ 
- 
-    Implements: Graph.Op, 
- 
-    initialize: function(viz) { 
-        this.viz = viz; 
-    } 
-}); 
- 
-/* 
-   Class: Hypertree.Plot 
- 
-   Performs plotting operations.
-
-   Extends:
-
-   All <Graph.Plot> methods
-
-   Access:
-
-   This instance can be accessed with the _fx_ parameter of the hypertree instance created.
-
-   Example:
-
-   (start code js)
-    var ht = new Hypertree(canvas, config);
-    ht.fx.placeLabel //or can also call any other <Hypertree.Plot> method
-   (end code)
-
-*/ 
-Hypertree.Plot = new Class({ 
- 
-    Implements: Graph.Plot, 
-   
-  initialize: function(viz) { 
-        this.viz = viz; 
-        this.config = viz.config; 
-    this.node = this.config.Node; 
-    this.edge = this.config.Edge; 
-        this.animation = new Animation; 
-        this.nodeTypes = new Hypertree.Plot.NodeTypes; 
-        this.edgeTypes = new Hypertree.Plot.EdgeTypes; 
-  }, 
-     
-    /* 
-       Method: hyperline 
-     
-       Plots a hyperline between two nodes. A hyperline is an arc of a circle which is orthogonal to the main circle. 
+       Overrides abstract method placeLabel in <Graph.Plot>.
 
        Parameters:
 
-       adj - A <Graph.Adjacence> object.
-       canvas - A <Canvas> instance.
-    */ 
-    hyperline: function(adj, canvas) { 
-        var node = adj.nodeFrom, child = adj.nodeTo, data = adj.data; 
-        var pos = node.pos.getc(), posChild = child.pos.getc(); 
-        var centerOfCircle = this.computeArcThroughTwoPoints(pos, posChild); 
-        var size = canvas.getSize(); 
-        var scale = Math.min(size.width, size.height)/2; 
-        if (centerOfCircle.a > 1000 || centerOfCircle.b > 1000 || centerOfCircle.ratio > 1000) { 
-            canvas.path('stroke', function(ctx) { 
-                ctx.moveTo(pos.x * scale, pos.y * scale); 
-                ctx.lineTo(posChild.x * scale, posChild.y * scale); 
-      }); 
-    } else { 
-          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.getCtx(); 
-          canvas.path('stroke', function(ctx) { 
-              ctx.arc(centerOfCircle.x*scale, centerOfCircle.y*scale, centerOfCircle.ratio*scale, angleBegin, angleEnd, sense); 
-          }); 
-    } 
-    }, 
-     
-    /* 
-       computeArcThroughTwoPoints 
-     
-       Calculates the arc parameters through two points.
-       
-       More information in <http://en.wikipedia.org/wiki/Poincar%C3%A9_disc_model#Analytic_geometry_constructions_in_the_hyperbolic_plane> 
-
-       Parameters:
-
-       p1 - A <Complex> instance.
-       p2 - A <Complex> instance.
-
-       Returns:
-
-       An object containing some arc properties.
-    */ 
-    computeArcThroughTwoPoints: function(p1, p2) { 
-        var aDen = (p1.x * p2.y - p1.y * p2.x), bDen = aDen; 
-        var sq1 = p1.squaredNorm(), sq2 = p2.squaredNorm(); 
-        //Fall back to a straight line 
-        if (aDen == 0) return { x:0, y:0, ratio: 1001 }; 
- 
-        var a = (p1.y * sq2 - p2.y * sq1 + p1.y - p2.y) / aDen; 
-        var b = (p2.x * sq1 - p1.x * sq2 + p2.x - p1.x) / bDen; 
-        var x = -a / 2; 
-        var y = -b / 2; 
-        var squaredRatio = (a * a + b * b) / 4 -1; 
-        //Fall back to a straight line         
-        if(squaredRatio < 0) return { x:0, y:0, ratio: 1001 }; 
-        var ratio = Math.sqrt(squaredRatio); 
-        var out= { 
-            x: x, 
-            y: y, 
-            ratio: ratio, 
-            a: a, 
-            b: b 
-        }; 
- 
-        return out; 
-  }, 
- 
-  /* 
-     sense 
-   
-     Sets angle direction to clockwise (true) or counterclockwise (false). 
+       tag - A DOM label element.
+       node - A <Graph.Node>.
+       controller - A configuration/controller object passed to the visualization.
       
-     Parameters: 
-   
-        angleBegin - Starting angle for drawing the arc. 
-        angleEnd - The HyperLine will be drawn from angleBegin to angleEnd. 
-   
-     Returns: 
-   
-        A Boolean instance describing the sense for drawing the HyperLine. 
-  */ 
-  sense: function(angleBegin, angleEnd) { 
-     return (angleBegin < angleEnd)? ((angleBegin + Math.PI > angleEnd)? false : true) :  
-         ((angleEnd + Math.PI > angleBegin)? true : false); 
-  }, 
-   
-
-    /* 
-      Method: placeLabel
-
-      Overrides abstract method placeLabel in <Graph.Plot>.
-
-      Parameters:
-
-      tag - A DOM label element.
-      node - A <Graph.Node>.
-      controller - A configuration/controller object passed to the visualization.
-     
      */
-    placeLabel: function(tag, node, controller) { 
-      var pos = node.pos.getc(true), canvas = this.viz.canvas; 
-      var radius= canvas.getSize(); 
-      var scale = node._scale; 
-      var labelPos= { 
-          x: Math.round(pos.x * scale + radius.width/2), 
-          y: Math.round(pos.y * scale + radius.height/2) 
-      }; 
-      var style = tag.style; 
-      style.left = labelPos.x + 'px'; 
-      style.top  = labelPos.y + 'px'; 
-      style.display = ''; 
-      controller.onPlaceLabel(tag, node); 
-  } 
-}); 
+    placeLabel: function(tag, node, controller) {
+      var pos = node.pos.getc(true), 
+          canvas = this.viz.canvas,
+          ox = canvas.translateOffsetX,
+          oy = canvas.translateOffsetY,
+          sx = canvas.scaleOffsetX,
+          sy = canvas.scaleOffsetY,
+          radius = canvas.getSize(),
+          r = this.viz.getRadius();
+      var labelPos = {
+        x: Math.round((pos.x * sx) * r + ox + radius.width / 2),
+        y: Math.round((pos.y * sy) * r + oy + radius.height / 2)
+      };
+      tag.setAttribute('x', labelPos.x);
+      tag.setAttribute('y', labelPos.y);
+      controller.onPlaceLabel(tag, node);
+    }
+  });
 
-/*
-  Class: Hypertree.Plot.NodeTypes
+  /*
+     Hypertree.Label.HTML
 
-  Here are implemented all kinds of node rendering functions. 
-  Rendering functions implemented are 'none', 'circle', 'triangle', 'rectangle', 'star' and 'square'.
+     Custom extension of <Graph.Label.HTML>.
 
-  You can add new Node types by implementing a new method in this class
+     Extends:
 
-  Example:
+     All <Graph.Label.HTML> methods.
 
-  (start code js)
-    Hypertree.Plot.NodeTypes.implement({
-      'newnodetypename': function(node, canvas) {
-        //Render my node here.
-      }
-    });
-  (end code)
+     See also:
 
-*/
-Hypertree.Plot.NodeTypes = new Class({ 
-    'none': function() {}, 
-     
-    'circle': function(node, canvas) { 
-        var nconfig = this.node, data = node.data; 
-        var nodeDim = nconfig.overridable && data && data.$dim || nconfig.dim; 
-    var p = node.pos.getc(), pos = p.scale(node._scale); 
-    var prod = nconfig.transform?  nodeDim * (1 - p.squaredNorm()) : nodeDim; 
-    if(prod >= nodeDim / 4) { 
-          canvas.path('fill', function(context) { 
-              context.arc(pos.x, pos.y, prod, 0, Math.PI * 2, true);           
-          }); 
-    } 
-    }, 
-     
-    'square': function(node, canvas) { 
-        var nconfig = this.node, data = node.data; 
-        var nodeDim = nconfig.overridable && data && data.$dim || nconfig.dim; 
-        var p = node.pos.getc(), pos = p.scale(node._scale); 
-        var prod = nconfig.transform?  nodeDim * (1 - p.squaredNorm()) : nodeDim; 
-        var nodeDim2 = 2 * prod; 
-        if (prod >= nodeDim / 4) { 
-      canvas.getCtx().fillRect(pos.x - prod, pos.y - prod, nodeDim2, nodeDim2); 
-    } 
-    }, 
- 
-    'rectangle': function(node, canvas) { 
-        var nconfig = this.node, data = node.data; 
-        var width = nconfig.overridable && data && data.$width || nconfig.width; 
-        var height = nconfig.overridable && data && data.$height || nconfig.height; 
-        var p = node.pos.getc(), pos = p.scale(node._scale); 
-    var prod = 1 - p.squaredNorm(); 
-        width = nconfig.transform?  width * prod : width; 
-    height = nconfig.transform?  height * prod : height; 
-    if(prod >= 0.25) { 
-            canvas.getCtx().fillRect(pos.x - width / 2, pos.y - height / 2, width, height);       
-    } 
- 
-    }, 
-     
-    'triangle': function(node, canvas) { 
-        var nconfig = this.node, data = node.data; 
-        var nodeDim = nconfig.overridable && data && data.$dim || nconfig.dim; 
-        var p = node.pos.getc(), pos = p.scale(node._scale); 
-        var prod = nconfig.transform?  nodeDim * (1 - p.squaredNorm()) : nodeDim; 
-        if (prod >= nodeDim / 4) { 
-      var c1x = pos.x,  
-      c1y = pos.y - prod,  
-      c2x = c1x - prod,  
-      c2y = pos.y + prod,  
-      c3x = c1x + prod,  
-      c3y = c2y; 
-      canvas.path('fill', function(ctx){ 
-        ctx.moveTo(c1x, c1y); 
-        ctx.lineTo(c2x, c2y); 
-        ctx.lineTo(c3x, c3y); 
-      }); 
-    } 
-    }, 
-     
-    'star': function(node, canvas) { 
-        var nconfig = this.node, data = node.data; 
-        var nodeDim = nconfig.overridable && data && data.$dim || nconfig.dim; 
-        var p = node.pos.getc(), pos = p.scale(node._scale); 
-        var prod = nconfig.transform?  nodeDim * (1 - p.squaredNorm()) : nodeDim; 
-        if (prod >= nodeDim / 4) { 
-      var ctx = canvas.getCtx(), pi5 = Math.PI / 5; 
-      ctx.save(); 
-      ctx.translate(pos.x, pos.y); 
-      ctx.beginPath(); 
-      ctx.moveTo(nodeDim, 0); 
-      for (var i = 0; i < 9; i++) { 
-        ctx.rotate(pi5); 
-        if (i % 2 == 0) { 
-          ctx.lineTo((prod / 0.525731) * 0.200811, 0); 
-        } 
-        else { 
-          ctx.lineTo(prod, 0); 
-        } 
-      } 
-      ctx.closePath(); 
-      ctx.fill(); 
-      ctx.restore(); 
-    } 
-    } 
-}); 
- 
- /*
-  Class: Hypertree.Plot.EdgeTypes
+     <Graph.Label.HTML>
 
-  Here are implemented all kinds of edge rendering functions. 
-  Rendering functions implemented are 'none', 'line' and 'hyperline'.
+  */
+  Hypertree.Label.HTML = new Class( {
+    Implements: Graph.Label.HTML,
 
-  You can add new Edge types by implementing a new method in this class
+    initialize: function(viz) {
+      this.viz = viz;
+    },
+    /* 
+       placeLabel
 
-  Example:
+       Overrides abstract method placeLabel in <Graph.Plot>.
 
-  (start code js)
-    Hypertree.Plot.EdgeTypes.implement({
-      'newedgetypename': function(adj, canvas) {
-        //Render my edge here.
-      }
-    });
-  (end code)
+       Parameters:
 
-*/
-Hypertree.Plot.EdgeTypes = new Class({ 
-    'none': function() {}, 
-     
-    'line': function(adj, canvas) { 
-    var s = adj.nodeFrom._scale; 
-        var pos = adj.nodeFrom.pos.getc(true); 
-        var posChild = adj.nodeTo.pos.getc(true); 
-        canvas.path('stroke', function(context) { 
-            context.moveTo(pos.x * s, pos.y * s); 
-            context.lineTo(posChild.x * s, posChild.y * s); 
-        }); 
-    }, 
- 
-    'hyperline': function(adj, canvas) { 
-    this.hyperline(adj, canvas); 
-  } 
-}); 
+       tag - A DOM label element.
+       node - A <Graph.Node>.
+       controller - A configuration/controller object passed to the visualization.
+      
+     */
+    placeLabel: function(tag, node, controller) {
+      var pos = node.pos.getc(true), 
+          canvas = this.viz.canvas,
+          ox = canvas.translateOffsetX,
+          oy = canvas.translateOffsetY,
+          sx = canvas.scaleOffsetX,
+          sy = canvas.scaleOffsetY,
+          radius = canvas.getSize(),
+          r = this.viz.getRadius();
+      var labelPos = {
+        x: Math.round((pos.x * sx) * r + ox + radius.width / 2),
+        y: Math.round((pos.y * sy) * r + oy + radius.height / 2)
+      };
+      var style = tag.style;
+      style.left = labelPos.x + 'px';
+      style.top = labelPos.y + 'px';
+      style.display = this.fitsInCanvas(labelPos, canvas) ? '' : 'none';
 
+      controller.onPlaceLabel(tag, node);
+    }
+  });
 
-/*
- * File: Treemap.js
- * 
- * Implements the <TM> class and other derived classes.
- *
- * Description:
- *
- * A Treemap is an information visualization technique, proven very useful when displaying large hierarchical structures on a constrained space. The idea behind a Treemap is to describe hierarchical relations as 'containment'. That means that if node B is child of node A, then B 'is contained' in A.
- *
- * Inspired by:
- *
- * Squarified Treemaps (Mark Bruls, Kees Huizing, and Jarke J. van Wijk) 
- *
- * <http://www.win.tue.nl/~vanwijk/stm.pdf>
- *
- * Tree visualization with tree-maps: 2-d space-filling approach (Ben Shneiderman)
- *
- * <http://hcil.cs.umd.edu/trs/91-03/91-03.html>
- *
- * Disclaimer:
- *
- * This visualization was built from scratch, taking only these papers as inspiration, and only shares some features with the Treemap papers mentioned above.
- *
- */
+  /*
+    Class: Hypertree.Plot.NodeTypes
 
-/*
-   Object: TM
+    This class contains a list of <Graph.Node> built-in types. 
+    Node types implemented are 'none', 'circle', 'triangle', 'rectangle', 'star', 'ellipse' and 'square'.
 
-  Abstract Treemap object.
+    You can add your custom node types, customizing your visualization to the extreme.
 
-   Implemented By:
-    
-    <TM.Squarified>, <TM.Strip> and <TM.SliceAndDice>.
-
-    Description:
-    
-    Implements layout and configuration options inherited by <TM.Squarified>, <TM.Strip> and <TM.SliceAndDice>.
-
-    All Treemap constructors take the same configuration object as parameter.
-
-    Two special _data_ keys are read from the JSON tree structure loaded by <Loader.loadJSON> to calculate 
-    node's color and dimensions. These properties are $area (for nodes dimensions) and $color. Both of these properties are floats.
-
-    This means that the tree structure defined in <Loader.loadJSON> should now look more like this
+    Example:
 
     (start code js)
-        var json = {  
-            "id": "aUniqueIdentifier",  
-            "name": "usually a nodes name",  
-            "data": {
-                "$area": 33, //some float value
-                "$color": 36, //-optional- some float value
-                "some key": "some value",
-                "some other key": "some other value"
-             },  
-            "children": [ 'other nodes or empty' ]  
-        };  
-    (end code)
-
-    If you want to know more about JSON tree structures and the _data_ property please read <Loader.loadJSON>.
-
-    Configuration:
-
-    *General*
-
-    - _rootId_ The id of the div container where the Treemap will be injected. Default's 'infovis'.
-    - _orientation_ For <TM.Strip> and <TM.SliceAndDice> only. The layout algorithm orientation. Possible values are 'h' or 'v'.
-    - _levelsToShow_ Max depth of the plotted tree. Useful when using the request method.
-    - _addLeftClickHandler_ Add a left click event handler to zoom in the Treemap view when clicking a node. Default's *false*. 
-    - _addRightClickHandler_ Add a right click event handler to zoom out the Treemap view. Default's *false*.
-    - _selectPathOnHover_ If setted to *true* all nodes contained in the path between the hovered node and the root node will have an *in-path* CSS class. Default's *false*.
-
-    *Nodes*
-    
-    There are two kinds of Treemap nodes.
-
-    (see treemapnode.png)
-
-    Inner nodes are nodes having children, like _Pearl Jam_.
-    
-    These nodes are represented by three div elements. A _content_ element, a _head_ element (where the title goes) and a _body_ element, where the children are laid out.
-    
-    (start code xml)
-    <div class="content">
-      <div class="head">Pearl Jam</div>
-      <div class="body">...other nodes here...</div>
-    </div>
-    (end code)
-
-      Leaves are optionally colored nodes laying at the "bottom" of the tree. For example, _Yield_, _Vs._ and _Riot Act_ are leaves.
-
-    These nodes are represented by two div elements. A _content_ element and a wrapped _leaf_ element
-
-    (start code xml)
-    <div class="content">
-      <div class="leaf">Yield</div>
-    </div>
-    (end code)
-
-    There are some configuration properties regarding Treemap nodes
-
-    - _titleHeight_ The height of the title (_head_) div container. Default's 13.
-    - _offset_ The separation offset between the _content_ div element and its contained div(s). Default's 4.
-
-    *Color*
-
-    _Color_ is an object containing as properties
-
-    - _allow_ If *true*, the algorithm will check for the JSON node data _$color_ property to add some color to the Treemap leaves. 
-    This color is calculated by interpolating a node's $color value range with a real RGB color range. 
-    By specifying min|maxValues for the $color property and min|maxColorValues for the RGB counterparts, the visualization is able to 
-    interpolate color values and assign a proper color to the leaf node. Default's *false*.
-    - _minValue_ The minimum value expected for the $color value property. Used for interpolating. Default's -100.
-    - _maxValue_ The maximum value expected for the $color value property. Used for interpolating. Default's 100.
-    - _minColorValue_ A three-element RGB array defining the color to be assigned to the _$color_ having _minValue_ as value. Default's [255, 0, 50].
-    - _maxColorValue_ A three-element RGB array defining the color to be assigned to the _$color_ having _maxValue_ as value. Default's [0, 255, 50].
-
-    *Tips*
-
-    _Tips_ is an object containing as properties
-
-    - _allow_ If *true*, a tooltip will be shown when a node is hovered. The tooltip is a div DOM element having "tip" as CSS class. Default's *false*. 
-    - _offsetX_ An offset added to the current tooltip x-position (which is the same as the current mouse position). Default's 20.
-    - _offsetY_ An offset added to the current tooltip y-position (which is the same as the current mouse position). Default's 20.
-    - _onShow(tooltip, node, isLeaf, domElement)_ Implement this method to change the HTML content of the tooltip when hovering a node.
-    
-    Parameters:
-      tooltip - The tooltip div element.
-      node - The corresponding JSON tree node (See also <Loader.loadJSON>).
-      isLeaf - Whether is a leaf or inner node.
-      domElement - The current hovered DOM element.
-    
-    *Controller options*
-
-    You can also implement controller functions inside the configuration object. These functions are
-    
-    - _onBeforeCompute(node)_ This method is called right before performing all computation and animations to the JIT visualization.
-    - _onAfterCompute()_ This method is triggered right after all animations or computations for the JIT visualizations ended.
-    - _onCreateElement(content, node, isLeaf, elem1, elem2)_ This method is called on each newly created node. 
-    
-    Parameters:
-      content - The div wrapper element with _content_ className.
-      node - The corresponding JSON tree node (See also <Loader.loadJSON>).
-      isLeaf - Whether is a leaf or inner node. If the node's an inner tree node, elem1 and elem2 will become the _head_ and _body_ div elements respectively. 
-      If the node's a _leaf_, then elem1 will become the div leaf element.
-    
-    - _onDestroyElement(content, node, isLeaf, elem1, elem2)_ This method is called before collecting each node. Takes the same parameters as onCreateElement.
-    - _request(nodeId, level, onComplete)_ This method is used for buffering information into the visualization. When clicking on an empty node,
-    the visualization will make a request for this node's subtrees, specifying a given level for this subtree (defined by _levelsToShow_). Once the request is completed, the _onComplete_ 
-object should be called with the given result.
-
-    See also <TM.Squarified>, <TM.SliceAndDice> and <TM.Strip>.
-
-
-*/
-this.TM = {
-
-  layout: {
-    orientation: "h",
-    vertical: function() { 
-      return this.orientation == "v"; 
-    },
-    horizontal: function() { 
-      return this.orientation == "h"; 
-    },
-    change: function() { 
-      this.orientation = this.vertical()? "h" : "v"; 
-    }
-  },
-  
-  innerController: {
-      onBeforeCompute:  $empty,
-      onAfterCompute:   $empty,
-      onComplete:       $empty,
-      onCreateElement:  $empty,
-      onDestroyElement: $empty,
-      request:          false
-    },
-
-    config: {
-      orientation: "h",
-      titleHeight: 13,
-      rootId: 'infovis',
-      offset:4,
-      levelsToShow: 3,
-      addLeftClickHandler: false,
-      addRightClickHandler: false,
-      selectPathOnHover: false,
-            
-      Color: {
-        allow: false,
-        minValue: -100,
-        maxValue: 100,
-        minColorValue: [255, 0, 50],
-        maxColorValue: [0, 255, 50]
-      },
-            
-      Tips: {
-        allow: false,
-        offsetX: 20,
-        offsetY: 20,
-        onShow: $empty
-      }
-    },
-  
-
-  initialize: function(controller) {
-    this.tree = null;
-    this.shownTree = null;
-    this.controller = this.config = $merge(this.config, 
-                    this.innerController, 
-                    controller);
-    this.rootId = this.config.rootId;
-    this.layout.orientation = this.config.orientation;
-        //add tooltip
-        if(this.config.Tips.allow && document.body) {
-            var tip = document.getElementById('_tooltip') || document.createElement('div');
-            tip.id = '_tooltip';
-            tip.className = 'tip';
-            var style = tip.style;
-            style.position = 'absolute';
-            style.display = 'none';
-            style.zIndex = 13000;
-            document.body.appendChild(tip);
-            this.tip = tip;
+      Hypertree.Plot.NodeTypes.implement({
+        'mySpecialType': {
+          'render': function(node, canvas) {
+            //print your custom node to canvas
+          },
+          //optional
+          'contains': function(node, pos) {
+            //return true if pos is inside the node or false otherwise
+          }
         }
-        
-        //purge
-        var that = this;
-        var fn = function() {
-            that.empty();
-            if(window.CollectGarbage) window.CollectGarbage();
-            delete fn;
-        };
-        if(window.addEventListener) {
-            window.addEventListener('unload', fn, false);
-        } else {
-            window.attachEvent('onunload', fn);
-        }
-  },
-
-    /*
-       Method: each
-    
-        Traverses head and leaf nodes applying a given function
-
-      Parameters:
-      
-        f - A function that takes as parameters the same as the onCreateElement and onDestroyElement methods described in <TM>.
-    */
-    each: function(f) {
-        (function rec(elem) {
-          if(!elem) return;
-          var ch = elem.childNodes, len = ch.length;
-          if(len > 0) {
-              f.apply(this, [elem, len === 1, ch[0], ch[1]]);
-          }
-          if (len > 1) {
-            for(var chi = ch[1].childNodes, i=0; i<chi.length; i++) {
-                rec(chi[i]);
-            }
-          }  
-        })($get(this.rootId).firstChild);
-    },
-
-  /*
-     toStyle
-  
-    Transforms a JSON into a CSS style string.
-  */
-  toStyle: function(obj) {
-    var ans = "";
-    for(var s in obj) ans += s + ":" + obj[s] + ";";
-    return ans;
-  },
-
-  /*
-     leaf
-  
-    Returns a boolean value specifying if the node is a tree leaf or not.
-  
-     Parameters:
-  
-        tree - A tree node (which is also a JSON tree object of course). <http://blog.thejit.org>
-
-     Returns:
-  
-         A boolean value specifying if the node is a tree leaf or not.
- 
-  */
-  leaf: function(tree) {
-    return tree.children == 0;
-  },
-
-  /*
-     Method: createBox
-  
-    Constructs the proper DOM layout from a json node.
-    
-        If the node's an _inner node_, 
-        this method calls <TM.contentBox>, <TM.bodyBox> and <TM.leafBox> 
-        to create the following HTML structure
-        
-        (start code xml)
-        <div class="content">
-          <div class="head">[Node name]</div>
-          <div class="body">[Node's children]</div>
-        </div>
-        (end code)
-
-        If the node's a leaf node, it creates the following structure 
-        by calling <TM.contentBox>, <TM.leafBox>
-
-        (start code xml)
-        <div class="content">
-          <div class="leaf">[Node name]</div>
-        </div>
-        (end code)
-
-
-     Parameters:
-
-        json - A JSON subtree. See also <Loader.loadJSON>. 
-      	coord - A coordinates object specifying width, height, left and top style properties.
-        html - html to inject into the _body_ element if the node is an inner Tree node.
-
-      Returns:
-
-          The HTML structure described above.
-
-      See also:
-
-        <TM>, <TM.contentBox>, <TM.bodyBox>, <TM.headBox>, <TM.leafBox>.
-
-  */
-  createBox: function(json, coord, html) {
-    var box;
-    if(!this.leaf(json)) {
-      box = this.headBox(json, coord) + this.bodyBox(html, coord);
-    } else {
-      box = this.leafBox(json, coord);
-    }
-    return this.contentBox(json, coord, box);
-  },
-  
-  /*
-     Method: plot
-  
-    Renders the Treemap.
-
-      Parameters:
-
-        json - A JSON tree structure preprocessed by some Treemap layout algorithm.
-
-      Returns:
-
-        The HTML to inject to the main visualization container.
-
-      See also:
-
-        <TM.createBox>.
-
-
-  */
-  plot: function(json) {
-    var coord = json.coord, html = "";
-    
-    if(this.leaf(json)) 
-      return this.createBox(json, coord, null);
-    
-    for(var i=0, ch=json.children; i<ch.length; i++) {
-    	var chi = ch[i], chcoord = chi.coord;
-    	//skip tiny nodes
-    	if(chcoord.width * chcoord.height > 1) {
-    		html+= this.plot(chi);	
-    	}
-    } 
-    return this.createBox(json, coord, html);
-  },
-
-
-  /*
-     Method: headBox
-  
-    Creates the _head_ div dom element that usually contains the name of a parent JSON tree node.
-  
-     Parameters:
-  
-        json - A JSON subtree. See also <Loader.loadJSON>.
-        coord - width and height base coordinate object.
-
-     Returns:
-  
-         A new _head_ div dom element that has _head_ as class name.
-
-        See also:
-
-          <TM.createBox>.
- 
-  */
-  headBox: function(json, coord) {
-    var config = this.config, offst = config.offset;
-    var c = {
-      'height': config.titleHeight + "px",
-      'width': (coord.width - offst) + "px",
-      'left':  offst / 2 + "px"
-    };
-    return "<div class=\"head\" style=\"" + this.toStyle(c) + "\">"
-         + json.name + "</div>";
-  },
-
-  /*
-     Method: bodyBox
-  
-    Creates the _body_ div dom element that usually contains a subtree dom element layout.
-  
-     Parameters:
-  
-        html - html that should be contained in the body html.
-        coord - width and height base coordinate object.
-
-     Returns:
-  
-         A new _body_ div dom element that has _body_ as class name.
- 
-        See also:
-
-          <TM.createBox>.
- 
-  */
-  bodyBox: function(html, coord) {
-    var config = this.config,
-    th = config.titleHeight,
-    offst = config.offset;
-    var c = {
-      'width': (coord.width - offst) + "px",
-      'height':(coord.height - offst - th) + "px",
-      'top':   (th + offst / 2) +  "px",
-      'left':  (offst / 2) + "px"
-    };
-    return "<div class=\"body\" style=\""
-      + this.toStyle(c) +"\">" + html + "</div>";
-  },
-
-
-
-  /*
-     Method: contentBox
-  
-    Creates the _content_ div dom element that usually contains a _leaf_ div dom element or _head_ and _body_ div dom elements.
-  
-     Parameters:
-  
-        json - A JSON node. See also <Loader.loadJSON>. 
-        coord - An object containing width, height, left and top coordinates.
-        html - input html wrapped by this tag.
-        
-     Returns:
-  
-         A new _content_ div dom element that has _content_ as class name.
-
-       See also:
-
-          <TM.createBox>.
- 
-  */
-  contentBox: function(json, coord, html) {
-    var c = {};
-    for(var i in coord) c[i] = coord[i] + "px";
-    return "<div class=\"content\" style=\"" + this.toStyle(c) 
-       + "\" id=\"" + json.id + "\">" + html + "</div>";
-  },
-
-
-  /*
-     Method: leafBox
-  
-    Creates the _leaf_ div dom element that usually contains nothing else.
-  
-     Parameters:
-  
-        json - A JSON subtree. See also <Loader.loadJSON>. 
-        coord - base with and height coordinate object.
-        
-     Returns:
-  
-         A new _leaf_ div dom element having _leaf_ as class name.
- 
-       See also:
-
-          <TM.createBox>.
- 
-
-  */
-  leafBox: function(json, coord) {
-    var config = this.config;
-    var backgroundColor = config.Color.allow && this.setColor(json), 
-    offst = config.offset,
-    width = coord.width - offst,
-    height = coord.height - offst;
-    var c = {
-      'top':   (offst / 2)  + "px",
-      'height':height + "px",
-      'width': width + "px",
-      'left': (offst / 2) + "px"
-    };
-    if(backgroundColor) c['background-color'] = backgroundColor;
-    return "<div class=\"leaf\" style=\"" + this.toStyle(c) + "\">" 
-        + json.name + "</div>";
-  },
-
-
-  /*
-     Method: setColor
-  
-        Calculates an hexa color string based on the _$color_ data node property.  
-  
-          This method is called by <TM.leafBox> to assign an hexadecimal color to each leaf node.
-          
-          This color is calculated by making a linear interpolation between _$color_ max and min values and 
-          RGB max and min values so that
-
-          > hex = (maxColorValue - minColorValue) / (maxValue - minValue) * (x - minValue) + minColorValue
-
-          where _x_ range is [minValue, maxValue] and 
-
-          - _minValue_
-          - _maxValue_
-          - _minColorValue_
-          - _maxColorValue_
-
-        are defined in the <TM> configuration object.
-
-        This method is called by <TM.leafBox> iif _Color.allow_ is setted to _true_.
-
-        Sometimes linear interpolation for coloring is just not enough. In that case you can re-implement this 
-        method so that it fits your coloring needs.
-
-        Some people might find useful to implement their own coloring interpolation method and to assign the resulting hex string 
-        to the _$color_ property. In that case we could re-implement the <TM.setColor> method like this
-
-        (start code js)
-          //TM.Strip, TM.SliceAndDice also work
-          TM.Squarified.implement({
-            'setColor': function(json) {
-              return json.data.$color;
-            }
-          });
-        (end code)
-
-      So that it returns the previously assigned hex string.
-
-     Parameters:
-  
-        json - A JSON tree node.
-
-     Returns:
-  
-         A String that represents a color in hex value.
- 
-  */
-  setColor: function(json) {
-    var c = this.config.Color,
-    maxcv = c.maxColorValue,
-    mincv = c.minColorValue,
-    maxv = c.maxValue,
-    minv = c.minValue,
-    diff = maxv - minv,
-    x = (json.data.$color - 0);
-    //linear interpolation    
-    var comp = function(i, x) { 
-      return Math.round((((maxcv[i] - mincv[i]) / diff) * (x - minv) + mincv[i])); 
-    };
-    
-    return $rgbToHex([ comp(0, x), comp(1, x), comp(2, x) ]);
-  },
-
-  /*
-     Method: enter
-  
-    Sets the _elem_ parameter as root and performs the layout.
-  
-     Parameters:
-  
-        elem - A JSON Tree node. See also <Loader.loadJSON>. 
-  */
-  enter: function(elem) {
-    this.view(elem.parentNode.id);
-  },
-  
-    /*
-       Method: onLeftClick
-    
-        Sets the _elem_ parameter as root and performs the layout. 
-        This method is called when _addLeftClickHandler_ is *true* and a 
-        node is left-clicked. You can override this method to add some custom behavior 
-        when the node is left clicked though.
-        
-        An Example for overriding this method could be
-        (start code js)
-        //TM.Strip or TM.SliceAndDice also work
-        TM.Squarified.implement({
-            'onLeftClick': function(elem) {
-                //some custom code...
-            }
-        });
-        (end code)
-        
-    
-       Parameters:
-    
-          elem - A JSON Tree node. See also <Loader.loadJSON>.
-          
-       See also:
-       
-          <TM.enter>
-    */
-    onLeftClick: function(elem) {
-        this.enter(elem);
-    },
-
-  /*
-     Method: out
-  
-    Sets the _parent_ node of the currently shown subtree as root and performs the layout.
-  
-  */
-  out: function() {
-    var parent = TreeUtil.getParent(this.tree, this.shownTree.id);
-    if(parent) {
-      if(this.controller.request)
-        TreeUtil.prune(parent, this.config.levelsToShow);
-      this.view(parent.id);
-    }
-  },
-  
-    /*
-       Method: onRightClick
-    
-        Sets the _parent_ node of the currently shown subtree as root and performs the layout. 
-        This method is called when _addRightClickHandler_ is *true* and a 
-        node is right-clicked. You can override this method to add some custom behavior 
-        when the node is right-clicked though.
-
-        An Example for overriding this method could be
-        (start code js)
-        //TM.Strip or TM.SliceAndDice also work
-        TM.Squarified.implement({
-            'onRightClick': function() {
-                //some custom code...
-            }
-        });
-        (end code)
-
-       See also:
-       
-          <TM.out>
-
-    */
-    onRightClick: function() {
-        this.out();
-    },
-
-  /*
-     Method: view
-  
-    Sets the root of the treemap to the specified node id and performs the layout.
-  
-     Parameters:
-  
-      id - A node identifier
-  */
-  view: function(id) {
-    var config = this.config, that = this;
-    var post = {
-      onComplete: function() {
-        that.loadTree(id);
-        $get(config.rootId).focus();
-      }
-    };
-
-    if (this.controller.request) {
-      var TUtil = TreeUtil;
-      TUtil.loadSubtrees(TUtil.getSubtree(this.tree, id),
-               $merge(this.controller, post));
-    } else {
-      post.onComplete();
-    }
-  },
-  
-  /*
-     Method: resetPath
-  
-       Sets an 'in-path' className for _leaf_ and _head_ elements which belong to the path between the given tree node 
-       and the visualization's root node.
-  
-     Parameters:
-  
-        tree - A JSON  tree node. See also <Loader.loadJSON>.
-  */
-  resetPath: function(tree) {
-    var root = this.rootId, previous = this.resetPath.previous;
-        this.resetPath.previous = tree || false;
-        function getParent(c) { 
-            var p = c.parentNode;
-            return p && (p.id != root) && p;
-         };
-         function toggleInPath(elem, remove) {
-            if(elem) {
-                var container = $get(elem.id);
-                if(container) {
-                    var parent = getParent(container);
-                    while(parent) {
-                        elem = parent.childNodes[0];
-                        if($hasClass(elem, 'in-path')) {
-                            if(remove == undefined || !!remove) $removeClass(elem, 'in-path');
-                        } else {
-                            if(!remove) $addClass(elem, 'in-path');
-                        }
-                        parent = getParent(parent);
-                    }
-                }
-            }
-         };
-         toggleInPath(previous, true);
-         toggleInPath(tree, false);                
-  },
-
-    
-    /*
-       Method: initializeElements
-    
-       Traverses the DOM tree applying the onCreateElement method.
-
-       The onCreateElement controller method should attach events and add some behavior to the DOM element
-       node created. *By default, the Treemap wont add any event to its elements.*
-    */
-    initializeElements: function() {
-      var cont = this.controller, that = this;
-      var ff = $lambda(false), tipsAllow = cont.Tips.allow;
-      this.each(function(content, isLeaf, elem1, elem2) {
-          var tree = TreeUtil.getSubtree(that.tree, content.id);
-          cont.onCreateElement(content, tree, isLeaf, elem1, elem2);
-
-          //eliminate context menu when right clicking
-          if(cont.addRightClickHandler) elem1.oncontextmenu = ff;
-
-          //add click handlers
-          if(cont.addLeftClickHandler || cont.addRightClickHandler) {
-            $addEvent(elem1, 'mouseup', function(e) {
-                var rightClick = (e.which == 3 || e.button == 2);
-                if (rightClick) {
-                    if(cont.addRightClickHandler) that.onRightClick();
-                }                     
-                else {
-                    if(cont.addLeftClickHandler) that.onLeftClick(elem1);
-                } 
-                    
-                //prevent default 
-                if (e.preventDefault) 
-                    e.preventDefault();
-                else 
-                    e.returnValue = false;
-            });
-          }
-          
-          //add path selection on hovering nodes
-          if(cont.selectPathOnHover || tipsAllow) {
-            $addEvent(elem1, 'mouseover', function(e){
-                if(cont.selectPathOnHover) {
-                    if (isLeaf) {
-                        $addClass(elem1, 'over-leaf');
-                    }
-                    else {
-                        $addClass(elem1, 'over-head');
-                        $addClass(content, 'over-content');
-                    }
-                    if (content.id) 
-                        that.resetPath(tree);
-                }
-                if(tipsAllow)
-                    cont.Tips.onShow(that.tip, tree, isLeaf, elem1);
-            });
-            
-            $addEvent(elem1, 'mouseout', function(e){
-                if(cont.selectPathOnHover) {
-                    if (isLeaf) {
-                        $removeClass(elem1, 'over-leaf');
-                    }
-                    else {
-                        $removeClass(elem1, 'over-head');
-                        $removeClass(content, 'over-content');
-                    }
-                    that.resetPath();
-                }
-                if(tipsAllow)
-                    that.tip.style.display = 'none';
-            });
-
-            if(tipsAllow) {
-                //Add mousemove event handler
-                $addEvent(elem1, 'mousemove', function(e, win){
-                    var tip = that.tip;
-                    //get mouse position
-                    win = win  || window;
-                    e = e || win.event;
-                    var doc = win.document;
-                    doc = doc.html || doc.body;
-                    var page = {
-                        x: e.pageX || e.clientX + doc.scrollLeft,
-                        y: e.pageY || e.clientY + doc.scrollTop
-                    };
-                    tip.style.display = '';
-                    //get window dimensions
-                    win = {
-                        'height': document.body.clientHeight,
-                        'width': document.body.clientWidth
-                    };
-                    //get tooltip dimensions
-                    var obj = {
-                      'width': tip.offsetWidth,
-                      'height': tip.offsetHeight  
-                    };
-                    //set tooltip position
-                    var style = tip.style, x = cont.Tips.offsetX, y = cont.Tips.offsetY;
-                    style.top = ((page.y + y + obj.height > win.height)?  
-                        (page.y - obj.height - y) : page.y + y) + 'px';
-                    style.left = ((page.x + obj.width + x > win.width)? 
-                        (page.x - obj.width - x) : page.x + x) + 'px';
-                });
-            }
-          }
       });
-    },
+    (end code)
 
-    /*
-       Method: destroyElements
-    
-       Traverses the tree applying the onDestroyElement method.
-
-       The onDestroyElement controller method should detach events and garbage collect the element.
-       *By default, the Treemap adds some garbage collect facilities for IE.*
-    */
-    destroyElements: function() {
-      if(this.controller.onDestroyElement != $empty) {
-          var cont = this.controller, that = this;
-          this.each(function(content, isLeaf, elem1, elem2) {
-              cont.onDestroyElement(content, TreeUtil.getSubtree(that.tree, content.id), isLeaf, elem1, elem2);
-          });
-      }  
-    },
-    
-    /*
-       Method: empty
-    
-        Empties the Treemap container (trying also to garbage collect things).
-    */
-    empty: function() {
-        this.destroyElements();
-        $clean($get(this.rootId));
-    },
-
-  /*
-     Method: loadTree
-  
-    Loads the subtree specified by _id_ and plots it on the layout container.
-  
-     Parameters:
-  
-        id - A subtree id.
   */
-  loadTree: function(id) {
-    this.empty();
-    this.loadJSON(TreeUtil.getSubtree(this.tree, id));
-  }
-  
-};
-
-/*
-   Class: TM.SliceAndDice
-
-  A JavaScript implementation of the Slice and Dice Treemap algorithm.
-
-  The <TM.SliceAndDice> constructor takes an _optional_ configuration object described in <TM>.
-
-    This visualization (as all other Treemap visualizations) is fed with JSON Tree structures.
-
-    The _$area_ node data key is required for calculating rectangles dimensions.
-
-    The _$color_ node data key is required if _Color_ _allow_ is *true* and is used for calculating 
-    leaves colors.
-
-    Extends:
-    <TM>
-
-    Parameters:
-
-    config - Configuration defined in <TM>.
-
-    Example:
-
-
-  Here's a way of instanciating the <TM.SliceAndDice> will all its _optional_ configuration features
-  
-  (start code js)
-
-  var tm = new TM.SliceAndDice({
-      orientation: "h",
-    titleHeight: 13,
-    rootId: 'infovis',
-    offset:4,
-    levelsToShow: 3,
-        addLeftClickHandler: false,
-        addRightClickHandler: false,
-        selectPathOnHover: false,
-            
-    Color: {
-      allow: false,
-      minValue: -100,
-      maxValue: 100,
-      minColorValue: [255, 0, 50],
-      maxColorValue: [0, 255, 50]
-          },
-          
-          Tips: {
-            allow: false,
-            offsetX; 20,
-            offsetY: 20,
-            onShow: function(tooltip, node, isLeaf, domElement) {}
-          },
-            
-          onBeforeCompute:  function(node) {
-            //Some stuff on before compute...
-          },
-          onAfterCompute:   function() {
-            //Some stuff on after compute...
-          },
-          onCreateElement:  function(content, node, isLeaf, head, body) {
-            //Some stuff onCreateElement
-          },
-          onDestroyElement: function(content, node, isLeaf, head, body) {
-            //Some stuff onDestroyElement
-          },
-        request:          false
-    });
-  tm.loadJSON(json);
-
-  (end code)
-
-*/
-TM.SliceAndDice = new Class({
-  Implements: TM,
-  /*
-     Method: loadJSON
-  
-    Loads the specified JSON tree and lays it on the main container.
-  
-     Parameters:
-  
-        json - A JSON Tree. See also <Loader.loadJSON>. 
-  */
-  loadJSON: function (json) {
-    this.controller.onBeforeCompute(json);
-    var container = $get(this.rootId),
-    config = this.config,
-    width = container.offsetWidth,
-    height = container.offsetHeight;
-    
-    var p = {
-      'coord': {
-        'top': 0,
-        'left': 0,
-        'width':  width,
-        'height': height + config.titleHeight + config.offset
+  Hypertree.Plot.NodeTypes = new Class({
+    'none': {
+      'render': $.empty,
+      'contains': $.lambda(false)
+    },
+    'circle': {
+      'render': function(node, canvas) {
+        var nconfig = this.node,
+            dim = node.getData('dim'),
+            p = node.pos.getc();
+        dim = nconfig.transform? dim * (1 - p.squaredNorm()) : dim;
+        p.$scale(node.scale);
+        if (dim > 0.2) {
+          this.nodeHelper.circle.render('fill', p, dim, canvas);
+        }
+      },
+      'contains': function(node, pos) {
+        var dim = node.getData('dim'),
+            npos = node.pos.getc().$scale(node.scale);
+        return this.nodeHelper.circle.contains(npos, pos, dim);
       }
-    };
-    
-    if(this.tree == null) this.tree = json;
-    this.shownTree = json;
-    this.compute(p, json, this.layout.orientation);
-    container.innerHTML = this.plot(json);
-        this.initializeElements();
-    this.controller.onAfterCompute(json);
-  },
-  
-  /*
-     Method: compute
-  
-    Called by loadJSON to calculate recursively all node positions and lay out the tree.
-  
-     Parameters:
-
-        par - The parent node of the json subtree.  
-        json - A JSON subtree. See also <Loader.loadJSON>.
-        orientation - The current orientation. This value is switched recursively.
-  */
-  compute: function(par, json, orientation) {
-    var config = this.config, 
-    coord = par.coord,
-    offst = config.offset,
-    width  = coord.width - offst,
-    height = coord.height - offst - config.titleHeight,
-    pdata = par.data,
-    fact = (pdata && ("$area" in pdata))? json.data.$area / pdata.$area : 1;
-    var otherSize, size, dim, pos, pos2;
-    
-    var horizontal = (orientation == "h");
-    if(horizontal) {
-      orientation = 'v';    
-      otherSize = height;
-      size = Math.round(width * fact);
-      dim = 'height';
-      pos = 'top';
-      pos2 = 'left';
-    } else {
-      orientation = 'h';    
-      otherSize = Math.round(height * fact);
-      size = width;
-      dim = 'width';
-      pos = 'left';
-      pos2 = 'top';
-    }
-    json.coord = {
-      'width':size,
-      'height':otherSize,
-      'top':0,
-      'left':0
-    };
-    var offsetSize = 0, tm = this;
-    $each(json.children, function(elem){
-      tm.compute(json, elem, orientation);
-      elem.coord[pos] = offsetSize;
-      elem.coord[pos2] = 0;
-      offsetSize += Math.floor(elem.coord[dim]);
-    });
-  }
-});
-
-
-/*
-   Class: TM.Area
-
-  Abstract Treemap class containing methods that are common to
-   aspect ratio related algorithms such as <TM.Squarified> and <TM.Strip>.
-
-    Implemented by:
-
-    <TM.Squarified>, <TM.Strip>
-*/
-TM.Area = new Class({
-
-  /*
-     Method: loadJSON
-  
-    Loads the specified JSON tree and lays it on the main container.
-  
-     Parameters:
-  
-        json - A JSON tree. See also <Loader.loadJSON>.
-  */
-  loadJSON: function (json) {
-    this.controller.onBeforeCompute(json);
-    var container = $get(this.rootId),
-    width = container.offsetWidth,
-    height = container.offsetHeight,
-    offst = this.config.offset,
-    offwdth = width - offst,
-    offhght = height - offst - this.config.titleHeight;
-
-    json.coord =  {
-      'height': height,
-      'width': width,
-      'top': 0,
-      'left': 0
-    };
-    var coord = $merge(json.coord, {
-      'width': offwdth,
-      'height': offhght
-    });
-
-    this.compute(json, coord);
-    container.innerHTML = this.plot(json);
-    if(this.tree == null) this.tree = json;
-    this.shownTree = json;
-    this.initializeElements();
-    this.controller.onAfterCompute(json);
-  },
-  
-  /*
-     Method: computeDim
-  
-    Computes dimensions and positions of a group of nodes
-    according to a custom layout row condition. 
-  
-     Parameters:
-
-        tail - An array of nodes.  
-          initElem - An array of nodes (containing the initial node to be laid).
-        w - A fixed dimension where nodes will be layed out.
-      	coord - A coordinates object specifying width, height, left and top style properties.
-      	comp - A custom comparison function
-  */
-  computeDim: function(tail, initElem, w, coord, comp) {
-    if(tail.length + initElem.length == 1) {
-      var l = (tail.length == 1)? tail : initElem;
-      this.layoutLast(l, w, coord);
-      return;
-    }
-    if(tail.length >= 2 && initElem.length == 0) {
-      initElem = [tail[0]];
-      tail = tail.slice(1);
-    }
-    if(tail.length == 0) {
-      if(initElem.length > 0) this.layoutRow(initElem, w, coord);
-      return;
-    }
-    var c = tail[0];
-    if(comp(initElem, w) >= comp([c].concat(initElem), w)) {
-      this.computeDim(tail.slice(1), initElem.concat([c]), w, coord, comp);
-    } else {
-      var newCoords = this.layoutRow(initElem, w, coord);
-      this.computeDim(tail, [], newCoords.dim, newCoords, comp);
-    }
-  },
-
-  
-  /*
-     Method: worstAspectRatio
-  
-    Calculates the worst aspect ratio of a group of rectangles. 
-        
-        See also:
-        
-        <http://en.wikipedia.org/wiki/Aspect_ratio>
-    
-     Parameters:
-
-      ch - An array of nodes.  
-        w - The fixed dimension where rectangles are being laid out.
-
-     Returns:
-  
-         The worst aspect ratio.
- 
-
-  */
-  worstAspectRatio: function(ch, w) {
-    if(!ch || ch.length == 0) return Number.MAX_VALUE;
-    var areaSum = 0, maxArea = 0, minArea = Number.MAX_VALUE;
-    for(var i=0; i<ch.length; i++) {
-      var area = ch[i]._area;
-      areaSum += area; 
-      minArea = (minArea < area)? minArea : area;
-      maxArea = (maxArea > area)? maxArea : area; 
-    }
-    var sqw = w * w, sqAreaSum = areaSum * areaSum;
-    return Math.max(sqw * maxArea / sqAreaSum,
-            sqAreaSum / (sqw * minArea));
-  },
-  
-  /*
-     Method: avgAspectRatio
-  
-    Calculates the average aspect ratio of a group of rectangles. 
-        
-        See also:
-        
-        <http://en.wikipedia.org/wiki/Aspect_ratio>
-    
-     Parameters:
-
-      ch - An array of nodes.  
-        w - The fixed dimension where rectangles are being laid out.
-
-     Returns:
-  
-         The average aspect ratio.
- 
-
-  */
-  avgAspectRatio: function(ch, w) {
-    if(!ch || ch.length == 0) return Number.MAX_VALUE;
-    var arSum = 0;
-    for(var i=0; i<ch.length; i++) {
-      var area = ch[i]._area;
-      var h = area / w;
-      arSum += (w > h)? w / h : h / w;
-    }
-    return arSum / ch.length;
-  },
-
-  /*
-     layoutLast
-  
-    Performs the layout of the last computed sibling.
-  
-     Parameters:
-
-        ch - An array of nodes.  
-        w - A fixed dimension where nodes will be layed out.
-      coord - A coordinates object specifying width, height, left and top style properties.
-  */
-  layoutLast: function(ch, w, coord) {
-    ch[0].coord = coord;
-  }
-  
-});
-
-
-
-
-/*
-   Class: TM.Squarified
-
-  A JavaScript implementation of the Squarified Treemap algorithm.
-
-  The <TM.Squarified> constructor takes an _optional_ configuration object described in <TM>.
-
-    This visualization (as all other Treemap visualizations) is fed with JSON Tree structures.
-
-    The _$area_ node data key is required for calculating rectangles dimensions.
-
-    The _$color_ node data key is required if _Color_ _allow_ is *true* and is used for calculating 
-    leaves colors.
-
-    Extends:
-    <TM> and <TM.Area>
-
-    Parameters:
-
-    config - Configuration defined in <TM>.
-
-    Example:
-
-
-  Here's a way of instanciating the <TM.Squarified> will all its _optional_ configuration features
-  
-  (start code js)
-
-  var tm = new TM.Squarified({
-    titleHeight: 13,
-    rootId: 'infovis',
-    offset:4,
-    levelsToShow: 3,
-    addLeftClickHandler: false,
-    addRightClickHandler: false,
-    selectPathOnHover: false,
-            
-    Color: {
-      allow: false,
-      minValue: -100,
-      maxValue: 100,
-      minColorValue: [255, 0, 50],
-      maxColorValue: [0, 255, 50]
     },
-          
-    Tips: {
-      allow: false,
-      offsetX: 20,
-      offsetY: 20,
-      onShow: function(tooltip, node, isLeaf, domElement) {}
+    'ellipse': {
+      'render': function(node, canvas) {
+        var pos = node.pos.getc().$scale(node.scale),
+            width = node.getData('width'),
+            height = node.getData('height');
+        this.nodeHelper.ellipse.render('fill', pos, width, height, canvas);
+      },
+      'contains': function(node, pos) {
+        var width = node.getData('width'),
+            height = node.getData('height'),
+            npos = node.pos.getc().$scale(node.scale);
+        return this.nodeHelper.circle.contains(npos, pos, width, height);
+      }
     },
-  
-      onBeforeCompute:  function(node) {
-        //Some stuff on before compute...
+    'square': {
+      'render': function(node, canvas) {
+        var nconfig = this.node,
+            dim = node.getData('dim'),
+            p = node.pos.getc();
+        dim = nconfig.transform? dim * (1 - p.squaredNorm()) : dim;
+        p.$scale(node.scale);
+        if (dim > 0.2) {
+          this.nodeHelper.square.render('fill', p, dim, canvas);
+        }
       },
-      onAfterCompute:   function() {
-        //Some stuff on after compute...
+      'contains': function(node, pos) {
+        var dim = node.getData('dim'),
+            npos = node.pos.getc().$scale(node.scale);
+        return this.nodeHelper.square.contains(npos, pos, dim);
+      }
+    },
+    'rectangle': {
+      'render': function(node, canvas) {
+        var nconfig = this.node,
+            width = node.getData('width'),
+            height = node.getData('height'),
+            pos = node.pos.getc();
+        width = nconfig.transform? width * (1 - pos.squaredNorm()) : width;
+        height = nconfig.transform? height * (1 - pos.squaredNorm()) : height;
+        pos.$scale(node.scale);
+        if (width > 0.2 && height > 0.2) {
+          this.nodeHelper.rectangle.render('fill', pos, width, height, canvas);
+        }
       },
-      onCreateElement:  function(content, node, isLeaf, head, body) {
-        //Some stuff onCreateElement
+      'contains': function(node, pos) {
+        var width = node.getData('width'),
+            height = node.getData('height'),
+            npos = node.pos.getc().$scale(node.scale);
+        return this.nodeHelper.rectangle.contains(npos, pos, width, height);
+      }
+    },
+    'triangle': {
+      'render': function(node, canvas) {
+        var nconfig = this.node,
+            dim = node.getData('dim'),
+            p = node.pos.getc();
+        dim = nconfig.transform? dim * (1 - p.squaredNorm()) : dim;
+        p.$scale(node.scale);
+        if (dim > 0.2) {
+          this.nodeHelper.triangle.render('fill', p, dim, canvas);
+        }
       },
-      onDestroyElement: function(content, node, isLeaf, head, body) {
-        //Some stuff onDestroyElement
+      'contains': function(node, pos) {
+        var dim = node.getData('dim'),
+            npos = node.pos.getc().$scale(node.scale);
+        return this.nodeHelper.triangle.contains(npos, pos, dim);
+      }
+    },
+    'star': {
+      'render': function(node, canvas) {
+        var nconfig = this.node,
+            dim = node.getData('dim'),
+            p = node.pos.getc();
+        dim = nconfig.transform? dim * (1 - p.squaredNorm()) : dim;
+        p.$scale(node.scale);
+        if (dim > 0.2) {
+          this.nodeHelper.star.render('fill', p, dim, canvas);
+        }
       },
-      request:          false
-    });
-  
-  	tm.loadJSON(json);
-
-  (end code)
-
-*/
-  
-TM.Squarified = new Class({
-  Implements: [TM, TM.Area],
-
-  /*
-     Method: compute
-  
-    Called by loadJSON to calculate recursively all node positions and lay out the tree.
-  
-     Parameters:
-
-        json - A JSON tree. See also <Loader.loadJSON>.
-      	coord - A coordinates object specifying width, height, left and top style properties.
-  */
-  compute: function(json, coord) {
-    if (!(coord.width >= coord.height && this.layout.horizontal())) 
-      this.layout.change();
-    var ch = json.children, config = this.config;
-    if(ch.length > 0) {
-      this.processChildrenLayout(json, ch, coord);
-      for(var i=0; i<ch.length; i++) {
-        var chcoord = ch[i].coord,
-        offst = config.offset,
-        height = chcoord.height - (config.titleHeight + offst),
-        width = chcoord.width - offst;
-        coord = {
-          'width':width,
-          'height':height,
-          'top':0,
-          'left':0
-        };
-        this.compute(ch[i], coord);
+      'contains': function(node, pos) {
+        var dim = node.getData('dim'),
+            npos = node.pos.getc().$scale(node.scale);
+        return this.nodeHelper.star.contains(npos, pos, dim);
       }
     }
-  },
+  });
 
   /*
-     Method: processChildrenLayout
+   Class: Hypertree.Plot.EdgeTypes
+
+    This class contains a list of <Graph.Adjacence> built-in types. 
+    Edge types implemented are 'none', 'line', 'arrow' and 'hyperline'.
   
-    Computes children real areas and other useful parameters for performing the Squarified algorithm.
+    You can add your custom edge types, customizing your visualization to the extreme.
   
-     Parameters:
-
-        par - The parent node of the json subtree.  
-        ch - An Array of nodes
-      coord - A coordinates object specifying width, height, left and top style properties.
-  */
-  processChildrenLayout: function(par, ch, coord) {
-    //compute children real areas
-    var parentArea = coord.width * coord.height;
-    var i, totalChArea=0, chArea = [];
-    for(i=0; i < ch.length; i++) {
-    	chArea[i] = parseFloat(ch[i].data.$area);
-    	totalChArea += chArea[i];
-    }
-    for(i=0; i<chArea.length; i++) {
-    	ch[i]._area = parentArea * chArea[i] / totalChArea;
-    }
-    var minimumSideValue = (this.layout.horizontal())? coord.height : coord.width;
-    ch.sort(function(a, b) { return (a._area <= b._area) - (a._area >= b._area); });
-    var initElem = [ch[0]];
-    var tail = ch.slice(1);
-    this.squarify(tail, initElem, minimumSideValue, coord);
-  },
-
-  /*
-    Method: squarify
-  
-    Performs an heuristic method to calculate div elements sizes in order to have a good aspect ratio.
-  
-     Parameters:
-
-        tail - An array of nodes.  
-        initElem - An array of nodes, containing the initial node to be laid out.
-        w - A fixed dimension where nodes will be laid out.
-      	coord - A coordinates object specifying width, height, left and top style properties.
-  */
-  squarify: function(tail, initElem, w, coord) {
-    this.computeDim(tail, initElem, w, coord, this.worstAspectRatio);
-  },
-  
-  /*
-     Method: layoutRow
-  
-    Performs the layout of an array of nodes.
-  
-     Parameters:
-
-        ch - An array of nodes.  
-        w - A fixed dimension where nodes will be laid out.
-      	coord - A coordinates object specifying width, height, left and top style properties.
-  */
-  layoutRow: function(ch, w, coord) {
-    if(this.layout.horizontal()) {
-      return this.layoutV(ch, w, coord);
-    } else {
-      return this.layoutH(ch, w, coord);
-    }
-  },
-  
-  layoutV: function(ch, w, coord) {
-    var totalArea = 0, rnd = Math.round; 
-    $each(ch, function(elem) { totalArea += elem._area; });
-    var width = rnd(totalArea / w), top =  0; 
-    for(var i=0; i<ch.length; i++) {
-      var h = rnd(ch[i]._area / width);
-      ch[i].coord = {
-        'height': h,
-        'width': width,
-        'top': coord.top + top,
-        'left': coord.left
-      };
-      top += h;
-    }
-    var ans = {
-      'height': coord.height,
-      'width': coord.width - width,
-      'top': coord.top,
-      'left': coord.left + width
-    };
-    //take minimum side value.
-    ans.dim = Math.min(ans.width, ans.height);
-    if(ans.dim != ans.height) this.layout.change();
-    return ans;
-  },
-  
-  layoutH: function(ch, w, coord) {
-    var totalArea = 0, rnd = Math.round; 
-    $each(ch, function(elem) { totalArea += elem._area; });
-    var height = rnd(totalArea / w),
-    top = coord.top, 
-    left = 0;
-    
-    for(var i=0; i<ch.length; i++) {
-      ch[i].coord = {
-        'height': height,
-        'width': rnd(ch[i]._area / height),
-        'top': top,
-        'left': coord.left + left
-      };
-      left += ch[i].coord.width;
-    }
-    var ans = {
-      'height': coord.height - height,
-      'width': coord.width,
-      'top': coord.top + height,
-      'left': coord.left
-    };
-    ans.dim = Math.min(ans.width, ans.height);
-    if(ans.dim != ans.width) this.layout.change();
-    return ans;
-  }
-});
-
-
-/*
-   Class: TM.Strip
-
-  A JavaScript implementation of the Strip Treemap algorithm.
-
-  The <TM.Strip> constructor takes an _optional_ configuration object described in <TM>.
-
-    This visualization (as all other Treemap visualizations) is fed with JSON Tree structures.
-
-    The _$area_ node data key is required for calculating rectangles dimensions.
-
-    The _$color_ node data key is required if _Color_ _allow_ is *true* and is used for calculating 
-    leaves colors.
-
-    Extends:
-    <TM> and <TM.Area>
-
-    Parameters:
-    
-    config - Configuration defined in <TM>.
-
     Example:
-
-
-  Here's a way of instanciating the <TM.Strip> will all its _optional_ configuration features
   
-  (start code js)
-
-  var tm = new TM.Strip({
-    titleHeight: 13,
-      orientation: "h",
-    rootId: 'infovis',
-    offset:4,
-    levelsToShow: 3,
-        addLeftClickHandler: false,
-        addRightClickHandler: false,
-        selectPathOnHover: false,
-            
-    Color: {
-      allow: false,
-      minValue: -100,
-      maxValue: 100,
-      minColorValue: [255, 0, 50],
-      maxColorValue: [0, 255, 50]
+    (start code js)
+      Hypertree.Plot.EdgeTypes.implement({
+        'mySpecialType': {
+          'render': function(adj, canvas) {
+            //print your custom edge to canvas
           },
-          
-          Tips: {
-            allow: false,
-            offsetX: 20,
-            offsetY: 20,
-            onShow: function(tooltip, node, isLeaf, domElement) {}
-          },
+          //optional
+          'contains': function(adj, pos) {
+            //return true if pos is inside the arc or false otherwise
+          }
+        }
+      });
+    (end code)
   
-          onBeforeCompute:  function(node) {
-            //Some stuff on before compute...
-          },
-          onAfterCompute:   function() {
-            //Some stuff on after compute...
-          },
-          onCreateElement:  function(content, node, isLeaf, head, body) {
-            //Some stuff onCreateElement
-          },
-          onDestroyElement: function(content, node, isLeaf, head, body) {
-            //Some stuff onDestroyElement
-          },
-        request:          false
-    });
-  tm.loadJSON(json);
-
-  (end code)
-
-*/
-  
-TM.Strip = new Class({
-  Implements: [ TM, TM.Area ],
-
-  /*
-     Method: compute
-  
-    Called by loadJSON to calculate recursively all node positions and lay out the tree.
-  
-     Parameters:
-
-        json - A JSON subtree. See also <Loader.loadJSON>. 
-      coord - A coordinates object specifying width, height, left and top style properties.
   */
-  compute: function(json, coord) {
-    var ch = json.children, config = this.config;
-    if(ch.length > 0) {
-      this.processChildrenLayout(json, ch, coord);
-      for(var i=0; i<ch.length; i++) {
-        var chcoord = ch[i].coord,
-        offst = config.offset,
-        height = chcoord.height - (config.titleHeight + offst),
-        width = chcoord.width - offst;
-        coord = {
-          'width':width,
-          'height':height,
-          'top':0,
-          'left':0
-        };
-        this.compute(ch[i], coord);
+  Hypertree.Plot.EdgeTypes = new Class({
+    'none': $.empty,
+    'line': {
+      'render': function(adj, canvas) {
+        var from = adj.nodeFrom.pos.getc(true),
+          to = adj.nodeTo.pos.getc(true),
+          r = adj.nodeFrom.scale;
+          this.edgeHelper.line.render({x:from.x*r, y:from.y*r}, {x:to.x*r, y:to.y*r}, canvas);
+      },
+      'contains': function(adj, pos) {
+        var from = adj.nodeFrom.pos.getc(true),
+            to = adj.nodeTo.pos.getc(true),
+            r = adj.nodeFrom.scale;
+            this.edgeHelper.line.contains({x:from.x*r, y:from.y*r}, {x:to.x*r, y:to.y*r}, pos, this.edge.epsilon);
       }
+    },
+    'arrow': {
+      'render': function(adj, canvas) {
+        var from = adj.nodeFrom.pos.getc(true),
+            to = adj.nodeTo.pos.getc(true),
+            r = adj.nodeFrom.scale,
+            dim = adj.getData('dim'),
+            direction = adj.data.$direction,
+            inv = (direction && direction.length>1 && direction[0] != adj.nodeFrom.id);
+        this.edgeHelper.arrow.render({x:from.x*r, y:from.y*r}, {x:to.x*r, y:to.y*r}, dim, inv, canvas);
+      },
+      'contains': function(adj, pos) {
+        var from = adj.nodeFrom.pos.getc(true),
+            to = adj.nodeTo.pos.getc(true),
+            r = adj.nodeFrom.scale;
+        this.edgeHelper.arrow.contains({x:from.x*r, y:from.y*r}, {x:to.x*r, y:to.y*r}, pos, this.edge.epsilon);
+      }
+    },
+    'hyperline': {
+      'render': function(adj, canvas) {
+        var from = adj.nodeFrom.pos.getc(),
+            to = adj.nodeTo.pos.getc(),
+            dim = this.viz.getRadius();
+        this.edgeHelper.hyperline.render(from, to, dim, canvas);
+      },
+      'contains': $.lambda(false)
     }
-  },
+  });
 
-  /*
-     Method: processChildrenLayout
-  
-    Computes children real areas and other useful parameters for performing the Strip algorithm.
-  
-     Parameters:
-
-        par - The parent node of the json subtree.  
-        ch - An Array of nodes
-      coord - A coordinates object specifying width, height, left and top style properties.
-  */
-  processChildrenLayout: function(par, ch, coord) {
-    //compute children real areas
-    var area = coord.width * coord.height;
-    var dataValue = parseFloat(par.data.$area);
-    $each(ch, function(elem) {
-      elem._area = area * parseFloat(elem.data.$area) / dataValue;
-    });
-    var side = (this.layout.horizontal())? coord.width : coord.height;
-    var initElem = [ch[0]];
-    var tail = ch.slice(1);
-    this.stripify(tail, initElem, side, coord);
-  },
-
-  /*
-     Method: stripify
-  
-    Performs an heuristic method to calculate div elements sizes in order to have 
-    a good compromise between aspect ratio and order.
-  
-     Parameters:
-
-        tail - An array of nodes.  
-        initElem - An array of nodes.
-        w - A fixed dimension where nodes will be layed out.
-      coord - A coordinates object specifying width, height, left and top style properties.
-  */
-  stripify: function(tail, initElem, w, coord) {
-    this.computeDim(tail, initElem, w, coord, this.avgAspectRatio);
-  },
-  
-  /*
-     Method: layoutRow
-  
-    Performs the layout of an array of nodes.
-  
-     Parameters:
-
-        ch - An array of nodes.  
-        w - A fixed dimension where nodes will be laid out.
-      	coord - A coordinates object specifying width, height, left and top style properties.
-  */
-  layoutRow: function(ch, w, coord) {
-    if(this.layout.horizontal()) {
-      return this.layoutH(ch, w, coord);
-    } else {
-      return this.layoutV(ch, w, coord);
-    }
-  },
-  
-  layoutV: function(ch, w, coord) {
-	//TODO(nico): handle node dimensions properly
-    var totalArea = 0, rnd = function(x) { return x; }; //Math.round; 
-    $each(ch, function(elem) { totalArea += elem._area; });
-    var width = rnd(totalArea / w), top =  0; 
-    for(var i=0; i<ch.length; i++) {
-      var h = rnd(ch[i]._area / width);
-      ch[i].coord = {
-        'height': h,
-        'width': width,
-        'top': coord.top + (w - h - top),
-        'left': coord.left
-      };
-      top += h;
-    }
-
-    var ans = {
-      'height': coord.height,
-      'width': coord.width - width,
-      'top': coord.top,
-      'left': coord.left + width,
-      'dim': w
-    };
-    return ans;
-  },
-  
-  layoutH: function(ch, w, coord) {
-    var totalArea = 0, rnd = function(x) { return x; }; //Math.round; 
-    $each(ch, function(elem) { totalArea += elem._area; });
-    var height = rnd(totalArea / w),
-    top = coord.height - height, 
-    left = 0;
-    
-    for(var i=0; i<ch.length; i++) {
-      ch[i].coord = {
-        'height': height,
-        'width': rnd(ch[i]._area / height),
-        'top': top,
-        'left': coord.left + left
-      };
-      left += ch[i].coord.width;
-    }
-    var ans = {
-      'height': coord.height - height,
-      'width': coord.width,
-      'top': coord.top,
-      'left': coord.left,
-      'dim': w
-    };
-    return ans;
-  }
-});
-
+})($jit.Hypertree);
 
 
 
diff --git a/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/rgraphkit/RGraph.js b/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/rgraphkit/RGraph.js
index 3dc9e04..96069b7 100644
--- a/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/rgraphkit/RGraph.js
+++ b/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/rgraphkit/RGraph.js
@@ -56,162 +56,150 @@
 		},
 		
 		load : function() {
-	      var parent = this;
+		  var parent = this;
 		  try {
-			var vis = this.getVisible();
-			if (vis == "false") {
-				// make invisible
-				return;
-			}
-			qx.ui.core.Widget.flushGlobalQueues();
-			if (this._viz == null) {
-				this.info("Creating rgraph.");
-				var qParent = document.getElementById(this._id);
-				var vizParent = document.createElement("div");
-				var vizId = "vizParent"+this._id;
-				vizParent.setAttribute("id", vizId);
-				qParent.appendChild(vizParent);
-				/* style for node labels */
-//				var vizStyle = ".node {color: white;background-color:transparent;cursor:pointer;font-weight:bold;opacity:0.9;} .node:hover {cursor:pointer;color: #222;background-color:white;font-weight:bold;opacity:1;}";
-//				qx.html.StyleSheet.createElement(vizStyle);
-				
-				var canvas = new Canvas('vizCanvas'+this._id, {
-			        'injectInto': "vizParent"+this._id,
-			        'width': this.getWidth(),
-			        'height': this.getHeight(),
-			        //Optional: create a background canvas and plot
-			        //concentric circles in it.
-			        'backgroundCanvas': {
-			            'styles': {
-			                'strokeStyle': '#555'
-			            },
-			            
-			            'impl': {
-			                'init': function(){},
-			                'plot': function(canvas, ctx){
-			                    var times = 6, d = 100;
-			                    var pi2 = Math.PI * 2;
-			                    for (var i = 1; i <= times; i++) {
-			                        ctx.beginPath();
-			                        ctx.arc(0, 0, i * d, 0, pi2, true);
-			                        ctx.stroke();
-			                        ctx.closePath();
-			                    }
-			                }
-			            }
-				    }
-			    });
-				qParent._canvas = canvas;
-
-				var widgetId = this._id;
-				vizParent.width = this.getWidth();
-				vizParent.height = this.getHeight();
-			    var rg = new RGraph(canvas, {
-			    	 //interpolation type, can be linear or polar  
-			        //interpolation: 'linear',  
-			        //parent-children distance  
-			        //levelDistance: 100,  
-			          //withLabels: true,
-			    	  duration: 1000,  
-			    	  fps: 25,
-			          Node: {  
+		    var vis = this.getVisible();
+		    if (vis == "false") {
+		      // make invisible
+		      return;
+		    }
+		    qx.ui.core.Widget.flushGlobalQueues();
+		    if (this._viz == null) {
+		      this.info("Creating rgraph.");
+		      var qParent = document.getElementById(this._id);
+		      var vizParent = document.createElement("div");
+		      var vizId = "vizParent"+this._id;
+		      vizParent.setAttribute("id", vizId);
+		      qParent.appendChild(vizParent);
+		      var widgetId = this._id;
+		      vizParent.width = this.getWidth();
+		      vizParent.height = this.getHeight();
+		      var rg = new $jit.RGraph({
+		        injectInto: vizId,
+		        //Optional: create a background canvas that plots  
+		        //concentric circles.  
+		        background: {  
+		          CanvasStyles: {  
+		            strokeStyle: '#555'  
+		          }  
+		        }, 
+		        //interpolation type, can be linear or polar  
+		        //interpolation: 'linear',  
+		        //parent-children distance  
+		        //levelDistance: 100,  
+		        //withLabels: true,
+		        duration: 1000,  
+		        fps: 25,
+		        Navigation: {  
+              enable:true,  
+              panning:true,
+              zooming: 20
+            },
+		        Node: {  
 //			            overridable: false,  
 //			            type: 'circle',
-			            color: '#ccddee'
+		          color: '#ccddee'
 //			            lineWidth: 1,  
 //			            height: 5,  
 //			            width: 5,  
 //			            dim: 3  
-			          },  
-			          Edge: {  
+		        },  
+		        Edge: {  
 //			            overridable: false,  
 //			            type: 'line',  
-			            color: '#772277' 
+		          color: '#772277' 
 //			            lineWidth: 1  
-			          },  
-			          onBeforeCompute: function(node) {  
-			            //do something onBeforeCompute  
-			          },  
-			          onAfterCompute: function(){
-			        	  var node = Graph.Util.getClosestNodeToOrigin(rg.graph, "pos");
-			        	  qParent.selection = node;
-			        	  //fire selection event
-			        	  var req = org.eclipse.swt.Request.getInstance();
-			        	  req.addParameter(widgetId + ".selectedNode", node.id);
-			        	  req.addEvent( "org.eclipse.swt.events.widgetSelected", widgetId );
-			        	  req.send();
-			          },  
-			        //Change some label dom properties.
-			          //This method is called each time a label is plotted.
-			          onPlaceLabel: function(domElement, node){
-			              var style = domElement.style;
-			              style.display = '';
-			              style.cursor = 'pointer';
-			              var font = parent.getFont();
-			              style.fontFamily = font.getFamily();
-			              style.fontStyle = font.generateStyle();
-			              var color = parent.getTextColor();
-			              if (node._depth <= 1) {
-			            	  style.fontSize = font.getSize();
-			            	  style.color = color;
-			            	  
-			              } else if(node._depth == 2){
-			            	  style.fontSize = font.getSize()-2;
-			            	  style.color = "#555";
-			            	  
-			              } else {
-			            	  style.display = 'none';
-			              }
-			              
-			              var left = parseInt(style.left);
-			              var w = domElement.offsetWidth;
-			              style.left = (left - w / 2) + 'px';
-			          }, 
-			          
-			          //Add a controller to make the tree move on click.  
-			          onCreateLabel: function(domElement, node) {  
-			        	  domElement.innerHTML = node.name;
-			        	  domElement.onclick = function() {
-			        		  rg.onClick(node.id);  
-			        	  };  
-			          },   
-			          onBeforePlotNode:function(node) {  
-			            //do something onBeforePlotNode  
-			          },  
-			          onAfterPlotNode: function(node) {  
-			            //do something onAfterPlotNode  
-			          },  
-			          onBeforePlotLine:function(adj) {  
-			            //do something onBeforePlotLine  
-			          },  
-			          onAfterPlotLine: function(adj) {  
-			            //do something onAfterPlotLine  
-			          }  
-			    });
-			    
-			    this.addEventListener("changeWidth", function(e) {
-					if (canvas != null) {
-						canvas.width = e.getValue();
-						vizParent.width = e.getValue();
-						canvas.resize(vizParent.width, vizParent.height);
-	             		rg.refresh();
-					}
-				});
-				this.addEventListener("changeHeight", function(e) {
-					if (canvas != null) {
-						canvas.height = e.getValue();
-						vizParent.height = e.getValue();
-						canvas.resize(vizParent.width, vizParent.height);
-						rg.refresh();
-					}
-				});
-			    
-				this._viz = rg;
-				this._vizParent = vizParent;
-			}
+		        },  
+		        onBeforeCompute: function(node) {  
+		          //do something onBeforeCompute  
+		        },  
+		        onAfterCompute: function(){
+		          var node = $jit.Graph.Util.getClosestNodeToOrigin(rg.graph, "current");
+		          qParent.selection = node;
+		          //fire selection event
+		          parent.info("Sending selected node: "+node.id);
+		          var req = org.eclipse.swt.Request.getInstance();
+		          req.addParameter(widgetId + ".selectedNode", node.id);
+		          req.addEvent( "org.eclipse.swt.events.widgetSelected", widgetId );
+		          req.send();
+		        },  
+		        //Change some label dom properties.
+		        //This method is called each time a label is plotted.
+		        onPlaceLabel: function(domElement, node){
+		          var style = domElement.style;
+		          style.display = '';
+		          style.cursor = 'pointer';
+		          var font = parent.getFont();
+		          style.fontFamily = font.getFamily();
+		          try {
+		            style.fontStyle = font.generateStyle();
+		          }
+		          catch (e) {
+		            //ignore..some items are not cross - browser compatible
+		          }
+		          var color = parent.getTextColor();
+		          if (node._depth <= 1) {
+		            style.fontSize = font.getSize();
+		            style.color = color;
+		            
+		          } else if(node._depth == 2){
+		            style.fontSize = font.getSize()-2;
+		            style.color = "#555";
+		            
+		          } else {
+		            style.display = 'none';
+		          }
+		          var left = parseInt(style.left);
+		          var w = domElement.offsetWidth;
+		          style.left = (left - w / 2) + 'px';
+		        }, 
+		        
+		        //Add a controller to make the tree move on click.  
+		        onCreateLabel: function(domElement, node) {  
+		          domElement.innerHTML = node.name;
+		          domElement.onclick = function() {
+		            rg.onClick(node.id);  
+		          };  
+		        },   
+		        onBeforePlotNode:function(node) {  
+		          //do something onBeforePlotNode  
+		        },  
+		        onAfterPlotNode: function(node) {  
+		          //do something onAfterPlotNode  
+		        },  
+		        onBeforePlotLine:function(adj) {  
+		          //do something onBeforePlotLine  
+		        },  
+		        onAfterPlotLine: function(adj) {  
+		          //do something onAfterPlotLine  
+		        }  
+		      });
+		      
+		      this.addEventListener("changeWidth", function(e) {
+		        if (vizParent != null) {
+		          vizParent.width = e.getValue();
+		          if (vizParent.height != null && vizParent.width != null) {
+		            rg.canvas.resize(vizParent.width, vizParent.height);
+		            rg.refresh();
+		          }
+		        }
+		      });
+		      this.addEventListener("changeHeight", function(e) {
+		        if (vizParent != null) {
+		          vizParent.height = e.getValue();
+		          if (vizParent.height != null && vizParent.width != null) {
+		            rg.canvas.resize(vizParent.width, vizParent.height);
+		            rg.refresh();
+		          }
+		        }
+		      });
+		      
+		      this._viz = rg;
+		      this._vizParent = vizParent;
+		    }
 		  }
 		  catch (e) {
-			 this.info(e);
+		    this.info(e);
 		  }
 		},
 		
@@ -221,23 +209,22 @@
 		},
 		
 		refreshData : function () {
-			try {
-		      var rg = this._viz;
-		      if (rg != null) {
-		    	  var data = this.getWidgetData();
-		    	  if (data != null) {
-		    		this.info("Loading rgraph data.");
-	//	    	    this.info(data);
-		    	    rg.loadJSON(data);
-		    	    this.info("Refreshing rgraph.");
-				    rg.refresh();
-				    ht.controller.onAfterCompute();
-		    	  }
+		  try {
+		    var rg = this._viz;
+		    if (rg != null) {
+		      var data = this.getWidgetData();
+		      if (data != null) {
+		        this.info("Loading rgraph data.");
+		        rg.loadJSON(data);
+		        this.info("Refreshing rgraph.");
+		        rg.refresh();
+		        rg.controller.onAfterCompute();
 		      }
-			}
-			catch (e) {
-				this.info(e);
-			}
+		    }
+		  }
+		  catch (e) {
+		    this.info(e);
+		  }
 		},
 		
 		setProperty : function (propName, propValue) {
@@ -278,6 +265,19 @@
         this.info(e);
       }
     },
+    
+    setZoom : function (percent) {
+      try {
+        var st = this._viz;
+        if (st != null) {
+          st.canvas.scale(percent,percent);
+          st.refresh();
+        }
+      }
+      catch (e) {
+        this.info(e);
+      }
+    },
 		
 		selectNode : function (id) {
 			try {
diff --git a/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/spacetreekit/SpaceTree.js b/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/spacetreekit/SpaceTree.js
index d82584d..168107d 100644
--- a/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/spacetreekit/SpaceTree.js
+++ b/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/spacetreekit/SpaceTree.js
@@ -56,166 +56,244 @@
 		},
 		
 		load : function() {
-	      var parent = this;
+		  var parent = this;
 		  try {
-			var vis = this.getVisible();
-			if (vis == "false") {
-				// make invisible
-				return;
-			}
-			qx.ui.core.Widget.flushGlobalQueues();
-			if (this._viz == null) {
-				this.info("Creating spacetree.");
-				var qParent = document.getElementById(this._id);
-				var vizParent = document.createElement("div");
-				var vizId = "vizParent"+this._id;
-				vizParent.setAttribute("id", vizId);
-				qParent.appendChild(vizParent);
-				/* style for node labels */
-//				var vizStyle = ".node {color: white;background-color:transparent;cursor:pointer;font-weight:bold;opacity:0.9;} .node:hover {cursor:pointer;color: #222;background-color:white;font-weight:bold;opacity:1;}";
-//				qx.html.StyleSheet.createElement(vizStyle);
-				
-				var canvas = new Canvas('vizCanvas'+this._id, {
-			        'injectInto': "vizParent"+this._id,
-			        'width': this.getWidth(),
-			        'height': this.getHeight()
-			    });
-				qParent._canvas = canvas;
-
-				var widgetId = this._id;
-				vizParent.width = this.getWidth();
-				vizParent.height = this.getHeight();
-			    var st = new ST(canvas, {
-			    	  orientation: "left",  
-			    	  levelsToShow: 2,  
-			    	  subtreeOffset: 8,  
-			    	  siblingOffset: 5,  
-			    	  levelDistance: 30,  
-			    	  withLabels: true,  
-			    	  align: "center",  
-			    	  multitree: false,  
-			    	  indent: 10,  
-			    	  //set distance between node and its children
-			        levelDistance: 50,
-			    	  Node: {  
-			    	    overridable: true,  
-			    	    type: 'rectangle',  
-			    	    color: '#ccb',  
-			    	    lineWidth: 1,  
-			    	    height: 20,  
-			    	    width: 90,  
-			    	    dim: 15,  
-			    	    align: "center"  
-			    	  },  
-			    	  Edge: {  
-			    	    overridable: true,  
-			    	    type: 'bezier',  
-			    	    color: '#ccc',  
-			    	    dim: 15,  
-			    	    lineWidth: 1  
-			    	  },  
-			    	  duration: 700,  
-			    	  fps: 25,  
-			    	  transition: Trans.Quart.easeInOut,  
-			    	  clearCanvas: true,  
-			    	  
-			    	  onBeforeCompute: function(node) {  
-			    	    //do something onBeforeCompute  
-			    	  },  
-			    	  onAfterCompute: function(){
-			        	  var node = Graph.Util.getClosestNodeToOrigin(st.graph, "pos");
-			        	  qParent.selection = node;
-			        	  //fire selection event
-			        	  var req = org.eclipse.swt.Request.getInstance();
-			        	  req.addParameter(widgetId + ".selectedNode", node.id);
-			        	  req.addEvent( "org.eclipse.swt.events.widgetSelected", widgetId );
-			        	  req.send();
-			          },  
-			    	  onCreateLabel:   function(domElement, node) {  
-			    		  domElement.id = node.id;            
-			    		  domElement.innerHTML = node.name;
-			    		  domElement.onclick = function(){
-			                  st.onClick(node.id);
-			              };
-			              //set label styles
-			              var style = domElement.style;
-			              var font = parent.getFont();
-			              style.fontFamily = font.getFamily();
-			              style.fontStyle = font.generateStyle();
-			              style.fontSize = font.getSize();
-			              style.width = 60 + 'px';
-			              style.height = 17 + 'px';            
-			              style.cursor = 'pointer';
-			              style.textAlign= 'center';
-			              style.paddingTop = '3px';  
-			              var color = parent.getTextColor();
-		            	  style.color = color;
-			    	  },  
-			    	  onPlaceLabel:    function(domElement, node) {  
-			    	    //do something onPlaceLabel  
-			    	  },  
-			    	  onBeforePlotNode:function(node) {  
-			    		//add some color to the nodes in the path between the
-			              //root node and the selected node.
-			              if (node.selected) {
-			                  node.data.$color = "#ff7";
-			              }
-			              else {
-			                  delete node.data.$color;
-			                  var GUtil = Graph.Util;
-			                  //if the node belongs to the last plotted level
-			                  if(!GUtil.anySubnode(node, "exist")) {
-			                      //count children number
-			                      var count = 0;
-			                      GUtil.eachSubnode(node, function(n) { count++; });
-			                      //assign a node color based on
-			                      //how many children it has
-			                      node.data.$color = ['#aaa', '#baa', '#caa', '#daa', '#eaa', '#faa'][count];                    
-			                  }
-			              }  
-			    	  },  
-			    	  onAfterPlotNode: function(node) {  
-			    	    //do something onAfterPlotNode  
-			    	  },  
-			    	  onBeforePlotLine:function(adj) {  
-			    		  if (adj.nodeFrom.selected && adj.nodeTo.selected) {
-			                  adj.data.$color = "#eed";
-			                  adj.data.$lineWidth = 3;
-			              }
-			              else {
-			                  delete adj.data.$color;
-			                  delete adj.data.$lineWidth;
-			              }  
-			    	  },  
-			    	  onAfterPlotLine: function(adj) {  
-			    	    //do something onAfterPlotLine  
-			    	  },
-			    	  request:         false
-			    });
-			    
-			    this.addEventListener("changeWidth", function(e) {
-					if (canvas != null) {
-						canvas.width = e.getValue();
-						vizParent.width = e.getValue();
-						canvas.resize(vizParent.width, vizParent.height);
-	             		st.refresh();
-					}
-				});
-				this.addEventListener("changeHeight", function(e) {
-					if (canvas != null) {
-						canvas.height = e.getValue();
-						vizParent.height = e.getValue();
-						canvas.resize(vizParent.width, vizParent.height);
-						st.refresh();
-					}
-				});
-			    
-				this._viz = st;
-				this._vizParent = vizParent;
-			}
+		    var vis = this.getVisible();
+		    if (vis == "false") {
+		      // make invisible
+		      return;
+		    }
+		    qx.ui.core.Widget.flushGlobalQueues();
+		    if (this._viz == null) {
+		      this.info("Creating spacetree.");
+		      var qParent = document.getElementById(this._id);
+		      var vizParent = document.createElement("div");
+		      var vizId = "vizParent"+this._id;
+		      vizParent.setAttribute("id", vizId);
+		      qParent.appendChild(vizParent);
+		      vizParent.width = this.getWidth();
+		      vizParent.height = this.getHeight();
+		      var widgetId = this._id;
+		      
+		      //Create a node rendering function that plots a fill 
+		      //rectangle and a stroke rectangle for borders 
+		      $jit.ST.Plot.NodeTypes.implement({ 
+		        'stroke-rect': {
+		          'render': function(node, canvas) { 
+		            var width = node.getData('width'), 
+		            height = node.getData('height'), 
+		            pos = this.getAlignedPos(node.pos.getc(true), width, height), 
+		            posX = pos.x + width/2, 
+		            posY = pos.y + height/2;
+		            this.nodeHelper.rectangle.render('fill', {x: posX, y: posY}, 
+		                width, height, canvas); 
+		            this.nodeHelper.rectangle.render('stroke', {x: posX, y: posY}, 
+		                width, height, canvas); 
+		          } 
+		        } 
+		      }); 
+		      
+		      var st = new $jit.ST({
+		        injectInto: vizId,
+		        orientation: "left",  
+		        levelsToShow: 2,  
+		        subtreeOffset: 8,  
+		        siblingOffset: 5,  
+		        levelDistance: 30,  
+		        withLabels: true,  
+		        align: "center",  
+		        multitree: false,  
+		        indent: 10,  
+		        //set distance between node and its children
+		        levelDistance: 50,
+		        Navigation: {  
+		          enable:true,  
+		          panning:true,
+		          zooming: 20
+		        },
+		        Node: {  
+		          overridable: true,
+		          type: 'stroke-rect',
+		          /*type: 'rectangle',*/  
+		          color: '#ccb',  
+		          lineWidth: 1,  
+		          height: 20,  
+		          width: 90,  
+		          dim: 15,  
+		          align: "center",
+		          CanvasStyles: {  
+		            fillStyle: '#ccb',  
+		            strokeStyle: '#829bcd',  
+		            lineWidth: 2  
+		          }  
+		        },  
+		        Edge: {  
+		          overridable: true,  
+		          type: 'bezier',  
+		          dim: 15,  
+		          lineWidth: 1  
+		        },
+		        Tips: {  
+		          enable: true,
+		          force: true,
+		          type: 'auto',  
+		          offsetX: 10,  
+		          offsetY: 10,  
+		          onShow: function(tip, node) {
+		            tip.style.border = "1px solid #c0c0c0";
+		            tip.style.backgroundColor = "#ddd";
+		            tip.innerHTML = node.name;
+		            tip.style.zIndex = 1000000000;
+		          }  
+		        },
+		        duration: 700,  
+		        fps: 25,  
+		        transition: $jit.Trans.Quart.easeInOut,  
+		        clearCanvas: true,  
+		        
+		        onBeforeCompute: function(node) {  
+		          //do something onBeforeCompute  
+		        },  
+		        onAfterCompute: function(){
+		          var node = $jit.Graph.Util.getClosestNodeToOrigin(st.graph, "current");
+		          qParent.selection = node;
+		          //fire selection event
+		          parent.info("Sending selected node: "+node.id);
+		          var req = org.eclipse.swt.Request.getInstance();
+		          req.addParameter(widgetId + ".selectedNode", node.id);
+		          req.addEvent( "org.eclipse.swt.events.widgetSelected", widgetId );
+		          req.send();
+		        },  
+		        onCreateLabel:   function(domElement, node) {  
+		          domElement.id = node.id;            
+		          domElement.innerHTML = node.name;
+		          domElement.onclick = function(){
+		            st.onClick(node.id);
+		          };
+		          //set label styles
+		          var style = domElement.style;
+		          var font = parent.getFont();
+  		          style.fontFamily = font.getFamily();
+  		          try {
+  		            style.fontStyle = font.generateStyle();
+  		          }
+  		          catch (e) {
+  		            //ignore..some items are not cross - browser compatible
+  		          }
+  		          style.fontSize = font.getSize();
+  		          style.width = st.controller.Node.width + 'px';
+  		          style.height = st.controller.Node.height + 'px';            
+  		          style.cursor = 'pointer';
+  		          style.textAlign= 'center';
+  		          style.verticalAlign = 'middle';
+  		          style.padding = '3px';
+  		          var color = parent.getTextColor();
+  		          style.color = color;
+		        },  
+		        onPlaceLabel:    function(domElement, node) {
+		          //future use
+		          var style = domElement.style;
+		          //todo align top and left based on scale
+		          var width = st.controller.Node.width*st.canvas.scaleOffsetX;
+              var height = st.controller.Node.height*st.canvas.scaleOffsetY; 
+              style.width = width + 'px';
+              style.height = height + 'px';
+		          
+              var pos = node.pos.getc(true); 
+              var w = node.getData('width');
+              var h = node.getData('height');
+              var radius = st.canvas.getSize();
+          
+              var ox = st.canvas.translateOffsetX;
+              var oy = st.canvas.translateOffsetY;
+              var sx = st.canvas.scaleOffsetX;
+              var sy = st.canvas.scaleOffsetY;
+              var posx = pos.x * sx + ox;
+              var posy = pos.y * sy + oy;
+              //use scaled width
+              var left = Math.round(posx - width / 2 + radius.width/2);
+              var top = Math.round(posy - height / 2 + radius.height/2);
+              style.left = left+'px';
+              style.top = top+'px';
+              var font = parent.getFont();
+              style.fontSize = Math.ceil(font.getSize()*sx)+'pt';
+              
+              //don't show if not in canvas
+              var canvasSize = st.canvas.getSize();
+              if(left >= canvasSize.width || left < 0 || top >= canvasSize.height || top < 0) {
+                style.display = 'none';
+              }
+              else {
+                style.display = '';
+              }
+		        },  
+		        onBeforePlotNode:function(node) {  
+		          //add some color to the nodes in the path between the
+		          //root node and the selected node.
+		          if (node.selected) {
+		            node.setCanvasStyle('strokeStyle', "#fff");
+		            node.setCanvasStyle('fillStyle', "#ff7");
+//		            node.data.$color = "#ff7";
+		          }
+		          else {
+//		            delete node.data.$color;
+		            node.setCanvasStyle('strokeStyle', "#829bcd");
+		            var GUtil = $jit.Graph.Util;
+		            //if the node belongs to the last plotted level
+		            if(!GUtil.anySubnode(node, "exist")) {
+		              //count children number
+		              var count = 0;
+		              GUtil.eachSubnode(node, function(n) { count++; });
+		              //assign a node color based on
+		              //how many children it has
+		              node.setCanvasStyle('fillStyle', ['#aaa', '#baa', '#caa', '#daa', '#eaa', '#faa'][count]);
+//		              node.data.$color = ['#aaa', '#baa', '#caa', '#daa', '#eaa', '#faa'][count];                    
+		            }
+		          }  
+		        },  
+		        onAfterPlotNode: function(node) {  
+		          //do something onAfterPlotNode  
+		        },  
+		        onBeforePlotLine:function(adj) {  
+		          if (adj.nodeFrom.selected && adj.nodeTo.selected) {
+		            adj.data.$color = "#fff";
+		            adj.data.$lineWidth = 3;
+		          }
+		          else {
+		            delete adj.data.$color;
+		            delete adj.data.$lineWidth;
+		          }  
+		        },  
+		        onAfterPlotLine: function(adj) {  
+		          //do something onAfterPlotLine  
+		        },
+		        request:         false
+		      });
+		      this.addEventListener("changeWidth", function(e) {
+		        if (vizParent != null) {
+		          vizParent.width = e.getValue();
+		          if (vizParent.height != null && vizParent.width != null) {
+		            st.canvas.resize(vizParent.width, vizParent.height);
+		            st.refresh();
+		          }
+		        }
+		      });
+		      this.addEventListener("changeHeight", function(e) {
+		        if (vizParent != null) {
+		          vizParent.height = e.getValue();
+		          if (vizParent.height != null && vizParent.width != null) {
+                st.canvas.resize(vizParent.width, vizParent.height);
+                st.refresh();
+              }
+		        }
+		      });
+		      
+		      this._viz = st;
+		      this._vizParent = vizParent;
+		    }
 		  }
 		  catch (e) {
-			 this.info(e);
+		    this.info(e);
 		  }
 		},
 		
@@ -231,15 +309,11 @@
 		    	  this.info("Loading spacetree data.");
 		    	  var data = this.getWidgetData();
 		    	  if (data != null) {
-	//	    	    this.info(data);
 		    	    st.loadJSON(data);
-		    	    this.info("Refreshing spacetree.");
 		    	    //compute node positions and layout  
-		    	    st.compute();  
+		    	    st.compute();
 		    	    //Emulate a click on the root node.  
-		    	    st.onClick(st.root); 
-//				    st.refresh();
-//	  		        ht.controller.onAfterCompute();
+		    	    st.onClick(st.root);
 		    	  }
 		      }
 			}
@@ -286,6 +360,35 @@
         this.info(e);
       }
     },
+    
+    setZoom : function (percent) {
+      try {
+        var st = this._viz;
+        if (st != null) {
+          st.canvas.scale(percent,percent);
+          st.refresh();
+        }
+      }
+      catch (e) {
+        this.info(e);
+      }
+    },
+    
+//    zoomToFit : function () {
+//      try {
+//        var st = this._viz;
+//        if (st != null) {
+//          var sz = st.canvas.getSize();
+//          var xratio = sz.width/st.canvas.virtualWidth;
+//          var yratio = sz.height/st.canvas.virtualHeight;
+//          st.canvas.scale(xratio,yratio);
+//          st.refresh();
+//        }
+//      }
+//      catch (e) {
+//        this.info(e);
+//      }
+//    },
 		
 		selectNode : function (id) {
 			try {
@@ -301,7 +404,6 @@
 		
 		_doResize : function() {
 			qx.ui.core.Widget.flushGlobalQueues();
-			
 		}
 	}
 });
\ No newline at end of file
diff --git a/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/treemapkit/TreeMap.js b/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/treemapkit/TreeMap.js
index bea477a..fc4377c 100644
--- a/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/treemapkit/TreeMap.js
+++ b/bundles/org.eclipse.rap.rwt.visualization.jit/src/org/eclipse/rap/rwt/visualization/jit/internal/treemapkit/TreeMap.js
@@ -57,118 +57,96 @@
 		},
 		
 		load : function() {
-	      var parent = this;
+		  var parent = this;
 		  try {
-			var vis = this.getVisible();
-			if (vis == "false") {
-				// make invisible
-				return;
-			}
-			qx.ui.core.Widget.flushGlobalQueues();
-			if (this._viz == null) {
-				this.info("Creating treemap.");
-				
-				var qParent = document.getElementById(this._id);
-				var vizParent = document.createElement("div");
-				var vizId = "vizParent"+this._id;
-				vizParent.setAttribute("id", vizId);
-				vizParent.setAttribute("style","position:absolute;overflow:hidden;display:table-cell;width:100%;vertical-align:middle;height:100%;z-order:auto;");
-				qParent.appendChild(vizParent);
-				
-				var vizStyle = "#infovis div {position:absolute;overflow:hidden;font-size:11px;font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;}#infovis .content {background-color:#333;border:0px solid #111;} #infovis .head {color:white;background-color:#444;} #infovis .head.in-path {background-color:#655;} #infovis .body {background-color:black;} #infovis .leaf {color:white;background-color:#111;display:table-cell;vertical-align:middle;border:1px solid #000;} #infovis .over-leaf {border:1px solid #9FD4FF;} #infovis .over-content {background-color: #9FD4FF;} #infovis .over-head {background-color:#A4D9FF;color:black;} .tip {color: #fff;width: 139px;background-color: black;opacity:0.9;filter:alpha(opacity=90);font-size:10px;font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;padding:7px;} .album {width:100px;margin:3px;} input {font-size:10px;font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;}";
-				vizStyle = vizStyle.replace(/infovis/gi,vizId);
-				qx.html.StyleSheet.createElement(vizStyle);
-				
-				var config = {
-				    titleHeight: 13,
-			        //The id of the treemap container
-			        rootId: vizId,
-			        //Set the max. depth to be shown for a subtree
-			        levelsToShow: 1,
-			        offset:1,
-
-			        //Add click handlers for
-			        //zooming the Treemap in and out
-			        addLeftClickHandler: true,
-			        addRightClickHandler: true,
-			        
-			        //When hovering a node highlight the nodes
-			        //between the root node and the hovered node. This
-			        //is done by adding the 'in-path' CSS class to each node.
-			        selectPathOnHover: true,
-			        Color: {  
-			            //Allow coloring  
-			            allow: true,  
-			            //Set min value and max value constraints  
-			            //for the *$color* property value.  
-			            //Default's to -100 and 100.  
-			            minValue: 1,  
-			            maxValue: 50,  
-			            //Set color range. Default's to reddish and greenish.  
-			            //It takes an array of three  
-			            //integers as R, G and B values.  
-			            minColorValue: [0, 255, 50],  
-			            maxColorValue: [255, 0, 50]  
-			        },
-			        
-			        //Allow tips
-			        Tips: {
-			          allow: true,
-			          //add positioning offsets
-			          offsetX: 20,
-			          offsetY: 20,
-			          //implement the onShow method to
-			          //add content to the tooltip when a node
-			          //is hovered
-			          onShow: function(tip, node, isLeaf, domElement) {
-			              tip.innerHTML = "<div>" + node.name + "</div>" + 
-			                "<div>" + this.makeHTMLFromData(node.data) + "</div>"; 
-			          },  
-
-			          //Aux method: Build the tooltip inner html by using the data property
-			          makeHTMLFromData: function(data){
-			              var html = '';
-			              html += "playcount" + ': ' + data.$area + '<br />';
-			              if ("$color" in data) 
-			                  html += "rank" + ': ' + data.$color + '<br />';
-			              if ("image" in data) 
-			                  html += "<img src=\"" + data.image + "\" />";
-			              return html;
-			          }
-			        },
-
-			        //Implement this method for retrieving a requested
-			        //subtree that has as root a node with id = nodeId,
-			        //and level as depth. This method could also make a server-side
-			        //call for the requested subtree. When completed, the onComplete 
-			        //callback method should be called.
-//			        request: function(nodeId, level, onComplete){
-//			        	this.info("Refreshing treemap child.");
-//			        	var data = this.getWidgetData();
-////				    	  this.info(data);
-////			        	if (data != undefined) {
-////					    	var root = data.substring(0,data.length); 
-////				            var subtree = TreeUtil.getSubtree(root, nodeId);
-////				            TreeUtil.prune(subtree, 1);
-////				            onComplete.onComplete(nodeId, subtree);
-////			        	}
-//			        },
-			        //Remove all events for the element before destroying it.
-			        onDestroyElement: function(content, tree, isLeaf, leaf){
-			            if(leaf.clearAttributes) leaf.clearAttributes();
-			        }
-			    };
-				var tm = null;
-				if (this._type == 2) {
-					tm = new TM.Strip(config);
-				}
-				else if (this._type == 1) {
-					tm = new TM.Squarified(config);
-				}
-				else {
-					tm = new TM.SliceAndDice(config);
-				}
-				
+		    var vis = this.getVisible();
+		    if (vis == "false") {
+		      // make invisible
+		      return;
+		    }
+		    qx.ui.core.Widget.flushGlobalQueues();
+		    if (this._viz == null) {
+		      this.info("Creating treemap.");
+		      
+		      var qParent = document.getElementById(this._id);
+		      var vizParent = document.createElement("div");
+		      var vizId = "vizParent"+this._id;
+		      vizParent.setAttribute("id", vizId);
+		      vizParent.setAttribute("style","position:absolute;overflow:hidden;display:table-cell;width:100%;vertical-align:middle;height:100%;z-order:auto;");
+		      vizParent.width = this.getWidth();
+		      vizParent.height = this.getHeight();
+		      qParent.appendChild(vizParent);
+		      var vizStyle = "#infovis div {position:absolute;overflow:hidden;font-size:11px;font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;}#infovis .content {background-color:#333;border:0px solid #111;} #infovis .head {color:white;background-color:#444;} #infovis .head.in-path {background-color:#655;} #infovis .body {background-color:black;} #infovis .leaf {color:white;background-color:#111;display:table-cell;vertical-align:middle;border:1px solid #000;} #infovis .over-leaf {border:1px solid #9FD4FF;} #infovis .over-content {background-color: #9FD4FF;} #infovis .over-head {background-color:#A4D9FF;color:black;} .tip {color: #fff;width: 139px;background-color: black;opacity:0.9;filter:alpha(opacity=90);font-size:10px;font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;padding:7px;} .album {width:100px;margin:3px;} input {font-size:10px;font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;}";
+		      vizStyle = vizStyle.replace(/infovis/gi,vizId);
+		      qx.html.StyleSheet.createElement(vizStyle);
+		      
+		      var tm = null;
+		      var config = {
+		          titleHeight: 15,
+		          animate: true,
+		          //The id of the treemap container
+		          injectInto: vizId,
+		          //Set the max. depth to be shown for a subtree
+		          levelsToShow: 1,
+		          offset:1,
+		          Events: {  
+		            enable: true,  
+		            onClick: function(node) {  
+		              if(node) tm.enter(node);  
+		            },  
+		            onRightClick: function() {  
+		              tm.out();  
+		            }  
+		          },  
+		          duration: 800,  
+		          //Enable tips  
+		          Tips: {  
+		            enable: true,  
+		            //add positioning offsets  
+		            offsetX: 20,  
+		            offsetY: 20,  
+		            //implement the onShow method to  
+		            //add content to the tooltip when a node  
+		            //is hovered  
+		            onShow: function(tip, node, isLeaf, domElement) {  
+		              var html = "<div class=\"tip-title\">" + node.name   
+		              + "</div><div class=\"tip-text\">";  
+		              var data = node.data;  
+		              if(data.playcount) {  
+		                html += "play count: " + data.playcount;  
+		              }  
+		              if(data.image) {  
+		                html += "<img src=\""+ data.image +"\" class=\"album\" />";  
+		              }  
+		              tip.innerHTML =  html;   
+		            }    
+		          },  
+		          //Add the name of the node in the correponding label  
+		          //This method is called once, on label creation.  
+		          onCreateLabel: function(domElement, node){  
+		            domElement.innerHTML = node.name;  
+		            var style = domElement.style;  
+		            style.display = '';  
+		            style.border = '1px solid transparent';  
+		            domElement.onmouseover = function() {  
+		              style.border = '1px solid #9FD4FF';  
+		            };  
+		            domElement.onmouseout = function() {  
+		              style.border = '1px solid transparent';  
+		            };  
+		          }  
+		      };
+		      
+		      if (this._type == 2) {
+		        tm = new $jit.TM.Strip(config);
+		      }
+		      else if (this._type == 1) {
+		        tm = new $jit.TM.Squarified(config);
+		      }
+		      else {
+		        tm = new $jit.TM.SliceAndDice(config);
+		      }
+		      
 //				tm.onLeftClick = function(elem) {
 //			        this.enter(elem);
 //			        var node = elem;
@@ -190,31 +168,37 @@
 //		        	req.send();
 //			        
 //			    };
-				
-			    this.addEventListener("changeWidth", function(e) {
-					vizParent.width = this.getWidth();
-					qx.client.Timer.once(function() {
-						parent.refreshData();
-					},this,100);
-				});
-				this.addEventListener("changeHeight", function(e) {
-					vizParent.height = this.getHeight();
-					qx.client.Timer.once(function() {
-						parent.refreshData();
-					},this,100);
-				});
-			    
-				this._viz = tm;
-				this._vizParent = vizParent;
-				//This is a hack to ensure that a refresh is called after the style tag above is 
-				//initialized
-				qx.client.Timer.once(function() {
-					this.refreshData();
-				},this,100);
-			}
+		      
+		      this.addEventListener("changeWidth", function(e) {
+		        vizParent.width = this.getWidth();
+		        if (vizParent.height != null && vizParent.width != null) {
+  		        tm.canvas.resize(vizParent.width, vizParent.height);
+//  		        qx.client.Timer.once(function() {
+//  		          parent.refreshData();
+//  		        },this,100);
+		        }
+		      });
+		      this.addEventListener("changeHeight", function(e) {
+		        vizParent.height = this.getHeight();
+		        if (vizParent.height != null && vizParent.width != null) {
+  		        tm.canvas.resize(vizParent.width, vizParent.height);
+//  		        qx.client.Timer.once(function() {
+//  		          parent.refreshData();
+//  		        },this,100);
+		        }
+		      });
+		      
+		      this._viz = tm;
+		      this._vizParent = vizParent;
+		      //This is a hack to ensure that a refresh is called after the style tag above is 
+		      //initialized
+//		      qx.client.Timer.once(function() {
+//		        this.refreshData();
+//		      },this,100);
+		    }
 		  }
 		  catch (e) {
-			 this.info(e);
+		    this.info(e);
 		  }
 		},
 		
@@ -231,7 +215,6 @@
 					if (data != null) {
 						this.info("Loading treemap data.");
 						tm.loadJSON(data);
-						this.info("Refreshing treemap.");
 					}
 				}
 			}