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


Former-commit-id: e869d59c5493e9e3cd511cf520f64b1690218a28

[submodule "couchdb/couchdb-lucene"] [submodule "couchdb/couchdb-lucene"]
path = couchdb/couchdb-lucene path = couchdb/couchdb-lucene
url = https://github.com/rnewson/couchdb-lucene.git url = https://github.com/rnewson/couchdb-lucene.git
[submodule "couchdb/settee"] [submodule "couchdb/settee"]
path = couchdb/settee path = couchdb/settee
url = https://github.com/inadarei/settee.git url = https://github.com/inadarei/settee.git
[submodule "lib/php-diff"] [submodule "lib/php-diff"]
path = lib/php-diff path = lib/php-diff
url = https://github.com/chrisboulton/php-diff.git url = https://github.com/chrisboulton/php-diff.git
[submodule "lib/Requests"] [submodule "lib/Requests"]
path = lib/Requests path = lib/Requests
url = https://github.com/rmccue/Requests.git url = https://github.com/rmccue/Requests.git
[submodule "javascripts/flotr2"] [submodule "javascripts/flotr2"]
path = javascripts/flotr2 path = javascripts/flotr2
url = https://github.com/HumbleSoftware/Flotr2.git url = https://github.com/HumbleSoftware/Flotr2.git
[submodule "lib/phpquery"] [submodule "lib/phpquery"]
path = lib/phpquery path = lib/phpquery
url = https://github.com/TobiaszCudnik/phpquery.git url = https://github.com/TobiaszCudnik/phpquery.git
[submodule "javascripts/sigma"] [submodule "javascripts/sigma"]
path = javascripts/sigma path = javascripts/sigma
url = https://github.com/jacomyal/sigma.js.git 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
<?php <?php
include_once('include/common.inc.php'); include_once('include/common.inc.php');
include_header(); include_header();
?> ?>
<div class="foundation-header"> <div class="foundation-header">
<h1><a href="about.php">About/FAQ</a></h1> <h1><a href="about.php">About/FAQ</a></h1>
<h4 class="subheader">Lorem ipsum.</h4> <h4 class="subheader">Lorem ipsum.</h4>
</div> </div>
<h2> What is this? </h2> <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>. 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> Open everything </h2> <h2> Open everything </h2>
All documents released CC-BY 3 AU All documents released CC-BY 3 AU
Open source git @ Open source git @
   
<h2>Organisational Data Sources</h2> <h2>Organisational Data Sources</h2>
   
http://www.comlaw.gov.au/Browse/Results/ByTitle/AdministrativeArrangementsOrders/Current/Ad/0 defines departments http://www.comlaw.gov.au/Browse/Results/ByTitle/AdministrativeArrangementsOrders/Current/Ad/0 defines departments
Agencies can be found in the Schedule to an Appropriation Bill (budget), Schedule to FMA Regulations and/or Public Service Act.<br> Agencies can be found in the Schedule to an Appropriation Bill (budget), Schedule to FMA Regulations and/or Public Service Act.<br>
   
http://www.finance.gov.au/publications/flipchart/docs/FMACACFlipchart.pdf summarises these. view-source:https://www.tenders.gov.au/?event=public.advancedsearch.home is great for the suspended/active status<br> http://www.finance.gov.au/publications/flipchart/docs/FMACACFlipchart.pdf summarises these. view-source:https://www.tenders.gov.au/?event=public.advancedsearch.home is great for the suspended/active status<br>
   
Fraud in gov depts by Fairfax Media http://www.smh.com.au/national/public-service-keeps-fraud-cases-private-20110923-1kpdr.html Fraud in gov depts by Fairfax Media http://www.smh.com.au/national/public-service-keeps-fraud-cases-private-20110923-1kpdr.html
   
When defining the hierachy, this system is designed towards monitoring accountablity. Thus large agencies that have registered their own ABN When defining the hierachy, this system is designed towards monitoring accountablity. Thus large agencies that have registered their own ABN
and have their own accountablity mechanisms/website receive a seperate record as a child of their department. and have their own accountablity mechanisms/website receive a seperate record as a child of their department.
Some small agencies will choose to simply rely on their parent department's accountablity measures.<br> Some small agencies will choose to simply rely on their parent department's accountablity measures.<br>
   
This flows through to organisation name and other/past names. A department that completely accounts for an agency will list that agency as an other child name. This flows through to organisation name and other/past names. A department that completely accounts for an agency will list that agency as an other child name.
As agencies themselves shift between departments, there may be scope for providing time ranges but typically the newest hierarchy will be the one recorded. As agencies themselves shift between departments, there may be scope for providing time ranges but typically the newest hierarchy will be the one recorded.
A department/agency name will be the newest active name assigned to that ABN.<br> A department/agency name will be the newest active name assigned to that ABN.<br>
   
ABN information is derived from the ABR. This is the definitive umpire about which former name should be linked to which current name. ABN information is derived from the ABR. This is the definitive umpire about which former name should be linked to which current name.
For example "Department of Transport and Regional Services" became "Department of Infrastructure, Transport, Regional Development and Local Government" (same ABN) For example "Department of Transport and Regional Services" became "Department of Infrastructure, Transport, Regional Development and Local Government" (same ABN)
however it later split into "Department of Infrastructure and Transport" (same ABN) however it later split into "Department of Infrastructure and Transport" (same ABN)
and "Department of Regional Australia, Regional Development and Local Government" (new ABN).<br> and "Department of Regional Australia, Regional Development and Local Government" (new ABN).<br>
   
Statistical information from http://www.apsc.gov.au/stateoftheservice/1011/statsbulletin/section1.html#t2total https://www.apsedii.gov.au/apsedii/CustomQueryx33.shtml Statistical information from http://www.apsc.gov.au/stateoftheservice/1011/statsbulletin/section1.html#t2total https://www.apsedii.gov.au/apsedii/CustomQueryx33.shtml
and individual annual reports.<br> and individual annual reports.<br>
   
