From: Maxious Date: Wed, 29 Aug 2012 10:18:51 +0000 Subject: Fix australian/american spelling of Organization X-Git-Url: https://maxious.lambdacomplex.org/git/?p=disclosr.git&a=commitdiff&h=b8819059a3537a6a66e08f6a01cb50ce45957b50 --- Fix australian/american spelling of Organization Former-commit-id: fe93f73332392945d7a9462b1a543c639e28c766 --- --- a/about.php +++ b/about.php @@ -1,16 +1,16 @@

About/FAQ

Lorem ipsum.

What is this?

-Disclosr is a project to monitor Australian Federal Government agencies +Disclo.gs is a project to monitor Australian Federal Government agencies compliance with their "proactive disclosure requirements". -OGRE (Open Government Realization Evaluation) is a ranking of compliance with these requirements. -Prometheus is the agent which polls agency websites to assess compliance. +

Attributions

+National Archives of Australia, Australian Governments’ Interactive Functions Thesaurus, 2nd edition, September 2005, published at http://www.naa.gov.au/recordkeeping/thesaurus/index.htm.

Open everything

All documents released CC-BY 3 AU --- a/admin/exportEmployees.csv.php +++ b/admin/exportEmployees.csv.php @@ -5,7 +5,6 @@ $format = "csv"; //$format = "json"; if (isset($_REQUEST['format'])) $format = $_REQUEST['format']; - setlocale(LC_CTYPE, 'C'); if ($format == "csv") { $headers = Array("name"); --- /dev/null +++ b/admin/naa-agift.json @@ -1,1 +1,2232 @@ - +[ + "Home", + "index.htm", + [ + "BUSINESS SUPPORT AND REGULATION", + "000411.htm", + [ + "Association registration", + "000396.htm" + ], + [ + "Business process auditing", + "000413.htm" + ], + [ + "Business registration and licensing", + "000414.htm" + ], + [ + "Business sponsorship", + "001371.htm" + ], + [ + "Consumer protection", + "000439.htm" + ], + [ + "Fair trading compliance", + "000482.htm", + [ + "Prices surveillance", + "000533.htm" + ], + [ + "Product safety", + "000534.htm" + ], + [ + "Trade practices compliance", + "000583.htm" + ] + ], + [ + "Financial institutions regulation", + "000485.htm" + ], + [ + "Government procurement regulation", + "000491.htm" + ], + [ + "Industry assistance schemes", + "000499.htm" + ], + [ + "Industry development", + "000500.htm" + ], + [ + "Insurance regulation", + "000501.htm", + [ + "General insurance", + "000489.htm" + ], + [ + "Life insurance", + "000505.htm" + ] + ], + [ + "Professional accreditation", + "000535.htm" + ], + [ + "Small business services", + "000565.htm", + [ + "Small business advocacy", + "000563.htm" + ], + [ + "Small business development", + "000564.htm" + ] + ], + [ + "Stock market regulation", + "000572.htm" + ] + ], + [ + "CIVIC INFRASTRUCTURE", + "000424.htm", + [ + "Civic management", + "000425.htm", + [ + "Architectural services", + "000387.htm" + ], + [ + "Building approval services", + "000406.htm" + ], + [ + "Building regulations and standards", + "000408.htm" + ], + [ + "Engineering services", + "000477.htm" + ], + [ + "Town planning", + "000582.htm" + ] + ], + [ + "Energy supply", + "000476.htm" + ], + [ + "Integrated services planning", + "000503.htm" + ], + [ + "Public housing", + "000536.htm", + [ + "Public housing construction", + "000537.htm" + ], + [ + "Public housing design", + "000538.htm" + ], + [ + "Public housing maintenance", + "000540.htm" + ] + ], + [ + "Public land management", + "000541.htm", + [ + "Burial ground management", + "000410.htm" + ], + [ + "Cultural centre management", + "000444.htm" + ], + [ + "Garden management", + "000488.htm" + ], + [ + "Memorial maintenance", + "000509.htm" + ], + [ + "Recreational park management", + "000549.htm" + ], + [ + "Sporting facilities management", + "000571.htm" + ] + ], + [ + "Regional development", + "000552.htm" + ], + [ + "Transport network maintenance", + "000586.htm" + ], + [ + "Waste management", + "001349.htm" + ] + ], + [ + "COMMUNICATIONS", + "000433.htm", + [ + "Advertising standards", + "000381.htm" + ], + [ + "Broadcasting", + "000403.htm", + [ + "Broadcasting standards", + "000404.htm" + ], + [ + "Radio broadcasting", + "000546.htm" + ], + [ + "Television broadcasting", + "000580.htm" + ] + ], + [ + "Call centre administration", + "000417.htm" + ], + [ + "Electronic commerce", + "000468.htm", + [ + "Authentication", + "000399.htm" + ], + [ + "Online transaction standards", + "000526.htm" + ] + ], + [ + "Government media", + "000490.htm" + ], + [ + "Information management standards", + "000283.htm", + [ + "Data management", + "000448.htm" + ], + [ + "Information dissemination", + "000502.htm" + ], + [ + "Information technology standards", + "000282.htm" + ] + ], + [ + "Media ownership regulation", + "000508.htm" + ], + [ + "Postal services", + "000530.htm", + [ + "Courier services", + "000441.htm" + ], + [ + "Electronic postal services", + "000469.htm" + ], + [ + "Retail postal services", + "000554.htm" + ] + ], + [ + "Publishing", + "000543.htm", + [ + "Electronic publishing", + "000470.htm" + ], + [ + "Publishing standards", + "000544.htm" + ], + [ + "Website development", + "000591.htm" + ] + ], + [ + "Radio communication", + "000547.htm", + [ + "Apparatus licensing", + "000385.htm" + ], + [ + "Spectrum management", + "000570.htm" + ] + ], + [ + "Satellite communication", + "000560.htm" + ], + [ + "Telecommunications", + "000578.htm", + [ + "Carriage service providers", + "000420.htm" + ], + [ + "Carrier licensing", + "000421.htm" + ], + [ + "Equipment licensing", + "000480.htm" + ], + [ + "Mobile telephone services", + "000516.htm" + ], + [ + "Telephone services", + "000579.htm" + ] + ] + ], + [ + "COMMUNITY SERVICES", + "000435.htm", + [ + "Accommodation services", + "000377.htm", + [ + "Defence housing", + "000458.htm" + ], + [ + "Emergency accommodation", + "000471.htm" + ], + [ + "Public housing entitlements", + "000539.htm" + ], + [ + "Refuge support", + "000551.htm" + ] + ], + [ + "Community support", + "000436.htm", + [ + "Adoption services", + "000378.htm" + ], + [ + "Aged care services", + "000382.htm" + ], + [ + "Child and youth support", + "000422.htm" + ], + [ + "Child-care services", + "000423.htm" + ], + [ + "Defence community programs", + "000452.htm" + ], + [ + "Family reunion programs", + "000483.htm" + ], + [ + "Veterans\" entitlements", + "000587.htm" + ] + ], + [ + "Counselling services", + "000440.htm" + ], + [ + "Emergency services", + "000474.htm", + [ + "Ambulance services", + "000384.htm" + ], + [ + "Emergency funding", + "000472.htm" + ], + [ + "Firefighting services", + "000487.htm" + ] + ], + [ + "Financial assistance", + "000484.htm", + [ + "Benefits", + "000402.htm" + ], + [ + "Income support schemes", + "000494.htm" + ] + ], + [ + "Natural disasters", + "000521.htm", + [ + "Disaster recovery", + "000462.htm" + ], + [ + "Disaster relief", + "000463.htm" + ] + ], + [ + "Rural community development", + "000557.htm" + ], + [ + "Social justice and equity", + "000566.htm" + ], + [ + "Transport access schemes", + "000901.htm" + ] + ], + [ + "CULTURAL AFFAIRS", + "000442.htm", + [ + "Arts development", + "000391.htm", + [ + "Arts funding", + "000393.htm" + ], + [ + "Arts incentive schemes", + "000394.htm" + ], + [ + "Arts promotion", + "000395.htm" + ] + ], + [ + "Collection access", + "000427.htm", + [ + "Descriptive standards", + "000461.htm" + ], + [ + "Finding aids development", + "000486.htm" + ], + [ + "Reference services", + "000550.htm" + ] + ], + [ + "Collection management", + "000430.htm", + [ + "Artefact export regulation", + "000390.htm" + ], + [ + "Collection accessioning", + "000428.htm" + ], + [ + "Collection acquisition", + "000429.htm" + ], + [ + "Collection storage", + "000432.htm" + ], + [ + "Preservation services", + "000532.htm" + ] + ], + [ + "Collection promotion", + "000431.htm", + [ + "Exhibition programs", + "000481.htm" + ], + [ + "Publicity programs", + "000542.htm" + ] + ], + [ + "Cultural awards and scholarships", + "000443.htm" + ], + [ + "Cultural festivals", + "000445.htm" + ], + [ + "Cultural gifts programs", + "000446.htm" + ], + [ + "Multicultural heritage promotion", + "000518.htm", + [ + "Multicultural festivals", + "000517.htm" + ], + [ + "Multicultural services", + "000519.htm" + ] + ] + ], + [ + "DEFENCE", + "000449.htm", + [ + "Australian Defence Forces", + "000397.htm", + [ + "Air Force", + "002266.htm" + ], + [ + "Army", + "000388.htm" + ], + [ + "Badges and insignia", + "000401.htm" + ], + [ + "Cadets", + "000416.htm" + ], + [ + "Defence force commands", + "000456.htm" + ], + [ + "Military bands", + "000510.htm" + ], + [ + "Navy", + "000524.htm" + ], + [ + "Reserves", + "000553.htm" + ], + [ + "Roulettes", + "000556.htm" + ] + ], + [ + "Defence estate management", + "000454.htm" + ], + [ + "Defence force careers", + "000455.htm", + [ + "Defence career development", + "000450.htm" + ], + [ + "Military employment services", + "000512.htm" + ] + ], + [ + "Defence strategic development", + "000574.htm", + [ + "Defence efficiency review", + "000453.htm" + ], + [ + "Defence reform program", + "000460.htm" + ] + ], + [ + "Defence strategic policy", + "000576.htm", + [ + "Strategic decisions", + "000573.htm" + ], + [ + "Strategic planning", + "000575.htm" + ] + ], + [ + "Defence strategic support", + "000577.htm", + [ + "Capital equipment programs", + "000419.htm" + ], + [ + "Defence industry", + "000459.htm" + ], + [ + "Defence science and technology", + "000905.htm" + ], + [ + "Logistics", + "000506.htm" + ] + ], + [ + "Emergency management", + "000473.htm", + [ + "Civil community assistance", + "000426.htm" + ], + [ + "Disaster support", + "000464.htm" + ] + ], + [ + "Military law", + "001814.htm" + ], + [ + "Military operations", + "000515.htm", + [ + "Australian theatre of war", + "000398.htm" + ], + [ + "Military exercises", + "000514.htm" + ], + [ + "Special operations", + "000569.htm" + ], + [ + "Warfare", + "000589.htm" + ] + ] + ], + [ + "EDUCATION AND TRAINING", + "000467.htm", + [ + "Arts education", + "000392.htm" + ], + [ + "Community education", + "000434.htm", + [ + "Adult education programs", + "000379.htm" + ], + [ + "Adult migrant education", + "000380.htm" + ], + [ + "Road safety awareness", + "000555.htm" + ] + ], + [ + "Curriculum development", + "000447.htm" + ], + [ + "Early childhood education", + "000466.htm" + ], + [ + "Military education and training", + "000511.htm", + [ + "Army reserve training", + "000389.htm" + ], + [ + "Defence college training", + "000451.htm" + ] + ], + [ + "Overseas skills recognition", + "000527.htm" + ], + [ + "School education", + "000561.htm", + [ + "Discipline programs", + "000465.htm" + ], + [ + "School transport regulation", + "000562.htm" + ], + [ + "Special needs programs", + "000568.htm" + ] + ], + [ + "Tertiary education", + "000581.htm" + ], + [ + "Vocational education", + "000588.htm", + [ + "Apprenticeship programs", + "000386.htm" + ], + [ + "Hospital teaching programs", + "000493.htm" + ], + [ + "Trainee programs", + "000584.htm" + ], + [ + "Workplace training", + "000595.htm" + ] + ] + ], + [ + "EMPLOYMENT", + "000016.htm", + [ + "Human resources development", + "000348.htm", + [ + "Occupational health and safety", + "000355.htm" + ], + [ + "Recruitment programs", + "000356.htm" + ], + [ + "Termination processes", + "002103.htm" + ], + [ + "Workers compensation schemes", + "000363.htm" + ], + [ + "Workplace discrimination monitoring", + "000364.htm" + ], + [ + "Workplace equity and justice programs", + "000365.htm" + ] + ], + [ + "Industrial awards and conditions", + "000349.htm", + [ + "Award conditions", + "000366.htm" + ], + [ + "Remuneration review", + "000368.htm" + ] + ], + [ + "Labour market programs", + "000347.htm", + [ + "Employment services marketing", + "000351.htm" + ], + [ + "Job placement programs", + "000352.htm" + ], + [ + "Job vacancy data management", + "000353.htm" + ] + ], + [ + "Workplace agreement processes", + "000350.htm", + [ + "Agreement bargaining processes", + "002141.htm" + ], + [ + "Agreement dispute mediation", + "000370.htm" + ], + [ + "Certified employment conditions", + "000371.htm" + ], + [ + "Employment advocacy services", + "000374.htm" + ] + ] + ], + [ + "ENVIRONMENT", + "000478.htm", + [ + "Built environment", + "000409.htm", + [ + "Building acoustics", + "000405.htm" + ], + [ + "Building preservation", + "000407.htm" + ] + ], + [ + "Climate information services", + "000203.htm" + ], + [ + "Conservation programs", + "000438.htm", + [ + "Endangered species protection", + "000475.htm" + ], + [ + "Landcare programs", + "000504.htm" + ], + [ + "Soil preservation programs", + "000567.htm" + ] + ], + [ + "Environmental impact assessment", + "000479.htm" + ], + [ + "Historic relic protection", + "000492.htm" + ], + [ + "Marine life protection programs", + "000507.htm" + ], + [ + "Natural heritage protection", + "000522.htm", + [ + "National park management", + "000520.htm" + ], + [ + "Nature reserve management", + "000523.htm" + ], + [ + "Wilderness area management", + "000593.htm" + ], + [ + "Wildlife sanctuary management", + "000594.htm" + ] + ], + [ + "Oceans governance", + "000525.htm" + ], + [ + "Pollutant prevention programs", + "000529.htm" + ], + [ + "World heritage listings", + "000596.htm" + ] + ], + [ + "FINANCE MANAGEMENT", + "000001.htm", + [ + "Commonwealth State funding", + "000017.htm" + ], + [ + "Currency", + "000018.htm", + [ + "Counterfeiting control", + "000027.htm" + ] + ], + [ + "Financial investment", + "000021.htm", + [ + "Investment scheme regulation", + "000031.htm" + ] + ], + [ + "Financial system management", + "002302.htm", + [ + "Financial system monitoring", + "002306.htm" + ], + [ + "Payments management", + "002297.htm" + ] + ], + [ + "Fiscal policy", + "000022.htm", + [ + "Public borrowing", + "000032.htm" + ], + [ + "Public debt", + "000033.htm" + ], + [ + "Public expenditure", + "000034.htm" + ] + ], + [ + "Foreign investment control", + "000030.htm" + ], + [ + "International monetary regulation", + "000023.htm" + ], + [ + "Monetary policy", + "000020.htm", + [ + "Exchange rates", + "000028.htm" + ], + [ + "Inflation", + "000029.htm" + ], + [ + "Interest rates", + "002295.htm" + ] + ], + [ + "Resource management", + "000024.htm", + [ + "Contract management", + "000037.htm" + ], + [ + "Financial administration", + "000035.htm" + ], + [ + "Financial budgeting", + "000036.htm" + ] + ], + [ + "Retirement income", + "000025.htm", + [ + "Asset assessment", + "000038.htm" + ], + [ + "Retirement saving", + "000039.htm" + ], + [ + "Superannuation regulation", + "000040.htm" + ] + ], + [ + "Statistical analysis", + "000019.htm" + ], + [ + "Taxation", + "000026.htm", + [ + "Income assessment", + "000041.htm" + ], + [ + "Revenue raising", + "000042.htm" + ], + [ + "Taxation compliance", + "000043.htm" + ] + ] + ], + [ + "GOVERNANCE", + "000002.htm", + [ + "Civic celebrations", + "000046.htm" + ], + [ + "Constitutional matters", + "000048.htm", + [ + "Constitutional conventions", + "000061.htm" + ], + [ + "Constitutional referenda", + "000062.htm" + ] + ], + [ + "Electoral matters", + "000049.htm", + [ + "Declaration of interests", + "000063.htm" + ], + [ + "Election campaigning", + "000064.htm" + ], + [ + "Electoral boundary assessment", + "000065.htm" + ] + ], + [ + "Honours and awards programs", + "000050.htm" + ], + [ + "Intergovernmental relations", + "000052.htm", + [ + "Cross-border cooperation", + "000067.htm" + ], + [ + "Intergovernment policy dissemination", + "000069.htm" + ] + ], + [ + "Legislative drafting", + "000053.htm", + [ + "Draft Bill amendment process", + "000070.htm" + ], + [ + "Legislation review", + "000071.htm" + ], + [ + "Preparation of legislative regulations", + "000072.htm" + ] + ], + [ + "Official protocol", + "000054.htm", + [ + "Ceremonial events and representation", + "000073.htm" + ], + [ + "Head of Government protocol", + "000074.htm" + ], + [ + "Official establishment management", + "000075.htm" + ], + [ + "Official hospitality", + "000076.htm" + ], + [ + "Official visits", + "000077.htm" + ] + ], + [ + "Parliamentary chamber support", + "000045.htm", + [ + "Hansard services", + "000058.htm" + ], + [ + "Parliamentary papers", + "000059.htm" + ], + [ + "Tabling of official documents", + "000060.htm" + ] + ], + [ + "Parliamentary committee and member support", + "000047.htm" + ], + [ + "Population census", + "000044.htm", + [ + "Demographic surveys", + "000055.htm" + ], + [ + "Household census", + "000056.htm" + ], + [ + "Population distribution analysis", + "000057.htm" + ] + ], + [ + "Public administration", + "002290.htm", + [ + "Government auditing", + "002291.htm" + ], + [ + "Public service", + "002292.htm" + ], + [ + "Territories administration", + "002293.htm" + ] + ], + [ + "Statistics", + "002289.htm" + ] + ], + [ + "HEALTH CARE", + "000003.htm", + [ + "Community health services", + "000078.htm", + [ + "Clinical health services", + "000087.htm" + ], + [ + "Health promotion", + "000089.htm" + ], + [ + "Primary health care", + "000090.htm" + ] + ], + [ + "Health insurance schemes", + "000079.htm" + ], + [ + "Health protocol administration", + "000080.htm", + [ + "Drugs and poisons regulation", + "000091.htm" + ], + [ + "Food hygiene regulation", + "000092.htm" + ], + [ + "Practitioner discipline", + "000093.htm" + ], + [ + "Practitioner registration", + "000095.htm" + ] + ], + [ + "Hospital services", + "000081.htm", + [ + "Hospital administration", + "000096.htm" + ], + [ + "Patient care", + "000097.htm" + ], + [ + "Pharmaceutical and drug support", + "000098.htm" + ] + ], + [ + "Medical research", + "000083.htm", + [ + "Ethical compliance", + "000099.htm" + ], + [ + "Medical aids regulation", + "000100.htm" + ], + [ + "Medical research funding", + "000101.htm" + ], + [ + "National referral laboratory services", + "000102.htm" + ], + [ + "Population-based research", + "000103.htm" + ] + ], + [ + "Public health services", + "000084.htm", + [ + "Epidemiology", + "000104.htm" + ], + [ + "Health disaster preparedness", + "000106.htm" + ], + [ + "Health hazard prevention", + "000105.htm" + ], + [ + "Medical quarantine services", + "000108.htm" + ], + [ + "Notifiable disease management", + "000109.htm" + ] + ], + [ + "Special needs services", + "000085.htm", + [ + "Dental health services", + "000111.htm" + ], + [ + "Independent living services", + "000112.htm" + ], + [ + "Mental health services", + "000113.htm" + ], + [ + "Residential services", + "000114.htm" + ] + ], + [ + "Veterans\" health services", + "000086.htm" + ] + ], + [ + "IMMIGRATION", + "000004.htm", + [ + "Citizenship", + "000115.htm", + [ + "Naturalisation assessment", + "000121.htm" + ], + [ + "Presentation arrangements", + "000122.htm" + ] + ], + [ + "Deportation", + "000116.htm" + ], + [ + "Detention programs", + "000117.htm" + ], + [ + "Migrant services", + "000118.htm", + [ + "Interpreter services", + "000123.htm" + ], + [ + "Migrant accommodation services", + "000125.htm" + ], + [ + "Migrant settlement programs", + "000126.htm" + ] + ], + [ + "Refugee services", + "000119.htm" + ], + [ + "Travel authorisation", + "000120.htm", + [ + "Permanent entry arrangements", + "000127.htm" + ], + [ + "Temporary entry arrangements", + "000128.htm" + ] + ] + ], + [ + "INDIGENOUS AFFAIRS", + "002285.htm", + [ + "Indigenous cultural heritage", + "000495.htm", + [ + "Cultural artefact repatriation", + "001772.htm" + ] + ], + [ + "Indigenous enterprise development", + "000496.htm" + ], + [ + "Indigenous health services", + "000082.htm" + ], + [ + "Indigenous heritage conservation", + "000497.htm" + ], + [ + "Indigenous reconciliation", + "000051.htm" + ], + [ + "Indigenous welfare programs", + "000498.htm" + ] + ], + [ + "INTERNATIONAL RELATIONS", + "000005.htm", + [ + "Consular services", + "000129.htm" + ], + [ + "Defence liaison", + "000132.htm", + [ + "Defence attachés", + "000140.htm" + ], + [ + "Defence cooperation programs", + "000141.htm" + ], + [ + "Defence personnel exchanges", + "000142.htm" + ], + [ + "Military advisors", + "000143.htm" + ] + ], + [ + "Diplomatic missions", + "000130.htm" + ], + [ + "International affairs", + "000131.htm", + [ + "Disarmament and arms control", + "000137.htm" + ], + [ + "Nuclear safeguards", + "000139.htm" + ] + ], + [ + "International treaties", + "000133.htm", + [ + "Treaty administration", + "001384.htm" + ] + ], + [ + "Overseas aid programs", + "000134.htm", + [ + "Development assistance programs", + "000146.htm" + ], + [ + "Overseas student scholarship programs", + "000147.htm" + ] + ], + [ + "Overseas promotion", + "000135.htm" + ], + [ + "Passport services", + "000136.htm" + ] + ], + [ + "JUSTICE ADMINISTRATION", + "000006.htm", + [ + "Administrative law", + "000149.htm", + [ + "Administrative decision appeal", + "000163.htm" + ], + [ + "Administrative decision review", + "000164.htm" + ], + [ + "Censorship standards", + "000165.htm" + ], + [ + "Copyright regulation", + "000166.htm" + ], + [ + "Recordkeeping standards", + "000167.htm" + ] + ], + [ + "Associations and corporate law", + "000150.htm", + [ + "Bankruptcy proceedings", + "000168.htm" + ], + [ + "Merger regulation", + "000169.htm" + ] + ], + [ + "Civil law", + "000151.htm", + [ + "Civil registration", + "000170.htm" + ], + [ + "Conveyancing", + "000171.htm" + ], + [ + "Dispute mediation programs", + "000174.htm" + ], + [ + "Family law proceedings", + "000172.htm" + ], + [ + "Probate applications", + "000173.htm" + ] + ], + [ + "Commissions of inquiry", + "000152.htm" + ], + [ + "Coronial law", + "000153.htm" + ], + [ + "Court reporting", + "000154.htm" + ], + [ + "Criminal law", + "000155.htm" + ], + [ + "Human rights obligations", + "000156.htm" + ], + [ + "Juvenile justice", + "000157.htm" + ], + [ + "Legal aid services", + "000158.htm" + ], + [ + "Local laws and ordinances", + "000159.htm" + ], + [ + "Native title claims", + "000160.htm", + [ + "Native title mediation programs", + "000180.htm" + ], + [ + "Native title settlement negotiations", + "000181.htm" + ] + ], + [ + "Privacy guideline monitoring", + "000161.htm" + ], + [ + "Prosecution services", + "000162.htm" + ] + ], + [ + "MARITIME SERVICES", + "000007.htm", + [ + "Cargo control", + "000198.htm" + ], + [ + "Harbour management", + "000183.htm" + ], + [ + "Marina management", + "000184.htm" + ], + [ + "Navigation", + "000185.htm", + [ + "Charting sea lanes", + "000191.htm" + ], + [ + "Light station maintenance", + "000192.htm" + ], + [ + "Navigation systems development", + "000193.htm" + ] + ], + [ + "Pilotage management", + "000182.htm" + ], + [ + "Port regulation", + "001471.htm" + ], + [ + "Sea passengers", + "001487.htm" + ], + [ + "Search and rescue", + "000187.htm", + [ + "Rescue coordination", + "000194.htm" + ], + [ + "Search technology development", + "000195.htm" + ] + ], + [ + "Ship personnel", + "000188.htm", + [ + "Personnel safety standards", + "000196.htm" + ], + [ + "Pilot licensing", + "000197.htm" + ] + ], + [ + "Ship safety", + "000189.htm", + [ + "Seaworthiness", + "000201.htm" + ], + [ + "Ship commissioning", + "000199.htm" + ], + [ + "Ship inspection", + "000200.htm" + ] + ] + ], + [ + "NATURAL RESOURCES", + "000009.htm", + [ + "Crown land administration", + "000218.htm" + ], + [ + "Energy resources", + "001451.htm", + [ + "Fossil fuel management", + "000226.htm" + ], + [ + "Renewable energy development", + "000227.htm" + ] + ], + [ + "Land use planning", + "000220.htm", + [ + "Land use zoning", + "001429.htm" + ] + ], + [ + "Land valuation", + "000222.htm" + ], + [ + "Mineral resources", + "000223.htm", + [ + "Land rehabilitation programs", + "000231.htm" + ], + [ + "Mining regulations", + "000232.htm" + ] + ], + [ + "Pollution emission control", + "000224.htm" + ], + [ + "Water resources", + "000225.htm", + [ + "Water conservation plans", + "000233.htm" + ], + [ + "Water quality monitoring", + "000234.htm" + ], + [ + "Water usage management", + "000235.htm" + ], + [ + "Waterway management", + "000236.htm" + ] + ] + ], + [ + "PRIMARY INDUSTRIES", + "000008.htm", + [ + "Chemical and pesticide regulation", + "000202.htm" + ], + [ + "Marine and rural regulation", + "000204.htm", + [ + "Agricultural industry", + "000208.htm" + ], + [ + "Fisheries industry", + "000209.htm" + ], + [ + "Food quality assurance", + "000213.htm" + ], + [ + "Forestry industry", + "000210.htm" + ] + ], + [ + "Marine and rural support", + "000205.htm", + [ + "Aquaculture industry", + "000212.htm" + ], + [ + "Horticultural industry", + "000214.htm" + ], + [ + "Pastoral industry", + "000215.htm" + ], + [ + "Silvicultural industry", + "000216.htm" + ], + [ + "Viticultural industry", + "000217.htm" + ] + ], + [ + "Quarantine", + "000211.htm" + ], + [ + "Rural field day promotion", + "000206.htm" + ], + [ + "Rural partnership programs", + "000207.htm" + ] + ], + [ + "SCIENCE", + "000010.htm", + [ + "Agricultural sciences", + "000237.htm" + ], + [ + "Animal and veterinary sciences", + "000238.htm" + ], + [ + "Applied sciences", + "000239.htm", + [ + "Engineering and technology sciences", + "000246.htm" + ], + [ + "Weights and measures standards", + "000247.htm" + ] + ], + [ + "Biological sciences", + "000240.htm", + [ + "Biochemistry", + "000248.htm" + ], + [ + "Biodiversity preservation", + "000249.htm" + ], + [ + "Biotechnology development", + "000250.htm" + ], + [ + "Botany", + "000251.htm" + ], + [ + "Entomology", + "000252.htm" + ], + [ + "Genetic engineering", + "000253.htm" + ], + [ + "Physiology", + "000254.htm" + ] + ], + [ + "Earth sciences", + "000241.htm", + [ + "Atmospheric sciences", + "000255.htm" + ], + [ + "Hydrology", + "000256.htm" + ], + [ + "Mineral exploration", + "000257.htm" + ], + [ + "Oceanography", + "000258.htm" + ], + [ + "Seismology", + "000259.htm" + ] + ], + [ + "Mathematical sciences", + "000242.htm" + ], + [ + "Medical and health sciences", + "001229.htm" + ], + [ + "Physical sciences", + "000244.htm", + [ + "Astronomical sciences", + "000260.htm" + ], + [ + "Atomic and molecular sciences", + "000261.htm" + ], + [ + "Nuclear sciences", + "000262.htm" + ] + ], + [ + "Spatial information research", + "000245.htm" + ] + ], + [ + "SECURITY", + "000011.htm", + [ + "Corrective services", + "000263.htm", + [ + "Community based corrections", + "000270.htm" + ], + [ + "Detention centre management", + "000271.htm" + ] + ], + [ + "External security", + "000264.htm", + [ + "Peacekeeping forces", + "000273.htm" + ] + ], + [ + "Information security", + "000265.htm" + ], + [ + "Intelligence", + "000266.htm", + [ + "Forensic analysis", + "000275.htm" + ], + [ + "Intelligence liaison", + "001159.htm" + ], + [ + "Intelligence support", + "000277.htm" + ] + ], + [ + "Law enforcement", + "000267.htm", + [ + "Community policing", + "000278.htm" + ], + [ + "Criminology", + "000279.htm" + ], + [ + "Police administration", + "000280.htm" + ], + [ + "Protective services", + "000281.htm" + ] + ], + [ + "National security", + "001548.htm", + [ + "Coastal surveillance", + "000269.htm" + ] + ] + ], + [ + "SPORT AND RECREATION", + "000012.htm", + [ + "Caravan and camping services", + "000284.htm" + ], + [ + "Community recreational programs", + "000285.htm", + [ + "Gambling support services", + "000290.htm" + ], + [ + "Public fitness promotion", + "000291.htm" + ], + [ + "Social club support", + "000292.htm" + ], + [ + "Volunteer support programs", + "000293.htm" + ] + ], + [ + "Gaming industry regulation", + "000286.htm" + ], + [ + "Park and reserve services", + "000289.htm" + ], + [ + "Sport and fitness development", + "000287.htm", + [ + "Athlete scholarship programs", + "000295.htm" + ], + [ + "Fundraising and donation schemes", + "000296.htm" + ], + [ + "Games administration", + "000297.htm" + ], + [ + "Games promotion", + "000298.htm" + ], + [ + "Sporting grants administration", + "000299.htm" + ] + ], + [ + "Sports drugs monitoring", + "000288.htm", + [ + "Doping detection research", + "000300.htm" + ] + ] + ], + [ + "TOURISM", + "000013.htm", + [ + "Tourism industry development", + "000301.htm" + ], + [ + "Tourist event promotion", + "000302.htm", + [ + "Advertising campaigns", + "000305.htm" + ] + ], + [ + "Travel missions", + "000303.htm" + ] + ], + [ + "TRADE", + "000014.htm", + [ + "Export regulation", + "000311.htm", + [ + "Export promotion", + "000320.htm" + ], + [ + "Prohibited export control", + "000321.htm" + ] + ], + [ + "Import regulation", + "000312.htm", + [ + "Prohibited import control", + "000323.htm" + ], + [ + "Tariff regulation", + "000322.htm" + ] + ], + [ + "Intellectual property management", + "000910.htm", + [ + "Copyright registration", + "000308.htm" + ], + [ + "Design registration", + "000310.htm" + ], + [ + "Patent registration", + "000315.htm" + ], + [ + "Trademark registration", + "000318.htm" + ] + ], + [ + "International trade agreements", + "000313.htm" + ], + [ + "Interstate trade agreements", + "000314.htm" + ], + [ + "Trade development programs", + "000316.htm" + ], + [ + "Trade expositions", + "000317.htm" + ] + ], + [ + "TRANSPORT", + "000015.htm", + [ + "Air transport", + "000324.htm", + [ + "Air transport safety", + "000332.htm" + ], + [ + "Aircraft standards", + "000330.htm" + ], + [ + "Airport services", + "000331.htm" + ] + ], + [ + "Freight movement regulation", + "000325.htm" + ], + [ + "Passenger services", + "000326.htm" + ], + [ + "Rail transport", + "000327.htm", + [ + "Rail harmonisation standards", + "000333.htm" + ], + [ + "Rail land acquisition regulation", + "000336.htm" + ], + [ + "Rail transport safety", + "000337.htm" + ], + [ + "Railway maintenance", + "000338.htm" + ], + [ + "Rolling stock regulation", + "000339.htm" + ] + ], + [ + "Road transport", + "000328.htm", + [ + "Driving licences administration", + "000340.htm" + ], + [ + "Road surface maintenance", + "000341.htm" + ], + [ + "Road traffic regulation", + "000342.htm" + ], + [ + "Road transport safety", + "000343.htm" + ], + [ + "Vehicle registration", + "000344.htm" + ], + [ + "Vehicle standards", + "000345.htm" + ] + ], + [ + "Transport infrastructure development", + "000329.htm" + ] + ] +] --- a/charts.php +++ b/charts.php @@ -1,6 +1,6 @@ get_db('disclosr-agencies'); ?>
--- a/getAgency.php +++ b/getAgency.php @@ -1,23 +1,27 @@ "; echo "" . $schemas['agency']["properties"][$key]['x-title'] . "
" . $schemas['agency']["properties"][$key]['description'] . ""; if (is_array($value)) { echo "
    "; foreach ($value as $subkey => $subvalue) { - if (isset($schemas['agency']["properties"][$key]['x-property'])) { - echo '
  1. '; - } else { - echo "
  2. "; - } + + echo "
  3. "; + echo "$subvalue
  4. "; } echo "
