Merge branch 'master' of ssh://apples.lambdacomplex.org/git/disclosr
Former-commit-id: d943194e8dfa31e9f1d3ccea08fd7be419d00c67
--- a/.gitmodules
+++ b/.gitmodules
@@ -19,4 +19,7 @@
[submodule "javascripts/sigma"]
path = javascripts/sigma
url = https://github.com/jacomyal/sigma.js.git
+[submodule "javascripts/bubbletree"]
+ path = javascripts/bubbletree
+ url = https://github.com/okfn/bubbletree.git
--- a/about.php
+++ b/about.php
@@ -1,16 +1,16 @@
<?php
include_once('include/common.inc.php');
-include_header();
+include_header('About');
?>
<div class="foundation-header">
<h1><a href="about.php">About/FAQ</a></h1>
<h4 class="subheader">Lorem ipsum.</h4>
</div>
<h2> What is this? </h2>
-Disclosr is a project to monitor Australian Federal Government agencies
+Disclo.gs is a project to monitor Australian Federal Government agencies
compliance with their <a href="http://www.oaic.gov.au/publications/other_operational/foi_policy_frequently_asked_questions.html#_Toc291837571">"proactive disclosure requirements"</a>.
-OGRE (Open Government Realization Evaluation) is a ranking of compliance with these requirements.
-Prometheus is the agent which polls agency websites to assess compliance.
+<h2> Attributions </h2>
+National Archives of Australia, Australian Governments’ Interactive Functions Thesaurus, 2nd edition, September 2005, published at http://www.naa.gov.au/recordkeeping/thesaurus/index.htm.
<h2> Open everything </h2>
All documents released CC-BY 3 AU
--- /dev/null
+++ b/admin/conflicts.php
@@ -1,1 +1,48 @@
+<?php
+include_once('../include/common.inc.php');
+include_header();
+ require_once '../lib/php-diff/lib/Diff.php';
+ require_once '../lib/php-diff/lib/Diff/Renderer/Html/SideBySide.php';
+
+$db = $server->get_db('disclosr-agencies');
+
+try {
+ $rows = $db->get_view("app", "getConflicts", null, true)->rows;
+ //print_r($rows);
+ foreach ($rows as $row) {
+echo "<h2>".$row->id."</h2>";
+$request = Requests::get($serverAddr."disclosr-agencies/".$row->id);
+$origSort = object_to_array(json_decode($request->body));
+ksort($origSort);
+ $origDoc = explode(",",json_encode($origSort));
+ foreach($row->value as $conflictRev) {
+$conflictURL = $serverAddr."disclosr-agencies/".$row->id."?rev=".$conflictRev;
+$request = Requests::get($conflictURL);
+$conflictSort = object_to_array(json_decode($request->body));
+ksort($conflictSort);
+ $conflictDoc = explode(",",json_encode($conflictSort));
+echo "curl -X DELETE ".$conflictURL."<br>".PHP_EOL;
+ // Options for generating the diff
+ $options = array(
+ //'ignoreWhitespace' => true,
+ //'ignoreCase' => true,
+ );
+
+ // Initialize the diff class
+ $diff = new Diff($conflictDoc, $origDoc, $options);
+
+ // Generate a side by side diff
+ $renderer = new Diff_Renderer_Html_SideBySide;
+ echo $diff->Render($renderer);
+}
+die();
+
+ }
+} catch (SetteeRestClientException $e) {
+ setteErrorHandler($e);
+}
+
+include_footer();
+?>
+
--- /dev/null
+++ b/admin/directory.gexf.php
@@ -1,1 +1,59 @@
+<?php
+$nodes = Array(Array("id" => "gov", "label" => "Federal Government"));
+$edges = Array();
+
+function addEdge($source, $target) {
+ global $edges;
+ $edges[] = Array("id" => md5($source . $target), "source" => $source, "target" => $target);
+}
+
+function addNode($id, $label, $pid) {
+ global $nodes;
+ $nodes[] = Array("id" => $id, "label" => $label , "pid" => $pid);
+}
+
+function addChildren($parentID, $parentXML) {
+ foreach ($parentXML as $childXML) {
+
+ if ($childXML->getName() == "organization" || $childXML->getName() == "organizationalUnit" || $childXML->getName() == "person") {
+ $attr = $childXML->attributes();
+ $id = $attr['UUID'];
+ if ($childXML->getName() == "organization" || $childXML->getName() == "organizationalUnit") {
+
+ $label = $childXML->name;
+ } else if ($childXML->getName() == "person") {
+ $label = $childXML->fullName;
+ }
+ addNode($id, $label, $parentID);
+ addEdge($id, $parentID);
+ addChildren($id, $childXML);
+ }
+ }
+}
+
+if (file_exists('directoryexport.xml')) {
+ $xml = simplexml_load_file('directoryexport.xml');
+
+ addChildren("gov", $xml);
+} else {
+ exit('Failed to open directoryexport.xml');
+}
+ header('Content-Type: application/gexf+xml');
+echo '<?xml version="1.0" encoding="UTF-8"?>
+<gexf xmlns="http://www.gexf.net/1.2draft" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd" version="1.2">
+ <graph mode="static" defaultedgetype="directed">
+ <nodes>';
+foreach ($nodes as $node) {
+ echo ' <node id="' . $node['id'] . '" label="' . htmlentities($node['label'],ENT_XML1) . '" ' . (isset($node['pid']) ? 'pid="' . $node['pid'] . '"' : "") . ' />';
+}
+echo '</nodes>
+ <edges>';
+foreach ($edges as $edge) {
+ echo ' <edge id="' . $edge['id'] . '" source="' . $edge['source'] . '" target="' . $edge['target'] . '" />';
+}
+echo '</edges>
+ </graph>
+</gexf>';
+?>
+
--- /dev/null
+++ b/admin/directoryexport.xml
--- a/admin/exportEmployees.csv.php
+++ b/admin/exportEmployees.csv.php
@@ -5,7 +5,6 @@
$format = "csv";
//$format = "json";
if (isset($_REQUEST['format'])) $format = $_REQUEST['format'];
-
setlocale(LC_CTYPE, 'C');
if ($format == "csv") {
$headers = Array("name");
--- a/admin/importAPSCEmployees.php
+++ b/admin/importAPSCEmployees.php
@@ -32,23 +32,35 @@
@$sums[$id][$timePeriod] += $data[1];
} else {
echo "<br>ERROR NAME MISSING FROM ID LIST<br><bR>" . PHP_EOL;
-
+
die();
-
}
}
fclose($handle);
}
}
foreach ($sums as $id => $sum) {
- echo $id. "<br>" . PHP_EOL;
+ echo $id . "<br>" . PHP_EOL;
$doc = $db->get($id);
- // print_r($doc);
- if (isset($doc->statistics)) $doc->statistics = Array();
+ echo $doc->name . "<br>" . PHP_EOL;
+ // print_r($doc);
+ $changed = false;
+ if (!isset($doc->statistics)) {
+ $changed = true;
+ $doc->statistics = Array();
+ }
foreach ($sum as $timePeriod => $value) {
- $doc->statistics["employees"][$timePeriod] = Array("value"=>$value, "source"=>"http://apsc.gov.au/stateoftheservice/");
+ if (!isset($doc->statistics->employees->$timePeriod->value)
+ || $doc->statistics->employees->$timePeriod->value != $value) {
+ $changed = true;
+ $doc->statistics["employees"][$timePeriod] = Array("value" => $value, "source" => "http://apsc.gov.au/stateoftheservice/");
+ }
}
- $db->save($doc);
+ if ($changed) {
+ $db->save($doc);
+ } else {
+ echo "not changed" . "<br>" . PHP_EOL;
+ }
}
// employees: timeperiod, source = apsc state of service, value
?>
--- /dev/null
+++ b/admin/metadata.py
@@ -1,1 +1,22 @@
+#http://packages.python.org/CouchDB/client.html
+import couchdb
+from BeautifulSoup import BeautifulSoup
+couch = couchdb.Server('http://127.0.0.1:5984/')
+
+# select database
+docsdb = couch['disclosr-documents']
+
+for row in docsdb.view('app/getMetadataExtractRequired'):
+ print row.id
+ html = docsdb.get_attachment(row.id,row.value.iterkeys().next()).read()
+ metadata = []
+ # http://www.crummy.com/software/BeautifulSoup/documentation.html
+ soup = BeautifulSoup(html)
+metatags = soup.meta
+ for metatag in metatags:
+ print metatag['name']
+ doc = docsdb.get(row.id)
+ //doc['metadata'] = metadata
+ //docsdb.save(doc)
+
Binary files /dev/null and b/admin/naa-agift.7z differ
--- /dev/null
+++ b/admin/naa-agift.json
@@ -1,1 +1,2232 @@
-
+[
+ "Home",
+ "index.htm",
+ [
+ "BUSINESS SUPPORT AND REGULATION",
+ "000411.htm",
+ [
+ "Association registration",
+ "000396.htm"
+ ],
+ [
+ "Business process auditing",
+ "000413.htm"
+ ],
+ [
+ "Business registration and licensing",
+ "000414.htm"
+ ],
+ [
+ "Business sponsorship",
+ "001371.htm"
+ ],
+ [
+ "Consumer protection",
+ "000439.htm"
+ ],
+ [
+ "Fair trading compliance",
+ "000482.htm",
+ [
+ "Prices surveillance",
+ "000533.htm"
+ ],
+ [
+ "Product safety",
+ "000534.htm"
+ ],
+ [
+ "Trade practices compliance",
+ "000583.htm"
+ ]
+ ],
+ [
+ "Financial institutions regulation",
+ "000485.htm"
+ ],
+ [
+ "Government procurement regulation",
+ "000491.htm"
+ ],
+ [
+ "Industry assistance schemes",
+ "000499.htm"
+ ],
+ [
+ "Industry development",
+ "000500.htm"
+ ],
+ [
+ "Insurance regulation",
+ "000501.htm",
+ [
+ "General insurance",
+ "000489.htm"
+ ],
+ [
+ "Life insurance",
+ "000505.htm"
+ ]
+ ],
+ [
+ "Professional accreditation",
+ "000535.htm"
+ ],
+ [
+ "Small business services",
+ "000565.htm",
+ [
+ "Small business advocacy",
+ "000563.htm"
+ ],
+ [
+ "Small business development",
+ "000564.htm"
+ ]
+ ],
+ [
+ "Stock market regulation",
+ "000572.htm"
+ ]
+ ],
+ [
+ "CIVIC INFRASTRUCTURE",
+ "000424.htm",
+ [
+ "Civic management",
+ "000425.htm",
+ [
+ "Architectural services",
+ "000387.htm"
+ ],
+ [
+ "Building approval services",
+ "000406.htm"
+ ],
+ [
+ "Building regulations and standards",
+ "000408.htm"
+ ],
+ [
+ "Engineering services",
+ "000477.htm"
+ ],
+ [
+ "Town planning",
+ "000582.htm"
+ ]
+ ],
+ [
+ "Energy supply",
+ "000476.htm"
+ ],
+ [
+ "Integrated services planning",
+ "000503.htm"
+ ],
+ [
+ "Public housing",
+ "000536.htm",
+ [
+ "Public housing construction",
+ "000537.htm"
+ ],
+ [
+ "Public housing design",
+ "000538.htm"
+ ],
+ [
+ "Public housing maintenance",
+ "000540.htm"
+ ]
+ ],
+ [
+ "Public land management",
+ "000541.htm",
+ [
+ "Burial ground management",
+ "000410.htm"
+ ],
+ [
+ "Cultural centre management",
+ "000444.htm"
+ ],
+ [
+ "Garden management",
+ "000488.htm"
+ ],
+ [
+ "Memorial maintenance",
+ "000509.htm"
+ ],
+ [
+ "Recreational park management",
+ "000549.htm"
+ ],
+ [
+ "Sporting facilities management",
+ "000571.htm"
+ ]
+ ],
+ [
+ "Regional development",
+ "000552.htm"
+ ],
+ [
+ "Transport network maintenance",
+ "000586.htm"
+ ],
+ [
+ "Waste management",
+ "001349.htm"
+ ]
+ ],
+ [
+ "COMMUNICATIONS",
+ "000433.htm",
+ [
+ "Advertising standards",
+ "000381.htm"
+ ],
+ [
+ "Broadcasting",
+ "000403.htm",
+ [
+ "Broadcasting standards",
+ "000404.htm"
+ ],
+ [
+ "Radio broadcasting",
+ "000546.htm"
+ ],
+ [
+ "Television broadcasting",
+ "000580.htm"
+ ]
+ ],
+ [
+ "Call centre administration",
+ "000417.htm"
+ ],
+ [
+ "Electronic commerce",
+ "000468.htm",
+ [
+ "Authentication",
+ "000399.htm"
+ ],
+ [
+ "Online transaction standards",
+ "000526.htm"
+ ]
+ ],
+ [
+ "Government media",
+ "000490.htm"
+ ],
+ [
+ "Information management standards",
+ "000283.htm",
+ [
+ "Data management",
+ "000448.htm"
+ ],
+ [
+ "Information dissemination",
+ "000502.htm"
+ ],
+ [
+ "Information technology standards",
+ "000282.htm"
+ ]
+ ],
+ [
+ "Media ownership regulation",
+ "000508.htm"
+ ],
+ [
+ "Postal services",
+ "000530.htm",
+ [
+ "Courier services",
+ "000441.htm"
+ ],
+ [
+ "Electronic postal services",
+ "000469.htm"
+ ],
+ [
+ "Retail postal services",
+ "000554.htm"
+ ]
+ ],
+ [
+ "Publishing",
+ "000543.htm",
+ [
+ "Electronic publishing",
+ "000470.htm"
+ ],
+ [
+ "Publishing standards",
+ "000544.htm"
+ ],
+ [
+ "Website development",
+ "000591.htm"
+ ]
+ ],
+ [
+ "Radio communication",
+ "000547.htm",
+ [
+ "Apparatus licensing",
+ "000385.htm"
+ ],
+ [
+ "Spectrum management",
+ "000570.htm"
+ ]
+ ],
+ [
+ "Satellite communication",
+ "000560.htm"
+ ],
+ [
+ "Telecommunications",
+ "000578.htm",
+ [
+ "Carriage service providers",
+ "000420.htm"
+ ],
+ [
+ "Carrier licensing",
+ "000421.htm"
+ ],
+ [
+ "Equipment licensing",
+ "000480.htm"
+ ],
+ [
+ "Mobile telephone services",
+ "000516.htm"
+ ],
+ [
+ "Telephone services",
+ "000579.htm"
+ ]
+ ]
+ ],
+ [
+ "COMMUNITY SERVICES",
+ "000435.htm",
+ [
+ "Accommodation services",
+ "000377.htm",
+ [
+ "Defence housing",
+ "000458.htm"
+ ],
+ [
+ "Emergency accommodation",
+ "000471.htm"
+ ],
+ [
+ "Public housing entitlements",
+ "000539.htm"
+ ],
+ [
+ "Refuge support",
+ "000551.htm"
+ ]
+ ],
+ [
+ "Community support",
+ "000436.htm",
+ [
+ "Adoption services",
+ "000378.htm"
+ ],
+ [
+ "Aged care services",
+ "000382.htm"
+ ],
+ [
+ "Child and youth support",
+ "000422.htm"
+ ],
+ [
+ "Child-care services",
+ "000423.htm"
+ ],
+ [
+ "Defence community programs",
+ "000452.htm"
+ ],
+ [
+ "Family reunion programs",
+ "000483.htm"
+ ],
+ [
+ "Veterans\" entitlements",
+ "000587.htm"
+ ]
+ ],
+ [
+ "Counselling services",
+ "000440.htm"
+ ],
+ [
+ "Emergency services",
+ "000474.htm",
+ [
+ "Ambulance services",
+ "000384.htm"
+ ],
+ [
+ "Emergency funding",
+ "000472.htm"
+ ],
+ [
+ "Firefighting services",
+ "000487.htm"
+ ]
+ ],
+ [
+ "Financial assistance",
+ "000484.htm",
+ [
+ "Benefits",
+ "000402.htm"
+ ],
+ [
+ "Income support schemes",
+ "000494.htm"
+ ]
+ ],
+ [
+ "Natural disasters",
+ "000521.htm",
+ [
+ "Disaster recovery",
+ "000462.htm"
+ ],
+ [
+ "Disaster relief",
+ "000463.htm"
+ ]
+ ],
+ [
+ "Rural community development",
+ "000557.htm"
+ ],
+ [
+ "Social justice and equity",
+ "000566.htm"
+ ],
+ [
+ "Transport access schemes",
+ "000901.htm"
+ ]
+ ],
+ [
+ "CULTURAL AFFAIRS",
+ "000442.htm",
+ [
+ "Arts development",
+ "000391.htm",
+ [
+ "Arts funding",
+ "000393.htm"
+ ],
+ [
+ "Arts incentive schemes",
+ "000394.htm"
+ ],
+ [
+ "Arts promotion",
+ "000395.htm"
+ ]
+ ],
+ [
+ "Collection access",
+ "000427.htm",
+ [
+ "Descriptive standards",
+ "000461.htm"
+ ],
+ [
+ "Finding aids development",
+ "000486.htm"
+ ],
+ [
+ "Reference services",
+ "000550.htm"
+ ]
+ ],
+ [
+ "Collection management",
+ "000430.htm",
+ [
+ "Artefact export regulation",
+ "000390.htm"
+ ],
+ [
+ "Collection accessioning",
+ "000428.htm"
+ ],
+ [
+ "Collection acquisition",
+ "000429.htm"
+ ],
+ [
+ "Collection storage",
+ "000432.htm"
+ ],
+ [
+ "Preservation services",
+ "000532.htm"
+ ]
+ ],
+ [
+ "Collection promotion",
+ "000431.htm",
+ [
+ "Exhibition programs",
+ "000481.htm"
+ ],
+ [
+ "Publicity programs",
+ "000542.htm"
+ ]
+ ],
+ [
+ "Cultural awards and scholarships",
+ "000443.htm"
+ ],
+ [
+ "Cultural festivals",
+ "000445.htm"
+ ],
+ [
+ "Cultural gifts programs",
+ "000446.htm"
+ ],
+ [
+ "Multicultural heritage promotion",
+ "000518.htm",
+ [
+ "Multicultural festivals",
+ "000517.htm"
+ ],
+ [
+ "Multicultural services",
+ "000519.htm"
+ ]
+ ]
+ ],
+ [
+ "DEFENCE",
+ "000449.htm",
+ [
+ "Australian Defence Forces",
+ "000397.htm",
+ [
+ "Air Force",
+ "002266.htm"
+ ],
+ [
+ "Army",
+ "000388.htm"
+ ],
+ [
+ "Badges and insignia",
+ "000401.htm"
+ ],
+ [
+ "Cadets",
+ "000416.htm"
+ ],
+ [
+ "Defence force commands",
+ "000456.htm"
+ ],
+ [
+ "Military bands",
+ "000510.htm"
+ ],
+ [
+ "Navy",
+ "000524.htm"
+ ],
+ [
+ "Reserves",
+ "000553.htm"
+ ],
+ [
+ "Roulettes",
+ "000556.htm"
+ ]
+ ],
+ [
+ "Defence estate management",
+ "000454.htm"
+ ],
+ [
+ "Defence force careers",
+ "000455.htm",
+ [
+ "Defence career development",
+ "000450.htm"
+ ],
+ [
+ "Military employment services",
+ "000512.htm"
+ ]
+ ],
+ [
+ "Defence strategic development",
+ "000574.htm",
+ [
+ "Defence efficiency review",
+ "000453.htm"
+ ],
+ [
+ "Defence reform program",
+ "000460.htm"
+ ]
+ ],
+ [
+ "Defence strategic policy",
+ "000576.htm",
+ [
+ "Strategic decisions",
+ "000573.htm"
+ ],
+ [
+ "Strategic planning",
+ "000575.htm"
+ ]
+ ],
+ [
+ "Defence strategic support",
+ "000577.htm",
+ [
+ "Capital equipment programs",
+ "000419.htm"
+ ],
+ [
+ "Defence industry",
+ "000459.htm"
+ ],
+ [
+ "Defence science and technology",
+ "000905.htm"
+ ],
+ [
+ "Logistics",
+ "000506.htm"
+ ]
+ ],
+ [
+ "Emergency management",
+ "000473.htm",
+ [
+ "Civil community assistance",
+ "000426.htm"
+ ],
+ [
+ "Disaster support",
+ "000464.htm"
+ ]
+ ],
+ [
+ "Military law",
+ "001814.htm"
+ ],
+ [
+ "Military operations",
+ "000515.htm",
+ [
+ "Australian theatre of war",
+ "000398.htm"
+ ],
+ [
+ "Military exercises",
+ "000514.htm"
+ ],
+ [
+ "Special operations",
+ "000569.htm"
+ ],
+ [
+ "Warfare",
+ "000589.htm"
+ ]
+ ]
+ ],
+ [
+ "EDUCATION AND TRAINING",
+ "000467.htm",
+ [
+ "Arts education",
+ "000392.htm"
+ ],
+ [
+ "Community education",
+ "000434.htm",
+ [
+ "Adult education programs",
+ "000379.htm"
+ ],
+ [
+ "Adult migrant education",
+ "000380.htm"
+ ],
+ [
+ "Road safety awareness",
+ "000555.htm"
+ ]
+ ],
+ [
+ "Curriculum development",
+ "000447.htm"
+ ],
+ [
+ "Early childhood education",
+ "000466.htm"
+ ],
+ [
+ "Military education and training",
+ "000511.htm",
+ [
+ "Army reserve training",
+ "000389.htm"
+ ],
+ [
+ "Defence college training",
+ "000451.htm"
+ ]
+ ],
+ [
+ "Overseas skills recognition",
+ "000527.htm"
+ ],
+ [
+ "School education",
+ "000561.htm",
+ [
+ "Discipline programs",
+ "000465.htm"
+ ],
+ [
+ "School transport regulation",
+ "000562.htm"
+ ],
+ [
+ "Special needs programs",
+ "000568.htm"
+ ]
+ ],
+ [
+ "Tertiary education",
+ "000581.htm"
+ ],
+ [
+ "Vocational education",
+ "000588.htm",
+ [
+ "Apprenticeship programs",
+ "000386.htm"
+ ],
+ [
+ "Hospital teaching programs",
+ "000493.htm"
+ ],
+ [
+ "Trainee programs",
+ "000584.htm"
+ ],
+ [
+ "Workplace training",
+ "000595.htm"
+ ]
+ ]
+ ],
+ [
+ "EMPLOYMENT",
+ "000016.htm",
+ [
+ "Human resources development",
+ "000348.htm",
+ [
+ "Occupational health and safety",
+ "000355.htm"
+ ],
+ [
+ "Recruitment programs",
+ "000356.htm"
+ ],
+ [
+ "Termination processes",
+ "002103.htm"
+ ],
+ [
+ "Workers compensation schemes",
+ "000363.htm"
+ ],
+ [
+ "Workplace discrimination monitoring",
+ "000364.htm"
+ ],
+ [
+ "Workplace equity and justice programs",
+ "000365.htm"
+ ]
+ ],
+ [
+ "Industrial awards and conditions",
+ "000349.htm",
+ [
+ "Award conditions",
+ "000366.htm"
+ ],
+ [
+ "Remuneration review",
+ "000368.htm"
+ ]
+ ],
+ [
+ "Labour market programs",
+ "000347.htm",
+ [
+ "Employment services marketing",
+ "000351.htm"
+ ],
+ [
+ "Job placement programs",
+ "000352.htm"
+ ],
+ [
+ "Job vacancy data management",
+ "000353.htm"
+ ]
+ ],
+ [
+ "Workplace agreement processes",
+ "000350.htm",
+ [
+ "Agreement bargaining processes",
+ "002141.htm"
+ ],
+ [
+ "Agreement dispute mediation",
+ "000370.htm"
+ ],
+ [
+ "Certified employment conditions",
+ "000371.htm"
+ ],
+ [
+ "Employment advocacy services",
+ "000374.htm"
+ ]
+ ]
+ ],
+ [
+ "ENVIRONMENT",
+ "000478.htm",
+ [
+ "Built environment",
+ "000409.htm",
+ [
+ "Building acoustics",
+ "000405.htm"
+ ],
+ [
+ "Building preservation",
+ "000407.htm"
+ ]
+ ],
+ [
+ "Climate information services",
+ "000203.htm"
+ ],
+ [
+ "Conservation programs",
+ "000438.htm",
+ [
+ "Endangered species protection",
+ "000475.htm"
+ ],
+ [
+ "Landcare programs",
+ "000504.htm"
+ ],
+ [
+ "Soil preservation programs",
+ "000567.htm"
+ ]
+ ],
+ [
+ "Environmental impact assessment",
+ "000479.htm"
+ ],
+ [
+ "Historic relic protection",
+ "000492.htm"
+ ],
+ [
+ "Marine life protection programs",
+ "000507.htm"
+ ],
+ [
+ "Natural heritage protection",
+ "000522.htm",
+ [
+ "National park management",
+ "000520.htm"
+ ],
+ [
+ "Nature reserve management",
+ "000523.htm"
+ ],
+ [
+ "Wilderness area management",
+ "000593.htm"
+ ],
+ [
+ "Wildlife sanctuary management",
+ "000594.htm"
+ ]
+ ],
+ [
+ "Oceans governance",
+ "000525.htm"
+ ],
+ [
+ "Pollutant prevention programs",
+ "000529.htm"
+ ],
+ [
+ "World heritage listings",
+ "000596.htm"
+ ]
+ ],
+ [
+ "FINANCE MANAGEMENT",
+ "000001.htm",
+ [
+ "Commonwealth State funding",
+ "000017.htm"
+ ],
+ [
+ "Currency",
+ "000018.htm",
+ [
+ "Counterfeiting control",
+ "000027.htm"
+ ]
+ ],
+ [
+ "Financial investment",
+ "000021.htm",
+ [
+ "Investment scheme regulation",
+ "000031.htm"
+ ]
+ ],
+ [
+ "Financial system management",
+ "002302.htm",
+ [
+ "Financial system monitoring",
+ "002306.htm"
+ ],
+ [
+ "Payments management",
+ "002297.htm"
+ ]
+ ],
+ [
+ "Fiscal policy",
+ "000022.htm",
+ [
+ "Public borrowing",
+ "000032.htm"
+ ],
+ [
+ "Public debt",
+ "000033.htm"
+ ],
+ [
+ "Public expenditure",
+ "000034.htm"
+ ]
+ ],
+ [
+ "Foreign investment control",
+ "000030.htm"
+ ],
+ [
+ "International monetary regulation",
+ "000023.htm"
+ ],
+ [
+ "Monetary policy",
+ "000020.htm",
+ [
+ "Exchange rates",
+ "000028.htm"
+ ],
+ [
+ "Inflation",
+ "000029.htm"
+ ],
+ [
+ "Interest rates",
+ "002295.htm"
+ ]
+ ],
+ [
+ "Resource management",
+ "000024.htm",
+ [
+ "Contract management",
+ "000037.htm"
+ ],
+ [
+ "Financial administration",
+ "000035.htm"
+ ],
+ [
+ "Financial budgeting",
+ "000036.htm"
+ ]
+ ],
+ [
+ "Retirement income",
+ "000025.htm",
+ [
+ "Asset assessment",
+ "000038.htm"
+ ],
+ [
+ "Retirement saving",
+ "000039.htm"
+ ],
+ [
+ "Superannuation regulation",
+ "000040.htm"
+ ]
+ ],
+ [
+ "Statistical analysis",
+ "000019.htm"
+ ],
+ [
+ "Taxation",
+ "000026.htm",
+ [
+ "Income assessment",
+ "000041.htm"
+ ],
+ [
+ "Revenue raising",
+ "000042.htm"
+ ],
+ [
+ "Taxation compliance",
+ "000043.htm"
+ ]
+ ]
+ ],
+ [
+ "GOVERNANCE",
+ "000002.htm",
+ [
+ "Civic celebrations",
+ "000046.htm"
+ ],
+ [
+ "Constitutional matters",
+ "000048.htm",
+ [
+ "Constitutional conventions",
+ "000061.htm"
+ ],
+ [
+ "Constitutional referenda",
+ "000062.htm"
+ ]
+ ],
+ [
+ "Electoral matters",
+ "000049.htm",
+ [
+ "Declaration of interests",
+ "000063.htm"
+ ],
+ [
+ "Election campaigning",
+ "000064.htm"
+ ],
+ [
+ "Electoral boundary assessment",
+ "000065.htm"
+ ]
+ ],
+ [
+ "Honours and awards programs",
+ "000050.htm"
+ ],
+ [
+ "Intergovernmental relations",
+ "000052.htm",
+ [
+ "Cross-border cooperation",
+ "000067.htm"
+ ],
+ [
+ "Intergovernment policy dissemination",
+ "000069.htm"
+ ]
+ ],
+ [
+ "Legislative drafting",
+ "000053.htm",
+ [
+ "Draft Bill amendment process",
+ "000070.htm"
+ ],
+ [
+ "Legislation review",
+ "000071.htm"
+ ],
+ [
+ "Preparation of legislative regulations",
+ "000072.htm"
+ ]
+ ],
+ [
+ "Official protocol",
+ "000054.htm",
+ [
+ "Ceremonial events and representation",
+ "000073.htm"
+ ],
+ [
+ "Head of Government protocol",
+ "000074.htm"
+ ],
+ [
+ "Official establishment management",
+ "000075.htm"
+ ],
+ [
+ "Official hospitality",
+ "000076.htm"
+ ],
+ [
+ "Official visits",
+ "000077.htm"
+ ]
+ ],
+ [
+ "Parliamentary chamber support",
+ "000045.htm",
+ [
+ "Hansard services",
+ "000058.htm"
+ ],
+ [
+ "Parliamentary papers",
+ "000059.htm"
+ ],
+ [
+ "Tabling of official documents",
+ "000060.htm"
+ ]
+ ],
+ [
+ "Parliamentary committee and member support",
+ "000047.htm"
+ ],
+ [
+ "Population census",
+ "000044.htm",
+ [
+ "Demographic surveys",
+ "000055.htm"
+ ],
+ [
+ "Household census",
+ "000056.htm"
+ ],
+ [
+ "Population distribution analysis",
+ "000057.htm"
+ ]
+ ],
+ [
+ "Public administration",
+ "002290.htm",
+ [
+ "Government auditing",
+ "002291.htm"
+ ],
+ [
+ "Public service",
+ "002292.htm"
+ ],
+ [
+ "Territories administration",
+ "002293.htm"
+ ]
+ ],
+ [
+ "Statistics",
+ "002289.htm"
+ ]
+ ],
+ [
+ "HEALTH CARE",
+ "000003.htm",
+ [
+ "Community health services",
+ "000078.htm",
+ [
+ "Clinical health services",
+ "000087.htm"
+ ],
+ [
+ "Health promotion",
+ "000089.htm"
+ ],
+ [
+ "Primary health care",
+ "000090.htm"
+ ]
+ ],
+ [
+ "Health insurance schemes",
+ "000079.htm"
+ ],
+ [
+ "Health protocol administration",
+ "000080.htm",
+ [
+ "Drugs and poisons regulation",
+ "000091.htm"
+ ],
+ [
+ "Food hygiene regulation",
+ "000092.htm"
+ ],
+ [
+ "Practitioner discipline",
+ "000093.htm"
+ ],
+ [
+ "Practitioner registration",
+ "000095.htm"
+ ]
+ ],
+ [
+ "Hospital services",
+ "000081.htm",
+ [
+ "Hospital administration",
+ "000096.htm"
+ ],
+ [
+ "Patient care",
+ "000097.htm"
+ ],
+ [
+ "Pharmaceutical and drug support",
+ "000098.htm"
+ ]
+ ],
+ [
+ "Medical research",
+ "000083.htm",
+ [
+ "Ethical compliance",
+ "000099.htm"
+ ],
+ [
+ "Medical aids regulation",
+ "000100.htm"
+ ],
+ [
+ "Medical research funding",
+ "000101.htm"
+ ],
+ [
+ "National referral laboratory services",
+ "000102.htm"
+ ],
+ [
+ "Population-based research",
+ "000103.htm"
+ ]
+ ],
+ [
+ "Public health services",
+ "000084.htm",
+ [
+ "Epidemiology",
+ "000104.htm"
+ ],
+ [
+ "Health disaster preparedness",
+ "000106.htm"
+ ],
+ [
+ "Health hazard prevention",
+ "000105.htm"
+ ],
+ [
+ "Medical quarantine services",
+ "000108.htm"
+ ],
+ [
+ "Notifiable disease management",
+ "000109.htm"
+ ]
+ ],
+ [
+ "Special needs services",
+ "000085.htm",
+ [
+ "Dental health services",
+ "000111.htm"
+ ],
+ [
+ "Independent living services",
+ "000112.htm"
+ ],
+ [
+ "Mental health services",
+ "000113.htm"
+ ],
+ [
+ "Residential services",
+ "000114.htm"
+ ]
+ ],
+ [
+ "Veterans\" health services",
+ "000086.htm"
+ ]
+ ],
+ [
+ "IMMIGRATION",
+ "000004.htm",
+ [
+ "Citizenship",
+ "000115.htm",
+ [
+ "Naturalisation assessment",
+ "000121.htm"
+ ],
+ [
+ "Presentation arrangements",
+ "000122.htm"
+ ]
+ ],
+ [
+ "Deportation",
+ "000116.htm"
+ ],
+ [
+ "Detention programs",
+ "000117.htm"
+ ],
+ [
+ "Migrant services",
+ "000118.htm",
+ [
+ "Interpreter services",
+ "000123.htm"
+ ],
+ [
+ "Migrant accommodation services",
+ "000125.htm"
+ ],
+ [
+ "Migrant settlement programs",
+ "000126.htm"
+ ]
+ ],
+ [
+ "Refugee services",
+ "000119.htm"
+ ],
+ [
+ "Travel authorisation",
+ "000120.htm",
+ [
+ "Permanent entry arrangements",
+ "000127.htm"
+ ],
+ [
+ "Temporary entry arrangements",
+ "000128.htm"
+ ]
+ ]
+ ],
+ [
+ "INDIGENOUS AFFAIRS",
+ "002285.htm",
+ [
+ "Indigenous cultural heritage",
+ "000495.htm",
+ [
+ "Cultural artefact repatriation",
+ "001772.htm"
+ ]
+ ],
+ [
+ "Indigenous enterprise development",
+ "000496.htm"
+ ],
+ [
+ "Indigenous health services",
+ "000082.htm"
+ ],
+ [
+ "Indigenous heritage conservation",
+ "000497.htm"
+ ],
+ [
+ "Indigenous reconciliation",
+ "000051.htm"
+ ],
+ [
+ "Indigenous welfare programs",
+ "000498.htm"
+ ]
+ ],
+ [
+ "INTERNATIONAL RELATIONS",
+ "000005.htm",
+ [
+ "Consular services",
+ "000129.htm"
+ ],
+ [
+ "Defence liaison",
+ "000132.htm",
+ [
+ "000140.htm"
+ ],
+ [
+ "Defence cooperation programs",
+ "000141.htm"
+ ],
+ [
+ "Defence personnel exchanges",
+ "000142.htm"
+ ],
+ [
+ "Military advisors",
+ "000143.htm"
+ ]
+ ],
+ [
+ "Diplomatic missions",
+ "000130.htm"
+ ],
+ [
+ "International affairs",
+ "000131.htm",
+ [
+ "Disarmament and arms control",
+ "000137.htm"
+ ],
+ [
+ "Nuclear safeguards",
+ "000139.htm"
+ ]
+ ],
+ [
+ "International treaties",
+ "000133.htm",
+ [
+ "Treaty administration",
+ "001384.htm"
+ ]
+ ],
+ [
+ "Overseas aid programs",
+ "000134.htm",
+ [
+ "Development assistance programs",
+ "000146.htm"
+ ],
+ [
+ "Overseas student scholarship programs",
+ "000147.htm"
+ ]
+ ],
+ [
+ "Overseas promotion",
+ "000135.htm"
+ ],
+ [
+ "Passport services",
+ "000136.htm"
+ ]
+ ],
+ [
+ "JUSTICE ADMINISTRATION",
+ "000006.htm",
+ [
+ "Administrative law",
+ "000149.htm",
+ [
+ "Administrative decision appeal",
+ "000163.htm"
+ ],
+ [
+ "Administrative decision review",
+ "000164.htm"
+ ],
+ [
+ "Censorship standards",
+ "000165.htm"
+ ],
+ [
+ "Copyright regulation",
+ "000166.htm"
+ ],
+ [
+ "Recordkeeping standards",
+ "000167.htm"
+ ]
+ ],
+ [
+ "Associations and corporate law",
+ "000150.htm",
+ [
+ "Bankruptcy proceedings",
+ "000168.htm"
+ ],
+ [
+ "Merger regulation",
+ "000169.htm"
+ ]
+ ],
+ [
+ "Civil law",
+ "000151.htm",
+ [
+ "Civil registration",
+ "000170.htm"
+ ],
+ [
+ "Conveyancing",
+ "000171.htm"
+ ],
+ [
+ "Dispute mediation programs",
+ "000174.htm"
+ ],
+ [
+ "Family law proceedings",
+ "000172.htm"
+ ],
+ [
+ "Probate applications",
+ "000173.htm"
+ ]
+ ],
+ [
+ "Commissions of inquiry",
+ "000152.htm"
+ ],
+ [
+ "Coronial law",
+ "000153.htm"
+ ],
+ [
+ "Court reporting",
+ "000154.htm"
+ ],
+ [
+ "Criminal law",
+ "000155.htm"
+ ],
+ [
+ "Human rights obligations",
+ "000156.htm"
+ ],
+ [
+ "Juvenile justice",
+ "000157.htm"
+ ],
+ [
+ "Legal aid services",
+ "000158.htm"
+ ],
+ [
+ "Local laws and ordinances",
+ "000159.htm"
+ ],
+ [
+ "Native title claims",
+ "000160.htm",
+ [
+ "Native title mediation programs",
+ "000180.htm"
+ ],
+ [
+ "Native title settlement negotiations",
+ "000181.htm"
+ ]
+ ],
+ [
+ "Privacy guideline monitoring",
+ "000161.htm"
+ ],
+ [
+ "Prosecution services",
+ "000162.htm"
+ ]
+ ],
+ [
+ "MARITIME SERVICES",
+ "000007.htm",
+ [
+ "Cargo control",
+ "000198.htm"
+ ],
+ [
+ "Harbour management",
+ "000183.htm"
+ ],
+ [
+ "Marina management",
+ "000184.htm"
+ ],
+ [
+ "Navigation",
+ "000185.htm",
+ [
+ "Charting sea lanes",
+ "000191.htm"
+ ],
+ [
+ "Light station maintenance",
+ "000192.htm"
+ ],
+ [
+ "Navigation systems development",
+ "000193.htm"
+ ]
+ ],
+ [
+ "Pilotage management",
+ "000182.htm"
+ ],
+ [
+ "Port regulation",
+ "001471.htm"
+ ],
+ [
+ "Sea passengers",
+ "001487.htm"
+ ],
+ [
+ "Search and rescue",
+ "000187.htm",
+ [
+ "Rescue coordination",
+ "000194.htm"
+ ],
+ [
+ "Search technology development",
+ "000195.htm"
+ ]
+ ],
+ [
+ "Ship personnel",
+ "000188.htm",
+ [
+ "Personnel safety standards",
+ "000196.htm"
+ ],
+ [
+ "Pilot licensing",
+ "000197.htm"
+ ]
+ ],
+ [
+ "Ship safety",
+ "000189.htm",
+ [
+ "Seaworthiness",
+ "000201.htm"
+ ],
+ [
+ "Ship commissioning",
+ "000199.htm"
+ ],
+ [
+ "Ship inspection",
+ "000200.htm"
+ ]
+ ]
+ ],
+ [
+ "NATURAL RESOURCES",
+ "000009.htm",
+ [
+ "Crown land administration",
+ "000218.htm"
+ ],
+ [
+ "Energy resources",
+ "001451.htm",
+ [
+ "Fossil fuel management",
+ "000226.htm"
+ ],
+ [
+ "Renewable energy development",
+ "000227.htm"
+ ]
+ ],
+ [
+ "Land use planning",
+ "000220.htm",
+ [
+ "Land use zoning",
+ "001429.htm"
+ ]
+ ],
+ [
+ "Land valuation",
+ "000222.htm"
+ ],
+ [
+ "Mineral resources",
+ "000223.htm",
+ [
+ "Land rehabilitation programs",
+ "000231.htm"
+ ],
+ [
+ "Mining regulations",
+ "000232.htm"
+ ]
+ ],
+ [
+ "Pollution emission control",
+ "000224.htm"
+ ],
+ [
+ "Water resources",
+ "000225.htm",
+ [
+ "Water conservation plans",
+ "000233.htm"
+ ],
+ [
+ "Water quality monitoring",
+ "000234.htm"
+ ],
+ [
+ "Water usage management",
+ "000235.htm"
+ ],
+ [
+ "Waterway management",
+ "000236.htm"
+ ]
+ ]
+ ],
+ [
+ "PRIMARY INDUSTRIES",
+ "000008.htm",
+ [
+ "Chemical and pesticide regulation",
+ "000202.htm"
+ ],
+ [
+ "Marine and rural regulation",
+ "000204.htm",
+ [
+ "Agricultural industry",
+ "000208.htm"
+ ],
+ [
+ "Fisheries industry",
+ "000209.htm"
+ ],
+ [
+ "Food quality assurance",
+ "000213.htm"
+ ],
+ [
+ "Forestry industry",
+ "000210.htm"
+ ]
+ ],
+ [
+ "Marine and rural support",
+ "000205.htm",
+ [
+ "Aquaculture industry",
+ "000212.htm"
+ ],
+ [
+ "Horticultural industry",
+ "000214.htm"
+ ],
+ [
+ "Pastoral industry",
+ "000215.htm"
+ ],
+ [
+ "Silvicultural industry",
+ "000216.htm"
+ ],
+ [
+ "Viticultural industry",
+ "000217.htm"
+ ]
+ ],
+ [
+ "Quarantine",
+ "000211.htm"
+ ],
+ [
+ "Rural field day promotion",
+ "000206.htm"
+ ],
+ [
+ "Rural partnership programs",
+ "000207.htm"
+ ]
+ ],
+ [
+ "SCIENCE",
+ "000010.htm",
+ [
+ "Agricultural sciences",
+ "000237.htm"
+ ],
+ [
+ "Animal and veterinary sciences",
+ "000238.htm"
+ ],
+ [
+ "Applied sciences",
+ "000239.htm",
+ [
+ "Engineering and technology sciences",
+ "000246.htm"
+ ],
+ [
+ "Weights and measures standards",
+ "000247.htm"
+ ]
+ ],
+ [
+ "Biological sciences",
+ "000240.htm",
+ [
+ "Biochemistry",
+ "000248.htm"
+ ],
+ [
+ "Biodiversity preservation",
+ "000249.htm"
+ ],
+ [
+ "Biotechnology development",
+ "000250.htm"
+ ],
+ [
+ "Botany",
+ "000251.htm"
+ ],
+ [
+ "Entomology",
+ "000252.htm"
+ ],
+ [
+ "Genetic engineering",
+ "000253.htm"
+ ],
+ [
+ "Physiology",
+ "000254.htm"
+ ]
+ ],
+ [
+ "Earth sciences",
+ "000241.htm",
+ [
+ "Atmospheric sciences",
+ "000255.htm"
+ ],
+ [
+ "Hydrology",
+ "000256.htm"
+ ],
+ [
+ "Mineral exploration",
+ "000257.htm"
+ ],
+ [
+ "Oceanography",
+ "000258.htm"
+ ],
+ [
+ "Seismology",
+ "000259.htm"
+ ]
+ ],
+ [
+ "Mathematical sciences",
+ "000242.htm"
+ ],
+ [
+ "Medical and health sciences",
+ "001229.htm"
+ ],
+ [
+ "Physical sciences",
+ "000244.htm",
+ [
+ "Astronomical sciences",
+ "000260.htm"
+ ],
+ [
+ "Atomic and molecular sciences",
+ "000261.htm"
+ ],
+ [
+ "Nuclear sciences",
+ "000262.htm"
+ ]
+ ],
+ [
+ "Spatial information research",
+ "000245.htm"
+ ]
+ ],
+ [
+ "SECURITY",
+ "000011.htm",
+ [
+ "Corrective services",
+ "000263.htm",
+ [
+ "Community based corrections",
+ "000270.htm"
+ ],
+ [
+ "Detention centre management",
+ "000271.htm"
+ ]
+ ],
+ [
+ "External security",
+ "000264.htm",
+ [
+ "Peacekeeping forces",
+ "000273.htm"
+ ]
+ ],
+ [
+ "Information security",
+ "000265.htm"
+ ],
+ [
+ "Intelligence",
+ "000266.htm",
+ [
+ "Forensic analysis",
+ "000275.htm"
+ ],
+ [
+ "Intelligence liaison",
+ "001159.htm"
+ ],
+ [
+ "Intelligence support",
+ "000277.htm"
+ ]
+ ],
+ [
+ "Law enforcement",
+ "000267.htm",
+ [
+ "Community policing",
+ "000278.htm"
+ ],
+ [
+ "Criminology",
+ "000279.htm"
+ ],
+ [
+ "Police administration",
+ "000280.htm"
+ ],
+ [
+ "Protective services",
+ "000281.htm"
+ ]
+ ],
+ [
+ "National security",
+ "001548.htm",
+ [
+ "Coastal surveillance",
+ "000269.htm"
+ ]
+ ]
+ ],
+ [
+ "SPORT AND RECREATION",
+ "000012.htm",
+ [
+ "Caravan and camping services",
+ "000284.htm"
+ ],
+ [
+ "Community recreational programs",
+ "000285.htm",
+ [
+ "Gambling support services",
+ "000290.htm"
+ ],
+ [
+ "Public fitness promotion",
+ "000291.htm"
+ ],
+ [
+ "Social club support",
+ "000292.htm"
+ ],
+ [
+ "Volunteer support programs",
+ "000293.htm"
+ ]
+ ],
+ [
+ "Gaming industry regulation",
+ "000286.htm"
+ ],
+ [
+ "Park and reserve services",
+ "000289.htm"
+ ],
+ [
+ "Sport and fitness development",
+ "000287.htm",
+ [
+ "Athlete scholarship programs",
+ "000295.htm"
+ ],
+ [
+ "Fundraising and donation schemes",
+ "000296.htm"
+ ],
+ [
+ "Games administration",
+ "000297.htm"
+ ],
+ [
+ "Games promotion",
+ "000298.htm"
+ ],
+ [
+ "Sporting grants administration",
+ "000299.htm"
+ ]
+ ],
+ [
+ "Sports drugs monitoring",
+ "000288.htm",
+ [
+ "Doping detection research",
+ "000300.htm"
+ ]
+ ]
+ ],
+ [
+ "TOURISM",
+ "000013.htm",
+ [
+ "Tourism industry development",
+ "000301.htm"
+ ],
+ [
+ "Tourist event promotion",
+ "000302.htm",
+ [
+ "Advertising campaigns",
+ "000305.htm"
+ ]
+ ],
+ [
+ "Travel missions",
+ "000303.htm"
+ ]
+ ],
+ [
+ "TRADE",
+ "000014.htm",
+ [
+ "Export regulation",
+ "000311.htm",
+ [
+ "Export promotion",
+ "000320.htm"
+ ],
+ [
+ "Prohibited export control",
+ "000321.htm"
+ ]
+ ],
+ [
+ "Import regulation",
+ "000312.htm",
+ [
+ "Prohibited import control",
+ "000323.htm"
+ ],
+ [
+ "Tariff regulation",
+ "000322.htm"
+ ]
+ ],
+ [
+ "Intellectual property management",
+ "000910.htm",
+ [
+ "Copyright registration",
+ "000308.htm"
+ ],
+ [
+ "Design registration",
+ "000310.htm"
+ ],
+ [
+ "Patent registration",
+ "000315.htm"
+ ],
+ [
+ "Trademark registration",
+ "000318.htm"
+ ]
+ ],
+ [
+ "International trade agreements",
+ "000313.htm"
+ ],
+ [
+ "Interstate trade agreements",
+ "000314.htm"
+ ],
+ [
+ "Trade development programs",
+ "000316.htm"
+ ],
+ [
+ "Trade expositions",
+ "000317.htm"
+ ]
+ ],
+ [
+ "TRANSPORT",
+ "000015.htm",
+ [
+ "Air transport",
+ "000324.htm",
+ [
+ "Air transport safety",
+ "000332.htm"
+ ],
+ [
+ "Aircraft standards",
+ "000330.htm"
+ ],
+ [
+ "Airport services",
+ "000331.htm"
+ ]
+ ],
+ [
+ "Freight movement regulation",
+ "000325.htm"
+ ],
+ [
+ "Passenger services",
+ "000326.htm"
+ ],
+ [
+ "Rail transport",
+ "000327.htm",
+ [
+ "Rail harmonisation standards",
+ "000333.htm"
+ ],
+ [
+ "Rail land acquisition regulation",
+ "000336.htm"
+ ],
+ [
+ "Rail transport safety",
+ "000337.htm"
+ ],
+ [
+ "Railway maintenance",
+ "000338.htm"
+ ],
+ [
+ "Rolling stock regulation",
+ "000339.htm"
+ ]
+ ],
+ [
+ "Road transport",
+ "000328.htm",
+ [
+ "Driving licences administration",
+ "000340.htm"
+ ],
+ [
+ "Road surface maintenance",
+ "000341.htm"
+ ],
+ [
+ "Road traffic regulation",
+ "000342.htm"
+ ],
+ [
+ "Road transport safety",
+ "000343.htm"
+ ],
+ [
+ "Vehicle registration",
+ "000344.htm"
+ ],
+ [
+ "Vehicle standards",
+ "000345.htm"
+ ]
+ ],
+ [
+ "Transport infrastructure development",
+ "000329.htm"
+ ]
+ ]
+]
--- /dev/null
+++ b/bubbletree.php
@@ -1,1 +1,126 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta charset="UTF-8"/>
+ <title>Minimal BubbleTree Demo</title>
+ <script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.js"></script>
+ <script type="text/javascript" src="javascripts/bubbletree/lib/jquery.history.js"></script>
+ <script type="text/javascript" src="javascripts/bubbletree/lib/raphael.js"></script>
+ <script type="text/javascript" src="javascripts/bubbletree/lib/vis4.js"></script>
+ <script type="text/javascript" src="javascripts/bubbletree/lib/Tween.js"></script>
+ <script type="text/javascript" src="javascripts/bubbletree/build/bubbletree.js"></script>
+ <link rel="stylesheet" type="text/css" href="javascripts/bubbletree/build/bubbletree.css" />
+ <script type="text/javascript" src="javascripts/bubbletree/styles/cofog.js"></script>
+
+
+ <script type="text/javascript">
+
+ $(function() {
+<?php
+include_once('include/common.inc.php');
+
+include("lib/Color.php");
+$color = new Lux_Color();
+
+$portfolios = Array();
+$total = 0;
+$db = $server->get_db('disclosr-agencies');
+try {
+ $rows = $db->get_view("app", "byDeptStateName", null, true)->rows;
+ foreach ($rows as $row) {
+ $portfolios[trim(str_replace(Array("Department of", "Department", "the", "'", "`"), "", $row->key))] = $row->value;
+ }
+} catch (SetteeRestClientException $e) {
+ setteErrorHandler($e);
+}
+
+$agencies = Array();
+try {
+ $rows = $db->get_view("app", "byCanonicalName", null, true)->rows;
+//print_r($rows);
+ foreach ($rows as $row) {
+ $employees = 0;
+ $portfolioid = 0;
+ if (isset($row->value->employees)) {
+ $employees = $row->value->employees;
+ }
+ if (isset($row->value->statistics->employees)) {
+ $agencyEmployeesArray = object_to_array($row->value->statistics->employees);
+ if (isset($agencyEmployeesArray["2010-2011"]["value"])) {
+ $employees = $agencyEmployeesArray["2010-2011"]["value"];
+ } else {
+ // bailout for agencies that are closed for business
+ continue;
+ }
+ }
+ if (!($employees > 0)) {
+ $employees = 0;
+ }
+ if (isset($row->value->parentOrg)) {
+ $portfolioid = $row->value->parentOrg;
+ }
+ if (isset($row->value->orgType) && $row->value->orgType == "FMA-DepartmentOfState") {
+ $portfolioid = $row->id;
+ }
+ $agencies[$portfolioid][$row->value->name] = $employees;
+ }
+} catch (SetteeRestClientException $e) {
+ setteErrorHandler($e);
+}
+//print_r($portfolios);
+//print_r($agencies);
+
+// http://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
+$golden_ratio_conjugate = 0.618033988749895;
+$h = 0.00+rand(0,10)/10; # use random start value
+foreach ($portfolios as $portfolioName => $portfolioID) {
+ $h += $golden_ratio_conjugate;
+
+ $h = fmod($h,1);
+ $portfolioColor = $color->hsv2hex(Array($h, .3, .99));
+ $subnodes = Array();
+ $portfolioEmployees = 0;
+ foreach ($agencies[$portfolioID] as $agencyName => $agencyEmployees) {
+ $agencyColor = $color->hsv2hex(Array($h / 10, rand(1, 10) / 10, abs(($h * (1 / 10)) - .5) + .5));
+ $subnodes[] = Array(
+ "label" => str_replace(Array("'", "`"), "", $agencyName),
+ "amount" => $agencyEmployees,
+ //"color" => "#" . $agencyColor
+ );
+ $portfolioEmployees += $agencyEmployees;
+ }
+ $nodes[] = Array(
+ "label" => $portfolioName,
+ "amount" => $portfolioEmployees,
+ //"color" => "#" . $portfolioColor,
+ "children" => $subnodes
+ );
+ $total += $portfolioEmployees;
+}
+$data = Array(
+ "label" => "Australian Federal Government",
+ "amount" => $total,
+ //"color" => "#000000",
+ "children" => $nodes
+);
+echo "var data =eval('('+'" . json_encode($data) . "'+')');";
+?>
+
+ new BubbleTree({
+ data: data,
+ container: '.bubbletree'
+ });
+
+
+ });
+
+ </script>
+ </head>
+ <body>
+ <div class="bubbletree-wrapper">
+ <div class="bubbletree"></div>
+ </div>
+ </body>
+</html>
+
--- a/charts.php
+++ b/charts.php
@@ -1,6 +1,6 @@
<?php
include_once('include/common.inc.php');
-include_header();
+include_header('Charts');
$db = $server->get_db('disclosr-agencies');
?>
<div class="foundation-header">
--- a/getAgency.php
+++ b/getAgency.php
@@ -1,7 +1,7 @@
<?php
include_once('include/common.inc.php');
-include_header();
+
function displayValue($key, $value, $mode) {
global $db, $schemas;
@@ -108,6 +108,7 @@
// edit?
$obj = $db->get($_REQUEST['id']);
+ include_header($obj->name);
//print_r($row);
if (sizeof($_POST) > 0) {
//print_r($_POST);
@@ -137,7 +138,7 @@
}
}
- $mode = "edit";
+ $mode = "view";
$rowArray = object_to_array($obj);
ksort($rowArray);
if ($mode == "edit") {
@@ -185,7 +186,8 @@
echo '<input id="submitbutton" type="submit"/></form>';
}
} else {
-
+// show all list
+ include_header('Agencies');
try {
/* $rows = $db->get_view("app", "showNamesABNs")->rows;
//print_r($rows);
--- /dev/null
+++ b/google676a414ad086cefb.html
@@ -1,1 +1,2 @@
+google-site-verification: google676a414ad086cefb.html
--- a/graph.php
+++ b/graph.php
@@ -113,12 +113,12 @@
sigInst.bind('downnodes',function(event){
var nodes = event.content;
});
- // Draw the graph :
- sigInst.draw();
// Start the ForceAtlas2 algorithm
// (requires "sigma.forceatlas2.js" to be included)
sigInst.startForceAtlas2();
+ // Draw the graph :
+ sigInst.draw();
}
if (document.addEventListener) {
--- a/include/common.inc.php
+++ b/include/common.inc.php
@@ -51,6 +51,9 @@
function phrase_to_tag ($phrase) {
return str_replace(" ","_",str_replace("'","",str_replace(",","",strtolower($phrase))));
}
+function local_url() {
+ return "http://" . $_SERVER['HTTP_HOST'] . rtrim(dirname($_SERVER['PHP_SELF']), '/\\') . "/";
+}
function GetDomain($url)
{
$nowww = ereg_replace('www\.','',$url);
--- a/include/couchdb.inc.php
+++ b/include/couchdb.inc.php
@@ -5,28 +5,28 @@
require ($basePath . 'couchdb/settee/src/settee.php');
function createDocumentsDesignDoc() {
- /*"views": {
- "web_server": {
- "map": "function(doc) {\n emit(doc.web_server, 1);\n}",
- "reduce": "function (key, values, rereduce) {\n return sum(values);\n}"
- },
- "byAgency": {
- "map": "function(doc) {\n emit(doc.agencyID, 1);\n}",
- "reduce": "function (key, values, rereduce) {\n return sum(values);\n}"
- },
- "byURL": {
- "map": "function(doc) {\n emit(doc.url, doc);\n}"
- },
- "agency": {
- "map": "function(doc) {\n emit(doc.agencyID, doc);\n}"
- },
- "byWebServer": {
- "map": "function(doc) {\n emit(doc.web_server, doc);\n}"
- },
- "getValidationRequired": {
- "map": "function(doc) {\nif (doc.mime_type == \"text/html\" \n&& typeof(doc.validation) == \"undefined\") {\n emit(doc._id, doc._attachments);\n}\n}"
- }
- }*/
+ /* "views": {
+ "web_server": {
+ "map": "function(doc) {\n emit(doc.web_server, 1);\n}",
+ "reduce": "function (key, values, rereduce) {\n return sum(values);\n}"
+ },
+ "byAgency": {
+ "map": "function(doc) {\n emit(doc.agencyID, 1);\n}",
+ "reduce": "function (key, values, rereduce) {\n return sum(values);\n}"
+ },
+ "byURL": {
+ "map": "function(doc) {\n emit(doc.url, doc);\n}"
+ },
+ "agency": {
+ "map": "function(doc) {\n emit(doc.agencyID, doc);\n}"
+ },
+ "byWebServer": {
+ "map": "function(doc) {\n emit(doc.web_server, doc);\n}"
+ },
+ "getValidationRequired": {
+ "map": "function(doc) {\nif (doc.mime_type == \"text/html\" \n&& typeof(doc.validation) == \"undefined\") {\n emit(doc._id, doc._attachments);\n}\n}"
+ }
+ } */
}
function createAgencyDesignDoc() {
@@ -95,7 +95,7 @@
}
}";
// http://stackoverflow.com/questions/646628/javascript-startswith
-$obj->views->score->map = 'if(!String.prototype.startsWith){
+ $obj->views->score->map = 'if(!String.prototype.startsWith){
String.prototype.startsWith = function (str) {
return !this.indexOf(str);
}
@@ -119,7 +119,7 @@
emit(count+doc._id, {id:doc._id, name: doc.name, score:count, orgType: doc.orgType, portfolio:portfolio});
}
}';
- $obj->views->scoreHas->map = 'if(!String.prototype.startsWith){
+ $obj->views->scoreHas->map = 'if(!String.prototype.startsWith){
String.prototype.startsWith = function (str) {
return !this.indexOf(str);
}
@@ -142,7 +142,7 @@
$obj->views->scoreHas->reduce = 'function (key, values, rereduce) {
return sum(values);
}';
- $obj->views->fieldNames->map = '
+ $obj->views->fieldNames->map = '
function(doc) {
for(var propName in doc) {
emit(propName, doc._id);
@@ -157,8 +157,7 @@
}
if (php_uname('n') == "vanille") {
-$serverAddr = 'http://192.168.178.21:5984/';
-
+ $serverAddr = 'http://192.168.178.21:5984/';
} else
if (php_uname('n') == "KYUUBEY") {
@@ -166,7 +165,8 @@
} else {
$serverAddr = 'http://127.0.0.1:5984/';
}
- $server = new SetteeServer($serverAddr);
+$server = new SetteeServer($serverAddr);
+
function setteErrorHandler($e) {
echo $e->getMessage() . "<br>" . PHP_EOL;
}
--- a/include/template.inc.php
+++ b/include/template.inc.php
@@ -1,6 +1,6 @@
<?php
-function include_header() {
+function include_header($title) {
global $basePath;
?>
<!DOCTYPE html>
@@ -16,7 +16,7 @@
<!-- Set the viewport width to device width for mobile -->
<meta name="viewport" content="width=device-width" />
- <title>Disclosr</title>
+ <title><?php echo $title; ?> - Disclosr</title>
<!-- Included CSS Files -->
<link rel="stylesheet" href="<?php echo $basePath ?>stylesheets/foundation.css">
@@ -71,9 +71,27 @@
<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="javascripts/flotr2/flotr2.js"></script>
+ <?php
+ if (strpos($_SERVER['SERVER_NAME'], ".gs")) {
+ ?>
+ <script type="text/javascript">
+
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-12341040-2']);
+ _gaq.push(['_trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+
+</script>
</body>
</html>
<?php }
+ }
+
--- /dev/null
+++ b/index.php
@@ -1,1 +1,9 @@
+<?php
+/* Redirect to a different page in the current directory that was requested */
+$host = $_SERVER['HTTP_HOST'];
+$uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\\');
+$extra = 'getAgency.php';
+header("Location: http://$host$uri/$extra");
+exit;
+?>
--- /dev/null
+++ b/javascripts/bubbletree
--- a/javascripts/flotr2
+++ b/javascripts/flotr2
--- a/javascripts/sigma
+++ b/javascripts/sigma
--- a/javascripts/sigma.min.js
+++ b/javascripts/sigma.min.js
@@ -1,63 +1,3552 @@
-/* sigmajs.org - an open-source light-weight JavaScript graph drawing library - Version: 0.1 - Author: Alexis Jacomy - License: MIT */
-var sigma={tools:{},classes:{},instances:{}};
-(function(){if(!Array.prototype.some)Array.prototype.some=function(i,n){var g=this.length;if("function"!=typeof i)throw new TypeError;for(var j=0;j<g;j++)if(j in this&&i.call(n,this[j],j,this))return!0;return!1};if(!Array.prototype.forEach)Array.prototype.forEach=function(i,n){var g=this.length;if("function"!=typeof i)throw new TypeError;for(var j=0;j<g;j++)j in this&&i.call(n,this[j],j,this)};if(!Array.prototype.map)Array.prototype.map=function(i,n){var g=this.length;if("function"!=typeof i)throw new TypeError;
-for(var j=Array(g),m=0;m<g;m++)m in this&&(j[m]=i.call(n,this[m],m,this));return j};if(!Array.prototype.filter)Array.prototype.filter=function(i,n){var g=this.length;if("function"!=typeof i)throw new TypeError;for(var j=[],m=0;m<g;m++)if(m in this){var t=this[m];i.call(n,t,m,this)&&j.push(t)}return j};if(!Object.keys)Object.keys=function(){var i=Object.prototype.hasOwnProperty,n=!{toString:null}.propertyIsEnumerable("toString"),g="toString,toLocaleString,valueOf,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,constructor".split(","),
-j=g.length;return function(m){if("object"!==typeof m&&"function"!==typeof m||null===m)throw new TypeError("Object.keys called on non-object");var t=[],p;for(p in m)i.call(m,p)&&t.push(p);if(n)for(p=0;p<j;p++)i.call(m,g[p])&&t.push(g[p]);return t}}()})();sigma.classes.Cascade=function(){this.p={};this.config=function(i,n){if("string"==typeof i&&void 0==n)return this.p[i];var g="object"==typeof i&&void 0==n?i:{};"string"==typeof i&&(g[i]=n);for(var j in g)void 0!=this.p[j]&&(this.p[j]=g[j]);return this}};
-sigma.classes.EventDispatcher=function(){var i={},n=this;this.one=function(g,j){if(!j||!g)return n;("string"==typeof g?g.split(" "):g).forEach(function(g){i[g]||(i[g]=[]);i[g].push({h:j,one:!0})});return n};this.bind=function(g,j){if(!j||!g)return n;("string"==typeof g?g.split(" "):g).forEach(function(g){i[g]||(i[g]=[]);i[g].push({h:j,one:!1})});return n};this.unbind=function(g,j){g||(i={});var m="string"==typeof g?g.split(" "):g;j?m.forEach(function(g){i[g]&&(i[g]=i[g].filter(function(g){return g.h!=
-j}));i[g]&&0==i[g].length&&delete i[g]}):m.forEach(function(g){delete i[g]});return n};this.dispatch=function(g,j){i[g]&&(i[g].forEach(function(i){i.h({type:g,content:j,target:n})}),i[g]=i[g].filter(function(g){return!g.one}));return n}};
-(function(){var i;function n(){function b(a){return{x:a.x,y:a.y,size:a.size,degree:a.degree,displayX:a.displayX,displayY:a.displayY,displaySize:a.displaySize,label:a.label,id:a.id,color:a.color,fixed:a.fixed,active:a.active,hidden:a.hidden,attr:a.attr}}function h(a){return{source:a.source.id,target:a.target.id,size:a.size,type:a.type,weight:a.weight,displaySize:a.displaySize,label:a.label,id:a.id,attr:a.attr,color:a.color}}function e(){d.nodes=[];d.nodesIndex={};d.edges=[];d.edgesIndex={};return d}
-sigma.classes.Cascade.call(this);sigma.classes.EventDispatcher.call(this);var d=this;this.p={minNodeSize:0,maxNodeSize:0,minEdgeSize:0,maxEdgeSize:0,scalingMode:"inside",nodesPowRatio:0.5,edgesPowRatio:0};this.borders={};e();this.addNode=function(a,b){if(d.nodesIndex[a])throw Error('Node "'+a+'" already exists.');var b=b||{},c={x:0,y:0,size:1,degree:0,fixed:!1,active:!1,hidden:!1,label:a.toString(),id:a.toString(),attr:{}},e;for(e in b)switch(e){case "id":break;case "x":case "y":case "size":c[e]=
-+b[e];break;case "fixed":case "active":case "hidden":c[e]=!!b[e];break;case "color":case "label":c[e]=b[e];break;default:c.attr[e]=b[e]}d.nodes.push(c);d.nodesIndex[a.toString()]=c;return d};this.addEdge=function(a,b,c,e){if(d.edgesIndex[a])throw Error('Edge "'+a+'" already exists.');if(!d.nodesIndex[b])throw Error("Edge's source \""+b+'" does not exist yet.');if(!d.nodesIndex[c])throw Error("Edge's target \""+c+'" does not exist yet.');e=e||{};b={source:d.nodesIndex[b],target:d.nodesIndex[c],size:1,
-weight:1,displaySize:0.5,label:a.toString(),id:a.toString(),attr:{}};b.source.degree++;b.target.degree++;for(var k in e)switch(k){case "id":case "source":case "target":break;case "size":b[k]=+e[k];break;case "color":b[k]=e[k].toString();break;case "type":b[k]=e[k].toString();break;case "label":b[k]=e[k];break;default:b.attr[k]=e[k]}d.edges.push(b);d.edgesIndex[a.toString()]=b;return d};this.dropNode=function(a){((a instanceof Array?a:[a])||[]).forEach(function(a){if(d.nodesIndex[a]){var c=null;d.nodes.some(function(b,
-d){return b.id==a?(c=d,!0):!1});null!=c&&d.nodes.splice(c,1);delete d.nodesIndex[a];d.edges=d.edges.filter(function(c){return c.source.id==a||c.target.id==a?(delete d.edgesIndex[c.id],!1):!0})}else sigma.log('Node "'+a+'" does not exist.')});return d};this.dropEdge=function(a){((a instanceof Array?a:[a])||[]).forEach(function(a){if(d.edgesIndex[a]){var c=null;d.edges.some(function(b,d){return b.id==a?(c=d,!0):!1});null!=c&&d.edges.splice(c,1);delete d.edgesIndex[a]}else sigma.log('Edge "'+a+'" does not exist.')});
-return d};this.iterEdges=function(a,b){var c=b?b.map(function(a){return d.edgesIndex[a]}):d.edges,e=c.map(h);e.forEach(a);c.forEach(function(a,c){var b=e[c],l;for(l in b)switch(l){case "id":case "weight":case "displaySize":break;case "size":a[l]=+b[l];break;case "source":case "target":a[l]=d.nodesIndex[l]||a[l];break;case "color":case "label":case "type":a[l]=(b[l]||"").toString();break;default:a.attr[l]=b[l]}});return d};this.iterNodes=function(a,e){var c=e?e.map(function(a){return d.nodesIndex[a]}):
-d.nodes,h=c.map(b);h.forEach(a);c.forEach(function(a,c){var b=h[c],d;for(d in b)switch(d){case "id":case "attr":case "degree":case "displayX":case "displayY":case "displaySize":break;case "x":case "y":case "size":a[d]=+b[d];break;case "fixed":case "active":case "hidden":a[d]=!!b[d];break;case "color":case "label":a[d]=b[d].toString();break;default:a.attr[d]=b[d]}});return d};this.getEdges=function(a){var b=((a instanceof Array?a:[a])||[]).map(function(a){return h(d.edgesIndex[a])});return a instanceof
-Array?b:b[0]};this.getNodes=function(a){var e=((a instanceof Array?a:[a])||[]).map(function(a){return b(d.nodesIndex[a])});return a instanceof Array?e:e[0]};this.empty=e;this.rescale=function(a,b,c,e){var k=0,f=0;c&&d.nodes.forEach(function(a){f=Math.max(a.size,f)});e&&d.edges.forEach(function(a){k=Math.max(a.size,k)});var f=f||1,k=k||1,h,g,s,q;c&&d.nodes.forEach(function(a){g=Math.max(a.x,g||a.x);h=Math.min(a.x,h||a.x);q=Math.max(a.y,q||a.y);s=Math.min(a.y,s||a.y)});var z="outside"==d.p.scalingMode?
-Math.max(a/Math.max(g-h,1),b/Math.max(q-s,1)):Math.min(a/Math.max(g-h,1),b/Math.max(q-s,1)),i,j;!d.p.maxNodeSize&&!d.p.minNodeSize?(i=1,j=0):d.p.maxNodeSize==d.p.minNodeSize?(i=0,j=d.p.maxNodeSize):(i=(d.p.maxNodeSize-d.p.minNodeSize)/f,j=d.p.minNodeSize);var m,w;!d.p.maxEdgeSize&&!d.p.minEdgeSize?(m=1,w=0):(m=d.p.maxEdgeSize==d.p.minEdgeSize?0:(d.p.maxEdgeSize-d.p.minEdgeSize)/k,w=d.p.minEdgeSize);c&&d.nodes.forEach(function(c){c.displaySize=c.size*i+j;if(!c.fixed)c.displayX=(c.x-(g+h)/2)*z+a/2,
-c.displayY=(c.y-(q+s)/2)*z+b/2});e&&d.edges.forEach(function(a){a.displaySize=a.size*m+w});return d};this.translate=function(a,b,c,e,k){var f=Math.pow(c,d.p.nodesPowRatio);e&&d.nodes.forEach(function(d){if(!d.fixed)d.displayX=d.displayX*c+a,d.displayY=d.displayY*c+b;d.displaySize*=f});f=Math.pow(c,d.p.edgesPowRatio);k&&d.edges.forEach(function(a){a.displaySize*=f});return d};this.setBorders=function(){d.borders={};d.nodes.forEach(function(a){d.borders.minX=Math.min(void 0==d.borders.minX?a.displayX-
-a.displaySize:d.borders.minX,a.displayX-a.displaySize);d.borders.maxX=Math.max(void 0==d.borders.maxX?a.displayX+a.displaySize:d.borders.maxX,a.displayX+a.displaySize);d.borders.minY=Math.min(void 0==d.borders.minY?a.displayY-a.displaySize:d.borders.minY,a.displayY-a.displaySize);d.borders.maxY=Math.max(void 0==d.borders.maxY?a.displayY-a.displaySize:d.borders.maxY,a.displayY-a.displaySize)})};this.checkHover=function(a,b){var c,e,k,f=[],h=[];d.nodes.forEach(function(d){if(d.hidden)d.hover=!1;else{c=
-Math.abs(d.displayX-a);e=Math.abs(d.displayY-b);k=d.displaySize;var s=d.hover,q=c<k&&e<k&&Math.sqrt(c*c+e*e)<k;if(s&&!q)d.hover=!1,h.push(d.id);else if(q&&!s)d.hover=!0,f.push(d.id)}});f.length&&d.dispatch("overnodes",f);h.length&&d.dispatch("outnodes",h);return d}}function g(b,h){function e(){var a;a="<p>GLOBAL :</p>";for(var b in d.p.globalProbes)a+="<p>"+b+" : "+d.p.globalProbes[b]()+"</p>";a+="<br><p>LOCAL :</p>";for(b in d.p.localProbes)a+="<p>"+b+" : "+d.p.localProbes[b]()+"</p>";d.p.dom.innerHTML=
-a;return d}sigma.classes.Cascade.call(this);var d=this;this.instance=b;this.monitoring=!1;this.p={fps:40,dom:h,globalProbes:{"Time (ms)":sigma.chronos.getExecutionTime,Queue:sigma.chronos.getQueuedTasksCount,Tasks:sigma.chronos.getTasksCount,FPS:sigma.chronos.getFPS},localProbes:{"Nodes count":function(){return d.instance.graph.nodes.length},"Edges count":function(){return d.instance.graph.edges.length}}};this.activate=function(){if(!d.monitoring)d.monitoring=window.setInterval(e,1E3/d.p.fps);return d};
-this.desactivate=function(){if(d.monitoring)window.clearInterval(d.monitoring),d.monitoring=null,d.p.dom.innerHTML="";return d}}function j(b){function h(b){if(a.p.mouseEnabled&&(e(a.mouseX,a.mouseY,a.ratio*(0<(void 0!=b.wheelDelta&&b.wheelDelta||void 0!=b.detail&&-b.detail)?a.p.zoomMultiply:1/a.p.zoomMultiply)),a.p.blockScroll))b.preventDefault?b.preventDefault():b.returnValue=!1}function e(b,c,e){if(!a.isMouseDown&&(window.clearInterval(a.interpolationID),n=void 0!=e,i=a.stageX,j=b,k=a.stageY,l=
-c,f=e||a.ratio,f=Math.min(Math.max(f,a.p.minRatio),a.p.maxRatio),u=a.p.directZooming?1-(n?a.p.zoomDelta:a.p.dragDelta):0,a.ratio!=f||a.stageX!=j||a.stageY!=l))d(),a.interpolationID=window.setInterval(d,50),a.dispatch("startinterpolate")}function d(){u+=n?a.p.zoomDelta:a.p.dragDelta;u=Math.min(u,1);var b=sigma.easing.quadratic.easeout(u),c=a.ratio;a.ratio=c*(1-b)+f*b;n?(a.stageX=j+(a.stageX-j)*a.ratio/c,a.stageY=l+(a.stageY-l)*a.ratio/c):(a.stageX=i*(1-b)+j*b,a.stageY=k*(1-b)+l*b);a.dispatch("interpolate");
-if(1<=u)window.clearInterval(a.interpolationID),b=a.ratio,n?(a.ratio=f,a.stageX=j+(a.stageX-j)*a.ratio/b,a.stageY=l+(a.stageY-l)*a.ratio/b):(a.stageX=j,a.stageY=l),a.dispatch("stopinterpolate")}sigma.classes.Cascade.call(this);sigma.classes.EventDispatcher.call(this);var a=this;this.p={minRatio:1,maxRatio:32,marginRatio:1,zoomDelta:0.1,dragDelta:0.3,zoomMultiply:2,directZooming:!1,blockScroll:!0,inertia:1.1,mouseEnabled:!0};var g=0,c=0,i=0,k=0,f=1,j=0,l=0,s=0,q=0,z=0,m=0,u=0,n=!1;this.stageY=this.stageX=
-0;this.ratio=1;this.mouseY=this.mouseX=0;this.isMouseDown=!1;b.addEventListener("DOMMouseScroll",h,!0);b.addEventListener("mousewheel",h,!0);b.addEventListener("mousemove",function(b){a.mouseX=void 0!=b.offsetX&&b.offsetX||void 0!=b.layerX&&b.layerX||void 0!=b.clientX&&b.clientX;a.mouseY=void 0!=b.offsetY&&b.offsetY||void 0!=b.layerY&&b.layerY||void 0!=b.clientY&&b.clientY;if(a.isMouseDown){var d=a.mouseX-g+i,f=a.mouseY-c+k;if(d!=a.stageX||f!=a.stageY)q=s,m=z,s=d,z=f,a.stageX=d,a.stageY=f,a.dispatch("drag")}a.dispatch("move");
-b.preventDefault?b.preventDefault():b.returnValue=!1},!0);b.addEventListener("mousedown",function(b){if(a.p.mouseEnabled)a.isMouseDown=!0,a.dispatch("mousedown"),i=a.stageX,k=a.stageY,g=a.mouseX,c=a.mouseY,q=s=a.stageX,m=z=a.stageY,a.dispatch("startdrag"),b.preventDefault?b.preventDefault():b.returnValue=!1},!0);document.addEventListener("mouseup",function(b){if(a.p.mouseEnabled&&a.isMouseDown)a.isMouseDown=!1,a.dispatch("mouseup"),(i!=a.stageX||k!=a.stageY)&&e(a.stageX+a.p.inertia*(a.stageX-q),a.stageY+
-a.p.inertia*(a.stageY-m)),b.preventDefault?b.preventDefault():b.returnValue=!1},!0);this.checkBorders=function(){return a};this.interpolate=e}function m(b,h,e,d,a,g,c){function i(a){var b=d,c="fixed"==f.p.labelSize?f.p.defaultLabelSize:f.p.labelSizeRatio*a.displaySize;b.font=(f.p.hoverFontStyle||f.p.fontStyle||"")+" "+c+"px "+(f.p.hoverFont||f.p.font||"");b.fillStyle="node"==f.p.labelHoverBGColor?a.color||f.p.defaultNodeColor:f.p.defaultHoverLabelBGColor;b.beginPath();if(f.p.labelHoverShadow)b.shadowOffsetX=
-0,b.shadowOffsetY=0,b.shadowBlur=4,b.shadowColor=f.p.labelHoverShadowColor;sigma.tools.drawRoundRect(b,Math.round(a.displayX-c/2-2),Math.round(a.displayY-c/2-2),Math.round(b.measureText(a.label).width+1.5*a.displaySize+c/2+4),Math.round(c+4),Math.round(c/2+2),"left");b.closePath();b.fill();b.shadowOffsetX=0;b.shadowOffsetY=0;b.shadowBlur=0;b.beginPath();b.fillStyle="node"==f.p.nodeBorderColor?a.color||f.p.defaultNodeColor:f.p.defaultNodeBorderColor;b.arc(Math.round(a.displayX),Math.round(a.displayY),
-a.displaySize+f.p.borderSize,0,2*Math.PI,!0);b.closePath();b.fill();b.beginPath();b.fillStyle="node"==f.p.nodeHoverColor?a.color||f.p.defaultNodeColor:f.p.defaultNodeHoverColor;b.arc(Math.round(a.displayX),Math.round(a.displayY),a.displaySize,0,2*Math.PI,!0);b.closePath();b.fill();b.fillStyle="node"==f.p.labelHoverColor?a.color||f.p.defaultNodeColor:f.p.defaultLabelHoverColor;b.fillText(a.label,Math.round(a.displayX+1.5*a.displaySize),Math.round(a.displayY+c/2-3));return f}function k(a){if(isNaN(a.x)||
-isNaN(a.y))throw Error("A node's coordinate is not a number (id: "+a.id+")");return!a.hidden&&a.displayX+a.displaySize>-j/3&&a.displayX-a.displaySize<4*j/3&&a.displayY+a.displaySize>-l/3&&a.displayY-a.displaySize<4*l/3}sigma.classes.Cascade.call(this);var f=this;this.p={labelColor:"default",defaultLabelColor:"#000",labelHoverBGColor:"default",defaultHoverLabelBGColor:"#fff",labelHoverShadow:!0,labelHoverShadowColor:"#000",labelHoverColor:"default",defaultLabelHoverColor:"#000",labelActiveBGColor:"default",
-defaultActiveLabelBGColor:"#fff",labelActiveShadow:!0,labelActiveShadowColor:"#000",labelActiveColor:"default",defaultLabelActiveColor:"#000",labelSize:"fixed",defaultLabelSize:12,labelSizeRatio:2,labelThreshold:6,font:"Arial",hoverFont:"",activeFont:"",fontStyle:"",hoverFontStyle:"",activeFontStyle:"",edgeColor:"source",defaultEdgeColor:"#aaa",defaultEdgeType:"line",defaultNodeColor:"#aaa",nodeHoverColor:"node",defaultNodeHoverColor:"#fff",nodeActiveColor:"node",defaultNodeActiveColor:"#fff",borderSize:0,
-nodeBorderColor:"node",defaultNodeBorderColor:"#fff",edgesSpeed:200,nodesSpeed:200,labelsSpeed:200};var j=g,l=c;this.currentLabelIndex=this.currentNodeIndex=this.currentEdgeIndex=0;this.task_drawLabel=function(){for(var b=a.nodes.length,c=0;c++<f.p.labelsSpeed&&f.currentLabelIndex<b;)if(f.isOnScreen(a.nodes[f.currentLabelIndex])){var d=a.nodes[f.currentLabelIndex++],h=e;if(d.displaySize>=f.p.labelThreshold){var k="fixed"==f.p.labelSize?f.p.defaultLabelSize:f.p.labelSizeRatio*d.displaySize;h.font=
-f.p.fontStyle+k+"px "+f.p.font;h.fillStyle="node"==f.p.labelColor?d.color||f.p.defaultNodeColor:f.p.defaultLabelColor;h.fillText(d.label,Math.round(d.displayX+1.5*d.displaySize),Math.round(d.displayY+k/2-3))}}else f.currentLabelIndex++;return f.currentLabelIndex<b};this.task_drawEdge=function(){for(var b=a.edges.length,c,d,e=0;e++<f.p.edgesSpeed&&f.currentEdgeIndex<b;)if(c=a.edges[f.currentEdgeIndex].source,d=a.edges[f.currentEdgeIndex].target,c.hidden||d.hidden||!f.isOnScreen(c)&&!f.isOnScreen(d))f.currentEdgeIndex++;
-else{c=a.edges[f.currentEdgeIndex++];d=c.source.displayX;var k=c.source.displayY,g=c.target.displayX,i=c.target.displayY,j=c.color;if(!j)switch(f.p.edgeColor){case "source":j=c.source.color||f.p.defaultNodeColor;break;case "target":j=c.target.color||f.p.defaultNodeColor;break;default:j=f.p.defaultEdgeColor}var l=h;switch(c.type||f.p.defaultEdgeType){case "curve":l.strokeStyle=j;l.lineWidth=c.displaySize/3;l.beginPath();l.moveTo(d,k);l.quadraticCurveTo((d+g)/2+(i-k)/4,(k+i)/2+(d-g)/4,g,i);l.stroke();
-break;default:l.strokeStyle=j,l.lineWidth=c.displaySize/3,l.beginPath(),l.moveTo(d,k),l.lineTo(g,i),l.stroke()}}return f.currentEdgeIndex<b};this.task_drawNode=function(){for(var c=a.nodes.length,d=0;d++<f.p.nodesSpeed&&f.currentNodeIndex<c;)if(f.isOnScreen(a.nodes[f.currentNodeIndex])){var e=a.nodes[f.currentNodeIndex++],k=Math.round(10*e.displaySize)/10,h=b;h.fillStyle=e.color;h.beginPath();h.arc(e.displayX,e.displayY,k,0,2*Math.PI,!0);h.closePath();h.fill();e.hover&&i(e)}else f.currentNodeIndex++;
-return f.currentNodeIndex<c};this.drawActiveNode=function(a){var b=d;if(!k(a))return f;var c="fixed"==f.p.labelSize?f.p.defaultLabelSize:f.p.labelSizeRatio*a.displaySize;b.font=(f.p.activeFontStyle||f.p.fontStyle||"")+" "+c+"px "+(f.p.activeFont||f.p.font||"");b.fillStyle="node"==f.p.labelHoverBGColor?a.color||f.p.defaultNodeColor:f.p.defaultActiveLabelBGColor;b.beginPath();if(f.p.labelActiveShadow)b.shadowOffsetX=0,b.shadowOffsetY=0,b.shadowBlur=4,b.shadowColor=f.p.labelActiveShadowColor;sigma.tools.drawRoundRect(b,
-Math.round(a.displayX-c/2-2),Math.round(a.displayY-c/2-2),Math.round(b.measureText(a.label).width+1.5*a.displaySize+c/2+4),Math.round(c+4),Math.round(c/2+2),"left");b.closePath();b.fill();b.shadowOffsetX=0;b.shadowOffsetY=0;b.shadowBlur=0;b.beginPath();b.fillStyle="node"==f.p.nodeBorderColor?a.color||f.p.defaultNodeColor:f.p.defaultNodeBorderColor;b.arc(Math.round(a.displayX),Math.round(a.displayY),a.displaySize+f.p.borderSize,0,2*Math.PI,!0);b.closePath();b.fill();b.beginPath();b.fillStyle="node"==
-f.p.nodeActiveColor?a.color||f.p.defaultNodeColor:f.p.defaultNodeActiveColor;b.arc(Math.round(a.displayX),Math.round(a.displayY),a.displaySize,0,2*Math.PI,!0);b.closePath();b.fill();b.fillStyle="node"==f.p.labelActiveColor?a.color||f.p.defaultNodeColor:f.p.defaultLabelActiveColor;b.fillText(a.label,Math.round(a.displayX+1.5*a.displaySize),Math.round(a.displayY+c/2-3));return f};this.drawHoverNode=i;this.isOnScreen=k;this.resize=function(a,b){j=a;l=b;return f}}function t(b,h){function e(){sigma.chronos.removeTask("node_"+
-c.id,2).removeTask("edge_"+c.id,2).removeTask("label_"+c.id,2).stopTasks();return c}function d(a,b){c.domElements[a]=document.createElement(b);c.domElements[a].style.position="absolute";c.domElements[a].setAttribute("id","sigma_"+a+"_"+c.id);c.domElements[a].setAttribute("class","sigma_"+a+"_"+b);c.domElements[a].setAttribute("width",c.width+"px");c.domElements[a].setAttribute("height",c.height+"px");c.domRoot.appendChild(c.domElements[a]);return c}function a(){c.p.drawHoverNodes&&(c.graph.checkHover(c.mousecaptor.mouseX,
-c.mousecaptor.mouseY),c.graph.nodes.forEach(function(a){a.hover&&!a.active&&c.plotter.drawHoverNode(a)}));return c}function A(){c.p.drawActiveNodes&&c.graph.nodes.forEach(function(a){a.active&&c.plotter.drawActiveNode(a)});return c}sigma.classes.Cascade.call(this);sigma.classes.EventDispatcher.call(this);var c=this;this.id=h.toString();this.p={auto:!0,drawNodes:2,drawEdges:1,drawLabels:2,lastNodes:2,lastEdges:0,lastLabels:2,drawHoverNodes:!0,drawActiveNodes:!0};this.domRoot=b;this.width=this.domRoot.offsetWidth;
-this.height=this.domRoot.offsetHeight;this.graph=new n;this.domElements={};d("edges","canvas");d("nodes","canvas");d("labels","canvas");d("hover","canvas");d("monitor","div");d("mouse","canvas");this.plotter=new m(this.domElements.nodes.getContext("2d"),this.domElements.edges.getContext("2d"),this.domElements.labels.getContext("2d"),this.domElements.hover.getContext("2d"),this.graph,this.width,this.height);this.monitor=new g(this,this.domElements.monitor);this.mousecaptor=new j(this.domElements.mouse,
-this.id);this.mousecaptor.bind("drag interpolate",function(){c.draw(c.p.auto?2:c.p.drawNodes,c.p.auto?0:c.p.drawEdges,c.p.auto?2:c.p.drawLabels,!0)}).bind("stopdrag stopinterpolate",function(){c.draw(c.p.auto?2:c.p.drawNodes,c.p.auto?1:c.p.drawEdges,c.p.auto?2:c.p.drawLabels,!0)}).bind("mousedown mouseup",function(a){var b=c.graph.nodes.filter(function(a){return!!a.hover}).map(function(a){return a.id});c.dispatch("mousedown"==a.type?"downgraph":"upgraph");b.length&&c.dispatch("mousedown"==a.type?
-"downnodes":"upnodes",b)}).bind("move",function(){c.domElements.hover.getContext("2d").clearRect(0,0,c.domElements.hover.width,c.domElements.hover.height);a();A()});sigma.chronos.bind("startgenerators",function(){sigma.chronos.getGeneratorsIDs().some(function(a){return!!a.match(RegExp("_ext_"+c.id+"$",""))})&&c.draw(c.p.auto?2:c.p.drawNodes,c.p.auto?0:c.p.drawEdges,c.p.auto?2:c.p.drawLabels)}).bind("stopgenerators",function(){c.draw()});for(var B=0;B<i.length;B++)i[B](this);this.draw=function(a,b,
-d,h){if(h&&sigma.chronos.getGeneratorsIDs().some(function(a){return!!a.match(RegExp("_ext_"+c.id+"$",""))}))return c;a=void 0==a?c.p.drawNodes:a;b=void 0==b?c.p.drawEdges:b;d=void 0==d?c.p.drawLabels:d;h={nodes:a,edges:b,labels:d};c.p.lastNodes=a;c.p.lastEdges=b;c.p.lastLabels=d;e();c.graph.rescale(c.width,c.height,0<a,0<b).setBorders();c.mousecaptor.checkBorders(c.graph.borders,c.width,c.height);c.graph.translate(c.mousecaptor.stageX,c.mousecaptor.stageY,c.mousecaptor.ratio,0<a,0<b);c.dispatch("graphscaled");
-for(var g in c.domElements)"canvas"==c.domElements[g].nodeName.toLowerCase()&&(void 0==h[g]||0<=h[g])&&c.domElements[g].getContext("2d").clearRect(0,0,c.domElements[g].width,c.domElements[g].height);c.plotter.currentEdgeIndex=0;c.plotter.currentNodeIndex=0;c.plotter.currentLabelIndex=0;g=null;h=!1;if(a)if(1<a)for(;c.plotter.task_drawNode(););else sigma.chronos.addTask(c.plotter.task_drawNode,"node_"+c.id,!1),h=!0,g="node_"+c.id;if(d)if(1<d)for(;c.plotter.task_drawLabel(););else g?sigma.chronos.queueTask(c.plotter.task_drawLabel,
-"label_"+c.id,g):sigma.chronos.addTask(c.plotter.task_drawLabel,"label_"+c.id,!1),h=!0,g="label_"+c.id;if(b)if(1<b)for(;c.plotter.task_drawEdge(););else g?sigma.chronos.queueTask(c.plotter.task_drawEdge,"edge_"+c.id,g):sigma.chronos.addTask(c.plotter.task_drawEdge,"edge_"+c.id,!1),h=!0,g="edge_"+c.id;c.dispatch("draw");c.refresh();h&&sigma.chronos.runTasks();return c};this.resize=function(a,b){var d=c.width,e=c.height;void 0!=a&&void 0!=b?(c.width=a,c.height=b):(c.width=c.domRoot.offsetWidth,c.height=
-c.domRoot.offsetHeight);if(d!=c.width||e!=c.height){for(var h in c.domElements)c.domElements[h].setAttribute("width",c.width+"px"),c.domElements[h].setAttribute("height",c.height+"px");c.plotter.resize(c.width,c.height);c.draw(c.p.lastNodes,c.p.lastEdges,c.p.lastLabels,!0)}return c};this.refresh=function(){c.domElements.hover.getContext("2d").clearRect(0,0,c.domElements.hover.width,c.domElements.hover.height);a();A();return c};this.drawHover=a;this.drawActive=A;this.clearSchedule=e;window.addEventListener("resize",
-function(){c.resize()})}function p(b){var h=this;sigma.classes.EventDispatcher.call(this);this._core=b;this.kill=function(){};this.getID=function(){return b.id};this.configProperties=function(e,d){var a=b.config(e,d);return a==b?h:a};this.drawingProperties=function(e,d){var a=b.plotter.config(e,d);return a==b.plotter?h:a};this.mouseProperties=function(e,d){var a=b.mousecaptor.config(e,d);return a==b.mousecaptor?h:a};this.graphProperties=function(e,d){var a=b.graph.config(e,d);return a==b.graph?h:
-a};this.getMouse=function(){return{mouseX:b.mousecaptor.mouseX,mouseY:b.mousecaptor.mouseY,down:b.mousecaptor.isMouseDown}};this.position=function(e,d,a){if(0==arguments.length)return{stageX:b.mousecaptor.stageX,stageY:b.mousecaptor.stageY,ratio:b.mousecaptor.ratio};b.mousecaptor.stageX=void 0!=e?e:b.mousecaptor.stageX;b.mousecaptor.stageY=void 0!=d?d:b.mousecaptor.stageY;b.mousecaptor.ratio=void 0!=a?a:b.mousecaptor.ratio;return h};this.goTo=function(e,d,a){b.mousecaptor.interpolate(e,d,a);return h};
-this.zoomTo=function(e,d,a){a=Math.min(Math.max(b.mousecaptor.config("minRatio"),a),b.mousecaptor.config("maxRatio"));a==b.mousecaptor.ratio?b.mousecaptor.interpolate(e-b.width/2+b.mousecaptor.stageX,d-b.height/2+b.mousecaptor.stageY):b.mousecaptor.interpolate((a*e-b.mousecaptor.ratio*b.width/2)/(a-b.mousecaptor.ratio),(a*d-b.mousecaptor.ratio*b.height/2)/(a-b.mousecaptor.ratio),a);return h};this.resize=function(e,d){b.resize(e,d);return h};this.draw=function(e,d,a,g){b.draw(e,d,a,g);return h};this.refresh=
-function(){b.refresh();return h};this.addGenerator=function(e,d,a){sigma.chronos.addGenerator(e+"_ext_"+b.id,d,a);return h};this.removeGenerator=function(e){sigma.chronos.removeGenerator(e+"_ext_"+b.id);return h};this.addNode=function(e,d){b.graph.addNode(e,d);return h};this.addEdge=function(e,d,a,g){b.graph.addEdge(e,d,a,g);return h};this.dropNode=function(e){b.graph.dropNode(e);return h};this.dropEdge=function(e){b.graph.dropEdge(e);return h};this.pushGraph=function(e,d){e.nodes&&e.nodes.forEach(function(a){a.id&&
-(!d||!b.graph.nodesIndex[a.id])&&h.addNode(a.id,a)});e.edges&&e.edges.forEach(function(a){(validID=a.source&&a.target&&a.id)&&(!d||!b.graph.edgesIndex[a.id])&&h.addNode(a.id,a.source,a.target,a)});return h};this.emptyGraph=function(){b.graph.empty();return h};this.getNodesCount=function(){return b.graph.nodes.length};this.getEdgesCount=function(){return b.graph.edges.length};this.iterNodes=function(e,d){b.graph.iterNodes(e,d);return h};this.iterEdges=function(e,d){b.graph.iterEdges(e,d);return h};
-this.getNodes=function(e){return b.graph.getNodes(e)};this.getEdges=function(e){return b.graph.getEdges(e)};this.activateMonitoring=function(){return b.monitor.activate()};this.desactivateMonitoring=function(){return b.monitor.desactivate()};b.bind("downnodes upnodes downgraph upgraph",function(b){h.dispatch(b.type,b.content)});b.graph.bind("overnodes outnodes",function(b){h.dispatch(b.type,b.content)})}var x=0;i=void 0;i=[];sigma.init=function(b){b=new t(b,(++x).toString());sigma.instances[x]=new p(b);
-return sigma.instances[x]};sigma.addPlugin=function(b,h,e){p.prototype[b]=h;i.push(e)};sigma.chronos=new function(){function b(a){window.setTimeout(a,0);return f}function h(){for(f.dispatch("frameinserted");m&&r.length&&e(););!m||!r.length?a():(w=(new Date).getTime(),q++,C=u-p,t=p-C,f.dispatch("insertframe"),b(h))}function e(){y%=r.length;if(!r[y].task()){var a=r[y].taskName;v=v.filter(function(b){b.taskParent==a&&r.push({taskName:b.taskName,task:b.task});return b.taskParent!=a});f.dispatch("killed",
-r.splice(y--,1)[0])}y++;u=(new Date).getTime()-w;return u<=t}function d(){m=!0;q=y=0;x=w=(new Date).getTime();f.dispatch("start");f.dispatch("insertframe");b(h);return f}function a(){f.dispatch("stop");m=!1;return f}function g(a,b,c){if("function"!=typeof a)throw Error('Task "'+b+'" is not a function');r.push({taskName:b,task:a});m=!(!m&&!(c&&d()||1));return f}function c(a){return a?Object.keys(o).filter(function(a){return!!o[a].on}).length:Object.keys(o).length}function i(){Object.keys(o).length?
-(f.dispatch("startgenerators"),f.unbind("killed",j),b(function(){for(var a in o)o[a].on=!0,g(o[a].task,a,!1)}),f.bind("killed",j).runTasks()):f.dispatch("stopgenerators");return f}function j(a){if(void 0!=o[a.content.taskName])o[a.content.taskName].del||!o[a.content.taskName].condition()?delete o[a.content.taskName]:o[a.content.taskName].on=!1,0==c(!0)&&i()}sigma.classes.EventDispatcher.call(this);var f=this,m=!1,l=80,n=0,q=0,p=1E3/l,t=p,u=0,x=0,w=0,C=0,o={},r=[],v=[],y=0;this.frequency=function(a){return void 0!=
-a?(l=Math.abs(1*a),p=1E3/l,q=0,f):l};this.runTasks=d;this.stopTasks=a;this.insertFrame=b;this.addTask=g;this.queueTask=function(a,b,c){if("function"!=typeof a)throw Error('Task "'+b+'" is not a function');if(!r.concat(v).some(function(a){return a.taskName==c}))throw Error('Parent task "'+c+'" of "'+b+'" is not attached.');v.push({taskParent:c,taskName:b,task:a});return f};this.removeTask=function(b,c){if(void 0==b)r=[],1==c?v=[]:2==c&&(r=v,v=[]),a();else{var d="string"==typeof b?b:"";r=r.filter(function(a){return("string"==
-typeof b?a.taskName==b:a.task==b)?(d=a.taskName,!1):!0});0<c&&(v=v.filter(function(a){1==c&&a.taskParent==d&&r.push(a);return a.taskParent!=d}))}m=!(r.length&&(!a()||1));return f};this.addGenerator=function(a,b,d){if(void 0!=o[a])return f;o[a]={task:b,condition:d};0==c(!0)&&i();return f};this.removeGenerator=function(a){if(o[a])o[a].on=!1,o[a].del=!0;return f};this.startGenerators=i;this.getGeneratorsIDs=function(){return Object.keys(o)};this.getFPS=function(){m&&(n=Math.round(1E4*(q/((new Date).getTime()-
-x)))/10);return n};this.getTasksCount=function(){return r.length};this.getQueuedTasksCount=function(){return v.length};this.getExecutionTime=function(){return w-x};return this};sigma.debugMode=0;sigma.log=function(){if(1==sigma.debugMode)for(var b in arguments)console.log(arguments[b]);else if(1<sigma.debugMode)for(b in arguments)throw Error(arguments[b]);return sigma};sigma.easing={linear:{},quadratic:{}};sigma.easing.linear.easenone=function(b){return b};sigma.easing.quadratic.easein=function(b){return b*
-b};sigma.easing.quadratic.easeout=function(b){return-b*(b-2)};sigma.easing.quadratic.easeinout=function(b){return 1>(b*=2)?0.5*b*b:-0.5*(--b*(b-2)-1)};sigma.tools.drawRoundRect=function(b,h,e,d,a,g,c){var g=g?g:0,i=c?c:[],i="string"==typeof i?i.split(" "):i,c=g&&(0<=i.indexOf("topleft")||0<=i.indexOf("top")||0<=i.indexOf("left")),j=g&&(0<=i.indexOf("topright")||0<=i.indexOf("top")||0<=i.indexOf("right")),f=g&&(0<=i.indexOf("bottomleft")||0<=i.indexOf("bottom")||0<=i.indexOf("left")),i=g&&(0<=i.indexOf("bottomright")||
-0<=i.indexOf("bottom")||0<=i.indexOf("right"));b.moveTo(h,e+g);c?b.arcTo(h,e,h+g,e,g):b.lineTo(h,e);j?(b.lineTo(h+d-g,e),b.arcTo(h+d,e,h+d,e+g,g)):b.lineTo(h+d,e);i?(b.lineTo(h+d,e+a-g),b.arcTo(h+d,e+a,h+d-g,e+a,g)):b.lineTo(h+d,e+a);f?(b.lineTo(h+g,e+a),b.arcTo(h,e+a,h,e+a-g,g)):b.lineTo(h,e+a);b.lineTo(h,e+g)};sigma.tools.getRGB=function(b,g){var b=b.toString(),e={r:0,g:0,b:0};if(3<=b.length&&"#"==b.charAt(0)){var d=b.length-1;6==d?e={r:parseInt(b.charAt(1)+b.charAt(2),16),g:parseInt(b.charAt(3)+
-b.charAt(4),16),b:parseInt(b.charAt(5)+b.charAt(5),16)}:3==d&&(e={r:parseInt(b.charAt(1)+b.charAt(1),16),g:parseInt(b.charAt(2)+b.charAt(2),16),b:parseInt(b.charAt(3)+b.charAt(3),16)})}g&&(e=[e.r,e.g,e.b]);return e};sigma.tools.rgbToHex=function(b,g,e){return sigma.tools.toHex(b)+sigma.tools.toHex(g)+sigma.tools.toHex(e)};sigma.tools.toHex=function(b){b=parseInt(b,10);if(isNaN(b))return"00";b=Math.max(0,Math.min(b,255));return"0123456789ABCDEF".charAt((b-b%16)/16)+"0123456789ABCDEF".charAt(b%16)};
-sigma.publicPrototype=p.prototype})();
-
+// Define packages:
+var sigma = {};
+sigma.tools = {};
+sigma.classes = {};
+sigma.instances = {};
+
+// Adding Array helpers, if not present yet:
+(function() {
+ if (!Array.prototype.some) {
+ Array.prototype.some = function(fun /*, thisp*/) {
+ var len = this.length;
+ if (typeof fun != 'function') {
+ throw new TypeError();
+ }
+
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++) {
+ if (i in this &&
+ fun.call(thisp, this[i], i, this)) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+ }
+
+ if (!Array.prototype.forEach) {
+ Array.prototype.forEach = function(fun /*, thisp*/) {
+ var len = this.length;
+ if (typeof fun != 'function') {
+ throw new TypeError();
+ }
+
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++) {
+ if (i in this) {
+ fun.call(thisp, this[i], i, this);
+ }
+ }
+ };
+ }
+
+ if (!Array.prototype.map) {
+ Array.prototype.map = function(fun /*, thisp*/) {
+ var len = this.length;
+ if (typeof fun != 'function') {
+ throw new TypeError();
+ }
+
+ var res = new Array(len);
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++) {
+ if (i in this) {
+ res[i] = fun.call(thisp, this[i], i, this);
+ }
+ }
+
+ return res;
+ };
+ }
+
+ if (!Array.prototype.filter) {
+ Array.prototype.filter = function(fun /*, thisp*/) {
+ var len = this.length;
+ if (typeof fun != 'function')
+ throw new TypeError();
+
+ var res = new Array();
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++) {
+ if (i in this) {
+ var val = this[i]; // in case fun mutates this
+ if (fun.call(thisp, val, i, this)) {
+ res.push(val);
+ }
+ }
+ }
+
+ return res;
+ };
+ }
+
+ if (!Object.keys) {
+ Object.keys = (function() {
+ var hasOwnProperty = Object.prototype.hasOwnProperty,
+ hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
+ dontEnums = [
+ 'toString',
+ 'toLocaleString',
+ 'valueOf',
+ 'hasOwnProperty',
+ 'isPrototypeOf',
+ 'propertyIsEnumerable',
+ 'constructor'
+ ],
+ dontEnumsLength = dontEnums.length;
+
+ return function(obj) {
+ if (typeof obj !== 'object' &&
+ typeof obj !== 'function' ||
+ obj === null
+ ) {
+ throw new TypeError('Object.keys called on non-object');
+ }
+
+ var result = [];
+
+ for (var prop in obj) {
+ if (hasOwnProperty.call(obj, prop)) result.push(prop);
+ }
+
+ if (hasDontEnumBug) {
+ for (var i = 0; i < dontEnumsLength; i++) {
+ if (hasOwnProperty.call(obj, dontEnums[i])) {
+ result.push(dontEnums[i]);
+ }
+ }
+ }
+ return result;
+ }
+ })();
+ }
+})();
+
+/**
+ * A jQuery like properties management class. It works like jQuery .css()
+ * method: You can call it with juste one string to get the corresponding
+ * property, with a string and anything else to set the corresponding property,
+ * or directly with an object, and then each pair string / object (or any type)
+ * will be set in the properties.
+ * @constructor
+ * @this {sigma.classes.Cascade}
+ */
+sigma.classes.Cascade = function() {
+ /**
+ * This instance properties.
+ * @protected
+ * @type {Object}
+ */
+ this.p = {};
+
+ /**
+ * The method to use to set/get any property of this instance.
+ * @param {(string|Object)} a1 If it is a string and if a2 is undefined,
+ * then it will return the corresponding
+ * property.
+ * If it is a string and if a2 is set, then it
+ * will set a2 as the property corresponding to
+ * a1, and return this.
+ * If it is an object, then each pair string /
+ * object (or any other type) will be set as a
+ * property.
+ * @param {*?} a2 The new property corresponding to a1 if a1 is
+ * a string.
+ * @return {(*|sigma.classes.Cascade)} Returns itself or the corresponding
+ * property.
+ */
+ this.config = function(a1, a2) {
+ if (typeof a1 == 'string' && a2 == undefined) {
+ return this.p[a1];
+ } else {
+ var o = (typeof a1 == 'object' && a2 == undefined) ? a1 : {};
+ if (typeof a1 == 'string') {
+ o[a1] = a2;
+ }
+
+ for (var k in o) {
+ if (this.p[k] != undefined) {
+ this.p[k] = o[k];
+ }
+ }
+ return this;
+ }
+ };
+};
+
+/**
+ * sigma.js custom event dispatcher class.
+ * @constructor
+ * @this {sigma.classes.EventDispatcher}
+ */
+sigma.classes.EventDispatcher = function() {
+ /**
+ * An object containing all the different handlers bound to one or many
+ * events, indexed by these events.
+ * @private
+ * @type {Object.<string,Object>}
+ */
+ var _h = {};
+
+ /**
+ * Represents "this", without the well-known scope issue.
+ * @private
+ * @type {sigma.classes.EventDispatcher}
+ */
+ var _self = this;
+
+ /**
+ * Will execute the handler the next (and only the next) time that the
+ * indicated event (or the indicated events) will be triggered.
+ * @param {string} events The name of the event (or the events
+ * separated by spaces).
+ * @param {function(Object)} handler The handler to bind.
+ * @return {sigma.classes.EventDispatcher} Returns itself.
+ */
+ function one(events, handler) {
+ if (!handler || !events) {
+ return _self;
+ }
+
+ var eArray = ((typeof events) == 'string') ? events.split(' ') : events;
+
+ eArray.forEach(function(event) {
+ if (!_h[event]) {
+ _h[event] = [];
+ }
+
+ _h[event].push({
+ 'h': handler,
+ 'one': true
+ });
+ });
+
+ return _self;
+ }
+
+ /**
+ * Will execute the handler everytime that the indicated event (or the
+ * indicated events) will be triggered.
+ * @param {string} events The name of the event (or the events
+ * separated by spaces).
+ * @param {function(Object)} handler The handler to bind.
+ * @return {sigma.classes.EventDispatcher} Returns itself.
+ */
+ function bind(events, handler) {
+ if (!handler || !events) {
+ return _self;
+ }
+
+ var eArray = ((typeof events) == 'string') ? events.split(' ') : events;
+
+ eArray.forEach(function(event) {
+ if (!_h[event]) {
+ _h[event] = [];
+ }
+
+ _h[event].push({
+ 'h': handler,
+ 'one': false
+ });
+ });
+
+ return _self;
+ }
+
+ /**
+ * Unbinds the handler from a specified event (or specified events).
+ * @param {?string} events The name of the event (or the events
+ * separated by spaces). If undefined,
+ * then all handlers are unbound.
+ * @param {?function(Object)} handler The handler to unbind. If undefined,
+ * each handler bound to the event or the
+ * events will be unbound.
+ * @return {sigma.classes.EventDispatcher} Returns itself.
+ */
+ function unbind(events, handler) {
+ if (!events) {
+ _h = {};
+ }
+
+ var eArray = typeof events == 'string' ? events.split(' ') : events;
+
+ if (handler) {
+ eArray.forEach(function(event) {
+ if (_h[event]) {
+ _h[event] = _h[event].filter(function(e) {
+ return e['h'] != handler;
+ });
+ }
+
+ if (_h[event] && _h[event].length == 0) {
+ delete _h[event];
+ }
+ });
+ }else {
+ eArray.forEach(function(event) {
+ delete _h[event];
+ });
+ }
+
+ return _self;
+ }
+
+ /**
+ * Executes each handler bound to the event
+ * @param {string} type The type of the event.
+ * @param {?Object} content The content of the event (optional).
+ * @return {sigma.classes.EventDispatcher} Returns itself.
+ */
+ function dispatch(type, content) {
+ if (_h[type]) {
+ _h[type].forEach(function(e) {
+ e['h']({
+ 'type': type,
+ 'content': content,
+ 'target': _self
+ });
+ });
+
+ _h[type] = _h[type].filter(function(e) {
+ return !e['one'];
+ });
+ }
+
+ return _self;
+ }
+
+ /* PUBLIC INTERFACE: */
+ this.one = one;
+ this.bind = bind;
+ this.unbind = unbind;
+ this.dispatch = dispatch;
+};
+
+(function() {
+// Define local shortcut:
+var id = 0;
+
+// Define local package:
+var local = {};
+local.plugins = [];
+
+sigma.init = function(dom) {
+ var inst = new Sigma(dom, (++id).toString());
+ sigma.instances[id] = new SigmaPublic(inst);
+ return sigma.instances[id];
+};
+
+/**
+ * This class listen to all the different mouse events, to normalize them and
+ * dispatch action events instead (from "startinterpolate" to "isdragging",
+ * etc).
+ * @constructor
+ * @extends sigma.classes.Cascade
+ * @extends sigma.classes.EventDispatcher
+ * @param {element} dom The DOM element to bind the handlers on.
+ * @this {MouseCaptor}
+ */
+function MouseCaptor(dom) {
+ sigma.classes.Cascade.call(this);
+ sigma.classes.EventDispatcher.call(this);
+
+ /**
+ * Represents "this", without the well-known scope issue.
+ * @private
+ * @type {MouseCaptor}
+ */
+ var self = this;
+
+ /**
+ * The DOM element to bind the handlers on.
+ * @type {element}
+ */
+ var dom = dom;
+
+ /**
+ * The different parameters that define how this instance should work.
+ * @see sigma.classes.Cascade
+ * @type {Object}
+ */
+ this.p = {
+ minRatio: 1,
+ maxRatio: 32,
+ marginRatio: 1,
+ zoomDelta: 0.1,
+ dragDelta: 0.3,
+ zoomMultiply: 2,
+ directZooming: false,
+ blockScroll: true,
+ inertia: 1.1,
+ mouseEnabled: true
+ };
+
+ var oldMouseX = 0;
+ var oldMouseY = 0;
+ var startX = 0;
+ var startY = 0;
+
+ var oldStageX = 0;
+ var oldStageY = 0;
+ var oldRatio = 1;
+
+ var targetRatio = 1;
+ var targetStageX = 0;
+ var targetStageY = 0;
+
+ var lastStageX = 0;
+ var lastStageX2 = 0;
+ var lastStageY = 0;
+ var lastStageY2 = 0;
+
+ var progress = 0;
+ var isZooming = false;
+
+ this.stageX = 0;
+ this.stageY = 0;
+ this.ratio = 1;
+
+ this.mouseX = 0;
+ this.mouseY = 0;
+
+ this.isMouseDown = false;
+
+ /**
+ * Extract the local X position from a mouse event.
+ * @private
+ * @param {event} e A mouse event.
+ * @return {number} The local X value of the mouse.
+ */
+ function getX(e) {
+ return e.offsetX != undefined && e.offsetX ||
+ e.layerX != undefined && e.layerX ||
+ e.clientX != undefined && e.clientX;
+ };
+
+ /**
+ * Extract the local Y position from a mouse event.
+ * @private
+ * @param {event} e A mouse event.
+ * @return {number} The local Y value of the mouse.
+ */
+ function getY(e) {
+ return e.offsetY != undefined && e.offsetY ||
+ e.layerY != undefined && e.layerY ||
+ e.clientY != undefined && e.clientY;
+ };
+
+ /**
+ * Extract the wheel delta from a mouse event.
+ * @private
+ * @param {event} e A mouse event.
+ * @return {number} The wheel delta of the mouse.
+ */
+ function getDelta(e) {
+ return e.wheelDelta != undefined && e.wheelDelta ||
+ e.detail != undefined && -e.detail;
+ };
+
+ /**
+ * The handler listening to the 'move' mouse event. It will set the mouseX
+ * and mouseY values as the mouse position values, prevent the default event,
+ * and dispatch a 'move' event.
+ * @private
+ * @param {event} event A 'move' mouse event.
+ */
+ function moveHandler(event) {
+ oldMouseX = self.mouseX;
+ oldMouseY = self.mouseY;
+
+ self.mouseX = getX(event);
+ self.mouseY = getY(event);
+
+ self.isMouseDown && drag(event);
+ self.dispatch('move');
+
+ if (event.preventDefault) {
+ event.preventDefault();
+ } else {
+ event.returnValue = false;
+ }
+ };
+
+ /**
+ * The handler listening to the 'up' mouse event. It will set the isMouseDown
+ * value as false, dispatch a 'mouseup' event, and trigger stopDrag().
+ * @private
+ * @param {event} event A 'up' mouse event.
+ */
+ function upHandler(event) {
+ if (self.p.mouseEnabled && self.isMouseDown) {
+ self.isMouseDown = false;
+ self.dispatch('mouseup');
+ stopDrag();
+
+ if (event.preventDefault) {
+ event.preventDefault();
+ } else {
+ event.returnValue = false;
+ }
+ }
+ };
+
+ /**
+ * The handler listening to the 'down' mouse event. It will set the
+ * isMouseDown value as true, dispatch a 'mousedown' event, and trigger
+ * startDrag().
+ * @private
+ * @param {event} event A 'down' mouse event.
+ */
+ function downHandler(event) {
+ if (self.p.mouseEnabled) {
+ self.isMouseDown = true;
+ oldMouseX = self.mouseX;
+ oldMouseY = self.mouseY;
+
+ self.dispatch('mousedown');
+
+ startDrag();
+
+ if (event.preventDefault) {
+ event.preventDefault();
+ } else {
+ event.returnValue = false;
+ }
+ }
+ };
+
+ /**
+ * The handler listening to the 'wheel' mouse event. It will trigger
+ * {@link startInterpolate} with the event delta as parameter.
+ * @private
+ * @param {event} event A 'wheel' mouse event.
+ */
+ function wheelHandler(event) {
+ if (self.p.mouseEnabled) {
+ startInterpolate(
+ self.mouseX,
+ self.mouseY,
+ self.ratio * (getDelta(event) > 0 ?
+ self.p.zoomMultiply :
+ 1 / self.p.zoomMultiply)
+ );
+
+ if (self.p['blockScroll']) {
+ if (event.preventDefault) {
+ event.preventDefault();
+ } else {
+ event.returnValue = false;
+ }
+ }
+ }
+ };
+
+ /**
+ * Will start computing the scene X and Y, until {@link stopDrag} is
+ * triggered.
+ */
+ function startDrag() {
+ oldStageX = self.stageX;
+ oldStageY = self.stageY;
+ startX = self.mouseX;
+ startY = self.mouseY;
+
+ lastStageX = self.stageX;
+ lastStageX2 = self.stageX;
+ lastStageY = self.stageY;
+ lastStageY2 = self.stageY;
+
+ self.dispatch('startdrag');
+ };
+
+ /**
+ * Stops computing the scene position.
+ */
+ function stopDrag() {
+ if (oldStageX != self.stageX || oldStageY != self.stageY) {
+ startInterpolate(
+ self.stageX + self.p.inertia * (self.stageX - lastStageX2),
+ self.stageY + self.p.inertia * (self.stageY - lastStageY2)
+ );
+ }
+ };
+
+ /**
+ * Computes the position of the scene, relatively to the mouse position, and
+ * dispatches a "drag" event.
+ */
+ function drag() {
+ var newStageX = self.mouseX - startX + oldStageX;
+ var newStageY = self.mouseY - startY + oldStageY;
+
+ if (newStageX != self.stageX || newStageY != self.stageY) {
+ lastStageX2 = lastStageX;
+ lastStageY2 = lastStageY;
+
+ lastStageX = newStageX;
+ lastStageY = newStageY;
+
+ self.stageX = newStageX;
+ self.stageY = newStageY;
+ self.dispatch('drag');
+ }
+ };
+
+ /**
+ * Will start computing the scene zoom ratio, until {@link stopInterpolate} is
+ * triggered.
+ * @param {number} x The new stage X.
+ * @param {number} y The new stage Y.
+ * @param {number} ratio The new zoom ratio.
+ */
+ function startInterpolate(x, y, ratio) {
+ if (self.isMouseDown) {
+ return;
+ }
+
+ window.clearInterval(self.interpolationID);
+ isZooming = ratio != undefined;
+
+ oldStageX = self.stageX;
+ targetStageX = x;
+
+ oldStageY = self.stageY;
+ targetStageY = y;
+
+ oldRatio = self.ratio;
+ targetRatio = ratio || self.ratio;
+ targetRatio = Math.min(
+ Math.max(targetRatio, self.p.minRatio),
+ self.p.maxRatio
+ );
+
+ progress =
+ self.p.directZooming ?
+ 1 - (isZooming ? self.p.zoomDelta : self.p.dragDelta) :
+ 0;
+
+ if (
+ self.ratio != targetRatio ||
+ self.stageX != targetStageX ||
+ self.stageY != targetStageY
+ ) {
+ interpolate();
+ self.interpolationID = window.setInterval(interpolate, 50);
+ self.dispatch('startinterpolate');
+ }
+ };
+
+ /**
+ * Stops the move interpolation.
+ */
+ function stopInterpolate() {
+ var oldRatio = self.ratio;
+
+ if (isZooming) {
+ self.ratio = targetRatio;
+ self.stageX = targetStageX +
+ (self.stageX - targetStageX) *
+ self.ratio /
+ oldRatio;
+ self.stageY = targetStageY +
+ (self.stageY - targetStageY) *
+ self.ratio /
+ oldRatio;
+ }else {
+ self.stageX = targetStageX;
+ self.stageY = targetStageY;
+ }
+
+ self.dispatch('stopinterpolate');
+ };
+
+ /**
+ * Computes the interpolate ratio and the position of the scene, relatively
+ * to the last mouse event delta received, and dispatches a "interpolate"
+ * event.
+ */
+ function interpolate() {
+ progress += (isZooming ? self.p.zoomDelta : self.p.dragDelta);
+ progress = Math.min(progress, 1);
+
+ var k = sigma.easing.quadratic.easeout(progress);
+ var oldRatio = self.ratio;
+
+ self.ratio = oldRatio * (1 - k) + targetRatio * k;
+
+ if (isZooming) {
+ self.stageX = targetStageX +
+ (self.stageX - targetStageX) *
+ self.ratio /
+ oldRatio;
+
+ self.stageY = targetStageY +
+ (self.stageY - targetStageY) *
+ self.ratio /
+ oldRatio;
+ } else {
+ self.stageX = oldStageX * (1 - k) + targetStageX * k;
+ self.stageY = oldStageY * (1 - k) + targetStageY * k;
+ }
+
+ self.dispatch('interpolate');
+ if (progress >= 1) {
+ window.clearInterval(self.interpolationID);
+ stopInterpolate();
+ }
+ };
+
+ /**
+ * Checks that there is always a part of the graph that is displayed, to
+ * avoid the user to drag the graph out of the stage.
+ * @param {Object} b An object containing the borders of the graph.
+ * @param {number} width The width of the stage.
+ * @param {number} height The height of the stage.
+ * @return {MouseCaptor} Returns itself.
+ */
+ function checkBorders(b, width, height) {
+ // TODO : Find the good formula
+ /*if (!isNaN(b.minX) && !isNaN(b.maxX)) {
+ self.stageX = Math.min(
+ self.stageX = Math.max(
+ self.stageX,
+ (b.minX - width) * self.ratio +
+ self.p.marginRatio*(b.maxX - b.minX)
+ ),
+ (b.maxX - width) * self.ratio +
+ width -
+ self.p.marginRatio*(b.maxX - b.minX)
+ );
+ }
+
+ if (!isNaN(b.minY) && !isNaN(b.maxY)) {
+ self.stageY = Math.min(
+ self.stageY = Math.max(
+ self.stageY,
+ (b.minY - height) * self.ratio +
+ self.p.marginRatio*(b.maxY - b.minY)
+ ),
+ (b.maxY - height) * self.ratio +
+ height -
+ self.p.marginRatio*(b.maxY - b.minY)
+ );
+ }*/
+
+ return self;
+ };
+
+ // ADD CALLBACKS
+ dom.addEventListener('DOMMouseScroll', wheelHandler, true);
+ dom.addEventListener('mousewheel', wheelHandler, true);
+ dom.addEventListener('mousemove', moveHandler, true);
+ dom.addEventListener('mousedown', downHandler, true);
+ document.addEventListener('mouseup', upHandler, true);
+
+ this.checkBorders = checkBorders;
+ this.interpolate = startInterpolate;
+}
+
+/**
+ * A class to monitor some local / global probes directly on an instance,
+ * inside a div DOM element.
+ * It executes different methods (called "probes") regularly, and displays
+ * the results on the element.
+ * @constructor
+ * @extends sigma.classes.Cascade
+ * @param {Sigma} instance The instance to monitor.
+ * @param {element} dom The div DOM element to draw write on.
+ * @this {Monitor}
+ */
+function Monitor(instance, dom) {
+ sigma.classes.Cascade.call(this);
+
+ /**
+ * Represents "this", without the well-known scope issue.
+ * @private
+ * @type {Monitor}
+ */
+ var self = this;
+
+ /**
+ * {@link Sigma} instance owning this Monitor instance.
+ * @type {Sigma}
+ */
+ this.instance = instance;
+
+ /**
+ * Determines if the monitoring is activated or not.
+ * @type {Boolean}
+ */
+ this.monitoring = false;
+
+ /**
+ * The different parameters that define how this instance should work. It
+ * also contains the different probes.
+ * @see sigma.classes.Cascade
+ * @type {Object}
+ */
+ this.p = {
+ fps: 40,
+ dom: dom,
+ globalProbes: {
+ 'Time (ms)': sigma.chronos.getExecutionTime,
+ 'Queue': sigma.chronos.getQueuedTasksCount,
+ 'Tasks': sigma.chronos.getTasksCount,
+ 'FPS': sigma.chronos.getFPS
+ },
+ localProbes: {
+ 'Nodes count': function() { return self.instance.graph.nodes.length; },
+ 'Edges count': function() { return self.instance.graph.edges.length; }
+ }
+ };
+
+ /**
+ * Activates the monitoring: Some texts describing some values about sigma.js
+ * or the owning {@link Sigma} instance will appear over the graph, but
+ * beneath the mouse sensible DOM element.
+ * @return {Monitor} Returns itself.
+ */
+ function activate() {
+ if (!self.monitoring) {
+ self.monitoring = window.setInterval(routine, 1000 / self.p.fps);
+ }
+
+ return self;
+ }
+
+ /**
+ * Desactivates the monitoring: Will disappear, and stop computing the
+ * different probes.
+ * @return {Monitor} Returns itself.
+ */
+ function desactivate() {
+ if (self.monitoring) {
+ window.clearInterval(self.monitoring);
+ self.monitoring = null;
+
+ self.p.dom.innerHTML = '';
+ }
+
+ return self;
+ }
+
+ /**
+ * The private method dedicated to compute the different values to observe.
+ * @private
+ * @return {Monitor} Returns itself.
+ */
+ function routine() {
+ var s = '';
+
+ s += '<p>GLOBAL :</p>';
+ for (var k in self.p.globalProbes) {
+ s += '<p>' + k + ' : ' + self.p.globalProbes[k]() + '</p>';
+ }
+
+ s += '<br><p>LOCAL :</p>';
+ for (var k in self.p.localProbes) {
+ s += '<p>' + k + ' : ' + self.p.localProbes[k]() + '</p>';
+ }
+
+ self.p.dom.innerHTML = s;
+
+ return self;
+ }
+
+ this.activate = activate;
+ this.desactivate = desactivate;
+}
+
+/**
+ * Sigma is the main class. It represents the core of any instance id sigma.js.
+ * It is private and can be initialized only from inside sigma.js. To see its
+ * public interface, see {@link SigmaPublic}.
+ * It owns its own {@link Graph}, {@link MouseCaptor}, {@link Plotter}
+ * and {@link Monitor}.
+ * @constructor
+ * @extends sigma.classes.Cascade
+ * @extends sigma.classes.EventDispatcher
+ * @param {element} root The DOM root of this instance (a div, for example).
+ * @param {string} id The ID of this instance.
+ * @this {Sigma}
+ */
+function Sigma(root, id) {
+ sigma.classes.Cascade.call(this);
+ sigma.classes.EventDispatcher.call(this);
+
+ /**
+ * Represents "this", without the well-known scope issue.
+ * @private
+ * @type {Sigma}
+ */
+ var self = this;
+
+ /**
+ * The ID of the instance.
+ * @type {string}
+ */
+ this.id = id.toString();
+
+ /**
+ * The different parameters that define how this instance should work.
+ * @see sigma.classes.Cascade
+ * @type {Object}
+ */
+ this.p = {
+ auto: true,
+ drawNodes: 2,
+ drawEdges: 1,
+ drawLabels: 2,
+ lastNodes: 2,
+ lastEdges: 0,
+ lastLabels: 2,
+ drawHoverNodes: true,
+ drawActiveNodes: true
+ };
+
+ /**
+ * The root DOM element of this instance, containing every other elements.
+ * @type {element}
+ */
+ this.domRoot = root;
+
+ /**
+ * The width of this instance - initially, the root's width.
+ * @type {number}
+ */
+ this.width = this.domRoot.offsetWidth;
+
+ /**
+ * The height of this instance - initially, the root's height.
+ * @type {number}
+ */
+ this.height = this.domRoot.offsetHeight;
+
+ /**
+ * The graph of this instance - initiallyempty.
+ * @type {Graph}
+ */
+ this.graph = new Graph();
+
+ /**
+ * An object referencing every DOM elements used by this instance.
+ * @type {Object}
+ */
+ this.domElements = {};
+
+ initDOM('edges', 'canvas');
+ initDOM('nodes', 'canvas');
+ initDOM('labels', 'canvas');
+ initDOM('hover', 'canvas');
+ initDOM('monitor', 'div');
+ initDOM('mouse', 'canvas');
+
+ /**
+ * The class dedicated to manage the drawing process of the graph of the
+ * different canvas.
+ * @type {Plotter}
+ */
+ this.plotter = new Plotter(
+ this.domElements.nodes.getContext('2d'),
+ this.domElements.edges.getContext('2d'),
+ this.domElements.labels.getContext('2d'),
+ this.domElements.hover.getContext('2d'),
+ this.graph,
+ this.width,
+ this.height
+ );
+
+ /**
+ * The class dedicated to monitor different probes about the running
+ * processes or the data, such as the number of nodes or edges, or how
+ * many times the graph is drawn per second.
+ * @type {Monitor}
+ */
+ this.monitor = new Monitor(
+ this,
+ this.domElements.monitor
+ );
+
+ /**
+ * The class dedicated to manage the different mouse events.
+ * @type {MouseCaptor}
+ */
+ this.mousecaptor = new MouseCaptor(
+ this.domElements.mouse,
+ this.id
+ );
+
+ // Interaction listeners:
+ this.mousecaptor.bind('drag interpolate', function(e) {
+ self.draw(
+ self.p.auto ? 2 : self.p.drawNodes,
+ self.p.auto ? 0 : self.p.drawEdges,
+ self.p.auto ? 2 : self.p.drawLabels,
+ true
+ );
+ }).bind('stopdrag stopinterpolate', function(e) {
+ self.draw(
+ self.p.auto ? 2 : self.p.drawNodes,
+ self.p.auto ? 1 : self.p.drawEdges,
+ self.p.auto ? 2 : self.p.drawLabels,
+ true
+ );
+ }).bind('mousedown mouseup', function(e) {
+ var targeted = self.graph.nodes.filter(function(n) {
+ return !!n['hover'];
+ }).map(function(n) {
+ return n.id;
+ });
+
+ self.dispatch(
+ e['type'] == 'mousedown' ?
+ 'downgraph' :
+ 'upgraph'
+ );
+
+ if (targeted.length) {
+ self.dispatch(
+ e['type'] == 'mousedown' ?
+ 'downnodes' :
+ 'upnodes',
+ targeted
+ );
+ }
+ }).bind('move', function() {
+ self.domElements.hover.getContext('2d').clearRect(
+ 0,
+ 0,
+ self.domElements.hover.width,
+ self.domElements.hover.height
+ );
+
+ drawHover();
+ drawActive();
+ });
+
+ sigma.chronos.bind('startgenerators', function() {
+ if (sigma.chronos.getGeneratorsIDs().some(function(id) {
+ return !!id.match(new RegExp('_ext_' + self.id + '$', ''));
+ })) {
+ self.draw(
+ self.p.auto ? 2 : self.p.drawNodes,
+ self.p.auto ? 0 : self.p.drawEdges,
+ self.p.auto ? 2 : self.p.drawLabels
+ );
+ }
+ }).bind('stopgenerators', function() {
+ self.draw();
+ });
+
+ /**
+ * Resizes the element, and redraws the graph with the last settings.
+ * @param {?number} w The new width (if undefined, it will use the root
+ * width).
+ * @param {?number} h The new height (if undefined, it will use the root
+ * height).
+ * @return {Sigma} Returns itself.
+ */
+ function resize(w, h) {
+ var oldW = self.width, oldH = self.height;
+
+ if (w != undefined && h != undefined) {
+ self.width = w;
+ self.height = h;
+ }else {
+ self.width = self.domRoot.offsetWidth;
+ self.height = self.domRoot.offsetHeight;
+ }
+
+ if (oldW != self.width || oldH != self.height) {
+ for (var k in self.domElements) {
+ self.domElements[k].setAttribute('width', self.width + 'px');
+ self.domElements[k].setAttribute('height', self.height + 'px');
+ }
+
+ self.plotter.resize(self.width, self.height);
+
+ self.draw(
+ self.p.lastNodes,
+ self.p.lastEdges,
+ self.p.lastLabels,
+ true
+ );
+ }
+ return self;
+ };
+
+ /**
+ * Kills every drawing task currently running. Basically, it stops this
+ * instance's drawing process.
+ * @return {Sigma} Returns itself.
+ */
+ function clearSchedule() {
+ sigma.chronos.removeTask(
+ 'node_' + self.id, 2
+ ).removeTask(
+ 'edge_' + self.id, 2
+ ).removeTask(
+ 'label_' + self.id, 2
+ ).stopTasks();
+ return self;
+ };
+
+ /**
+ * Initialize a DOM element, that will be stores by this instance, to make
+ * automatic these elements resizing.
+ * @private
+ * @param {string} id The element's ID.
+ * @param {string} type The element's nodeName (Example : canvas, div, ...).
+ * @return {Sigma} Returns itself.
+ */
+ function initDOM(id, type) {
+ self.domElements[id] = document.createElement(type);
+ self.domElements[id].style.position = 'absolute';
+ self.domElements[id].setAttribute('id', 'sigma_' + id + '_' + self.id);
+ self.domElements[id].setAttribute('class', 'sigma_' + id + '_' + type);
+ self.domElements[id].setAttribute('width', self.width + 'px');
+ self.domElements[id].setAttribute('height', self.height + 'px');
+
+ self.domRoot.appendChild(self.domElements[id]);
+ return self;
+ };
+
+ /**
+ * Starts the graph drawing process. The three first parameters indicate
+ * how the different layers have to be drawn:
+ * . -1: The layer is not drawn, but it is not erased.
+ * . 0: The layer is not drawn.
+ * . 1: The layer is drawn progressively.
+ * . 2: The layer is drawn directly.
+ * @param {?number} nodes Determines if and how the nodes must be drawn.
+ * @param {?number} edges Determines if and how the edges must be drawn.
+ * @param {?number} labels Determines if and how the labels must be drawn.
+ * @param {?boolean} safe If true, nothing will happen if any generator
+ * affiliated to this instance is currently running
+ * (an iterative layout, for example).
+ * @return {Sigma} Returns itself.
+ */
+ function draw(nodes, edges, labels, safe) {
+ if (safe && sigma.chronos.getGeneratorsIDs().some(function(id) {
+ return !!id.match(new RegExp('_ext_' + self.id + '$', ''));
+ })) {
+ return self;
+ }
+
+ var n = (nodes == undefined) ? self.p.drawNodes : nodes;
+ var e = (edges == undefined) ? self.p.drawEdges : edges;
+ var l = (labels == undefined) ? self.p.drawLabels : labels;
+
+ var params = {
+ nodes: n,
+ edges: e,
+ labels: l
+ };
+
+ self.p.lastNodes = n;
+ self.p.lastEdges = e;
+ self.p.lastLabels = l;
+
+ // Remove tasks:
+ clearSchedule();
+
+ // Rescale graph:
+ self.graph.rescale(
+ self.width,
+ self.height,
+ n > 0,
+ e > 0
+ ).setBorders();
+
+ self.mousecaptor.checkBorders(
+ self.graph.borders,
+ self.width,
+ self.height
+ );
+
+ self.graph.translate(
+ self.mousecaptor.stageX,
+ self.mousecaptor.stageY,
+ self.mousecaptor.ratio,
+ n > 0,
+ e > 0
+ );
+
+ self.dispatch(
+ 'graphscaled'
+ );
+
+ // Clear scene:
+ for (var k in self.domElements) {
+ if (
+ self.domElements[k].nodeName.toLowerCase() == 'canvas' &&
+ (params[k] == undefined || params[k] >= 0)
+ ) {
+ self.domElements[k].getContext('2d').clearRect(
+ 0,
+ 0,
+ self.domElements[k].width,
+ self.domElements[k].height
+ );
+ }
+ }
+
+ self.plotter.currentEdgeIndex = 0;
+ self.plotter.currentNodeIndex = 0;
+ self.plotter.currentLabelIndex = 0;
+
+ var previous = null;
+ var start = false;
+
+ if (n) {
+ if (n > 1) {
+ while (self.plotter.task_drawNode()) {}
+ }else {
+ sigma.chronos.addTask(
+ self.plotter.task_drawNode,
+ 'node_' + self.id,
+ false
+ );
+
+ start = true;
+ previous = 'node_' + self.id;
+ }
+ }
+
+ if (l) {
+ if (l > 1) {
+ while (self.plotter.task_drawLabel()) {}
+ } else {
+ if (previous) {
+ sigma.chronos.queueTask(
+ self.plotter.task_drawLabel,
+ 'label_' + self.id,
+ previous
+ );
+ } else {
+ sigma.chronos.addTask(
+ self.plotter.task_drawLabel,
+ 'label_' + self.id,
+ false
+ );
+ }
+
+ start = true;
+ previous = 'label_' + self.id;
+ }
+ }
+
+ if (e) {
+ if (e > 1) {
+ while (self.plotter.task_drawEdge()) {}
+ }else {
+ if (previous) {
+ sigma.chronos.queueTask(
+ self.plotter.task_drawEdge,
+ 'edge_' + self.id,
+ previous
+ );
+ }else {
+ sigma.chronos.addTask(
+ self.plotter.task_drawEdge,
+ 'edge_' + self.id,
+ false
+ );
+ }
+
+ start = true;
+ previous = 'edge_' + self.id;
+ }
+ }
+
+ self.dispatch(
+ 'draw'
+ );
+
+ self.refresh();
+
+ start && sigma.chronos.runTasks();
+ return self;
+ };
+
+ /**
+ * Draws the hover and active nodes labels.
+ * @return {Sigma} Returns itself.
+ */
+ function refresh() {
+ self.domElements.hover.getContext('2d').clearRect(
+ 0,
+ 0,
+ self.domElements.hover.width,
+ self.domElements.hover.height
+ );
+
+ drawHover();
+ drawActive();
+
+ return self;
+ }
+
+ /**
+ * Draws the hover nodes labels. This method is applied directly, and does
+ * not use the pseudo-asynchronous tasks process.
+ * @return {Sigma} Returns itself.
+ */
+ function drawHover() {
+ if (self.p.drawHoverNodes) {
+ self.graph.checkHover(
+ self.mousecaptor.mouseX,
+ self.mousecaptor.mouseY
+ );
+
+ self.graph.nodes.forEach(function(node) {
+ if (node.hover && !node.active) {
+ self.plotter.drawHoverNode(node);
+ }
+ });
+ }
+
+ return self;
+ }
+
+ /**
+ * Draws the active nodes labels. This method is applied directly, and does
+ * not use the pseudo-asynchronous tasks process.
+ * @return {Sigma} Returns itself.
+ */
+ function drawActive() {
+ if (self.p.drawActiveNodes) {
+ self.graph.nodes.forEach(function(node) {
+ if (node.active) {
+ self.plotter.drawActiveNode(node);
+ }
+ });
+ }
+
+ return self;
+ }
+
+ // Apply plugins:
+ for (var i = 0; i < local.plugins.length; i++) {
+ local.plugins[i](this);
+ }
+
+ this.draw = draw;
+ this.resize = resize;
+ this.refresh = refresh;
+ this.drawHover = drawHover;
+ this.drawActive = drawActive;
+ this.clearSchedule = clearSchedule;
+
+ window.addEventListener('resize', function() {
+ self.resize();
+ });
+}
+
+/**
+ * This class draws the graph on the different canvas DOM elements. It just
+ * contains all the different methods to draw the graph, synchronously or
+ * pseudo-asynchronously.
+ * @constructor
+ * @param {CanvasRenderingContext2D} nodesCtx Context dedicated to draw nodes.
+ * @param {CanvasRenderingContext2D} edgesCtx Context dedicated to draw edges.
+ * @param {CanvasRenderingContext2D} labelsCtx Context dedicated to draw
+ * labels.
+ * @param {CanvasRenderingContext2D} hoverCtx Context dedicated to draw hover
+ * nodes labels.
+ * @param {Graph} graph A reference to the graph to
+ * draw.
+ * @param {number} w The width of the DOM root
+ * element.
+ * @param {number} h The width of the DOM root
+ * element.
+ * @extends sigma.classes.Cascade
+ * @this {Plotter}
+ */
+function Plotter(nodesCtx, edgesCtx, labelsCtx, hoverCtx, graph, w, h) {
+ sigma.classes.Cascade.call(this);
+
+ /**
+ * Represents "this", without the well-known scope issue.
+ * @private
+ * @type {Plotter}
+ */
+ var self = this;
+
+ /**
+ * The different parameters that define how this instance should work.
+ * @see sigma.classes.Cascade
+ * @type {Object}
+ */
+ this.p = {
+ // -------
+ // LABELS:
+ // -------
+ // Label color:
+ // - 'node'
+ // - default (then defaultLabelColor
+ // will be used instead)
+ labelColor: 'default',
+ defaultLabelColor: '#000',
+ // Label hover background color:
+ // - 'node'
+ // - default (then defaultHoverLabelBGColor
+ // will be used instead)
+ labelHoverBGColor: 'default',
+ defaultHoverLabelBGColor: '#fff',
+ // Label hover shadow:
+ labelHoverShadow: true,
+ labelHoverShadowColor: '#000',
+ // Label hover color:
+ // - 'node'
+ // - default (then defaultLabelHoverColor
+ // will be used instead)
+ labelHoverColor: 'default',
+ defaultLabelHoverColor: '#000',
+ // Label active background color:
+ // - 'node'
+ // - default (then defaultActiveLabelBGColor
+ // will be used instead)
+ labelActiveBGColor: 'default',
+ defaultActiveLabelBGColor: '#fff',
+ // Label active shadow:
+ labelActiveShadow: true,
+ labelActiveShadowColor: '#000',
+ // Label active color:
+ // - 'node'
+ // - default (then defaultLabelActiveColor
+ // will be used instead)
+ labelActiveColor: 'default',
+ defaultLabelActiveColor: '#000',
+ // Label size:
+ // - 'fixed'
+ // - 'proportional'
+ // Label size:
+ // - 'fixed'
+ // - 'proportional'
+ labelSize: 'fixed',
+ defaultLabelSize: 12, // for fixed display only
+ labelSizeRatio: 2, // for proportional display only
+ labelThreshold: 6,
+ font: 'Arial',
+ hoverFont: '',
+ activeFont: '',
+ fontStyle: '',
+ hoverFontStyle: '',
+ activeFontStyle: '',
+ // ------
+ // EDGES:
+ // ------
+ // Edge color:
+ // - 'source'
+ // - 'target'
+ // - default (then defaultEdgeColor or edge['color']
+ // will be used instead)
+ edgeColor: 'source',
+ defaultEdgeColor: '#aaa',
+ defaultEdgeType: 'line',
+ // ------
+ // NODES:
+ // ------
+ defaultNodeColor: '#aaa',
+ // HOVER:
+ // Node hover color:
+ // - 'node'
+ // - default (then defaultNodeHoverColor
+ // will be used instead)
+ nodeHoverColor: 'node',
+ defaultNodeHoverColor: '#fff',
+ // ACTIVE:
+ // Node active color:
+ // - 'node'
+ // - default (then defaultNodeActiveColor
+ // will be used instead)
+ nodeActiveColor: 'node',
+ defaultNodeActiveColor: '#fff',
+ // Node border color:
+ // - 'node'
+ // - default (then defaultNodeBorderColor
+ // will be used instead)
+ borderSize: 0,
+ nodeBorderColor: 'node',
+ defaultNodeBorderColor: '#fff',
+ // --------
+ // PROCESS:
+ // --------
+ edgesSpeed: 200,
+ nodesSpeed: 200,
+ labelsSpeed: 200
+ };
+
+ /**
+ * The canvas context dedicated to draw the nodes.
+ * @type {CanvasRenderingContext2D}
+ */
+ var nodesCtx = nodesCtx;
+
+ /**
+ * The canvas context dedicated to draw the edges.
+ * @type {CanvasRenderingContext2D}
+ */
+ var edgesCtx = edgesCtx;
+
+ /**
+ * The canvas context dedicated to draw the labels.
+ * @type {CanvasRenderingContext2D}
+ */
+ var labelsCtx = labelsCtx;
+
+ /**
+ * The canvas context dedicated to draw the hover nodes.
+ * @type {CanvasRenderingContext2D}
+ */
+ var hoverCtx = hoverCtx;
+
+ /**
+ * A reference to the graph to draw.
+ * @type {Graph}
+ */
+ var graph = graph;
+
+ /**
+ * The width of the stage to draw on.
+ * @type {number}
+ */
+ var width = w;
+
+ /**
+ * The height of the stage to draw on.
+ * @type {number}
+ */
+ var height = h;
+
+ /**
+ * The index of the next edge to draw.
+ * @type {number}
+ */
+ this.currentEdgeIndex = 0;
+
+ /**
+ * The index of the next node to draw.
+ * @type {number}
+ */
+ this.currentNodeIndex = 0;
+
+ /**
+ * The index of the next label to draw.
+ * @type {number}
+ */
+ this.currentLabelIndex = 0;
+
+ /**
+ * An atomic function to drawn the N next edges, with N as edgesSpeed.
+ * The counter is {@link this.currentEdgeIndex}.
+ * This function has been designed to work with {@link sigma.chronos}, that
+ * will insert frames at the middle of the calls, to make the edges drawing
+ * process fluid for the user.
+ * @see sigma.chronos
+ * @return {boolean} Returns true if all the edges are drawn and false else.
+ */
+ function task_drawEdge() {
+ var c = graph.edges.length;
+ var s, t, i = 0;
+
+ while (i++< self.p.edgesSpeed && self.currentEdgeIndex < c) {
+ e = graph.edges[self.currentEdgeIndex];
+ s = e['source'];
+ t = e['target'];
+ if (e['hidden'] ||
+ s['hidden'] ||
+ t['hidden'] ||
+ (!self.isOnScreen(s) && !self.isOnScreen(t))) {
+ self.currentEdgeIndex++;
+ }else {
+ drawEdge(graph.edges[self.currentEdgeIndex++]);
+ }
+ }
+
+ return self.currentEdgeIndex < c;
+ };
+
+ /**
+ * An atomic function to drawn the N next nodes, with N as nodesSpeed.
+ * The counter is {@link this.currentEdgeIndex}.
+ * This function has been designed to work with {@link sigma.chronos}, that
+ * will insert frames at the middle of the calls, to make the nodes drawing
+ * process fluid for the user.
+ * @see sigma.chronos
+ * @return {boolean} Returns true if all the nodes are drawn and false else.
+ */
+ function task_drawNode() {
+ var c = graph.nodes.length;
+ var i = 0;
+
+ while (i++< self.p.nodesSpeed && self.currentNodeIndex < c) {
+ if (!self.isOnScreen(graph.nodes[self.currentNodeIndex])) {
+ self.currentNodeIndex++;
+ }else {
+ drawNode(graph.nodes[self.currentNodeIndex++]);
+ }
+ }
+
+ return self.currentNodeIndex < c;
+ };
+
+ /**
+ * An atomic function to drawn the N next labels, with N as labelsSpeed.
+ * The counter is {@link this.currentEdgeIndex}.
+ * This function has been designed to work with {@link sigma.chronos}, that
+ * will insert frames at the middle of the calls, to make the labels drawing
+ * process fluid for the user.
+ * @see sigma.chronos
+ * @return {boolean} Returns true if all the labels are drawn and false else.
+ */
+ function task_drawLabel() {
+ var c = graph.nodes.length;
+ var i = 0;
+
+ while (i++< self.p.labelsSpeed && self.currentLabelIndex < c) {
+ if (!self.isOnScreen(graph.nodes[self.currentLabelIndex])) {
+ self.currentLabelIndex++;
+ }else {
+ drawLabel(graph.nodes[self.currentLabelIndex++]);
+ }
+ }
+
+ return self.currentLabelIndex < c;
+ };
+
+ /**
+ * Draws one node to the corresponding canvas.
+ * @param {Object} node The node to draw.
+ * @return {Plotter} Returns itself.
+ */
+ function drawNode(node) {
+ var size = Math.round(node['displaySize'] * 10) / 10;
+ var ctx = nodesCtx;
+
+ ctx.fillStyle = node['color'];
+ ctx.beginPath();
+ ctx.arc(node['displayX'],
+ node['displayY'],
+ size,
+ 0,
+ Math.PI * 2,
+ true);
+
+ ctx.closePath();
+ ctx.fill();
+
+ node['hover'] && drawHoverNode(node);
+ return self;
+ };
+
+ /**
+ * Draws one edge to the corresponding canvas.
+ * @param {Object} edge The edge to draw.
+ * @return {Plotter} Returns itself.
+ */
+ function drawEdge(edge) {
+ var x1 = edge['source']['displayX'];
+ var y1 = edge['source']['displayY'];
+ var x2 = edge['target']['displayX'];
+ var y2 = edge['target']['displayY'];
+ var color = edge['color'];
+
+ if (!color) {
+ switch (self.p.edgeColor) {
+ case 'source':
+ color = edge['source']['color'] ||
+ self.p.defaultNodeColor;
+ break;
+ case 'target':
+ color = edge['target']['color'] ||
+ self.p.defaultNodeColor;
+ break;
+ default:
+ color = self.p.defaultEdgeColor;
+ break;
+ }
+ }
+
+ var ctx = edgesCtx;
+
+ switch (edge['type'] || self.p.defaultEdgeType) {
+ case 'curve':
+ ctx.strokeStyle = color;
+ ctx.lineWidth = edge['displaySize'] / 3;
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.quadraticCurveTo((x1 + x2) / 2 + (y2 - y1) / 4,
+ (y1 + y2) / 2 + (x1 - x2) / 4,
+ x2,
+ y2);
+ ctx.stroke();
+ break;
+ case 'line':
+ default:
+ ctx.strokeStyle = color;
+ ctx.lineWidth = edge['displaySize'] / 3;
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x2, y2);
+
+ ctx.stroke();
+ break;
+ }
+
+ return self;
+ };
+
+ /**
+ * Draws one label to the corresponding canvas.
+ * @param {Object} node The label to draw.
+ * @return {Plotter} Returns itself.
+ */
+ function drawLabel(node) {
+ var ctx = labelsCtx;
+
+ if (node['displaySize'] >= self.p.labelThreshold || node['forceLabel']) {
+ var fontSize = self.p.labelSize == 'fixed' ?
+ self.p.defaultLabelSize :
+ self.p.labelSizeRatio * node['displaySize'];
+
+ ctx.font = self.p.fontStyle + fontSize + 'px ' + self.p.font;
+
+ ctx.fillStyle = self.p.labelColor == 'node' ?
+ (node['color'] || self.p.defaultNodeColor) :
+ self.p.defaultLabelColor;
+ ctx.fillText(
+ node['label'],
+ Math.round(node['displayX'] + node['displaySize'] * 1.5),
+ Math.round(node['displayY'] + fontSize / 2 - 3)
+ );
+ }
+
+ return self;
+ };
+
+ /**
+ * Draws one hover node to the corresponding canvas.
+ * @param {Object} node The hover node to draw.
+ * @return {Plotter} Returns itself.
+ */
+ function drawHoverNode(node) {
+ var ctx = hoverCtx;
+
+ var fontSize = self.p.labelSize == 'fixed' ?
+ self.p.defaultLabelSize :
+ self.p.labelSizeRatio * node['displaySize'];
+
+ ctx.font = (self.p.hoverFontStyle || self.p.fontStyle || '') + ' ' +
+ fontSize + 'px ' +
+ (self.p.hoverFont || self.p.font || '');
+
+ ctx.fillStyle = self.p.labelHoverBGColor == 'node' ?
+ (node['color'] || self.p.defaultNodeColor) :
+ self.p.defaultHoverLabelBGColor;
+
+ // Label background:
+ ctx.beginPath();
+
+ if (self.p.labelHoverShadow) {
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 0;
+ ctx.shadowBlur = 4;
+ ctx.shadowColor = self.p.labelHoverShadowColor;
+ }
+
+ sigma.tools.drawRoundRect(
+ ctx,
+ Math.round(node['displayX'] - fontSize / 2 - 2),
+ Math.round(node['displayY'] - fontSize / 2 - 2),
+ Math.round(ctx.measureText(node['label']).width +
+ node['displaySize'] * 1.5 +
+ fontSize / 2 + 4),
+ Math.round(fontSize + 4),
+ Math.round(fontSize / 2 + 2),
+ 'left'
+ );
+ ctx.closePath();
+ ctx.fill();
+
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 0;
+ ctx.shadowBlur = 0;
+
+ // Node border:
+ ctx.beginPath();
+ ctx.fillStyle = self.p.nodeBorderColor == 'node' ?
+ (node['color'] || self.p.defaultNodeColor) :
+ self.p.defaultNodeBorderColor;
+ ctx.arc(Math.round(node['displayX']),
+ Math.round(node['displayY']),
+ node['displaySize'] + self.p.borderSize,
+ 0,
+ Math.PI * 2,
+ true);
+ ctx.closePath();
+ ctx.fill();
+
+ // Node:
+ ctx.beginPath();
+ ctx.fillStyle = self.p.nodeHoverColor == 'node' ?
+ (node['color'] || self.p.defaultNodeColor) :
+ self.p.defaultNodeHoverColor;
+ ctx.arc(Math.round(node['displayX']),
+ Math.round(node['displayY']),
+ node['displaySize'],
+ 0,
+ Math.PI * 2,
+ true);
+
+ ctx.closePath();
+ ctx.fill();
+
+ // Label:
+ ctx.fillStyle = self.p.labelHoverColor == 'node' ?
+ (node['color'] || self.p.defaultNodeColor) :
+ self.p.defaultLabelHoverColor;
+ ctx.fillText(
+ node['label'],
+ Math.round(node['displayX'] + node['displaySize'] * 1.5),
+ Math.round(node['displayY'] + fontSize / 2 - 3)
+ );
+
+ return self;
+ };
+
+ /**
+ * Draws one active node to the corresponding canvas.
+ * @param {Object} node The active node to draw.
+ * @return {Plotter} Returns itself.
+ */
+ function drawActiveNode(node) {
+ var ctx = hoverCtx;
+
+ if (!isOnScreen(node)) {
+ return self;
+ }
+
+ var fontSize = self.p.labelSize == 'fixed' ?
+ self.p.defaultLabelSize :
+ self.p.labelSizeRatio * node['displaySize'];
+
+ ctx.font = (self.p.activeFontStyle || self.p.fontStyle || '') + ' ' +
+ fontSize + 'px ' +
+ (self.p.activeFont || self.p.font || '');
+
+ ctx.fillStyle = self.p.labelHoverBGColor == 'node' ?
+ (node['color'] || self.p.defaultNodeColor) :
+ self.p.defaultActiveLabelBGColor;
+
+ // Label background:
+ ctx.beginPath();
+
+ if (self.p.labelActiveShadow) {
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 0;
+ ctx.shadowBlur = 4;
+ ctx.shadowColor = self.p.labelActiveShadowColor;
+ }
+
+ sigma.tools.drawRoundRect(
+ ctx,
+ Math.round(node['displayX'] - fontSize / 2 - 2),
+ Math.round(node['displayY'] - fontSize / 2 - 2),
+ Math.round(ctx.measureText(node['label']).width +
+ node['displaySize'] * 1.5 +
+ fontSize / 2 + 4),
+ Math.round(fontSize + 4),
+ Math.round(fontSize / 2 + 2),
+ 'left'
+ );
+ ctx.closePath();
+ ctx.fill();
+
+ ctx.shadowOffsetX = 0;
+ ctx.shadowOffsetY = 0;
+ ctx.shadowBlur = 0;
+
+ // Node border:
+ ctx.beginPath();
+ ctx.fillStyle = self.p.nodeBorderColor == 'node' ?
+ (node['color'] || self.p.defaultNodeColor) :
+ self.p.defaultNodeBorderColor;
+ ctx.arc(Math.round(node['displayX']),
+ Math.round(node['displayY']),
+ node['displaySize'] + self.p.borderSize,
+ 0,
+ Math.PI * 2,
+ true);
+ ctx.closePath();
+ ctx.fill();
+
+ // Node:
+ ctx.beginPath();
+ ctx.fillStyle = self.p.nodeActiveColor == 'node' ?
+ (node['color'] || self.p.defaultNodeColor) :
+ self.p.defaultNodeActiveColor;
+ ctx.arc(Math.round(node['displayX']),
+ Math.round(node['displayY']),
+ node['displaySize'],
+ 0,
+ Math.PI * 2,
+ true);
+
+ ctx.closePath();
+ ctx.fill();
+
+ // Label:
+ ctx.fillStyle = self.p.labelActiveColor == 'node' ?
+ (node['color'] || self.p.defaultNodeColor) :
+ self.p.defaultLabelActiveColor;
+ ctx.fillText(
+ node['label'],
+ Math.round(node['displayX'] + node['displaySize'] * 1.5),
+ Math.round(node['displayY'] + fontSize / 2 - 3)
+ );
+
+ return self;
+ };
+
+ /**
+ * Determines if a node is on the screen or not. The limits here are
+ * bigger than the actual screen, to avoid seeing labels disappear during
+ * the graph manipulation.
+ * @param {Object} node The node to check if it is on or out the screen.
+ * @return {boolean} Returns false if the node is hidden or not on the screen
+ * or true else.
+ */
+ function isOnScreen(node) {
+ if (isNaN(node['x']) || isNaN(node['y'])) {
+ throw (new Error('A node\'s coordinate is not a ' +
+ 'number (id: ' + node['id'] + ')')
+ );
+ }
+
+ return !node['hidden'] &&
+ (node['displayX'] + node['displaySize'] > -width / 3) &&
+ (node['displayX'] - node['displaySize'] < width * 4 / 3) &&
+ (node['displayY'] + node['displaySize'] > -height / 3) &&
+ (node['displayY'] - node['displaySize'] < height * 4 / 3);
+ };
+
+ /**
+ * Resizes this instance.
+ * @param {number} w The new width.
+ * @param {number} h The new height.
+ * @return {Plotter} Returns itself.
+ */
+ function resize(w, h) {
+ width = w;
+ height = h;
+
+ return self;
+ }
+
+ this.task_drawLabel = task_drawLabel;
+ this.task_drawEdge = task_drawEdge;
+ this.task_drawNode = task_drawNode;
+ this.drawActiveNode = drawActiveNode;
+ this.drawHoverNode = drawHoverNode;
+ this.isOnScreen = isOnScreen;
+ this.resize = resize;
+}
+
+function SigmaPublic(sigmaInstance) {
+ var s = sigmaInstance;
+ var self = this;
+ sigma.classes.EventDispatcher.call(this);
+
+ this._core = sigmaInstance;
+
+ this.kill = function() {
+ // TODO
+ };
+
+ this.getID = function() {
+ return s.id;
+ };
+
+ // Config:
+ this.configProperties = function(a1, a2) {
+ var res = s.config(a1, a2);
+ return res == s ? self : res;
+ };
+
+ this.drawingProperties = function(a1, a2) {
+ var res = s.plotter.config(a1, a2);
+ return res == s.plotter ? self : res;
+ };
+
+ this.mouseProperties = function(a1, a2) {
+ var res = s.mousecaptor.config(a1, a2);
+ return res == s.mousecaptor ? self : res;
+ };
+
+ this.graphProperties = function(a1, a2) {
+ var res = s.graph.config(a1, a2);
+ return res == s.graph ? self : res;
+ };
+
+ this.getMouse = function() {
+ return {
+ mouseX: s.mousecaptor.mouseX,
+ mouseY: s.mousecaptor.mouseY,
+ down: s.mousecaptor.isMouseDown
+ };
+ };
+
+ // Actions:
+ this.position = function(stageX, stageY, ratio) {
+ if (arguments.length == 0) {
+ return {
+ stageX: s.mousecaptor.stageX,
+ stageY: s.mousecaptor.stageY,
+ ratio: s.mousecaptor.ratio
+ };
+ }else {
+ s.mousecaptor.stageX = stageX != undefined ?
+ stageX :
+ s.mousecaptor.stageX;
+ s.mousecaptor.stageY = stageY != undefined ?
+ stageY :
+ s.mousecaptor.stageY;
+ s.mousecaptor.ratio = ratio != undefined ?
+ ratio :
+ s.mousecaptor.ratio;
+
+ return self;
+ }
+ };
+
+ this.goTo = function(stageX, stageY, ratio) {
+ s.mousecaptor.interpolate(stageX, stageY, ratio);
+ return self;
+ };
+
+ this.zoomTo = function(x, y, ratio) {
+ ratio = Math.min(
+ Math.max(s.mousecaptor.config('minRatio'), ratio),
+ s.mousecaptor.config('maxRatio')
+ );
+ if (ratio == s.mousecaptor.ratio) {
+ s.mousecaptor.interpolate(
+ x - s.width / 2 + s.mousecaptor.stageX,
+ y - s.height / 2 + s.mousecaptor.stageY
+ );
+ }else {
+ s.mousecaptor.interpolate(
+ (ratio * x - s.mousecaptor.ratio * s.width/2) /
+ (ratio - s.mousecaptor.ratio),
+ (ratio * y - s.mousecaptor.ratio * s.height/2) /
+ (ratio - s.mousecaptor.ratio),
+ ratio
+ );
+ }
+ return self;
+ };
+
+ this.resize = function(w, h) {
+ s.resize(w, h);
+ return self;
+ };
+
+ this.draw = function(nodes, edges, labels, safe) {
+ s.draw(nodes, edges, labels, safe);
+ return self;
+ };
+
+ this.refresh = function() {
+ s.refresh();
+ return self;
+ };
+
+ // Tasks methods:
+ this.addGenerator = function(id, task, condition) {
+ sigma.chronos.addGenerator(id + '_ext_' + s.id, task, condition);
+ return self;
+ };
+
+ this.removeGenerator = function(id) {
+ sigma.chronos.removeGenerator(id + '_ext_' + s.id);
+ return self;
+ };
+
+ // Graph methods:
+ this.addNode = function(id, params) {
+ s.graph.addNode(id, params);
+ return self;
+ };
+
+ this.addEdge = function(id, source, target, params) {
+ s.graph.addEdge(id, source, target, params);
+ return self;
+ }
+
+ this.dropNode = function(v) {
+ s.graph.dropNode(v);
+ return self;
+ };
+
+ this.dropEdge = function(v) {
+ s.graph.dropEdge(v);
+ return self;
+ };
+
+ this.pushGraph = function(object, safe) {
+ object.nodes && object.nodes.forEach(function(node) {
+ node['id'] && (!safe || !s.graph.nodesIndex[node['id']]) &&
+ self.addNode(node['id'], node);
+ });
+
+ var isEdgeValid;
+ object.edges && object.edges.forEach(function(edge) {
+ validID = edge['source'] && edge['target'] && edge['id'];
+ validID &&
+ (!safe || !s.graph.edgesIndex[edge['id']]) &&
+ self.addNode(
+ edge['id'],
+ edge['source'],
+ edge['target'],
+ edge
+ );
+ });
+
+ return self;
+ };
+
+ this.emptyGraph = function() {
+ s.graph.empty();
+ return self;
+ };
+
+ this.getNodesCount = function() {
+ return s.graph.nodes.length;
+ };
+
+ this.getEdgesCount = function() {
+ return s.graph.edges.length;
+ };
+
+ this.iterNodes = function(fun, ids) {
+ s.graph.iterNodes(fun, ids);
+ return self;
+ };
+
+ this.iterEdges = function(fun, ids) {
+ s.graph.iterEdges(fun, ids);
+ return self;
+ };
+
+ this.getNodes = function(ids) {
+ return s.graph.getNodes(ids);
+ };
+
+ this.getEdges = function(ids) {
+ return s.graph.getEdges(ids);
+ };
+
+ // Monitoring
+ this.activateMonitoring = function() {
+ return s.monitor.activate();
+ };
+
+ this.desactivateMonitoring = function() {
+ return s.monitor.desactivate();
+ };
+
+ // Events
+ s.bind('downnodes upnodes downgraph upgraph', function(e) {
+ self.dispatch(e.type, e.content);
+ });
+
+ s.graph.bind('overnodes outnodes', function(e) {
+ self.dispatch(e.type, e.content);
+ });
+}
+
+/**
+ * The graph data model used in sigma.js.
+ * @constructor
+ * @extends sigma.classes.Cascade
+ * @extends sigma.classes.EventDispatcher
+ * @this {Graph}
+ */
+function Graph() {
+ sigma.classes.Cascade.call(this);
+ sigma.classes.EventDispatcher.call(this);
+
+ /**
+ * Represents "this", without the well-known scope issue.
+ * @private
+ * @type {Graph}
+ */
+ var self = this;
+
+ /**
+ * The different parameters that determine how the nodes and edges should be
+ * translated and rescaled.
+ * @type {Object}
+ */
+ this.p = {
+ minNodeSize: 0,
+ maxNodeSize: 0,
+ minEdgeSize: 0,
+ maxEdgeSize: 0,
+ // Scaling mode:
+ // - 'inside' (default)
+ // - 'outside'
+ scalingMode: 'inside',
+ nodesPowRatio: 0.5,
+ edgesPowRatio: 0
+ };
+
+ /**
+ * Contains the borders of the graph. These are useful to avoid the user to
+ * drag the graph out of the canvas.
+ * @type {Object}
+ */
+ this.borders = {};
+
+ /**
+ * Inserts a node in the graph.
+ * @param {string} id The node's ID.
+ * @param {object} params An object containing the different parameters
+ * of the node.
+ * @return {Graph} Returns itself.
+ */
+ function addNode(id, params) {
+ if (self.nodesIndex[id]) {
+ throw new Error('Node "' + id + '" already exists.');
+ }
+
+ params = params || {};
+ var n = {
+ // Numbers :
+ 'x': 0,
+ 'y': 0,
+ 'size': 1,
+ 'degree': 0,
+ 'inDegree': 0,
+ 'outDegree': 0,
+ // Flags :
+ 'fixed': false,
+ 'active': false,
+ 'hidden': false,
+ 'forceLabel': false,
+ // Strings :
+ 'label': id.toString(),
+ 'id': id.toString(),
+ // Custom attributes :
+ 'attr': {}
+ };
+
+ for (var k in params) {
+ switch (k) {
+ case 'id':
+ break;
+ case 'x':
+ case 'y':
+ case 'size':
+ n[k] = +params[k];
+ break;
+ case 'fixed':
+ case 'active':
+ case 'hidden':
+ case 'forceLabel':
+ n[k] = !!params[k];
+ break;
+ case 'color':
+ case 'label':
+ n[k] = params[k];
+ break;
+ default:
+ n['attr'][k] = params[k];
+ }
+ }
+
+ self.nodes.push(n);
+ self.nodesIndex[id.toString()] = n;
+
+ return self;
+ };
+
+ /**
+ * Generates the clone of a node, to make it easier to be exported.
+ * @private
+ * @param {Object} node The node to clone.
+ * @return {Object} The clone of the node.
+ */
+ function cloneNode(node) {
+ return {
+ 'x': node['x'],
+ 'y': node['y'],
+ 'size': node['size'],
+ 'degree': node['degree'],
+ 'inDegree': node['inDegree'],
+ 'outDegree': node['outDegree'],
+ 'displayX': node['displayX'],
+ 'displayY': node['displayY'],
+ 'displaySize': node['displaySize'],
+ 'label': node['label'],
+ 'id': node['id'],
+ 'color': node['color'],
+ 'fixed': node['fixed'],
+ 'active': node['active'],
+ 'hidden': node['hidden'],
+ 'forceLabel': node['forceLabel'],
+ 'attr': node['attr']
+ };
+ };
+
+ /**
+ * Checks the clone of a node, and inserts its values when possible. For
+ * example, it is possible to modify the size or the color of a node, but it
+ * is not possible to modify its display values or its id.
+ * @private
+ * @param {Object} node The original node.
+ * @param {Object} copy The clone.
+ * @return {Graph} Returns itself.
+ */
+ function checkNode(node, copy) {
+ for (var k in copy) {
+ switch (k) {
+ case 'id':
+ case 'attr':
+ case 'degree':
+ case 'inDegree':
+ case 'outDegree':
+ case 'displayX':
+ case 'displayY':
+ case 'displaySize':
+ break;
+ case 'x':
+ case 'y':
+ case 'size':
+ node[k] = +copy[k];
+ break;
+ case 'fixed':
+ case 'active':
+ case 'hidden':
+ case 'forceLabel':
+ node[k] = !!copy[k];
+ break;
+ case 'color':
+ case 'label':
+ node[k] = (copy[k] || '').toString();
+ break;
+ default:
+ node['attr'][k] = copy[k];
+ }
+ }
+
+ return self;
+ };
+
+ /**
+ * Deletes one or several nodes from the graph, and the related edges.
+ * @param {(string|Array.<string>)} v A string ID, or an Array of several
+ * IDs.
+ * @return {Graph} Returns itself.
+ */
+ function dropNode(v) {
+ var a = (v instanceof Array ? v : [v]) || [];
+
+ a.forEach(function(id) {
+ if (self.nodesIndex[id]) {
+ var index = null;
+ self.nodes.some(function(n, i) {
+ if (n['id'] == id) {
+ index = i;
+ return true;
+ }
+ return false;
+ });
+
+ index != null && self.nodes.splice(index, 1);
+ delete self.nodesIndex[id];
+
+ var edgesToRemove = [];
+ self.edges = self.edges.filter(function(e) {
+ if (e['source']['id'] == id) {
+ delete self.edgesIndex[e['id']];
+ e['target']['degree']--;
+ e['target']['inDegree']--;
+ return false;
+ }else if (e['target']['id'] == id) {
+ delete self.edgesIndex[e['id']];
+ e['source']['degree']--;
+ e['source']['outDegree']--;
+ return false;
+ }
+ return true;
+ });
+ }else {
+ sigma.log('Node "' + id + '" does not exist.');
+ }
+ });
+
+ return self;
+ };
+
+ /**
+ * Inserts an edge in the graph.
+ * @param {string} id The edge ID.
+ * @param {string} source The ID of the edge source.
+ * @param {string} target The ID of the edge target.
+ * @param {object} params An object containing the different parameters
+ * of the edge.
+ * @return {Graph} Returns itself.
+ */
+ function addEdge(id, source, target, params) {
+ if (self.edgesIndex[id]) {
+ throw new Error('Edge "' + id + '" already exists.');
+ }
+
+ if (!self.nodesIndex[source]) {
+ var s = 'Edge\'s source "' + source + '" does not exist yet.';
+ throw new Error(s);
+ }
+
+ if (!self.nodesIndex[target]) {
+ var s = 'Edge\'s target "' + target + '" does not exist yet.';
+ throw new Error(s);
+ }
+
+ params = params || {};
+ var e = {
+ 'source': self.nodesIndex[source],
+ 'target': self.nodesIndex[target],
+ 'size': 1,
+ 'weight': 1,
+ 'displaySize': 0.5,
+ 'label': id.toString(),
+ 'id': id.toString(),
+ 'hidden': false,
+ 'attr': {}
+ };
+
+ e['source']['degree']++;
+ e['source']['outDegree']++;
+ e['target']['degree']++;
+ e['target']['inDegree']++;
+
+ for (var k in params) {
+ switch (k) {
+ case 'id':
+ case 'source':
+ case 'target':
+ break;
+ case 'hidden':
+ e[k] = !!params[k];
+ break;
+ case 'size':
+ case 'weight':
+ e[k] = +params[k];
+ break;
+ case 'color':
+ e[k] = params[k].toString();
+ break;
+ case 'type':
+ e[k] = params[k].toString();
+ break;
+ case 'label':
+ e[k] = params[k];
+ break;
+ default:
+ e['attr'][k] = params[k];
+ }
+ }
+
+ self.edges.push(e);
+ self.edgesIndex[id.toString()] = e;
+
+ return self;
+ };
+
+ /**
+ * Generates the clone of a edge, to make it easier to be exported.
+ * @private
+ * @param {Object} edge The edge to clone.
+ * @return {Object} The clone of the edge.
+ */
+ function cloneEdge(edge) {
+ return {
+ 'source': edge['source']['id'],
+ 'target': edge['target']['id'],
+ 'size': edge['size'],
+ 'type': edge['type'],
+ 'weight': edge['weight'],
+ 'displaySize': edge['displaySize'],
+ 'label': edge['label'],
+ 'hidden': edge['hidden'],
+ 'id': edge['id'],
+ 'attr': edge['attr'],
+ 'color': edge['color']
+ };
+ };
+
+ /**
+ * Checks the clone of an edge, and inserts its values when possible. For
+ * example, it is possible to modify the label or the type of an edge, but it
+ * is not possible to modify its display values or its id.
+ * @private
+ * @param {Object} edge The original edge.
+ * @param {Object} copy The clone.
+ * @return {Graph} Returns itself.
+ */
+ function checkEdge(edge, copy) {
+ for (var k in copy) {
+ switch (k) {
+ case 'id':
+ case 'displaySize':
+ break;
+ case 'weight':
+ case 'size':
+ edge[k] = +copy[k];
+ break;
+ case 'source':
+ case 'target':
+ edge[k] = self.nodesIndex[k] || edge[k];
+ break;
+ case 'hidden':
+ edge[k] = !!copy[k];
+ break;
+ case 'color':
+ case 'label':
+ case 'type':
+ edge[k] = (copy[k] || '').toString();
+ break;
+ default:
+ edge['attr'][k] = copy[k];
+ }
+ }
+
+ return self;
+ };
+
+ /**
+ * Deletes one or several edges from the graph.
+ * @param {(string|Array.<string>)} v A string ID, or an Array of several
+ * IDs.
+ * @return {Graph} Returns itself.
+ */
+ function dropEdge(v) {
+ var a = (v instanceof Array ? v : [v]) || [];
+
+ a.forEach(function(id) {
+ if (self.edgesIndex[id]) {
+ self.edgesIndex[id]['source']['degree']--;
+ self.edgesIndex[id]['source']['outDegree']--;
+ self.edgesIndex[id]['target']['degree']--;
+ self.edgesIndex[id]['target']['inDegree']--;
+
+ var index = null;
+ self.edges.some(function(n, i) {
+ if (n['id'] == id) {
+ index = i;
+ return true;
+ }
+ return false;
+ });
+
+ index != null && self.edges.splice(index, 1);
+ delete self.edgesIndex[id];
+ }else {
+ sigma.log('Edge "' + id + '" does not exist.');
+ }
+ });
+
+ return self;
+ };
+
+ /**
+ * Deletes every nodes and edges from the graph.
+ * @return {Graph} Returns itself.
+ */
+ function empty() {
+ self.nodes = [];
+ self.nodesIndex = {};
+ self.edges = [];
+ self.edgesIndex = {};
+
+ return self;
+ };
+
+ /**
+ * Computes the display x, y and size of each node, relatively to the
+ * original values and the borders determined in the parameters, such as
+ * each node is in the described area.
+ * @param {number} w The area width (actually the width of the DOM
+ * root).
+ * @param {number} h The area height (actually the height of the
+ * DOM root).
+ * @param {boolean} parseNodes Indicates if the nodes have to be parsed.
+ * @param {boolean} parseEdges Indicates if the edges have to be parsed.
+ * @return {Graph} Returns itself.
+ */
+ function rescale(w, h, parseNodes, parseEdges) {
+ var weightMax = 0, sizeMax = 0;
+
+ parseNodes && self.nodes.forEach(function(node) {
+ sizeMax = Math.max(node['size'], sizeMax);
+ });
+
+ parseEdges && self.edges.forEach(function(edge) {
+ weightMax = Math.max(edge['size'], weightMax);
+ });
+
+ sizeMax = sizeMax || 1;
+ weightMax = weightMax || 1;
+
+ // Recenter the nodes:
+ var xMin, xMax, yMin, yMax;
+ parseNodes && self.nodes.forEach(function(node) {
+ xMax = Math.max(node['x'], xMax || node['x']);
+ xMin = Math.min(node['x'], xMin || node['x']);
+ yMax = Math.max(node['y'], yMax || node['y']);
+ yMin = Math.min(node['y'], yMin || node['y']);
+ });
+
+ // First, we compute the scaling ratio, without considering the sizes
+ // of the nodes : Each node will have its center in the canvas, but might
+ // be partially out of it.
+ var scale = self.p.scalingMode == 'outside' ?
+ Math.max(w / Math.max(xMax - xMin, 1),
+ h / Math.max(yMax - yMin, 1)) :
+ Math.min(w / Math.max(xMax - xMin, 1),
+ h / Math.max(yMax - yMin, 1));
+
+ // Then, we correct that scaling ratio considering a margin, which is
+ // basically the size of the biggest node.
+ // This has to be done as a correction since to compare the size of the
+ // biggest node to the X and Y values, we have to first get an
+ // approximation of the scaling ratio.
+ var margin = (self.p.maxNodeSize || sizeMax) / scale;
+ xMax += margin;
+ xMin -= margin;
+ yMax += margin;
+ yMin -= margin;
+
+ scale = self.p.scalingMode == 'outside' ?
+ Math.max(w / Math.max(xMax - xMin, 1),
+ h / Math.max(yMax - yMin, 1)) :
+ Math.min(w / Math.max(xMax - xMin, 1),
+ h / Math.max(yMax - yMin, 1));
+
+ // Size homothetic parameters:
+ var a, b;
+ if (!self.p.maxNodeSize && !self.p.minNodeSize) {
+ a = 1;
+ b = 0;
+ }else if (self.p.maxNodeSize == self.p.minNodeSize) {
+ a = 0;
+ b = self.p.maxNodeSize;
+ }else {
+ a = (self.p.maxNodeSize - self.p.minNodeSize) / sizeMax;
+ b = self.p.minNodeSize;
+ }
+
+ var c, d;
+ if (!self.p.maxEdgeSize && !self.p.minEdgeSize) {
+ c = 1;
+ d = 0;
+ }else if (self.p.maxEdgeSize == self.p.minEdgeSize) {
+ c = 0;
+ d = self.p.minEdgeSize;
+ }else {
+ c = (self.p.maxEdgeSize - self.p.minEdgeSize) / weightMax;
+ d = self.p.minEdgeSize;
+ }
+
+ // Rescale the nodes:
+ parseNodes && self.nodes.forEach(function(node) {
+ node['displaySize'] = node['size'] * a + b;
+
+ if (!node['fixed']) {
+ node['displayX'] = (node['x'] - (xMax + xMin) / 2) * scale + w / 2;
+ node['displayY'] = (node['y'] - (yMax + yMin) / 2) * scale + h / 2;
+ }
+ });
+
+ parseEdges && self.edges.forEach(function(edge) {
+ edge['displaySize'] = edge['size'] * c + d;
+ });
+
+ return self;
+ };
+
+ /**
+ * Translates the display values of the nodes and edges relatively to the
+ * scene position and zoom ratio.
+ * @param {number} sceneX The x position of the scene.
+ * @param {number} sceneY The y position of the scene.
+ * @param {number} ratio The zoom ratio of the scene.
+ * @param {boolean} parseNodes Indicates if the nodes have to be parsed.
+ * @param {boolean} parseEdges Indicates if the edges have to be parsed.
+ * @return {Graph} Returns itself.
+ */
+ function translate(sceneX, sceneY, ratio, parseNodes, parseEdges) {
+ var sizeRatio = Math.pow(ratio, self.p.nodesPowRatio);
+ parseNodes && self.nodes.forEach(function(node) {
+ if (!node['fixed']) {
+ node['displayX'] = node['displayX'] * ratio + sceneX;
+ node['displayY'] = node['displayY'] * ratio + sceneY;
+ }
+
+ node['displaySize'] = node['displaySize'] * sizeRatio;
+ });
+
+ sizeRatio = Math.pow(ratio, self.p.edgesPowRatio);
+ parseEdges && self.edges.forEach(function(edge) {
+ edge['displaySize'] = edge['displaySize'] * sizeRatio;
+ });
+
+ return self;
+ };
+
+ /**
+ * Determines the borders of the graph as it will be drawn. It is used to
+ * avoid the user to drag the graph out of the canvas.
+ */
+ function setBorders() {
+ self.borders = {};
+
+ self.nodes.forEach(function(node) {
+ self.borders.minX = Math.min(
+ self.borders.minX == undefined ?
+ node['displayX'] - node['displaySize'] :
+ self.borders.minX,
+ node['displayX'] - node['displaySize']
+ );
+
+ self.borders.maxX = Math.max(
+ self.borders.maxX == undefined ?
+ node['displayX'] + node['displaySize'] :
+ self.borders.maxX,
+ node['displayX'] + node['displaySize']
+ );
+
+ self.borders.minY = Math.min(
+ self.borders.minY == undefined ?
+ node['displayY'] - node['displaySize'] :
+ self.borders.minY,
+ node['displayY'] - node['displaySize']
+ );
+
+ self.borders.maxY = Math.max(
+ self.borders.maxY == undefined ?
+ node['displayY'] - node['displaySize'] :
+ self.borders.maxY,
+ node['displayY'] - node['displaySize']
+ );
+ });
+ }
+
+ /**
+ * Checks which nodes are under the (mX, mY) points, representing the mouse
+ * position.
+ * @param {number} mX The mouse X position.
+ * @param {number} mY The mouse Y position.
+ * @return {Graph} Returns itself.
+ */
+ function checkHover(mX, mY) {
+ var dX, dY, s, over = [], out = [];
+ self.nodes.forEach(function(node) {
+ if (node['hidden']) {
+ node['hover'] = false;
+ return;
+ }
+
+ dX = Math.abs(node['displayX'] - mX);
+ dY = Math.abs(node['displayY'] - mY);
+ s = node['displaySize'];
+
+ var oldH = node['hover'];
+ var newH = dX < s && dY < s && Math.sqrt(dX * dX + dY * dY) < s;
+
+ if (oldH && !newH) {
+ node['hover'] = false;
+ out.push(node.id);
+ } else if (newH && !oldH) {
+ node['hover'] = true;
+ over.push(node.id);
+ }
+ });
+
+ over.length && self.dispatch('overnodes', over);
+ out.length && self.dispatch('outnodes', out);
+
+ return self;
+ };
+
+ /**
+ * Applies a function to a clone of each node (or indicated nodes), and then
+ * tries to apply the modifications made on the clones to the original nodes.
+ * @param {function(Object)} fun The function to execute.
+ * @param {?Array.<string>} ids An Array of node IDs (optional).
+ * @return {Graph} Returns itself.
+ */
+ function iterNodes(fun, ids) {
+ var a = ids ? ids.map(function(id) {
+ return self.nodesIndex[id];
+ }) : self.nodes;
+
+ var aCopies = a.map(cloneNode);
+ aCopies.forEach(fun);
+
+ a.forEach(function(n, i) {
+ checkNode(n, aCopies[i]);
+ });
+
+ return self;
+ };
+
+ /**
+ * Applies a function to a clone of each edge (or indicated edges), and then
+ * tries to apply the modifications made on the clones to the original edges.
+ * @param {function(Object)} fun The function to execute.
+ * @param {?Array.<string>} ids An Array of edge IDs (optional).
+ * @return {Graph} Returns itself.
+ */
+ function iterEdges(fun, ids) {
+ var a = ids ? ids.map(function(id) {
+ return self.edgesIndex[id];
+ }) : self.edges;
+
+ var aCopies = a.map(cloneEdge);
+ aCopies.forEach(fun);
+
+ a.forEach(function(e, i) {
+ checkEdge(e, aCopies[i]);
+ });
+
+ return self;
+ };
+
+ /**
+ * Returns a specific node clone or an array of specified node clones.
+ * @param {(string|Array.<string>)} ids The ID or an array of node IDs.
+ * @return {(Object|Array.<Object>)} The clone or the array of clones.
+ */
+ function getNodes(ids) {
+ var a = ((ids instanceof Array ? ids : [ids]) || []).map(function(id) {
+ return cloneNode(self.nodesIndex[id]);
+ });
+
+ return (ids instanceof Array ? a : a[0]);
+ };
+
+ /**
+ * Returns a specific edge clone or an array of specified edge clones.
+ * @param {(string|Array.<string>)} ids The ID or an array of edge IDs.
+ * @return {(Object|Array.<Object>)} The clone or the array of clones.
+ */
+ function getEdges(ids) {
+ var a = ((ids instanceof Array ? ids : [ids]) || []).map(function(id) {
+ return cloneEdge(self.edgesIndex[id]);
+ });
+
+ return (ids instanceof Array ? a : a[0]);
+ };
+
+ empty();
+
+ this.addNode = addNode;
+ this.addEdge = addEdge;
+ this.dropNode = dropNode;
+ this.dropEdge = dropEdge;
+
+ this.iterEdges = iterEdges;
+ this.iterNodes = iterNodes;
+
+ this.getEdges = getEdges;
+ this.getNodes = getNodes;
+
+ this.empty = empty;
+ this.rescale = rescale;
+ this.translate = translate;
+ this.setBorders = setBorders;
+ this.checkHover = checkHover;
+}
+
+sigma.easing = {
+ linear: {},
+ quadratic: {}
+};
+
+sigma.easing.linear.easenone = function(k) {
+ return k;
+};
+
+sigma.easing.quadratic.easein = function(k) {
+ return k * k;
+};
+
+sigma.easing.quadratic.easeout = function(k) {
+ return - k * (k - 2);
+};
+
+sigma.easing.quadratic.easeinout = function(k) {
+ if ((k *= 2) < 1) return 0.5 * k * k;
+ return - 0.5 * (--k * (k - 2) - 1);
+};
+
+sigma.debugMode = 0;
+
+sigma.log = function() {
+ if (sigma.debugMode == 1) {
+ for (var k in arguments) {
+ console.log(arguments[k]);
+ }
+ }else if (sigma.debugMode > 1) {
+ for (var k in arguments) {
+ throw new Error(arguments[k]);
+ }
+ }
+
+ return sigma;
+};
+
+/**
+ * Add a function to the prototype of SigmaPublic, but with access to the
+ * Sigma class properties.
+ * @param {string} pluginName [description].
+ * @param {function} caller [description].
+ * @param {function(Sigma)} launcher [description].
+ */
+sigma.addPlugin = function(pluginName, caller, launcher) {
+ SigmaPublic.prototype[pluginName] = caller;
+ local.plugins.push(launcher);
+};
+sigma.tools.drawRoundRect = function(ctx, x, y, w, h, ellipse, corners) {
+ var e = ellipse ? ellipse : 0;
+ var c = corners ? corners : [];
+ c = ((typeof c) == 'string') ? c.split(' ') : c;
+
+ var tl = e && (c.indexOf('topleft') >= 0 ||
+ c.indexOf('top') >= 0 ||
+ c.indexOf('left') >= 0);
+ var tr = e && (c.indexOf('topright') >= 0 ||
+ c.indexOf('top') >= 0 ||
+ c.indexOf('right') >= 0);
+ var bl = e && (c.indexOf('bottomleft') >= 0 ||
+ c.indexOf('bottom') >= 0 ||
+ c.indexOf('left') >= 0);
+ var br = e && (c.indexOf('bottomright') >= 0 ||
+ c.indexOf('bottom') >= 0 ||
+ c.indexOf('right') >= 0);
+
+ ctx.moveTo(x, y + e);
+
+ if (tl) {
+ ctx.arcTo(x, y, x + e, y, e);
+ }else {
+ ctx.lineTo(x, y);
+ }
+
+ if (tr) {
+ ctx.lineTo(x + w - e, y);
+ ctx.arcTo(x + w, y, x + w, y + e, e);
+ }else {
+ ctx.lineTo(x + w, y);
+ }
+
+ if (br) {
+ ctx.lineTo(x + w, y + h - e);
+ ctx.arcTo(x + w, y + h, x + w - e, y + h, e);
+ }else {
+ ctx.lineTo(x + w, y + h);
+ }
+
+ if (bl) {
+ ctx.lineTo(x + e, y + h);
+ ctx.arcTo(x, y + h, x, y + h - e, e);
+ }else {
+ ctx.lineTo(x, y + h);
+ }
+
+ ctx.lineTo(x, y + e);
+};
+
+sigma.tools.getRGB = function(s, asArray) {
+ s = s.toString();
+ var res = {
+ 'r': 0,
+ 'g': 0,
+ 'b': 0
+ };
+
+ if (s.length >= 3) {
+ if (s.charAt(0) == '#') {
+ var l = s.length - 1;
+ if (l == 6) {
+ res = {
+ 'r': parseInt(s.charAt(1) + s.charAt(2), 16),
+ 'g': parseInt(s.charAt(3) + s.charAt(4), 16),
+ 'b': parseInt(s.charAt(5) + s.charAt(5), 16)
+ };
+ }else if (l == 3) {
+ res = {
+ 'r': parseInt(s.charAt(1) + s.charAt(1), 16),
+ 'g': parseInt(s.charAt(2) + s.charAt(2), 16),
+ 'b': parseInt(s.charAt(3) + s.charAt(3), 16)
+ };
+ }
+ }
+ }
+
+ if (asArray) {
+ res = [
+ res['r'],
+ res['g'],
+ res['b']
+ ];
+ }
+
+ return res;
+};
+
+sigma.tools.rgbToHex = function(R, G, B) {
+ return sigma.tools.toHex(R) + sigma.tools.toHex(G) + sigma.tools.toHex(B);
+};
+
+sigma.tools.toHex = function(n) {
+ n = parseInt(n, 10);
+
+ if (isNaN(n)) {
+ return '00';
+ }
+ n = Math.max(0, Math.min(n, 255));
+ return '0123456789ABCDEF'.charAt((n - n % 16) / 16) +
+ '0123456789ABCDEF'.charAt(n % 16);
+};
+
+/**
+ * sigma.chronos manages frames insertion to simulate asynchronous computing.
+ * It has been designed to make possible to execute heavy computing tasks
+ * for the browser, without freezing it.
+ * @constructor
+ * @extends sigma.classes.Cascade
+ * @extends sigma.classes.EventDispatcher
+ * @this {sigma.chronos}
+ */
+sigma.chronos = new (function() {
+ sigma.classes.EventDispatcher.call(this);
+
+ /**
+ * Represents "this", without the well-known scope issue.
+ * @private
+ * @type {sigma.chronos}
+ */
+ var self = this;
+
+ /**
+ * Indicates whether any task is actively running or not.
+ * @private
+ * @type {boolean}
+ */
+ var isRunning = false;
+
+ /**
+ * Indicates the FPS "goal", that will define the theoretical
+ * frame length.
+ * @private
+ * @type {number}
+ */
+ var fpsReq = 80;
+
+ /**
+ * Stores the last computed FPS value (FPS is computed only when any
+ * task is running).
+ * @private
+ * @type {number}
+ */
+ var lastFPS = 0;
+
+ /**
+ * The number of frames inserted since the last start.
+ * @private
+ * @type {number}
+ */
+ var framesCount = 0;
+
+ /**
+ * The theoretical frame time.
+ * @private
+ * @type {number}
+ */
+ var frameTime = 1000 / fpsReq;
+
+ /**
+ * The theoretical frame length, minus the last measured delay.
+ * @private
+ * @type {number}
+ */
+ var correctedFrameTime = frameTime;
+
+ /**
+ * The measured length of the last frame.
+ * @private
+ * @type {number}
+ */
+ var effectiveTime = 0;
+
+ /**
+ * The time passed since the last runTasks action.
+ * @private
+ * @type {number}
+ */
+ var currentTime = 0;
+
+ /**
+ * The time when the last frame was inserted.
+ * @private
+ * @type {number}
+ */
+ var startTime = 0;
+
+ /**
+ * The difference between the theoretical frame length and the
+ * last measured frame length.
+ * @private
+ * @type {number}
+ */
+ var delay = 0;
+
+ /**
+ * The container of all active generators.
+ * @private
+ * @type {Object.<string, Object>}
+ */
+ var generators = {};
+
+ /**
+ * The array of all the referenced and active tasks.
+ * @private
+ * @type {Array.<Object>}
+ */
+ var tasks = [];
+
+ /**
+ * The array of all the referenced and queued tasks.
+ * @private
+ * @type {Array.<Object>}
+ */
+ var queuedTasks = [];
+
+ /**
+ * The index of the next task to execute.
+ * @private
+ * @type {number}
+ */
+ var taskIndex = 0;
+
+
+ /**
+ * Inserts a frame before executing the callback.
+ * @param {function()} callback The callback to execute after having
+ * inserted the frame.
+ * @return {sigma.chronos} Returns itself.
+ */
+ function insertFrame(callback) {
+ window.setTimeout(callback, 0);
+ return self;
+ }
+
+ /**
+ * The local method that executes routine, and inserts frames when needed.
+ * It dispatches a "frameinserted" event after having inserted any frame,
+ * and an "insertframe" event before.
+ * @private
+ */
+ function frameInserter() {
+ self.dispatch('frameinserted');
+ while (isRunning && tasks.length && routine()) {}
+
+ if (!isRunning || !tasks.length) {
+ stopTasks();
+ } else {
+ startTime = (new Date()).getTime();
+ framesCount++;
+ delay = effectiveTime - frameTime;
+ correctedFrameTime = frameTime - delay;
+
+ self.dispatch('insertframe');
+ insertFrame(frameInserter);
+ }
+ };
+
+ /**
+ * The local method that executes the tasks, and compares the current frame
+ * length to the ideal frame length.
+ * @private
+ * @return {boolean} Returns false if the current frame should be ended,
+ * and true else.
+ */
+ function routine() {
+ taskIndex = taskIndex % tasks.length;
+
+ if (!tasks[taskIndex].task()) {
+ var n = tasks[taskIndex].taskName;
+
+ queuedTasks = queuedTasks.filter(function(e) {
+ (e.taskParent == n) && tasks.push({
+ taskName: e.taskName,
+ task: e.task
+ });
+ return e.taskParent != n;
+ });
+
+ self.dispatch('killed', tasks.splice(taskIndex--, 1)[0]);
+ }
+
+ taskIndex++;
+ effectiveTime = (new Date()).getTime() - startTime;
+ return effectiveTime <= correctedFrameTime;
+ };
+
+ /**
+ * Starts tasks execution.
+ * @return {sigma.chronos} Returns itself.
+ */
+ function runTasks() {
+ isRunning = true;
+ taskIndex = 0;
+ framesCount = 0;
+
+ startTime = (new Date()).getTime();
+ currentTime = startTime;
+
+ self.dispatch('start');
+ self.dispatch('insertframe');
+ insertFrame(frameInserter);
+ return self;
+ };
+
+ /**
+ * Stops tasks execution, and dispatch a "stop" event.
+ * @return {sigma.chronos} Returns itself.
+ */
+ function stopTasks() {
+ self.dispatch('stop');
+ isRunning = false;
+ return self;
+ };
+
+ /**
+ * A task is a function that will be executed continuously while it returns
+ * true. As soon as it return false, the task will be removed.
+ * If several tasks are present, they will be executed in parallele.
+ * This method will add the task to this execution process.
+ * @param {function(): boolean} task The task to add.
+ * @param {string} name The name of the worker, used for
+ * managing the different tasks.
+ * @param {boolean} autostart If true, sigma.chronos will start
+ * automatically if it is not working
+ * yet.
+ * @return {sigma.chronos} Returns itself.
+ */
+ function addTask(task, name, autostart) {
+ if (typeof task != 'function') {
+ throw new Error('Task "' + name + '" is not a function');
+ }
+
+ tasks.push({
+ taskName: name,
+ task: task
+ });
+
+ isRunning = !!(isRunning || (autostart && runTasks()) || true);
+ return self;
+ };
+
+ /**
+ * Will add a task that will be start to be executed as soon as a task
+ * named as the parent will be removed.
+ * @param {function(): boolean} task The task to add.
+ * @param {string} name The name of the worker, used for
+ * managing the different tasks.
+ * @param {string} parent The name of the parent task.
+ * @return {sigma.chronos} Returns itself.
+ */
+ function queueTask(task, name, parent) {
+ if (typeof task != 'function') {
+ throw new Error('Task "' + name + '" is not a function');
+ }
+
+ if (!tasks.concat(queuedTasks).some(function(e) {
+ return e.taskName == parent;
+ })) {
+ throw new Error(
+ 'Parent task "' + parent + '" of "' + name + '" is not attached.'
+ );
+ }
+
+ queuedTasks.push({
+ taskParent: parent,
+ taskName: name,
+ task: task
+ });
+
+ return self;
+ };
+
+ /**
+ * Removes a task.
+ * @param {string} v If v is undefined, then every tasks will
+ * be removed. If not, each task named v will
+ * be removed.
+ * @param {number} queueStatus Determines the queued tasks behaviour. If 0,
+ * then nothing will happen. If 1, the tasks
+ * queued to any removed task will be triggered.
+ * If 2, the tasks queued to any removed task
+ * will be removed as well.
+ * @return {sigma.chronos} Returns itself.
+ */
+ function removeTask(v, queueStatus) {
+ if (v == undefined) {
+ tasks = [];
+ if (queueStatus == 1) {
+ queuedTasks = [];
+ }else if (queueStatus == 2) {
+ tasks = queuedTasks;
+ queuedTasks = [];
+ }
+ stopTasks();
+ } else {
+ var n = (typeof v == 'string') ? v : '';
+ tasks = tasks.filter(function(e) {
+ if ((typeof v == 'string') ? e.taskName == v : e.task == v) {
+ n = e.taskName;
+ return false;
+ }
+ return true;
+ });
+
+ if (queueStatus > 0) {
+ queuedTasks = queuedTasks.filter(function(e) {
+ if (queueStatus == 1 && e.taskParent == n) {
+ tasks.push(e);
+ }
+ return e.taskParent != n;
+ });
+ }
+ }
+
+ isRunning = !!(!tasks.length || (stopTasks() && false));
+ return self;
+ };
+
+ /**
+ * A generator is a pair task/condition. The task will be executed
+ * while it returns true.
+ * When it returns false, the condition will be tested. If
+ * the condition returns true, the task will be executed
+ * again at the next process iteration. If not, the generator
+ * is removed.
+ * If several generators are present, they will be executed one
+ * by one: When the first stops, the second will start, etc. When
+ * they are all ended, then the conditions will be tested to know
+ * which generators have to be started again.
+ * @param {string} id The generators ID.
+ * @param {function(): boolean} task The generator's task.
+ * @param {function(): boolean} condition The generator's condition.
+ * @return {sigma.chronos} Returns itself.
+ */
+ function addGenerator(id, task, condition) {
+ if (generators[id] != undefined) {
+ return self;
+ }
+
+ generators[id] = {
+ task: task,
+ condition: condition
+ };
+
+ getGeneratorsCount(true) == 0 && startGenerators();
+ return self;
+ };
+
+ /**
+ * Removes a generator. It means that the task will continue being eecuted
+ * until it returns false, but then the
+ * condition will not be tested.
+ * @param {string} id The generator's ID.
+ * @return {sigma.chronos} Returns itself.
+ */
+ function removeGenerator(id) {
+ if (generators[id]) {
+ generators[id].on = false;
+ generators[id].del = true;
+ }
+ return self;
+ };
+
+ /**
+ * Returns the number of generators.
+ * @private
+ * @param {boolean} running If true, returns the number of active
+ * generators instead.
+ * @return {sigma.chronos} Returns itself.
+ */
+ function getGeneratorsCount(running) {
+ return running ?
+ Object.keys(generators).filter(function(id) {
+ return !!generators[id].on;
+ }).length :
+ Object.keys(generators).length;
+ };
+
+ /**
+ * Returns the array of the generators IDs.
+ * @return {array.<string>} The array of IDs.
+ */
+ function getGeneratorsIDs() {
+ return Object.keys(generators);
+ }
+
+ /**
+ * startGenerators is the method that manages which generator
+ * is the next to start when another one stops. It will dispatch
+ * a "stopgenerators" event if there is no more generator to start,
+ * and a "startgenerators" event else.
+ * @return {sigma.chronos} Returns itself.
+ */
+ function startGenerators() {
+ if (!Object.keys(generators).length) {
+ self.dispatch('stopgenerators');
+ }else {
+ self.dispatch('startgenerators');
+
+ self.unbind('killed', onTaskEnded);
+ insertFrame(function() {
+ for (var k in generators) {
+ generators[k].on = true;
+ addTask(
+ generators[k].task,
+ k,
+ false
+ );
+ }
+ });
+
+ self.bind('killed', onTaskEnded).runTasks();
+ }
+
+ return self;
+ };
+
+ /**
+ * A callback triggered everytime the task of a generator stops, that will
+ * test the related generator's condition, and see if there is still any
+ * generator to start.
+ * @private
+ * @param {Object} e The sigma.chronos "killed" event.
+ */
+ function onTaskEnded(e) {
+ if (generators[e['content'].taskName] != undefined) {
+ if (generators[e['content'].taskName].del ||
+ !generators[e['content'].taskName].condition()) {
+ delete generators[e['content'].taskName];
+ }else {
+ generators[e['content'].taskName].on = false;
+ }
+
+ if (getGeneratorsCount(true) == 0) {
+ startGenerators();
+ }
+ }
+ };
+
+ /**
+ * Either set or returns the fpsReq property. This property determines
+ * the number of frames that should be inserted per second.
+ * @param {?number} v The frequency asked.
+ * @return {(Chronos|number)} Returns the frequency if v is undefined, and
+ * itself else.
+ */
+ function frequency(v) {
+ if (v != undefined) {
+ fpsReq = Math.abs(1 * v);
+ frameTime = 1000 / fpsReq;
+ framesCount = 0;
+ return self;
+ } else {
+ return fpsReq;
+ }
+ };
+
+ /**
+ * Returns the actual average number of frames that are inserted per
+ * second.
+ * @return {number} The actual average FPS.
+ */
+ function getFPS() {
+ if (isRunning) {
+ lastFPS =
+ Math.round(
+ framesCount /
+ ((new Date()).getTime() - currentTime) *
+ 10000
+ ) / 10;
+ }
+
+ return lastFPS;
+ };
+
+ /**
+ * Returns the number of tasks.
+ * @return {number} The number of tasks.
+ */
+ function getTasksCount() {
+ return tasks.length;
+ }
+
+ /**
+ * Returns the number of queued tasks.
+ * @return {number} The number of queued tasks.
+ */
+ function getQueuedTasksCount() {
+ return queuedTasks.length;
+ }
+
+ /**
+ * Returns how long sigma.chronos has active tasks running
+ * without interuption for, in ms.
+ * @return {number} The time chronos is running without interuption for.
+ */
+ function getExecutionTime() {
+ return startTime - currentTime;
+ }
+
+ this.frequency = frequency;
+
+ this.runTasks = runTasks;
+ this.stopTasks = stopTasks;
+ this.insertFrame = insertFrame;
+
+ this.addTask = addTask;
+ this.queueTask = queueTask;
+ this.removeTask = removeTask;
+
+ this.addGenerator = addGenerator;
+ this.removeGenerator = removeGenerator;
+ this.startGenerators = startGenerators;
+ this.getGeneratorsIDs = getGeneratorsIDs;
+
+ this.getFPS = getFPS;
+ this.getTasksCount = getTasksCount;
+ this.getQueuedTasksCount = getQueuedTasksCount;
+ this.getExecutionTime = getExecutionTime;
+
+ return this;
+})();
+
+sigma.publicPrototype = SigmaPublic.prototype;
+})();
+
+
--- /dev/null
+++ b/lib/Color.php
@@ -1,1 +1,502 @@
-
+<?php
+/**
+ *
+ * Color values manipulation utilities. Provides methods to convert from and to
+ * Hex, RGB, HSV and HSL color representattions.
+ *
+ * Several color conversion logic are based on pseudo-code from
+ * http://www.easyrgb.com/math.php
+ *
+ * @category Lux
+ *
+ * @package Lux_Color
+ *
+ * @author Rodrigo Moraes <rodrigo.moraes@gmail.com>
+ *
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ *
+ * @version $Id$
+ *
+ */
+class Lux_Color
+{
+ /**
+ *
+ * Converts hexadecimal colors to RGB.
+ *
+ * @param string $hex Hexadecimal value. Accepts values with 3 or 6 numbers,
+ * with or without #, e.g., CCC, #CCC, CCCCCC or #CCCCCC.
+ *
+ * @return array RGB values: 0 => R, 1 => G, 2 => B
+ *
+ */
+ public function hex2rgb($hex)
+ {
+ // Remove #.
+ if (strpos($hex, '#') === 0) {
+ $hex = substr($hex, 1);
+ }
+
+ if (strlen($hex) == 3) {
+ $hex .= $hex;
+ }
+
+ if (strlen($hex) != 6) {
+ return false;
+ }
+
+ // Convert each tuple to decimal.
+ $r = hexdec(substr($hex, 0, 2));
+ $g = hexdec(substr($hex, 2, 2));
+ $b = hexdec(substr($hex, 4, 2));
+
+ return array($r, $g, $b);
+ }
+
+ /**
+ *
+ * Converts hexadecimal colors to HSV.
+ *
+ * @param string $hex Hexadecimal value. Accepts values with 3 or 6 numbers,
+ * with or without #, e.g., CCC, #CCC, CCCCCC or #CCCCCC.
+ *
+ * @return array HSV values: 0 => H, 1 => S, 2 => V
+ *
+ */
+ public function hex2hsv($hex)
+ {
+ return $this->rgb2hsv($this->hex2rgb($hex));
+ }
+
+ /**
+ *
+ * Converts hexadecimal colors to HSL.
+ *
+ * @param string $hex Hexadecimal value. Accepts values with 3 or 6 numbers,
+ * with or without #, e.g., CCC, #CCC, CCCCCC or #CCCCCC.
+ *
+ * @return array HSL values: 0 => H, 1 => S, 2 => L
+ *
+ */
+ public function hex2hsl($hex)
+ {
+ return $this->rgb2hsl($this->hex2rgb($hex));
+ }
+
+ /**
+ *
+ * Converts RGB colors to hexadecimal.
+ *
+ * @param array $rgb RGB values: 0 => R, 1 => G, 2 => B
+ *
+ * @return string Hexadecimal value with six digits, e.g., CCCCCC.
+ *
+ */
+ public function rgb2hex($rgb)
+ {
+ if(count($rgb) < 3) {
+ return false;
+ }
+
+ list($r, $g, $b) = $rgb;
+
+ // From php.net.
+ $r = 0x10000 * max(0, min(255, $r));
+ $g = 0x100 * max(0, min(255, $g));
+ $b = max(0, min(255, $b));
+
+ return strtoupper(str_pad(dechex($r + $g + $b), 6, 0, STR_PAD_LEFT));
+ }
+
+ /**
+ *
+ * Converts RGB to HSV.
+ *
+ * @param array $rgb RGB values: 0 => R, 1 => G, 2 => B
+ *
+ * @return array HSV values: 0 => H, 1 => S, 2 => V
+ *
+ */
+ public function rgb2hsv($rgb)
+ {
+ // RGB values = 0 ÷ 255
+ $var_R = ($rgb[0] / 255);
+ $var_G = ($rgb[1] / 255);
+ $var_B = ($rgb[2] / 255);
+
+ // Min. value of RGB
+ $var_Min = min($var_R, $var_G, $var_B);
+
+ // Max. value of RGB
+ $var_Max = max($var_R, $var_G, $var_B);
+
+ // Delta RGB value
+ $del_Max = $var_Max - $var_Min;
+
+ $V = $var_Max;
+
+ // This is a gray, no chroma...
+ if ( $del_Max == 0 ) {
+ // HSV results = 0 ÷ 1
+ $H = 0;
+ $S = 0;
+ } else {
+ // Chromatic data...
+ $S = $del_Max / $var_Max;
+
+ $del_R = ((($var_Max - $var_R) / 6) + ($del_Max / 2)) / $del_Max;
+ $del_G = ((($var_Max - $var_G) / 6) + ($del_Max / 2)) / $del_Max;
+ $del_B = ((($var_Max - $var_B) / 6) + ($del_Max / 2)) / $del_Max;
+
+ if ($var_R == $var_Max) {
+ $H = $del_B - $del_G;
+ } else if ($var_G == $var_Max) {
+ $H = (1 / 3) + $del_R - $del_B;
+ } else if ($var_B == $var_Max) {
+ $H = (2 / 3) + $del_G - $del_R;
+ }
+
+ if ($H < 0) {
+ $H += 1;
+ }
+ if ($H > 1) {
+ $H -= 1;
+ }
+ }
+
+ // Returns agnostic values.
+ // Range will depend on the application: e.g. $H*360, $S*100, $V*100.
+ return array($H, $S, $V);
+ }
+
+ /**
+ *
+ * Converts RGB to HSL.
+ *
+ * @param array $rgb RGB values: 0 => R, 1 => G, 2 => B
+ *
+ * @return array HSL values: 0 => H, 1 => S, 2 => L
+ *
+ */
+ public function rgb2hsl($rgb)
+ {
+ // Where RGB values = 0 ÷ 255.
+ $var_R = $rgb[0] / 255;
+ $var_G = $rgb[1] / 255;
+ $var_B = $rgb[2] / 255;
+
+ // Min. value of RGB
+ $var_Min = min($var_R, $var_G, $var_B);
+ // Max. value of RGB
+ $var_Max = max($var_R, $var_G, $var_B);
+ // Delta RGB value
+ $del_Max = $var_Max - $var_Min;
+
+ $L = ($var_Max + $var_Min) / 2;
+
+ if ( $del_Max == 0 ) {
+ // This is a gray, no chroma...
+ // HSL results = 0 ÷ 1
+ $H = 0;
+ $S = 0;
+ } else {
+ // Chromatic data...
+ if ($L < 0.5) {
+ $S = $del_Max / ($var_Max + $var_Min);
+ } else {
+ $S = $del_Max / ( 2 - $var_Max - $var_Min );
+ }
+
+ $del_R = ((($var_Max - $var_R) / 6) + ($del_Max / 2)) / $del_Max;
+ $del_G = ((($var_Max - $var_G) / 6) + ($del_Max / 2)) / $del_Max;
+ $del_B = ((($var_Max - $var_B) / 6) + ($del_Max / 2)) / $del_Max;
+
+ if ($var_R == $var_Max) {
+ $H = $del_B - $del_G;
+ } else if ($var_G == $var_Max) {
+ $H = ( 1 / 3 ) + $del_R - $del_B;
+ } else if ($var_B == $var_Max) {
+ $H = ( 2 / 3 ) + $del_G - $del_R;
+ }
+
+ if ($H < 0) {
+ $H += 1;
+ }
+ if ($H > 1) {
+ $H -= 1;
+ }
+ }
+
+ return array($H, $S, $L);
+ }
+
+ /**
+ *
+ * Converts HSV colors to hexadecimal.
+ *
+ * @param array $hsv HSV values: 0 => H, 1 => S, 2 => V
+ *
+ * @return string Hexadecimal value with six digits, e.g., CCCCCC.
+ *
+ */
+ public function hsv2hex($hsv)
+ {
+ return $this->rgb2hex($this->hsv2rgb($hsv));
+ }
+
+ /**
+ *
+ * Converts HSV to RGB.
+ *
+ * @param array $hsv HSV values: 0 => H, 1 => S, 2 => V
+ *
+ * @return array RGB values: 0 => R, 1 => G, 2 => B
+ *
+ */
+ public function hsv2rgb($hsv)
+ {
+ $H = $hsv[0];
+ $S = $hsv[1];
+ $V = $hsv[2];
+
+ // HSV values = 0 ÷ 1
+ if ($S == 0) {
+ $R = $V * 255;
+ $G = $V * 255;
+ $B = $V * 255;
+ } else {
+ $var_h = $H * 6;
+ // H must be < 1
+ if ( $var_h == 6 ) {
+ $var_h = 0;
+ }
+ // Or ... $var_i = floor( $var_h )
+ $var_i = floor( $var_h );
+ $var_1 = $V * ( 1 - $S );
+ $var_2 = $V * ( 1 - $S * ( $var_h - $var_i ) );
+ $var_3 = $V * ( 1 - $S * ( 1 - ( $var_h - $var_i ) ) );
+
+ switch($var_i) {
+ case 0:
+ $var_r = $V;
+ $var_g = $var_3;
+ $var_b = $var_1;
+ break;
+ case 1:
+ $var_r = $var_2;
+ $var_g = $V;
+ $var_b = $var_1;
+ break;
+ case 2:
+ $var_r = $var_1;
+ $var_g = $V;
+ $var_b = $var_3;
+ break;
+ case 3:
+ $var_r = $var_1;
+ $var_g = $var_2;
+ $var_b = $V;
+ break;
+ case 4:
+ $var_r = $var_3;
+ $var_g = $var_1;
+ $var_b = $V;
+ break;
+ default:
+ $var_r = $V;
+ $var_g = $var_1;
+ $var_b = $var_2;
+ }
+
+ //RGB results = 0 ÷ 255
+ $R = $var_r * 255;
+ $G = $var_g * 255;
+ $B = $var_b * 255;
+ }
+
+ return array($R, $G, $B);
+ }
+
+ /**
+ *
+ * Converts HSV colors to HSL.
+ *
+ * @param array $hsv HSV values: 0 => H, 1 => S, 2 => V
+ *
+ * @return array HSL values: 0 => H, 1 => S, 2 => L
+ *
+ */
+ public function hsv2hsl($hsv)
+ {
+ return $this->rgb2hsl($this->hsv2rgb($hsv));
+ }
+
+ /**
+ *
+ * Converts hexadecimal colors to HSL.
+ *
+ * @param array $hsl HSL values: 0 => H, 1 => S, 2 => L
+ *
+ * @return string Hexadecimal value. Accepts values with 3 or 6 numbers,
+ * with or without #, e.g., CCC, #CCC, CCCCCC or #CCCCCC.
+ *
+ */
+ public function hsl2hex($hsl)
+ {
+ return $this->rgb2hex($this->hsl2rgb($hsl));
+ }
+
+ /**
+ *
+ * Converts HSL to RGB.
+ *
+ * @param array $hsv HSL values: 0 => H, 1 => S, 2 => L
+ *
+ * @return array RGB values: 0 => R, 1 => G, 2 => B
+ *
+ */
+ public function hsl2rgb($hsl)
+ {
+ list($H, $S, $L) = $hsl;
+
+ if ($S == 0) {
+ // HSL values = 0 ÷ 1
+ // RGB results = 0 ÷ 255
+ $R = $L * 255;
+ $G = $L * 255;
+ $B = $L * 255;
+ } else {
+ if ($L < 0.5) {
+ $var_2 = $L * (1 + $S);
+ } else {
+ $var_2 = ($L + $S) - ($S * $L);
+ }
+
+ $var_1 = 2 * $L - $var_2;
+
+ $R = 255 * $this->_hue2rgb($var_1, $var_2, $H + (1 / 3));
+ $G = 255 * $this->_hue2rgb($var_1, $var_2, $H);
+ $B = 255 * $this->_hue2rgb($var_1, $var_2, $H - (1 / 3));
+ }
+
+ return array($R, $G, $B);
+ }
+
+ /**
+ *
+ * Support method for hsl2rgb(): converts hue ro RGB.
+ *
+ * @param
+ *
+ * @param
+ *
+ * @param
+ *
+ * @return int
+ *
+ */
+ protected function _hue2rgb($v1, $v2, $vH)
+ {
+ if ($vH < 0) {
+ $vH += 1;
+ }
+
+ if ($vH > 1) {
+ $vH -= 1;
+ }
+
+ if ((6 * $vH) < 1) {
+ return ($v1 + ($v2 - $v1) * 6 * $vH);
+ }
+
+ if ((2 * $vH) < 1) {
+ return $v2;
+ }
+
+ if ((3 * $vH) < 2) {
+ return ($v1 + ($v2 - $v1) * (( 2 / 3) - $vH) * 6);
+ }
+
+ return $v1;
+ }
+
+ /**
+ *
+ * Converts hexadecimal colors to HSL.
+ *
+ * @param array $hsl HSL values: 0 => H, 1 => S, 2 => L
+ *
+ * @return array HSV values: 0 => H, 1 => S, 2 => V
+ *
+ */
+ public function hsl2hsv($hsl)
+ {
+ return $this->rgb2hsv($this->hsl2rgb($hsl));
+ }
+
+ /**
+ *
+ * Updates HSV values.
+ *
+ * @param array $hsv HSV values: 0 => H, 1 => S, 2 => V
+ *
+ * @param array $values Values to update: 0 => value to add to H (0 to 360),
+ * 1 and 2 => values to multiply S and V (0 to 100). Example:
+ *
+ * {{{code:php
+ * // Update saturation to 80% in the provided HSV.
+ * $hsv = array(120, 0.75, 0.75);
+ * $new_hsv = $color->updateHsv($hsv, array(null, 80, null));
+ * }}}
+ *
+ */
+ public function updateHsv($hsv, $values)
+ {
+ if (isset($values[0])) {
+ $hsv[0] = max(0, min(360, ($hsv[0] + $values[0])));
+ }
+
+ if (isset($values[1])) {
+ $hsv[1] = max(0, min(1, ($hsv[1] * ($values[1] / 100))));
+ }
+
+ if (isset($values[2])) {
+ $hsv[2] = max(0, min(1, ($hsv[2] * ($values[2] / 100))));
+ }
+
+ return $hsv;
+ }
+
+ /**
+ *
+ * Updates HSL values.
+ *
+ * @param array $hsl HSL values: 0 => H, 1 => S, 2 => L
+ *
+ * @param array $values Values to update: 0 => value to add to H (0 to 360),
+ * 1 and 2 => values to multiply S and V (0 to 100). Example:
+ *
+ * {{{code:php
+ * // Update saturation to 80% in the provided HSL.
+ * $hsl = array(120, 0.75, 0.75);
+ * $new_hsl = $color->updateHsl($hsl, array(null, 80, null));
+ * }}}
+ *
+ */
+ public function updateHsl($hsl, $values)
+ {
+ if (isset($values[0])) {
+ $hsl[0] = max(0, min(360, ($hsl[0] + $values[0])));
+ }
+
+ if (isset($values[1])) {
+ $hsl[1] = max(0, min(1, ($hsl[1] * ($values[1] / 100))));
+ }
+
+ if (isset($values[2])) {
+ $hsl[2] = max(0, min(1, ($hsl[2] * ($values[2] / 100))));
+ }
+
+ return $hsl;
+ }
+}
--- a/robots.txt
+++ b/robots.txt
@@ -3,3 +3,4 @@
User-agent: *
Disallow: /admin/
+Sitemap: http://orgs.disclosurelo.gs/sitemap.xml.php
--- a/score.php
+++ b/score.php
@@ -1,7 +1,7 @@
<?php
include_once('include/common.inc.php');
-include_header();
+include_header('Scores');
$db = $server->get_db('disclosr-agencies');
--- a/search.php
+++ b/search.php
@@ -1,6 +1,6 @@
<?php
include_once('include/common.inc.php');
-include_header();
+include_header('Search');
?>
<div class="foundation-header">
<h1><a href="search.php">Search</a></h1>
--- /dev/null
+++ b/sitemap.xml.php
@@ -1,1 +1,25 @@
+<?php
+include ('include/common.inc.php');
+$last_updated = date('Y-m-d', @filemtime('cbrfeed.zip'));
+header("Content-Type: text/xml");
+echo "<?xml version='1.0' encoding='UTF-8'?>";
+echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
+echo " <url><loc>" . local_url() . "index.php</loc><priority>1.0</priority></url>\n";
+foreach (scandir("./") as $file) {
+ if (strpos($file, ".php") !== false && $file != "index.php" && $file != "sitemap.xml.php")
+ echo " <url><loc>" . local_url() . "$file</loc><priority>0.3</priority></url>\n";
+}
+
+$db = $server->get_db('disclosr-agencies');
+try {
+ $rows = $db->get_view("app", "byCanonicalName")->rows;
+ foreach ($rows as $row) {
+ echo '<url><loc>' . local_url() . 'getAgency.php?id=' . $row->value->_id . "</loc><priority>0.6</priority></url>\n";
+ }
+} catch (SetteeRestClientException $e) {
+ setteErrorHandler($e);
+}
+echo '</urlset>';
+?>
+
--- a/unimplemented/foundation.html
+++ /dev/null
@@ -1,137 +1,1 @@
-<!DOCTYPE html>
-<!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ -->
-<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en"> <![endif]-->
-<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8" lang="en"> <![endif]-->
-<!--[if IE 8]> <html class="no-js lt-ie9" lang="en"> <![endif]-->
-<!--[if gt IE 8]><!--> <html lang="en"> <!--<![endif]-->
-<head>
- <meta charset="utf-8" />
-
- <!-- Set the viewport width to device width for mobile -->
- <meta name="viewport" content="width=device-width" />
-
- <title>Welcome to Foundation</title>
-
- <!-- Included CSS Files -->
- <link rel="stylesheet" href="stylesheets/foundation.css">
- <link rel="stylesheet" href="stylesheets/app.css">
-
- <!--[if lt IE 9]>
- <link rel="stylesheet" href="stylesheets/ie.css">
- <![endif]-->
-
-
- <!-- IE Fix for HTML5 Tags -->
- <!--[if lt IE 9]>
- <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
- <![endif]-->
-
-</head>
-<body>
-
- <!-- container -->
- <div class="container">
-
- <div class="row">
- <div class="twelve columns">
- <h2>Welcome to Foundation</h2>
- <p>This is version 2.1.4 released on December 19, 2011</p>
- <hr />
- </div>
- </div>
-
- <div class="row">
- <div class="eight columns">
- <h3>The Grid</h3>
-
- <!-- Grid Example -->
- <div class="row">
- <div class="twelve columns">
- <div class="panel">
- <p>This is a twelve column section in a row. Each of these includes a div.panel element so you can see where the columns are - it's not required at all for the grid.</p>
- </div>
- </div>
- </div>
- <div class="row">
- <div class="six columns">
- <div class="panel">
- <p>Six columns</p>
- </div>
- </div>
- <div class="six columns">
- <div class="panel">
- <p>Six columns</p>
- </div>
- </div>
- </div>
- <div class="row">
- <div class="four columns">
- <div class="panel">
- <p>Four columns</p>
- </div>
- </div>
- <div class="four columns">
- <div class="panel">
- <p>Four columns</p>
- </div>
- </div>
- <div class="four columns">
- <div class="panel">
- <p>Four columns</p>
- </div>
- </div>
- </div>
-
- <h3>Tabs</h3>
- <dl class="tabs">
- <dd><a href="#simple1" class="active">Simple Tab 1</a></dd>
- <dd><a href="#simple2">Simple Tab 2</a></dd>
- <dd><a href="#simple3">Simple Tab 3</a></dd>
- </dl>
-
- <ul class="tabs-content">
- <li class="active" id="simple1Tab">This is simple tab 1's content. Pretty neat, huh?</li>
- <li id="simple2Tab">This is simple tab 2's content. Now you see it!</li>
- <li id="simple3Tab">This is simple tab 3's content. It's, you know...okay.</li>
- </ul>
-
- <h3>Buttons</h3>
-
- <p><a href="#" class="small blue button">Small Blue Button</a></p>
- <p><a href="#" class="blue button">Medium Blue Button</a></p>
- <p><a href="#" class="large blue button">Large Blue Button</a></p>
-
- <p><a href="#" class="nice radius small blue button">Nice Blue Button</a></p>
- <p><a href="#" class="nice radius blue button">Nice Blue Button</a></p>
- <p><a href="#" class="nice radius large blue button">Nice Blue Button</a></p>
-
- </div>
-
- <div class="four columns">
- <h4>Getting Started</h4>
- <p>We're stoked you want to try Foundation! To get going, this file (index.html) includes some basic styles you can modify, play around with, or totally destroy to get going.</p>
-
- <h4>Other Resources</h4>
- <p>Once you've exhausted the fun in this document, you should check out:</p>
- <ul class="disc">
- <li><a href="http://foundation.zurb.com/docs">Foundation Documentation</a><br />Everything you need to know about using the framework.</li>
- <li><a href="http://github.com/zurb/foundation">Foundation on Github</a><br />Latest code, issue reports, feature requests and more.</li>
- <li><a href="http://twitter.com/foundationzurb">@foundationzurb</a><br />Ping us on Twitter if you have questions. If you build something with this we'd love to see it (and send you a totally boss sticker).</li>
- </ul>
- </div>
- </div>
-
- </div>
- <!-- container -->
-
-
-
-
- <!-- Included JS Files -->
- <script src="javascripts/foundation.js"></script>
- <script src="javascripts/app.js"></script>
-
-</body>
-</html>
-
--- a/unimplemented/humans.txt
+++ /dev/null
@@ -1,8 +1,1 @@
-/* Foundation was made by ZURB, an interaction design and design strategy firm in Campbell, CA */
-/* zurb.com */
-/* humanstxt.org */
-/* SITE */
- Standards: HTML5, CSS3
- Components: jQuery, Orbit, Reveal
- Software: Coda, Textmate, Git
--- a/unimplemented/lastUpdated.php
+++ /dev/null
@@ -1,2 +1,1 @@
-for each agency, record when last changed (number of days too) and show a couple of URLs that were in that change
--- a/unimplemented/validation.php
+++ /dev/null
--- a/unimplemented/webservers.php
+++ /dev/null
@@ -1,1 +1,1 @@
-for each agency, find a scrapped document and read the webserver off it
+
--- /dev/null
+++ b/webserver.php
@@ -1,1 +1,43 @@
+<?php
+include_once('include/common.inc.php');
+include_header('Webserver and Accessiblity');
+
+echo "<table>
+ <tr><th>name</th><th>webserver</th><th>accessiblity errors</th></tr>";
+$agenciesdb = $server->get_db('disclosr-agencies');
+$docsdb = $server->get_db('disclosr-documents');
+try {
+ $rows = $agenciesdb->get_view("app", "all", null, true)->rows;
+
+
+ if ($rows) {
+ foreach ($rows as $row) {
+
+ echo "<tr><td>" . $row->value->name . "</td>";
+ if (isset($row->value->website)) {
+ try {
+ $website = $docsdb->get(md5($row->value->website));
+ $serverParts = explode(" ",$website->web_server);
+ echo "<td>" . $serverParts[0] . "</td>";
+ if (!isset($website->validation)) {
+ echo "<td>?</td>";
+ } else {
+ if ($website->validation == "") {
+ echo "<td>No error</td>";
+ } else {
+ echo "<td><pre>" . str_replace("<", "<", $website->validation) . "</pre></td>";
+ }
+ }
+ } catch (SetteeRestClientException $e) {
+ // setteErrorHandler($e);
+ }
+ }
+ echo "</tr>";
+ }
+ }
+} catch (SetteeRestClientException $e) {
+ setteErrorHandler($e);
+}
+include_footer();
+?>