<h2>Webpage Assessment</h2> <h2>Webpage Assessment</h2>
Much due care has been put into correctly recording disclosure URLs. Typically the "About", "Corporate", "Publications" and "Sitemap" sections are checked at the very least. Much due care has been put into correctly recording disclosure URLs. Typically the "About", "Corporate", "Publications" and "Sitemap" sections are checked at the very least.
Occasionally it is nessicary to use a site or Google search. In several rare cases, there is a secret "Disclosure" navigation menu you can find if you find one of the mandatory publishing obligations in that category (seriously).<br> Occasionally it is nessicary to use a site or Google search. In several rare cases, there is a secret "Disclosure" navigation menu you can find if you find one of the mandatory publishing obligations in that category (seriously).<br>
Some rules about leniency:<br> Some rules about leniency:<br>
<ul> <ul>
<li>An empty FOI disclosure log counts, a page outlining what the FOI Act is does not.</li> <li>An empty FOI disclosure log counts, a page outlining what the FOI Act is does not.</li>
<li>A disclosure log in PDF or Word format counts :(</li> <li>A disclosure log in PDF or Word format counts :(</li>
<li>An empty File/Record list counts (although that's very minimalistic that you have no files, electronic or paper)</li> <li>An empty File/Record list counts (although that's very minimalistic that you have no files, electronic or paper)</li>
<li>Only a current information publication scheme page counts, not a s.9 FOI Act page or an organisation chart.</li> <li>Only a current information publication scheme page counts, not a s.9 FOI Act page or an organisation chart.</li>
<li>If there isn't a page easily listing all current and past Annual Reports, the most current one (html, pdf) counts.</li> <li>If there isn't a page easily listing all current and past Annual Reports, the most current one (html, pdf) counts.</li>
<li>Consultancy contracts might not need it's own webpage (if in Annual Report), grants/appointments might not apply to all organisations but Legal Services Expenditure (and all other obligations) does need a webpage. </li> <li>Consultancy contracts might not need it's own webpage (if in Annual Report), grants/appointments might not apply to all organisations but Legal Services Expenditure (and all other obligations) does need a webpage. </li>
   
<h2>Open Government Scoring</h2> <h2>Open Government Scoring</h2>
+1 point for every true Has... attribute<br> +1 point for every true Has... attribute<br>
-1 point for every false Has... (ie. Has Not) attribute</br> -1 point for every false Has... (ie. Has Not) attribute</br>
   
Don't like this? Make your own score, suggest a better scoring mechanism.</br> Don't like this? Make your own score, suggest a better scoring mechanism.</br>
   
<?php <?php
include_footer(); include_footer();
?> ?>
  <?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();
  ?>
 
  <?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>';
  ?>
 
<?php <?php
   
include_once("../include/common.inc.php"); include_once("../include/common.inc.php");
   
$format = "csv"; $format = "csv";
//$format = "json"; //$format = "json";
if (isset($_REQUEST['format'])) $format = $_REQUEST['format']; if (isset($_REQUEST['format'])) $format = $_REQUEST['format'];
   
setlocale(LC_CTYPE, 'C'); setlocale(LC_CTYPE, 'C');
if ($format == "csv") { if ($format == "csv") {
$headers = Array("name"); $headers = Array("name");
} else { } else {
$headers = Array(); $headers = Array();
} }
   
$db = $server->get_db('disclosr-agencies'); $db = $server->get_db('disclosr-agencies');
try { try {
$rows = $db->get_view("app", "all", null, true)->rows; $rows = $db->get_view("app", "all", null, true)->rows;
   
$dataValues = Array(); $dataValues = Array();
foreach ($rows as $row) { foreach ($rows as $row) {
if (isset($row->value->statistics->employees)) { if (isset($row->value->statistics->employees)) {
   
$headers = array_unique(array_merge($headers, array_keys(object_to_array($row->value->statistics->employees)))); $headers = array_unique(array_merge($headers, array_keys(object_to_array($row->value->statistics->employees))));
   
} }
} }
} catch (SetteeRestClientException $e) { } catch (SetteeRestClientException $e) {
setteErrorHandler($e); setteErrorHandler($e);
} }
   
$fp = fopen('php://output', 'w'); $fp = fopen('php://output', 'w');
if ($fp && $db) { if ($fp && $db) {
if ($format == "csv") { if ($format == "csv") {
header('Content-Type: text/csv; charset=utf-8'); header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="export.employeestats.' . date("c") . '.csv"'); header('Content-Disposition: attachment; filename="export.employeestats.' . date("c") . '.csv"');
} }
header('Pragma: no-cache'); header('Pragma: no-cache');
header('Expires: 0'); header('Expires: 0');
if ($format == "csv") { if ($format == "csv") {
fputcsv($fp, $headers); fputcsv($fp, $headers);
} else if ($format == "json") { } else if ($format == "json") {
echo '{ echo '{
"labels" : ["' . implode('","', $headers) . '"],'.PHP_EOL; "labels" : ["' . implode('","', $headers) . '"],'.PHP_EOL;
} }
try { try {
$agencies = $db->get_view("app", "all", null, true)->rows; $agencies = $db->get_view("app", "all", null, true)->rows;
//print_r($agencies); //print_r($agencies);
$first = true; $first = true;
if ($format == "json") { if ($format == "json") {
echo '"data" : ['.PHP_EOL; echo '"data" : ['.PHP_EOL;
} }
foreach ($agencies as $agency) { foreach ($agencies as $agency) {
   
if (isset($agency->value->statistics->employees)) { if (isset($agency->value->statistics->employees)) {
$row = Array(); $row = Array();
$agencyEmployeesArray = object_to_array($agency->value->statistics->employees); $agencyEmployeesArray = object_to_array($agency->value->statistics->employees);
foreach ($headers as $i => $fieldName) { foreach ($headers as $i => $fieldName) {
if (isset($agencyEmployeesArray[$fieldName])) { if (isset($agencyEmployeesArray[$fieldName])) {
$row[] = '['.$i.','.$agencyEmployeesArray[$fieldName]["value"].']'; $row[] = '['.$i.','.$agencyEmployeesArray[$fieldName]["value"].']';
} else { } else {
$row[] = '['.$i.',0]'; $row[] = '['.$i.',0]';
} }
} }
if ($format == "csv") { if ($format == "csv") {
fputcsv($fp, array_values($row)); fputcsv($fp, array_values($row));
} else if ($format == "json") { } else if ($format == "json") {
if (!$first) echo ","; if (!$first) echo ",";
echo '{"data" : [' . implode(",", array_values($row)) . '], "label": "'.$agency->value->name.'", "lines" : { "show" : true }, "points" : { "show" : true }}'.PHP_EOL; echo '{"data" : [' . implode(",", array_values($row)) . '], "label": "'.$agency->value->name.'", "lines" : { "show" : true }, "points" : { "show" : true }}'.PHP_EOL;
$first = false; $first = false;
} }
} }
} }
if ($format == "json") { if ($format == "json") {
echo '] echo ']
}'.PHP_EOL; }'.PHP_EOL;
} }
} catch (SetteeRestClientException $e) { } catch (SetteeRestClientException $e) {
setteErrorHandler($e); setteErrorHandler($e);
} }
   
die; die;
} }
?> ?>
   
<?php <?php
   
require_once '../include/common.inc.php'; require_once '../include/common.inc.php';
   
$db = $server->get_db('disclosr-agencies'); $db = $server->get_db('disclosr-agencies');
$rows = $db->get_view("app", "byName")->rows; $rows = $db->get_view("app", "byName")->rows;
$nametoid = Array(); $nametoid = Array();
$sums = Array(); $sums = Array();
foreach ($rows as $row) { foreach ($rows as $row) {
$nametoid[trim($row->key)] = $row->value; $nametoid[trim($row->key)] = $row->value;
} }
$employeeCSVs = Array("2002-2003" => "0203apsemployees.csv", $employeeCSVs = Array("2002-2003" => "0203apsemployees.csv",
"2003-2004" => "0304apsemployees.csv", "2003-2004" => "0304apsemployees.csv",
"2004-2005" => "0405apsemployees.csv", "2004-2005" => "0405apsemployees.csv",
"2005-2006" => "0506apsemployees.csv", "2005-2006" => "0506apsemployees.csv",
"2006-2007" => "0607apsemployees.csv", "2006-2007" => "0607apsemployees.csv",
"2007-2008" => "0708apsemployees.csv", "2007-2008" => "0708apsemployees.csv",
"2008-2009" => "0809apsemployees.csv", "2008-2009" => "0809apsemployees.csv",
"2009-2010" => "0910apsemployees.csv", "2009-2010" => "0910apsemployees.csv",
"2010-2011" => "1011apsemployees.csv" "2010-2011" => "1011apsemployees.csv"
); );
foreach ($employeeCSVs as $timePeriod => $employeeCSV) { foreach ($employeeCSVs as $timePeriod => $employeeCSV) {
echo $employeeCSV . "<br>" . PHP_EOL; echo $employeeCSV . "<br>" . PHP_EOL;
$row = 1; $row = 1;
if (($handle = fopen($employeeCSV, "r")) !== FALSE) { if (($handle = fopen($employeeCSV, "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) { while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
//print_r($data); //print_r($data);
$name = trim($data[0]); $name = trim($data[0]);
if (isset($nametoid[$name])) { if (isset($nametoid[$name])) {
$id = $nametoid[$name]; $id = $nametoid[$name];
//echo $id . "<br>" . PHP_EOL; //echo $id . "<br>" . PHP_EOL;
@$sums[$id][$timePeriod] += $data[1]; @$sums[$id][$timePeriod] += $data[1];
} else { } else {
echo "<br>ERROR NAME MISSING FROM ID LIST<br><bR>" . PHP_EOL; echo "<br>ERROR NAME MISSING FROM ID LIST<br><bR>" . PHP_EOL;
   
die(); die();
   
} }
} }
fclose($handle); fclose($handle);
} }
} }
foreach ($sums as $id => $sum) { foreach ($sums as $id => $sum) {
echo $id. "<br>" . PHP_EOL; echo $id . "<br>" . PHP_EOL;
$doc = $db->get($id); $doc = $db->get($id);
// print_r($doc); echo $doc->name . "<br>" . PHP_EOL;
if (isset($doc->statistics)) $doc->statistics = Array(); // print_r($doc);
  $changed = false;
  if (!isset($doc->statistics)) {
  $changed = true;
  $doc->statistics = Array();
  }
foreach ($sum as $timePeriod => $value) { 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 // employees: timeperiod, source = apsc state of service, value
?> ?>
   
<?php <?php
   
require_once '../include/common.inc.php'; require_once '../include/common.inc.php';
require($basePath.'lib/phpquery/phpQuery/phpQuery.php'); require($basePath . 'lib/phpquery/phpQuery/phpQuery.php');
   
$db = $server->get_db('disclosr-agencies'); $db = $server->get_db('disclosr-agencies');
$rows = $db->get_view("app", "byName")->rows; $rows = $db->get_view("app", "byName")->rows;
$nametoid = Array(); $nametoid = Array();
$accounts = Array(); $accounts = Array();
foreach ($rows as $row) { foreach ($rows as $row) {
$nametoid[trim($row->key)] = $row->value; $nametoid[trim($row->key)] = $row->value;
} }
   
function extractHTMLAccounts($url, $accountType) { function extractHTMLAccounts($url, $accountType) {
global $accounts, $nametoid; global $accounts, $nametoid;
$request = Requests::get($url); $request = Requests::get($url);
$doc = phpQuery::newDocumentHTML($request->body); $doc = phpQuery::newDocumentHTML($request->body);
phpQuery::selectDocument($doc); phpQuery::selectDocument($doc);
foreach (pq('tr')->elements as $tr) { foreach (pq('tr')->elements as $tr) {
//echo $tr->nodeValue.PHP_EOL; //echo $tr->nodeValue.PHP_EOL;
$agency = ""; $agency = "";
$url = ""; $url = "";
foreach ($tr->childNodes as $td) { foreach ($tr->childNodes as $td) {
$class = $td->getAttribute("class"); $class = $td->getAttribute("class");
//echo "cccc $class ".$td->nodeValue.PHP_EOL; //echo "cccc $class ".$td->nodeValue.PHP_EOL;
if ($class == "s11" || $class == "s10" || $class == "s7") { if ($class == "s11" || $class == "s10" || $class == "s7") {
$agency = $td->nodeValue; $agency = $td->nodeValue;
} else if ($class == "s6" || $class == "s9"){ } else if ($class == "s6" || $class == "s9") {
$url = $td->nodeValue; $url = $td->nodeValue;
foreach($td->childNodes as $a) { foreach ($td->childNodes as $a) {
$href = $a->getAttribute("href"); $href = $a->getAttribute("href");
if ($href != "") { if ($href != "") {
$url = $href; $url = $href;
}  
}  
}  
}  
if ($agency != "" && $url != "") {  
if (!in_array(trim($agency), array_keys($nametoid))) {  
echo trim($agency)." missing" . PHP_EOL;  
} else {  
// echo $agency." = ".$url.PHP_EOL;  
$accounts[$nametoid[trim($agency)]][$accountType][] = $url;  
} }
  }
} }
  }
  if ($agency != "" && $url != "") {
  if (!in_array(trim($agency), array_keys($nametoid))) {
  echo trim($agency) . " missing" . PHP_EOL;
  } else {
  // echo $agency." = ".$url.PHP_EOL;
  $accounts[$nametoid[trim($agency)]][$accountType][] = $url;
  }
  }
} }
   
} }
   
function extractCSVAccounts($url, $accountType, $nameField, $accountField, $filter) { function extractCSVAccounts($url, $accountType, $nameField, $accountField, $filter) {
global $accounts, $nametoid; global $accounts, $nametoid;
$request = Requests::get($url); $request = Requests::get($url);
$Data = str_getcsv($request->body, "\n"); //parse the rows $Data = str_getcsv($request->body, "\n"); //parse the rows
$headers = Array(); $headers = Array();
foreach ($Data as $num => $line) { foreach ($Data as $num => $line) {
$Row = str_getcsv($line, ",",'"'); $Row = str_getcsv($line, ",", '"');
if ($num == 0) { if ($num == 0) {
} else if ($num == 1) { } else if ($num == 1) {
$headers = $Row; $headers = $Row;
//print_r($headers); //print_r($headers);
} else { } else {
if (isset($Row[array_search($nameField, $headers)])) { if (isset($Row[array_search($nameField, $headers)])) {
$agencyName = $Row[array_search($nameField, $headers)]; $agencyName = $Row[array_search($nameField, $headers)];
if (!$filter || $Row[array_search("State", $headers)] == "NAT") { if (!$filter || $Row[array_search("State", $headers)] == "NAT") {
if (!in_array(trim($agencyName), array_keys($nametoid))) { if (!in_array(trim($agencyName), array_keys($nametoid))) {
echo trim($agencyName)." missing" . PHP_EOL; echo trim($agencyName) . " missing" . PHP_EOL;
} else { } else {
// echo $Row[array_search($nameField, $headers)] . PHP_EOL; // echo $Row[array_search($nameField, $headers)] . PHP_EOL;
$accounts[$nametoid[trim($agencyName)]][$accountType][] = $Row[array_search($accountField, $headers)]; $accounts[$nametoid[trim($agencyName)]][$accountType][] = $Row[array_search($accountField, $headers)];
} }
} }
} else { } else {
//echo "error finding agency" . $line . PHP_EOL; //echo "error finding agency" . $line . PHP_EOL;
} }
} }
} }
} }
   
// http://agimo.govspace.gov.au/page/gov2register/ // http://agimo.govspace.gov.au/page/gov2register/
// twitter // twitter
extractCSVAccounts("https://docs.google.com/spreadsheet/pub?key=0Ap1exl80wB8OdHNKVmQ5RVlvQWpibDAxNHkzcU1nV2c&single=true&gid=0&output=csv", "Twitter", "Agency/Body/Event", "", true); extractCSVAccounts("https://docs.google.com/spreadsheet/pub?key=0Ap1exl80wB8OdHNKVmQ5RVlvQWpibDAxNHkzcU1nV2c&single=true&gid=0&output=csv", "Twitter", "Agency/Body/Event", "", true);
// RSS // RSS
extractHTMLAccounts("https://docs.google.com/spreadsheet/pub?hl=en_GB&hl=en_GB&key=0Ah41IAK0HzSTdGJxandJREhLSGlWWUZfZ2xKOTNHZ0E&output=html", "RSS"); extractHTMLAccounts("https://docs.google.com/spreadsheet/pub?hl=en_GB&hl=en_GB&key=0Ah41IAK0HzSTdGJxandJREhLSGlWWUZfZ2xKOTNHZ0E&output=html", "RSS");
// facebook // facebook
extractHTMLAccounts("https://docs.google.com/spreadsheet/pub?hl=en_GB&hl=en_GB&key=0Ah41IAK0HzSTdGtjcW9vOXdyZ3pOV21vQU51VmhzQnc&single=true&gid=0&output=html", "Facebook"); extractHTMLAccounts("https://docs.google.com/spreadsheet/pub?hl=en_GB&hl=en_GB&key=0Ah41IAK0HzSTdGtjcW9vOXdyZ3pOV21vQU51VmhzQnc&single=true&gid=0&output=html", "Facebook");
  foreach ($accounts as $id => $accountTypes) {
  echo $id . "<br>" . PHP_EOL;
  $doc = object_to_array($db->get($id));
  // print_r($doc);
   
  foreach ($accountTypes as $accountType => $accounts) {
  if (!isset($doc["has" . $accountType]) || !is_array($doc["has" . $accountType])) {
  $doc["has" . $accountType] = Array();
  }
  $doc["has" . $accountType] = array_unique(array_merge($doc["has" . $accountType], $accounts));
  }
  $db->save($doc);
  }
?> ?>
   
file:b/admin/metadata.py (new)
  #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)
 
  #http://packages.python.org/CouchDB/client.html
  import couchdb
  import json
  import pprint
  import re
  from tidylib import tidy_document
 
  couch = couchdb.Server('http://127.0.0.1:5984/')
 
  # select database
  docsdb = couch['disclosr-documents']
 
  def f(x):
  invalid = re.compile(r"ensure|testing|flicker|updating|longdesc|Accessibility Checks|not recognized")
  valid = re.compile(r"line")
  return (not invalid.search(x)) and valid.search(x) and x != ''
 
  for row in docsdb.view('app/getValidationRequired'):
  print row.id
  html = docsdb.get_attachment(row.id,row.value.iterkeys().next()).read()
  #print html
  document, errors = tidy_document(html,options={'accessibility-check':1,'show-warnings':0,'markup':0},keep_doc=True)
  #http://www.aprompt.ca/Tidy/accessibilitychecks.html
  #print document
  errors = '\n'.join(filter(f,errors.split('\n')))
  #print errors
  doc = docsdb.get(row.id)
  doc['validation'] = errors
  docsdb.save(doc)
 
file:b/bubbletree.php (new)
 
  <!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>
 
directory:a/couchdb/settee -> directory:b/couchdb/settee
   
<?php <?php
   
include_once('include/common.inc.php'); include_once('include/common.inc.php');
include_header(); include_header();
   
function displayValue($key, $value, $mode) { function displayValue($key, $value, $mode) {
global $db, $schemas; global $db, $schemas;
if ($mode == "view") { if ($mode == "view") {
   
echo "<tr>"; echo "<tr>";
   
echo "<td>" . $schemas['agency']["properties"][$key]['x-title'] . "<br><small>" . $schemas['agency']["properties"][$key]['description'] . "</small></td><td>"; echo "<td>" . $schemas['agency']["properties"][$key]['x-title'] . "<br><small>" . $schemas['agency']["properties"][$key]['description'] . "</small></td><td>";
if (is_array($value)) { if (is_array($value)) {
echo "<ol>"; echo "<ol>";
foreach ($value as $subkey => $subvalue) { foreach ($value as $subkey => $subvalue) {
if (isset($schemas['agency']["properties"][$key]['x-property'])) { if (isset($schemas['agency']["properties"][$key]['x-property'])) {
echo '<li property="' . $schemas['agency']["properties"][$key]['x-property'] . '">'; echo '<li property="' . $schemas['agency']["properties"][$key]['x-property'] . '">';
} else { } else {
echo "<li>"; echo "<li>";
} }
echo "$subvalue</li>"; echo "$subvalue</li>";
} }
echo "</ol></td></tr>"; echo "</ol></td></tr>";
} else { } else {
if (isset($schemas['agency']["properties"][$key]['x-property'])) { if (isset($schemas['agency']["properties"][$key]['x-property'])) {
echo '<span property="' . $schemas['agency']["properties"][$key]['x-property'] . '">'; echo '<span property="' . $schemas['agency']["properties"][$key]['x-property'] . '">';
} else { } else {
echo "<span>"; echo "<span>";
} }
if ((strpos($key, "URL") > 0 || $key == 'website') && $value != "") { if ((strpos($key, "URL") > 0 || $key == 'website') && $value != "") {
echo "<a href='$value'>view</a></span>"; echo "<a href='$value'>view</a></span>";
} else { } else {
echo "$value</span>"; echo "$value</span>";
} }
} }
echo "</td></tr>"; echo "</td></tr>";
} }
if ($mode == "edit") { if ($mode == "edit") {
if (is_array($value)) { if (is_array($value)) {
echo '<div class="row"> echo '<div class="row">
<div class="seven columns"> <div class="seven columns">
<fieldset> <fieldset>
<h5>' . $key . '</h5>'; <h5>' . $key . '</h5>';
foreach ($value as $subkey => $subvalue) { foreach ($value as $subkey => $subvalue) {
echo "<label>$subkey</label><input class='input-text' type='text' id='$key$subkey' name='$key" . '[' . $subkey . "]' value='$subvalue'/></tr>"; echo "<label>$subkey</label><input class='input-text' type='text' id='$key$subkey' name='$key" . '[' . $subkey . "]' value='$subvalue'/></tr>";
} }
echo "</fieldset> echo "</fieldset>
</div> </div>
</div>"; </div>";
} else { } else {
if (strpos($key, "_") === 0) { if (strpos($key, "_") === 0) {
echo"<input type='hidden' id='$key' name='$key' value='$value'/>"; echo"<input type='hidden' id='$key' name='$key' value='$value'/>";
} else if ($key == "parentOrg") { } else if ($key == "parentOrg") {
echo "<label for='$key'>$key</label><select id='$key' name='$key'><option value=''> Select... </option>"; echo "<label for='$key'>$key</label><select id='$key' name='$key'><option value=''> Select... </option>";
$rows = $db->get_view("app", "byDeptStateName")->rows; $rows = $db->get_view("app", "byDeptStateName")->rows;
//print_r($rows); //print_r($rows);
foreach ($rows as $row) { foreach ($rows as $row) {
echo "<option value='{$row->value}'" . (($row->value == $value) ? "SELECTED" : "") . " >" . str_replace("Department of ", "", $row->key) . "</option>"; echo "<option value='{$row->value}'" . (($row->value == $value) ? "SELECTED" : "") . " >" . str_replace("Department of ", "", $row->key) . "</option>";
} }
echo" </select>"; echo" </select>";
} else { } else {
echo "<label>$key</label><input class='input-text' type='text' id='$key' name='$key' value='$value'/>"; echo "<label>$key</label><input class='input-text' type='text' id='$key' name='$key' value='$value'/>";
if ((strpos($key, "URL") > 0 || $key == 'website') && $value != "") { if ((strpos($key, "URL") > 0 || $key == 'website') && $value != "") {
echo "<a href='$value'>view</a>"; echo "<a href='$value'>view</a>";
} }
if ($key == 'abn') { if ($key == 'abn') {
echo "<a href='http://www.abr.business.gov.au/SearchByAbn.aspx?SearchText=$value'>view abn</a>"; echo "<a href='http://www.abr.business.gov.au/SearchByAbn.aspx?SearchText=$value'>view abn</a>";
} }
} }
} }
} }
// //
} }
   
function addDefaultFields($row) { function addDefaultFields($row) {
global $schemas; global $schemas;
$defaultFields = array_keys($schemas['agency']['properties']); $defaultFields = array_keys($schemas['agency']['properties']);
foreach ($defaultFields as $defaultField) { foreach ($defaultFields as $defaultField) {
if (!isset($row[$defaultField])) { if (!isset($row[$defaultField])) {
if ($schemas['agency']['properties'][$defaultField]['type'] == "string") { if ($schemas['agency']['properties'][$defaultField]['type'] == "string") {
$row[$defaultField] = ""; $row[$defaultField] = "";
} }
if ($schemas['agency']['properties'][$defaultField]['type'] == "array") { if ($schemas['agency']['properties'][$defaultField]['type'] == "array") {
$row[$defaultField] = Array(""); $row[$defaultField] = Array("");
} }
} else if ($schemas['agency']['properties'][$defaultField]['type'] == "array") { } else if ($schemas['agency']['properties'][$defaultField]['type'] == "array") {
if (is_array($row[$defaultField])) { if (is_array($row[$defaultField])) {
$row[$defaultField][] = ""; $row[$defaultField][] = "";
$row[$defaultField][] = ""; $row[$defaultField][] = "";
$row[$defaultField][] = ""; $row[$defaultField][] = "";
} else { } else {
$value = $row[$defaultField]; $value = $row[$defaultField];
$row[$defaultField] = Array($value); $row[$defaultField] = Array($value);
$row[$defaultField][] = ""; $row[$defaultField][] = "";
$row[$defaultField][] = ""; $row[$defaultField][] = "";
} }
} }
} }
return $row; return $row;
} }
   
$db = $server->get_db('disclosr-agencies'); $db = $server->get_db('disclosr-agencies');
   
if (isset($_REQUEST['id'])) { if (isset($_REQUEST['id'])) {
//get an agency record as json/html, search by name/abn/id //get an agency record as json/html, search by name/abn/id
// by name = startkey="Ham"&endkey="Ham\ufff0" // by name = startkey="Ham"&endkey="Ham\ufff0"
// edit? // edit?
   
$obj = $db->get($_REQUEST['id']); $obj = $db->get($_REQUEST['id']);
//print_r($row); //print_r($row);
if (sizeof($_POST) > 0) { if (sizeof($_POST) > 0) {
//print_r($_POST); //print_r($_POST);
foreach ($_POST as $postkey => $postvalue) { foreach ($_POST as $postkey => $postvalue) {
if ($postvalue == "") { if ($postvalue == "") {
unset($_POST[$postkey]); unset($_POST[$postkey]);
} }
if (is_array($postvalue)) { if (is_array($postvalue)) {
if (count($postvalue) == 1 && $postvalue[0] == "") { if (count($postvalue) == 1 && $postvalue[0] == "") {
unset($_POST[$postkey]); unset($_POST[$postkey]);
} else { } else {
foreach ($_POST[$postkey] as $key => &$value) { foreach ($_POST[$postkey] as $key => &$value) {
if ($value == "") { if ($value == "") {
unset($_POST[$postkey][$key]); unset($_POST[$postkey][$key]);
} }
} }
} }
} }
} }
if (isset($_POST['_id']) && $db->get_rev($_POST['_id']) == $_POST['_rev']) { if (isset($_POST['_id']) && $db->get_rev($_POST['_id']) == $_POST['_rev']) {
echo "Edited version was latest version, continue saving"; echo "Edited version was latest version, continue saving";
$newdoc = $_POST; $newdoc = $_POST;
$newdoc['metadata']['lastModified'] = time(); $newdoc['metadata']['lastModified'] = time();
$obj = $db->save($newdoc); $obj = $db->save($newdoc);
} else { } else {
echo "ALERT doc revised by someone else while editing. Document not saved."; echo "ALERT doc revised by someone else while editing. Document not saved.";
} }
} }
   
$mode = "edit"; $mode = "view";
$rowArray = object_to_array($obj); $rowArray = object_to_array($obj);
ksort($rowArray); ksort($rowArray);
if ($mode == "edit") { if ($mode == "edit") {
$row = addDefaultFields($rowArray); $row = addDefaultFields($rowArray);
} else { } else {
$row = $rowArray; $row = $rowArray;
} }
   
if ($mode == "view") { if ($mode == "view") {
echo '<div typeof="schema:GovernmentOrganisation" about="#' . $row['_id'] . '"><table width="100%">'; echo '<div typeof="schema:GovernmentOrganisation" about="#' . $row['_id'] . '"><table width="100%">';
echo '<tr> <td colspan="2"><h3>' . $row['name'] . "</h3></td></tr>"; echo '<tr> <td colspan="2"><h3>' . $row['name'] . "</h3></td></tr>";
echo "<tr><th>Field Name</th><th>Field Value</th></tr>"; echo "<tr><th>Field Name</th><th>Field Value</th></tr>";
} }
if ($mode == "edit") { if ($mode == "edit") {
?> ?>
<input id="addfield" type="button" value="Add Field"/> <input id="addfield" type="button" value="Add Field"/>
<script> <script>
window.onload = function() { window.onload = function() {
$(document).ready(function() { $(document).ready(function() {
// put all your jQuery goodness in here. // put all your jQuery goodness in here.
// http://charlie.griefer.com/blog/2009/09/17/jquery-dynamically-adding-form-elements/ // http://charlie.griefer.com/blog/2009/09/17/jquery-dynamically-adding-form-elements/
$('#addfield').click(function() { $('#addfield').click(function() {
var field_name=window.prompt("fieldname?",""); var field_name=window.prompt("fieldname?","");
if (field_name !="") { if (field_name !="") {
$('#submitbutton').before($('<span></span>') $('#submitbutton').before($('<span></span>')
.append("<label>"+field_name+"</label>") .append("<label>"+field_name+"</label>")
.append("<input class='input-text' type='text' id='"+field_name+"' name='"+field_name+"'/>") .append("<input class='input-text' type='text' id='"+field_name+"' name='"+field_name+"'/>")
); );
} }
}); });
}); });
}; };
</script> </script>
<form id="editform" class="nice" method="post"> <form id="editform" class="nice" method="post">
<?php <?php
   
} }
foreach ($row as $key => $value) { foreach ($row as $key => $value) {
echo displayValue($key, $value, $mode); echo displayValue($key, $value, $mode);
} }
if ($mode == "view") { if ($mode == "view") {
echo "</table></div>"; echo "</table></div>";
} }
if ($mode == "edit") { if ($mode == "edit") {
echo '<input id="submitbutton" type="submit"/></form>'; echo '<input id="submitbutton" type="submit"/></form>';
} }
} else { } else {
   
try { try {
/* $rows = $db->get_view("app", "showNamesABNs")->rows; /* $rows = $db->get_view("app", "showNamesABNs")->rows;
//print_r($rows); //print_r($rows);
foreach ($rows as $row) { foreach ($rows as $row) {
// print_r($row); // print_r($row);
echo '<li><a href="getAgency.php?id=' . $row->key . '">' . echo '<li><a href="getAgency.php?id=' . $row->key . '">' .
(isset($row->value->name) && $row->value->name != "" ? $row->value->name : "NO NAME " . $row->value->abn) (isset($row->value->name) && $row->value->name != "" ? $row->value->name : "NO NAME " . $row->value->abn)
. '</a></li>'; . '</a></li>';
} */ } */
$rows = $db->get_view("app", "byCanonicalName")->rows; $rows = $db->get_view("app", "byCanonicalName")->rows;
//print_r($rows); //print_r($rows);
echo '<ul>'; echo '<ul>';
foreach ($rows as $row) { foreach ($rows as $row) {
// print_r($row); // print_r($row);
echo '<li typeof="schema:GovernmentOrganisation foaf:Organization" about="getAgency.php?id=' . $row->value->_id . '"> echo '<li typeof="schema:GovernmentOrganisation foaf:Organization" about="getAgency.php?id=' . $row->value->_id . '">
<a href="getAgency.php?id=' . $row->value->_id . '" rel="schema:url foaf:page" property="schema:name foaf:name">' . <a href="getAgency.php?id=' . $row->value->_id . '" rel="schema:url foaf:page" property="schema:name foaf:name">' .
$row->value->name $row->value->name
. '</a></li>'; . '</a></li>';
} }
echo "</ul>"; echo "</ul>";
} catch (SetteeRestClientException $e) { } catch (SetteeRestClientException $e) {
setteErrorHandler($e); setteErrorHandler($e);
} }
} }
include_footer(); include_footer();
?> ?>
   
  google-site-verification: google676a414ad086cefb.html
 
file:a/graph.php -> file:b/graph.php
<?php <?php
include_once('include/common.inc.php'); include_once('include/common.inc.php');
//include_header(); //include_header();
$format = "html"; $format = "html";
if (isset($_REQUEST['format'])) { if (isset($_REQUEST['format'])) {
$format = $_REQUEST['format']; $format = $_REQUEST['format'];
} }
   
function add_node($id, $label, $parent="") { function add_node($id, $label, $parent="") {
global $format; global $format;
if ($format == "html") { if ($format == "html") {
// echo "nodes[\"$id\"] = graph.newNode({label: \"$label\"});" . PHP_EOL; // echo "nodes[\"$id\"] = graph.newNode({label: \"$label\"});" . PHP_EOL;
} }
if ($format == "dot" && $label != "") { if ($format == "dot" && $label != "") {
echo "$id [label=\"$label\"];". PHP_EOL; echo "$id [label=\"$label\"];". PHP_EOL;
} }
if ($format == "gexf") { if ($format == "gexf") {
echo "<node id='$id' label=\"".htmlentities($label,ENT_XML1)."\" ".($parent != ""? "pid='$parent'><viz:size value='1'/>":"><viz:size value='2'/>") echo "<node id='$id' label=\"".htmlentities($label,ENT_XML1)."\" ".($parent != ""? "pid='$parent'><viz:size value='1'/>":"><viz:size value='2'/>")
."<viz:color b='".rand(0,255)."' g='".rand(0,255)."' r='".rand(0,255)."'/>" ."<viz:color b='".rand(0,255)."' g='".rand(0,255)."' r='".rand(0,255)."'/>"
."</node>". PHP_EOL; ."</node>". PHP_EOL;
} }
} }
   
function add_edge($from, $to, $color) { function add_edge($from, $to, $color) {
global $format; global $format;
if ($format == "html") { if ($format == "html") {
// echo "graph.newEdge(nodes[\"$from\"], nodes['$to'], {color: '$color'});" . PHP_EOL; // echo "graph.newEdge(nodes[\"$from\"], nodes['$to'], {color: '$color'});" . PHP_EOL;
} }
if ($format == "dot") { if ($format == "dot") {
echo "$from -> $to ".($color != ""? "[color=$color]":"").";". PHP_EOL; echo "$from -> $to ".($color != ""? "[color=$color]":"").";". PHP_EOL;
} }
if ($format == "gexf") { if ($format == "gexf") {
echo "<edge id='$from$to' source='$from' target='$to' />". PHP_EOL; echo "<edge id='$from$to' source='$from' target='$to' />". PHP_EOL;
} }
} }
if ($format == "gexf") { if ($format == "gexf") {
//header('Content-Type: text/xml'); //header('Content-Type: text/xml');
header('Content-Type: application/gexf+xml'); header('Content-Type: application/gexf+xml');
echo '<?xml version="1.0" encoding="UTF-8"?> echo '<?xml version="1.0" encoding="UTF-8"?>
<gexf xmlns="http://www.gexf.net/1.2draft" xmlns:viz="http://www.gexf.net/1.2draft/viz" version="1.2"> <gexf xmlns="http://www.gexf.net/1.2draft" xmlns:viz="http://www.gexf.net/1.2draft/viz" version="1.2">
<meta lastmodifieddate="2009-03-20"> <meta lastmodifieddate="2009-03-20">
<creator>Gexf.net</creator> <creator>Gexf.net</creator>
<description>A hello world! file</description> <description>A hello world! file</description>
</meta> </meta>
<graph mode="static" defaultedgetype="directed"> <graph mode="static" defaultedgetype="directed">
<nodes>'. PHP_EOL; <nodes>'. PHP_EOL;
} }
   
if ($format == "dot") { if ($format == "dot") {
echo 'digraph g {'. PHP_EOL; echo 'digraph g {'. PHP_EOL;
} }
$db = $server->get_db('disclosr-agencies'); $db = $server->get_db('disclosr-agencies');
add_node("fedg","Federal Government - Commonwealth of Australia"); add_node("fedg","Federal Government - Commonwealth of Australia");
try { try {
$rows = $db->get_view("app", "byCanonicalName", null, true)->rows; $rows = $db->get_view("app", "byCanonicalName", null, true)->rows;
//print_r($rows); //print_r($rows);
foreach ($rows as $row) { foreach ($rows as $row) {
add_node($row->id, $row->key); add_node($row->id, $row->key);
} }
} catch (SetteeRestClientException $e) { } catch (SetteeRestClientException $e) {
setteErrorHandler($e); setteErrorHandler($e);
} }
if ($format == "gexf") { if ($format == "gexf") {
echo '</nodes> echo '</nodes>
<edges>'. PHP_EOL; <edges>'. PHP_EOL;
} }
try { try {
$rows = $db->get_view("app", "byDeptStateName", null, true)->rows; $rows = $db->get_view("app", "byDeptStateName", null, true)->rows;
//print_r($rows); //print_r($rows);
foreach ($rows as $row) { foreach ($rows as $row) {
add_edge("fedg", $row->value, 'yellow'); add_edge("fedg", $row->value, 'yellow');
} }
} catch (SetteeRestClientException $e) { } catch (SetteeRestClientException $e) {
setteErrorHandler($e); setteErrorHandler($e);
} }
   
try { try {
$rows = $db->get_view("app", "parentOrgs", null, true)->rows; $rows = $db->get_view("app", "parentOrgs", null, true)->rows;
// print_r($rows); // print_r($rows);
foreach ($rows as $row) { foreach ($rows as $row) {
add_edge($row->key, $row->value, 'blue'); add_edge($row->key, $row->value, 'blue');
} }
} catch (SetteeRestClientException $e) { } catch (SetteeRestClientException $e) {
setteErrorHandler($e); setteErrorHandler($e);
} }
if ($format == "html") { if ($format == "html") {
?> ?>
<div id="sigma-example" width="960" style="min-height:800px;background-color: #333;"></div> <div id="sigma-example" width="960" style="min-height:800px;background-color: #333;"></div>
<script src="javascripts/sigma.min.js"></script> <script src="javascripts/sigma.min.js"></script>
<script src="javascripts/sigma/plugins/sigma.parseGexf.js"></script> <script src="javascripts/sigma/plugins/sigma.parseGexf.js"></script>
<script src="javascripts/sigma/plugins/sigma.forceatlas2.js"></script> <script src="javascripts/sigma/plugins/sigma.forceatlas2.js"></script>
<script type="text/javascript">function init() { <script type="text/javascript">function init() {
// Instanciate sigma.js and customize rendering : // Instanciate sigma.js and customize rendering :
var sigInst = sigma.init(document.getElementById('sigma-example')).drawingProperties({ var sigInst = sigma.init(document.getElementById('sigma-example')).drawingProperties({
defaultLabelColor: '#fff', defaultLabelColor: '#fff',
defaultLabelSize: 14, defaultLabelSize: 14,
defaultLabelBGColor: '#fff', defaultLabelBGColor: '#fff',
defaultLabelHoverColor: '#000', defaultLabelHoverColor: '#000',
labelThreshold: 6, labelThreshold: 6,
defaultEdgeType: 'curve' defaultEdgeType: 'curve'
}).graphProperties({ }).graphProperties({
minNodeSize: 0.5, minNodeSize: 0.5,
maxNodeSize: 5, maxNodeSize: 5,
minEdgeSize: 5, minEdgeSize: 5,
maxEdgeSize: 5 maxEdgeSize: 5
}).mouseProperties({ }).mouseProperties({
maxRatio: 32 maxRatio: 32
}); });
   
// Parse a GEXF encoded file to fill the graph // Parse a GEXF encoded file to fill the graph
// (requires "sigma.parseGexf.js" to be included) // (requires "sigma.parseGexf.js" to be included)
sigInst.parseGexf('graph.php?format=gexf'); sigInst.parseGexf('graph.php?format=gexf');
sigInst.bind('downnodes',function(event){ sigInst.bind('downnodes',function(event){
var nodes = event.content; var nodes = event.content;
}); });
// Draw the graph :  
sigInst.draw();  
// Start the ForceAtlas2 algorithm // Start the ForceAtlas2 algorithm
// (requires "sigma.forceatlas2.js" to be included) // (requires "sigma.forceatlas2.js" to be included)
sigInst.startForceAtlas2(); sigInst.startForceAtlas2();
  // Draw the graph :
  sigInst.draw();
} }
   
if (document.addEventListener) { if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", init, false); document.addEventListener("DOMContentLoaded", init, false);
} else { } else {
window.onload = init; window.onload = init;
} }
</script> </script>
   
<?php <?php
} }
if ($format == "dot") { if ($format == "dot") {
echo "}"; echo "}";
} }
if ($format == "gexf") { if ($format == "gexf") {
echo ' </edges> echo ' </edges>
</graph> </graph>
</gexf>'. PHP_EOL; </gexf>'. PHP_EOL;
} }
//include_footer(); //include_footer();
?> ?>
   
   
<?php <?php
   
date_default_timezone_set("Australia/Sydney"); date_default_timezone_set("Australia/Sydney");
   
$basePath = ""; $basePath = "";
if (strstr($_SERVER['PHP_SELF'], "alaveteli/") if (strstr($_SERVER['PHP_SELF'], "alaveteli/")
|| strstr($_SERVER['PHP_SELF'], "admin/") || strstr($_SERVER['PHP_SELF'], "admin/")
|| strstr($_SERVER['PHP_SELF'], "lib/") || strstr($_SERVER['PHP_SELF'], "lib/")
|| strstr($_SERVER['PHP_SELF'], "include/")) || strstr($_SERVER['PHP_SELF'], "include/"))
$basePath = "../"; $basePath = "../";
   
include_once ('couchdb.inc.php'); include_once ('couchdb.inc.php');
include_once ('template.inc.php'); include_once ('template.inc.php');
require_once $basePath.'lib/Requests/library/Requests.php'; require_once $basePath.'lib/Requests/library/Requests.php';
   
Requests::register_autoloader(); Requests::register_autoloader();
# Convert a stdClass to an Array. http://www.php.net/manual/en/language.types.object.php#102735 # Convert a stdClass to an Array. http://www.php.net/manual/en/language.types.object.php#102735
   
function object_to_array(stdClass $Class) { function object_to_array(stdClass $Class) {
# Typecast to (array) automatically converts stdClass -> array. # Typecast to (array) automatically converts stdClass -> array.
$Class = (array) $Class; $Class = (array) $Class;
   
# Iterate through the former properties looking for any stdClass properties. # Iterate through the former properties looking for any stdClass properties.
# Recursively apply (array). # Recursively apply (array).
foreach ($Class as $key => $value) { foreach ($Class as $key => $value) {
if (is_object($value) && get_class($value) === 'stdClass') { if (is_object($value) && get_class($value) === 'stdClass') {
$Class[$key] = object_to_array($value); $Class[$key] = object_to_array($value);
} }
} }
return $Class; return $Class;
} }
   
# Convert an Array to stdClass. http://www.php.net/manual/en/language.types.object.php#102735 # Convert an Array to stdClass. http://www.php.net/manual/en/language.types.object.php#102735
   
function array_to_object(array $array) { function array_to_object(array $array) {
# Iterate through our array looking for array values. # Iterate through our array looking for array values.
# If found recurvisely call itself. # If found recurvisely call itself.
foreach ($array as $key => $value) { foreach ($array as $key => $value) {
if (is_array($value)) { if (is_array($value)) {
$array[$key] = array_to_object($value); $array[$key] = array_to_object($value);
} }
} }
   
# Typecast to (object) will automatically convert array -> stdClass # Typecast to (object) will automatically convert array -> stdClass
return (object) $array; return (object) $array;
} }
   
function dept_to_portfolio($deptName) { function dept_to_portfolio($deptName) {
return trim(str_replace("Department of", "", str_replace("Department of the", "Department of", $deptName))); return trim(str_replace("Department of", "", str_replace("Department of the", "Department of", $deptName)));
} }
function phrase_to_tag ($phrase) { function phrase_to_tag ($phrase) {
return str_replace(" ","_",str_replace("'","",str_replace(",","",strtolower($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) function GetDomain($url)
{ {
$nowww = ereg_replace('www\.','',$url); $nowww = ereg_replace('www\.','',$url);
$domain = parse_url($nowww); $domain = parse_url($nowww);
if(!empty($domain["host"])) if(!empty($domain["host"]))
{ {
return $domain["host"]; return $domain["host"];
} else } else
{ {
return $domain["path"]; return $domain["path"];
} }
} }
   
<?php <?php
   
include $basePath . "schemas/schemas.inc.php"; include $basePath . "schemas/schemas.inc.php";
   
require ($basePath . 'couchdb/settee/src/settee.php'); require ($basePath . 'couchdb/settee/src/settee.php');
   
function createDocumentsDesignDoc() { function createDocumentsDesignDoc() {
/*"views": { /* "views": {
"web_server": { "web_server": {
"map": "function(doc) {\n emit(doc.web_server, 1);\n}", "map": "function(doc) {\n emit(doc.web_server, 1);\n}",
"reduce": "function (key, values, rereduce) {\n return sum(values);\n}" "reduce": "function (key, values, rereduce) {\n return sum(values);\n}"
}, },
"byAgency": { "byAgency": {
"map": "function(doc) {\n emit(doc.agencyID, 1);\n}", "map": "function(doc) {\n emit(doc.agencyID, 1);\n}",
"reduce": "function (key, values, rereduce) {\n return sum(values);\n}" "reduce": "function (key, values, rereduce) {\n return sum(values);\n}"
}, },
"byURL": { "byURL": {
"map": "function(doc) {\n emit(doc.url, doc);\n}" "map": "function(doc) {\n emit(doc.url, doc);\n}"
}, },
"agency": { "agency": {
"map": "function(doc) {\n emit(doc.agencyID, doc);\n}" "map": "function(doc) {\n emit(doc.agencyID, doc);\n}"
}, },
"byWebServer": { "byWebServer": {
"map": "function(doc) {\n emit(doc.web_server, doc);\n}" "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() { function createAgencyDesignDoc() {
global $db; global $db;
$obj = new stdClass(); $obj = new stdClass();
$obj->_id = "_design/" . urlencode("app"); $obj->_id = "_design/" . urlencode("app");
$obj->language = "javascript"; $obj->language = "javascript";
$obj->views->all->map = "function(doc) { emit(doc._id, doc); };"; $obj->views->all->map = "function(doc) { emit(doc._id, doc); };";
$obj->views->byABN->map = "function(doc) { emit(doc.abn, doc); };"; $obj->views->byABN->map = "function(doc) { emit(doc.abn, doc); };";
$obj->views->byCanonicalName->map = "function(doc) { $obj->views->byCanonicalName->map = "function(doc) {
if (doc.parentOrg || doc.orgType == 'FMA-DepartmentOfState') { if (doc.parentOrg || doc.orgType == 'FMA-DepartmentOfState') {
emit(doc.name, doc); emit(doc.name, doc);
} }
};"; };";
$obj->views->byDeptStateName->map = "function(doc) { $obj->views->byDeptStateName->map = "function(doc) {
if (doc.orgType == 'FMA-DepartmentOfState') { if (doc.orgType == 'FMA-DepartmentOfState') {
emit(doc.name, doc._id); emit(doc.name, doc._id);
} }
};"; };";
$obj->views->parentOrgs->map = "function(doc) { $obj->views->parentOrgs->map = "function(doc) {
if (doc.parentOrg) { if (doc.parentOrg) {
emit(doc._id, doc.parentOrg); emit(doc._id, doc.parentOrg);
} }
};"; };";
$obj->views->byName->map = 'function(doc) { $obj->views->byName->map = 'function(doc) {
if (typeof(doc["status"]) == "undefined" || doc["status"] != "suspended") { if (typeof(doc["status"]) == "undefined" || doc["status"] != "suspended") {
emit(doc.name, doc._id); emit(doc.name, doc._id);
if (typeof(doc.shortName) != "undefined" && doc.shortName != doc.name) { if (typeof(doc.shortName) != "undefined" && doc.shortName != doc.name) {
emit(doc.shortName, doc._id); emit(doc.shortName, doc._id);
} }
for (name in doc.otherNames) { for (name in doc.otherNames) {
if (doc.otherNames[name] != "" && doc.otherNames[name] != doc.name) { if (doc.otherNames[name] != "" && doc.otherNames[name] != doc.name) {
emit(doc.otherNames[name], doc._id); emit(doc.otherNames[name], doc._id);
} }
} }
for (name in doc.foiBodies) { for (name in doc.foiBodies) {
if (doc.foiBodies[name] != "" && doc.foiBodies[name] != doc.name) { if (doc.foiBodies[name] != "" && doc.foiBodies[name] != doc.name) {
emit(doc.foiBodies[name], doc._id); emit(doc.foiBodies[name], doc._id);
} }
} }
} }
};'; };';
   
$obj->views->foiEmails->map = "function(doc) { $obj->views->foiEmails->map = "function(doc) {
emit(doc._id, doc.foiEmail); emit(doc._id, doc.foiEmail);
};"; };";
   
$obj->views->byLastModified->map = "function(doc) { emit(doc.metadata.lastModified, doc); }"; $obj->views->byLastModified->map = "function(doc) { emit(doc.metadata.lastModified, doc); }";
$obj->views->getActive->map = 'function(doc) { if (doc.status == "active") { emit(doc._id, doc); } };'; $obj->views->getActive->map = 'function(doc) { if (doc.status == "active") { emit(doc._id, doc); } };';
$obj->views->getSuspended->map = 'function(doc) { if (doc.status == "suspended") { emit(doc._id, doc); } };'; $obj->views->getSuspended->map = 'function(doc) { if (doc.status == "suspended") { emit(doc._id, doc); } };';
$obj->views->getScrapeRequired->map = "function(doc) { $obj->views->getScrapeRequired->map = "function(doc) {
   
var lastScrape = Date.parse(doc.metadata.lastScraped); var lastScrape = Date.parse(doc.metadata.lastScraped);
   
var today = new Date(); var today = new Date();
   
if (!lastScrape || lastScrape.getTime() + 1000 != today.getTime()) { if (!lastScrape || lastScrape.getTime() + 1000 != today.getTime()) {
emit(doc._id, doc); emit(doc._id, doc);
} }
   
};"; };";
$obj->views->showNamesABNs->map = "function(doc) { emit(doc._id, {name: doc.name, abn: doc.abn}); };"; $obj->views->showNamesABNs->map = "function(doc) { emit(doc._id, {name: doc.name, abn: doc.abn}); };";
$obj->views->getConflicts->map = "function(doc) { $obj->views->getConflicts->map = "function(doc) {
if (doc._conflicts) { if (doc._conflicts) {
emit(null, [doc._rev].concat(doc._conflicts)); emit(null, [doc._rev].concat(doc._conflicts));
} }
}"; }";
// http://stackoverflow.com/questions/646628/javascript-startswith // 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) { String.prototype.startsWith = function (str) {
return !this.indexOf(str); return !this.indexOf(str);
} }
} }
   
function(doc) { function(doc) {
count = 0; count = 0;
if (doc["status"] != "suspended") { if (doc["status"] != "suspended") {
for(var propName in doc) { for(var propName in doc) {
if(typeof(doc[propName]) != "undefined" && doc[propName] != "") { if(typeof(doc[propName]) != "undefined" && doc[propName] != "") {
count++; count++;
} }
} }
portfolio = doc.parentOrg; portfolio = doc.parentOrg;
if (doc.orgType == "FMA-DepartmentOfState") { if (doc.orgType == "FMA-DepartmentOfState") {
portfolio = doc._id; portfolio = doc._id;
} }
if (doc.orgType == "Court-Commonwealth" || doc.orgType == "FMA-DepartmentOfParliament") { if (doc.orgType == "Court-Commonwealth" || doc.orgType == "FMA-DepartmentOfParliament") {
portfolio = doc.orgType; portfolio = doc.orgType;
} }
emit(count+doc._id, {id:doc._id, name: doc.name, score:count, orgType: doc.orgType, portfolio:portfolio}); 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) { String.prototype.startsWith = function (str) {
return !this.indexOf(str); return !this.indexOf(str);
} }
} }
if(!String.prototype.endsWith){ if(!String.prototype.endsWith){
String.prototype.endsWith = function(suffix) { String.prototype.endsWith = function(suffix) {
    return this.indexOf(suffix, this.length - suffix.length) !== -1;     return this.indexOf(suffix, this.length - suffix.length) !== -1;
}; };
} }
function(doc) { function(doc) {
if (typeof(doc["status"]) == "undefined" || doc["status"] != "suspended") { if (typeof(doc["status"]) == "undefined" || doc["status"] != "suspended") {
for(var propName in doc) { for(var propName in doc) {
if(typeof(doc[propName]) != "undefined" && (propName.startsWith("has") || propName.endsWith("URL"))) { if(typeof(doc[propName]) != "undefined" && (propName.startsWith("has") || propName.endsWith("URL"))) {
emit(propName, 1); emit(propName, 1);
} }
} }
emit("total", 1); emit("total", 1);
} }
}'; }';
$obj->views->scoreHas->reduce = 'function (key, values, rereduce) { $obj->views->scoreHas->reduce = 'function (key, values, rereduce) {
return sum(values); return sum(values);
}'; }';
$obj->views->fieldNames->map = ' $obj->views->fieldNames->map = '
function(doc) { function(doc) {
for(var propName in doc) { for(var propName in doc) {
emit(propName, doc._id); emit(propName, doc._id);
} }
}'; }';
$obj->views->fieldNames->reduce = 'function (key, values, rereduce) { $obj->views->fieldNames->reduce = 'function (key, values, rereduce) {
return values.length; return values.length;
}'; }';
// allow safe updates (even if slightly slower due to extra: rev-detection check). // allow safe updates (even if slightly slower due to extra: rev-detection check).
return $db->save($obj, true); return $db->save($obj, true);
} }
   
if (php_uname('n') == "vanille") { if (php_uname('n') == "vanille") {
$serverAddr = 'http://192.168.178.21:5984/'; $serverAddr = 'http://192.168.178.21:5984/';
   
} else } else
if (php_uname('n') == "KYUUBEY") { if (php_uname('n') == "KYUUBEY") {
   
$serverAddr = 'http://192.168.1.148:5984/'; $serverAddr = 'http://192.168.1.148:5984/';
} else { } else {
$serverAddr = 'http://127.0.0.1:5984/'; $serverAddr = 'http://127.0.0.1:5984/';
} }
$server = new SetteeServer($serverAddr); $server = new SetteeServer($serverAddr);
   
function setteErrorHandler($e) { function setteErrorHandler($e) {
echo $e->getMessage() . "<br>" . PHP_EOL; echo $e->getMessage() . "<br>" . PHP_EOL;
} }
   
file:b/index.php (new)
  <?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)
 
   
directory:a/javascripts/sigma -> directory:b/javascripts/sigma
   
/* sigmajs.org - an open-source light-weight JavaScript graph drawing library - Version: 0.1 - Author: Alexis Jacomy - License: MIT */ // Define packages:
var sigma={tools:{},classes:{},instances:{}}; var sigma = {};
(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; sigma.tools = {};
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(","), sigma.classes = {};
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.instances = {};
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}}; // Adding Array helpers, if not present yet:
(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} (function() {
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]= if (!Array.prototype.some) {
+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, Array.prototype.some = function(fun /*, thisp*/) {
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, var len = this.length;
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.')}); if (typeof fun != 'function') {
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]}): throw new TypeError();
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, var thisp = arguments[1];
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- for (var i = 0; i < len; i++) {
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= if (i in this &&
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= fun.call(thisp, this[i], i, this)) {
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}; return true;
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"); return false;
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)|| if (!Array.prototype.forEach) {
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", Array.prototype.forEach = function(fun /*, thisp*/) {
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, var len = this.length;
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= if (typeof fun != 'function') {
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++; throw new TypeError();
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, var thisp = arguments[1];
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"== for (var i = 0; i < len; i++) {
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_"+ if (i in this) {
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, fun.call(thisp, this[i], i, this);
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, if (!Array.prototype.map) {
"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= Array.prototype.map = function(fun /*, thisp*/) {
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", var len = this.length;
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: if (typeof fun != 'function') {
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}; throw new TypeError();
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}; var res = new Array(len);
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); var thisp = arguments[1];
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", for (var i = 0; i < len; i++) {
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? if (i in this) {
(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!= res[i] = fun.call(thisp, this[i], i, this);
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")|| return res;
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})();  
  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)
  <?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
# www.robotstxt.org/ # www.robotstxt.org/
# www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449 # www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449
   
User-agent: * User-agent: *
Disallow: /admin/ Disallow: /admin/
  Sitemap: http://orgs.disclosurelo.gs/sitemap.xml.php
file:b/sitemap.xml.php (new)
  <?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>';
  ?>
 
<!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>  
 
/* 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  
for each agency, record when last changed (number of days too) and show a couple of URLs that were in that change  
 
 
for each agency, find a scrapped document and read the webserver off it  
file:b/webserver.php (new)
  <?php
 
  include_once('include/common.inc.php');
  include_header();
 
  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();
  ?>