Merge branch 'master' of ssh://apples.lambdacomplex.org/git/disclosr
Merge branch 'master' of ssh://apples.lambdacomplex.org/git/disclosr


Former-commit-id: d943194e8dfa31e9f1d3ccea08fd7be419d00c67

--- a/.gitmodules
+++ b/.gitmodules
@@ -19,4 +19,7 @@
 [submodule "javascripts/sigma"]
 	path = javascripts/sigma
 	url = https://github.com/jacomyal/sigma.js.git
+[submodule "javascripts/bubbletree"]
+	path = javascripts/bubbletree
+	url = https://github.com/okfn/bubbletree.git
 

file:a/about.php -> file:b/about.php
--- a/about.php
+++ b/about.php
@@ -1,16 +1,16 @@
 <?php
 include_once('include/common.inc.php');
-include_header();
+include_header('About');
 ?>
 <div class="foundation-header">
     <h1><a href="about.php">About/FAQ</a></h1>
     <h4 class="subheader">Lorem ipsum.</h4>
 </div>
 <h2> What is this? </h2>
-Disclosr is a project to monitor Australian Federal Government agencies 
+Disclo.gs is a project to monitor Australian Federal Government agencies 
 compliance with their <a href="http://www.oaic.gov.au/publications/other_operational/foi_policy_frequently_asked_questions.html#_Toc291837571">"proactive disclosure requirements"</a>.
-OGRE (Open Government Realization Evaluation) is a ranking of compliance with these requirements.
-Prometheus is the agent which polls agency websites to assess compliance.
+<h2> Attributions </h2>
+National Archives of Australia, Australian Governments’ Interactive Functions Thesaurus, 2nd edition, September 2005, published at http://www.naa.gov.au/recordkeeping/thesaurus/index.htm.
 
 <h2> Open everything </h2>
 All documents released CC-BY 3 AU

--- /dev/null
+++ b/admin/conflicts.php
@@ -1,1 +1,48 @@
+<?php
 
+include_once('../include/common.inc.php');
+include_header();
+                require_once '../lib/php-diff/lib/Diff.php';
+                require_once '../lib/php-diff/lib/Diff/Renderer/Html/SideBySide.php';
+
+$db = $server->get_db('disclosr-agencies');
+
+try {
+    $rows = $db->get_view("app", "getConflicts", null, true)->rows;
+    //print_r($rows);
+    foreach ($rows as $row) {
+echo "<h2>".$row->id."</h2>";
+$request = Requests::get($serverAddr."disclosr-agencies/".$row->id);
+$origSort = object_to_array(json_decode($request->body));
+ksort($origSort);
+    $origDoc = explode(",",json_encode($origSort));
+	foreach($row->value as $conflictRev) {
+$conflictURL = $serverAddr."disclosr-agencies/".$row->id."?rev=".$conflictRev;
+$request = Requests::get($conflictURL);
+$conflictSort = object_to_array(json_decode($request->body));
+ksort($conflictSort);
+    $conflictDoc = explode(",",json_encode($conflictSort));
+echo "curl -X DELETE ".$conflictURL."<br>".PHP_EOL;
+                // Options for generating the diff
+                $options = array(
+                        //'ignoreWhitespace' => true,
+                        //'ignoreCase' => true,
+                );
+
+                // Initialize the diff class
+                $diff = new Diff($conflictDoc, $origDoc, $options);
+
+                // Generate a side by side diff
+                $renderer = new Diff_Renderer_Html_SideBySide;
+                echo $diff->Render($renderer);
+}
+die();
+	
+    }
+} catch (SetteeRestClientException $e) {
+    setteErrorHandler($e);
+}
+
+include_footer();
+?>
+

--- /dev/null
+++ b/admin/directory.gexf.php
@@ -1,1 +1,59 @@
+<?php
 
+$nodes = Array(Array("id" => "gov", "label" => "Federal Government"));
+$edges = Array();
+
+function addEdge($source, $target) {
+    global $edges;
+    $edges[] = Array("id" => md5($source . $target), "source" => $source, "target" => $target);
+}
+
+function addNode($id, $label, $pid) {
+    global $nodes;
+    $nodes[] = Array("id" => $id, "label" => $label , "pid" => $pid);
+}
+
+function addChildren($parentID, $parentXML) {
+    foreach ($parentXML as $childXML) {
+
+        if ($childXML->getName() == "organization" || $childXML->getName() == "organizationalUnit" || $childXML->getName() == "person") {
+            $attr = $childXML->attributes();
+            $id = $attr['UUID'];
+            if ($childXML->getName() == "organization" || $childXML->getName() == "organizationalUnit") {
+
+                $label = $childXML->name;
+            } else if ($childXML->getName() == "person") {
+                  $label = $childXML->fullName;
+            }
+            addNode($id, $label, $parentID);
+            addEdge($id, $parentID);
+            addChildren($id, $childXML);
+        }
+    }
+}
+
+if (file_exists('directoryexport.xml')) {
+    $xml = simplexml_load_file('directoryexport.xml');
+
+    addChildren("gov", $xml);
+} else {
+    exit('Failed to open directoryexport.xml');
+}
+  header('Content-Type: application/gexf+xml');
+echo '<?xml version="1.0" encoding="UTF-8"?>
+<gexf xmlns="http://www.gexf.net/1.2draft" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd" version="1.2">
+    <graph mode="static" defaultedgetype="directed">
+        <nodes>';
+foreach ($nodes as $node) {
+    echo '          <node id="' . $node['id'] . '" label="' . htmlentities($node['label'],ENT_XML1) . '" ' . (isset($node['pid']) ? 'pid="' . $node['pid'] . '"' : "") . ' />';
+}
+echo '</nodes>
+        <edges>';
+foreach ($edges as $edge) {
+    echo '            <edge id="' . $edge['id'] . '" source="' . $edge['source'] . '" target="' . $edge['target'] . '" />';
+}
+echo '</edges>
+    </graph>
+</gexf>';
+?>
+

--- /dev/null
+++ b/admin/directoryexport.xml

--- a/admin/exportEmployees.csv.php
+++ b/admin/exportEmployees.csv.php
@@ -5,7 +5,6 @@
 $format = "csv";
 //$format = "json";
 if (isset($_REQUEST['format']))  $format = $_REQUEST['format'];
-
 setlocale(LC_CTYPE, 'C');
 if ($format == "csv") {
     $headers = Array("name");

--- a/admin/importAPSCEmployees.php
+++ b/admin/importAPSCEmployees.php
@@ -32,23 +32,35 @@
                 @$sums[$id][$timePeriod] += $data[1];
             } else {
                 echo "<br>ERROR NAME MISSING FROM ID LIST<br><bR>" . PHP_EOL;
-                
+
                 die();
-               
             }
         }
         fclose($handle);
     }
 }
 foreach ($sums as $id => $sum) {
-    echo $id. "<br>" . PHP_EOL;
+    echo $id . "<br>" . PHP_EOL;
     $doc = $db->get($id);
-   // print_r($doc);
-    if (isset($doc->statistics)) $doc->statistics = Array();
+     echo $doc->name . "<br>" . PHP_EOL;
+    // print_r($doc);
+    $changed = false;
+    if (!isset($doc->statistics)) {
+        $changed = true;
+        $doc->statistics = Array();
+    }
     foreach ($sum as $timePeriod => $value) {
-        $doc->statistics["employees"][$timePeriod] = Array("value"=>$value, "source"=>"http://apsc.gov.au/stateoftheservice/");
+        if (!isset($doc->statistics->employees->$timePeriod->value) 
+                || $doc->statistics->employees->$timePeriod->value != $value) {
+            $changed = true;
+            $doc->statistics["employees"][$timePeriod] = Array("value" => $value, "source" => "http://apsc.gov.au/stateoftheservice/");
+        }
     }
-    $db->save($doc);
+    if ($changed) {
+        $db->save($doc);
+    } else {
+        echo "not changed" . "<br>" . PHP_EOL;
+    }
 }
 // employees: timeperiod, source = apsc state of service, value 
 ?>

file:b/admin/metadata.py (new)
--- /dev/null
+++ b/admin/metadata.py
@@ -1,1 +1,22 @@
+#http://packages.python.org/CouchDB/client.html
+import couchdb
+from BeautifulSoup import BeautifulSoup
 
+couch = couchdb.Server('http://127.0.0.1:5984/')
+
+# select database
+docsdb = couch['disclosr-documents']
+
+for row in docsdb.view('app/getMetadataExtractRequired'): 
+    print row.id
+    html = docsdb.get_attachment(row.id,row.value.iterkeys().next()).read()
+    metadata = []
+     # http://www.crummy.com/software/BeautifulSoup/documentation.html
+            soup = BeautifulSoup(html)
+metatags = soup.meta
+    for metatag in metatags:
+        print metatag['name']
+    doc = docsdb.get(row.id)
+    //doc['metadata'] = metadata
+    //docsdb.save(doc)
+

 Binary files /dev/null and b/admin/naa-agift.7z differ