"; @@ -28,8 +32,8 @@ echo ""; } if ((strpos($key, "URL") > 0 || $key == 'website') && $value != "") { - echo "view"; - } else { + echo "$value"; + } else { echo "$value"; } } @@ -61,7 +65,8 @@ } else { echo ""; if ((strpos($key, "URL") > 0 || $key == 'website') && $value != "") { - echo "view"; + echo "view"; + } if ($key == 'abn') { echo "view abn"; @@ -108,6 +113,7 @@ // edit? $obj = $db->get($_REQUEST['id']); + include_header($obj->name); //print_r($row); if (sizeof($_POST) > 0) { //print_r($_POST); @@ -137,7 +143,7 @@ } } - $mode = "edit"; + $mode = "view"; $rowArray = object_to_array($obj); ksort($rowArray); if ($mode == "edit") { @@ -147,8 +153,8 @@ } if ($mode == "view") { - echo '
'; - echo '"; + echo '

' . $row['name'] . "

'; + echo '"; echo ""; } if ($mode == "edit") { @@ -185,7 +191,8 @@ echo ''; } } else { - +// show all list + include_header('Agencies'); try { /* $rows = $db->get_view("app", "showNamesABNs")->rows; //print_r($rows); @@ -200,10 +207,10 @@ echo '"; } catch (SetteeRestClientException $e) { --- /dev/null +++ b/google676a414ad086cefb.html @@ -1,1 +1,2 @@ +google-site-verification: google676a414ad086cefb.html --- a/include/common.inc.php +++ b/include/common.inc.php @@ -51,6 +51,9 @@ function phrase_to_tag ($phrase) { return str_replace(" ","_",str_replace("'","",str_replace(",","",strtolower($phrase)))); } +function local_url() { + return "http://" . $_SERVER['HTTP_HOST'] . rtrim(dirname($_SERVER['PHP_SELF']), '/\\') . "/"; +} function GetDomain($url) { $nowww = ereg_replace('www\.','',$url); --- a/include/couchdb.inc.php +++ b/include/couchdb.inc.php @@ -161,7 +161,7 @@ } else if (php_uname('n') == "KYUUBEY") { - $serverAddr = 'http://127.0.0.1:5984/'; + $serverAddr = 'http://192.168.1.148:5984/'; } else { $serverAddr = 'http://127.0.0.1:5984/'; } --- a/include/template.inc.php +++ b/include/template.inc.php @@ -1,6 +1,6 @@ @@ -16,7 +16,7 @@ - Disclosr + <?php echo $title; ?> - Disclosr @@ -71,9 +71,27 @@ + + --- a/javascripts/bubbletree +++ b/javascripts/bubbletree --- a/javascripts/flotr2 +++ b/javascripts/flotr2 --- a/javascripts/sigma +++ b/javascripts/sigma --- a/javascripts/sigma.min.js +++ b/javascripts/sigma.min.js @@ -1,63 +1,3552 @@ -/* sigmajs.org - an open-source light-weight JavaScript graph drawing library - Version: 0.1 - Author: Alexis Jacomy - License: MIT */ -var sigma={tools:{},classes:{},instances:{}}; -(function(){if(!Array.prototype.some)Array.prototype.some=function(i,n){var g=this.length;if("function"!=typeof i)throw new TypeError;for(var j=0;j";a+="

LOCAL :

";for(b in d.p.localProbes)a+="

"+b+" : "+d.p.localProbes[b]()+"

";d.p.dom.innerHTML= -a;return d}sigma.classes.Cascade.call(this);var d=this;this.instance=b;this.monitoring=!1;this.p={fps:40,dom:h,globalProbes:{"Time (ms)":sigma.chronos.getExecutionTime,Queue:sigma.chronos.getQueuedTasksCount,Tasks:sigma.chronos.getTasksCount,FPS:sigma.chronos.getFPS},localProbes:{"Nodes count":function(){return d.instance.graph.nodes.length},"Edges count":function(){return d.instance.graph.edges.length}}};this.activate=function(){if(!d.monitoring)d.monitoring=window.setInterval(e,1E3/d.p.fps);return d}; -this.desactivate=function(){if(d.monitoring)window.clearInterval(d.monitoring),d.monitoring=null,d.p.dom.innerHTML="";return d}}function j(b){function h(b){if(a.p.mouseEnabled&&(e(a.mouseX,a.mouseY,a.ratio*(0<(void 0!=b.wheelDelta&&b.wheelDelta||void 0!=b.detail&&-b.detail)?a.p.zoomMultiply:1/a.p.zoomMultiply)),a.p.blockScroll))b.preventDefault?b.preventDefault():b.returnValue=!1}function e(b,c,e){if(!a.isMouseDown&&(window.clearInterval(a.interpolationID),n=void 0!=e,i=a.stageX,j=b,k=a.stageY,l= -c,f=e||a.ratio,f=Math.min(Math.max(f,a.p.minRatio),a.p.maxRatio),u=a.p.directZooming?1-(n?a.p.zoomDelta:a.p.dragDelta):0,a.ratio!=f||a.stageX!=j||a.stageY!=l))d(),a.interpolationID=window.setInterval(d,50),a.dispatch("startinterpolate")}function d(){u+=n?a.p.zoomDelta:a.p.dragDelta;u=Math.min(u,1);var b=sigma.easing.quadratic.easeout(u),c=a.ratio;a.ratio=c*(1-b)+f*b;n?(a.stageX=j+(a.stageX-j)*a.ratio/c,a.stageY=l+(a.stageY-l)*a.ratio/c):(a.stageX=i*(1-b)+j*b,a.stageY=k*(1-b)+l*b);a.dispatch("interpolate"); -if(1<=u)window.clearInterval(a.interpolationID),b=a.ratio,n?(a.ratio=f,a.stageX=j+(a.stageX-j)*a.ratio/b,a.stageY=l+(a.stageY-l)*a.ratio/b):(a.stageX=j,a.stageY=l),a.dispatch("stopinterpolate")}sigma.classes.Cascade.call(this);sigma.classes.EventDispatcher.call(this);var a=this;this.p={minRatio:1,maxRatio:32,marginRatio:1,zoomDelta:0.1,dragDelta:0.3,zoomMultiply:2,directZooming:!1,blockScroll:!0,inertia:1.1,mouseEnabled:!0};var g=0,c=0,i=0,k=0,f=1,j=0,l=0,s=0,q=0,z=0,m=0,u=0,n=!1;this.stageY=this.stageX= -0;this.ratio=1;this.mouseY=this.mouseX=0;this.isMouseDown=!1;b.addEventListener("DOMMouseScroll",h,!0);b.addEventListener("mousewheel",h,!0);b.addEventListener("mousemove",function(b){a.mouseX=void 0!=b.offsetX&&b.offsetX||void 0!=b.layerX&&b.layerX||void 0!=b.clientX&&b.clientX;a.mouseY=void 0!=b.offsetY&&b.offsetY||void 0!=b.layerY&&b.layerY||void 0!=b.clientY&&b.clientY;if(a.isMouseDown){var d=a.mouseX-g+i,f=a.mouseY-c+k;if(d!=a.stageX||f!=a.stageY)q=s,m=z,s=d,z=f,a.stageX=d,a.stageY=f,a.dispatch("drag")}a.dispatch("move"); -b.preventDefault?b.preventDefault():b.returnValue=!1},!0);b.addEventListener("mousedown",function(b){if(a.p.mouseEnabled)a.isMouseDown=!0,a.dispatch("mousedown"),i=a.stageX,k=a.stageY,g=a.mouseX,c=a.mouseY,q=s=a.stageX,m=z=a.stageY,a.dispatch("startdrag"),b.preventDefault?b.preventDefault():b.returnValue=!1},!0);document.addEventListener("mouseup",function(b){if(a.p.mouseEnabled&&a.isMouseDown)a.isMouseDown=!1,a.dispatch("mouseup"),(i!=a.stageX||k!=a.stageY)&&e(a.stageX+a.p.inertia*(a.stageX-q),a.stageY+ -a.p.inertia*(a.stageY-m)),b.preventDefault?b.preventDefault():b.returnValue=!1},!0);this.checkBorders=function(){return a};this.interpolate=e}function m(b,h,e,d,a,g,c){function i(a){var b=d,c="fixed"==f.p.labelSize?f.p.defaultLabelSize:f.p.labelSizeRatio*a.displaySize;b.font=(f.p.hoverFontStyle||f.p.fontStyle||"")+" "+c+"px "+(f.p.hoverFont||f.p.font||"");b.fillStyle="node"==f.p.labelHoverBGColor?a.color||f.p.defaultNodeColor:f.p.defaultHoverLabelBGColor;b.beginPath();if(f.p.labelHoverShadow)b.shadowOffsetX= -0,b.shadowOffsetY=0,b.shadowBlur=4,b.shadowColor=f.p.labelHoverShadowColor;sigma.tools.drawRoundRect(b,Math.round(a.displayX-c/2-2),Math.round(a.displayY-c/2-2),Math.round(b.measureText(a.label).width+1.5*a.displaySize+c/2+4),Math.round(c+4),Math.round(c/2+2),"left");b.closePath();b.fill();b.shadowOffsetX=0;b.shadowOffsetY=0;b.shadowBlur=0;b.beginPath();b.fillStyle="node"==f.p.nodeBorderColor?a.color||f.p.defaultNodeColor:f.p.defaultNodeBorderColor;b.arc(Math.round(a.displayX),Math.round(a.displayY), -a.displaySize+f.p.borderSize,0,2*Math.PI,!0);b.closePath();b.fill();b.beginPath();b.fillStyle="node"==f.p.nodeHoverColor?a.color||f.p.defaultNodeColor:f.p.defaultNodeHoverColor;b.arc(Math.round(a.displayX),Math.round(a.displayY),a.displaySize,0,2*Math.PI,!0);b.closePath();b.fill();b.fillStyle="node"==f.p.labelHoverColor?a.color||f.p.defaultNodeColor:f.p.defaultLabelHoverColor;b.fillText(a.label,Math.round(a.displayX+1.5*a.displaySize),Math.round(a.displayY+c/2-3));return f}function k(a){if(isNaN(a.x)|| -isNaN(a.y))throw Error("A node's coordinate is not a number (id: "+a.id+")");return!a.hidden&&a.displayX+a.displaySize>-j/3&&a.displayX-a.displaySize<4*j/3&&a.displayY+a.displaySize>-l/3&&a.displayY-a.displaySize<4*l/3}sigma.classes.Cascade.call(this);var f=this;this.p={labelColor:"default",defaultLabelColor:"#000",labelHoverBGColor:"default",defaultHoverLabelBGColor:"#fff",labelHoverShadow:!0,labelHoverShadowColor:"#000",labelHoverColor:"default",defaultLabelHoverColor:"#000",labelActiveBGColor:"default", -defaultActiveLabelBGColor:"#fff",labelActiveShadow:!0,labelActiveShadowColor:"#000",labelActiveColor:"default",defaultLabelActiveColor:"#000",labelSize:"fixed",defaultLabelSize:12,labelSizeRatio:2,labelThreshold:6,font:"Arial",hoverFont:"",activeFont:"",fontStyle:"",hoverFontStyle:"",activeFontStyle:"",edgeColor:"source",defaultEdgeColor:"#aaa",defaultEdgeType:"line",defaultNodeColor:"#aaa",nodeHoverColor:"node",defaultNodeHoverColor:"#fff",nodeActiveColor:"node",defaultNodeActiveColor:"#fff",borderSize:0, -nodeBorderColor:"node",defaultNodeBorderColor:"#fff",edgesSpeed:200,nodesSpeed:200,labelsSpeed:200};var j=g,l=c;this.currentLabelIndex=this.currentNodeIndex=this.currentEdgeIndex=0;this.task_drawLabel=function(){for(var b=a.nodes.length,c=0;c++=f.p.labelThreshold){var k="fixed"==f.p.labelSize?f.p.defaultLabelSize:f.p.labelSizeRatio*d.displaySize;h.font= -f.p.fontStyle+k+"px "+f.p.font;h.fillStyle="node"==f.p.labelColor?d.color||f.p.defaultNodeColor:f.p.defaultLabelColor;h.fillText(d.label,Math.round(d.displayX+1.5*d.displaySize),Math.round(d.displayY+k/2-3))}}else f.currentLabelIndex++;return f.currentLabelIndex(b*=2)?0.5*b*b:-0.5*(--b*(b-2)-1)};sigma.tools.drawRoundRect=function(b,h,e,d,a,g,c){var g=g?g:0,i=c?c:[],i="string"==typeof i?i.split(" "):i,c=g&&(0<=i.indexOf("topleft")||0<=i.indexOf("top")||0<=i.indexOf("left")),j=g&&(0<=i.indexOf("topright")||0<=i.indexOf("top")||0<=i.indexOf("right")),f=g&&(0<=i.indexOf("bottomleft")||0<=i.indexOf("bottom")||0<=i.indexOf("left")),i=g&&(0<=i.indexOf("bottomright")|| -0<=i.indexOf("bottom")||0<=i.indexOf("right"));b.moveTo(h,e+g);c?b.arcTo(h,e,h+g,e,g):b.lineTo(h,e);j?(b.lineTo(h+d-g,e),b.arcTo(h+d,e,h+d,e+g,g)):b.lineTo(h+d,e);i?(b.lineTo(h+d,e+a-g),b.arcTo(h+d,e+a,h+d-g,e+a,g)):b.lineTo(h+d,e+a);f?(b.lineTo(h+g,e+a),b.arcTo(h,e+a,h,e+a-g,g)):b.lineTo(h,e+a);b.lineTo(h,e+g)};sigma.tools.getRGB=function(b,g){var b=b.toString(),e={r:0,g:0,b:0};if(3<=b.length&&"#"==b.charAt(0)){var d=b.length-1;6==d?e={r:parseInt(b.charAt(1)+b.charAt(2),16),g:parseInt(b.charAt(3)+ -b.charAt(4),16),b:parseInt(b.charAt(5)+b.charAt(5),16)}:3==d&&(e={r:parseInt(b.charAt(1)+b.charAt(1),16),g:parseInt(b.charAt(2)+b.charAt(2),16),b:parseInt(b.charAt(3)+b.charAt(3),16)})}g&&(e=[e.r,e.g,e.b]);return e};sigma.tools.rgbToHex=function(b,g,e){return sigma.tools.toHex(b)+sigma.tools.toHex(g)+sigma.tools.toHex(e)};sigma.tools.toHex=function(b){b=parseInt(b,10);if(isNaN(b))return"00";b=Math.max(0,Math.min(b,255));return"0123456789ABCDEF".charAt((b-b%16)/16)+"0123456789ABCDEF".charAt(b%16)}; -sigma.publicPrototype=p.prototype})(); - +// Define packages: +var sigma = {}; +sigma.tools = {}; +sigma.classes = {}; +sigma.instances = {}; + +// Adding Array helpers, if not present yet: +(function() { + if (!Array.prototype.some) { + Array.prototype.some = function(fun /*, thisp*/) { + var len = this.length; + if (typeof fun != 'function') { + throw new TypeError(); + } + + var thisp = arguments[1]; + for (var i = 0; i < len; i++) { + if (i in this && + fun.call(thisp, this[i], i, this)) { + return true; + } + } + + return false; + }; + } + + if (!Array.prototype.forEach) { + Array.prototype.forEach = function(fun /*, thisp*/) { + var len = this.length; + if (typeof fun != 'function') { + throw new TypeError(); + } + + var thisp = arguments[1]; + for (var i = 0; i < len; i++) { + if (i in this) { + fun.call(thisp, this[i], i, this); + } + } + }; + } + + if (!Array.prototype.map) { + Array.prototype.map = function(fun /*, thisp*/) { + var len = this.length; + if (typeof fun != 'function') { + throw new TypeError(); + } + + var res = new Array(len); + var thisp = arguments[1]; + for (var i = 0; i < len; i++) { + if (i in this) { + res[i] = fun.call(thisp, this[i], i, this); + } + } + + return res; + }; + } + + if (!Array.prototype.filter) { + Array.prototype.filter = function(fun /*, thisp*/) { + var len = this.length; + if (typeof fun != 'function') + throw new TypeError(); + + var res = new Array(); + var thisp = arguments[1]; + for (var i = 0; i < len; i++) { + if (i in this) { + var val = this[i]; // in case fun mutates this + if (fun.call(thisp, val, i, this)) { + res.push(val); + } + } + } + + return res; + }; + } + + if (!Object.keys) { + Object.keys = (function() { + var hasOwnProperty = Object.prototype.hasOwnProperty, + hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), + dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ], + dontEnumsLength = dontEnums.length; + + return function(obj) { + if (typeof obj !== 'object' && + typeof obj !== 'function' || + obj === null + ) { + throw new TypeError('Object.keys called on non-object'); + } + + var result = []; + + for (var prop in obj) { + if (hasOwnProperty.call(obj, prop)) result.push(prop); + } + + if (hasDontEnumBug) { + for (var i = 0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + return result; + } + })(); + } +})(); + +/** + * A jQuery like properties management class. It works like jQuery .css() + * method: You can call it with juste one string to get the corresponding + * property, with a string and anything else to set the corresponding property, + * or directly with an object, and then each pair string / object (or any type) + * will be set in the properties. + * @constructor + * @this {sigma.classes.Cascade} + */ +sigma.classes.Cascade = function() { + /** + * This instance properties. + * @protected + * @type {Object} + */ + this.p = {}; + + /** + * The method to use to set/get any property of this instance. + * @param {(string|Object)} a1 If it is a string and if a2 is undefined, + * then it will return the corresponding + * property. + * If it is a string and if a2 is set, then it + * will set a2 as the property corresponding to + * a1, and return this. + * If it is an object, then each pair string / + * object (or any other type) will be set as a + * property. + * @param {*?} a2 The new property corresponding to a1 if a1 is + * a string. + * @return {(*|sigma.classes.Cascade)} Returns itself or the corresponding + * property. + */ + this.config = function(a1, a2) { + if (typeof a1 == 'string' && a2 == undefined) { + return this.p[a1]; + } else { + var o = (typeof a1 == 'object' && a2 == undefined) ? a1 : {}; + if (typeof a1 == 'string') { + o[a1] = a2; + } + + for (var k in o) { + if (this.p[k] != undefined) { + this.p[k] = o[k]; + } + } + return this; + } + }; +}; + +/** + * sigma.js custom event dispatcher class. + * @constructor + * @this {sigma.classes.EventDispatcher} + */ +sigma.classes.EventDispatcher = function() { + /** + * An object containing all the different handlers bound to one or many + * events, indexed by these events. + * @private + * @type {Object.} + */ + 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 += '

GLOBAL :

'; + for (var k in self.p.globalProbes) { + s += '

' + k + ' : ' + self.p.globalProbes[k]() + '

'; + } + + s += '

LOCAL :

'; + for (var k in self.p.localProbes) { + s += '

' + k + ' : ' + self.p.localProbes[k]() + '

'; + } + + 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.)} 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.)} 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.} 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.} 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.)} ids The ID or an array of node IDs. + * @return {(Object|Array.)} 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.)} ids The ID or an array of edge IDs. + * @return {(Object|Array.)} 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.} + */ + var generators = {}; + + /** + * The array of all the referenced and active tasks. + * @private + * @type {Array.} + */ + var tasks = []; + + /** + * The array of all the referenced and queued tasks. + * @private + * @type {Array.} + */ + 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.} 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; +})(); + + --- a/robots.txt +++ b/robots.txt @@ -3,3 +3,4 @@ User-agent: * Disallow: /admin/ +Sitemap: http://orgs.disclosurelo.gs/sitemap.xml.php --- a/schemas/agency.json.php +++ b/schemas/agency.json.php @@ -4,9 +4,9 @@ "description" => "Representation of government agency and online transparency measures", "type" => "object", "properties" => Array( - "name" => Array("type" => "string", "required" => true, "x-property" => "schema:name foaf:name", "x-title" => "Name", "description" => "Name, most recent and broadest"), + "name" => Array("type" => "string", "required" => true, "x-itemprop" => "name", "x-property" => "schema:name foaf:name", "x-title" => "Name", "description" => "Name, most recent and broadest"), "shortName" => Array("type" => "string", "required" => false, "x-title" => "Short Name", "description" => "Name shortened, usually to an acronym"), - "foiEmail" => Array("type" => "string", "required" => false, "x-title" => "FOI Contact Email", "description" => "FOI contact email if not foi@"), + "foiEmail" => Array("type" => "string", "required" => false, "x-title" => "FOI Contact Email", "x-itemprop" => "email", "description" => "FOI contact email if not foi@"), "sameAs" => Array("type" => "array", "required" => false, "x-property"=>"owl:sameAs","x-title" => "Same As", "description" => "Same as other URLs/URIs for this entity", "items" => Array("type" => "string")), "otherNames" => Array("type" => "array", "required" => true, "x-title" => "Past/Other Names", "description" => "Other names for organisation", @@ -15,7 +15,7 @@ "items" => Array("type" => "string")), "orgType" => Array("type" => "string", "required" => true, "x-title" => "Organisation Type", "description" => "Org type based on legal formation via FMA/CAC legislation etc."), "parentOrg" => Array("type" => "string", "required" => true, "x-title" => "Parent Organisation", "description" => "Parent organisation, usually a department of state"), - "website" => Array("type" => "string", "required" => true, "x-title" => "Website", "x-property" => "schema:url foaf:homepage", "description" => "Website URL"), + "website" => Array("type" => "string", "required" => true, "x-title" => "Website", "x-itemprop" => "url", "x-property" => "schema:url foaf:homepage", "description" => "Website URL"), "abn" => Array("type" => "string", "required" => true, "x-title" => "Australian Business Number", "description" => "ABN from business register"), "employees" => Array("type" => "string", "required" => true, "x-title" => "2010-2011 employees", "description" => "2010-2011 employees"), "contractListURL" => Array("type" => "string", "required" => true, "x-title" => "Contract Listing", "description" => "Departmental and agency contracts, mandated by the Senate" ), --- a/score.php +++ b/score.php @@ -1,7 +1,7 @@ get_db('disclosr-agencies'); --- a/search.php +++ b/search.php @@ -1,6 +1,6 @@

Search

--- /dev/null +++ b/sitemap.xml.php @@ -1,1 +1,25 @@ +"; +echo '' . "\n"; +echo " " . local_url() . "index.php1.0\n"; +foreach (scandir("./") as $file) { + if (strpos($file, ".php") !== false && $file != "index.php" && $file != "sitemap.xml.php") + echo " " . local_url() . "$file0.3\n"; +} + +$db = $server->get_db('disclosr-agencies'); +try { + $rows = $db->get_view("app", "byCanonicalName")->rows; + foreach ($rows as $row) { + echo '' . local_url() . 'getAgency.php?id=' . $row->value->_id . "0.6\n"; + } +} catch (SetteeRestClientException $e) { + setteErrorHandler($e); +} +echo ''; +?> + --- a/webserver.php +++ b/webserver.php @@ -1,7 +1,7 @@
";

' . $row['name'] . "

Field NameField Value
namewebserveraccessiblity errors