[submodule "couchdb/couchdb-lucene"] | [submodule "couchdb/couchdb-lucene"] |
path = couchdb/couchdb-lucene | path = couchdb/couchdb-lucene |
url = https://github.com/rnewson/couchdb-lucene.git | url = https://github.com/rnewson/couchdb-lucene.git |
[submodule "couchdb/settee"] | [submodule "couchdb/settee"] |
path = couchdb/settee | path = couchdb/settee |
url = https://github.com/inadarei/settee.git | url = https://github.com/inadarei/settee.git |
[submodule "lib/springy"] | |
path = lib/springy | |
url = https://github.com/dhotson/springy.git | |
[submodule "lib/php-diff"] | [submodule "lib/php-diff"] |
path = lib/php-diff | path = lib/php-diff |
url = https://github.com/chrisboulton/php-diff.git | url = https://github.com/chrisboulton/php-diff.git |
[submodule "lib/Requests"] | [submodule "lib/Requests"] |
path = lib/Requests | path = lib/Requests |
url = https://github.com/rmccue/Requests.git | url = https://github.com/rmccue/Requests.git |
[submodule "javascripts/flotr2"] | [submodule "javascripts/flotr2"] |
path = javascripts/flotr2 | path = javascripts/flotr2 |
url = https://github.com/HumbleSoftware/Flotr2.git | url = https://github.com/HumbleSoftware/Flotr2.git |
[submodule "lib/phpquery"] | [submodule "lib/phpquery"] |
path = lib/phpquery | path = lib/phpquery |
url = https://github.com/TobiaszCudnik/phpquery.git | url = https://github.com/TobiaszCudnik/phpquery.git |
[submodule "javascripts/sigma"] | |
path = javascripts/sigma | |
url = https://github.com/jacomyal/sigma.js.git | |
[submodule "javascripts/bubbletree"] | |
path = javascripts/bubbletree | |
url = https://github.com/okfn/bubbletree.git | |
<?php | <?php |
include_once('include/common.inc.php'); | include_once('include/common.inc.php'); |
include_header(); | include_header(); |
?> | ?> |
<div class="foundation-header"> | <div class="foundation-header"> |
<h1><a href="about.php">About/FAQ</a></h1> | <h1><a href="about.php">About/FAQ</a></h1> |
<h4 class="subheader">Lorem ipsum.</h4> | <h4 class="subheader">Lorem ipsum.</h4> |
</div> | </div> |
<h2> What is this? </h2> | <h2> What is this? </h2> |
Disclosr is a project to monitor Australian Federal Government agencies | Disclo.gs is a project to monitor Australian Federal Government agencies |
compliance with their <a href="http://www.oaic.gov.au/publications/other_operational/foi_policy_frequently_asked_questions.html#_Toc291837571">"proactive disclosure requirements"</a>. | compliance with their <a href="http://www.oaic.gov.au/publications/other_operational/foi_policy_frequently_asked_questions.html#_Toc291837571">"proactive disclosure requirements"</a>. |
OGRE (Open Government Realization Evaluation) is a ranking of compliance with these requirements. | |
Prometheus is the agent which polls agency websites to assess compliance. | |
<h2> Open everything </h2> | <h2> Open everything </h2> |
All documents released CC-BY 3 AU | All documents released CC-BY 3 AU |
Open source git @ | Open source git @ |
<h2>Organisational Data Sources</h2> | <h2>Organisational Data Sources</h2> |
http://www.comlaw.gov.au/Browse/Results/ByTitle/AdministrativeArrangementsOrders/Current/Ad/0 defines departments | http://www.comlaw.gov.au/Browse/Results/ByTitle/AdministrativeArrangementsOrders/Current/Ad/0 defines departments |
Agencies can be found in the Schedule to an Appropriation Bill (budget), Schedule to FMA Regulations and/or Public Service Act.<br> | Agencies can be found in the Schedule to an Appropriation Bill (budget), Schedule to FMA Regulations and/or Public Service Act.<br> |
http://www.finance.gov.au/publications/flipchart/docs/FMACACFlipchart.pdf summarises these. view-source:https://www.tenders.gov.au/?event=public.advancedsearch.home is great for the suspended/active status<br> | http://www.finance.gov.au/publications/flipchart/docs/FMACACFlipchart.pdf summarises these. view-source:https://www.tenders.gov.au/?event=public.advancedsearch.home is great for the suspended/active status<br> |
Fraud in gov depts by Fairfax Media http://www.smh.com.au/national/public-service-keeps-fraud-cases-private-20110923-1kpdr.html | Fraud in gov depts by Fairfax Media http://www.smh.com.au/national/public-service-keeps-fraud-cases-private-20110923-1kpdr.html |
When defining the hierachy, this system is designed towards monitoring accountablity. Thus large agencies that have registered their own ABN | When defining the hierachy, this system is designed towards monitoring accountablity. Thus large agencies that have registered their own ABN |
and have their own accountablity mechanisms/website receive a seperate record as a child of their department. | and have their own accountablity mechanisms/website receive a seperate record as a child of their department. |
Some small agencies will choose to simply rely on their parent department's accountablity measures.<br> | Some small agencies will choose to simply rely on their parent department's accountablity measures.<br> |
This flows through to organisation name and other/past names. A department that completely accounts for an agency will list that agency as an other child name. | This flows through to organisation name and other/past names. A department that completely accounts for an agency will list that agency as an other child name. |
As agencies themselves shift between departments, there may be scope for providing time ranges but typically the newest hierarchy will be the one recorded. | As agencies themselves shift between departments, there may be scope for providing time ranges but typically the newest hierarchy will be the one recorded. |
A department/agency name will be the newest active name assigned to that ABN.<br> | A department/agency name will be the newest active name assigned to that ABN.<br> |
ABN information is derived from the ABR. This is the definitive umpire about which former name should be linked to which current name. | ABN information is derived from the ABR. This is the definitive umpire about which former name should be linked to which current name. |
For example "Department of Transport and Regional Services" became "Department of Infrastructure, Transport, Regional Development and Local Government" (same ABN) | For example "Department of Transport and Regional Services" became "Department of Infrastructure, Transport, Regional Development and Local Government" (same ABN) |
however it later split into "Department of Infrastructure and Transport" (same ABN) | however it later split into "Department of Infrastructure and Transport" (same ABN) |
and "Department of Regional Australia, Regional Development and Local Government" (new ABN).<br> | and "Department of Regional Australia, Regional Development and Local Government" (new ABN).<br> |
Statistical information from http://www.apsc.gov.au/stateoftheservice/1011/statsbulletin/section1.html#t2total https://www.apsedii.gov.au/apsedii/CustomQueryx33.shtml | Statistical information from http://www.apsc.gov.au/stateoftheservice/1011/statsbulletin/section1.html#t2total https://www.apsedii.gov.au/apsedii/CustomQueryx33.shtml |
and individual annual reports.<br> | and individual annual reports.<br> |
<h2>Webpage Assessment</h2> | <h2>Webpage Assessment</h2> |
Much due care has been put into correctly recording disclosure URLs. Typically the "About", "Corporate", "Publications" and "Sitemap" sections are checked at the very least. | Much due care has been put into correctly recording disclosure URLs. Typically the "About", "Corporate", "Publications" and "Sitemap" sections are checked at the very least. |
Occasionally it is nessicary to use a site or Google search. In several rare cases, there is a secret "Disclosure" navigation menu you can find if you find one of the mandatory publishing obligations in that category (seriously).<br> | Occasionally it is nessicary to use a site or Google search. In several rare cases, there is a secret "Disclosure" navigation menu you can find if you find one of the mandatory publishing obligations in that category (seriously).<br> |
Some rules about leniency:<br> | Some rules about leniency:<br> |
<ul> | <ul> |
<li>An empty FOI disclosure log counts, a page outlining what the FOI Act is does not.</li> | <li>An empty FOI disclosure log counts, a page outlining what the FOI Act is does not.</li> |
<li>A disclosure log in PDF or Word format counts :(</li> | <li>A disclosure log in PDF or Word format counts :(</li> |
<li>An empty File/Record list counts (although that's very minimalistic that you have no files, electronic or paper)</li> | <li>An empty File/Record list counts (although that's very minimalistic that you have no files, electronic or paper)</li> |
<li>Only a current information publication scheme page counts, not a s.9 FOI Act page or an organisation chart.</li> | <li>Only a current information publication scheme page counts, not a s.9 FOI Act page or an organisation chart.</li> |
<li>If there isn't a page easily listing all current and past Annual Reports, the most current one (html, pdf) counts.</li> | <li>If there isn't a page easily listing all current and past Annual Reports, the most current one (html, pdf) counts.</li> |
<li>Consultancy contracts might not need it's own webpage (if in Annual Report), grants/appointments might not apply to all organisations but Legal Services Expenditure (and all other obligations) does need a webpage. </li> | <li>Consultancy contracts might not need it's own webpage (if in Annual Report), grants/appointments might not apply to all organisations but Legal Services Expenditure (and all other obligations) does need a webpage. </li> |
<h2>Open Government Scoring</h2> | <h2>Open Government Scoring</h2> |
+1 point for every true Has... attribute<br> | +1 point for every true Has... attribute<br> |
-1 point for every false Has... (ie. Has Not) attribute</br> | -1 point for every false Has... (ie. Has Not) attribute</br> |
Don't like this? Make your own score, suggest a better scoring mechanism.</br> | Don't like this? Make your own score, suggest a better scoring mechanism.</br> |
<?php | <?php |
include_footer(); | include_footer(); |
?> | ?> |
<?php | |
include_once('../include/common.inc.php'); | |
include_header(); | |
require_once '../lib/php-diff/lib/Diff.php'; | |
require_once '../lib/php-diff/lib/Diff/Renderer/Html/SideBySide.php'; | |
$db = $server->get_db('disclosr-agencies'); | |
try { | |
$rows = $db->get_view("app", "getConflicts", null, true)->rows; | |
//print_r($rows); | |
foreach ($rows as $row) { | |
echo "<h2>".$row->id."</h2>"; | |
$request = Requests::get($serverAddr."disclosr-agencies/".$row->id); | |
$origSort = object_to_array(json_decode($request->body)); | |
ksort($origSort); | |
$origDoc = explode(",",json_encode($origSort)); | |
foreach($row->value as $conflictRev) { | |
$conflictURL = $serverAddr."disclosr-agencies/".$row->id."?rev=".$conflictRev; | |
$request = Requests::get($conflictURL); | |
$conflictSort = object_to_array(json_decode($request->body)); | |
ksort($conflictSort); | |
$conflictDoc = explode(",",json_encode($conflictSort)); | |
echo "curl -X DELETE ".$conflictURL."<br>".PHP_EOL; | |
// Options for generating the diff | |
$options = array( | |
//'ignoreWhitespace' => true, | |
//'ignoreCase' => true, | |
); | |
// Initialize the diff class | |
$diff = new Diff($conflictDoc, $origDoc, $options); | |
// Generate a side by side diff | |
$renderer = new Diff_Renderer_Html_SideBySide; | |
echo $diff->Render($renderer); | |
} | |
die(); | |
} | |
} catch (SetteeRestClientException $e) { | |
setteErrorHandler($e); | |
} | |
include_footer(); | |
?> | |
<?php | |
$nodes = Array(Array("id" => "gov", "label" => "Federal Government")); | |
$edges = Array(); | |
function addEdge($source, $target) { | |
global $edges; | |
$edges[] = Array("id" => md5($source . $target), "source" => $source, "target" => $target); | |
} | |
function addNode($id, $label, $pid) { | |
global $nodes; | |
$nodes[] = Array("id" => $id, "label" => $label , "pid" => $pid); | |
} | |
function addChildren($parentID, $parentXML) { | |
foreach ($parentXML as $childXML) { | |
if ($childXML->getName() == "organization" || $childXML->getName() == "organizationalUnit" || $childXML->getName() == "person") { | |
$attr = $childXML->attributes(); | |
$id = $attr['UUID']; | |
if ($childXML->getName() == "organization" || $childXML->getName() == "organizationalUnit") { | |
$label = $childXML->name; | |
} else if ($childXML->getName() == "person") { | |
$label = $childXML->fullName; | |
} | |
addNode($id, $label, $parentID); | |
addEdge($id, $parentID); | |
addChildren($id, $childXML); | |
} | |
} | |
} | |
if (file_exists('directoryexport.xml')) { | |
$xml = simplexml_load_file('directoryexport.xml'); | |
addChildren("gov", $xml); | |
} else { | |
exit('Failed to open directoryexport.xml'); | |
} | |
header('Content-Type: application/gexf+xml'); | |
echo '<?xml version="1.0" encoding="UTF-8"?> | |
<gexf xmlns="http://www.gexf.net/1.2draft" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd" version="1.2"> | |
<graph mode="static" defaultedgetype="directed"> | |
<nodes>'; | |
foreach ($nodes as $node) { | |
echo ' <node id="' . $node['id'] . '" label="' . htmlentities($node['label'],ENT_XML1) . '" ' . (isset($node['pid']) ? 'pid="' . $node['pid'] . '"' : "") . ' />'; | |
} | |
echo '</nodes> | |
<edges>'; | |
foreach ($edges as $edge) { | |
echo ' <edge id="' . $edge['id'] . '" source="' . $edge['source'] . '" target="' . $edge['target'] . '" />'; | |
} | |
echo '</edges> | |
</graph> | |
</gexf>'; | |
?> | |
<?php | <?php |
include_once("../include/common.inc.php"); | include_once("../include/common.inc.php"); |
$format = "csv"; | $format = "csv"; |
//$format = "json"; | //$format = "json"; |
if (isset($_REQUEST['format'])) $format = $_REQUEST['format']; | if (isset($_REQUEST['format'])) $format = $_REQUEST['format']; |
setlocale(LC_CTYPE, 'C'); | setlocale(LC_CTYPE, 'C'); |
if ($format == "csv") { | if ($format == "csv") { |
$headers = Array("name"); | $headers = Array("name"); |
} else { | } else { |
$headers = Array(); | $headers = Array(); |
} | } |
$db = $server->get_db('disclosr-agencies'); | $db = $server->get_db('disclosr-agencies'); |
try { | try { |
$rows = $db->get_view("app", "all", null, true)->rows; | $rows = $db->get_view("app", "all", null, true)->rows; |
$dataValues = Array(); | $dataValues = Array(); |
foreach ($rows as $row) { | foreach ($rows as $row) { |
if (isset($row->value->statistics->employees)) { | if (isset($row->value->statistics->employees)) { |
$headers = array_unique(array_merge($headers, array_keys(object_to_array($row->value->statistics->employees)))); | $headers = array_unique(array_merge($headers, array_keys(object_to_array($row->value->statistics->employees)))); |
} | } |
} | } |
} catch (SetteeRestClientException $e) { | } catch (SetteeRestClientException $e) { |
setteErrorHandler($e); | setteErrorHandler($e); |
} | } |
$fp = fopen('php://output', 'w'); | $fp = fopen('php://output', 'w'); |
if ($fp && $db) { | if ($fp && $db) { |
if ($format == "csv") { | if ($format == "csv") { |
header('Content-Type: text/csv; charset=utf-8'); | header('Content-Type: text/csv; charset=utf-8'); |
header('Content-Disposition: attachment; filename="export.employeestats.' . date("c") . '.csv"'); | header('Content-Disposition: attachment; filename="export.employeestats.' . date("c") . '.csv"'); |
} | } |
header('Pragma: no-cache'); | header('Pragma: no-cache'); |
header('Expires: 0'); | header('Expires: 0'); |
if ($format == "csv") { | if ($format == "csv") { |
fputcsv($fp, $headers); | fputcsv($fp, $headers); |
} else if ($format == "json") { | } else if ($format == "json") { |
echo '{ | echo '{ |
"labels" : ["' . implode('","', $headers) . '"],'.PHP_EOL; | "labels" : ["' . implode('","', $headers) . '"],'.PHP_EOL; |
} | } |
try { | try { |
$agencies = $db->get_view("app", "all", null, true)->rows; | $agencies = $db->get_view("app", "all", null, true)->rows; |
//print_r($agencies); | //print_r($agencies); |
$first = true; | $first = true; |
if ($format == "json") { | if ($format == "json") { |
echo '"data" : ['.PHP_EOL; | echo '"data" : ['.PHP_EOL; |
} | } |
foreach ($agencies as $agency) { | foreach ($agencies as $agency) { |
if (isset($agency->value->statistics->employees)) { | if (isset($agency->value->statistics->employees)) { |
$row = Array(); | $row = Array(); |
$agencyEmployeesArray = object_to_array($agency->value->statistics->employees); | $agencyEmployeesArray = object_to_array($agency->value->statistics->employees); |
foreach ($headers as $i => $fieldName) { | foreach ($headers as $i => $fieldName) { |
if (isset($agencyEmployeesArray[$fieldName])) { | if (isset($agencyEmployeesArray[$fieldName])) { |
$row[] = '['.$i.','.$agencyEmployeesArray[$fieldName]["value"].']'; | $row[] = '['.$i.','.$agencyEmployeesArray[$fieldName]["value"].']'; |
} else { | } else { |
$row[] = '['.$i.',0]'; | $row[] = '['.$i.',0]'; |
} | } |
} | } |
if ($format == "csv") { | if ($format == "csv") { |
fputcsv($fp, array_values($row)); | fputcsv($fp, array_values($row)); |
} else if ($format == "json") { | } else if ($format == "json") { |
if (!$first) echo ","; | if (!$first) echo ","; |
echo '{"data" : [' . implode(",", array_values($row)) . '], "label": "'.$agency->value->name.'", "lines" : { "show" : true }, "points" : { "show" : true }}'.PHP_EOL; | echo '{"data" : [' . implode(",", array_values($row)) . '], "label": "'.$agency->value->name.'", "lines" : { "show" : true }, "points" : { "show" : true }}'.PHP_EOL; |
$first = false; | $first = false; |
} | } |
} | } |
} | } |
if ($format == "json") { | if ($format == "json") { |
echo '] | echo '] |
}'.PHP_EOL; | }'.PHP_EOL; |
} | } |
} catch (SetteeRestClientException $e) { | } catch (SetteeRestClientException $e) { |
setteErrorHandler($e); | setteErrorHandler($e); |
} | } |
die; | die; |
} | } |
?> | ?> |
<?php | <?php |
require_once '../include/common.inc.php'; | require_once '../include/common.inc.php'; |
$db = $server->get_db('disclosr-agencies'); | $db = $server->get_db('disclosr-agencies'); |
$rows = $db->get_view("app", "byName")->rows; | $rows = $db->get_view("app", "byName")->rows; |
$nametoid = Array(); | $nametoid = Array(); |
$sums = Array(); | $sums = Array(); |
foreach ($rows as $row) { | foreach ($rows as $row) { |
$nametoid[trim($row->key)] = $row->value; | $nametoid[trim($row->key)] = $row->value; |
} | } |
$employeeCSVs = Array("2002-2003" => "0203apsemployees.csv", | $employeeCSVs = Array("2002-2003" => "0203apsemployees.csv", |
"2003-2004" => "0304apsemployees.csv", | "2003-2004" => "0304apsemployees.csv", |
"2004-2005" => "0405apsemployees.csv", | "2004-2005" => "0405apsemployees.csv", |
"2005-2006" => "0506apsemployees.csv", | "2005-2006" => "0506apsemployees.csv", |
"2006-2007" => "0607apsemployees.csv", | "2006-2007" => "0607apsemployees.csv", |
"2007-2008" => "0708apsemployees.csv", | "2007-2008" => "0708apsemployees.csv", |
"2008-2009" => "0809apsemployees.csv", | "2008-2009" => "0809apsemployees.csv", |
"2009-2010" => "0910apsemployees.csv", | "2009-2010" => "0910apsemployees.csv", |
"2010-2011" => "1011apsemployees.csv" | "2010-2011" => "1011apsemployees.csv" |
); | ); |
foreach ($employeeCSVs as $timePeriod => $employeeCSV) { | foreach ($employeeCSVs as $timePeriod => $employeeCSV) { |
echo $employeeCSV . "<br>" . PHP_EOL; | echo $employeeCSV . "<br>" . PHP_EOL; |
$row = 1; | $row = 1; |
if (($handle = fopen($employeeCSV, "r")) !== FALSE) { | if (($handle = fopen($employeeCSV, "r")) !== FALSE) { |
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) { | while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) { |
//print_r($data); | //print_r($data); |
$name = trim($data[0]); | $name = trim($data[0]); |
if (isset($nametoid[$name])) { | if (isset($nametoid[$name])) { |
$id = $nametoid[$name]; | $id = $nametoid[$name]; |
//echo $id . "<br>" . PHP_EOL; | //echo $id . "<br>" . PHP_EOL; |
@$sums[$id][$timePeriod] += $data[1]; | @$sums[$id][$timePeriod] += $data[1]; |
} else { | } else { |
echo "<br>ERROR NAME MISSING FROM ID LIST<br><bR>" . PHP_EOL; | echo "<br>ERROR NAME MISSING FROM ID LIST<br><bR>" . PHP_EOL; |
die(); | die(); |
} | } |
} | } |
fclose($handle); | fclose($handle); |
} | } |
} | } |
foreach ($sums as $id => $sum) { | foreach ($sums as $id => $sum) { |
echo $id. "<br>" . PHP_EOL; | echo $id . "<br>" . PHP_EOL; |
$doc = $db->get($id); | $doc = $db->get($id); |
// print_r($doc); | echo $doc->name . "<br>" . PHP_EOL; |
if (isset($doc->statistics)) $doc->statistics = Array(); | // print_r($doc); |
$changed = false; | |
if (!isset($doc->statistics)) { | |
$changed = true; | |
$doc->statistics = Array(); | |
} | |
foreach ($sum as $timePeriod => $value) { | foreach ($sum as $timePeriod => $value) { |
$doc->statistics["employees"][$timePeriod] = Array("value"=>$value, "source"=>"http://apsc.gov.au/stateoftheservice/"); | if (!isset($doc->statistics->employees->$timePeriod->value) |
|| $doc->statistics->employees->$timePeriod->value != $value) { | |
$changed = true; | |
$doc->statistics["employees"][$timePeriod] = Array("value" => $value, "source" => "http://apsc.gov.au/stateoftheservice/"); | |
} | |
} | } |
$db->save($doc); | if ($changed) { |
$db->save($doc); | |
} else { | |
echo "not changed" . "<br>" . PHP_EOL; | |
} | |
} | } |
// employees: timeperiod, source = apsc state of service, value | // employees: timeperiod, source = apsc state of service, value |
?> | ?> |
#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) | |
<!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> | |
<?php | <?php |
include_once('include/common.inc.php'); | include_once('include/common.inc.php'); |
include_header(); | include_header(); |
function displayValue($key, $value, $mode) { | function displayValue($key, $value, $mode) { |
global $db, $schemas; | global $db, $schemas; |
if ($mode == "view") { | if ($mode == "view") { |
echo "<tr>"; | echo "<tr>"; |
echo "<td>" . $schemas['agency']["properties"][$key]['x-title'] . "<br><small>" . $schemas['agency']["properties"][$key]['description'] . "</small></td><td>"; | echo "<td>" . $schemas['agency']["properties"][$key]['x-title'] . "<br><small>" . $schemas['agency']["properties"][$key]['description'] . "</small></td><td>"; |
if (is_array($value)) { | if (is_array($value)) { |
echo "<ol>"; | echo "<ol>"; |
foreach ($value as $subkey => $subvalue) { | foreach ($value as $subkey => $subvalue) { |
if (isset($schemas['agency']["properties"][$key]['x-property'])) { | if (isset($schemas['agency']["properties"][$key]['x-property'])) { |
echo '<li property="' . $schemas['agency']["properties"][$key]['x-property'] . '">'; | echo '<li property="' . $schemas['agency']["properties"][$key]['x-property'] . '">'; |
} else { | } else { |
echo "<li>"; | echo "<li>"; |
} | } |
echo "$subvalue</li>"; | echo "$subvalue</li>"; |
} | } |
echo "</ol></td></tr>"; | echo "</ol></td></tr>"; |
} else { | } else { |
if (isset($schemas['agency']["properties"][$key]['x-property'])) { | if (isset($schemas['agency']["properties"][$key]['x-property'])) { |
echo '<span property="' . $schemas['agency']["properties"][$key]['x-property'] . '">'; | echo '<span property="' . $schemas['agency']["properties"][$key]['x-property'] . '">'; |
} else { | } else { |
echo "<span>"; | echo "<span>"; |
} | } |
if ((strpos($key, "URL") > 0 || $key == 'website') && $value != "") { | if ((strpos($key, "URL") > 0 || $key == 'website') && $value != "") { |
echo "<a href='$value'>view</a></span>"; | echo "<a href='$value'>view</a></span>"; |
} else { | } else { |
echo "$value</span>"; | echo "$value</span>"; |
} | } |
} | } |
echo "</td></tr>"; | echo "</td></tr>"; |
} | } |
if ($mode == "edit") { | if ($mode == "edit") { |
if (is_array($value)) { | if (is_array($value)) { |
echo '<div class="row"> | echo '<div class="row"> |
<div class="seven columns"> | <div class="seven columns"> |
<fieldset> | <fieldset> |
<h5>' . $key . '</h5>'; | <h5>' . $key . '</h5>'; |
foreach ($value as $subkey => $subvalue) { | foreach ($value as $subkey => $subvalue) { |
echo "<label>$subkey</label><input class='input-text' type='text' id='$key$subkey' name='$key" . '[' . $subkey . "]' value='$subvalue'/></tr>"; | echo "<label>$subkey</label><input class='input-text' type='text' id='$key$subkey' name='$key" . '[' . $subkey . "]' value='$subvalue'/></tr>"; |
} | } |
echo "</fieldset> | echo "</fieldset> |
</div> | </div> |
</div>"; | </div>"; |
} else { | } else { |
if (strpos($key, "_") === 0) { | if (strpos($key, "_") === 0) { |
echo"<input type='hidden' id='$key' name='$key' value='$value'/>"; | echo"<input type='hidden' id='$key' name='$key' value='$value'/>"; |
} else if ($key == "parentOrg") { | } else if ($key == "parentOrg") { |
echo "<label for='$key'>$key</label><select id='$key' name='$key'><option value=''> Select... </option>"; | echo "<label for='$key'>$key</label><select id='$key' name='$key'><option value=''> Select... </option>"; |
$rows = $db->get_view("app", "byDeptStateName")->rows; | $rows = $db->get_view("app", "byDeptStateName")->rows; |
//print_r($rows); | //print_r($rows); |
foreach ($rows as $row) { | foreach ($rows as $row) { |
echo "<option value='{$row->value}'" . (($row->value == $value) ? "SELECTED" : "") . " >" . str_replace("Department of ", "", $row->key) . "</option>"; | echo "<option value='{$row->value}'" . (($row->value == $value) ? "SELECTED" : "") . " >" . str_replace("Department of ", "", $row->key) . "</option>"; |
} | } |
echo" </select>"; | echo" </select>"; |
} else { | } else { |
echo "<label>$key</label><input class='input-text' type='text' id='$key' name='$key' value='$value'/>"; | echo "<label>$key</label><input class='input-text' type='text' id='$key' name='$key' value='$value'/>"; |
if ((strpos($key, "URL") > 0 || $key == 'website') && $value != "") { | if ((strpos($key, "URL") > 0 || $key == 'website') && $value != "") { |
echo "<a href='$value'>view</a>"; | echo "<a href='$value'>view</a>"; |
} | } |
if ($key == 'abn') { | if ($key == 'abn') { |
echo "<a href='http://www.abr.business.gov.au/SearchByAbn.aspx?SearchText=$value'>view abn</a>"; | echo "<a href='http://www.abr.business.gov.au/SearchByAbn.aspx?SearchText=$value'>view abn</a>"; |
} | } |
} | } |
} | } |
} | } |
// | // |
} | } |
function addDefaultFields($row) { | function addDefaultFields($row) { |
global $schemas; | global $schemas; |
$defaultFields = array_keys($schemas['agency']['properties']); | $defaultFields = array_keys($schemas['agency']['properties']); |
foreach ($defaultFields as $defaultField) { | foreach ($defaultFields as $defaultField) { |
if (!isset($row[$defaultField])) { | if (!isset($row[$defaultField])) { |
if ($schemas['agency']['properties'][$defaultField]['type'] == "string") { | if ($schemas['agency']['properties'][$defaultField]['type'] == "string") { |
$row[$defaultField] = ""; | $row[$defaultField] = ""; |
} | } |
if ($schemas['agency']['properties'][$defaultField]['type'] == "array") { | if ($schemas['agency']['properties'][$defaultField]['type'] == "array") { |
$row[$defaultField] = Array(""); | $row[$defaultField] = Array(""); |
} | } |
} else if ($schemas['agency']['properties'][$defaultField]['type'] == "array") { | } else if ($schemas['agency']['properties'][$defaultField]['type'] == "array") { |
if (is_array($row[$defaultField])) { | if (is_array($row[$defaultField])) { |
$row[$defaultField][] = ""; | $row[$defaultField][] = ""; |
$row[$defaultField][] = ""; | $row[$defaultField][] = ""; |
$row[$defaultField][] = ""; | $row[$defaultField][] = ""; |
} else { | } else { |
$value = $row[$defaultField]; | $value = $row[$defaultField]; |
$row[$defaultField] = Array($value); | $row[$defaultField] = Array($value); |
$row[$defaultField][] = ""; | $row[$defaultField][] = ""; |
$row[$defaultField][] = ""; | $row[$defaultField][] = ""; |
} | } |
} | } |
} | } |
return $row; | return $row; |
} | } |
$db = $server->get_db('disclosr-agencies'); | $db = $server->get_db('disclosr-agencies'); |
if (isset($_REQUEST['id'])) { | if (isset($_REQUEST['id'])) { |
//get an agency record as json/html, search by name/abn/id | //get an agency record as json/html, search by name/abn/id |
// by name = startkey="Ham"&endkey="Ham\ufff0" | // by name = startkey="Ham"&endkey="Ham\ufff0" |
// edit? | // edit? |
$obj = $db->get($_REQUEST['id']); | $obj = $db->get($_REQUEST['id']); |
//print_r($row); | //print_r($row); |
if (sizeof($_POST) > 0) { | if (sizeof($_POST) > 0) { |
//print_r($_POST); | //print_r($_POST); |
foreach ($_POST as $postkey => $postvalue) { | foreach ($_POST as $postkey => $postvalue) { |
if ($postvalue == "") { | if ($postvalue == "") { |
unset($_POST[$postkey]); | unset($_POST[$postkey]); |
} | } |
if (is_array($postvalue)) { | if (is_array($postvalue)) { |
if (count($postvalue) == 1 && $postvalue[0] == "") { | if (count($postvalue) == 1 && $postvalue[0] == "") { |
unset($_POST[$postkey]); | unset($_POST[$postkey]); |
} else { | } else { |
foreach ($_POST[$postkey] as $key => &$value) { | foreach ($_POST[$postkey] as $key => &$value) { |
if ($value == "") { | if ($value == "") { |
unset($_POST[$postkey][$key]); | unset($_POST[$postkey][$key]); |
} | } |
} | } |
} | } |
} | } |
} | } |
if (isset($_POST['_id']) && $db->get_rev($_POST['_id']) == $_POST['_rev']) { | if (isset($_POST['_id']) && $db->get_rev($_POST['_id']) == $_POST['_rev']) { |
echo "Edited version was latest version, continue saving"; | echo "Edited version was latest version, continue saving"; |
$newdoc = $_POST; | $newdoc = $_POST; |
$newdoc['metadata']['lastModified'] = time(); | $newdoc['metadata']['lastModified'] = time(); |
$obj = $db->save($newdoc); | $obj = $db->save($newdoc); |
} else { | } else { |
echo "ALERT doc revised by someone else while editing. Document not saved."; | echo "ALERT doc revised by someone else while editing. Document not saved."; |
} | } |
} | } |
$mode = "edit"; | $mode = "view"; |
$rowArray = object_to_array($obj); | $rowArray = object_to_array($obj); |
ksort($rowArray); | ksort($rowArray); |
if ($mode == "edit") { | if ($mode == "edit") { |
$row = addDefaultFields($rowArray); | $row = addDefaultFields($rowArray); |
} else { | } else { |
$row = $rowArray; | $row = $rowArray; |
} | } |
if ($mode == "view") { | if ($mode == "view") { |
echo '<div typeof="schema:GovernmentOrganisation" about="#' . $row['_id'] . '"><table width="100%">'; | echo '<div typeof="schema:GovernmentOrganisation" about="#' . $row['_id'] . '"><table width="100%">'; |
echo '<tr> <td colspan="2"><h3>' . $row['name'] . "</h3></td></tr>"; | echo '<tr> <td colspan="2"><h3>' . $row['name'] . "</h3></td></tr>"; |
echo "<tr><th>Field Name</th><th>Field Value</th></tr>"; | echo "<tr><th>Field Name</th><th>Field Value</th></tr>"; |
} | } |
if ($mode == "edit") { | if ($mode == "edit") { |
?> | ?> |
<input id="addfield" type="button" value="Add Field"/> | <input id="addfield" type="button" value="Add Field"/> |
<script> | <script> |
window.onload = function() { | window.onload = function() { |
$(document).ready(function() { | $(document).ready(function() { |
// put all your jQuery goodness in here. | // put all your jQuery goodness in here. |
// http://charlie.griefer.com/blog/2009/09/17/jquery-dynamically-adding-form-elements/ | // http://charlie.griefer.com/blog/2009/09/17/jquery-dynamically-adding-form-elements/ |
$('#addfield').click(function() { | $('#addfield').click(function() { |
var field_name=window.prompt("fieldname?",""); | var field_name=window.prompt("fieldname?",""); |
if (field_name !="") { | if (field_name !="") { |
$('#submitbutton').before($('<span></span>') | $('#submitbutton').before($('<span></span>') |
.append("<label>"+field_name+"</label>") | .append("<label>"+field_name+"</label>") |
.append("<input class='input-text' type='text' id='"+field_name+"' name='"+field_name+"'/>") | .append("<input class='input-text' type='text' id='"+field_name+"' name='"+field_name+"'/>") |
); | ); |
} | } |
}); | }); |
}); | }); |
}; | }; |
</script> | </script> |
<form id="editform" class="nice" method="post"> | <form id="editform" class="nice" method="post"> |
<?php | <?php |
} | } |
foreach ($row as $key => $value) { | foreach ($row as $key => $value) { |
echo displayValue($key, $value, $mode); | echo displayValue($key, $value, $mode); |
} | } |
if ($mode == "view") { | if ($mode == "view") { |
echo "</table></div>"; | echo "</table></div>"; |
} | } |
if ($mode == "edit") { | if ($mode == "edit") { |
echo '<input id="submitbutton" type="submit"/></form>'; | echo '<input id="submitbutton" type="submit"/></form>'; |
} | } |
} else { | } else { |
try { | try { |
/* $rows = $db->get_view("app", "showNamesABNs")->rows; | /* $rows = $db->get_view("app", "showNamesABNs")->rows; |
//print_r($rows); | //print_r($rows); |
foreach ($rows as $row) { | foreach ($rows as $row) { |
// print_r($row); | // print_r($row); |
echo '<li><a href="getAgency.php?id=' . $row->key . '">' . | echo '<li><a href="getAgency.php?id=' . $row->key . '">' . |
(isset($row->value->name) && $row->value->name != "" ? $row->value->name : "NO NAME " . $row->value->abn) | (isset($row->value->name) && $row->value->name != "" ? $row->value->name : "NO NAME " . $row->value->abn) |
. '</a></li>'; | . '</a></li>'; |
} */ | } */ |
$rows = $db->get_view("app", "byCanonicalName")->rows; | $rows = $db->get_view("app", "byCanonicalName")->rows; |
//print_r($rows); | //print_r($rows); |
echo '<ul>'; | echo '<ul>'; |
foreach ($rows as $row) { | foreach ($rows as $row) { |
// print_r($row); | // print_r($row); |
echo '<li typeof="schema:GovernmentOrganisation foaf:Organization" about="getAgency.php?id=' . $row->value->_id . '"> | echo '<li typeof="schema:GovernmentOrganisation foaf:Organization" about="getAgency.php?id=' . $row->value->_id . '"> |
<a href="getAgency.php?id=' . $row->value->_id . '" rel="schema:url foaf:page" property="schema:name foaf:name">' . | <a href="getAgency.php?id=' . $row->value->_id . '" rel="schema:url foaf:page" property="schema:name foaf:name">' . |
$row->value->name | $row->value->name |
. '</a></li>'; | . '</a></li>'; |
} | } |
echo "</ul>"; | echo "</ul>"; |
} catch (SetteeRestClientException $e) { | } catch (SetteeRestClientException $e) { |
setteErrorHandler($e); | setteErrorHandler($e); |
} | } |
} | } |
include_footer(); | include_footer(); |
?> | ?> |
google-site-verification: google676a414ad086cefb.html | |
<?php | <?php |
include_once('include/common.inc.php'); | include_once('include/common.inc.php'); |
//include_header(); | //include_header(); |
$format = "html"; | $format = "html"; |
if (isset($_REQUEST['format'])) { | if (isset($_REQUEST['format'])) { |
$format = $_REQUEST['format']; | $format = $_REQUEST['format']; |
} | } |
function add_node($id, $label) { | function add_node($id, $label, $parent="") { |
global $format; | global $format; |
if ($format == "html") { | if ($format == "html") { |
echo "nodes[\"$id\"] = graph.newNode({label: \"$label\"});" . PHP_EOL; | // echo "nodes[\"$id\"] = graph.newNode({label: \"$label\"});" . PHP_EOL; |
} | } |
if ($format == "dot" && $label != "") { | if ($format == "dot" && $label != "") { |
echo "$id [label=\"$label\"];". PHP_EOL; | echo "$id [label=\"$label\"];". PHP_EOL; |
} | } |
if ($format == "gexf") { | |
echo "<node id='$id' label=\"".htmlentities($label,ENT_XML1)."\" ".($parent != ""? "pid='$parent'><viz:size value='1'/>":"><viz:size value='2'/>") | |
."<viz:color b='".rand(0,255)."' g='".rand(0,255)."' r='".rand(0,255)."'/>" | |
."</node>". PHP_EOL; | |
} | |
} | } |
function add_edge($from, $to, $color) { | function add_edge($from, $to, $color) { |
global $format; | global $format; |
if ($format == "html") { | if ($format == "html") { |
echo "graph.newEdge(nodes[\"$from\"], nodes['$to'], {color: '$color'});" . PHP_EOL; | // echo "graph.newEdge(nodes[\"$from\"], nodes['$to'], {color: '$color'});" . PHP_EOL; |
} | } |
if ($format == "dot") { | if ($format == "dot") { |
echo "$from -> $to ".($color != ""? "[color=$color]":"").";". PHP_EOL; | echo "$from -> $to ".($color != ""? "[color=$color]":"").";". PHP_EOL; |
} | } |
if ($format == "gexf") { | |
echo "<edge id='$from$to' source='$from' target='$to' />". PHP_EOL; | |
} | |
} | |
if ($format == "gexf") { | |
//header('Content-Type: text/xml'); | |
header('Content-Type: application/gexf+xml'); | |
echo '<?xml version="1.0" encoding="UTF-8"?> | |
<gexf xmlns="http://www.gexf.net/1.2draft" xmlns:viz="http://www.gexf.net/1.2draft/viz" version="1.2"> | |
<meta lastmodifieddate="2009-03-20"> | |
<creator>Gexf.net</creator> | |
<description>A hello world! file</description> | |
</meta> | |
<graph mode="static" defaultedgetype="directed"> | |
<nodes>'. PHP_EOL; | |
} | } |
if ($format == "html") { | |
?> | |
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> | |
<script src="lib/springy/springy.js"></script> | |
<script src="lib/springy/springyui.js"></script> | |
<script> | |
var graph = new Graph(); | |
var nodes = []; | |
<?php | |
} | |
if ($format == "dot") { | if ($format == "dot") { |
echo 'digraph g {'. PHP_EOL; | echo 'digraph g {'. PHP_EOL; |
} | } |
$db = $server->get_db('disclosr-agencies'); | $db = $server->get_db('disclosr-agencies'); |
add_node("fedg","Federal Government - Commonwealth of Australia"); | add_node("fedg","Federal Government - Commonwealth of Australia"); |
try { | try { |
$rows = $db->get_view("app", "byCanonicalName", null, true)->rows; | $rows = $db->get_view("app", "byCanonicalName", null, true)->rows; |
//print_r($rows); | //print_r($rows); |
foreach ($rows as $row) { | foreach ($rows as $row) { |
add_node($row->id, $row->key); | add_node($row->id, $row->key); |
} | } |
} catch (SetteeRestClientException $e) { | } catch (SetteeRestClientException $e) { |
setteErrorHandler($e); | setteErrorHandler($e); |
} | } |
if ($format == "gexf") { | |
echo '</nodes> | |
<edges>'. PHP_EOL; | |
} | |
try { | try { |
$rows = $db->get_view("app", "byDeptStateName", null, true)->rows; | $rows = $db->get_view("app", "byDeptStateName", null, true)->rows; |
//print_r($rows); | //print_r($rows); |
foreach ($rows as $row) { | foreach ($rows as $row) { |
add_edge("fedg", $row->value, 'yellow'); | add_edge("fedg", $row->value, 'yellow'); |
} | } |
} catch (SetteeRestClientException $e) { | } catch (SetteeRestClientException $e) { |
setteErrorHandler($e); | setteErrorHandler($e); |
} | } |
try { | try { |
$rows = $db->get_view("app", "parentOrgs", null, true)->rows; | $rows = $db->get_view("app", "parentOrgs", null, true)->rows; |
// print_r($rows); | // print_r($rows); |
foreach ($rows as $row) { | foreach ($rows as $row) { |
add_edge($row->key, $row->value, 'blue'); | add_edge($row->key, $row->value, 'blue'); |
} | } |
} catch (SetteeRestClientException $e) { | } catch (SetteeRestClientException $e) { |
setteErrorHandler($e); | setteErrorHandler($e); |
} | } |
if ($format == "html") { | if ($format == "html") { |
?> | ?> |
window.onload = function() { | <div id="sigma-example" width="960" style="min-height:800px;background-color: #333;"></div> |
$(document).ready(function() { | <script src="javascripts/sigma.min.js"></script> |
var springy = $('#springydemo').springy({ | <script src="javascripts/sigma/plugins/sigma.parseGexf.js"></script> |
graph: graph | <script src="javascripts/sigma/plugins/sigma.forceatlas2.js"></script> |
}); | <script type="text/javascript">function init() { |
}); | // Instanciate sigma.js and customize rendering : |
}; | var sigInst = sigma.init(document.getElementById('sigma-example')).drawingProperties({ |
</script> | defaultLabelColor: '#fff', |
defaultLabelSize: 14, | |
defaultLabelBGColor: '#fff', | |
defaultLabelHoverColor: '#000', | |
labelThreshold: 6, | |
defaultEdgeType: 'curve' | |
}).graphProperties({ | |
minNodeSize: 0.5, | |
maxNodeSize: 5, | |
minEdgeSize: 5, | |
maxEdgeSize: 5 | |
}).mouseProperties({ | |
maxRatio: 32 | |
}); | |
<canvas id="springydemo" width="1260" height="680" /> | // Parse a GEXF encoded file to fill the graph |
// (requires "sigma.parseGexf.js" to be included) | |
sigInst.parseGexf('graph.php?format=gexf'); | |
sigInst.bind('downnodes',function(event){ | |
var nodes = event.content; | |
}); | |
// Start the ForceAtlas2 algorithm | |
// (requires "sigma.forceatlas2.js" to be included) | |
sigInst.startForceAtlas2(); | |
// Draw the graph : | |
sigInst.draw(); | |
} | |
if (document.addEventListener) { | |
document.addEventListener("DOMContentLoaded", init, false); | |
} else { | |
window.onload = init; | |
} | |
</script> | |
<?php | <?php |
} | } |
if ($format == "dot") { | if ($format == "dot") { |
echo "}"; | echo "}"; |
} | } |
if ($format == "gexf") { | |
echo ' </edges> | |
</graph> | |
</gexf>'. PHP_EOL; | |
} | |
//include_footer(); | //include_footer(); |
?> | ?> |
<?php | <?php |
date_default_timezone_set("Australia/Sydney"); | date_default_timezone_set("Australia/Sydney"); |
$basePath = ""; | $basePath = ""; |
if (strstr($_SERVER['PHP_SELF'], "alaveteli/") | if (strstr($_SERVER['PHP_SELF'], "alaveteli/") |
|| strstr($_SERVER['PHP_SELF'], "admin/") | || strstr($_SERVER['PHP_SELF'], "admin/") |
|| strstr($_SERVER['PHP_SELF'], "lib/") | || strstr($_SERVER['PHP_SELF'], "lib/") |
|| strstr($_SERVER['PHP_SELF'], "include/")) | || strstr($_SERVER['PHP_SELF'], "include/")) |
$basePath = "../"; | $basePath = "../"; |
include_once ('couchdb.inc.php'); | include_once ('couchdb.inc.php'); |
include_once ('template.inc.php'); | include_once ('template.inc.php'); |
require_once $basePath.'lib/Requests/library/Requests.php'; | require_once $basePath.'lib/Requests/library/Requests.php'; |
Requests::register_autoloader(); | Requests::register_autoloader(); |
# Convert a stdClass to an Array. http://www.php.net/manual/en/language.types.object.php#102735 | # Convert a stdClass to an Array. http://www.php.net/manual/en/language.types.object.php#102735 |
function object_to_array(stdClass $Class) { | function object_to_array(stdClass $Class) { |
# Typecast to (array) automatically converts stdClass -> array. | # Typecast to (array) automatically converts stdClass -> array. |
$Class = (array) $Class; | $Class = (array) $Class; |
# Iterate through the former properties looking for any stdClass properties. | # Iterate through the former properties looking for any stdClass properties. |
# Recursively apply (array). | # Recursively apply (array). |
foreach ($Class as $key => $value) { | foreach ($Class as $key => $value) { |
if (is_object($value) && get_class($value) === 'stdClass') { | if (is_object($value) && get_class($value) === 'stdClass') { |
$Class[$key] = object_to_array($value); | $Class[$key] = object_to_array($value); |
} | } |
} | } |
return $Class; | return $Class; |
} | } |
# Convert an Array to stdClass. http://www.php.net/manual/en/language.types.object.php#102735 | # Convert an Array to stdClass. http://www.php.net/manual/en/language.types.object.php#102735 |
function array_to_object(array $array) { | function array_to_object(array $array) { |
# Iterate through our array looking for array values. | # Iterate through our array looking for array values. |
# If found recurvisely call itself. | # If found recurvisely call itself. |
foreach ($array as $key => $value) { | foreach ($array as $key => $value) { |
if (is_array($value)) { | if (is_array($value)) { |
$array[$key] = array_to_object($value); | $array[$key] = array_to_object($value); |
} | } |
} | } |
# Typecast to (object) will automatically convert array -> stdClass | # Typecast to (object) will automatically convert array -> stdClass |
return (object) $array; | return (object) $array; |
} | } |
function dept_to_portfolio($deptName) { | function dept_to_portfolio($deptName) { |
return trim(str_replace("Department of", "", str_replace("Department of the", "Department of", $deptName))); | return trim(str_replace("Department of", "", str_replace("Department of the", "Department of", $deptName))); |
} | } |
function phrase_to_tag ($phrase) { | function phrase_to_tag ($phrase) { |
return str_replace(" ","_",str_replace("'","",str_replace(",","",strtolower($phrase)))); | return str_replace(" ","_",str_replace("'","",str_replace(",","",strtolower($phrase)))); |
} | } |
function local_url() { | |
return "http://" . $_SERVER['HTTP_HOST'] . rtrim(dirname($_SERVER['PHP_SELF']), '/\\') . "/"; | |
} | |
function GetDomain($url) | function GetDomain($url) |
{ | { |
$nowww = ereg_replace('www\.','',$url); | $nowww = ereg_replace('www\.','',$url); |
$domain = parse_url($nowww); | $domain = parse_url($nowww); |
if(!empty($domain["host"])) | if(!empty($domain["host"])) |
{ | { |
return $domain["host"]; | return $domain["host"]; |
} else | } else |
{ | { |
return $domain["path"]; | return $domain["path"]; |
} | } |
} | } |
<?php | <?php |
include $basePath . "schemas/schemas.inc.php"; | include $basePath . "schemas/schemas.inc.php"; |
require ($basePath . 'couchdb/settee/src/settee.php'); | require ($basePath . 'couchdb/settee/src/settee.php'); |
function createDocumentsDesignDoc() { | function createDocumentsDesignDoc() { |
/*"views": { | /* "views": { |
"web_server": { | "web_server": { |
"map": "function(doc) {\n emit(doc.web_server, 1);\n}", | "map": "function(doc) {\n emit(doc.web_server, 1);\n}", |
"reduce": "function (key, values, rereduce) {\n return sum(values);\n}" | "reduce": "function (key, values, rereduce) {\n return sum(values);\n}" |
}, | }, |
"byAgency": { | "byAgency": { |
"map": "function(doc) {\n emit(doc.agencyID, 1);\n}", | "map": "function(doc) {\n emit(doc.agencyID, 1);\n}", |
"reduce": "function (key, values, rereduce) {\n return sum(values);\n}" | "reduce": "function (key, values, rereduce) {\n return sum(values);\n}" |
}, | }, |
"byURL": { | "byURL": { |
"map": "function(doc) {\n emit(doc.url, doc);\n}" | "map": "function(doc) {\n emit(doc.url, doc);\n}" |
}, | }, |
"agency": { | "agency": { |
"map": "function(doc) {\n emit(doc.agencyID, doc);\n}" | "map": "function(doc) {\n emit(doc.agencyID, doc);\n}" |
}, | }, |
"byWebServer": { | "byWebServer": { |
"map": "function(doc) {\n emit(doc.web_server, doc);\n}" | "map": "function(doc) {\n emit(doc.web_server, doc);\n}" |
}, | }, |
"getValidationRequired": { | "getValidationRequired": { |
"map": "function(doc) {\nif (doc.mime_type == \"text/html\" \n&& typeof(doc.validation) == \"undefined\") {\n emit(doc._id, doc._attachments);\n}\n}" | "map": "function(doc) {\nif (doc.mime_type == \"text/html\" \n&& typeof(doc.validation) == \"undefined\") {\n emit(doc._id, doc._attachments);\n}\n}" |
} | } |
}*/ | } */ |
} | } |
function createAgencyDesignDoc() { | function createAgencyDesignDoc() { |
global $db; | global $db; |
$obj = new stdClass(); | $obj = new stdClass(); |
$obj->_id = "_design/" . urlencode("app"); | $obj->_id = "_design/" . urlencode("app"); |
$obj->language = "javascript"; | $obj->language = "javascript"; |
$obj->views->all->map = "function(doc) { emit(doc._id, doc); };"; | $obj->views->all->map = "function(doc) { emit(doc._id, doc); };"; |
$obj->views->byABN->map = "function(doc) { emit(doc.abn, doc); };"; | $obj->views->byABN->map = "function(doc) { emit(doc.abn, doc); };"; |
$obj->views->byCanonicalName->map = "function(doc) { | $obj->views->byCanonicalName->map = "function(doc) { |
if (doc.parentOrg || doc.orgType == 'FMA-DepartmentOfState') { | if (doc.parentOrg || doc.orgType == 'FMA-DepartmentOfState') { |
emit(doc.name, doc); | emit(doc.name, doc); |
} | } |
};"; | };"; |
$obj->views->byDeptStateName->map = "function(doc) { | $obj->views->byDeptStateName->map = "function(doc) { |
if (doc.orgType == 'FMA-DepartmentOfState') { | if (doc.orgType == 'FMA-DepartmentOfState') { |
emit(doc.name, doc._id); | emit(doc.name, doc._id); |
} | } |
};"; | };"; |
$obj->views->parentOrgs->map = "function(doc) { | $obj->views->parentOrgs->map = "function(doc) { |
if (doc.parentOrg) { | if (doc.parentOrg) { |
emit(doc._id, doc.parentOrg); | emit(doc._id, doc.parentOrg); |
} | } |
};"; | };"; |
$obj->views->byName->map = 'function(doc) { | $obj->views->byName->map = 'function(doc) { |
if (typeof(doc["status"]) == "undefined" || doc["status"] != "suspended") { | if (typeof(doc["status"]) == "undefined" || doc["status"] != "suspended") { |
emit(doc.name, doc._id); | emit(doc.name, doc._id); |
if (typeof(doc.shortName) != "undefined" && doc.shortName != doc.name) { | if (typeof(doc.shortName) != "undefined" && doc.shortName != doc.name) { |
emit(doc.shortName, doc._id); | emit(doc.shortName, doc._id); |
} | } |
for (name in doc.otherNames) { | for (name in doc.otherNames) { |
if (doc.otherNames[name] != "" && doc.otherNames[name] != doc.name) { | if (doc.otherNames[name] != "" && doc.otherNames[name] != doc.name) { |
emit(doc.otherNames[name], doc._id); | emit(doc.otherNames[name], doc._id); |
} | } |
} | } |
for (name in doc.foiBodies) { | for (name in doc.foiBodies) { |
if (doc.foiBodies[name] != "" && doc.foiBodies[name] != doc.name) { | if (doc.foiBodies[name] != "" && doc.foiBodies[name] != doc.name) { |
emit(doc.foiBodies[name], doc._id); | emit(doc.foiBodies[name], doc._id); |
} | } |
} | } |
} | } |
};'; | };'; |
$obj->views->foiEmails->map = "function(doc) { | $obj->views->foiEmails->map = "function(doc) { |
emit(doc._id, doc.foiEmail); | emit(doc._id, doc.foiEmail); |
};"; | };"; |
$obj->views->byLastModified->map = "function(doc) { emit(doc.metadata.lastModified, doc); }"; | $obj->views->byLastModified->map = "function(doc) { emit(doc.metadata.lastModified, doc); }"; |
$obj->views->getActive->map = 'function(doc) { if (doc.status == "active") { emit(doc._id, doc); } };'; | $obj->views->getActive->map = 'function(doc) { if (doc.status == "active") { emit(doc._id, doc); } };'; |
$obj->views->getSuspended->map = 'function(doc) { if (doc.status == "suspended") { emit(doc._id, doc); } };'; | $obj->views->getSuspended->map = 'function(doc) { if (doc.status == "suspended") { emit(doc._id, doc); } };'; |
$obj->views->getScrapeRequired->map = "function(doc) { | $obj->views->getScrapeRequired->map = "function(doc) { |
var lastScrape = Date.parse(doc.metadata.lastScraped); | var lastScrape = Date.parse(doc.metadata.lastScraped); |
var today = new Date(); | var today = new Date(); |
if (!lastScrape || lastScrape.getTime() + 1000 != today.getTime()) { | if (!lastScrape || lastScrape.getTime() + 1000 != today.getTime()) { |
emit(doc._id, doc); | emit(doc._id, doc); |
} | } |
};"; | };"; |
$obj->views->showNamesABNs->map = "function(doc) { emit(doc._id, {name: doc.name, abn: doc.abn}); };"; | $obj->views->showNamesABNs->map = "function(doc) { emit(doc._id, {name: doc.name, abn: doc.abn}); };"; |
$obj->views->getConflicts->map = "function(doc) { | $obj->views->getConflicts->map = "function(doc) { |
if (doc._conflicts) { | if (doc._conflicts) { |
emit(null, [doc._rev].concat(doc._conflicts)); | emit(null, [doc._rev].concat(doc._conflicts)); |
} | } |
}"; | }"; |
// http://stackoverflow.com/questions/646628/javascript-startswith | // http://stackoverflow.com/questions/646628/javascript-startswith |
$obj->views->score->map = 'if(!String.prototype.startsWith){ | $obj->views->score->map = 'if(!String.prototype.startsWith){ |
String.prototype.startsWith = function (str) { | String.prototype.startsWith = function (str) { |
return !this.indexOf(str); | return !this.indexOf(str); |
} | } |
} | } |
function(doc) { | function(doc) { |
count = 0; | count = 0; |
if (doc["status"] != "suspended") { | if (doc["status"] != "suspended") { |
for(var propName in doc) { | for(var propName in doc) { |
if(typeof(doc[propName]) != "undefined" && doc[propName] != "") { | if(typeof(doc[propName]) != "undefined" && doc[propName] != "") { |
count++; | count++; |
} | } |
} | } |
portfolio = doc.parentOrg; | portfolio = doc.parentOrg; |
if (doc.orgType == "FMA-DepartmentOfState") { | if (doc.orgType == "FMA-DepartmentOfState") { |
portfolio = doc._id; | portfolio = doc._id; |
} | } |
if (doc.orgType == "Court-Commonwealth" || doc.orgType == "FMA-DepartmentOfParliament") { | if (doc.orgType == "Court-Commonwealth" || doc.orgType == "FMA-DepartmentOfParliament") { |
portfolio = doc.orgType; | portfolio = doc.orgType; |
} | } |
emit(count+doc._id, {id:doc._id, name: doc.name, score:count, orgType: doc.orgType, portfolio:portfolio}); | emit(count+doc._id, {id:doc._id, name: doc.name, score:count, orgType: doc.orgType, portfolio:portfolio}); |
} | } |
}'; | }'; |
$obj->views->scoreHas->map = 'if(!String.prototype.startsWith){ | $obj->views->scoreHas->map = 'if(!String.prototype.startsWith){ |
String.prototype.startsWith = function (str) { | String.prototype.startsWith = function (str) { |
return !this.indexOf(str); | return !this.indexOf(str); |
} | } |
} | } |
if(!String.prototype.endsWith){ | if(!String.prototype.endsWith){ |
String.prototype.endsWith = function(suffix) { | String.prototype.endsWith = function(suffix) { |
return this.indexOf(suffix, this.length - suffix.length) !== -1; | return this.indexOf(suffix, this.length - suffix.length) !== -1; |
}; | }; |
} | } |
function(doc) { | function(doc) { |
if (typeof(doc["status"]) == "undefined" || doc["status"] != "suspended") { | if (typeof(doc["status"]) == "undefined" || doc["status"] != "suspended") { |
for(var propName in doc) { | for(var propName in doc) { |
if(typeof(doc[propName]) != "undefined" && (propName.startsWith("has") || propName.endsWith("URL"))) { | if(typeof(doc[propName]) != "undefined" && (propName.startsWith("has") || propName.endsWith("URL"))) { |
emit(propName, 1); | emit(propName, 1); |
} | } |
} | } |
emit("total", 1); | emit("total", 1); |
} | } |
}'; | }'; |
$obj->views->scoreHas->reduce = 'function (key, values, rereduce) { | $obj->views->scoreHas->reduce = 'function (key, values, rereduce) { |
return sum(values); | return sum(values); |
}'; | }'; |
$obj->views->fieldNames->map = ' | $obj->views->fieldNames->map = ' |
function(doc) { | function(doc) { |
for(var propName in doc) { | for(var propName in doc) { |
emit(propName, doc._id); | emit(propName, doc._id); |
} | } |
}'; | }'; |
$obj->views->fieldNames->reduce = 'function (key, values, rereduce) { | $obj->views->fieldNames->reduce = 'function (key, values, rereduce) { |
return values.length; | return values.length; |
}'; | }'; |
// allow safe updates (even if slightly slower due to extra: rev-detection check). | // allow safe updates (even if slightly slower due to extra: rev-detection check). |
return $db->save($obj, true); | return $db->save($obj, true); |
} | } |
if (php_uname('n') == "vanille") { | if (php_uname('n') == "vanille") { |
$serverAddr = 'http://192.168.178.21:5984/'; | $serverAddr = 'http://192.168.178.21:5984/'; |
} else | } else |
if (php_uname('n') == "KYUUBEY") { | if (php_uname('n') == "KYUUBEY") { |
$serverAddr = 'http://192.168.1.148:5984/'; | $serverAddr = 'http://192.168.1.148:5984/'; |
} else { | } else { |
$serverAddr = 'http://127.0.0.1:5984/'; | $serverAddr = 'http://127.0.0.1:5984/'; |
} | } |
$server = new SetteeServer($serverAddr); | $server = new SetteeServer($serverAddr); |
function setteErrorHandler($e) { | function setteErrorHandler($e) { |
echo $e->getMessage() . "<br>" . PHP_EOL; | echo $e->getMessage() . "<br>" . PHP_EOL; |
} | } |
<?php | <?php |
function include_header() { | function include_header() { |
global $basePath; | global $basePath; |
?> | ?> |
<!DOCTYPE html> | <!DOCTYPE html> |
<!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ --> | <!-- 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 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 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 IE 8]> <html class="no-js lt-ie9" lang="en"> <![endif]--> |
<!--[if gt IE 8]><!--> <html lang="en"> <!--<![endif]--> | <!--[if gt IE 8]><!--> <html lang="en"> <!--<![endif]--> |
<head> | <head> |
<meta charset="utf-8" /> | <meta charset="utf-8" /> |
<!-- Set the viewport width to device width for mobile --> | <!-- Set the viewport width to device width for mobile --> |
<meta name="viewport" content="width=device-width" /> | <meta name="viewport" content="width=device-width" /> |
<title>Disclosr</title> | <title>Disclosr</title> |
<!-- Included CSS Files --> | <!-- Included CSS Files --> |
<link rel="stylesheet" href="<?php echo $basePath ?>stylesheets/foundation.css"> | <link rel="stylesheet" href="<?php echo $basePath ?>stylesheets/foundation.css"> |
<link rel="stylesheet" href="<?php echo $basePath ?>stylesheets/app.css"> | <link rel="stylesheet" href="<?php echo $basePath ?>stylesheets/app.css"> |
<!--[if lt IE 9]> | <!--[if lt IE 9]> |
<link rel="stylesheet" href="<?php echo $basePath ?>stylesheets/ie.css"> | <link rel="stylesheet" href="<?php echo $basePath ?>stylesheets/ie.css"> |
<![endif]--> | <![endif]--> |
<!-- IE Fix for HTML5 Tags --> | <!-- IE Fix for HTML5 Tags --> |
<!--[if lt IE 9]> | <!--[if lt IE 9]> |
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script> | <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script> |
<![endif]--> | <![endif]--> |
</head> | </head> |
<body xmlns:schema="http://schema.org/" xmlns:foaf="http://xmlns.com/foaf/0.1/"> | <body xmlns:schema="http://schema.org/" xmlns:foaf="http://xmlns.com/foaf/0.1/"> |
<!-- navBar --> | <!-- navBar --> |
<div id="navbar" class="container"> | <div id="navbar" class="container"> |
<div class="row"> | <div class="row"> |
<div class="four columns"> | <div class="four columns"> |
<h1><a href="/">Disclosr</a></h1> | <h1><a href="/">Disclosr</a></h1> |
</div> | </div> |
<div class="eight columns hide-on-phones"> | <div class="eight columns hide-on-phones"> |
<strong class="right"> | <strong class="right"> |
<a href="getAgency.php">Agencies</a> | <a href="getAgency.php">Agencies</a> |
<a href="about.php">About/FAQ</a> | <a href="about.php">About/FAQ</a> |
</strong> | </strong> |
</div> | </div> |
</div> | </div> |
</div> | </div> |
<!-- /navBar --> | <!-- /navBar --> |
<!-- container --> | <!-- container --> |
<div class="container"> | <div class="container"> |
<?php } | <?php } |
function include_footer() { | function include_footer() { |
global $basePath; | global $basePath; |
?> | ?> |
</div> | </div> |
<!-- container --> | <!-- container --> |
<!-- Included JS Files --> | <!-- Included JS Files --> |
<script src="<?php echo $basePath; ?>javascripts/foundation.js"></script> | <script src="<?php echo $basePath; ?>javascripts/foundation.js"></script> |
<script src="<?php echo $basePath; ?>javascripts/app.js"></script> | <script src="<?php echo $basePath; ?>javascripts/app.js"></script> |
<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script> | <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script> |
<script type="text/javascript" src="javascripts/flotr2/flotr2.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> | </body> |
</html> | </html> |
<?php } | <?php } |
} | |
<?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; | |
?> | |
// 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; | |
})(); | |
<?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; | |
} | |
} |
# www.robotstxt.org/ | # www.robotstxt.org/ |
# www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449 | # www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449 |
User-agent: * | User-agent: * |
Disallow: /admin/ | Disallow: /admin/ |
Sitemap: http://orgs.disclosurelo.gs/sitemap.xml.php |
<?php | |
include ('include/common.inc.php'); | |
$last_updated = date('Y-m-d', @filemtime('cbrfeed.zip')); | |
header("Content-Type: text/xml"); | |
echo "<?xml version='1.0' encoding='UTF-8'?>"; | |
echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n"; | |
echo " <url><loc>" . local_url() . "index.php</loc><priority>1.0</priority></url>\n"; | |
foreach (scandir("./") as $file) { | |
if (strpos($file, ".php") !== false && $file != "index.php" && $file != "sitemap.xml.php") | |
echo " <url><loc>" . local_url() . "$file</loc><priority>0.3</priority></url>\n"; | |
} | |
$db = $server->get_db('disclosr-agencies'); | |
try { | |
$rows = $db->get_view("app", "byCanonicalName")->rows; | |
foreach ($rows as $row) { | |
echo '<url><loc>' . local_url() . 'getAgency.php?id=' . $row->value->_id . "</loc><priority>0.6</priority></url>\n"; | |
} | |
} catch (SetteeRestClientException $e) { | |
setteErrorHandler($e); | |
} | |
echo '</urlset>'; | |
?> | |
<!DOCTYPE html> | |
<!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ --> | |
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en"> <![endif]--> | |
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8" lang="en"> <![endif]--> | |
<!--[if IE 8]> <html class="no-js lt-ie9" lang="en"> <![endif]--> | |
<!--[if gt IE 8]><!--> <html lang="en"> <!--<![endif]--> | |
<head> | |
<meta charset="utf-8" /> | |
<!-- Set the viewport width to device width for mobile --> | |
<meta name="viewport" content="width=device-width" /> | |
<title>Welcome to Foundation</title> | |
<!-- Included CSS Files --> | |
<link rel="stylesheet" href="stylesheets/foundation.css"> | |
<link rel="stylesheet" href="stylesheets/app.css"> | |
<!--[if lt IE 9]> | |
<link rel="stylesheet" href="stylesheets/ie.css"> | |
<![endif]--> | |
<!-- IE Fix for HTML5 Tags --> | |
<!--[if lt IE 9]> | |
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script> | |
<![endif]--> | |
</head> | |
<body> | |
<!-- container --> | |
<div class="container"> | |
<div class="row"> | |
<div class="twelve columns"> | |
<h2>Welcome to Foundation</h2> | |
<p>This is version 2.1.4 released on December 19, 2011</p> | |
<hr /> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="eight columns"> | |
<h3>The Grid</h3> | |
<!-- Grid Example --> | |
<div class="row"> | |
<div class="twelve columns"> | |
<div class="panel"> | |
<p>This is a twelve column section in a row. Each of these includes a div.panel element so you can see where the columns are - it's not required at all for the grid.</p> | |
</div> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="six columns"> | |
<div class="panel"> | |
<p>Six columns</p> | |
</div> | |
</div> | |
<div class="six columns"> | |
<div class="panel"> | |
<p>Six columns</p> | |
</div> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="four columns"> | |
<div class="panel"> | |
<p>Four columns</p> | |
</div> | |
</div> | |
<div class="four columns"> | |
<div class="panel"> | |
<p>Four columns</p> | |
</div> | |
</div> | |
<div class="four columns"> | |
<div class="panel"> | |
<p>Four columns</p> | |
</div> | |
</div> | |
</div> | |
<h3>Tabs</h3> | |
<dl class="tabs"> | |
<dd><a href="#simple1" class="active">Simple Tab 1</a></dd> | |
<dd><a href="#simple2">Simple Tab 2</a></dd> | |
<dd><a href="#simple3">Simple Tab 3</a></dd> | |
</dl> | |
<ul class="tabs-content"> | |
<li class="active" id="simple1Tab">This is simple tab 1's content. Pretty neat, huh?</li> | |
<li id="simple2Tab">This is simple tab 2's content. Now you see it!</li> | |
<li id="simple3Tab">This is simple tab 3's content. It's, you know...okay.</li> | |
</ul> | |
<h3>Buttons</h3> | |
<p><a href="#" class="small blue button">Small Blue Button</a></p> | |
<p><a href="#" class="blue button">Medium Blue Button</a></p> | |
<p><a href="#" class="large blue button">Large Blue Button</a></p> | |
<p><a href="#" class="nice radius small blue button">Nice Blue Button</a></p> | |
<p><a href="#" class="nice radius blue button">Nice Blue Button</a></p> | |
<p><a href="#" class="nice radius large blue button">Nice Blue Button</a></p> | |
</div> | |
<div class="four columns"> | |
<h4>Getting Started</h4> | |
<p>We're stoked you want to try Foundation! To get going, this file (index.html) includes some basic styles you can modify, play around with, or totally destroy to get going.</p> | |
<h4>Other Resources</h4> | |
<p>Once you've exhausted the fun in this document, you should check out:</p> | |
<ul class="disc"> | |
<li><a href="http://foundation.zurb.com/docs">Foundation Documentation</a><br />Everything you need to know about using the framework.</li> | |
<li><a href="http://github.com/zurb/foundation">Foundation on Github</a><br />Latest code, issue reports, feature requests and more.</li> | |
<li><a href="http://twitter.com/foundationzurb">@foundationzurb</a><br />Ping us on Twitter if you have questions. If you build something with this we'd love to see it (and send you a totally boss sticker).</li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
<!-- container --> | |
<!-- Included JS Files --> | |
<script src="javascripts/foundation.js"></script> | |
<script src="javascripts/app.js"></script> | |
</body> | |
</html> | |
/* Foundation was made by ZURB, an interaction design and design strategy firm in Campbell, CA */ | |
/* zurb.com */ | |
/* humanstxt.org */ | |
/* SITE */ | |
Standards: HTML5, CSS3 | |
Components: jQuery, Orbit, Reveal | |
Software: Coda, Textmate, Git |
for each agency, record when last changed (number of days too) and show a couple of URLs that were in that change | |
for each agency, find a scrapped document and read the webserver off it |
<?php | |
include_once('include/common.inc.php'); | |
include_header(); | |
echo "<table> | |
<tr><th>name</th><th>webserver</th><th>accessiblity errors</th></tr>"; | |
$agenciesdb = $server->get_db('disclosr-agencies'); | |
$docsdb = $server->get_db('disclosr-documents'); | |
try { | |
$rows = $agenciesdb->get_view("app", "all", null, true)->rows; | |
if ($rows) { | |
foreach ($rows as $row) { | |
echo "<tr><td>" . $row->value->name . "</td>"; | |
if (isset($row->value->website)) { | |
try { | |
$website = $docsdb->get(md5($row->value->website)); | |
$serverParts = explode(" ",$website->web_server); | |
echo "<td>" . $serverParts[0] . "</td>"; | |
if (!isset($website->validation)) { | |
echo "<td>?</td>"; | |
} else { | |
if ($website->validation == "") { | |
echo "<td>No error</td>"; | |
} else { | |
echo "<td><pre>" . str_replace("<", "<", $website->validation) . "</pre></td>"; | |
} | |
} | |
} catch (SetteeRestClientException $e) { | |
// setteErrorHandler($e); | |
} | |
} | |
echo "</tr>"; | |
} | |
} | |
} catch (SetteeRestClientException $e) { | |
setteErrorHandler($e); | |
} | |
include_footer(); | |
?> |