--- /dev/null
+++ b/admin/naa-agift.json
@@ -1,1 +1,2232 @@
-
+[
+   "Home",
+   "index.htm",
+   [
+      "BUSINESS SUPPORT AND REGULATION",
+      "000411.htm",
+      [
+         "Association registration",
+         "000396.htm"
+      ],
+      [
+         "Business process auditing",
+         "000413.htm"
+      ],
+      [
+         "Business registration and licensing",
+         "000414.htm"
+      ],
+      [
+         "Business sponsorship",
+         "001371.htm"
+      ],
+      [
+         "Consumer protection",
+         "000439.htm"
+      ],
+      [
+         "Fair trading compliance",
+         "000482.htm",
+         [
+            "Prices surveillance",
+            "000533.htm"
+         ],
+         [
+            "Product safety",
+            "000534.htm"
+         ],
+         [
+            "Trade practices compliance",
+            "000583.htm"
+         ]
+      ],
+      [
+         "Financial institutions regulation",
+         "000485.htm"
+      ],
+      [
+         "Government procurement regulation",
+         "000491.htm"
+      ],
+      [
+         "Industry assistance schemes",
+         "000499.htm"
+      ],
+      [
+         "Industry development",
+         "000500.htm"
+      ],
+      [
+         "Insurance regulation",
+         "000501.htm",
+         [
+            "General insurance",
+            "000489.htm"
+         ],
+         [
+            "Life insurance",
+            "000505.htm"
+         ]
+      ],
+      [
+         "Professional accreditation",
+         "000535.htm"
+      ],
+      [
+         "Small business services",
+         "000565.htm",
+         [
+            "Small business advocacy",
+            "000563.htm"
+         ],
+         [
+            "Small business development",
+            "000564.htm"
+         ]
+      ],
+      [
+         "Stock market regulation",
+         "000572.htm"
+      ]
+   ],
+   [
+      "CIVIC INFRASTRUCTURE",
+      "000424.htm",
+      [
+         "Civic management",
+         "000425.htm",
+         [
+            "Architectural services",
+            "000387.htm"
+         ],
+         [
+            "Building approval services",
+            "000406.htm"
+         ],
+         [
+            "Building regulations and standards",
+            "000408.htm"
+         ],
+         [
+            "Engineering services",
+            "000477.htm"
+         ],
+         [
+            "Town planning",
+            "000582.htm"
+         ]
+      ],
+      [
+         "Energy supply",
+         "000476.htm"
+      ],
+      [
+         "Integrated services planning",
+         "000503.htm"
+      ],
+      [
+         "Public housing",
+         "000536.htm",
+         [
+            "Public housing construction",
+            "000537.htm"
+         ],
+         [
+            "Public housing design",
+            "000538.htm"
+         ],
+         [
+            "Public housing maintenance",
+            "000540.htm"
+         ]
+      ],
+      [
+         "Public land management",
+         "000541.htm",
+         [
+            "Burial ground management",
+            "000410.htm"
+         ],
+         [
+            "Cultural centre management",
+            "000444.htm"
+         ],
+         [
+            "Garden management",
+            "000488.htm"
+         ],
+         [
+            "Memorial maintenance",
+            "000509.htm"
+         ],
+         [
+            "Recreational park management",
+            "000549.htm"
+         ],
+         [
+            "Sporting facilities management",
+            "000571.htm"
+         ]
+      ],
+      [
+         "Regional development",
+         "000552.htm"
+      ],
+      [
+         "Transport network maintenance",
+         "000586.htm"
+      ],
+      [
+         "Waste management",
+         "001349.htm"
+      ]
+   ],
+   [
+      "COMMUNICATIONS",
+      "000433.htm",
+      [
+         "Advertising standards",
+         "000381.htm"
+      ],
+      [
+         "Broadcasting",
+         "000403.htm",
+         [
+            "Broadcasting standards",
+            "000404.htm"
+         ],
+         [
+            "Radio broadcasting",
+            "000546.htm"
+         ],
+         [
+            "Television broadcasting",
+            "000580.htm"
+         ]
+      ],
+      [
+         "Call centre administration",
+         "000417.htm"
+      ],
+      [
+         "Electronic commerce",
+         "000468.htm",
+         [
+            "Authentication",
+            "000399.htm"
+         ],
+         [
+            "Online transaction standards",
+            "000526.htm"
+         ]
+      ],
+      [
+         "Government media",
+         "000490.htm"
+      ],
+      [
+         "Information management standards",
+         "000283.htm",
+         [
+            "Data management",
+            "000448.htm"
+         ],
+         [
+            "Information dissemination",
+            "000502.htm"
+         ],
+         [
+            "Information technology standards",
+            "000282.htm"
+         ]
+      ],
+      [
+         "Media ownership regulation",
+         "000508.htm"
+      ],
+      [
+         "Postal services",
+         "000530.htm",
+         [
+            "Courier services",
+            "000441.htm"
+         ],
+         [
+            "Electronic postal services",
+            "000469.htm"
+         ],
+         [
+            "Retail postal services",
+            "000554.htm"
+         ]
+      ],
+      [
+         "Publishing",
+         "000543.htm",
+         [
+            "Electronic publishing",
+            "000470.htm"
+         ],
+         [
+            "Publishing standards",
+            "000544.htm"
+         ],
+         [
+            "Website development",
+            "000591.htm"
+         ]
+      ],
+      [
+         "Radio communication",
+         "000547.htm",
+         [
+            "Apparatus licensing",
+            "000385.htm"
+         ],
+         [
+            "Spectrum management",
+            "000570.htm"
+         ]
+      ],
+      [
+         "Satellite communication",
+         "000560.htm"
+      ],
+      [
+         "Telecommunications",
+         "000578.htm",
+         [
+            "Carriage service providers",
+            "000420.htm"
+         ],
+         [
+            "Carrier licensing",
+            "000421.htm"
+         ],
+         [
+            "Equipment licensing",
+            "000480.htm"
+         ],
+         [
+            "Mobile telephone services",
+            "000516.htm"
+         ],
+         [
+            "Telephone services",
+            "000579.htm"
+         ]
+      ]
+   ],
+   [
+      "COMMUNITY SERVICES",
+      "000435.htm",
+      [
+         "Accommodation services",
+         "000377.htm",
+         [
+            "Defence housing",
+            "000458.htm"
+         ],
+         [
+            "Emergency accommodation",
+            "000471.htm"
+         ],
+         [
+            "Public housing entitlements",
+            "000539.htm"
+         ],
+         [
+            "Refuge support",
+            "000551.htm"
+         ]
+      ],
+      [
+         "Community support",
+         "000436.htm",
+         [
+            "Adoption services",
+            "000378.htm"
+         ],
+         [
+            "Aged care services",
+            "000382.htm"
+         ],
+         [
+            "Child and youth support",
+            "000422.htm"
+         ],
+         [
+            "Child-care services",
+            "000423.htm"
+         ],
+         [
+            "Defence community programs",
+            "000452.htm"
+         ],
+         [
+            "Family reunion programs",
+            "000483.htm"
+         ],
+         [
+            "Veterans\" entitlements",
+            "000587.htm"
+         ]
+      ],
+      [
+         "Counselling services",
+         "000440.htm"
+      ],
+      [
+         "Emergency services",
+         "000474.htm",
+         [
+            "Ambulance services",
+            "000384.htm"
+         ],
+         [
+            "Emergency funding",
+            "000472.htm"
+         ],
+         [
+            "Firefighting services",
+            "000487.htm"
+         ]
+      ],
+      [
+         "Financial assistance",
+         "000484.htm",
+         [
+            "Benefits",
+            "000402.htm"
+         ],
+         [
+            "Income support schemes",
+            "000494.htm"
+         ]
+      ],
+      [
+         "Natural disasters",
+         "000521.htm",
+         [
+            "Disaster recovery",
+            "000462.htm"
+         ],
+         [
+            "Disaster relief",
+            "000463.htm"
+         ]
+      ],
+      [
+         "Rural community development",
+         "000557.htm"
+      ],
+      [
+         "Social justice and equity",
+         "000566.htm"
+      ],
+      [
+         "Transport access schemes",
+         "000901.htm"
+      ]
+   ],
+   [
+      "CULTURAL AFFAIRS",
+      "000442.htm",
+      [
+         "Arts development",
+         "000391.htm",
+         [
+            "Arts funding",
+            "000393.htm"
+         ],
+         [
+            "Arts incentive schemes",
+            "000394.htm"
+         ],
+         [
+            "Arts promotion",
+            "000395.htm"
+         ]
+      ],
+      [
+         "Collection access",
+         "000427.htm",
+         [
+            "Descriptive standards",
+            "000461.htm"
+         ],
+         [
+            "Finding aids development",
+            "000486.htm"
+         ],
+         [
+            "Reference services",
+            "000550.htm"
+         ]
+      ],
+      [
+         "Collection management",
+         "000430.htm",
+         [
+            "Artefact export regulation",
+            "000390.htm"
+         ],
+         [
+            "Collection accessioning",
+            "000428.htm"
+         ],
+         [
+            "Collection acquisition",
+            "000429.htm"
+         ],
+         [
+            "Collection storage",
+            "000432.htm"
+         ],
+         [
+            "Preservation services",
+            "000532.htm"
+         ]
+      ],
+      [
+         "Collection promotion",
+         "000431.htm",
+         [
+            "Exhibition programs",
+            "000481.htm"
+         ],
+         [
+            "Publicity programs",
+            "000542.htm"
+         ]
+      ],
+      [
+         "Cultural awards and scholarships",
+         "000443.htm"
+      ],
+      [
+         "Cultural festivals",
+         "000445.htm"
+      ],
+      [
+         "Cultural gifts programs",
+         "000446.htm"
+      ],
+      [
+         "Multicultural heritage promotion",
+         "000518.htm",
+         [
+            "Multicultural festivals",
+            "000517.htm"
+         ],
+         [
+            "Multicultural services",
+            "000519.htm"
+         ]
+      ]
+   ],
+   [
+      "DEFENCE",
+      "000449.htm",
+      [
+         "Australian Defence Forces",
+         "000397.htm",
+         [
+            "Air Force",
+            "002266.htm"
+         ],
+         [
+            "Army",
+            "000388.htm"
+         ],
+         [
+            "Badges and insignia",
+            "000401.htm"
+         ],
+         [
+            "Cadets",
+            "000416.htm"
+         ],
+         [
+            "Defence force commands",
+            "000456.htm"
+         ],
+         [
+            "Military bands",
+            "000510.htm"
+         ],
+         [
+            "Navy",
+            "000524.htm"
+         ],
+         [
+            "Reserves",
+            "000553.htm"
+         ],
+         [
+            "Roulettes",
+            "000556.htm"
+         ]
+      ],
+      [
+         "Defence estate management",
+         "000454.htm"
+      ],
+      [
+         "Defence force careers",
+         "000455.htm",
+         [
+            "Defence career development",
+            "000450.htm"
+         ],
+         [
+            "Military employment services",
+            "000512.htm"
+         ]
+      ],
+      [
+         "Defence strategic development",
+         "000574.htm",
+         [
+            "Defence efficiency review",
+            "000453.htm"
+         ],
+         [
+            "Defence reform program",
+            "000460.htm"
+         ]
+      ],
+      [
+         "Defence strategic policy",
+         "000576.htm",
+         [
+            "Strategic decisions",
+            "000573.htm"
+         ],
+         [
+            "Strategic planning",
+            "000575.htm"
+         ]
+      ],
+      [
+         "Defence strategic support",
+         "000577.htm",
+         [
+            "Capital equipment programs",
+            "000419.htm"
+         ],
+         [
+            "Defence industry",
+            "000459.htm"
+         ],
+         [
+            "Defence science and technology",
+            "000905.htm"
+         ],
+         [
+            "Logistics",
+            "000506.htm"
+         ]
+      ],
+      [
+         "Emergency management",
+         "000473.htm",
+         [
+            "Civil community assistance",
+            "000426.htm"
+         ],
+         [
+            "Disaster support",
+            "000464.htm"
+         ]
+      ],
+      [
+         "Military law",
+         "001814.htm"
+      ],
+      [
+         "Military operations",
+         "000515.htm",
+         [
+            "Australian theatre of war",
+            "000398.htm"
+         ],
+         [
+            "Military exercises",
+            "000514.htm"
+         ],
+         [
+            "Special operations",
+            "000569.htm"
+         ],
+         [
+            "Warfare",
+            "000589.htm"
+         ]
+      ]
+   ],
+   [
+      "EDUCATION AND TRAINING",
+      "000467.htm",
+      [
+         "Arts education",
+         "000392.htm"
+      ],
+      [
+         "Community education",
+         "000434.htm",
+         [
+            "Adult education programs",
+            "000379.htm"
+         ],
+         [
+            "Adult migrant education",
+            "000380.htm"
+         ],
+         [
+            "Road safety awareness",
+            "000555.htm"
+         ]
+      ],
+      [
+         "Curriculum development",
+         "000447.htm"
+      ],
+      [
+         "Early childhood education",
+         "000466.htm"
+      ],
+      [
+         "Military education and training",
+         "000511.htm",
+         [
+            "Army reserve training",
+            "000389.htm"
+         ],
+         [
+            "Defence college training",
+            "000451.htm"
+         ]
+      ],
+      [
+         "Overseas skills recognition",
+         "000527.htm"
+      ],
+      [
+         "School education",
+         "000561.htm",
+         [
+            "Discipline programs",
+            "000465.htm"
+         ],
+         [
+            "School transport regulation",
+            "000562.htm"
+         ],
+         [
+            "Special needs programs",
+            "000568.htm"
+         ]
+      ],
+      [
+         "Tertiary education",
+         "000581.htm"
+      ],
+      [
+         "Vocational education",
+         "000588.htm",
+         [
+            "Apprenticeship programs",
+            "000386.htm"
+         ],
+         [
+            "Hospital teaching programs",
+            "000493.htm"
+         ],
+         [
+            "Trainee programs",
+            "000584.htm"
+         ],
+         [
+            "Workplace training",
+            "000595.htm"
+         ]
+      ]
+   ],
+   [
+      "EMPLOYMENT",
+      "000016.htm",
+      [
+         "Human resources development",
+         "000348.htm",
+         [
+            "Occupational health and safety",
+            "000355.htm"
+         ],
+         [
+            "Recruitment programs",
+            "000356.htm"
+         ],
+         [
+            "Termination processes",
+            "002103.htm"
+         ],
+         [
+            "Workers compensation schemes",
+            "000363.htm"
+         ],
+         [
+            "Workplace discrimination monitoring",
+            "000364.htm"
+         ],
+         [
+            "Workplace equity and justice programs",
+            "000365.htm"
+         ]
+      ],
+      [
+         "Industrial awards and conditions",
+         "000349.htm",
+         [
+            "Award conditions",
+            "000366.htm"
+         ],
+         [
+            "Remuneration review",
+            "000368.htm"
+         ]
+      ],
+      [
+         "Labour market programs",
+         "000347.htm",
+         [
+            "Employment services marketing",
+            "000351.htm"
+         ],
+         [
+            "Job placement programs",
+            "000352.htm"
+         ],
+         [
+            "Job vacancy data management",
+            "000353.htm"
+         ]
+      ],
+      [
+         "Workplace agreement processes",
+         "000350.htm",
+         [
+            "Agreement bargaining processes",
+            "002141.htm"
+         ],
+         [
+            "Agreement dispute mediation",
+            "000370.htm"
+         ],
+         [
+            "Certified employment conditions",
+            "000371.htm"
+         ],
+         [
+            "Employment advocacy services",
+            "000374.htm"
+         ]
+      ]
+   ],
+   [
+      "ENVIRONMENT",
+      "000478.htm",
+      [
+         "Built environment",
+         "000409.htm",
+         [
+            "Building acoustics",
+            "000405.htm"
+         ],
+         [
+            "Building preservation",
+            "000407.htm"
+         ]
+      ],
+      [
+         "Climate information services",
+         "000203.htm"
+      ],
+      [
+         "Conservation programs",
+         "000438.htm",
+         [
+            "Endangered species protection",
+            "000475.htm"
+         ],
+         [
+            "Landcare programs",
+            "000504.htm"
+         ],
+         [
+            "Soil preservation programs",
+            "000567.htm"
+         ]
+      ],
+      [
+         "Environmental impact assessment",
+         "000479.htm"
+      ],
+      [
+         "Historic relic protection",
+         "000492.htm"
+      ],
+      [
+         "Marine life protection programs",
+         "000507.htm"
+      ],
+      [
+         "Natural heritage protection",
+         "000522.htm",
+         [
+            "National park management",
+            "000520.htm"
+         ],
+         [
+            "Nature reserve management",
+            "000523.htm"
+         ],
+         [
+            "Wilderness area management",
+            "000593.htm"
+         ],
+         [
+            "Wildlife sanctuary management",
+            "000594.htm"
+         ]
+      ],
+      [
+         "Oceans governance",
+         "000525.htm"
+      ],
+      [
+         "Pollutant prevention programs",
+         "000529.htm"
+      ],
+      [
+         "World heritage listings",
+         "000596.htm"
+      ]
+   ],
+   [
+      "FINANCE MANAGEMENT",
+      "000001.htm",
+      [
+         "Commonwealth State funding",
+         "000017.htm"
+      ],
+      [
+         "Currency",
+         "000018.htm",
+         [
+            "Counterfeiting control",
+            "000027.htm"
+         ]
+      ],
+      [
+         "Financial investment",
+         "000021.htm",
+         [
+            "Investment scheme regulation",
+            "000031.htm"
+         ]
+      ],
+      [
+         "Financial system management",
+         "002302.htm",
+         [
+            "Financial system monitoring",
+            "002306.htm"
+         ],
+         [
+            "Payments management",
+            "002297.htm"
+         ]
+      ],
+      [
+         "Fiscal policy",
+         "000022.htm",
+         [
+            "Public borrowing",
+            "000032.htm"
+         ],
+         [
+            "Public debt",
+            "000033.htm"
+         ],
+         [
+            "Public expenditure",
+            "000034.htm"
+         ]
+      ],
+      [
+         "Foreign investment control",
+         "000030.htm"
+      ],
+      [
+         "International monetary regulation",
+         "000023.htm"
+      ],
+      [
+         "Monetary policy",
+         "000020.htm",
+         [
+            "Exchange rates",
+            "000028.htm"
+         ],
+         [
+            "Inflation",
+            "000029.htm"
+         ],
+         [
+            "Interest rates",
+            "002295.htm"
+         ]
+      ],
+      [
+         "Resource management",
+         "000024.htm",
+         [
+            "Contract management",
+            "000037.htm"
+         ],
+         [
+            "Financial administration",
+            "000035.htm"
+         ],
+         [
+            "Financial budgeting",
+            "000036.htm"
+         ]
+      ],
+      [
+         "Retirement income",
+         "000025.htm",
+         [
+            "Asset assessment",
+            "000038.htm"
+         ],
+         [
+            "Retirement saving",
+            "000039.htm"
+         ],
+         [
+            "Superannuation regulation",
+            "000040.htm"
+         ]
+      ],
+      [
+         "Statistical analysis",
+         "000019.htm"
+      ],
+      [
+         "Taxation",
+         "000026.htm",
+         [
+            "Income assessment",
+            "000041.htm"
+         ],
+         [
+            "Revenue raising",
+            "000042.htm"
+         ],
+         [
+            "Taxation compliance",
+            "000043.htm"
+         ]
+      ]
+   ],
+   [
+      "GOVERNANCE",
+      "000002.htm",
+      [
+         "Civic celebrations",
+         "000046.htm"
+      ],
+      [
+         "Constitutional matters",
+         "000048.htm",
+         [
+            "Constitutional conventions",
+            "000061.htm"
+         ],
+         [
+            "Constitutional referenda",
+            "000062.htm"
+         ]
+      ],
+      [
+         "Electoral matters",
+         "000049.htm",
+         [
+            "Declaration of interests",
+            "000063.htm"
+         ],
+         [
+            "Election campaigning",
+            "000064.htm"
+         ],
+         [
+            "Electoral boundary assessment",
+            "000065.htm"
+         ]
+      ],
+      [
+         "Honours and awards programs",
+         "000050.htm"
+      ],
+      [
+         "Intergovernmental relations",
+         "000052.htm",
+         [
+            "Cross-border cooperation",
+            "000067.htm"
+         ],
+         [
+            "Intergovernment policy dissemination",
+            "000069.htm"
+         ]
+      ],
+      [
+         "Legislative drafting",
+         "000053.htm",
+         [
+            "Draft Bill amendment process",
+            "000070.htm"
+         ],
+         [
+            "Legislation review",
+            "000071.htm"
+         ],
+         [
+            "Preparation of legislative regulations",
+            "000072.htm"
+         ]
+      ],
+      [
+         "Official protocol",
+         "000054.htm",
+         [
+            "Ceremonial events and representation",
+            "000073.htm"
+         ],
+         [
+            "Head of Government protocol",
+            "000074.htm"
+         ],
+         [
+            "Official establishment management",
+            "000075.htm"
+         ],
+         [
+            "Official hospitality",
+            "000076.htm"
+         ],
+         [
+            "Official visits",
+            "000077.htm"
+         ]
+      ],
+      [
+         "Parliamentary chamber support",
+         "000045.htm",
+         [
+            "Hansard services",
+            "000058.htm"
+         ],
+         [
+            "Parliamentary papers",
+            "000059.htm"
+         ],
+         [
+            "Tabling of official documents",
+            "000060.htm"
+         ]
+      ],
+      [
+         "Parliamentary committee and member support",
+         "000047.htm"
+      ],
+      [
+         "Population census",
+         "000044.htm",
+         [
+            "Demographic surveys",
+            "000055.htm"
+         ],
+         [
+            "Household census",
+            "000056.htm"
+         ],
+         [
+            "Population distribution analysis",
+            "000057.htm"
+         ]
+      ],
+      [
+         "Public administration",
+         "002290.htm",
+         [
+            "Government auditing",
+            "002291.htm"
+         ],
+         [
+            "Public service",
+            "002292.htm"
+         ],
+         [
+            "Territories administration",
+            "002293.htm"
+         ]
+      ],
+      [
+         "Statistics",
+         "002289.htm"
+      ]
+   ],
+   [
+      "HEALTH CARE",
+      "000003.htm",
+      [
+         "Community health services",
+         "000078.htm",
+         [
+            "Clinical health services",
+            "000087.htm"
+         ],
+         [
+            "Health promotion",
+            "000089.htm"
+         ],
+         [
+            "Primary health care",
+            "000090.htm"
+         ]
+      ],
+      [
+         "Health insurance schemes",
+         "000079.htm"
+      ],
+      [
+         "Health protocol administration",
+         "000080.htm",
+         [
+            "Drugs and poisons regulation",
+            "000091.htm"
+         ],
+         [
+            "Food hygiene regulation",
+            "000092.htm"
+         ],
+         [
+            "Practitioner discipline",
+            "000093.htm"
+         ],
+         [
+            "Practitioner registration",
+            "000095.htm"
+         ]
+      ],
+      [
+         "Hospital services",
+         "000081.htm",
+         [
+            "Hospital administration",
+            "000096.htm"
+         ],
+         [
+            "Patient care",
+            "000097.htm"
+         ],
+         [
+            "Pharmaceutical and drug support",
+            "000098.htm"
+         ]
+      ],
+      [
+         "Medical research",
+         "000083.htm",
+         [
+            "Ethical compliance",
+            "000099.htm"
+         ],
+         [
+            "Medical aids regulation",
+            "000100.htm"
+         ],
+         [
+            "Medical research funding",
+            "000101.htm"
+         ],
+         [
+            "National referral laboratory services",
+            "000102.htm"
+         ],
+         [
+            "Population-based research",
+            "000103.htm"
+         ]
+      ],
+      [
+         "Public health services",
+         "000084.htm",
+         [
+            "Epidemiology",
+            "000104.htm"
+         ],
+         [
+            "Health disaster preparedness",
+            "000106.htm"
+         ],
+         [
+            "Health hazard prevention",
+            "000105.htm"
+         ],
+         [
+            "Medical quarantine services",
+            "000108.htm"
+         ],
+         [
+            "Notifiable disease management",
+            "000109.htm"
+         ]
+      ],
+      [
+         "Special needs services",
+         "000085.htm",
+         [
+            "Dental health services",
+            "000111.htm"
+         ],
+         [
+            "Independent living services",
+            "000112.htm"
+         ],
+         [
+            "Mental health services",
+            "000113.htm"
+         ],
+         [
+            "Residential services",
+            "000114.htm"
+         ]
+      ],
+      [
+         "Veterans\" health services",
+         "000086.htm"
+      ]
+   ],
+   [
+      "IMMIGRATION",
+      "000004.htm",
+      [
+         "Citizenship",
+         "000115.htm",
+         [
+            "Naturalisation assessment",
+            "000121.htm"
+         ],
+         [
+            "Presentation arrangements",
+            "000122.htm"
+         ]
+      ],
+      [
+         "Deportation",
+         "000116.htm"
+      ],
+      [
+         "Detention programs",
+         "000117.htm"
+      ],
+      [
+         "Migrant services",
+         "000118.htm",
+         [
+            "Interpreter services",
+            "000123.htm"
+         ],
+         [
+            "Migrant accommodation services",
+            "000125.htm"
+         ],
+         [
+            "Migrant settlement programs",
+            "000126.htm"
+         ]
+      ],
+      [
+         "Refugee services",
+         "000119.htm"
+      ],
+      [
+         "Travel authorisation",
+         "000120.htm",
+         [
+            "Permanent entry arrangements",
+            "000127.htm"
+         ],
+         [
+            "Temporary entry arrangements",
+            "000128.htm"
+         ]
+      ]
+   ],
+   [
+      "INDIGENOUS AFFAIRS",
+      "002285.htm",
+      [
+         "Indigenous cultural heritage",
+         "000495.htm",
+         [
+            "Cultural artefact repatriation",
+            "001772.htm"
+         ]
+      ],
+      [
+         "Indigenous enterprise development",
+         "000496.htm"
+      ],
+      [
+         "Indigenous health services",
+         "000082.htm"
+      ],
+      [
+         "Indigenous heritage conservation",
+         "000497.htm"
+      ],
+      [
+         "Indigenous reconciliation",
+         "000051.htm"
+      ],
+      [
+         "Indigenous welfare programs",
+         "000498.htm"
+      ]
+   ],
+   [
+      "INTERNATIONAL RELATIONS",
+      "000005.htm",
+      [
+         "Consular services",
+         "000129.htm"
+      ],
+      [
+         "Defence liaison",
+         "000132.htm",
+         [

+            "000140.htm"
+         ],
+         [
+            "Defence cooperation programs",
+            "000141.htm"
+         ],
+         [
+            "Defence personnel exchanges",
+            "000142.htm"
+         ],
+         [
+            "Military advisors",
+            "000143.htm"
+         ]
+      ],
+      [
+         "Diplomatic missions",
+         "000130.htm"
+      ],
+      [
+         "International affairs",
+         "000131.htm",
+         [
+            "Disarmament and arms control",
+            "000137.htm"
+         ],
+         [
+            "Nuclear safeguards",
+            "000139.htm"
+         ]
+      ],
+      [
+         "International treaties",
+         "000133.htm",
+         [
+            "Treaty administration",
+            "001384.htm"
+         ]
+      ],
+      [
+         "Overseas aid programs",
+         "000134.htm",
+         [
+            "Development assistance programs",
+            "000146.htm"
+         ],
+         [
+            "Overseas student scholarship programs",
+            "000147.htm"
+         ]
+      ],
+      [
+         "Overseas promotion",
+         "000135.htm"
+      ],
+      [
+         "Passport services",
+         "000136.htm"
+      ]
+   ],
+   [
+      "JUSTICE ADMINISTRATION",
+      "000006.htm",
+      [
+         "Administrative law",
+         "000149.htm",
+         [
+            "Administrative decision appeal",
+            "000163.htm"
+         ],
+         [
+            "Administrative decision review",
+            "000164.htm"
+         ],
+         [
+            "Censorship standards",
+            "000165.htm"
+         ],
+         [
+            "Copyright regulation",
+            "000166.htm"
+         ],
+         [
+            "Recordkeeping standards",
+            "000167.htm"
+         ]
+      ],
+      [
+         "Associations and corporate law",
+         "000150.htm",
+         [
+            "Bankruptcy proceedings",
+            "000168.htm"
+         ],
+         [
+            "Merger regulation",
+            "000169.htm"
+         ]
+      ],
+      [
+         "Civil law",
+         "000151.htm",
+         [
+            "Civil registration",
+            "000170.htm"
+         ],
+         [
+            "Conveyancing",
+            "000171.htm"
+         ],
+         [
+            "Dispute mediation programs",
+            "000174.htm"
+         ],
+         [
+            "Family law proceedings",
+            "000172.htm"
+         ],
+         [
+            "Probate applications",
+            "000173.htm"
+         ]
+      ],
+      [
+         "Commissions of inquiry",
+         "000152.htm"
+      ],
+      [
+         "Coronial law",
+         "000153.htm"
+      ],
+      [
+         "Court reporting",
+         "000154.htm"
+      ],
+      [
+         "Criminal law",
+         "000155.htm"
+      ],
+      [
+         "Human rights obligations",
+         "000156.htm"
+      ],
+      [
+         "Juvenile justice",
+         "000157.htm"
+      ],
+      [
+         "Legal aid services",
+         "000158.htm"
+      ],
+      [
+         "Local laws and ordinances",
+         "000159.htm"
+      ],
+      [
+         "Native title claims",
+         "000160.htm",
+         [
+            "Native title mediation programs",
+            "000180.htm"
+         ],
+         [
+            "Native title settlement negotiations",
+            "000181.htm"
+         ]
+      ],
+      [
+         "Privacy guideline monitoring",
+         "000161.htm"
+      ],
+      [
+         "Prosecution services",
+         "000162.htm"
+      ]
+   ],
+   [
+      "MARITIME SERVICES",
+      "000007.htm",
+      [
+         "Cargo control",
+         "000198.htm"
+      ],
+      [
+         "Harbour management",
+         "000183.htm"
+      ],
+      [
+         "Marina management",
+         "000184.htm"
+      ],
+      [
+         "Navigation",
+         "000185.htm",
+         [
+            "Charting sea lanes",
+            "000191.htm"
+         ],
+         [
+            "Light station maintenance",
+            "000192.htm"
+         ],
+         [
+            "Navigation systems development",
+            "000193.htm"
+         ]
+      ],
+      [
+         "Pilotage management",
+         "000182.htm"
+      ],
+      [
+         "Port regulation",
+         "001471.htm"
+      ],
+      [
+         "Sea passengers",
+         "001487.htm"
+      ],
+      [
+         "Search and rescue",
+         "000187.htm",
+         [
+            "Rescue coordination",
+            "000194.htm"
+         ],
+         [
+            "Search technology development",
+            "000195.htm"
+         ]
+      ],
+      [
+         "Ship personnel",
+         "000188.htm",
+         [
+            "Personnel safety standards",
+            "000196.htm"
+         ],
+         [
+            "Pilot licensing",
+            "000197.htm"
+         ]
+      ],
+      [
+         "Ship safety",
+         "000189.htm",
+         [
+            "Seaworthiness",
+            "000201.htm"
+         ],
+         [
+            "Ship commissioning",
+            "000199.htm"
+         ],
+         [
+            "Ship inspection",
+            "000200.htm"
+         ]
+      ]
+   ],
+   [
+      "NATURAL RESOURCES",
+      "000009.htm",
+      [
+         "Crown land administration",
+         "000218.htm"
+      ],
+      [
+         "Energy resources",
+         "001451.htm",
+         [
+            "Fossil fuel management",
+            "000226.htm"
+         ],
+         [
+            "Renewable energy development",
+            "000227.htm"
+         ]
+      ],
+      [
+         "Land use planning",
+         "000220.htm",
+         [
+            "Land use zoning",
+            "001429.htm"
+         ]
+      ],
+      [
+         "Land valuation",
+         "000222.htm"
+      ],
+      [
+         "Mineral resources",
+         "000223.htm",
+         [
+            "Land rehabilitation programs",
+            "000231.htm"
+         ],
+         [
+            "Mining regulations",
+            "000232.htm"
+         ]
+      ],
+      [
+         "Pollution emission control",
+         "000224.htm"
+      ],
+      [
+         "Water resources",
+         "000225.htm",
+         [
+            "Water conservation plans",
+            "000233.htm"
+         ],
+         [
+            "Water quality monitoring",
+            "000234.htm"
+         ],
+         [
+            "Water usage management",
+            "000235.htm"
+         ],
+         [
+            "Waterway management",
+            "000236.htm"
+         ]
+      ]
+   ],
+   [
+      "PRIMARY INDUSTRIES",
+      "000008.htm",
+      [
+         "Chemical and pesticide regulation",
+         "000202.htm"
+      ],
+      [
+         "Marine and rural regulation",
+         "000204.htm",
+         [
+            "Agricultural industry",
+            "000208.htm"
+         ],
+         [
+            "Fisheries industry",
+            "000209.htm"
+         ],
+         [
+            "Food quality assurance",
+            "000213.htm"
+         ],
+         [
+            "Forestry industry",
+            "000210.htm"
+         ]
+      ],
+      [
+         "Marine and rural support",
+         "000205.htm",
+         [
+            "Aquaculture industry",
+            "000212.htm"
+         ],
+         [
+            "Horticultural industry",
+            "000214.htm"
+         ],
+         [
+            "Pastoral industry",
+            "000215.htm"
+         ],
+         [
+            "Silvicultural industry",
+            "000216.htm"
+         ],
+         [
+            "Viticultural industry",
+            "000217.htm"
+         ]
+      ],
+      [
+         "Quarantine",
+         "000211.htm"
+      ],
+      [
+         "Rural field day promotion",
+         "000206.htm"
+      ],
+      [
+         "Rural partnership programs",
+         "000207.htm"
+      ]
+   ],
+   [
+      "SCIENCE",
+      "000010.htm",
+      [
+         "Agricultural sciences",
+         "000237.htm"
+      ],
+      [
+         "Animal and veterinary sciences",
+         "000238.htm"
+      ],
+      [
+         "Applied sciences",
+         "000239.htm",
+         [
+            "Engineering and technology sciences",
+            "000246.htm"
+         ],
+         [
+            "Weights and measures standards",
+            "000247.htm"
+         ]
+      ],
+      [
+         "Biological sciences",
+         "000240.htm",
+         [
+            "Biochemistry",
+            "000248.htm"
+         ],
+         [
+            "Biodiversity preservation",
+            "000249.htm"
+         ],
+         [
+            "Biotechnology development",
+            "000250.htm"
+         ],
+         [
+            "Botany",
+            "000251.htm"
+         ],
+         [
+            "Entomology",
+            "000252.htm"
+         ],
+         [
+            "Genetic engineering",
+            "000253.htm"
+         ],
+         [
+            "Physiology",
+            "000254.htm"
+         ]
+      ],
+      [
+         "Earth sciences",
+         "000241.htm",
+         [
+            "Atmospheric sciences",
+            "000255.htm"
+         ],
+         [
+            "Hydrology",
+            "000256.htm"
+         ],
+         [
+            "Mineral exploration",
+            "000257.htm"
+         ],
+         [
+            "Oceanography",
+            "000258.htm"
+         ],
+         [
+            "Seismology",
+            "000259.htm"
+         ]
+      ],
+      [
+         "Mathematical sciences",
+         "000242.htm"
+      ],
+      [
+         "Medical and health sciences",
+         "001229.htm"
+      ],
+      [
+         "Physical sciences",
+         "000244.htm",
+         [
+            "Astronomical sciences",
+            "000260.htm"
+         ],
+         [
+            "Atomic and molecular sciences",
+            "000261.htm"
+         ],
+         [
+            "Nuclear sciences",
+            "000262.htm"
+         ]
+      ],
+      [
+         "Spatial information research",
+         "000245.htm"
+      ]
+   ],
+   [
+      "SECURITY",
+      "000011.htm",
+      [
+         "Corrective services",
+         "000263.htm",
+         [
+            "Community based corrections",
+            "000270.htm"
+         ],
+         [
+            "Detention centre management",
+            "000271.htm"
+         ]
+      ],
+      [
+         "External security",
+         "000264.htm",
+         [
+            "Peacekeeping forces",
+            "000273.htm"
+         ]
+      ],
+      [
+         "Information security",
+         "000265.htm"
+      ],
+      [
+         "Intelligence",
+         "000266.htm",
+         [
+            "Forensic analysis",
+            "000275.htm"
+         ],
+         [
+            "Intelligence liaison",
+            "001159.htm"
+         ],
+         [
+            "Intelligence support",
+            "000277.htm"
+         ]
+      ],
+      [
+         "Law enforcement",
+         "000267.htm",
+         [
+            "Community policing",
+            "000278.htm"
+         ],
+         [
+            "Criminology",
+            "000279.htm"
+         ],
+         [
+            "Police administration",
+            "000280.htm"
+         ],
+         [
+            "Protective services",
+            "000281.htm"
+         ]
+      ],
+      [
+         "National security",
+         "001548.htm",
+         [
+            "Coastal surveillance",
+            "000269.htm"
+         ]
+      ]
+   ],
+   [
+      "SPORT AND RECREATION",
+      "000012.htm",
+      [
+         "Caravan and camping services",
+         "000284.htm"
+      ],
+      [
+         "Community recreational programs",
+         "000285.htm",
+         [
+            "Gambling support services",
+            "000290.htm"
+         ],
+         [
+            "Public fitness promotion",
+            "000291.htm"
+         ],
+         [
+            "Social club support",
+            "000292.htm"
+         ],
+         [
+            "Volunteer support programs",
+            "000293.htm"
+         ]
+      ],
+      [
+         "Gaming industry regulation",
+         "000286.htm"
+      ],
+      [
+         "Park and reserve services",
+         "000289.htm"
+      ],
+      [
+         "Sport and fitness development",
+         "000287.htm",
+         [
+            "Athlete scholarship programs",
+            "000295.htm"
+         ],
+         [
+            "Fundraising and donation schemes",
+            "000296.htm"
+         ],
+         [
+            "Games administration",
+            "000297.htm"
+         ],
+         [
+            "Games promotion",
+            "000298.htm"
+         ],
+         [
+            "Sporting grants administration",
+            "000299.htm"
+         ]
+      ],
+      [
+         "Sports drugs monitoring",
+         "000288.htm",
+         [
+            "Doping detection research",
+            "000300.htm"
+         ]
+      ]
+   ],
+   [
+      "TOURISM",
+      "000013.htm",
+      [
+         "Tourism industry development",
+         "000301.htm"
+      ],
+      [
+         "Tourist event promotion",
+         "000302.htm",
+         [
+            "Advertising campaigns",
+            "000305.htm"
+         ]
+      ],
+      [
+         "Travel missions",
+         "000303.htm"
+      ]
+   ],
+   [
+      "TRADE",
+      "000014.htm",
+      [
+         "Export regulation",
+         "000311.htm",
+         [
+            "Export promotion",
+            "000320.htm"
+         ],
+         [
+            "Prohibited export control",
+            "000321.htm"
+         ]
+      ],
+      [
+         "Import regulation",
+         "000312.htm",
+         [
+            "Prohibited import control",
+            "000323.htm"
+         ],
+         [
+            "Tariff regulation",
+            "000322.htm"
+         ]
+      ],
+      [
+         "Intellectual property management",
+         "000910.htm",
+         [
+            "Copyright registration",
+            "000308.htm"
+         ],
+         [
+            "Design registration",
+            "000310.htm"
+         ],
+         [
+            "Patent registration",
+            "000315.htm"
+         ],
+         [
+            "Trademark registration",
+            "000318.htm"
+         ]
+      ],
+      [
+         "International trade agreements",
+         "000313.htm"
+      ],
+      [
+         "Interstate trade agreements",
+         "000314.htm"
+      ],
+      [
+         "Trade development programs",
+         "000316.htm"
+      ],
+      [
+         "Trade expositions",
+         "000317.htm"
+      ]
+   ],
+   [
+      "TRANSPORT",
+      "000015.htm",
+      [
+         "Air transport",
+         "000324.htm",
+         [
+            "Air transport safety",
+            "000332.htm"
+         ],
+         [
+            "Aircraft standards",
+            "000330.htm"
+         ],
+         [
+            "Airport services",
+            "000331.htm"
+         ]
+      ],
+      [
+         "Freight movement regulation",
+         "000325.htm"
+      ],
+      [
+         "Passenger services",
+         "000326.htm"
+      ],
+      [
+         "Rail transport",
+         "000327.htm",
+         [
+            "Rail harmonisation standards",
+            "000333.htm"
+         ],
+         [
+            "Rail land acquisition regulation",
+            "000336.htm"
+         ],
+         [
+            "Rail transport safety",
+            "000337.htm"
+         ],
+         [
+            "Railway maintenance",
+            "000338.htm"
+         ],
+         [
+            "Rolling stock regulation",
+            "000339.htm"
+         ]
+      ],
+      [
+         "Road transport",
+         "000328.htm",
+         [
+            "Driving licences administration",
+            "000340.htm"
+         ],
+         [
+            "Road surface maintenance",
+            "000341.htm"
+         ],
+         [
+            "Road traffic regulation",
+            "000342.htm"
+         ],
+         [
+            "Road transport safety",
+            "000343.htm"
+         ],
+         [
+            "Vehicle registration",
+            "000344.htm"
+         ],
+         [
+            "Vehicle standards",
+            "000345.htm"
+         ]
+      ],
+      [
+         "Transport infrastructure development",
+         "000329.htm"
+      ]
+   ]
+]

file:b/bubbletree.php (new)
--- /dev/null
+++ b/bubbletree.php
@@ -1,1 +1,126 @@
 
+<!DOCTYPE html> 
+<html xmlns="http://www.w3.org/1999/xhtml"> 
+    <head> 
+        <meta charset="UTF-8"/> 
+        <title>Minimal BubbleTree Demo</title> 
+        <script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.js"></script> 
+        <script type="text/javascript" src="javascripts/bubbletree/lib/jquery.history.js"></script> 
+        <script type="text/javascript" src="javascripts/bubbletree/lib/raphael.js"></script> 
+        <script type="text/javascript" src="javascripts/bubbletree/lib/vis4.js"></script> 
+        <script type="text/javascript" src="javascripts/bubbletree/lib/Tween.js"></script> 
+        <script type="text/javascript" src="javascripts/bubbletree/build/bubbletree.js"></script> 
+        <link rel="stylesheet" type="text/css" href="javascripts/bubbletree/build/bubbletree.css" /> 
+        <script type="text/javascript" src="javascripts/bubbletree/styles/cofog.js"></script> 
+
+
+        <script type="text/javascript"> 
+       
+            $(function() {
+<?php
+include_once('include/common.inc.php');
+
+include("lib/Color.php");
+$color = new Lux_Color();
+
+$portfolios = Array();
+$total = 0;
+$db = $server->get_db('disclosr-agencies');
+try {
+    $rows = $db->get_view("app", "byDeptStateName", null, true)->rows;
+    foreach ($rows as $row) {
+        $portfolios[trim(str_replace(Array("Department of", "Department", "the", "'", "`"), "", $row->key))] = $row->value;
+    }
+} catch (SetteeRestClientException $e) {
+    setteErrorHandler($e);
+}
+
+$agencies = Array();
+try {
+    $rows = $db->get_view("app", "byCanonicalName", null, true)->rows;
+//print_r($rows);
+    foreach ($rows as $row) {
+        $employees = 0;
+        $portfolioid = 0;
+        if (isset($row->value->employees)) {
+            $employees = $row->value->employees;
+        }
+        if (isset($row->value->statistics->employees)) {
+            $agencyEmployeesArray = object_to_array($row->value->statistics->employees);
+            if (isset($agencyEmployeesArray["2010-2011"]["value"])) {
+                $employees = $agencyEmployeesArray["2010-2011"]["value"];
+            } else {
+                // bailout for agencies that are closed for business
+                continue;
+            }
+        }
+        if (!($employees > 0)) {
+            $employees = 0;
+        }
+        if (isset($row->value->parentOrg)) {
+            $portfolioid = $row->value->parentOrg;
+        }
+        if (isset($row->value->orgType) && $row->value->orgType == "FMA-DepartmentOfState") {
+            $portfolioid = $row->id;
+        }
+        $agencies[$portfolioid][$row->value->name] = $employees;
+    }
+} catch (SetteeRestClientException $e) {
+    setteErrorHandler($e);
+}
+//print_r($portfolios);
+//print_r($agencies);
+
+// http://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
+$golden_ratio_conjugate = 0.618033988749895;
+$h = 0.00+rand(0,10)/10; # use random start value
+foreach ($portfolios as $portfolioName => $portfolioID) {
+  $h += $golden_ratio_conjugate;
+  
+  $h =  fmod($h,1);
+    $portfolioColor = $color->hsv2hex(Array($h, .3, .99));
+    $subnodes = Array();
+    $portfolioEmployees = 0;
+    foreach ($agencies[$portfolioID] as $agencyName => $agencyEmployees) {
+        $agencyColor = $color->hsv2hex(Array($h / 10, rand(1, 10) / 10, abs(($h * (1 / 10)) - .5) + .5));
+        $subnodes[] = Array(
+            "label" => str_replace(Array("'", "`"), "", $agencyName),
+            "amount" => $agencyEmployees,
+            //"color" => "#" . $agencyColor
+        );
+        $portfolioEmployees += $agencyEmployees;
+    }
+    $nodes[] = Array(
+        "label" => $portfolioName,
+        "amount" => $portfolioEmployees,
+        //"color" => "#" . $portfolioColor,
+        "children" => $subnodes
+    );
+    $total += $portfolioEmployees;
+}
+$data = Array(
+    "label" => "Australian Federal Government",
+    "amount" => $total,
+    //"color" => "#000000",
+    "children" => $nodes
+);
+echo "var data =eval('('+'" . json_encode($data) . "'+')');";
+?>
+
+        new BubbleTree({
+            data: data,
+            container: '.bubbletree'
+        });
+		
+			
+    });
+     
+        </script> 
+    </head> 
+    <body> 
+        <div class="bubbletree-wrapper"> 
+            <div class="bubbletree"></div> 
+        </div> 
+    </body> 
+</html> 
+

file:a/charts.php -> file:b/charts.php
--- a/charts.php
+++ b/charts.php
@@ -1,6 +1,6 @@
 <?php
 include_once('include/common.inc.php');
-include_header();
+include_header('Charts');
 $db = $server->get_db('disclosr-agencies');
 ?>
 <div class="foundation-header">

--- a/getAgency.php
+++ b/getAgency.php
@@ -1,7 +1,7 @@
 <?php
 
 include_once('include/common.inc.php');
-include_header();
+
 
 function displayValue($key, $value, $mode) {
     global $db, $schemas;
@@ -108,6 +108,7 @@
 // edit?
 
     $obj = $db->get($_REQUEST['id']);
+    include_header($obj->name);
 //print_r($row);
     if (sizeof($_POST) > 0) {
 //print_r($_POST);
@@ -137,7 +138,7 @@
         }
     }
 
-    $mode = "edit";
+    $mode = "view";
     $rowArray = object_to_array($obj);
 ksort($rowArray);
     if ($mode == "edit") {
@@ -185,7 +186,8 @@
             echo '<input id="submitbutton" type="submit"/></form>';
         }
     } else {
-
+// show all list
+        include_header('Agencies');
         try {
             /* $rows = $db->get_view("app", "showNamesABNs")->rows;
               //print_r($rows);

--- /dev/null
+++ b/google676a414ad086cefb.html
@@ -1,1 +1,2 @@
+google-site-verification: google676a414ad086cefb.html
 

file:a/graph.php -> file:b/graph.php
--- a/graph.php
+++ b/graph.php
@@ -113,12 +113,12 @@
  sigInst.bind('downnodes',function(event){
     var nodes = event.content;
  });
-  // Draw the graph :
-  sigInst.draw();
   // Start the ForceAtlas2 algorithm
   // (requires "sigma.forceatlas2.js" to be included)
   sigInst.startForceAtlas2();
   
+  // Draw the graph :
+  sigInst.draw();
 }
 
 if (document.addEventListener) {

--- a/include/common.inc.php
+++ b/include/common.inc.php
@@ -51,6 +51,9 @@
 function phrase_to_tag ($phrase) {
     return str_replace(" ","_",str_replace("'","",str_replace(",","",strtolower($phrase))));
 }
+function local_url() {
+    return "http://" . $_SERVER['HTTP_HOST'] . rtrim(dirname($_SERVER['PHP_SELF']), '/\\') . "/";
+}
 function GetDomain($url)
 {
 $nowww = ereg_replace('www\.','',$url);

--- a/include/couchdb.inc.php
+++ b/include/couchdb.inc.php
@@ -5,28 +5,28 @@
 require ($basePath . 'couchdb/settee/src/settee.php');
 
 function createDocumentsDesignDoc() {
-    /*"views": {
-       "web_server": {
-           "map": "function(doc) {\n  emit(doc.web_server, 1);\n}",
-           "reduce": "function (key, values, rereduce) {\n    return sum(values);\n}"
-       },
-       "byAgency": {
-           "map": "function(doc) {\n  emit(doc.agencyID, 1);\n}",
-           "reduce": "function (key, values, rereduce) {\n    return sum(values);\n}"
-       },
-       "byURL": {
-           "map": "function(doc) {\n  emit(doc.url, doc);\n}"
-       },
-       "agency": {
-           "map": "function(doc) {\n  emit(doc.agencyID, doc);\n}"
-       },
-       "byWebServer": {
-           "map": "function(doc) {\n  emit(doc.web_server, doc);\n}"
-       },
-  "getValidationRequired": {
-       "map": "function(doc) {\nif (doc.mime_type == \"text/html\" \n&& typeof(doc.validation) == \"undefined\") {\n  emit(doc._id, doc._attachments);\n}\n}"
-   }
-   }*/
+    /* "views": {
+      "web_server": {
+      "map": "function(doc) {\n  emit(doc.web_server, 1);\n}",
+      "reduce": "function (key, values, rereduce) {\n    return sum(values);\n}"
+      },
+      "byAgency": {
+      "map": "function(doc) {\n  emit(doc.agencyID, 1);\n}",
+      "reduce": "function (key, values, rereduce) {\n    return sum(values);\n}"
+      },
+      "byURL": {
+      "map": "function(doc) {\n  emit(doc.url, doc);\n}"
+      },
+      "agency": {
+      "map": "function(doc) {\n  emit(doc.agencyID, doc);\n}"
+      },
+      "byWebServer": {
+      "map": "function(doc) {\n  emit(doc.web_server, doc);\n}"
+      },
+      "getValidationRequired": {
+      "map": "function(doc) {\nif (doc.mime_type == \"text/html\" \n&& typeof(doc.validation) == \"undefined\") {\n  emit(doc._id, doc._attachments);\n}\n}"
+      }
+      } */
 }
 
 function createAgencyDesignDoc() {
@@ -95,7 +95,7 @@
   }
 }";
     // http://stackoverflow.com/questions/646628/javascript-startswith
-$obj->views->score->map =  'if(!String.prototype.startsWith){
+    $obj->views->score->map = 'if(!String.prototype.startsWith){
     String.prototype.startsWith = function (str) {
         return !this.indexOf(str);
     }
@@ -119,7 +119,7 @@
         emit(count+doc._id, {id:doc._id, name: doc.name, score:count, orgType: doc.orgType, portfolio:portfolio});
     }
 }';
-        $obj->views->scoreHas->map = 'if(!String.prototype.startsWith){
+    $obj->views->scoreHas->map = 'if(!String.prototype.startsWith){
     String.prototype.startsWith = function (str) {
         return !this.indexOf(str);
     }
@@ -142,7 +142,7 @@
     $obj->views->scoreHas->reduce = 'function (key, values, rereduce) {
     return sum(values);
 }';
-        $obj->views->fieldNames->map = '
+    $obj->views->fieldNames->map = '
 function(doc) {
 for(var propName in doc) {
      	emit(propName, doc._id);
@@ -157,8 +157,7 @@
 }
 
 if (php_uname('n') == "vanille") {
-$serverAddr = 'http://192.168.178.21:5984/';
-   
+    $serverAddr = 'http://192.168.178.21:5984/';
 } else
 if (php_uname('n') == "KYUUBEY") {
 
@@ -166,7 +165,8 @@
 } else {
     $serverAddr = 'http://127.0.0.1:5984/';
 }
- $server = new SetteeServer($serverAddr);
+$server = new SetteeServer($serverAddr);
+
 function setteErrorHandler($e) {
     echo $e->getMessage() . "<br>" . PHP_EOL;
 }

--- a/include/template.inc.php
+++ b/include/template.inc.php
@@ -1,6 +1,6 @@
 <?php
 
-function include_header() {
+function include_header($title) {
     global $basePath;
     ?>
     <!DOCTYPE html>
@@ -16,7 +16,7 @@
             <!-- Set the viewport width to device width for mobile -->
             <meta name="viewport" content="width=device-width" />
 
-            <title>Disclosr</title>
+            <title><?php echo $title; ?> - Disclosr</title>
 
             <!-- Included CSS Files -->
             <link rel="stylesheet" href="<?php echo $basePath ?>stylesheets/foundation.css">
@@ -71,9 +71,27 @@
            <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
             
     <script type="text/javascript" src="javascripts/flotr2/flotr2.js"></script>
+        <?php
+        if (strpos($_SERVER['SERVER_NAME'], ".gs")) {
+            ?>
+    <script type="text/javascript">
+
+  var _gaq = _gaq || [];
+  _gaq.push(['_setAccount', 'UA-12341040-2']);
+  _gaq.push(['_trackPageview']);
+
+  (function() {
+    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+  })();
+
+</script>
 
         </body>
     </html>
 
 <?php }
+            }
+            
 

file:b/index.php (new)
--- /dev/null
+++ b/index.php
@@ -1,1 +1,9 @@
+<?php
+/* Redirect to a different page in the current directory that was requested */
+$host  = $_SERVER['HTTP_HOST'];
+$uri   = rtrim(dirname($_SERVER['PHP_SELF']), '/\\');
+$extra = 'getAgency.php';
+header("Location: http://$host$uri/$extra");
+exit;
+?>
 

directory:b/javascripts/bubbletree (new)
--- /dev/null
+++ b/javascripts/bubbletree

--- a/javascripts/flotr2
+++ b/javascripts/flotr2

directory:a/javascripts/sigma -> directory:b/javascripts/sigma
--- a/javascripts/sigma
+++ b/javascripts/sigma

--- a/javascripts/sigma.min.js
+++ b/javascripts/sigma.min.js
@@ -1,63 +1,3552 @@
-/* sigmajs.org - an open-source light-weight JavaScript graph drawing library - Version: 0.1 - Author:  Alexis Jacomy - License: MIT */
-var sigma={tools:{},classes:{},instances:{}};
-(function(){if(!Array.prototype.some)Array.prototype.some=function(i,n){var g=this.length;if("function"!=typeof i)throw new TypeError;for(var j=0;j<g;j++)if(j in this&&i.call(n,this[j],j,this))return!0;return!1};if(!Array.prototype.forEach)Array.prototype.forEach=function(i,n){var g=this.length;if("function"!=typeof i)throw new TypeError;for(var j=0;j<g;j++)j in this&&i.call(n,this[j],j,this)};if(!Array.prototype.map)Array.prototype.map=function(i,n){var g=this.length;if("function"!=typeof i)throw new TypeError;
-for(var j=Array(g),m=0;m<g;m++)m in this&&(j[m]=i.call(n,this[m],m,this));return j};if(!Array.prototype.filter)Array.prototype.filter=function(i,n){var g=this.length;if("function"!=typeof i)throw new TypeError;for(var j=[],m=0;m<g;m++)if(m in this){var t=this[m];i.call(n,t,m,this)&&j.push(t)}return j};if(!Object.keys)Object.keys=function(){var i=Object.prototype.hasOwnProperty,n=!{toString:null}.propertyIsEnumerable("toString"),g="toString,toLocaleString,valueOf,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,constructor".split(","),
-j=g.length;return function(m){if("object"!==typeof m&&"function"!==typeof m||null===m)throw new TypeError("Object.keys called on non-object");var t=[],p;for(p in m)i.call(m,p)&&t.push(p);if(n)for(p=0;p<j;p++)i.call(m,g[p])&&t.push(g[p]);return t}}()})();sigma.classes.Cascade=function(){this.p={};this.config=function(i,n){if("string"==typeof i&&void 0==n)return this.p[i];var g="object"==typeof i&&void 0==n?i:{};"string"==typeof i&&(g[i]=n);for(var j in g)void 0!=this.p[j]&&(this.p[j]=g[j]);return this}};
-sigma.classes.EventDispatcher=function(){var i={},n=this;this.one=function(g,j){if(!j||!g)return n;("string"==typeof g?g.split(" "):g).forEach(function(g){i[g]||(i[g]=[]);i[g].push({h:j,one:!0})});return n};this.bind=function(g,j){if(!j||!g)return n;("string"==typeof g?g.split(" "):g).forEach(function(g){i[g]||(i[g]=[]);i[g].push({h:j,one:!1})});return n};this.unbind=function(g,j){g||(i={});var m="string"==typeof g?g.split(" "):g;j?m.forEach(function(g){i[g]&&(i[g]=i[g].filter(function(g){return g.h!=
-j}));i[g]&&0==i[g].length&&delete i[g]}):m.forEach(function(g){delete i[g]});return n};this.dispatch=function(g,j){i[g]&&(i[g].forEach(function(i){i.h({type:g,content:j,target:n})}),i[g]=i[g].filter(function(g){return!g.one}));return n}};
-(function(){var i;function n(){function b(a){return{x:a.x,y:a.y,size:a.size,degree:a.degree,displayX:a.displayX,displayY:a.displayY,displaySize:a.displaySize,label:a.label,id:a.id,color:a.color,fixed:a.fixed,active:a.active,hidden:a.hidden,attr:a.attr}}function h(a){return{source:a.source.id,target:a.target.id,size:a.size,type:a.type,weight:a.weight,displaySize:a.displaySize,label:a.label,id:a.id,attr:a.attr,color:a.color}}function e(){d.nodes=[];d.nodesIndex={};d.edges=[];d.edgesIndex={};return d}
-sigma.classes.Cascade.call(this);sigma.classes.EventDispatcher.call(this);var d=this;this.p={minNodeSize:0,maxNodeSize:0,minEdgeSize:0,maxEdgeSize:0,scalingMode:"inside",nodesPowRatio:0.5,edgesPowRatio:0};this.borders={};e();this.addNode=function(a,b){if(d.nodesIndex[a])throw Error('Node "'+a+'" already exists.');var b=b||{},c={x:0,y:0,size:1,degree:0,fixed:!1,active:!1,hidden:!1,label:a.toString(),id:a.toString(),attr:{}},e;for(e in b)switch(e){case "id":break;case "x":case "y":case "size":c[e]=
-+b[e];break;case "fixed":case "active":case "hidden":c[e]=!!b[e];break;case "color":case "label":c[e]=b[e];break;default:c.attr[e]=b[e]}d.nodes.push(c);d.nodesIndex[a.toString()]=c;return d};this.addEdge=function(a,b,c,e){if(d.edgesIndex[a])throw Error('Edge "'+a+'" already exists.');if(!d.nodesIndex[b])throw Error("Edge's source \""+b+'" does not exist yet.');if(!d.nodesIndex[c])throw Error("Edge's target \""+c+'" does not exist yet.');e=e||{};b={source:d.nodesIndex[b],target:d.nodesIndex[c],size:1,
-weight:1,displaySize:0.5,label:a.toString(),id:a.toString(),attr:{}};b.source.degree++;b.target.degree++;for(var k in e)switch(k){case "id":case "source":case "target":break;case "size":b[k]=+e[k];break;case "color":b[k]=e[k].toString();break;case "type":b[k]=e[k].toString();break;case "label":b[k]=e[k];break;default:b.attr[k]=e[k]}d.edges.push(b);d.edgesIndex[a.toString()]=b;return d};this.dropNode=function(a){((a instanceof Array?a:[a])||[]).forEach(function(a){if(d.nodesIndex[a]){var c=null;d.nodes.some(function(b,
-d){return b.id==a?(c=d,!0):!1});null!=c&&d.nodes.splice(c,1);delete d.nodesIndex[a];d.edges=d.edges.filter(function(c){return c.source.id==a||c.target.id==a?(delete d.edgesIndex[c.id],!1):!0})}else sigma.log('Node "'+a+'" does not exist.')});return d};this.dropEdge=function(a){((a instanceof Array?a:[a])||[]).forEach(function(a){if(d.edgesIndex[a]){var c=null;d.edges.some(function(b,d){return b.id==a?(c=d,!0):!1});null!=c&&d.edges.splice(c,1);delete d.edgesIndex[a]}else sigma.log('Edge "'+a+'" does not exist.')});
-return d};this.iterEdges=function(a,b){var c=b?b.map(function(a){return d.edgesIndex[a]}):d.edges,e=c.map(h);e.forEach(a);c.forEach(function(a,c){var b=e[c],l;for(l in b)switch(l){case "id":case "weight":case "displaySize":break;case "size":a[l]=+b[l];break;case "source":case "target":a[l]=d.nodesIndex[l]||a[l];break;case "color":case "label":case "type":a[l]=(b[l]||"").toString();break;default:a.attr[l]=b[l]}});return d};this.iterNodes=function(a,e){var c=e?e.map(function(a){return d.nodesIndex[a]}):
-d.nodes,h=c.map(b);h.forEach(a);c.forEach(function(a,c){var b=h[c],d;for(d in b)switch(d){case "id":case "attr":case "degree":case "displayX":case "displayY":case "displaySize":break;case "x":case "y":case "size":a[d]=+b[d];break;case "fixed":case "active":case "hidden":a[d]=!!b[d];break;case "color":case "label":a[d]=b[d].toString();break;default:a.attr[d]=b[d]}});return d};this.getEdges=function(a){var b=((a instanceof Array?a:[a])||[]).map(function(a){return h(d.edgesIndex[a])});return a instanceof
-Array?b:b[0]};this.getNodes=function(a){var e=((a instanceof Array?a:[a])||[]).map(function(a){return b(d.nodesIndex[a])});return a instanceof Array?e:e[0]};this.empty=e;this.rescale=function(a,b,c,e){var k=0,f=0;c&&d.nodes.forEach(function(a){f=Math.max(a.size,f)});e&&d.edges.forEach(function(a){k=Math.max(a.size,k)});var f=f||1,k=k||1,h,g,s,q;c&&d.nodes.forEach(function(a){g=Math.max(a.x,g||a.x);h=Math.min(a.x,h||a.x);q=Math.max(a.y,q||a.y);s=Math.min(a.y,s||a.y)});var z="outside"==d.p.scalingMode?
-Math.max(a/Math.max(g-h,1),b/Math.max(q-s,1)):Math.min(a/Math.max(g-h,1),b/Math.max(q-s,1)),i,j;!d.p.maxNodeSize&&!d.p.minNodeSize?(i=1,j=0):d.p.maxNodeSize==d.p.minNodeSize?(i=0,j=d.p.maxNodeSize):(i=(d.p.maxNodeSize-d.p.minNodeSize)/f,j=d.p.minNodeSize);var m,w;!d.p.maxEdgeSize&&!d.p.minEdgeSize?(m=1,w=0):(m=d.p.maxEdgeSize==d.p.minEdgeSize?0:(d.p.maxEdgeSize-d.p.minEdgeSize)/k,w=d.p.minEdgeSize);c&&d.nodes.forEach(function(c){c.displaySize=c.size*i+j;if(!c.fixed)c.displayX=(c.x-(g+h)/2)*z+a/2,
-c.displayY=(c.y-(q+s)/2)*z+b/2});e&&d.edges.forEach(function(a){a.displaySize=a.size*m+w});return d};this.translate=function(a,b,c,e,k){var f=Math.pow(c,d.p.nodesPowRatio);e&&d.nodes.forEach(function(d){if(!d.fixed)d.displayX=d.displayX*c+a,d.displayY=d.displayY*c+b;d.displaySize*=f});f=Math.pow(c,d.p.edgesPowRatio);k&&d.edges.forEach(function(a){a.displaySize*=f});return d};this.setBorders=function(){d.borders={};d.nodes.forEach(function(a){d.borders.minX=Math.min(void 0==d.borders.minX?a.displayX-
-a.displaySize:d.borders.minX,a.displayX-a.displaySize);d.borders.maxX=Math.max(void 0==d.borders.maxX?a.displayX+a.displaySize:d.borders.maxX,a.displayX+a.displaySize);d.borders.minY=Math.min(void 0==d.borders.minY?a.displayY-a.displaySize:d.borders.minY,a.displayY-a.displaySize);d.borders.maxY=Math.max(void 0==d.borders.maxY?a.displayY-a.displaySize:d.borders.maxY,a.displayY-a.displaySize)})};this.checkHover=function(a,b){var c,e,k,f=[],h=[];d.nodes.forEach(function(d){if(d.hidden)d.hover=!1;else{c=
-Math.abs(d.displayX-a);e=Math.abs(d.displayY-b);k=d.displaySize;var s=d.hover,q=c<k&&e<k&&Math.sqrt(c*c+e*e)<k;if(s&&!q)d.hover=!1,h.push(d.id);else if(q&&!s)d.hover=!0,f.push(d.id)}});f.length&&d.dispatch("overnodes",f);h.length&&d.dispatch("outnodes",h);return d}}function g(b,h){function e(){var a;a="<p>GLOBAL :</p>";for(var b in d.p.globalProbes)a+="<p>"+b+" : "+d.p.globalProbes[b]()+"</p>";a+="<br><p>LOCAL :</p>";for(b in d.p.localProbes)a+="<p>"+b+" : "+d.p.localProbes[b]()+"</p>";d.p.dom.innerHTML=
-a;return d}sigma.classes.Cascade.call(this);var d=this;this.instance=b;this.monitoring=!1;this.p={fps:40,dom:h,globalProbes:{"Time (ms)":sigma.chronos.getExecutionTime,Queue:sigma.chronos.getQueuedTasksCount,Tasks:sigma.chronos.getTasksCount,FPS:sigma.chronos.getFPS},localProbes:{"Nodes count":function(){return d.instance.graph.nodes.length},"Edges count":function(){return d.instance.graph.edges.length}}};this.activate=function(){if(!d.monitoring)d.monitoring=window.setInterval(e,1E3/d.p.fps);return d};
-this.desactivate=function(){if(d.monitoring)window.clearInterval(d.monitoring),d.monitoring=null,d.p.dom.innerHTML="";return d}}function j(b){function h(b){if(a.p.mouseEnabled&&(e(a.mouseX,a.mouseY,a.ratio*(0<(void 0!=b.wheelDelta&&b.wheelDelta||void 0!=b.detail&&-b.detail)?a.p.zoomMultiply:1/a.p.zoomMultiply)),a.p.blockScroll))b.preventDefault?b.preventDefault():b.returnValue=!1}function e(b,c,e){if(!a.isMouseDown&&(window.clearInterval(a.interpolationID),n=void 0!=e,i=a.stageX,j=b,k=a.stageY,l=
-c,f=e||a.ratio,f=Math.min(Math.max(f,a.p.minRatio),a.p.maxRatio),u=a.p.directZooming?1-(n?a.p.zoomDelta:a.p.dragDelta):0,a.ratio!=f||a.stageX!=j||a.stageY!=l))d(),a.interpolationID=window.setInterval(d,50),a.dispatch("startinterpolate")}function d(){u+=n?a.p.zoomDelta:a.p.dragDelta;u=Math.min(u,1);var b=sigma.easing.quadratic.easeout(u),c=a.ratio;a.ratio=c*(1-b)+f*b;n?(a.stageX=j+(a.stageX-j)*a.ratio/c,a.stageY=l+(a.stageY-l)*a.ratio/c):(a.stageX=i*(1-b)+j*b,a.stageY=k*(1-b)+l*b);a.dispatch("interpolate");
-if(1<=u)window.clearInterval(a.interpolationID),b=a.ratio,n?(a.ratio=f,a.stageX=j+(a.stageX-j)*a.ratio/b,a.stageY=l+(a.stageY-l)*a.ratio/b):(a.stageX=j,a.stageY=l),a.dispatch("stopinterpolate")}sigma.classes.Cascade.call(this);sigma.classes.EventDispatcher.call(this);var a=this;this.p={minRatio:1,maxRatio:32,marginRatio:1,zoomDelta:0.1,dragDelta:0.3,zoomMultiply:2,directZooming:!1,blockScroll:!0,inertia:1.1,mouseEnabled:!0};var g=0,c=0,i=0,k=0,f=1,j=0,l=0,s=0,q=0,z=0,m=0,u=0,n=!1;this.stageY=this.stageX=
-0;this.ratio=1;this.mouseY=this.mouseX=0;this.isMouseDown=!1;b.addEventListener("DOMMouseScroll",h,!0);b.addEventListener("mousewheel",h,!0);b.addEventListener("mousemove",function(b){a.mouseX=void 0!=b.offsetX&&b.offsetX||void 0!=b.layerX&&b.layerX||void 0!=b.clientX&&b.clientX;a.mouseY=void 0!=b.offsetY&&b.offsetY||void 0!=b.layerY&&b.layerY||void 0!=b.clientY&&b.clientY;if(a.isMouseDown){var d=a.mouseX-g+i,f=a.mouseY-c+k;if(d!=a.stageX||f!=a.stageY)q=s,m=z,s=d,z=f,a.stageX=d,a.stageY=f,a.dispatch("drag")}a.dispatch("move");
-b.preventDefault?b.preventDefault():b.returnValue=!1},!0);b.addEventListener("mousedown",function(b){if(a.p.mouseEnabled)a.isMouseDown=!0,a.dispatch("mousedown"),i=a.stageX,k=a.stageY,g=a.mouseX,c=a.mouseY,q=s=a.stageX,m=z=a.stageY,a.dispatch("startdrag"),b.preventDefault?b.preventDefault():b.returnValue=!1},!0);document.addEventListener("mouseup",function(b){if(a.p.mouseEnabled&&a.isMouseDown)a.isMouseDown=!1,a.dispatch("mouseup"),(i!=a.stageX||k!=a.stageY)&&e(a.stageX+a.p.inertia*(a.stageX-q),a.stageY+
-a.p.inertia*(a.stageY-m)),b.preventDefault?b.preventDefault():b.returnValue=!1},!0);this.checkBorders=function(){return a};this.interpolate=e}function m(b,h,e,d,a,g,c){function i(a){var b=d,c="fixed"==f.p.labelSize?f.p.defaultLabelSize:f.p.labelSizeRatio*a.displaySize;b.font=(f.p.hoverFontStyle||f.p.fontStyle||"")+" "+c+"px "+(f.p.hoverFont||f.p.font||"");b.fillStyle="node"==f.p.labelHoverBGColor?a.color||f.p.defaultNodeColor:f.p.defaultHoverLabelBGColor;b.beginPath();if(f.p.labelHoverShadow)b.shadowOffsetX=
-0,b.shadowOffsetY=0,b.shadowBlur=4,b.shadowColor=f.p.labelHoverShadowColor;sigma.tools.drawRoundRect(b,Math.round(a.displayX-c/2-2),Math.round(a.displayY-c/2-2),Math.round(b.measureText(a.label).width+1.5*a.displaySize+c/2+4),Math.round(c+4),Math.round(c/2+2),"left");b.closePath();b.fill();b.shadowOffsetX=0;b.shadowOffsetY=0;b.shadowBlur=0;b.beginPath();b.fillStyle="node"==f.p.nodeBorderColor?a.color||f.p.defaultNodeColor:f.p.defaultNodeBorderColor;b.arc(Math.round(a.displayX),Math.round(a.displayY),
-a.displaySize+f.p.borderSize,0,2*Math.PI,!0);b.closePath();b.fill();b.beginPath();b.fillStyle="node"==f.p.nodeHoverColor?a.color||f.p.defaultNodeColor:f.p.defaultNodeHoverColor;b.arc(Math.round(a.displayX),Math.round(a.displayY),a.displaySize,0,2*Math.PI,!0);b.closePath();b.fill();b.fillStyle="node"==f.p.labelHoverColor?a.color||f.p.defaultNodeColor:f.p.defaultLabelHoverColor;b.fillText(a.label,Math.round(a.displayX+1.5*a.displaySize),Math.round(a.displayY+c/2-3));return f}function k(a){if(isNaN(a.x)||
-isNaN(a.y))throw Error("A node's coordinate is not a number (id: "+a.id+")");return!a.hidden&&a.displayX+a.displaySize>-j/3&&a.displayX-a.displaySize<4*j/3&&a.displayY+a.displaySize>-l/3&&a.displayY-a.displaySize<4*l/3}sigma.classes.Cascade.call(this);var f=this;this.p={labelColor:"default",defaultLabelColor:"#000",labelHoverBGColor:"default",defaultHoverLabelBGColor:"#fff",labelHoverShadow:!0,labelHoverShadowColor:"#000",labelHoverColor:"default",defaultLabelHoverColor:"#000",labelActiveBGColor:"default",
-defaultActiveLabelBGColor:"#fff",labelActiveShadow:!0,labelActiveShadowColor:"#000",labelActiveColor:"default",defaultLabelActiveColor:"#000",labelSize:"fixed",defaultLabelSize:12,labelSizeRatio:2,labelThreshold:6,font:"Arial",hoverFont:"",activeFont:"",fontStyle:"",hoverFontStyle:"",activeFontStyle:"",edgeColor:"source",defaultEdgeColor:"#aaa",defaultEdgeType:"line",defaultNodeColor:"#aaa",nodeHoverColor:"node",defaultNodeHoverColor:"#fff",nodeActiveColor:"node",defaultNodeActiveColor:"#fff",borderSize:0,
-nodeBorderColor:"node",defaultNodeBorderColor:"#fff",edgesSpeed:200,nodesSpeed:200,labelsSpeed:200};var j=g,l=c;this.currentLabelIndex=this.currentNodeIndex=this.currentEdgeIndex=0;this.task_drawLabel=function(){for(var b=a.nodes.length,c=0;c++<f.p.labelsSpeed&&f.currentLabelIndex<b;)if(f.isOnScreen(a.nodes[f.currentLabelIndex])){var d=a.nodes[f.currentLabelIndex++],h=e;if(d.displaySize>=f.p.labelThreshold){var k="fixed"==f.p.labelSize?f.p.defaultLabelSize:f.p.labelSizeRatio*d.displaySize;h.font=
-f.p.fontStyle+k+"px "+f.p.font;h.fillStyle="node"==f.p.labelColor?d.color||f.p.defaultNodeColor:f.p.defaultLabelColor;h.fillText(d.label,Math.round(d.displayX+1.5*d.displaySize),Math.round(d.displayY+k/2-3))}}else f.currentLabelIndex++;return f.currentLabelIndex<b};this.task_drawEdge=function(){for(var b=a.edges.length,c,d,e=0;e++<f.p.edgesSpeed&&f.currentEdgeIndex<b;)if(c=a.edges[f.currentEdgeIndex].source,d=a.edges[f.currentEdgeIndex].target,c.hidden||d.hidden||!f.isOnScreen(c)&&!f.isOnScreen(d))f.currentEdgeIndex++;
-else{c=a.edges[f.currentEdgeIndex++];d=c.source.displayX;var k=c.source.displayY,g=c.target.displayX,i=c.target.displayY,j=c.color;if(!j)switch(f.p.edgeColor){case "source":j=c.source.color||f.p.defaultNodeColor;break;case "target":j=c.target.color||f.p.defaultNodeColor;break;default:j=f.p.defaultEdgeColor}var l=h;switch(c.type||f.p.defaultEdgeType){case "curve":l.strokeStyle=j;l.lineWidth=c.displaySize/3;l.beginPath();l.moveTo(d,k);l.quadraticCurveTo((d+g)/2+(i-k)/4,(k+i)/2+(d-g)/4,g,i);l.stroke();
-break;default:l.strokeStyle=j,l.lineWidth=c.displaySize/3,l.beginPath(),l.moveTo(d,k),l.lineTo(g,i),l.stroke()}}return f.currentEdgeIndex<b};this.task_drawNode=function(){for(var c=a.nodes.length,d=0;d++<f.p.nodesSpeed&&f.currentNodeIndex<c;)if(f.isOnScreen(a.nodes[f.currentNodeIndex])){var e=a.nodes[f.currentNodeIndex++],k=Math.round(10*e.displaySize)/10,h=b;h.fillStyle=e.color;h.beginPath();h.arc(e.displayX,e.displayY,k,0,2*Math.PI,!0);h.closePath();h.fill();e.hover&&i(e)}else f.currentNodeIndex++;
-return f.currentNodeIndex<c};this.drawActiveNode=function(a){var b=d;if(!k(a))return f;var c="fixed"==f.p.labelSize?f.p.defaultLabelSize:f.p.labelSizeRatio*a.displaySize;b.font=(f.p.activeFontStyle||f.p.fontStyle||"")+" "+c+"px "+(f.p.activeFont||f.p.font||"");b.fillStyle="node"==f.p.labelHoverBGColor?a.color||f.p.defaultNodeColor:f.p.defaultActiveLabelBGColor;b.beginPath();if(f.p.labelActiveShadow)b.shadowOffsetX=0,b.shadowOffsetY=0,b.shadowBlur=4,b.shadowColor=f.p.labelActiveShadowColor;sigma.tools.drawRoundRect(b,
-Math.round(a.displayX-c/2-2),Math.round(a.displayY-c/2-2),Math.round(b.measureText(a.label).width+1.5*a.displaySize+c/2+4),Math.round(c+4),Math.round(c/2+2),"left");b.closePath();b.fill();b.shadowOffsetX=0;b.shadowOffsetY=0;b.shadowBlur=0;b.beginPath();b.fillStyle="node"==f.p.nodeBorderColor?a.color||f.p.defaultNodeColor:f.p.defaultNodeBorderColor;b.arc(Math.round(a.displayX),Math.round(a.displayY),a.displaySize+f.p.borderSize,0,2*Math.PI,!0);b.closePath();b.fill();b.beginPath();b.fillStyle="node"==
-f.p.nodeActiveColor?a.color||f.p.defaultNodeColor:f.p.defaultNodeActiveColor;b.arc(Math.round(a.displayX),Math.round(a.displayY),a.displaySize,0,2*Math.PI,!0);b.closePath();b.fill();b.fillStyle="node"==f.p.labelActiveColor?a.color||f.p.defaultNodeColor:f.p.defaultLabelActiveColor;b.fillText(a.label,Math.round(a.displayX+1.5*a.displaySize),Math.round(a.displayY+c/2-3));return f};this.drawHoverNode=i;this.isOnScreen=k;this.resize=function(a,b){j=a;l=b;return f}}function t(b,h){function e(){sigma.chronos.removeTask("node_"+
-c.id,2).removeTask("edge_"+c.id,2).removeTask("label_"+c.id,2).stopTasks();return c}function d(a,b){c.domElements[a]=document.createElement(b);c.domElements[a].style.position="absolute";c.domElements[a].setAttribute("id","sigma_"+a+"_"+c.id);c.domElements[a].setAttribute("class","sigma_"+a+"_"+b);c.domElements[a].setAttribute("width",c.width+"px");c.domElements[a].setAttribute("height",c.height+"px");c.domRoot.appendChild(c.domElements[a]);return c}function a(){c.p.drawHoverNodes&&(c.graph.checkHover(c.mousecaptor.mouseX,
-c.mousecaptor.mouseY),c.graph.nodes.forEach(function(a){a.hover&&!a.active&&c.plotter.drawHoverNode(a)}));return c}function A(){c.p.drawActiveNodes&&c.graph.nodes.forEach(function(a){a.active&&c.plotter.drawActiveNode(a)});return c}sigma.classes.Cascade.call(this);sigma.classes.EventDispatcher.call(this);var c=this;this.id=h.toString();this.p={auto:!0,drawNodes:2,drawEdges:1,drawLabels:2,lastNodes:2,lastEdges:0,lastLabels:2,drawHoverNodes:!0,drawActiveNodes:!0};this.domRoot=b;this.width=this.domRoot.offsetWidth;
-this.height=this.domRoot.offsetHeight;this.graph=new n;this.domElements={};d("edges","canvas");d("nodes","canvas");d("labels","canvas");d("hover","canvas");d("monitor","div");d("mouse","canvas");this.plotter=new m(this.domElements.nodes.getContext("2d"),this.domElements.edges.getContext("2d"),this.domElements.labels.getContext("2d"),this.domElements.hover.getContext("2d"),this.graph,this.width,this.height);this.monitor=new g(this,this.domElements.monitor);this.mousecaptor=new j(this.domElements.mouse,
-this.id);this.mousecaptor.bind("drag interpolate",function(){c.draw(c.p.auto?2:c.p.drawNodes,c.p.auto?0:c.p.drawEdges,c.p.auto?2:c.p.drawLabels,!0)}).bind("stopdrag stopinterpolate",function(){c.draw(c.p.auto?2:c.p.drawNodes,c.p.auto?1:c.p.drawEdges,c.p.auto?2:c.p.drawLabels,!0)}).bind("mousedown mouseup",function(a){var b=c.graph.nodes.filter(function(a){return!!a.hover}).map(function(a){return a.id});c.dispatch("mousedown"==a.type?"downgraph":"upgraph");b.length&&c.dispatch("mousedown"==a.type?
-"downnodes":"upnodes",b)}).bind("move",function(){c.domElements.hover.getContext("2d").clearRect(0,0,c.domElements.hover.width,c.domElements.hover.height);a();A()});sigma.chronos.bind("startgenerators",function(){sigma.chronos.getGeneratorsIDs().some(function(a){return!!a.match(RegExp("_ext_"+c.id+"$",""))})&&c.draw(c.p.auto?2:c.p.drawNodes,c.p.auto?0:c.p.drawEdges,c.p.auto?2:c.p.drawLabels)}).bind("stopgenerators",function(){c.draw()});for(var B=0;B<i.length;B++)i[B](this);this.draw=function(a,b,
-d,h){if(h&&sigma.chronos.getGeneratorsIDs().some(function(a){return!!a.match(RegExp("_ext_"+c.id+"$",""))}))return c;a=void 0==a?c.p.drawNodes:a;b=void 0==b?c.p.drawEdges:b;d=void 0==d?c.p.drawLabels:d;h={nodes:a,edges:b,labels:d};c.p.lastNodes=a;c.p.lastEdges=b;c.p.lastLabels=d;e();c.graph.rescale(c.width,c.height,0<a,0<b).setBorders();c.mousecaptor.checkBorders(c.graph.borders,c.width,c.height);c.graph.translate(c.mousecaptor.stageX,c.mousecaptor.stageY,c.mousecaptor.ratio,0<a,0<b);c.dispatch("graphscaled");
-for(var g in c.domElements)"canvas"==c.domElements[g].nodeName.toLowerCase()&&(void 0==h[g]||0<=h[g])&&c.domElements[g].getContext("2d").clearRect(0,0,c.domElements[g].width,c.domElements[g].height);c.plotter.currentEdgeIndex=0;c.plotter.currentNodeIndex=0;c.plotter.currentLabelIndex=0;g=null;h=!1;if(a)if(1<a)for(;c.plotter.task_drawNode(););else sigma.chronos.addTask(c.plotter.task_drawNode,"node_"+c.id,!1),h=!0,g="node_"+c.id;if(d)if(1<d)for(;c.plotter.task_drawLabel(););else g?sigma.chronos.queueTask(c.plotter.task_drawLabel,
-"label_"+c.id,g):sigma.chronos.addTask(c.plotter.task_drawLabel,"label_"+c.id,!1),h=!0,g="label_"+c.id;if(b)if(1<b)for(;c.plotter.task_drawEdge(););else g?sigma.chronos.queueTask(c.plotter.task_drawEdge,"edge_"+c.id,g):sigma.chronos.addTask(c.plotter.task_drawEdge,"edge_"+c.id,!1),h=!0,g="edge_"+c.id;c.dispatch("draw");c.refresh();h&&sigma.chronos.runTasks();return c};this.resize=function(a,b){var d=c.width,e=c.height;void 0!=a&&void 0!=b?(c.width=a,c.height=b):(c.width=c.domRoot.offsetWidth,c.height=
-c.domRoot.offsetHeight);if(d!=c.width||e!=c.height){for(var h in c.domElements)c.domElements[h].setAttribute("width",c.width+"px"),c.domElements[h].setAttribute("height",c.height+"px");c.plotter.resize(c.width,c.height);c.draw(c.p.lastNodes,c.p.lastEdges,c.p.lastLabels,!0)}return c};this.refresh=function(){c.domElements.hover.getContext("2d").clearRect(0,0,c.domElements.hover.width,c.domElements.hover.height);a();A();return c};this.drawHover=a;this.drawActive=A;this.clearSchedule=e;window.addEventListener("resize",
-function(){c.resize()})}function p(b){var h=this;sigma.classes.EventDispatcher.call(this);this._core=b;this.kill=function(){};this.getID=function(){return b.id};this.configProperties=function(e,d){var a=b.config(e,d);return a==b?h:a};this.drawingProperties=function(e,d){var a=b.plotter.config(e,d);return a==b.plotter?h:a};this.mouseProperties=function(e,d){var a=b.mousecaptor.config(e,d);return a==b.mousecaptor?h:a};this.graphProperties=function(e,d){var a=b.graph.config(e,d);return a==b.graph?h:
-a};this.getMouse=function(){return{mouseX:b.mousecaptor.mouseX,mouseY:b.mousecaptor.mouseY,down:b.mousecaptor.isMouseDown}};this.position=function(e,d,a){if(0==arguments.length)return{stageX:b.mousecaptor.stageX,stageY:b.mousecaptor.stageY,ratio:b.mousecaptor.ratio};b.mousecaptor.stageX=void 0!=e?e:b.mousecaptor.stageX;b.mousecaptor.stageY=void 0!=d?d:b.mousecaptor.stageY;b.mousecaptor.ratio=void 0!=a?a:b.mousecaptor.ratio;return h};this.goTo=function(e,d,a){b.mousecaptor.interpolate(e,d,a);return h};
-this.zoomTo=function(e,d,a){a=Math.min(Math.max(b.mousecaptor.config("minRatio"),a),b.mousecaptor.config("maxRatio"));a==b.mousecaptor.ratio?b.mousecaptor.interpolate(e-b.width/2+b.mousecaptor.stageX,d-b.height/2+b.mousecaptor.stageY):b.mousecaptor.interpolate((a*e-b.mousecaptor.ratio*b.width/2)/(a-b.mousecaptor.ratio),(a*d-b.mousecaptor.ratio*b.height/2)/(a-b.mousecaptor.ratio),a);return h};this.resize=function(e,d){b.resize(e,d);return h};this.draw=function(e,d,a,g){b.draw(e,d,a,g);return h};this.refresh=
-function(){b.refresh();return h};this.addGenerator=function(e,d,a){sigma.chronos.addGenerator(e+"_ext_"+b.id,d,a);return h};this.removeGenerator=function(e){sigma.chronos.removeGenerator(e+"_ext_"+b.id);return h};this.addNode=function(e,d){b.graph.addNode(e,d);return h};this.addEdge=function(e,d,a,g){b.graph.addEdge(e,d,a,g);return h};this.dropNode=function(e){b.graph.dropNode(e);return h};this.dropEdge=function(e){b.graph.dropEdge(e);return h};this.pushGraph=function(e,d){e.nodes&&e.nodes.forEach(function(a){a.id&&
-(!d||!b.graph.nodesIndex[a.id])&&h.addNode(a.id,a)});e.edges&&e.edges.forEach(function(a){(validID=a.source&&a.target&&a.id)&&(!d||!b.graph.edgesIndex[a.id])&&h.addNode(a.id,a.source,a.target,a)});return h};this.emptyGraph=function(){b.graph.empty();return h};this.getNodesCount=function(){return b.graph.nodes.length};this.getEdgesCount=function(){return b.graph.edges.length};this.iterNodes=function(e,d){b.graph.iterNodes(e,d);return h};this.iterEdges=function(e,d){b.graph.iterEdges(e,d);return h};
-this.getNodes=function(e){return b.graph.getNodes(e)};this.getEdges=function(e){return b.graph.getEdges(e)};this.activateMonitoring=function(){return b.monitor.activate()};this.desactivateMonitoring=function(){return b.monitor.desactivate()};b.bind("downnodes upnodes downgraph upgraph",function(b){h.dispatch(b.type,b.content)});b.graph.bind("overnodes outnodes",function(b){h.dispatch(b.type,b.content)})}var x=0;i=void 0;i=[];sigma.init=function(b){b=new t(b,(++x).toString());sigma.instances[x]=new p(b);
-return sigma.instances[x]};sigma.addPlugin=function(b,h,e){p.prototype[b]=h;i.push(e)};sigma.chronos=new function(){function b(a){window.setTimeout(a,0);return f}function h(){for(f.dispatch("frameinserted");m&&r.length&&e(););!m||!r.length?a():(w=(new Date).getTime(),q++,C=u-p,t=p-C,f.dispatch("insertframe"),b(h))}function e(){y%=r.length;if(!r[y].task()){var a=r[y].taskName;v=v.filter(function(b){b.taskParent==a&&r.push({taskName:b.taskName,task:b.task});return b.taskParent!=a});f.dispatch("killed",
-r.splice(y--,1)[0])}y++;u=(new Date).getTime()-w;return u<=t}function d(){m=!0;q=y=0;x=w=(new Date).getTime();f.dispatch("start");f.dispatch("insertframe");b(h);return f}function a(){f.dispatch("stop");m=!1;return f}function g(a,b,c){if("function"!=typeof a)throw Error('Task "'+b+'" is not a function');r.push({taskName:b,task:a});m=!(!m&&!(c&&d()||1));return f}function c(a){return a?Object.keys(o).filter(function(a){return!!o[a].on}).length:Object.keys(o).length}function i(){Object.keys(o).length?
-(f.dispatch("startgenerators"),f.unbind("killed",j),b(function(){for(var a in o)o[a].on=!0,g(o[a].task,a,!1)}),f.bind("killed",j).runTasks()):f.dispatch("stopgenerators");return f}function j(a){if(void 0!=o[a.content.taskName])o[a.content.taskName].del||!o[a.content.taskName].condition()?delete o[a.content.taskName]:o[a.content.taskName].on=!1,0==c(!0)&&i()}sigma.classes.EventDispatcher.call(this);var f=this,m=!1,l=80,n=0,q=0,p=1E3/l,t=p,u=0,x=0,w=0,C=0,o={},r=[],v=[],y=0;this.frequency=function(a){return void 0!=
-a?(l=Math.abs(1*a),p=1E3/l,q=0,f):l};this.runTasks=d;this.stopTasks=a;this.insertFrame=b;this.addTask=g;this.queueTask=function(a,b,c){if("function"!=typeof a)throw Error('Task "'+b+'" is not a function');if(!r.concat(v).some(function(a){return a.taskName==c}))throw Error('Parent task "'+c+'" of "'+b+'" is not attached.');v.push({taskParent:c,taskName:b,task:a});return f};this.removeTask=function(b,c){if(void 0==b)r=[],1==c?v=[]:2==c&&(r=v,v=[]),a();else{var d="string"==typeof b?b:"";r=r.filter(function(a){return("string"==
-typeof b?a.taskName==b:a.task==b)?(d=a.taskName,!1):!0});0<c&&(v=v.filter(function(a){1==c&&a.taskParent==d&&r.push(a);return a.taskParent!=d}))}m=!(r.length&&(!a()||1));return f};this.addGenerator=function(a,b,d){if(void 0!=o[a])return f;o[a]={task:b,condition:d};0==c(!0)&&i();return f};this.removeGenerator=function(a){if(o[a])o[a].on=!1,o[a].del=!0;return f};this.startGenerators=i;this.getGeneratorsIDs=function(){return Object.keys(o)};this.getFPS=function(){m&&(n=Math.round(1E4*(q/((new Date).getTime()-
-x)))/10);return n};this.getTasksCount=function(){return r.length};this.getQueuedTasksCount=function(){return v.length};this.getExecutionTime=function(){return w-x};return this};sigma.debugMode=0;sigma.log=function(){if(1==sigma.debugMode)for(var b in arguments)console.log(arguments[b]);else if(1<sigma.debugMode)for(b in arguments)throw Error(arguments[b]);return sigma};sigma.easing={linear:{},quadratic:{}};sigma.easing.linear.easenone=function(b){return b};sigma.easing.quadratic.easein=function(b){return b*
-b};sigma.easing.quadratic.easeout=function(b){return-b*(b-2)};sigma.easing.quadratic.easeinout=function(b){return 1>(b*=2)?0.5*b*b:-0.5*(--b*(b-2)-1)};sigma.tools.drawRoundRect=function(b,h,e,d,a,g,c){var g=g?g:0,i=c?c:[],i="string"==typeof i?i.split(" "):i,c=g&&(0<=i.indexOf("topleft")||0<=i.indexOf("top")||0<=i.indexOf("left")),j=g&&(0<=i.indexOf("topright")||0<=i.indexOf("top")||0<=i.indexOf("right")),f=g&&(0<=i.indexOf("bottomleft")||0<=i.indexOf("bottom")||0<=i.indexOf("left")),i=g&&(0<=i.indexOf("bottomright")||
-0<=i.indexOf("bottom")||0<=i.indexOf("right"));b.moveTo(h,e+g);c?b.arcTo(h,e,h+g,e,g):b.lineTo(h,e);j?(b.lineTo(h+d-g,e),b.arcTo(h+d,e,h+d,e+g,g)):b.lineTo(h+d,e);i?(b.lineTo(h+d,e+a-g),b.arcTo(h+d,e+a,h+d-g,e+a,g)):b.lineTo(h+d,e+a);f?(b.lineTo(h+g,e+a),b.arcTo(h,e+a,h,e+a-g,g)):b.lineTo(h,e+a);b.lineTo(h,e+g)};sigma.tools.getRGB=function(b,g){var b=b.toString(),e={r:0,g:0,b:0};if(3<=b.length&&"#"==b.charAt(0)){var d=b.length-1;6==d?e={r:parseInt(b.charAt(1)+b.charAt(2),16),g:parseInt(b.charAt(3)+
-b.charAt(4),16),b:parseInt(b.charAt(5)+b.charAt(5),16)}:3==d&&(e={r:parseInt(b.charAt(1)+b.charAt(1),16),g:parseInt(b.charAt(2)+b.charAt(2),16),b:parseInt(b.charAt(3)+b.charAt(3),16)})}g&&(e=[e.r,e.g,e.b]);return e};sigma.tools.rgbToHex=function(b,g,e){return sigma.tools.toHex(b)+sigma.tools.toHex(g)+sigma.tools.toHex(e)};sigma.tools.toHex=function(b){b=parseInt(b,10);if(isNaN(b))return"00";b=Math.max(0,Math.min(b,255));return"0123456789ABCDEF".charAt((b-b%16)/16)+"0123456789ABCDEF".charAt(b%16)};
-sigma.publicPrototype=p.prototype})();
-
+// Define packages:
+var sigma = {};
+sigma.tools = {};
+sigma.classes = {};
+sigma.instances = {};
+
+// Adding Array helpers, if not present yet:
+(function() {
+  if (!Array.prototype.some) {
+    Array.prototype.some = function(fun /*, thisp*/) {
+      var len = this.length;
+      if (typeof fun != 'function') {
+        throw new TypeError();
+      }
+
+      var thisp = arguments[1];
+      for (var i = 0; i < len; i++) {
+        if (i in this &&
+            fun.call(thisp, this[i], i, this)) {
+          return true;
+        }
+      }
+
+      return false;
+    };
+  }
+
+  if (!Array.prototype.forEach) {
+    Array.prototype.forEach = function(fun /*, thisp*/) {
+      var len = this.length;
+      if (typeof fun != 'function') {
+        throw new TypeError();
+      }
+
+      var thisp = arguments[1];
+      for (var i = 0; i < len; i++) {
+        if (i in this) {
+          fun.call(thisp, this[i], i, this);
+        }
+      }
+    };
+  }
+
+  if (!Array.prototype.map) {
+    Array.prototype.map = function(fun /*, thisp*/) {
+      var len = this.length;
+      if (typeof fun != 'function') {
+        throw new TypeError();
+      }
+
+      var res = new Array(len);
+      var thisp = arguments[1];
+      for (var i = 0; i < len; i++) {
+        if (i in this) {
+          res[i] = fun.call(thisp, this[i], i, this);
+        }
+      }
+
+      return res;
+    };
+  }
+
+  if (!Array.prototype.filter) {
+    Array.prototype.filter = function(fun /*, thisp*/) {
+      var len = this.length;
+      if (typeof fun != 'function')
+        throw new TypeError();
+
+      var res = new Array();
+      var thisp = arguments[1];
+      for (var i = 0; i < len; i++) {
+        if (i in this) {
+          var val = this[i]; // in case fun mutates this
+          if (fun.call(thisp, val, i, this)) {
+            res.push(val);
+          }
+        }
+      }
+
+      return res;
+    };
+  }
+
+  if (!Object.keys) {
+    Object.keys = (function() {
+      var hasOwnProperty = Object.prototype.hasOwnProperty,
+          hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
+          dontEnums = [
+            'toString',
+            'toLocaleString',
+            'valueOf',
+            'hasOwnProperty',
+            'isPrototypeOf',
+            'propertyIsEnumerable',
+            'constructor'
+          ],
+          dontEnumsLength = dontEnums.length;
+
+      return function(obj) {
+        if (typeof obj !== 'object' &&
+            typeof obj !== 'function' ||
+            obj === null
+        ) {
+          throw new TypeError('Object.keys called on non-object');
+        }
+
+        var result = [];
+
+        for (var prop in obj) {
+          if (hasOwnProperty.call(obj, prop)) result.push(prop);
+        }
+
+        if (hasDontEnumBug) {
+          for (var i = 0; i < dontEnumsLength; i++) {
+            if (hasOwnProperty.call(obj, dontEnums[i])) {
+              result.push(dontEnums[i]);
+            }
+          }
+        }
+        return result;
+      }
+    })();
+  }
+})();
+
+/**
+ * A jQuery like properties management class. It works like jQuery .css()
+ * method: You can call it with juste one string to get the corresponding
+ * property, with a string and anything else to set the corresponding property,
+ * or directly with an object, and then each pair string / object (or any type)
+ * will be set in the properties.
+ * @constructor
+ * @this {sigma.classes.Cascade}
+ */
+sigma.classes.Cascade = function() {
+  /**
+   * This instance properties.
+   * @protected
+   * @type {Object}
+   */
+  this.p = {};
+
+  /**
+   * The method to use to set/get any property of this instance.
+   * @param  {(string|Object)} a1 If it is a string and if a2 is undefined,
+   *                              then it will return the corresponding
+   *                              property.
+   *                              If it is a string and if a2 is set, then it
+   *                              will set a2 as the property corresponding to
+   *                              a1, and return this.
+   *                              If it is an object, then each pair string /
+   *                              object (or any other type) will be set as a
+   *                              property.
+   * @param  {*?} a2              The new property corresponding to a1 if a1 is
+   *                              a string.
+   * @return {(*|sigma.classes.Cascade)} Returns itself or the corresponding
+   *                                     property.
+   */
+  this.config = function(a1, a2) {
+    if (typeof a1 == 'string' && a2 == undefined) {
+      return this.p[a1];
+    } else {
+      var o = (typeof a1 == 'object' && a2 == undefined) ? a1 : {};
+      if (typeof a1 == 'string') {
+        o[a1] = a2;
+      }
+
+      for (var k in o) {
+        if (this.p[k] != undefined) {
+          this.p[k] = o[k];
+        }
+      }
+      return this;
+    }
+  };
+};
+
+/**
+ * sigma.js custom event dispatcher class.
+ * @constructor
+ * @this {sigma.classes.EventDispatcher}
+ */
+sigma.classes.EventDispatcher = function() {
+  /**
+   * An object containing all the different handlers bound to one or many
+   * events, indexed by these events.
+   * @private
+   * @type {Object.<string,Object>}
+   */
+  var _h = {};
+
+  /**
+   * Represents "this", without the well-known scope issue.
+   * @private
+   * @type {sigma.classes.EventDispatcher}
+   */
+  var _self = this;
+
+  /**
+   * Will execute the handler the next (and only the next) time that the
+   * indicated event (or the indicated events) will be triggered.
+   * @param  {string} events            The name of the event (or the events
+   *                                    separated by spaces).
+   * @param  {function(Object)} handler The handler to bind.
+   * @return {sigma.classes.EventDispatcher} Returns itself.
+   */
+  function one(events, handler) {
+    if (!handler || !events) {
+      return _self;
+    }
+
+    var eArray = ((typeof events) == 'string') ? events.split(' ') : events;
+
+    eArray.forEach(function(event) {
+      if (!_h[event]) {
+        _h[event] = [];
+      }
+
+      _h[event].push({
+        'h': handler,
+        'one': true
+      });
+    });
+
+    return _self;
+  }
+
+  /**
+   * Will execute the handler everytime that the indicated event (or the
+   * indicated events) will be triggered.
+   * @param  {string} events            The name of the event (or the events
+   *                                    separated by spaces).
+   * @param  {function(Object)} handler The handler to bind.
+   * @return {sigma.classes.EventDispatcher} Returns itself.
+   */
+  function bind(events, handler) {
+    if (!handler || !events) {
+      return _self;
+    }
+
+    var eArray = ((typeof events) == 'string') ? events.split(' ') : events;
+
+    eArray.forEach(function(event) {
+      if (!_h[event]) {
+        _h[event] = [];
+      }
+
+      _h[event].push({
+        'h': handler,
+        'one': false
+      });
+    });
+
+    return _self;
+  }
+
+  /**
+   * Unbinds the handler from a specified event (or specified events).
+   * @param  {?string} events            The name of the event (or the events
+   *                                     separated by spaces). If undefined,
+   *                                     then all handlers are unbound.
+   * @param  {?function(Object)} handler The handler to unbind. If undefined,
+   *                                     each handler bound to the event or the
+   *                                     events will be unbound.
+   * @return {sigma.classes.EventDispatcher} Returns itself.
+   */
+  function unbind(events, handler) {
+    if (!events) {
+      _h = {};
+    }
+
+    var eArray = typeof events == 'string' ? events.split(' ') : events;
+
+    if (handler) {
+      eArray.forEach(function(event) {
+        if (_h[event]) {
+          _h[event] = _h[event].filter(function(e) {
+            return e['h'] != handler;
+          });
+        }
+
+        if (_h[event] && _h[event].length == 0) {
+          delete _h[event];
+        }
+      });
+    }else {
+      eArray.forEach(function(event) {
+        delete _h[event];
+      });
+    }
+
+    return _self;
+  }
+
+  /**
+   * Executes each handler bound to the event
+   * @param  {string} type     The type of the event.
+   * @param  {?Object} content The content of the event (optional).
+   * @return {sigma.classes.EventDispatcher} Returns itself.
+   */
+  function dispatch(type, content) {
+    if (_h[type]) {
+      _h[type].forEach(function(e) {
+        e['h']({
+          'type': type,
+          'content': content,
+          'target': _self
+        });
+      });
+
+      _h[type] = _h[type].filter(function(e) {
+        return !e['one'];
+      });
+    }
+
+    return _self;
+  }
+
+  /* PUBLIC INTERFACE: */
+  this.one = one;
+  this.bind = bind;
+  this.unbind = unbind;
+  this.dispatch = dispatch;
+};
+
+(function() {
+// Define local shortcut:
+var id = 0;
+
+// Define local package:
+var local = {};
+local.plugins = [];
+
+sigma.init = function(dom) {
+  var inst = new Sigma(dom, (++id).toString());
+  sigma.instances[id] = new SigmaPublic(inst);
+  return sigma.instances[id];
+};
+
+/**
+ * This class listen to all the different mouse events, to normalize them and
+ * dispatch action events instead (from "startinterpolate" to "isdragging",
+ * etc).
+ * @constructor
+ * @extends sigma.classes.Cascade
+ * @extends sigma.classes.EventDispatcher
+ * @param {element} dom The DOM element to bind the handlers on.
+ * @this {MouseCaptor}
+ */
+function MouseCaptor(dom) {
+  sigma.classes.Cascade.call(this);
+  sigma.classes.EventDispatcher.call(this);
+
+  /**
+   * Represents "this", without the well-known scope issue.
+   * @private
+   * @type {MouseCaptor}
+   */
+  var self = this;
+
+  /**
+   * The DOM element to bind the handlers on.
+   * @type {element}
+   */
+  var dom = dom;
+
+  /**
+   * The different parameters that define how this instance should work.
+   * @see sigma.classes.Cascade
+   * @type {Object}
+   */
+  this.p = {
+    minRatio: 1,
+    maxRatio: 32,
+    marginRatio: 1,
+    zoomDelta: 0.1,
+    dragDelta: 0.3,
+    zoomMultiply: 2,
+    directZooming: false,
+    blockScroll: true,
+    inertia: 1.1,
+    mouseEnabled: true
+  };
+
+  var oldMouseX = 0;
+  var oldMouseY = 0;
+  var startX = 0;
+  var startY = 0;
+
+  var oldStageX = 0;
+  var oldStageY = 0;
+  var oldRatio = 1;
+
+  var targetRatio = 1;
+  var targetStageX = 0;
+  var targetStageY = 0;
+
+  var lastStageX = 0;
+  var lastStageX2 = 0;
+  var lastStageY = 0;
+  var lastStageY2 = 0;
+
+  var progress = 0;
+  var isZooming = false;
+
+  this.stageX = 0;
+  this.stageY = 0;
+  this.ratio = 1;
+
+  this.mouseX = 0;
+  this.mouseY = 0;
+
+  this.isMouseDown = false;
+
+  /**
+   * Extract the local X position from a mouse event.
+   * @private
+   * @param  {event} e A mouse event.
+   * @return {number} The local X value of the mouse.
+   */
+  function getX(e) {
+    return e.offsetX != undefined && e.offsetX ||
+           e.layerX != undefined && e.layerX ||
+           e.clientX != undefined && e.clientX;
+  };
+
+  /**
+   * Extract the local Y position from a mouse event.
+   * @private
+   * @param  {event} e A mouse event.
+   * @return {number} The local Y value of the mouse.
+   */
+  function getY(e) {
+    return e.offsetY != undefined && e.offsetY ||
+           e.layerY != undefined && e.layerY ||
+           e.clientY != undefined && e.clientY;
+  };
+
+  /**
+   * Extract the wheel delta from a mouse event.
+   * @private
+   * @param  {event} e A mouse event.
+   * @return {number} The wheel delta of the mouse.
+   */
+  function getDelta(e) {
+    return e.wheelDelta != undefined && e.wheelDelta ||
+           e.detail != undefined && -e.detail;
+  };
+
+  /**
+   * The handler listening to the 'move' mouse event. It will set the mouseX
+   * and mouseY values as the mouse position values, prevent the default event,
+   * and dispatch a 'move' event.
+   * @private
+   * @param  {event} event A 'move' mouse event.
+   */
+  function moveHandler(event) {
+    oldMouseX = self.mouseX;
+    oldMouseY = self.mouseY;
+
+    self.mouseX = getX(event);
+    self.mouseY = getY(event);
+
+    self.isMouseDown && drag(event);
+    self.dispatch('move');
+
+    if (event.preventDefault) {
+      event.preventDefault();
+    } else {
+      event.returnValue = false;
+    }
+  };
+
+  /**
+   * The handler listening to the 'up' mouse event. It will set the isMouseDown
+   * value as false, dispatch a 'mouseup' event, and trigger stopDrag().
+   * @private
+   * @param  {event} event A 'up' mouse event.
+   */
+  function upHandler(event) {
+    if (self.p.mouseEnabled && self.isMouseDown) {
+      self.isMouseDown = false;
+      self.dispatch('mouseup');
+      stopDrag();
+
+      if (event.preventDefault) {
+        event.preventDefault();
+      } else {
+        event.returnValue = false;
+      }
+    }
+  };
+
+  /**
+   * The handler listening to the 'down' mouse event. It will set the
+   * isMouseDown value as true, dispatch a 'mousedown' event, and trigger
+   * startDrag().
+   * @private
+   * @param  {event} event A 'down' mouse event.
+   */
+  function downHandler(event) {
+    if (self.p.mouseEnabled) {
+      self.isMouseDown = true;
+      oldMouseX = self.mouseX;
+      oldMouseY = self.mouseY;
+
+      self.dispatch('mousedown');
+
+      startDrag();
+
+      if (event.preventDefault) {
+        event.preventDefault();
+      } else {
+        event.returnValue = false;
+      }
+    }
+  };
+
+  /**
+   * The handler listening to the 'wheel' mouse event. It will trigger
+   * {@link startInterpolate} with the event delta as parameter.
+   * @private
+   * @param  {event} event A 'wheel' mouse event.
+   */
+  function wheelHandler(event) {
+    if (self.p.mouseEnabled) {
+      startInterpolate(
+        self.mouseX,
+        self.mouseY,
+        self.ratio * (getDelta(event) > 0 ?
+          self.p.zoomMultiply :
+          1 / self.p.zoomMultiply)
+      );
+
+      if (self.p['blockScroll']) {
+        if (event.preventDefault) {
+          event.preventDefault();
+        } else {
+          event.returnValue = false;
+        }
+      }
+    }
+  };
+
+  /**
+   * Will start computing the scene X and Y, until {@link stopDrag} is
+   * triggered.
+   */
+  function startDrag() {
+    oldStageX = self.stageX;
+    oldStageY = self.stageY;
+    startX = self.mouseX;
+    startY = self.mouseY;
+
+    lastStageX = self.stageX;
+    lastStageX2 = self.stageX;
+    lastStageY = self.stageY;
+    lastStageY2 = self.stageY;
+
+    self.dispatch('startdrag');
+  };
+
+  /**
+   * Stops computing the scene position.
+   */
+  function stopDrag() {
+    if (oldStageX != self.stageX || oldStageY != self.stageY) {
+      startInterpolate(
+        self.stageX + self.p.inertia * (self.stageX - lastStageX2),
+        self.stageY + self.p.inertia * (self.stageY - lastStageY2)
+      );
+    }
+  };
+
+  /**
+   * Computes the position of the scene, relatively to the mouse position, and
+   * dispatches a "drag" event.
+   */
+  function drag() {
+    var newStageX = self.mouseX - startX + oldStageX;
+    var newStageY = self.mouseY - startY + oldStageY;
+
+    if (newStageX != self.stageX || newStageY != self.stageY) {
+      lastStageX2 = lastStageX;
+      lastStageY2 = lastStageY;
+
+      lastStageX = newStageX;
+      lastStageY = newStageY;
+
+      self.stageX = newStageX;
+      self.stageY = newStageY;
+      self.dispatch('drag');
+    }
+  };
+
+  /**
+   * Will start computing the scene zoom ratio, until {@link stopInterpolate} is
+   * triggered.
+   * @param {number} x     The new stage X.
+   * @param {number} y     The new stage Y.
+   * @param {number} ratio The new zoom ratio.
+   */
+  function startInterpolate(x, y, ratio) {
+    if (self.isMouseDown) {
+      return;
+    }
+
+    window.clearInterval(self.interpolationID);
+    isZooming = ratio != undefined;
+
+    oldStageX = self.stageX;
+    targetStageX = x;
+
+    oldStageY = self.stageY;
+    targetStageY = y;
+
+    oldRatio = self.ratio;
+    targetRatio = ratio || self.ratio;
+    targetRatio = Math.min(
+      Math.max(targetRatio, self.p.minRatio),
+      self.p.maxRatio
+    );
+
+    progress =
+      self.p.directZooming ?
+      1 - (isZooming ? self.p.zoomDelta : self.p.dragDelta) :
+      0;
+
+    if (
+      self.ratio != targetRatio ||
+      self.stageX != targetStageX ||
+      self.stageY != targetStageY
+    ) {
+      interpolate();
+      self.interpolationID = window.setInterval(interpolate, 50);
+      self.dispatch('startinterpolate');
+    }
+  };
+
+  /**
+   * Stops the move interpolation.
+   */
+  function stopInterpolate() {
+    var oldRatio = self.ratio;
+
+    if (isZooming) {
+      self.ratio = targetRatio;
+      self.stageX = targetStageX +
+                    (self.stageX - targetStageX) *
+                    self.ratio /
+                    oldRatio;
+      self.stageY = targetStageY +
+                    (self.stageY - targetStageY) *
+                    self.ratio /
+                    oldRatio;
+    }else {
+      self.stageX = targetStageX;
+      self.stageY = targetStageY;
+    }
+
+    self.dispatch('stopinterpolate');
+  };
+
+  /**
+   * Computes the interpolate ratio and the position of the scene, relatively
+   * to the last mouse event delta received, and dispatches a "interpolate"
+   * event.
+   */
+  function interpolate() {
+    progress += (isZooming ? self.p.zoomDelta : self.p.dragDelta);
+    progress = Math.min(progress, 1);
+
+    var k = sigma.easing.quadratic.easeout(progress);
+    var oldRatio = self.ratio;
+
+    self.ratio = oldRatio * (1 - k) + targetRatio * k;
+
+    if (isZooming) {
+      self.stageX = targetStageX +
+                    (self.stageX - targetStageX) *
+                    self.ratio /
+                    oldRatio;
+
+      self.stageY = targetStageY +
+                    (self.stageY - targetStageY) *
+                    self.ratio /
+                    oldRatio;
+    } else {
+      self.stageX = oldStageX * (1 - k) + targetStageX * k;
+      self.stageY = oldStageY * (1 - k) + targetStageY * k;
+    }
+
+    self.dispatch('interpolate');
+    if (progress >= 1) {
+      window.clearInterval(self.interpolationID);
+      stopInterpolate();
+    }
+  };
+
+  /**
+   * Checks that there is always a part of the graph that is displayed, to
+   * avoid the user to drag the graph out of the stage.
+   * @param  {Object} b      An object containing the borders of the graph.
+   * @param  {number} width  The width of the stage.
+   * @param  {number} height The height of the stage.
+   * @return {MouseCaptor} Returns itself.
+   */
+  function checkBorders(b, width, height) {
+    // TODO : Find the good formula
+    /*if (!isNaN(b.minX) && !isNaN(b.maxX)) {
+      self.stageX = Math.min(
+        self.stageX = Math.max(
+          self.stageX,
+          (b.minX - width) * self.ratio +
+            self.p.marginRatio*(b.maxX - b.minX)
+        ),
+        (b.maxX - width) * self.ratio +
+          width -
+          self.p.marginRatio*(b.maxX - b.minX)
+      );
+    }
+
+    if (!isNaN(b.minY) && !isNaN(b.maxY)) {
+      self.stageY = Math.min(
+        self.stageY = Math.max(
+          self.stageY,
+          (b.minY - height) * self.ratio +
+            self.p.marginRatio*(b.maxY - b.minY)
+        ),
+        (b.maxY - height) * self.ratio +
+          height -
+          self.p.marginRatio*(b.maxY - b.minY)
+      );
+    }*/
+
+    return self;
+  };
+
+  // ADD CALLBACKS
+  dom.addEventListener('DOMMouseScroll', wheelHandler, true);
+  dom.addEventListener('mousewheel', wheelHandler, true);
+  dom.addEventListener('mousemove', moveHandler, true);
+  dom.addEventListener('mousedown', downHandler, true);
+  document.addEventListener('mouseup', upHandler, true);
+
+  this.checkBorders = checkBorders;
+  this.interpolate = startInterpolate;
+}
+
+/**
+ * A class to monitor some local / global probes directly on an instance,
+ * inside a div DOM element.
+ * It executes different methods (called "probes") regularly, and displays
+ * the results on the element.
+ * @constructor
+ * @extends sigma.classes.Cascade
+ * @param {Sigma} instance The instance to monitor.
+ * @param {element} dom    The div DOM element to draw write on.
+ * @this {Monitor}
+ */
+function Monitor(instance, dom) {
+  sigma.classes.Cascade.call(this);
+
+  /**
+   * Represents "this", without the well-known scope issue.
+   * @private
+   * @type {Monitor}
+   */
+  var self = this;
+
+  /**
+   * {@link Sigma} instance owning this Monitor instance.
+   * @type {Sigma}
+   */
+  this.instance = instance;
+
+  /**
+   * Determines if the monitoring is activated or not.
+   * @type {Boolean}
+   */
+  this.monitoring = false;
+
+  /**
+   * The different parameters that define how this instance should work. It
+   * also contains the different probes.
+   * @see sigma.classes.Cascade
+   * @type {Object}
+   */
+  this.p = {
+    fps: 40,
+    dom: dom,
+    globalProbes: {
+      'Time (ms)': sigma.chronos.getExecutionTime,
+      'Queue': sigma.chronos.getQueuedTasksCount,
+      'Tasks': sigma.chronos.getTasksCount,
+      'FPS': sigma.chronos.getFPS
+    },
+    localProbes: {
+      'Nodes count': function() { return self.instance.graph.nodes.length; },
+      'Edges count': function() { return self.instance.graph.edges.length; }
+    }
+  };
+
+  /**
+   * Activates the monitoring: Some texts describing some values about sigma.js
+   * or the owning {@link Sigma} instance will appear over the graph, but
+   * beneath the mouse sensible DOM element.
+   * @return {Monitor} Returns itself.
+   */
+  function activate() {
+    if (!self.monitoring) {
+      self.monitoring = window.setInterval(routine, 1000 / self.p.fps);
+    }
+
+    return self;
+  }
+
+  /**
+   * Desactivates the monitoring: Will disappear, and stop computing the
+   * different probes.
+   * @return {Monitor} Returns itself.
+   */
+  function desactivate() {
+    if (self.monitoring) {
+      window.clearInterval(self.monitoring);
+      self.monitoring = null;
+
+      self.p.dom.innerHTML = '';
+    }
+
+    return self;
+  }
+
+  /**
+   * The private method dedicated to compute the different values to observe.
+   * @private
+   * @return {Monitor} Returns itself.
+   */
+  function routine() {
+    var s = '';
+
+    s += '<p>GLOBAL :</p>';
+    for (var k in self.p.globalProbes) {
+      s += '<p>' + k + ' : ' + self.p.globalProbes[k]() + '</p>';
+    }
+
+    s += '<br><p>LOCAL :</p>';
+    for (var k in self.p.localProbes) {
+      s += '<p>' + k + ' : ' + self.p.localProbes[k]() + '</p>';
+    }
+
+    self.p.dom.innerHTML = s;
+
+    return self;
+  }
+
+  this.activate = activate;
+  this.desactivate = desactivate;
+}
+
+/**
+ * Sigma is the main class. It represents the core of any instance id sigma.js.
+ * It is private and can be initialized only from inside sigma.js. To see its
+ * public interface, see {@link SigmaPublic}.
+ * It owns its own {@link Graph}, {@link MouseCaptor}, {@link Plotter}
+ * and {@link Monitor}.
+ * @constructor
+ * @extends sigma.classes.Cascade
+ * @extends sigma.classes.EventDispatcher
+ * @param {element} root The DOM root of this instance (a div, for example).
+ * @param {string} id    The ID of this instance.
+ * @this {Sigma}
+ */
+function Sigma(root, id) {
+  sigma.classes.Cascade.call(this);
+  sigma.classes.EventDispatcher.call(this);
+
+  /**
+   * Represents "this", without the well-known scope issue.
+   * @private
+   * @type {Sigma}
+   */
+  var self = this;
+
+  /**
+   * The ID of the instance.
+   * @type {string}
+   */
+  this.id = id.toString();
+
+  /**
+   * The different parameters that define how this instance should work.
+   * @see sigma.classes.Cascade
+   * @type {Object}
+   */
+  this.p = {
+    auto: true,
+    drawNodes: 2,
+    drawEdges: 1,
+    drawLabels: 2,
+    lastNodes: 2,
+    lastEdges: 0,
+    lastLabels: 2,
+    drawHoverNodes: true,
+    drawActiveNodes: true
+  };
+
+  /**
+   * The root DOM element of this instance, containing every other elements.
+   * @type {element}
+   */
+  this.domRoot = root;
+
+  /**
+   * The width of this instance - initially, the root's width.
+   * @type {number}
+   */
+  this.width = this.domRoot.offsetWidth;
+
+  /**
+   * The height of this instance - initially, the root's height.
+   * @type {number}
+   */
+  this.height = this.domRoot.offsetHeight;
+
+  /**
+   * The graph of this instance - initiallyempty.
+   * @type {Graph}
+   */
+  this.graph = new Graph();
+
+  /**
+   * An object referencing every DOM elements used by this instance.
+   * @type {Object}
+   */
+  this.domElements = {};
+
+  initDOM('edges', 'canvas');
+  initDOM('nodes', 'canvas');
+  initDOM('labels', 'canvas');
+  initDOM('hover', 'canvas');
+  initDOM('monitor', 'div');
+  initDOM('mouse', 'canvas');
+
+  /**
+   * The class dedicated to manage the drawing process of the graph of the
+   * different canvas.
+   * @type {Plotter}
+   */
+  this.plotter = new Plotter(
+    this.domElements.nodes.getContext('2d'),
+    this.domElements.edges.getContext('2d'),
+    this.domElements.labels.getContext('2d'),
+    this.domElements.hover.getContext('2d'),
+    this.graph,
+    this.width,
+    this.height
+  );
+
+  /**
+   * The class dedicated to monitor different probes about the running
+   * processes or the data, such as the number of nodes or edges, or how
+   * many times the graph is drawn per second.
+   * @type {Monitor}
+   */
+  this.monitor = new Monitor(
+    this,
+    this.domElements.monitor
+  );
+
+  /**
+   * The class dedicated to manage the different mouse events.
+   * @type {MouseCaptor}
+   */
+  this.mousecaptor = new MouseCaptor(
+    this.domElements.mouse,
+    this.id
+  );
+
+  // Interaction listeners:
+  this.mousecaptor.bind('drag interpolate', function(e) {
+    self.draw(
+      self.p.auto ? 2 : self.p.drawNodes,
+      self.p.auto ? 0 : self.p.drawEdges,
+      self.p.auto ? 2 : self.p.drawLabels,
+      true
+    );
+  }).bind('stopdrag stopinterpolate', function(e) {
+    self.draw(
+      self.p.auto ? 2 : self.p.drawNodes,
+      self.p.auto ? 1 : self.p.drawEdges,
+      self.p.auto ? 2 : self.p.drawLabels,
+      true
+    );
+  }).bind('mousedown mouseup', function(e) {
+    var targeted = self.graph.nodes.filter(function(n) {
+      return !!n['hover'];
+    }).map(function(n) {
+      return n.id;
+    });
+
+    self.dispatch(
+      e['type'] == 'mousedown' ?
+        'downgraph' :
+        'upgraph'
+    );
+
+    if (targeted.length) {
+      self.dispatch(
+        e['type'] == 'mousedown' ?
+          'downnodes' :
+          'upnodes',
+        targeted
+      );
+    }
+  }).bind('move', function() {
+    self.domElements.hover.getContext('2d').clearRect(
+      0,
+      0,
+      self.domElements.hover.width,
+      self.domElements.hover.height
+    );
+
+    drawHover();
+    drawActive();
+  });
+
+  sigma.chronos.bind('startgenerators', function() {
+    if (sigma.chronos.getGeneratorsIDs().some(function(id) {
+      return !!id.match(new RegExp('_ext_' + self.id + '$', ''));
+    })) {
+      self.draw(
+        self.p.auto ? 2 : self.p.drawNodes,
+        self.p.auto ? 0 : self.p.drawEdges,
+        self.p.auto ? 2 : self.p.drawLabels
+      );
+    }
+  }).bind('stopgenerators', function() {
+    self.draw();
+  });
+
+  /**
+   * Resizes the element, and redraws the graph with the last settings.
+   * @param  {?number} w The new width (if undefined, it will use the root
+   *                     width).
+   * @param  {?number} h The new height (if undefined, it will use the root
+   *                     height).
+   * @return {Sigma} Returns itself.
+   */
+  function resize(w, h) {
+    var oldW = self.width, oldH = self.height;
+
+    if (w != undefined && h != undefined) {
+      self.width = w;
+      self.height = h;
+    }else {
+      self.width = self.domRoot.offsetWidth;
+      self.height = self.domRoot.offsetHeight;
+    }
+
+    if (oldW != self.width || oldH != self.height) {
+      for (var k in self.domElements) {
+        self.domElements[k].setAttribute('width', self.width + 'px');
+        self.domElements[k].setAttribute('height', self.height + 'px');
+      }
+
+      self.plotter.resize(self.width, self.height);
+
+      self.draw(
+        self.p.lastNodes,
+        self.p.lastEdges,
+        self.p.lastLabels,
+        true
+      );
+    }
+    return self;
+  };
+
+  /**
+   * Kills every drawing task currently running. Basically, it stops this
+   * instance's drawing process.
+   * @return {Sigma} Returns itself.
+   */
+  function clearSchedule() {
+    sigma.chronos.removeTask(
+      'node_' + self.id, 2
+    ).removeTask(
+      'edge_' + self.id, 2
+    ).removeTask(
+      'label_' + self.id, 2
+    ).stopTasks();
+    return self;
+  };
+
+  /**
+   * Initialize a DOM element, that will be stores by this instance, to make
+   * automatic these elements resizing.
+   * @private
+   * @param  {string} id   The element's ID.
+   * @param  {string} type The element's nodeName (Example : canvas, div, ...).
+   * @return {Sigma} Returns itself.
+   */
+  function initDOM(id, type) {
+    self.domElements[id] = document.createElement(type);
+    self.domElements[id].style.position = 'absolute';
+    self.domElements[id].setAttribute('id', 'sigma_' + id + '_' + self.id);
+    self.domElements[id].setAttribute('class', 'sigma_' + id + '_' + type);
+    self.domElements[id].setAttribute('width', self.width + 'px');
+    self.domElements[id].setAttribute('height', self.height + 'px');
+
+    self.domRoot.appendChild(self.domElements[id]);
+    return self;
+  };
+
+  /**
+   * Starts the graph drawing process. The three first parameters indicate
+   * how the different layers have to be drawn:
+   * . -1: The layer is not drawn, but it is not erased.
+   * . 0:  The layer is not drawn.
+   * . 1:  The layer is drawn progressively.
+   * . 2:  The layer is drawn directly.
+   * @param  {?number} nodes  Determines if and how the nodes must be drawn.
+   * @param  {?number} edges  Determines if and how the edges must be drawn.
+   * @param  {?number} labels Determines if and how the labels must be drawn.
+   * @param  {?boolean} safe  If true, nothing will happen if any generator
+   *                          affiliated to this instance is currently running
+   *                          (an iterative layout, for example).
+   * @return {Sigma} Returns itself.
+   */
+  function draw(nodes, edges, labels, safe) {
+    if (safe && sigma.chronos.getGeneratorsIDs().some(function(id) {
+      return !!id.match(new RegExp('_ext_' + self.id + '$', ''));
+    })) {
+      return self;
+    }
+
+    var n = (nodes == undefined) ? self.p.drawNodes : nodes;
+    var e = (edges == undefined) ? self.p.drawEdges : edges;
+    var l = (labels == undefined) ? self.p.drawLabels : labels;
+
+    var params = {
+      nodes: n,
+      edges: e,
+      labels: l
+    };
+
+    self.p.lastNodes = n;
+    self.p.lastEdges = e;
+    self.p.lastLabels = l;
+
+    // Remove tasks:
+    clearSchedule();
+
+    // Rescale graph:
+    self.graph.rescale(
+      self.width,
+      self.height,
+      n > 0,
+      e > 0
+    ).setBorders();
+
+    self.mousecaptor.checkBorders(
+      self.graph.borders,
+      self.width,
+      self.height
+    );
+
+    self.graph.translate(
+      self.mousecaptor.stageX,
+      self.mousecaptor.stageY,
+      self.mousecaptor.ratio,
+      n > 0,
+      e > 0
+    );
+
+    self.dispatch(
+      'graphscaled'
+    );
+
+    // Clear scene:
+    for (var k in self.domElements) {
+      if (
+        self.domElements[k].nodeName.toLowerCase() == 'canvas' &&
+        (params[k] == undefined || params[k] >= 0)
+      ) {
+        self.domElements[k].getContext('2d').clearRect(
+          0,
+          0,
+          self.domElements[k].width,
+          self.domElements[k].height
+        );
+      }
+    }
+
+    self.plotter.currentEdgeIndex = 0;
+    self.plotter.currentNodeIndex = 0;
+    self.plotter.currentLabelIndex = 0;
+
+    var previous = null;
+    var start = false;
+
+    if (n) {
+      if (n > 1) {
+        while (self.plotter.task_drawNode()) {}
+      }else {
+        sigma.chronos.addTask(
+          self.plotter.task_drawNode,
+          'node_' + self.id,
+          false
+        );
+
+        start = true;
+        previous = 'node_' + self.id;
+      }
+    }
+
+    if (l) {
+      if (l > 1) {
+        while (self.plotter.task_drawLabel()) {}
+      } else {
+        if (previous) {
+          sigma.chronos.queueTask(
+            self.plotter.task_drawLabel,
+            'label_' + self.id,
+            previous
+          );
+        } else {
+          sigma.chronos.addTask(
+            self.plotter.task_drawLabel,
+            'label_' + self.id,
+            false
+          );
+        }
+
+        start = true;
+        previous = 'label_' + self.id;
+      }
+    }
+
+    if (e) {
+      if (e > 1) {
+        while (self.plotter.task_drawEdge()) {}
+      }else {
+        if (previous) {
+          sigma.chronos.queueTask(
+            self.plotter.task_drawEdge,
+            'edge_' + self.id,
+            previous
+          );
+        }else {
+          sigma.chronos.addTask(
+            self.plotter.task_drawEdge,
+            'edge_' + self.id,
+            false
+          );
+        }
+
+        start = true;
+        previous = 'edge_' + self.id;
+      }
+    }
+
+    self.dispatch(
+      'draw'
+    );
+
+    self.refresh();
+
+    start && sigma.chronos.runTasks();
+    return self;
+  };
+
+  /**
+   * Draws the hover and active nodes labels.
+   * @return {Sigma} Returns itself.
+   */
+  function refresh() {
+    self.domElements.hover.getContext('2d').clearRect(
+      0,
+      0,
+      self.domElements.hover.width,
+      self.domElements.hover.height
+    );
+
+    drawHover();
+    drawActive();
+
+    return self;
+  }
+
+  /**
+   * Draws the hover nodes labels. This method is applied directly, and does
+   * not use the pseudo-asynchronous tasks process.
+   * @return {Sigma} Returns itself.
+   */
+  function drawHover() {
+    if (self.p.drawHoverNodes) {
+      self.graph.checkHover(
+        self.mousecaptor.mouseX,
+        self.mousecaptor.mouseY
+      );
+
+      self.graph.nodes.forEach(function(node) {
+        if (node.hover && !node.active) {
+          self.plotter.drawHoverNode(node);
+        }
+      });
+    }
+
+    return self;
+  }
+
+  /**
+   * Draws the active nodes labels. This method is applied directly, and does
+   * not use the pseudo-asynchronous tasks process.
+   * @return {Sigma} Returns itself.
+   */
+  function drawActive() {
+    if (self.p.drawActiveNodes) {
+      self.graph.nodes.forEach(function(node) {
+        if (node.active) {
+          self.plotter.drawActiveNode(node);
+        }
+      });
+    }
+
+    return self;
+  }
+
+  // Apply plugins:
+  for (var i = 0; i < local.plugins.length; i++) {
+    local.plugins[i](this);
+  }
+
+  this.draw = draw;
+  this.resize = resize;
+  this.refresh = refresh;
+  this.drawHover = drawHover;
+  this.drawActive = drawActive;
+  this.clearSchedule = clearSchedule;
+
+  window.addEventListener('resize', function() {
+    self.resize();
+  });
+}
+
+/**
+ * This class draws the graph on the different canvas DOM elements. It just
+ * contains all the different methods to draw the graph, synchronously or
+ * pseudo-asynchronously.
+ * @constructor
+ * @param {CanvasRenderingContext2D} nodesCtx  Context dedicated to draw nodes.
+ * @param {CanvasRenderingContext2D} edgesCtx  Context dedicated to draw edges.
+ * @param {CanvasRenderingContext2D} labelsCtx Context dedicated to draw
+ *                                             labels.
+ * @param {CanvasRenderingContext2D} hoverCtx  Context dedicated to draw hover
+ *                                             nodes labels.
+ * @param {Graph} graph                        A reference to the graph to
+ *                                             draw.
+ * @param {number} w                           The width of the DOM root
+ *                                             element.
+ * @param {number} h                           The width of the DOM root
+ *                                             element.
+ * @extends sigma.classes.Cascade
+ * @this {Plotter}
+ */
+function Plotter(nodesCtx, edgesCtx, labelsCtx, hoverCtx, graph, w, h) {
+  sigma.classes.Cascade.call(this);
+
+  /**
+   * Represents "this", without the well-known scope issue.
+   * @private
+   * @type {Plotter}
+   */
+  var self = this;
+
+  /**
+   * The different parameters that define how this instance should work.
+   * @see sigma.classes.Cascade
+   * @type {Object}
+   */
+  this.p = {
+    // -------
+    // LABELS:
+    // -------
+    //   Label color:
+    //   - 'node'
+    //   - default (then defaultLabelColor
+    //              will be used instead)
+    labelColor: 'default',
+    defaultLabelColor: '#000',
+    //   Label hover background color:
+    //   - 'node'
+    //   - default (then defaultHoverLabelBGColor
+    //              will be used instead)
+    labelHoverBGColor: 'default',
+    defaultHoverLabelBGColor: '#fff',
+    //   Label hover shadow:
+    labelHoverShadow: true,
+    labelHoverShadowColor: '#000',
+    //   Label hover color:
+    //   - 'node'
+    //   - default (then defaultLabelHoverColor
+    //              will be used instead)
+    labelHoverColor: 'default',
+    defaultLabelHoverColor: '#000',
+    //   Label active background color:
+    //   - 'node'
+    //   - default (then defaultActiveLabelBGColor
+    //              will be used instead)
+    labelActiveBGColor: 'default',
+    defaultActiveLabelBGColor: '#fff',
+    //   Label active shadow:
+    labelActiveShadow: true,
+    labelActiveShadowColor: '#000',
+    //   Label active color:
+    //   - 'node'
+    //   - default (then defaultLabelActiveColor
+    //              will be used instead)
+    labelActiveColor: 'default',
+    defaultLabelActiveColor: '#000',
+    //   Label size:
+    //   - 'fixed'
+    //   - 'proportional'
+    //   Label size:
+    //   - 'fixed'
+    //   - 'proportional'
+    labelSize: 'fixed',
+    defaultLabelSize: 12, // for fixed display only
+    labelSizeRatio: 2,    // for proportional display only
+    labelThreshold: 6,
+    font: 'Arial',
+    hoverFont: '',
+    activeFont: '',
+    fontStyle: '',
+    hoverFontStyle: '',
+    activeFontStyle: '',
+    // ------
+    // EDGES:
+    // ------
+    //   Edge color:
+    //   - 'source'
+    //   - 'target'
+    //   - default (then defaultEdgeColor or edge['color']
+    //              will be used instead)
+    edgeColor: 'source',
+    defaultEdgeColor: '#aaa',
+    defaultEdgeType: 'line',
+    // ------
+    // NODES:
+    // ------
+    defaultNodeColor: '#aaa',
+    // HOVER:
+    //   Node hover color:
+    //   - 'node'
+    //   - default (then defaultNodeHoverColor
+    //              will be used instead)
+    nodeHoverColor: 'node',
+    defaultNodeHoverColor: '#fff',
+    // ACTIVE:
+    //   Node active color:
+    //   - 'node'
+    //   - default (then defaultNodeActiveColor
+    //              will be used instead)
+    nodeActiveColor: 'node',
+    defaultNodeActiveColor: '#fff',
+    //   Node border color:
+    //   - 'node'
+    //   - default (then defaultNodeBorderColor
+    //              will be used instead)
+    borderSize: 0,
+    nodeBorderColor: 'node',
+    defaultNodeBorderColor: '#fff',
+    // --------
+    // PROCESS:
+    // --------
+    edgesSpeed: 200,
+    nodesSpeed: 200,
+    labelsSpeed: 200
+  };
+
+  /**
+   * The canvas context dedicated to draw the nodes.
+   * @type {CanvasRenderingContext2D}
+   */
+  var nodesCtx = nodesCtx;
+
+  /**
+   * The canvas context dedicated to draw the edges.
+   * @type {CanvasRenderingContext2D}
+   */
+  var edgesCtx = edgesCtx;
+
+  /**
+   * The canvas context dedicated to draw the labels.
+   * @type {CanvasRenderingContext2D}
+   */
+  var labelsCtx = labelsCtx;
+
+  /**
+   * The canvas context dedicated to draw the hover nodes.
+   * @type {CanvasRenderingContext2D}
+   */
+  var hoverCtx = hoverCtx;
+
+  /**
+   * A reference to the graph to draw.
+   * @type {Graph}
+   */
+  var graph = graph;
+
+  /**
+   * The width of the stage to draw on.
+   * @type {number}
+   */
+  var width = w;
+
+  /**
+   * The height of the stage to draw on.
+   * @type {number}
+   */
+  var height = h;
+
+  /**
+   * The index of the next edge to draw.
+   * @type {number}
+   */
+  this.currentEdgeIndex = 0;
+
+  /**
+   * The index of the next node to draw.
+   * @type {number}
+   */
+  this.currentNodeIndex = 0;
+
+  /**
+   * The index of the next label to draw.
+   * @type {number}
+   */
+  this.currentLabelIndex = 0;
+
+  /**
+   * An atomic function to drawn the N next edges, with N as edgesSpeed.
+   * The counter is {@link this.currentEdgeIndex}.
+   * This function has been designed to work with {@link sigma.chronos}, that
+   * will insert frames at the middle of the calls, to make the edges drawing
+   * process fluid for the user.
+   * @see sigma.chronos
+   * @return {boolean} Returns true if all the edges are drawn and false else.
+   */
+  function task_drawEdge() {
+    var c = graph.edges.length;
+    var s, t, i = 0;
+
+    while (i++< self.p.edgesSpeed && self.currentEdgeIndex < c) {
+      e = graph.edges[self.currentEdgeIndex];
+      s = e['source'];
+      t = e['target'];
+      if (e['hidden'] ||
+          s['hidden'] ||
+          t['hidden'] ||
+          (!self.isOnScreen(s) && !self.isOnScreen(t))) {
+        self.currentEdgeIndex++;
+      }else {
+        drawEdge(graph.edges[self.currentEdgeIndex++]);
+      }
+    }
+
+    return self.currentEdgeIndex < c;
+  };
+
+  /**
+   * An atomic function to drawn the N next nodes, with N as nodesSpeed.
+   * The counter is {@link this.currentEdgeIndex}.
+   * This function has been designed to work with {@link sigma.chronos}, that
+   * will insert frames at the middle of the calls, to make the nodes drawing
+   * process fluid for the user.
+   * @see sigma.chronos
+   * @return {boolean} Returns true if all the nodes are drawn and false else.
+   */
+  function task_drawNode() {
+    var c = graph.nodes.length;
+    var i = 0;
+
+    while (i++< self.p.nodesSpeed && self.currentNodeIndex < c) {
+      if (!self.isOnScreen(graph.nodes[self.currentNodeIndex])) {
+        self.currentNodeIndex++;
+      }else {
+        drawNode(graph.nodes[self.currentNodeIndex++]);
+      }
+    }
+
+    return self.currentNodeIndex < c;
+  };
+
+  /**
+   * An atomic function to drawn the N next labels, with N as labelsSpeed.
+   * The counter is {@link this.currentEdgeIndex}.
+   * This function has been designed to work with {@link sigma.chronos}, that
+   * will insert frames at the middle of the calls, to make the labels drawing
+   * process fluid for the user.
+   * @see sigma.chronos
+   * @return {boolean} Returns true if all the labels are drawn and false else.
+   */
+  function task_drawLabel() {
+    var c = graph.nodes.length;
+    var i = 0;
+
+    while (i++< self.p.labelsSpeed && self.currentLabelIndex < c) {
+      if (!self.isOnScreen(graph.nodes[self.currentLabelIndex])) {
+        self.currentLabelIndex++;
+      }else {
+        drawLabel(graph.nodes[self.currentLabelIndex++]);
+      }
+    }
+
+    return self.currentLabelIndex < c;
+  };
+
+  /**
+   * Draws one node to the corresponding canvas.
+   * @param  {Object} node The node to draw.
+   * @return {Plotter} Returns itself.
+   */
+  function drawNode(node) {
+    var size = Math.round(node['displaySize'] * 10) / 10;
+    var ctx = nodesCtx;
+
+    ctx.fillStyle = node['color'];
+    ctx.beginPath();
+    ctx.arc(node['displayX'],
+            node['displayY'],
+            size,
+            0,
+            Math.PI * 2,
+            true);
+
+    ctx.closePath();
+    ctx.fill();
+
+    node['hover'] && drawHoverNode(node);
+    return self;
+  };
+
+  /**
+   * Draws one edge to the corresponding canvas.
+   * @param  {Object} edge The edge to draw.
+   * @return {Plotter} Returns itself.
+   */
+  function drawEdge(edge) {
+    var x1 = edge['source']['displayX'];
+    var y1 = edge['source']['displayY'];
+    var x2 = edge['target']['displayX'];
+    var y2 = edge['target']['displayY'];
+    var color = edge['color'];
+
+    if (!color) {
+      switch (self.p.edgeColor) {
+        case 'source':
+          color = edge['source']['color'] ||
+                  self.p.defaultNodeColor;
+          break;
+        case 'target':
+          color = edge['target']['color'] ||
+                  self.p.defaultNodeColor;
+          break;
+        default:
+          color = self.p.defaultEdgeColor;
+          break;
+      }
+    }
+
+    var ctx = edgesCtx;
+
+    switch (edge['type'] || self.p.defaultEdgeType) {
+      case 'curve':
+        ctx.strokeStyle = color;
+        ctx.lineWidth = edge['displaySize'] / 3;
+        ctx.beginPath();
+        ctx.moveTo(x1, y1);
+        ctx.quadraticCurveTo((x1 + x2) / 2 + (y2 - y1) / 4,
+                             (y1 + y2) / 2 + (x1 - x2) / 4,
+                             x2,
+                             y2);
+        ctx.stroke();
+        break;
+      case 'line':
+      default:
+        ctx.strokeStyle = color;
+        ctx.lineWidth = edge['displaySize'] / 3;
+        ctx.beginPath();
+        ctx.moveTo(x1, y1);
+        ctx.lineTo(x2, y2);
+
+        ctx.stroke();
+        break;
+    }
+
+    return self;
+  };
+
+  /**
+   * Draws one label to the corresponding canvas.
+   * @param  {Object} node The label to draw.
+   * @return {Plotter} Returns itself.
+   */
+  function drawLabel(node) {
+    var ctx = labelsCtx;
+
+    if (node['displaySize'] >= self.p.labelThreshold || node['forceLabel']) {
+      var fontSize = self.p.labelSize == 'fixed' ?
+                     self.p.defaultLabelSize :
+                     self.p.labelSizeRatio * node['displaySize'];
+
+      ctx.font = self.p.fontStyle + fontSize + 'px ' + self.p.font;
+
+      ctx.fillStyle = self.p.labelColor == 'node' ?
+                      (node['color'] || self.p.defaultNodeColor) :
+                      self.p.defaultLabelColor;
+      ctx.fillText(
+        node['label'],
+        Math.round(node['displayX'] + node['displaySize'] * 1.5),
+        Math.round(node['displayY'] + fontSize / 2 - 3)
+      );
+    }
+
+    return self;
+  };
+
+  /**
+   * Draws one hover node to the corresponding canvas.
+   * @param  {Object} node The hover node to draw.
+   * @return {Plotter} Returns itself.
+   */
+  function drawHoverNode(node) {
+    var ctx = hoverCtx;
+
+    var fontSize = self.p.labelSize == 'fixed' ?
+                   self.p.defaultLabelSize :
+                   self.p.labelSizeRatio * node['displaySize'];
+
+    ctx.font = (self.p.hoverFontStyle || self.p.fontStyle || '') + ' ' +
+               fontSize + 'px ' +
+               (self.p.hoverFont || self.p.font || '');
+
+    ctx.fillStyle = self.p.labelHoverBGColor == 'node' ?
+                    (node['color'] || self.p.defaultNodeColor) :
+                    self.p.defaultHoverLabelBGColor;
+
+    // Label background:
+    ctx.beginPath();
+
+    if (self.p.labelHoverShadow) {
+      ctx.shadowOffsetX = 0;
+      ctx.shadowOffsetY = 0;
+      ctx.shadowBlur = 4;
+      ctx.shadowColor = self.p.labelHoverShadowColor;
+    }
+
+    sigma.tools.drawRoundRect(
+      ctx,
+      Math.round(node['displayX'] - fontSize / 2 - 2),
+      Math.round(node['displayY'] - fontSize / 2 - 2),
+      Math.round(ctx.measureText(node['label']).width +
+        node['displaySize'] * 1.5 +
+        fontSize / 2 + 4),
+      Math.round(fontSize + 4),
+      Math.round(fontSize / 2 + 2),
+      'left'
+    );
+    ctx.closePath();
+    ctx.fill();
+
+    ctx.shadowOffsetX = 0;
+    ctx.shadowOffsetY = 0;
+    ctx.shadowBlur = 0;
+
+    // Node border:
+    ctx.beginPath();
+    ctx.fillStyle = self.p.nodeBorderColor == 'node' ?
+                    (node['color'] || self.p.defaultNodeColor) :
+                    self.p.defaultNodeBorderColor;
+    ctx.arc(Math.round(node['displayX']),
+            Math.round(node['displayY']),
+            node['displaySize'] + self.p.borderSize,
+            0,
+            Math.PI * 2,
+            true);
+    ctx.closePath();
+    ctx.fill();
+
+    // Node:
+    ctx.beginPath();
+    ctx.fillStyle = self.p.nodeHoverColor == 'node' ?
+                    (node['color'] || self.p.defaultNodeColor) :
+                    self.p.defaultNodeHoverColor;
+    ctx.arc(Math.round(node['displayX']),
+            Math.round(node['displayY']),
+            node['displaySize'],
+            0,
+            Math.PI * 2,
+            true);
+
+    ctx.closePath();
+    ctx.fill();
+
+    // Label:
+    ctx.fillStyle = self.p.labelHoverColor == 'node' ?
+                    (node['color'] || self.p.defaultNodeColor) :
+                    self.p.defaultLabelHoverColor;
+    ctx.fillText(
+      node['label'],
+      Math.round(node['displayX'] + node['displaySize'] * 1.5),
+      Math.round(node['displayY'] + fontSize / 2 - 3)
+    );
+
+    return self;
+  };
+
+  /**
+   * Draws one active node to the corresponding canvas.
+   * @param  {Object} node The active node to draw.
+   * @return {Plotter} Returns itself.
+   */
+  function drawActiveNode(node) {
+    var ctx = hoverCtx;
+
+    if (!isOnScreen(node)) {
+      return self;
+    }
+
+    var fontSize = self.p.labelSize == 'fixed' ?
+                   self.p.defaultLabelSize :
+                   self.p.labelSizeRatio * node['displaySize'];
+
+    ctx.font = (self.p.activeFontStyle || self.p.fontStyle || '') + ' ' +
+               fontSize + 'px ' +
+               (self.p.activeFont || self.p.font || '');
+
+    ctx.fillStyle = self.p.labelHoverBGColor == 'node' ?
+                    (node['color'] || self.p.defaultNodeColor) :
+                    self.p.defaultActiveLabelBGColor;
+
+    // Label background:
+    ctx.beginPath();
+
+    if (self.p.labelActiveShadow) {
+      ctx.shadowOffsetX = 0;
+      ctx.shadowOffsetY = 0;
+      ctx.shadowBlur = 4;
+      ctx.shadowColor = self.p.labelActiveShadowColor;
+    }
+
+    sigma.tools.drawRoundRect(
+      ctx,
+      Math.round(node['displayX'] - fontSize / 2 - 2),
+      Math.round(node['displayY'] - fontSize / 2 - 2),
+      Math.round(ctx.measureText(node['label']).width +
+        node['displaySize'] * 1.5 +
+        fontSize / 2 + 4),
+      Math.round(fontSize + 4),
+      Math.round(fontSize / 2 + 2),
+      'left'
+    );
+    ctx.closePath();
+    ctx.fill();
+
+    ctx.shadowOffsetX = 0;
+    ctx.shadowOffsetY = 0;
+    ctx.shadowBlur = 0;
+
+    // Node border:
+    ctx.beginPath();
+    ctx.fillStyle = self.p.nodeBorderColor == 'node' ?
+                    (node['color'] || self.p.defaultNodeColor) :
+                    self.p.defaultNodeBorderColor;
+    ctx.arc(Math.round(node['displayX']),
+            Math.round(node['displayY']),
+            node['displaySize'] + self.p.borderSize,
+            0,
+            Math.PI * 2,
+            true);
+    ctx.closePath();
+    ctx.fill();
+
+    // Node:
+    ctx.beginPath();
+    ctx.fillStyle = self.p.nodeActiveColor == 'node' ?
+                    (node['color'] || self.p.defaultNodeColor) :
+                    self.p.defaultNodeActiveColor;
+    ctx.arc(Math.round(node['displayX']),
+            Math.round(node['displayY']),
+            node['displaySize'],
+            0,
+            Math.PI * 2,
+            true);
+
+    ctx.closePath();
+    ctx.fill();
+
+    // Label:
+    ctx.fillStyle = self.p.labelActiveColor == 'node' ?
+                    (node['color'] || self.p.defaultNodeColor) :
+                    self.p.defaultLabelActiveColor;
+    ctx.fillText(
+      node['label'],
+      Math.round(node['displayX'] + node['displaySize'] * 1.5),
+      Math.round(node['displayY'] + fontSize / 2 - 3)
+    );
+
+    return self;
+  };
+
+  /**
+   * Determines if a node is on the screen or not. The limits here are
+   * bigger than the actual screen, to avoid seeing labels disappear during
+   * the graph manipulation.
+   * @param  {Object}  node The node to check if it is on or out the screen.
+   * @return {boolean} Returns false if the node is hidden or not on the screen
+   *                   or true else.
+   */
+  function isOnScreen(node) {
+    if (isNaN(node['x']) || isNaN(node['y'])) {
+      throw (new Error('A node\'s coordinate is not a ' +
+                       'number (id: ' + node['id'] + ')')
+      );
+    }
+
+    return !node['hidden'] &&
+           (node['displayX'] + node['displaySize'] > -width / 3) &&
+           (node['displayX'] - node['displaySize'] < width * 4 / 3) &&
+           (node['displayY'] + node['displaySize'] > -height / 3) &&
+           (node['displayY'] - node['displaySize'] < height * 4 / 3);
+  };
+
+  /**
+   * Resizes this instance.
+   * @param  {number} w The new width.
+   * @param  {number} h The new height.
+   * @return {Plotter} Returns itself.
+   */
+  function resize(w, h) {
+    width = w;
+    height = h;
+
+    return self;
+  }
+
+  this.task_drawLabel = task_drawLabel;
+  this.task_drawEdge = task_drawEdge;
+  this.task_drawNode = task_drawNode;
+  this.drawActiveNode = drawActiveNode;
+  this.drawHoverNode = drawHoverNode;
+  this.isOnScreen = isOnScreen;
+  this.resize = resize;
+}
+
+function SigmaPublic(sigmaInstance) {
+  var s = sigmaInstance;
+  var self = this;
+  sigma.classes.EventDispatcher.call(this);
+
+  this._core = sigmaInstance;
+
+  this.kill = function() {
+    // TODO
+  };
+
+  this.getID = function() {
+    return s.id;
+  };
+
+  // Config:
+  this.configProperties = function(a1, a2) {
+    var res = s.config(a1, a2);
+    return res == s ? self : res;
+  };
+
+  this.drawingProperties = function(a1, a2) {
+    var res = s.plotter.config(a1, a2);
+    return res == s.plotter ? self : res;
+  };
+
+  this.mouseProperties = function(a1, a2) {
+    var res = s.mousecaptor.config(a1, a2);
+    return res == s.mousecaptor ? self : res;
+  };
+
+  this.graphProperties = function(a1, a2) {
+    var res = s.graph.config(a1, a2);
+    return res == s.graph ? self : res;
+  };
+
+  this.getMouse = function() {
+    return {
+      mouseX: s.mousecaptor.mouseX,
+      mouseY: s.mousecaptor.mouseY,
+      down: s.mousecaptor.isMouseDown
+    };
+  };
+
+  // Actions:
+  this.position = function(stageX, stageY, ratio) {
+    if (arguments.length == 0) {
+      return {
+        stageX: s.mousecaptor.stageX,
+        stageY: s.mousecaptor.stageY,
+        ratio: s.mousecaptor.ratio
+      };
+    }else {
+      s.mousecaptor.stageX = stageX != undefined ?
+        stageX :
+        s.mousecaptor.stageX;
+      s.mousecaptor.stageY = stageY != undefined ?
+        stageY :
+        s.mousecaptor.stageY;
+      s.mousecaptor.ratio = ratio != undefined ?
+        ratio :
+        s.mousecaptor.ratio;
+
+      return self;
+    }
+  };
+
+  this.goTo = function(stageX, stageY, ratio) {
+    s.mousecaptor.interpolate(stageX, stageY, ratio);
+    return self;
+  };
+
+  this.zoomTo = function(x, y, ratio) {
+    ratio = Math.min(
+              Math.max(s.mousecaptor.config('minRatio'), ratio),
+              s.mousecaptor.config('maxRatio')
+            );
+    if (ratio == s.mousecaptor.ratio) {
+      s.mousecaptor.interpolate(
+        x - s.width / 2 + s.mousecaptor.stageX,
+        y - s.height / 2 + s.mousecaptor.stageY
+      );
+    }else {
+      s.mousecaptor.interpolate(
+        (ratio * x - s.mousecaptor.ratio * s.width/2) /
+        (ratio - s.mousecaptor.ratio),
+        (ratio * y - s.mousecaptor.ratio * s.height/2) /
+        (ratio - s.mousecaptor.ratio),
+        ratio
+      );
+    }
+    return self;
+  };
+
+  this.resize = function(w, h) {
+    s.resize(w, h);
+    return self;
+  };
+
+  this.draw = function(nodes, edges, labels, safe) {
+    s.draw(nodes, edges, labels, safe);
+    return self;
+  };
+
+  this.refresh = function() {
+    s.refresh();
+    return self;
+  };
+
+  // Tasks methods:
+  this.addGenerator = function(id, task, condition) {
+    sigma.chronos.addGenerator(id + '_ext_' + s.id, task, condition);
+    return self;
+  };
+
+  this.removeGenerator = function(id) {
+    sigma.chronos.removeGenerator(id + '_ext_' + s.id);
+    return self;
+  };
+
+  // Graph methods:
+  this.addNode = function(id, params) {
+    s.graph.addNode(id, params);
+    return self;
+  };
+
+  this.addEdge = function(id, source, target, params) {
+    s.graph.addEdge(id, source, target, params);
+    return self;
+  }
+
+  this.dropNode = function(v) {
+    s.graph.dropNode(v);
+    return self;
+  };
+
+  this.dropEdge = function(v) {
+    s.graph.dropEdge(v);
+    return self;
+  };
+
+  this.pushGraph = function(object, safe) {
+    object.nodes && object.nodes.forEach(function(node) {
+      node['id'] && (!safe || !s.graph.nodesIndex[node['id']]) &&
+                    self.addNode(node['id'], node);
+    });
+
+    var isEdgeValid;
+    object.edges && object.edges.forEach(function(edge) {
+      validID = edge['source'] && edge['target'] && edge['id'];
+      validID &&
+        (!safe || !s.graph.edgesIndex[edge['id']]) &&
+        self.addNode(
+          edge['id'],
+          edge['source'],
+          edge['target'],
+          edge
+        );
+    });
+
+    return self;
+  };
+
+  this.emptyGraph = function() {
+    s.graph.empty();
+    return self;
+  };
+
+  this.getNodesCount = function() {
+    return s.graph.nodes.length;
+  };
+
+  this.getEdgesCount = function() {
+    return s.graph.edges.length;
+  };
+
+  this.iterNodes = function(fun, ids) {
+    s.graph.iterNodes(fun, ids);
+    return self;
+  };
+
+  this.iterEdges = function(fun, ids) {
+    s.graph.iterEdges(fun, ids);
+    return self;
+  };
+
+  this.getNodes = function(ids) {
+    return s.graph.getNodes(ids);
+  };
+
+  this.getEdges = function(ids) {
+    return s.graph.getEdges(ids);
+  };
+
+  // Monitoring
+  this.activateMonitoring = function() {
+    return s.monitor.activate();
+  };
+
+  this.desactivateMonitoring = function() {
+    return s.monitor.desactivate();
+  };
+
+  // Events
+  s.bind('downnodes upnodes downgraph upgraph', function(e) {
+    self.dispatch(e.type, e.content);
+  });
+
+  s.graph.bind('overnodes outnodes', function(e) {
+    self.dispatch(e.type, e.content);
+  });
+}
+
+/**
+ * The graph data model used in sigma.js.
+ * @constructor
+ * @extends sigma.classes.Cascade
+ * @extends sigma.classes.EventDispatcher
+ * @this {Graph}
+ */
+function Graph() {
+  sigma.classes.Cascade.call(this);
+  sigma.classes.EventDispatcher.call(this);
+
+  /**
+   * Represents "this", without the well-known scope issue.
+   * @private
+   * @type {Graph}
+   */
+  var self = this;
+
+  /**
+   * The different parameters that determine how the nodes and edges should be
+   * translated and rescaled.
+   * @type {Object}
+   */
+  this.p = {
+    minNodeSize: 0,
+    maxNodeSize: 0,
+    minEdgeSize: 0,
+    maxEdgeSize: 0,
+    //   Scaling mode:
+    //   - 'inside' (default)
+    //   - 'outside'
+    scalingMode: 'inside',
+    nodesPowRatio: 0.5,
+    edgesPowRatio: 0
+  };
+
+  /**
+   * Contains the borders of the graph. These are useful to avoid the user to
+   * drag the graph out of the canvas.
+   * @type {Object}
+   */
+  this.borders = {};
+
+  /**
+   * Inserts a node in the graph.
+   * @param {string} id     The node's ID.
+   * @param {object} params An object containing the different parameters
+   *                        of the node.
+   * @return {Graph} Returns itself.
+   */
+  function addNode(id, params) {
+    if (self.nodesIndex[id]) {
+      throw new Error('Node "' + id + '" already exists.');
+    }
+
+    params = params || {};
+    var n = {
+      // Numbers :
+      'x': 0,
+      'y': 0,
+      'size': 1,
+      'degree': 0,
+      'inDegree': 0,
+      'outDegree': 0,
+      // Flags :
+      'fixed': false,
+      'active': false,
+      'hidden': false,
+      'forceLabel': false,
+      // Strings :
+      'label': id.toString(),
+      'id': id.toString(),
+      // Custom attributes :
+      'attr': {}
+    };
+
+    for (var k in params) {
+      switch (k) {
+        case 'id':
+          break;
+        case 'x':
+        case 'y':
+        case 'size':
+          n[k] = +params[k];
+          break;
+        case 'fixed':
+        case 'active':
+        case 'hidden':
+        case 'forceLabel':
+          n[k] = !!params[k];
+          break;
+        case 'color':
+        case 'label':
+          n[k] = params[k];
+          break;
+        default:
+          n['attr'][k] = params[k];
+      }
+    }
+
+    self.nodes.push(n);
+    self.nodesIndex[id.toString()] = n;
+
+    return self;
+  };
+
+  /**
+   * Generates the clone of a node, to make it easier to be exported.
+   * @private
+   * @param  {Object} node The node to clone.
+   * @return {Object} The clone of the node.
+   */
+  function cloneNode(node) {
+    return {
+      'x': node['x'],
+      'y': node['y'],
+      'size': node['size'],
+      'degree': node['degree'],
+      'inDegree': node['inDegree'],
+      'outDegree': node['outDegree'],
+      'displayX': node['displayX'],
+      'displayY': node['displayY'],
+      'displaySize': node['displaySize'],
+      'label': node['label'],
+      'id': node['id'],
+      'color': node['color'],
+      'fixed': node['fixed'],
+      'active': node['active'],
+      'hidden': node['hidden'],
+      'forceLabel': node['forceLabel'],
+      'attr': node['attr']
+    };
+  };
+
+  /**
+   * Checks the clone of a node, and inserts its values when possible. For
+   * example, it is possible to modify the size or the color of a node, but it
+   * is not possible to modify its display values or its id.
+   * @private
+   * @param  {Object} node The original node.
+   * @param  {Object} copy The clone.
+   * @return {Graph} Returns itself.
+   */
+  function checkNode(node, copy) {
+    for (var k in copy) {
+      switch (k) {
+        case 'id':
+        case 'attr':
+        case 'degree':
+        case 'inDegree':
+        case 'outDegree':
+        case 'displayX':
+        case 'displayY':
+        case 'displaySize':
+          break;
+        case 'x':
+        case 'y':
+        case 'size':
+          node[k] = +copy[k];
+          break;
+        case 'fixed':
+        case 'active':
+        case 'hidden':
+        case 'forceLabel':
+          node[k] = !!copy[k];
+          break;
+        case 'color':
+        case 'label':
+          node[k] = (copy[k] || '').toString();
+          break;
+        default:
+          node['attr'][k] = copy[k];
+      }
+    }
+
+    return self;
+  };
+
+  /**
+   * Deletes one or several nodes from the graph, and the related edges.
+   * @param  {(string|Array.<string>)} v A string ID, or an Array of several
+   *                                     IDs.
+   * @return {Graph} Returns itself.
+   */
+  function dropNode(v) {
+    var a = (v instanceof Array ? v : [v]) || [];
+
+    a.forEach(function(id) {
+      if (self.nodesIndex[id]) {
+        var index = null;
+        self.nodes.some(function(n, i) {
+          if (n['id'] == id) {
+            index = i;
+            return true;
+          }
+          return false;
+        });
+
+        index != null && self.nodes.splice(index, 1);
+        delete self.nodesIndex[id];
+
+        var edgesToRemove = [];
+        self.edges = self.edges.filter(function(e) {
+          if (e['source']['id'] == id) {
+            delete self.edgesIndex[e['id']];
+            e['target']['degree']--;
+            e['target']['inDegree']--;
+            return false;
+          }else if (e['target']['id'] == id) {
+            delete self.edgesIndex[e['id']];
+            e['source']['degree']--;
+            e['source']['outDegree']--;
+            return false;
+          }
+          return true;
+        });
+      }else {
+        sigma.log('Node "' + id + '" does not exist.');
+      }
+    });
+
+    return self;
+  };
+
+  /**
+   * Inserts an edge in the graph.
+   * @param {string} id     The edge ID.
+   * @param {string} source The ID of the edge source.
+   * @param {string} target The ID of the edge target.
+   * @param {object} params An object containing the different parameters
+   *                        of the edge.
+   * @return {Graph} Returns itself.
+   */
+  function addEdge(id, source, target, params) {
+    if (self.edgesIndex[id]) {
+      throw new Error('Edge "' + id + '" already exists.');
+    }
+
+    if (!self.nodesIndex[source]) {
+      var s = 'Edge\'s source "' + source + '" does not exist yet.';
+      throw new Error(s);
+    }
+
+    if (!self.nodesIndex[target]) {
+      var s = 'Edge\'s target "' + target + '" does not exist yet.';
+      throw new Error(s);
+    }
+
+    params = params || {};
+    var e = {
+      'source': self.nodesIndex[source],
+      'target': self.nodesIndex[target],
+      'size': 1,
+      'weight': 1,
+      'displaySize': 0.5,
+      'label': id.toString(),
+      'id': id.toString(),
+      'hidden': false,
+      'attr': {}
+    };
+
+    e['source']['degree']++;
+    e['source']['outDegree']++;
+    e['target']['degree']++;
+    e['target']['inDegree']++;
+
+    for (var k in params) {
+      switch (k) {
+        case 'id':
+        case 'source':
+        case 'target':
+          break;
+        case 'hidden':
+          e[k] = !!params[k];
+          break;
+        case 'size':
+        case 'weight':
+          e[k] = +params[k];
+          break;
+        case 'color':
+          e[k] = params[k].toString();
+          break;
+        case 'type':
+          e[k] = params[k].toString();
+          break;
+        case 'label':
+          e[k] = params[k];
+          break;
+        default:
+          e['attr'][k] = params[k];
+      }
+    }
+
+    self.edges.push(e);
+    self.edgesIndex[id.toString()] = e;
+
+    return self;
+  };
+
+  /**
+   * Generates the clone of a edge, to make it easier to be exported.
+   * @private
+   * @param  {Object} edge The edge to clone.
+   * @return {Object} The clone of the edge.
+   */
+  function cloneEdge(edge) {
+    return {
+      'source': edge['source']['id'],
+      'target': edge['target']['id'],
+      'size': edge['size'],
+      'type': edge['type'],
+      'weight': edge['weight'],
+      'displaySize': edge['displaySize'],
+      'label': edge['label'],
+      'hidden': edge['hidden'],
+      'id': edge['id'],
+      'attr': edge['attr'],
+      'color': edge['color']
+    };
+  };
+
+  /**
+   * Checks the clone of an edge, and inserts its values when possible. For
+   * example, it is possible to modify the label or the type of an edge, but it
+   * is not possible to modify its display values or its id.
+   * @private
+   * @param  {Object} edge The original edge.
+   * @param  {Object} copy The clone.
+   * @return {Graph} Returns itself.
+   */
+  function checkEdge(edge, copy) {
+    for (var k in copy) {
+      switch (k) {
+        case 'id':
+        case 'displaySize':
+          break;
+        case 'weight':
+        case 'size':
+          edge[k] = +copy[k];
+          break;
+        case 'source':
+        case 'target':
+          edge[k] = self.nodesIndex[k] || edge[k];
+          break;
+        case 'hidden':
+          edge[k] = !!copy[k];
+          break;
+        case 'color':
+        case 'label':
+        case 'type':
+          edge[k] = (copy[k] || '').toString();
+          break;
+        default:
+          edge['attr'][k] = copy[k];
+      }
+    }
+
+    return self;
+  };
+
+  /**
+   * Deletes one or several edges from the graph.
+   * @param  {(string|Array.<string>)} v A string ID, or an Array of several
+   *                                     IDs.
+   * @return {Graph} Returns itself.
+   */
+  function dropEdge(v) {
+    var a = (v instanceof Array ? v : [v]) || [];
+
+    a.forEach(function(id) {
+      if (self.edgesIndex[id]) {
+        self.edgesIndex[id]['source']['degree']--;
+        self.edgesIndex[id]['source']['outDegree']--;
+        self.edgesIndex[id]['target']['degree']--;
+        self.edgesIndex[id]['target']['inDegree']--;
+
+        var index = null;
+        self.edges.some(function(n, i) {
+          if (n['id'] == id) {
+            index = i;
+            return true;
+          }
+          return false;
+        });
+
+        index != null && self.edges.splice(index, 1);
+        delete self.edgesIndex[id];
+      }else {
+        sigma.log('Edge "' + id + '" does not exist.');
+      }
+    });
+
+    return self;
+  };
+
+  /**
+   * Deletes every nodes and edges from the graph.
+   * @return {Graph} Returns itself.
+   */
+  function empty() {
+    self.nodes = [];
+    self.nodesIndex = {};
+    self.edges = [];
+    self.edgesIndex = {};
+
+    return self;
+  };
+
+  /**
+   * Computes the display x, y and size of each node, relatively to the
+   * original values and the borders determined in the parameters, such as
+   * each node is in the described area.
+   * @param  {number} w           The area width (actually the width of the DOM
+   *                              root).
+   * @param  {number} h           The area height (actually the height of the
+   *                              DOM root).
+   * @param  {boolean} parseNodes Indicates if the nodes have to be parsed.
+   * @param  {boolean} parseEdges Indicates if the edges have to be parsed.
+   * @return {Graph} Returns itself.
+   */
+  function rescale(w, h, parseNodes, parseEdges) {
+    var weightMax = 0, sizeMax = 0;
+
+    parseNodes && self.nodes.forEach(function(node) {
+      sizeMax = Math.max(node['size'], sizeMax);
+    });
+
+    parseEdges && self.edges.forEach(function(edge) {
+      weightMax = Math.max(edge['size'], weightMax);
+    });
+
+    sizeMax = sizeMax || 1;
+    weightMax = weightMax || 1;
+
+    // Recenter the nodes:
+    var xMin, xMax, yMin, yMax;
+    parseNodes && self.nodes.forEach(function(node) {
+      xMax = Math.max(node['x'], xMax || node['x']);
+      xMin = Math.min(node['x'], xMin || node['x']);
+      yMax = Math.max(node['y'], yMax || node['y']);
+      yMin = Math.min(node['y'], yMin || node['y']);
+    });
+
+    // First, we compute the scaling ratio, without considering the sizes
+    // of the nodes : Each node will have its center in the canvas, but might
+    // be partially out of it.
+    var scale = self.p.scalingMode == 'outside' ?
+                Math.max(w / Math.max(xMax - xMin, 1),
+                         h / Math.max(yMax - yMin, 1)) :
+                Math.min(w / Math.max(xMax - xMin, 1),
+                         h / Math.max(yMax - yMin, 1));
+
+    // Then, we correct that scaling ratio considering a margin, which is
+    // basically the size of the biggest node.
+    // This has to be done as a correction since to compare the size of the
+    // biggest node to the X and Y values, we have to first get an
+    // approximation of the scaling ratio.
+    var margin = (self.p.maxNodeSize || sizeMax) / scale;
+    xMax += margin;
+    xMin -= margin;
+    yMax += margin;
+    yMin -= margin;
+
+    scale = self.p.scalingMode == 'outside' ?
+            Math.max(w / Math.max(xMax - xMin, 1),
+                     h / Math.max(yMax - yMin, 1)) :
+            Math.min(w / Math.max(xMax - xMin, 1),
+                     h / Math.max(yMax - yMin, 1));
+
+    // Size homothetic parameters:
+    var a, b;
+    if (!self.p.maxNodeSize && !self.p.minNodeSize) {
+      a = 1;
+      b = 0;
+    }else if (self.p.maxNodeSize == self.p.minNodeSize) {
+      a = 0;
+      b = self.p.maxNodeSize;
+    }else {
+      a = (self.p.maxNodeSize - self.p.minNodeSize) / sizeMax;
+      b = self.p.minNodeSize;
+    }
+
+    var c, d;
+    if (!self.p.maxEdgeSize && !self.p.minEdgeSize) {
+      c = 1;
+      d = 0;
+    }else if (self.p.maxEdgeSize == self.p.minEdgeSize) {
+      c = 0;
+      d = self.p.minEdgeSize;
+    }else {
+      c = (self.p.maxEdgeSize - self.p.minEdgeSize) / weightMax;
+      d = self.p.minEdgeSize;
+    }
+
+    // Rescale the nodes:
+    parseNodes && self.nodes.forEach(function(node) {
+      node['displaySize'] = node['size'] * a + b;
+
+      if (!node['fixed']) {
+        node['displayX'] = (node['x'] - (xMax + xMin) / 2) * scale + w / 2;
+        node['displayY'] = (node['y'] - (yMax + yMin) / 2) * scale + h / 2;
+      }
+    });
+
+    parseEdges && self.edges.forEach(function(edge) {
+      edge['displaySize'] = edge['size'] * c + d;
+    });
+
+    return self;
+  };
+
+  /**
+   * Translates the display values of the nodes and edges relatively to the
+   * scene position and zoom ratio.
+   * @param  {number} sceneX      The x position of the scene.
+   * @param  {number} sceneY      The y position of the scene.
+   * @param  {number} ratio       The zoom ratio of the scene.
+   * @param  {boolean} parseNodes Indicates if the nodes have to be parsed.
+   * @param  {boolean} parseEdges Indicates if the edges have to be parsed.
+   * @return {Graph} Returns itself.
+   */
+  function translate(sceneX, sceneY, ratio, parseNodes, parseEdges) {
+    var sizeRatio = Math.pow(ratio, self.p.nodesPowRatio);
+    parseNodes && self.nodes.forEach(function(node) {
+      if (!node['fixed']) {
+        node['displayX'] = node['displayX'] * ratio + sceneX;
+        node['displayY'] = node['displayY'] * ratio + sceneY;
+      }
+
+      node['displaySize'] = node['displaySize'] * sizeRatio;
+    });
+
+    sizeRatio = Math.pow(ratio, self.p.edgesPowRatio);
+    parseEdges && self.edges.forEach(function(edge) {
+      edge['displaySize'] = edge['displaySize'] * sizeRatio;
+    });
+
+    return self;
+  };
+
+  /**
+   * Determines the borders of the graph as it will be drawn. It is used to
+   * avoid the user to drag the graph out of the canvas.
+   */
+  function setBorders() {
+    self.borders = {};
+
+    self.nodes.forEach(function(node) {
+      self.borders.minX = Math.min(
+        self.borders.minX == undefined ?
+          node['displayX'] - node['displaySize'] :
+          self.borders.minX,
+        node['displayX'] - node['displaySize']
+      );
+
+      self.borders.maxX = Math.max(
+        self.borders.maxX == undefined ?
+          node['displayX'] + node['displaySize'] :
+          self.borders.maxX,
+        node['displayX'] + node['displaySize']
+      );
+
+      self.borders.minY = Math.min(
+        self.borders.minY == undefined ?
+          node['displayY'] - node['displaySize'] :
+          self.borders.minY,
+        node['displayY'] - node['displaySize']
+      );
+
+      self.borders.maxY = Math.max(
+        self.borders.maxY == undefined ?
+          node['displayY'] - node['displaySize'] :
+          self.borders.maxY,
+        node['displayY'] - node['displaySize']
+      );
+    });
+  }
+
+  /**
+   * Checks which nodes are under the (mX, mY) points, representing the mouse
+   * position.
+   * @param  {number} mX The mouse X position.
+   * @param  {number} mY The mouse Y position.
+   * @return {Graph} Returns itself.
+   */
+  function checkHover(mX, mY) {
+    var dX, dY, s, over = [], out = [];
+    self.nodes.forEach(function(node) {
+      if (node['hidden']) {
+        node['hover'] = false;
+        return;
+      }
+
+      dX = Math.abs(node['displayX'] - mX);
+      dY = Math.abs(node['displayY'] - mY);
+      s = node['displaySize'];
+
+      var oldH = node['hover'];
+      var newH = dX < s && dY < s && Math.sqrt(dX * dX + dY * dY) < s;
+
+      if (oldH && !newH) {
+        node['hover'] = false;
+        out.push(node.id);
+      } else if (newH && !oldH) {
+        node['hover'] = true;
+        over.push(node.id);
+      }
+    });
+
+    over.length && self.dispatch('overnodes', over);
+    out.length && self.dispatch('outnodes', out);
+
+    return self;
+  };
+
+  /**
+   * Applies a function to a clone of each node (or indicated nodes), and then
+   * tries to apply the modifications made on the clones to the original nodes.
+   * @param  {function(Object)} fun The function to execute.
+   * @param  {?Array.<string>} ids  An Array of node IDs (optional).
+   * @return {Graph} Returns itself.
+   */
+  function iterNodes(fun, ids) {
+    var a = ids ? ids.map(function(id) {
+      return self.nodesIndex[id];
+    }) : self.nodes;
+
+    var aCopies = a.map(cloneNode);
+    aCopies.forEach(fun);
+
+    a.forEach(function(n, i) {
+      checkNode(n, aCopies[i]);
+    });
+
+    return self;
+  };
+
+  /**
+   * Applies a function to a clone of each edge (or indicated edges), and then
+   * tries to apply the modifications made on the clones to the original edges.
+   * @param  {function(Object)} fun The function to execute.
+   * @param  {?Array.<string>} ids  An Array of edge IDs (optional).
+   * @return {Graph} Returns itself.
+   */
+  function iterEdges(fun, ids) {
+    var a = ids ? ids.map(function(id) {
+      return self.edgesIndex[id];
+    }) : self.edges;
+
+    var aCopies = a.map(cloneEdge);
+    aCopies.forEach(fun);
+
+    a.forEach(function(e, i) {
+      checkEdge(e, aCopies[i]);
+    });
+
+    return self;
+  };
+
+  /**
+   * Returns a specific node clone or an array of specified node clones.
+   * @param  {(string|Array.<string>)} ids The ID or an array of node IDs.
+   * @return {(Object|Array.<Object>)} The clone or the array of clones.
+   */
+  function getNodes(ids) {
+    var a = ((ids instanceof Array ? ids : [ids]) || []).map(function(id) {
+      return cloneNode(self.nodesIndex[id]);
+    });
+
+    return (ids instanceof Array ? a : a[0]);
+  };
+
+  /**
+   * Returns a specific edge clone or an array of specified edge clones.
+   * @param  {(string|Array.<string>)} ids The ID or an array of edge IDs.
+   * @return {(Object|Array.<Object>)} The clone or the array of clones.
+   */
+  function getEdges(ids) {
+    var a = ((ids instanceof Array ? ids : [ids]) || []).map(function(id) {
+      return cloneEdge(self.edgesIndex[id]);
+    });
+
+    return (ids instanceof Array ? a : a[0]);
+  };
+
+  empty();
+
+  this.addNode = addNode;
+  this.addEdge = addEdge;
+  this.dropNode = dropNode;
+  this.dropEdge = dropEdge;
+
+  this.iterEdges = iterEdges;
+  this.iterNodes = iterNodes;
+
+  this.getEdges = getEdges;
+  this.getNodes = getNodes;
+
+  this.empty = empty;
+  this.rescale = rescale;
+  this.translate = translate;
+  this.setBorders = setBorders;
+  this.checkHover = checkHover;
+}
+
+sigma.easing = {
+  linear: {},
+  quadratic: {}
+};
+
+sigma.easing.linear.easenone = function(k) {
+  return k;
+};
+
+sigma.easing.quadratic.easein = function(k) {
+  return k * k;
+};
+
+sigma.easing.quadratic.easeout = function(k) {
+  return - k * (k - 2);
+};
+
+sigma.easing.quadratic.easeinout = function(k) {
+  if ((k *= 2) < 1) return 0.5 * k * k;
+  return - 0.5 * (--k * (k - 2) - 1);
+};
+
+sigma.debugMode = 0;
+
+sigma.log = function() {
+  if (sigma.debugMode == 1) {
+    for (var k in arguments) {
+      console.log(arguments[k]);
+    }
+  }else if (sigma.debugMode > 1) {
+    for (var k in arguments) {
+      throw new Error(arguments[k]);
+    }
+  }
+
+  return sigma;
+};
+
+/**
+ * Add a function to the prototype of SigmaPublic, but with access to the
+ * Sigma class properties.
+ * @param {string} pluginName        [description].
+ * @param {function} caller          [description].
+ * @param {function(Sigma)} launcher [description].
+ */
+sigma.addPlugin = function(pluginName, caller, launcher) {
+  SigmaPublic.prototype[pluginName] = caller;
+  local.plugins.push(launcher);
+};
+sigma.tools.drawRoundRect = function(ctx, x, y, w, h, ellipse, corners) {
+  var e = ellipse ? ellipse : 0;
+  var c = corners ? corners : [];
+  c = ((typeof c) == 'string') ? c.split(' ') : c;
+
+  var tl = e && (c.indexOf('topleft') >= 0 ||
+                 c.indexOf('top') >= 0 ||
+                 c.indexOf('left') >= 0);
+  var tr = e && (c.indexOf('topright') >= 0 ||
+                 c.indexOf('top') >= 0 ||
+                 c.indexOf('right') >= 0);
+  var bl = e && (c.indexOf('bottomleft') >= 0 ||
+                 c.indexOf('bottom') >= 0 ||
+                 c.indexOf('left') >= 0);
+  var br = e && (c.indexOf('bottomright') >= 0 ||
+                 c.indexOf('bottom') >= 0 ||
+                 c.indexOf('right') >= 0);
+
+  ctx.moveTo(x, y + e);
+
+  if (tl) {
+    ctx.arcTo(x, y, x + e, y, e);
+  }else {
+    ctx.lineTo(x, y);
+  }
+
+  if (tr) {
+    ctx.lineTo(x + w - e, y);
+    ctx.arcTo(x + w, y, x + w, y + e, e);
+  }else {
+    ctx.lineTo(x + w, y);
+  }
+
+  if (br) {
+    ctx.lineTo(x + w, y + h - e);
+    ctx.arcTo(x + w, y + h, x + w - e, y + h, e);
+  }else {
+    ctx.lineTo(x + w, y + h);
+  }
+
+  if (bl) {
+    ctx.lineTo(x + e, y + h);
+    ctx.arcTo(x, y + h, x, y + h - e, e);
+  }else {
+    ctx.lineTo(x, y + h);
+  }
+
+  ctx.lineTo(x, y + e);
+};
+
+sigma.tools.getRGB = function(s, asArray) {
+  s = s.toString();
+  var res = {
+    'r': 0,
+    'g': 0,
+    'b': 0
+  };
+
+  if (s.length >= 3) {
+    if (s.charAt(0) == '#') {
+      var l = s.length - 1;
+      if (l == 6) {
+        res = {
+          'r': parseInt(s.charAt(1) + s.charAt(2), 16),
+          'g': parseInt(s.charAt(3) + s.charAt(4), 16),
+          'b': parseInt(s.charAt(5) + s.charAt(5), 16)
+        };
+      }else if (l == 3) {
+        res = {
+          'r': parseInt(s.charAt(1) + s.charAt(1), 16),
+          'g': parseInt(s.charAt(2) + s.charAt(2), 16),
+          'b': parseInt(s.charAt(3) + s.charAt(3), 16)
+        };
+      }
+    }
+  }
+
+  if (asArray) {
+    res = [
+      res['r'],
+      res['g'],
+      res['b']
+    ];
+  }
+
+  return res;
+};
+
+sigma.tools.rgbToHex = function(R, G, B) {
+  return sigma.tools.toHex(R) + sigma.tools.toHex(G) + sigma.tools.toHex(B);
+};
+
+sigma.tools.toHex = function(n) {
+  n = parseInt(n, 10);
+
+  if (isNaN(n)) {
+    return '00';
+  }
+  n = Math.max(0, Math.min(n, 255));
+  return '0123456789ABCDEF'.charAt((n - n % 16) / 16) +
+         '0123456789ABCDEF'.charAt(n % 16);
+};
+
+/**
+ * sigma.chronos manages frames insertion to simulate asynchronous computing.
+ * It has been designed to make possible to execute heavy computing tasks
+ * for the browser, without freezing it.
+ * @constructor
+ * @extends sigma.classes.Cascade
+ * @extends sigma.classes.EventDispatcher
+ * @this {sigma.chronos}
+ */
+sigma.chronos = new (function() {
+  sigma.classes.EventDispatcher.call(this);
+
+  /**
+   * Represents "this", without the well-known scope issue.
+   * @private
+   * @type {sigma.chronos}
+   */
+  var self = this;
+
+  /**
+   * Indicates whether any task is actively running or not.
+   * @private
+   * @type {boolean}
+   */
+  var isRunning = false;
+
+  /**
+   * Indicates the FPS "goal", that will define the theoretical
+   * frame length.
+   * @private
+   * @type {number}
+   */
+  var fpsReq = 80;
+
+  /**
+   * Stores the last computed FPS value (FPS is computed only when any
+   * task is running).
+   * @private
+   * @type {number}
+   */
+  var lastFPS = 0;
+
+  /**
+   * The number of frames inserted since the last start.
+   * @private
+   * @type {number}
+   */
+  var framesCount = 0;
+
+  /**
+   * The theoretical frame time.
+   * @private
+   * @type {number}
+   */
+  var frameTime = 1000 / fpsReq;
+
+  /**
+   * The theoretical frame length, minus the last measured delay.
+   * @private
+   * @type {number}
+   */
+  var correctedFrameTime = frameTime;
+
+  /**
+   * The measured length of the last frame.
+   * @private
+   * @type {number}
+   */
+  var effectiveTime = 0;
+
+  /**
+   * The time passed since the last runTasks action.
+   * @private
+   * @type {number}
+   */
+  var currentTime = 0;
+
+  /**
+   * The time when the last frame was inserted.
+   * @private
+   * @type {number}
+   */
+  var startTime = 0;
+
+  /**
+   * The difference between the theoretical frame length and the
+   * last measured frame length.
+   * @private
+   * @type {number}
+   */
+  var delay = 0;
+
+  /**
+   * The container of all active generators.
+   * @private
+   * @type {Object.<string, Object>}
+   */
+  var generators = {};
+
+  /**
+   * The array of all the referenced and active tasks.
+   * @private
+   * @type {Array.<Object>}
+   */
+  var tasks = [];
+
+  /**
+   * The array of all the referenced and queued tasks.
+   * @private
+   * @type {Array.<Object>}
+   */
+  var queuedTasks = [];
+
+  /**
+   * The index of the next task to execute.
+   * @private
+   * @type {number}
+   */
+  var taskIndex = 0;
+
+
+  /**
+   * Inserts a frame before executing the callback.
+   * @param  {function()} callback The callback to execute after having
+   *                               inserted the frame.
+   * @return {sigma.chronos} Returns itself.
+   */
+  function insertFrame(callback) {
+    window.setTimeout(callback, 0);
+    return self;
+  }
+
+  /**
+   * The local method that executes routine, and inserts frames when needed.
+   * It dispatches a "frameinserted" event after having inserted any frame,
+   * and an "insertframe" event before.
+   * @private
+   */
+  function frameInserter() {
+    self.dispatch('frameinserted');
+    while (isRunning && tasks.length && routine()) {}
+
+    if (!isRunning || !tasks.length) {
+      stopTasks();
+    } else {
+      startTime = (new Date()).getTime();
+      framesCount++;
+      delay = effectiveTime - frameTime;
+      correctedFrameTime = frameTime - delay;
+
+      self.dispatch('insertframe');
+      insertFrame(frameInserter);
+    }
+  };
+
+  /**
+   * The local method that executes the tasks, and compares the current frame
+   * length to the ideal frame length.
+   * @private
+   * @return {boolean} Returns false if the current frame should be ended,
+   *                   and true else.
+   */
+  function routine() {
+    taskIndex = taskIndex % tasks.length;
+
+    if (!tasks[taskIndex].task()) {
+      var n = tasks[taskIndex].taskName;
+
+      queuedTasks = queuedTasks.filter(function(e) {
+        (e.taskParent == n) && tasks.push({
+          taskName: e.taskName,
+          task: e.task
+        });
+        return e.taskParent != n;
+      });
+
+      self.dispatch('killed', tasks.splice(taskIndex--, 1)[0]);
+    }
+
+    taskIndex++;
+    effectiveTime = (new Date()).getTime() - startTime;
+    return effectiveTime <= correctedFrameTime;
+  };
+
+  /**
+   * Starts tasks execution.
+   * @return {sigma.chronos} Returns itself.
+   */
+  function runTasks() {
+    isRunning = true;
+    taskIndex = 0;
+    framesCount = 0;
+
+    startTime = (new Date()).getTime();
+    currentTime = startTime;
+
+    self.dispatch('start');
+    self.dispatch('insertframe');
+    insertFrame(frameInserter);
+    return self;
+  };
+
+  /**
+   * Stops tasks execution, and dispatch a "stop" event.
+   * @return {sigma.chronos} Returns itself.
+   */
+  function stopTasks() {
+    self.dispatch('stop');
+    isRunning = false;
+    return self;
+  };
+
+  /**
+   * A task is a function that will be executed continuously while it returns
+   * true. As soon as it return false, the task will be removed.
+   * If several tasks are present, they will be executed in parallele.
+   * This method will add the task to this execution process.
+   * @param {function(): boolean} task     The task to add.
+   * @param {string} name                  The name of the worker, used for
+   *                                       managing the different tasks.
+   * @param {boolean} autostart            If true, sigma.chronos will start
+   *                                       automatically if it is not working
+   *                                       yet.
+   * @return {sigma.chronos} Returns itself.
+   */
+  function addTask(task, name, autostart) {
+    if (typeof task != 'function') {
+      throw new Error('Task "' + name + '" is not a function');
+    }
+
+    tasks.push({
+      taskName: name,
+      task: task
+    });
+
+    isRunning = !!(isRunning || (autostart && runTasks()) || true);
+    return self;
+  };
+
+  /**
+   * Will add a task that will be start to be executed as soon as a task
+   * named as the parent will be removed.
+   * @param {function(): boolean} task     The task to add.
+   * @param {string} name                  The name of the worker, used for
+   *                                       managing the different tasks.
+   * @param {string} parent                The name of the parent task.
+   * @return {sigma.chronos} Returns itself.
+   */
+  function queueTask(task, name, parent) {
+    if (typeof task != 'function') {
+      throw new Error('Task "' + name + '" is not a function');
+    }
+
+    if (!tasks.concat(queuedTasks).some(function(e) {
+      return e.taskName == parent;
+    })) {
+      throw new Error(
+        'Parent task "' + parent + '" of "' + name + '" is not attached.'
+      );
+    }
+
+    queuedTasks.push({
+      taskParent: parent,
+      taskName: name,
+      task: task
+    });
+
+    return self;
+  };
+
+  /**
+   * Removes a task.
+   * @param  {string} v           If v is undefined, then every tasks will
+   *                              be removed. If not, each task named v will
+   *                              be removed.
+   * @param  {number} queueStatus Determines the queued tasks behaviour. If 0,
+   *                              then nothing will happen. If 1, the tasks
+   *                              queued to any removed task will be triggered.
+   *                              If 2, the tasks queued to any removed task
+   *                              will be removed as well.
+   * @return {sigma.chronos} Returns itself.
+   */
+  function removeTask(v, queueStatus) {
+    if (v == undefined) {
+      tasks = [];
+      if (queueStatus == 1) {
+        queuedTasks = [];
+      }else if (queueStatus == 2) {
+        tasks = queuedTasks;
+        queuedTasks = [];
+      }
+      stopTasks();
+    } else {
+      var n = (typeof v == 'string') ? v : '';
+      tasks = tasks.filter(function(e) {
+        if ((typeof v == 'string') ? e.taskName == v : e.task == v) {
+          n = e.taskName;
+          return false;
+        }
+        return true;
+      });
+
+      if (queueStatus > 0) {
+        queuedTasks = queuedTasks.filter(function(e) {
+          if (queueStatus == 1 && e.taskParent == n) {
+            tasks.push(e);
+          }
+          return e.taskParent != n;
+        });
+      }
+    }
+
+    isRunning = !!(!tasks.length || (stopTasks() && false));
+    return self;
+  };
+
+  /**
+   * A generator is a pair task/condition. The task will be executed
+   * while it returns true.
+   * When it returns false, the condition will be tested. If
+   * the condition returns true, the task will be executed
+   * again at the next process iteration. If not, the generator
+   * is removed.
+   * If several generators are present, they will be executed one
+   * by one: When the first stops, the second will start, etc. When
+   * they are all ended, then the conditions will be tested to know
+   * which generators have to be started again.
+   * @param {string} id                     The generators ID.
+   * @param {function(): boolean} task      The generator's task.
+   * @param {function(): boolean} condition The generator's condition.
+   * @return {sigma.chronos} Returns itself.
+   */
+  function addGenerator(id, task, condition) {
+    if (generators[id] != undefined) {
+      return self;
+    }
+
+    generators[id] = {
+      task: task,
+      condition: condition
+    };
+
+    getGeneratorsCount(true) == 0 && startGenerators();
+    return self;
+  };
+
+  /**
+   * Removes a generator. It means that the task will continue being eecuted
+   * until it returns false, but then the
+   * condition will not be tested.
+   * @param  {string} id The generator's ID.
+   * @return {sigma.chronos} Returns itself.
+   */
+  function removeGenerator(id) {
+    if (generators[id]) {
+      generators[id].on = false;
+      generators[id].del = true;
+    }
+    return self;
+  };
+
+  /**
+   * Returns the number of generators.
+   * @private
+   * @param  {boolean} running If true, returns the number of active
+   *                          generators instead.
+   * @return {sigma.chronos} Returns itself.
+   */
+  function getGeneratorsCount(running) {
+    return running ?
+      Object.keys(generators).filter(function(id) {
+        return !!generators[id].on;
+      }).length :
+      Object.keys(generators).length;
+  };
+
+  /**
+   * Returns the array of the generators IDs.
+   * @return {array.<string>} The array of IDs.
+   */
+  function getGeneratorsIDs() {
+    return Object.keys(generators);
+  }
+
+  /**
+   * startGenerators is the method that manages which generator
+   * is the next to start when another one stops. It will dispatch
+   * a "stopgenerators" event if there is no more generator to start,
+   * and a "startgenerators" event else.
+   * @return {sigma.chronos} Returns itself.
+   */
+  function startGenerators() {
+    if (!Object.keys(generators).length) {
+      self.dispatch('stopgenerators');
+    }else {
+      self.dispatch('startgenerators');
+
+      self.unbind('killed', onTaskEnded);
+      insertFrame(function() {
+        for (var k in generators) {
+          generators[k].on = true;
+          addTask(
+            generators[k].task,
+            k,
+            false
+          );
+        }
+      });
+
+      self.bind('killed', onTaskEnded).runTasks();
+    }
+
+    return self;
+  };
+
+  /**
+   * A callback triggered everytime the task of a generator stops, that will
+   * test the related generator's condition, and see if there is still any
+   * generator to start.
+   * @private
+   * @param  {Object} e The sigma.chronos "killed" event.
+   */
+  function onTaskEnded(e) {
+    if (generators[e['content'].taskName] != undefined) {
+      if (generators[e['content'].taskName].del ||
+          !generators[e['content'].taskName].condition()) {
+        delete generators[e['content'].taskName];
+      }else {
+        generators[e['content'].taskName].on = false;
+      }
+
+      if (getGeneratorsCount(true) == 0) {
+        startGenerators();
+      }
+    }
+  };
+
+  /**
+   * Either set or returns the fpsReq property. This property determines
+   * the number of frames that should be inserted per second.
+   * @param  {?number} v The frequency asked.
+   * @return {(Chronos|number)} Returns the frequency if v is undefined, and
+   *                          itself else.
+   */
+  function frequency(v) {
+    if (v != undefined) {
+      fpsReq = Math.abs(1 * v);
+      frameTime = 1000 / fpsReq;
+      framesCount = 0;
+      return self;
+    } else {
+      return fpsReq;
+    }
+  };
+
+  /**
+   * Returns the actual average number of frames that are inserted per
+   * second.
+   * @return {number} The actual average FPS.
+   */
+  function getFPS() {
+    if (isRunning) {
+      lastFPS =
+        Math.round(
+          framesCount /
+          ((new Date()).getTime() - currentTime) *
+          10000
+        ) / 10;
+    }
+
+    return lastFPS;
+  };
+
+  /**
+   * Returns the number of tasks.
+   * @return {number} The number of tasks.
+   */
+  function getTasksCount() {
+    return tasks.length;
+  }
+
+  /**
+   * Returns the number of queued tasks.
+   * @return {number} The number of queued tasks.
+   */
+  function getQueuedTasksCount() {
+    return queuedTasks.length;
+  }
+
+  /**
+   * Returns how long sigma.chronos has active tasks running
+   * without interuption for, in ms.
+   * @return {number} The time chronos is running without interuption for.
+   */
+  function getExecutionTime() {
+    return startTime - currentTime;
+  }
+
+  this.frequency = frequency;
+
+  this.runTasks = runTasks;
+  this.stopTasks = stopTasks;
+  this.insertFrame = insertFrame;
+
+  this.addTask = addTask;
+  this.queueTask = queueTask;
+  this.removeTask = removeTask;
+
+  this.addGenerator = addGenerator;
+  this.removeGenerator = removeGenerator;
+  this.startGenerators = startGenerators;
+  this.getGeneratorsIDs = getGeneratorsIDs;
+
+  this.getFPS = getFPS;
+  this.getTasksCount = getTasksCount;
+  this.getQueuedTasksCount = getQueuedTasksCount;
+  this.getExecutionTime = getExecutionTime;
+
+  return this;
+})();
+
+sigma.publicPrototype = SigmaPublic.prototype;
+})();
+
+

file:b/lib/Color.php (new)
--- /dev/null
+++ b/lib/Color.php
@@ -1,1 +1,502 @@
-
+<?php

+/**

+ *

+ * Color values manipulation utilities. Provides methods to convert from and to

+ * Hex, RGB, HSV and HSL color representattions.

+ *

+ * Several color conversion logic are based on pseudo-code from

+ * http://www.easyrgb.com/math.php

+ *

+ * @category Lux

+ *

+ * @package Lux_Color

+ *

+ * @author Rodrigo Moraes <rodrigo.moraes@gmail.com>

+ *

+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License

+ *

+ * @version $Id$

+ *

+ */

+class Lux_Color

+{

+    /**

+     *

+     * Converts hexadecimal colors to RGB.

+     *

+     * @param string $hex Hexadecimal value. Accepts values with 3 or 6 numbers,

+     * with or without #, e.g., CCC, #CCC, CCCCCC or #CCCCCC.

+     *

+     * @return array RGB values: 0 => R, 1 => G, 2 => B

+     *

+     */

+    public function hex2rgb($hex)

+    {

+        // Remove #.

+        if (strpos($hex, '#') === 0) {

+            $hex = substr($hex, 1);

+        }

+

+        if (strlen($hex) == 3) {

+            $hex .= $hex;

+        }

+

+        if (strlen($hex) != 6) {

+            return false;

+        }

+

+        // Convert each tuple to decimal.

+        $r = hexdec(substr($hex, 0, 2));

+        $g = hexdec(substr($hex, 2, 2));

+        $b = hexdec(substr($hex, 4, 2));

+

+        return array($r, $g, $b);

+    }

+

+    /**

+     *

+     * Converts hexadecimal colors to HSV.

+     *

+     * @param string $hex Hexadecimal value. Accepts values with 3 or 6 numbers,

+     * with or without #, e.g., CCC, #CCC, CCCCCC or #CCCCCC.

+     *

+     * @return array HSV values: 0 => H, 1 => S, 2 => V

+     *

+     */

+    public function hex2hsv($hex)

+    {

+        return $this->rgb2hsv($this->hex2rgb($hex));

+    }

+

+    /**

+     *

+     * Converts hexadecimal colors to HSL.

+     *

+     * @param string $hex Hexadecimal value. Accepts values with 3 or 6 numbers,

+     * with or without #, e.g., CCC, #CCC, CCCCCC or #CCCCCC.

+     *

+     * @return array HSL values: 0 => H, 1 => S, 2 => L

+     *

+     */

+    public function hex2hsl($hex)

+    {

+        return $this->rgb2hsl($this->hex2rgb($hex));

+    }

+

+    /**

+     *

+     * Converts RGB colors to hexadecimal.

+     *

+     * @param array $rgb RGB values: 0 => R, 1 => G, 2 => B

+     *

+     * @return string Hexadecimal value with six digits, e.g., CCCCCC.

+     *

+     */

+    public function rgb2hex($rgb)

+    {

+        if(count($rgb) < 3) {

+            return false;

+        }

+

+        list($r, $g, $b) = $rgb;

+

+        // From php.net.

+        $r = 0x10000 * max(0, min(255, $r));

+        $g = 0x100 * max(0, min(255, $g));

+        $b = max(0, min(255, $b));

+

+        return strtoupper(str_pad(dechex($r + $g + $b), 6, 0, STR_PAD_LEFT));

+    }

+

+    /**

+     *

+     * Converts RGB to HSV.

+     *

+     * @param array $rgb RGB values: 0 => R, 1 => G, 2 => B

+     *

+     * @return array HSV values: 0 => H, 1 => S, 2 => V

+     *

+     */

+    public function rgb2hsv($rgb)

+    {

+        // RGB values = 0 ÷ 255

+        $var_R = ($rgb[0] / 255);

+        $var_G = ($rgb[1] / 255);

+        $var_B = ($rgb[2] / 255);

+

+        // Min. value of RGB

+        $var_Min = min($var_R, $var_G, $var_B);

+

+        // Max. value of RGB

+        $var_Max = max($var_R, $var_G, $var_B);

+

+        // Delta RGB value

+        $del_Max = $var_Max - $var_Min;

+

+        $V = $var_Max;

+

+        // This is a gray, no chroma...

+        if ( $del_Max == 0 ) {

+           // HSV results = 0 ÷ 1

+           $H = 0;

+           $S = 0;

+        } else {

+           // Chromatic data...

+           $S = $del_Max / $var_Max;

+

+           $del_R = ((($var_Max - $var_R) / 6) + ($del_Max / 2)) / $del_Max;

+           $del_G = ((($var_Max - $var_G) / 6) + ($del_Max / 2)) / $del_Max;

+           $del_B = ((($var_Max - $var_B) / 6) + ($del_Max / 2)) / $del_Max;

+

+           if ($var_R == $var_Max) {

+               $H = $del_B - $del_G;

+           } else if ($var_G == $var_Max) {

+               $H = (1 / 3) + $del_R - $del_B;

+           } else if ($var_B == $var_Max) {

+               $H = (2 / 3) + $del_G - $del_R;

+           }

+

+           if ($H < 0) {

+               $H += 1;

+           }

+           if ($H > 1) {

+               $H -= 1;

+           }

+        }

+

+        // Returns agnostic values.

+        // Range will depend on the application: e.g. $H*360, $S*100, $V*100.

+        return array($H, $S, $V);

+    }

+

+    /**

+     *

+     * Converts RGB to HSL.

+     *

+     * @param array $rgb RGB values: 0 => R, 1 => G, 2 => B

+     *

+     * @return array HSL values: 0 => H, 1 => S, 2 => L

+     *

+     */

+    public function rgb2hsl($rgb)

+    {

+        // Where RGB values = 0 ÷ 255.

+        $var_R = $rgb[0] / 255;

+        $var_G = $rgb[1] / 255;

+        $var_B = $rgb[2] / 255;

+

+        // Min. value of RGB

+        $var_Min = min($var_R, $var_G, $var_B);

+        // Max. value of RGB

+        $var_Max = max($var_R, $var_G, $var_B);

+        // Delta RGB value

+        $del_Max = $var_Max - $var_Min;

+

+        $L = ($var_Max + $var_Min) / 2;

+

+        if ( $del_Max == 0 ) {

+            // This is a gray, no chroma...

+            // HSL results = 0 ÷ 1

+            $H = 0;

+            $S = 0;

+        } else {

+            // Chromatic data...

+            if ($L < 0.5) {

+                $S = $del_Max / ($var_Max + $var_Min);

+            } else {

+                $S = $del_Max / ( 2 - $var_Max - $var_Min );

+            }

+

+            $del_R = ((($var_Max - $var_R) / 6) + ($del_Max / 2)) / $del_Max;

+            $del_G = ((($var_Max - $var_G) / 6) + ($del_Max / 2)) / $del_Max;

+            $del_B = ((($var_Max - $var_B) / 6) + ($del_Max / 2)) / $del_Max;

+

+            if ($var_R == $var_Max) {

+                $H = $del_B - $del_G;

+            } else if ($var_G == $var_Max) {

+                $H = ( 1 / 3 ) + $del_R - $del_B;

+            } else if ($var_B == $var_Max) {

+                $H = ( 2 / 3 ) + $del_G - $del_R;

+            }

+

+            if ($H < 0) {

+                $H += 1;

+            }

+            if ($H > 1) {

+                $H -= 1;

+            }

+        }

+

+        return array($H, $S, $L);

+    }

+

+    /**

+     *

+     * Converts HSV colors to hexadecimal.

+     *

+     * @param array $hsv HSV values: 0 => H, 1 => S, 2 => V

+     *

+     * @return string Hexadecimal value with six digits, e.g., CCCCCC.

+     *

+     */

+    public function hsv2hex($hsv)

+    {

+        return $this->rgb2hex($this->hsv2rgb($hsv));

+    }

+

+    /**

+     *

+     * Converts HSV to RGB.

+     *

+     * @param array $hsv HSV values: 0 => H, 1 => S, 2 => V

+     *

+     * @return array RGB values: 0 => R, 1 => G, 2 => B

+     *

+     */

+    public function hsv2rgb($hsv)

+    {

+        $H = $hsv[0];

+        $S = $hsv[1];

+        $V = $hsv[2];

+

+        // HSV values = 0 ÷ 1

+        if ($S == 0) {

+            $R = $V * 255;

+            $G = $V * 255;

+            $B = $V * 255;

+        } else {

+            $var_h = $H * 6;

+            // H must be < 1

+            if ( $var_h == 6 ) {

+                $var_h = 0;

+            }

+            // Or ... $var_i = floor( $var_h )

+            $var_i = floor( $var_h );

+            $var_1 = $V * ( 1 - $S );

+            $var_2 = $V * ( 1 - $S * ( $var_h - $var_i ) );

+            $var_3 = $V * ( 1 - $S * ( 1 - ( $var_h - $var_i ) ) );

+

+            switch($var_i) {

+                case 0:

+                    $var_r = $V;

+                    $var_g = $var_3;

+                    $var_b = $var_1;

+                    break;

+                case 1:

+                    $var_r = $var_2;

+                    $var_g = $V;

+                    $var_b = $var_1;

+                    break;

+                case 2:

+                    $var_r = $var_1;

+                    $var_g = $V;

+                    $var_b = $var_3;

+                    break;

+                case 3:

+                    $var_r = $var_1;

+                    $var_g = $var_2;

+                    $var_b = $V;

+                    break;

+                case 4:

+                    $var_r = $var_3;

+                    $var_g = $var_1;

+                    $var_b = $V;

+                    break;

+                default:

+                    $var_r = $V;

+                    $var_g = $var_1;

+                    $var_b = $var_2;

+            }

+

+            //RGB results = 0 ÷ 255

+            $R = $var_r * 255;

+            $G = $var_g * 255;

+            $B = $var_b * 255;

+        }

+

+        return array($R, $G, $B);

+    }

+

+    /**

+     *

+     * Converts HSV colors to HSL.

+     *

+     * @param array $hsv HSV values: 0 => H, 1 => S, 2 => V

+     *

+     * @return array HSL values: 0 => H, 1 => S, 2 => L

+     *

+     */

+    public function hsv2hsl($hsv)

+    {

+        return $this->rgb2hsl($this->hsv2rgb($hsv));

+    }

+

+    /**

+     *

+     * Converts hexadecimal colors to HSL.

+     *

+     * @param array $hsl HSL values: 0 => H, 1 => S, 2 => L

+     *

+     * @return string Hexadecimal value. Accepts values with 3 or 6 numbers,

+     * with or without #, e.g., CCC, #CCC, CCCCCC or #CCCCCC.

+     *

+     */

+    public function hsl2hex($hsl)

+    {

+        return $this->rgb2hex($this->hsl2rgb($hsl));

+    }

+

+    /**

+     *

+     * Converts HSL to RGB.

+     *

+     * @param array $hsv HSL values: 0 => H, 1 => S, 2 => L

+     *

+     * @return array RGB values: 0 => R, 1 => G, 2 => B

+     *

+     */

+    public function hsl2rgb($hsl)

+    {

+        list($H, $S, $L) = $hsl;

+

+        if ($S == 0) {

+            // HSL values = 0 ÷ 1

+            // RGB results = 0 ÷ 255

+            $R = $L * 255;

+            $G = $L * 255;

+            $B = $L * 255;

+        } else {

+            if ($L < 0.5) {

+                $var_2 = $L * (1 + $S);

+            } else {

+                $var_2 = ($L + $S) - ($S * $L);

+            }

+

+            $var_1 = 2 * $L - $var_2;

+

+            $R = 255 * $this->_hue2rgb($var_1, $var_2, $H + (1 / 3));

+            $G = 255 * $this->_hue2rgb($var_1, $var_2, $H);

+            $B = 255 * $this->_hue2rgb($var_1, $var_2, $H - (1 / 3));

+        }

+

+        return array($R, $G, $B);

+    }

+

+    /**

+     *

+     * Support method for hsl2rgb(): converts hue ro RGB.

+     *

+     * @param

+     *

+     * @param

+     *

+     * @param

+     *

+     * @return int

+     *

+     */

+    protected function _hue2rgb($v1, $v2, $vH)

+    {

+        if ($vH < 0) {

+            $vH += 1;

+        }

+

+        if ($vH > 1) {

+            $vH -= 1;

+        }

+

+        if ((6 * $vH) < 1) {

+            return ($v1 + ($v2 - $v1) * 6 * $vH);

+        }

+

+        if ((2 * $vH) < 1) {

+            return $v2;

+        }

+

+        if ((3 * $vH) < 2) {

+            return ($v1 + ($v2 - $v1) * (( 2 / 3) - $vH) * 6);

+        }

+

+        return $v1;

+    }

+

+    /**

+     *

+     * Converts hexadecimal colors to HSL.

+     *

+     * @param array $hsl HSL values: 0 => H, 1 => S, 2 => L

+     *

+     * @return array HSV values: 0 => H, 1 => S, 2 => V

+     *

+     */

+    public function hsl2hsv($hsl)

+    {

+        return $this->rgb2hsv($this->hsl2rgb($hsl));

+    }

+

+    /**

+     *

+     * Updates HSV values.

+     *

+     * @param array $hsv HSV values: 0 => H, 1 => S, 2 => V

+     *

+     * @param array $values Values to update: 0 => value to add to H (0 to 360),

+     * 1 and 2 => values to multiply S and V (0 to 100). Example:

+     *

+     * {{{code:php

+     *     // Update saturation to 80% in the provided HSV.

+     *     $hsv = array(120, 0.75, 0.75);

+     *     $new_hsv = $color->updateHsv($hsv, array(null, 80, null));

+     * }}}

+     *

+     */

+    public function updateHsv($hsv, $values)

+    {

+        if (isset($values[0])) {

+            $hsv[0] = max(0, min(360, ($hsv[0] + $values[0])));

+        }

+

+        if (isset($values[1])) {

+            $hsv[1] = max(0, min(1, ($hsv[1] * ($values[1] / 100))));

+        }

+

+        if (isset($values[2])) {

+            $hsv[2] = max(0, min(1, ($hsv[2] * ($values[2] / 100))));

+        }

+

+        return $hsv;

+    }

+

+    /**

+     *

+     * Updates HSL values.

+     *

+     * @param array $hsl HSL values: 0 => H, 1 => S, 2 => L

+     *

+     * @param array $values Values to update: 0 => value to add to H (0 to 360),

+     * 1 and 2 => values to multiply S and V (0 to 100). Example:

+     *

+     * {{{code:php

+     *     // Update saturation to 80% in the provided HSL.

+     *     $hsl = array(120, 0.75, 0.75);

+     *     $new_hsl = $color->updateHsl($hsl, array(null, 80, null));

+     * }}}

+     *

+     */

+    public function updateHsl($hsl, $values)

+    {

+        if (isset($values[0])) {

+            $hsl[0] = max(0, min(360, ($hsl[0] + $values[0])));

+        }

+

+        if (isset($values[1])) {

+            $hsl[1] = max(0, min(1, ($hsl[1] * ($values[1] / 100))));

+        }

+

+        if (isset($values[2])) {

+            $hsl[2] = max(0, min(1, ($hsl[2] * ($values[2] / 100))));

+        }

+

+        return $hsl;

+    }

+}

file:a/robots.txt -> file:b/robots.txt
--- a/robots.txt
+++ b/robots.txt
@@ -3,3 +3,4 @@
 
 User-agent: *
 Disallow: /admin/
+Sitemap: http://orgs.disclosurelo.gs/sitemap.xml.php

file:a/score.php -> file:b/score.php
--- a/score.php
+++ b/score.php
@@ -1,7 +1,7 @@
 <?php
 
 include_once('include/common.inc.php');
-include_header();
+include_header('Scores');
 
 $db = $server->get_db('disclosr-agencies');
 

file:a/search.php -> file:b/search.php
--- a/search.php
+++ b/search.php
@@ -1,6 +1,6 @@
 <?php
 include_once('include/common.inc.php');
-include_header();
+include_header('Search');
 ?>
 <div class="foundation-header">
     <h1><a href="search.php">Search</a></h1>

file:b/sitemap.xml.php (new)
--- /dev/null
+++ b/sitemap.xml.php
@@ -1,1 +1,25 @@
+<?php
 
+include ('include/common.inc.php');
+$last_updated = date('Y-m-d', @filemtime('cbrfeed.zip'));
+header("Content-Type: text/xml");
+echo "<?xml version='1.0' encoding='UTF-8'?>";
+echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
+echo " <url><loc>" . local_url() . "index.php</loc><priority>1.0</priority></url>\n";
+foreach (scandir("./") as $file) {
+    if (strpos($file, ".php") !== false && $file != "index.php" && $file != "sitemap.xml.php")
+        echo " <url><loc>" . local_url() . "$file</loc><priority>0.3</priority></url>\n";
+}
+
+$db = $server->get_db('disclosr-agencies');
+try {
+    $rows = $db->get_view("app", "byCanonicalName")->rows;
+    foreach ($rows as $row) {
+        echo '<url><loc>' . local_url() . 'getAgency.php?id=' . $row->value->_id . "</loc><priority>0.6</priority></url>\n";
+    }
+} catch (SetteeRestClientException $e) {
+    setteErrorHandler($e);
+}
+echo '</urlset>';
+?>
+

--- a/unimplemented/foundation.html
+++ /dev/null
@@ -1,137 +1,1 @@
-<!DOCTYPE html>
 
-<!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ -->
-<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en"> <![endif]-->
-<!--[if IE 7]>    <html class="no-js lt-ie9 lt-ie8" lang="en"> <![endif]-->
-<!--[if IE 8]>    <html class="no-js lt-ie9" lang="en"> <![endif]-->
-<!--[if gt IE 8]><!--> <html lang="en"> <!--<![endif]-->
-<head>
-	<meta charset="utf-8" />
-
-	<!-- Set the viewport width to device width for mobile -->
-	<meta name="viewport" content="width=device-width" />
-
-	<title>Welcome to Foundation</title>
-  
-	<!-- Included CSS Files -->
-	<link rel="stylesheet" href="stylesheets/foundation.css">
-	<link rel="stylesheet" href="stylesheets/app.css">
-
-	<!--[if lt IE 9]>
-		<link rel="stylesheet" href="stylesheets/ie.css">
-	<![endif]-->
-
-
-	<!-- IE Fix for HTML5 Tags -->
-	<!--[if lt IE 9]>
-		<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
-	<![endif]-->
-
-</head>
-<body>
-
-	<!-- container -->
-	<div class="container">
-
-		<div class="row">
-			<div class="twelve columns">
-				<h2>Welcome to Foundation</h2>
-				<p>This is version 2.1.4 released on December 19, 2011</p>
-				<hr />
-			</div>
-		</div>
-
-		<div class="row">
-			<div class="eight columns">
-				<h3>The Grid</h3>
-
-				<!-- Grid Example -->
-				<div class="row">
-					<div class="twelve columns">
-						<div class="panel">
-							<p>This is a twelve column section in a row. Each of these includes a div.panel element so you can see where the columns are - it's not required at all for the grid.</p>
-						</div>
-					</div>
-				</div>
-				<div class="row">
-					<div class="six columns">
-						<div class="panel">
-							<p>Six columns</p>
-						</div>
-					</div>
-					<div class="six columns">
-						<div class="panel">
-							<p>Six columns</p>
-						</div>
-					</div>
-				</div>
-				<div class="row">
-					<div class="four columns">
-						<div class="panel">
-							<p>Four columns</p>
-						</div>
-					</div>
-					<div class="four columns">
-						<div class="panel">
-							<p>Four columns</p>
-						</div>
-					</div>
-					<div class="four columns">
-						<div class="panel">
-							<p>Four columns</p>
-						</div>
-					</div>
-				</div>
-
-				<h3>Tabs</h3>
-				<dl class="tabs">
-					<dd><a href="#simple1" class="active">Simple Tab 1</a></dd>
-					<dd><a href="#simple2">Simple Tab 2</a></dd>
-					<dd><a href="#simple3">Simple Tab 3</a></dd>
-				</dl>
-
-				<ul class="tabs-content">
-					<li class="active" id="simple1Tab">This is simple tab 1's content. Pretty neat, huh?</li>
-					<li id="simple2Tab">This is simple tab 2's content. Now you see it!</li>
-					<li id="simple3Tab">This is simple tab 3's content. It's, you know...okay.</li>
-				</ul>
-
-				<h3>Buttons</h3>
-
-				<p><a href="#" class="small blue button">Small Blue Button</a></p>
-				<p><a href="#" class="blue button">Medium Blue Button</a></p>
-				<p><a href="#" class="large blue button">Large Blue Button</a></p>
-
-				<p><a href="#" class="nice radius small blue button">Nice Blue Button</a></p>
-				<p><a href="#" class="nice radius blue button">Nice Blue Button</a></p>
-				<p><a href="#" class="nice radius large blue button">Nice Blue Button</a></p>
-
-			</div>
-
-			<div class="four columns">			
-				<h4>Getting Started</h4>
-				<p>We're stoked you want to try Foundation! To get going, this file (index.html) includes some basic styles you can modify, play around with, or totally destroy to get going.</p>
-
-				<h4>Other Resources</h4>
-				<p>Once you've exhausted the fun in this document, you should check out:</p>
-				<ul class="disc">
-					<li><a href="http://foundation.zurb.com/docs">Foundation Documentation</a><br />Everything you need to know about using the framework.</li>
-					<li><a href="http://github.com/zurb/foundation">Foundation on Github</a><br />Latest code, issue reports, feature requests and more.</li>
-					<li><a href="http://twitter.com/foundationzurb">@foundationzurb</a><br />Ping us on Twitter if you have questions. If you build something with this we'd love to see it (and send you a totally boss sticker).</li>
-				</ul>
-			</div>
-		</div>
-
-	</div>
-	<!-- container -->
-
-
-
-
-	<!-- Included JS Files -->
-	<script src="javascripts/foundation.js"></script>
-	<script src="javascripts/app.js"></script>
-
-</body>
-</html>
-

--- a/unimplemented/humans.txt
+++ /dev/null
@@ -1,8 +1,1 @@
-/* Foundation was made by ZURB, an interaction design and design strategy firm in Campbell, CA */
-/* zurb.com */
-/* humanstxt.org */
 
-/* SITE */
-  Standards: HTML5, CSS3
-  Components: jQuery, Orbit, Reveal
-  Software: Coda, Textmate, Git

--- a/unimplemented/lastUpdated.php
+++ /dev/null
@@ -1,2 +1,1 @@
-for each agency, record when last changed (number of days too) and show a couple of URLs that were in that change
 

--- a/unimplemented/validation.php
+++ /dev/null

--- a/unimplemented/webservers.php
+++ /dev/null
@@ -1,1 +1,1 @@
-for each agency, find a scrapped document and read the webserver off it
+

file:b/webserver.php (new)
--- /dev/null
+++ b/webserver.php
@@ -1,1 +1,43 @@
+<?php
 
+include_once('include/common.inc.php');
+include_header('Webserver and Accessiblity');
+
+echo "<table>
+    <tr><th>name</th><th>webserver</th><th>accessiblity errors</th></tr>";
+$agenciesdb = $server->get_db('disclosr-agencies');
+$docsdb = $server->get_db('disclosr-documents');
+try {
+    $rows = $agenciesdb->get_view("app", "all", null, true)->rows;
+
+
+    if ($rows) {
+        foreach ($rows as $row) {
+
+            echo "<tr><td>" . $row->value->name . "</td>";
+            if (isset($row->value->website)) {
+                try {
+                    $website = $docsdb->get(md5($row->value->website));
+                    $serverParts = explode(" ",$website->web_server);
+                    echo "<td>" . $serverParts[0] . "</td>";
+                    if (!isset($website->validation)) {
+                        echo "<td>?</td>";
+                    } else {
+                        if ($website->validation == "") {
+                            echo "<td>No error</td>";
+                        } else {
+                      echo "<td><pre>" . str_replace("<", "&lt;", $website->validation) . "</pre></td>";
+                        }
+                    }
+                } catch (SetteeRestClientException $e) {
+                   // setteErrorHandler($e);
+                }
+            }
+            echo "</tr>";
+        }
+    }
+} catch (SetteeRestClientException $e) {
+    setteErrorHandler($e);
+}
+include_footer();
+?>