amon fixes
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,4 +1,7 @@
[submodule "lib/bubbletree"]
path = lib/bubbletree
url = https://github.com/okfn/bubbletree.git
+[submodule "lib/amon-php"]
+ path = lib/amon-php
+ url = https://github.com/martinrusev/amon-php.git
--- a/admin/agency2portfolio.php
+++ /dev/null
@@ -1,42 +1,1 @@
-<?php
-// Returns portfolio scraped live from directory.gov.au
-// or null if can't find a portfolio
-function agency2portfolio ($agency) {
- static $cache = array();
- if (isset($cache[$agency])) { return $cache[$agency]; }
- $c = curl_init('http://www.directory.gov.au/searchres.php');
- curl_setopt($c, CURLOPT_POST, true);
- curl_setopt($c, CURLOPT_HEADER, false);
- curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($c, CURLOPT_REFERER, 'http://www.directory.gov.au/adsearch.php');
- curl_setopt($c, CURLOPT_USERAGENT, 'Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3');
- curl_setopt($c, CURLOPT_POSTFIELDS, array(
- 'advkeywordfield' => '',
- 'advorgunitfield' => $agency,
- 'advrolefield' => '',
- 'advsection' => 'All',
- 'advsurnamefield' => '',
- 'search' => 'Submit Query'
- ));
- $results = curl_exec($c);
-
- if (preg_match('#<span\s+class="standardlinks"><a\s+href="([^"]+)">#smi', $results, $m)) {
- $nextURL = $m[1];
- } else {
- $cache[$agency] = false; return false;
- }
-
- curl_setopt($c, CURLOPT_URL, 'http://www.directory.gov.au' . $nextURL);
- curl_setopt($c, CURLOPT_HTTPGET, true);
- curl_setopt($c, CURLOPT_REFERER, 'http://www.directory.gov.au/searchres.php');
- $results = curl_exec($c);
- if (preg_match('#portfolios:\s+([^<]+)#ims', $results, $m)) {
- $cache[$agency] = $m[1]; return $m[1];
- } else {
- $cache[$agency] = false; return false;
- }
-}
-
-?>
-
--- a/admin/fixoldamend.php
+++ b/admin/fixoldamend.php
@@ -1,7 +1,8 @@
<?php
-
-include_once("../lib/common.inc.php");
-
+if (php_sapi_name() != "cli") {
+
+include_once ("../lib/common.inc.php");
+auth();
$query = 'update contractnotice set "parentCN" = null where "parentCN" = \'0\'';
$result = $conn->prepare($query);
$result->execute();
@@ -37,4 +38,5 @@
echo "parent CN unexpected - $oldCN doesn't look like child of {$record['parentCN']}, rather the ID suggests child of $parentCN <br>\n";
}
}
+}
--- a/admin/import.php
+++ b/admin/import.php
@@ -1,7 +1,8 @@
<?php
-
+if (php_sapi_name() != "cli") {
+
include_once ("../lib/common.inc.php");
-
+auth();
$contractNoticeFields = array(
"importFile",
"agencyName",
@@ -185,6 +186,8 @@
flush();
// run post import data processing
+ //
+$dbConn->exec("update datasets set \"lastUpdated\" = NOW() where title = 'Contract Notices'");
// cn
echo "link amend<br>";
include ("linkAmendments.php");
@@ -194,5 +197,6 @@
//include ("setAgencyStatus.php");
//include ("setAgencyURLABN.php");
}
+}
?>
--- a/admin/importUNSPSC.php
+++ b/admin/importUNSPSC.php
@@ -1,5 +1,8 @@
<?php
- include_once("../lib/common.inc.php");
+if (php_sapi_name() != "cli") {
+
+include_once ("../lib/common.inc.php");
+auth();
// display existing
@@ -49,5 +52,6 @@
echo "<br> $success records successfully created";
flush();
fclose($handle);
+}
?>
--- a/admin/linkAmendments.php
+++ b/admin/linkAmendments.php
@@ -1,5 +1,8 @@
<?php
+if (php_sapi_name() != "cli") {
+
include_once ("../lib/common.inc.php");
+auth();
$query = 'update contractnotice set "parentCN" = null where "parentCN" = \'0\'';
$query = $conn->prepare($query);
$query->execute();
@@ -58,5 +61,6 @@
else print_r($errors);
}
}
+}
?>
--- a/admin/neo4jimporter/pom.xml
+++ b/admin/neo4jimporter/pom.xml
@@ -7,7 +7,7 @@
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j</artifactId>
- <version>1.6.1</version>
+ <version>1.8.RC1</version>
</dependency>
<dependency>
<groupId>postgresql</groupId>
--- a/admin/neo4jimporter/src/main/java/Importer.java
+++ b/admin/neo4jimporter/src/main/java/Importer.java
@@ -1,8 +1,4 @@
-import java.io.ObjectInputStream.GetField;
-import java.math.BigInteger;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
@@ -10,7 +6,6 @@
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.HashMap;
-import java.util.Map;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.GraphDatabaseService;
@@ -18,9 +13,9 @@
import org.neo4j.graphdb.index.BatchInserterIndex;
import org.neo4j.graphdb.index.BatchInserterIndexProvider;
import org.neo4j.helpers.collection.MapUtil;
+import org.neo4j.index.impl.lucene.LuceneBatchInserterIndexProvider;
import org.neo4j.kernel.impl.batchinsert.BatchInserter;
import org.neo4j.kernel.impl.batchinsert.BatchInserterImpl;
-import org.neo4j.index.impl.lucene.*;
public class Importer {
@@ -84,8 +79,7 @@
// Execute the query
ResultSet rs = stmt.executeQuery("SELECT contractnotice.\"agencyName\", "
+ " contractnotice.\"supplierABN\",contractnotice.\"supplierName\",sum(value) as sum "
- + "FROM public.contractnotice where contractnotice.\"agencyName\" != 'Department of Defence'"
- + " AND contractnotice.\"agencyName\" != 'Defence Materiel Organisation' GROUP BY contractnotice.\"agencyName\", "
+ + "FROM public.contractnotice GROUP BY contractnotice.\"agencyName\", "
+ " contractnotice.\"supplierABN\",contractnotice.\"supplierName\"");
String previousAgency = "";
GraphDatabaseService gds = inserter.getGraphDbService();
--- a/admin/partialdata/import.php
+++ b/admin/partialdata/import.php
@@ -1,5 +1,6 @@
<?php
-include_once ("../../lib/common.inc.php");
+if (php_sapi_name() == "cli") {
+ include_once ("../../lib/common.inc.php");
function processFile($fpath, $tablename)
{
global $conn;
@@ -149,5 +150,6 @@
$success+= processFile($path . $fname, "contractnotice");
}
+}
?>
--- a/admin/partialdata/importamendments.php
+++ b/admin/partialdata/importamendments.php
@@ -1,5 +1,6 @@
<?php
-include_once ("../../lib/common.inc.php");
+if (php_sapi_name() == "cli") {
+ include_once ("../../lib/common.inc.php");
/*
update contractnotice set "supplierABN" = a."supplierABN"
from contractnotice as cn inner join (select "supplierABN",
@@ -322,6 +323,6 @@
$success+= processFile($path . $fname, "contractnotice");
}
-
+}
?>
--- a/admin/partialdata/scraper.txt
+++ b/admin/partialdata/scraper.txt
@@ -1,4 +1,5 @@
<?php
+if (php_sapi_name() == "cli") {
date_default_timezone_set('Australia/Melbourne');
$split = false;
function format_bytes($size) {
@@ -70,5 +71,6 @@
} else {
getFile($startDate, $days, "" , "");
}
+}
?>
--- a/admin/setAgencyStatus.php
+++ /dev/null
@@ -1,26 +1,1 @@
-<?php
-include_once("../lib/common.inc.php");
-$active = Array('0EC8D805-C293-3ADD-E51A93667D977314|Administrative Appeals Tribunal','0ECAA17D-9862-5309-101D94A126495C7F|Aged Care Standards and Accreditation Agency Ltd','CE45735B-F9FC-04DA-F299C730D9BE613E|Airservices Australia','0EC8DF71-ED98-8593-2D776EE93D9BA248|Attorney-General\'s Department','0ECA6B4C-DE4F-A04A-DBFB5F39B050B6D7|AusAid','0ECA6F64-F6B8-F406-E490A1479A7A717B|Austrade','D2724D99-F230-A93A-93D62A8DCC187940|Australia Council for the Arts','D26C4D0A-FF6E-612A-C6FEDB2D5E71AE4A|Australian Antarctic Division','C8E854CD-E966-9A35-078FF6702EC1ECED|Australian Broadcasting Corporation','0ECB50CF-028A-FB9D-3E5EF0F9708E4BC7|Australian Bureau of Statistics','0ECA671F-EA1F-8560-1D7C90408694B565|Australian Centre for International Agricultural Research','A861A42C-B32C-85A8-0F719005E27F4E23|Australian Commission for Law Enforcement Integrity','C8F9DAF8-9763-EA22-FA44B6BBC12B0B94|Australian Communications and Media Authority (ACMA)','0ECB2BB9-B19C-4929-87EC63542388D079|Australian Competition and Consumer Commission','0EC8E42E-EB6A-15DA-3F001AFB9C2B8D34|Australian Crime Commission','0EC8E8CE-9BCF-0651-D4362CF53DB8E4DC|Australian Customs and Border Protection Service','CD51BCA4-9DC7-9494-B7D944029D03A1F9|Australian Electoral Commission','D248626B-0BDD-B201-1A1C7C978AD973D2|Australian Fair Pay Commission','0EC8EDF3-E025-2F80-AEB39B350641C81F|Australian Federal Police','0EC8B90B-CBF5-8EC4-2C4E261041A25E16|Australian Fisheries Management Authority','0ECABD48-0388-F8AB-00BD4EB12FA44067|Australian Hearing Services','0EC920D5-CEB1-81FC-5A107A206F4A6BF3|Australian Human Rights Commission','D257B064-D245-51CC-F69A50FF4620915B|Australian Industrial Registry','0EC8F7B1-03ED-C4C3-70FBDF105ADB2FB6|Australian Institute of Criminology','D32EC07E-E22E-5805-1240CF68033C5E15|Australian Institute of Family Studies','0ECA9D77-A0B8-448A-53945B4DFACEE685|Australian Institute of Health and Welfare','D31CF0E2-B1A4-DE74-5D7B7B1DC97EA1DC|Australian Institute of Marine Science','0EC8FC41-C1BE-587A-A19920F43BA616BF|Australian Law Reform Commission','CE5DCAA3-D793-2FEE-B3C263B84AF2676C|Australian Maritime Safety Authority','0ECAED96-0B3B-1D24-56412EC3384996B0|Australian National Audit Office (ANAO)','D281D726-938C-D76D-A346348BEFC509F9|Australian National Maritime Museum','D31E738F-F139-2BC2-EBF2764D7812EA10|Australian Nuclear Science and Technology Organisation (ANSTO)','0ECB3D20-C837-FBE2-B1C32937F87CBB8F|Australian Office of Financial Management','82C5CFA1-0088-7B9E-9A3EF95987B55045|Australian Organ and Tissue Donation and Transplantation Authority','0EC8BF2B-B23F-0A07-0659BB9760EFC94E|Australian Pesticides and Veterinary Medicines Authority','0ECB5AFF-F4DC-06E3-97B3118B978F33BD|Australian Prudential Regulation Authority (APRA)','0ECAF18D-044A-0F5E-AD942B0CD5BB1ACC|Australian Public Service Commission','0ECA88C8-B803-ECE2-00F37CE4797C74BC|Australian Radiation Protection and Nuclear Safety Agency (ARPANSA)','D320882B-ED70-8061-6A5B2F8E64D6F575|Australian Research Council','CD5A2434-9592-2217-49970AF89E5956F8|Australian Reward Investment Alliance (ARIA)','0ECB44F1-BC3B-58E0-96CD33517E10E38B|Australian Securities and Investments Commission','0EC9056F-C13F-ED84-07B410D54C4A1013|Australian Security Intelligence Organisation','D303A5DD-B83B-EC45-588E3680B4E842E4|Australian Sports Anti-Doping Authority (ASADA)','0ECB54CA-EE15-8B26-3C430607E8DB3D4E|Australian Taxation Office','0EC900DB-D645-6C40-DD8BEA03CC15D392|Australian Transaction Reports and Analysis Centre (AUSTRAC)','77068F86-0A15-723A-C2FC1601C44FFCD2|Australian Transport Safety Bureau','0EC9AC89-DEB3-1441-2441891A3772863E|Australian War Memorial','D28B4F07-C2F2-2D4F-1C9229D177088373|Bureau of Meteorology','0ECA90FE-A44B-2EAA-08039CC6C43E728D|Cancer Australia','0ECAB1CF-D480-0C8B-70670DFC4FFDAC56|Centrelink','CE608086-A4DC-45F6-1179DCC148CF91F7|Civil Aviation Safety Authority','D24AFCAC-A2B2-4766-17C9A3EC3AAE4205|Comcare','166B9AD2-ACC9-B56F-4D5B47BD325DEB25|Commonwealth Grants Commission','CD5BDE89-9805-D921-0DAAFDBD5312D0D5|Comsuper','0ECB5EF6-CC7B-7C8E-32798557B78F6D98|Corporations and Markets Advisory Committee','0EC909FF-D902-5FD3-6660ADB353470F95|Crimtrac','0ECAC700-BDE8-458D-6A4E91C8B151A270|CRS Australia','D3275762-D634-4459-D33A26E1BEA05D14|CSIRO','0EC99E6C-0375-2DAF-A8338121FCBD6F7A|Defence Housing Australia','0EC9A29C-E478-AD19-3F0F29F6BE4914BF|Defence Materiel Organisation','0EC892FD-E156-BCB0-ADF5D534E584BC18|Department of Agriculture, Fisheries and Forestry','FB2B0C69-0AAF-1B2B-8186EC38772F8FC1|Department of Broadband, Communications and the Digital Economy','69368049-A8EF-0896-EA9C603C856788D9|Department of Climate Change and Energy Efficiency','0EC98EF9-E020-5DA9-DCB10135BE81BD2B|Department of Defence','D23E9B50-DAE3-6701-D90A2582EE48A4DA|Department of Education, Employment and Workplace Relations','D2FBF89C-D43D-4B3E-527132AC0D41ABBB|Department of Families, Housing, Community Services and Indigenous Affairs','CD39DC0D-D4B3-DFEC-DDDCD52E651F06EA|Department of Finance and Deregulation','0ECA5FC9-A4AD-0A3A-97A8DD3FD9D44CB1|Department of Foreign Affairs and Trade','0ECA80C0-EC9F-B5B9-0D3E8A4537C39808|Department of Health and Ageing','0ECAADBC-B488-A53B-F4F6672EC813849A|Department of Human Services','0ECB6A9B-BA91-CB7D-36C62C5C9191D403|Department of Immigration & Citizenship','047091E8-CEE4-A9FF-BF552F538D279807|Department of Infrastructure and Transport','FADB0367-F000-E0B9-8F79FD109F613AB6|Department of Innovation, Industry, Science and Research','0ECB7ED8-BB19-D108-324A8A05B1966ABF|Department of Parliamentary Services','2F03BD8D-F73D-C35C-729559C6C70C6602|Department of Regional Australia, Regional Development and Local Government','FADFD268-9BF5-97E4-2F247B92F74E82C0|Department of Resources, Energy and Tourism','62304115-AA13-68D1-592110A8021DA68E|Department of Sustainability, Environment, Water, Population and Communities','0ECB8490-9B07-1898-98170CDD42A9DFF7|Department of the House of Representatives','0ECAE795-F858-203D-FD1C9FF933EF4A37|Department of the Prime Minister and Cabinet','0ECB8A6E-B354-715B-C9DEDE4A6DA3C393|Department of the Senate','0ECB25C0-02C3-6BAA-4738951514F489A9|Department of the Treasury','0EC9A85E-BA37-0A60-47A828CD70D831FC|Department of Veterans\' Affairs','D2E272D5-DDD0-178B-F1BE5A9181D3ACAE|Director of National Parks','D2F85A2B-E563-118E-0CB302E02C74397C|Equal Opportunity for Women in the Workplace Agency','0ECA73FA-FAAD-62B5-48581DF753E57CCA|Export Finance and Insurance Corporation (EFIC)','A535F827-E58E-89FB-7F58107CBBD0D79A|Fair Work Australia','0EC91301-C770-6470-480E879920826ABD|Family Court of Australia','0EC91799-A81B-262C-CE6ACAB2622F0566|Federal Court of Australia','0EC91C3C-BB6B-97BF-76BEF63DD0905AAF|Federal Magistrates Court','08355C10-AB46-67AA-421E334D1B45E125|Food Standards Australia New Zealand','CD5E362E-A615-1102-67814B74731025AD|Future Fund Management Agency','D33926C6-BB30-4A30-3A29A5821CFB7AE6|Geoscience Australia','0EC8CA07-AE70-7EB2-5A174487D4BD6236|Grains Research and Development Corporation','D2E5D48D-08A4-5CC9-5378FEA3CD8E649C|Great Barrier Reef Marine Park Authority','0EC925CD-F184-26AF-DE8A84DE3CBA3790|Insolvency and Trustee Service Australia (ITSA)','0ECB302F-AADD-2BB1-392F50E0E94CB076|Inspector-General of Taxation','D3174EBA-F3C0-28E6-7A669C23CF0B3040|IP Australia','0ECAB5DF-B75F-EC35-92EA7821EFF77C0D|Medicare Australia','0ECB74A7-BE11-FC3C-8696F7F93D7612C0|Migration Review Tribunal and Refugee Review Tribunal (MRT-RRT)','F8E42DAB-9570-A008-97294775650CCE6E|Murray-Darling Basin Authority','0A18A552-0B56-257B-70BE077385CE2EDC|National Archives of Australia','0ECA8CEA-08C9-6D55-C57A7A102DFFBC8A|National Blood Authority','D2347B3A-F330-0317-68AB0C4F7240B9A5|National Capital Authority','0ECB3416-FEB1-F5E0-FB4760831CA2F66E|National Competition Council','7DEE189B-0A00-07D1-2D1C778EDAEFEF6D|National Film and Sound Archive','D2E7830B-9692-3124-1C2E1D74AAC8FFFF|National Gallery of Australia','0ECA957E-E479-2758-528FA4E576A27A5D|National Health and Medical Research Council','D2EA1497-0BB3-AFBD-76559FA87101FF06|National Library of Australia','D2EC2CF2-FEEE-8FB2-46193EDF8EA2F503|National Museum of Australia','0EC92A40-B2DA-D4FF-16895BCD00F9E20F|National Native Title Tribunal','D33DB6EB-FEE1-F686-7385E65E48749DD2|National Offshore Petroleum Safety Authority','D2EF702D-EF9B-B363-BDA3A4950C5ED57F|National Water Commission','0ECB00AF-04FE-735D-82D0B84D1A19F95A|Office of National Assessments','0EC93653-9FCE-12DD-DAAC73E23DCAE8CA|Office of Parliamentary Counsel','0ECB48E3-9198-6F2C-9737A69F9D15177A|Office of the Auditing and Assurance Standards Board','0ECB4CD6-A6D6-4334-D550948337CE9F56|Office of the Australian Accounting Standards Board','D25ACD14-ABA3-F69F-E1ADA4503ED97C82|Office of the Australian Building and Construction Commissioner (ABCC)','D331236A-BDE0-5CD7-24EF78F052CB2B50|Office of the Australian Information Commissioner','0ECAF6CA-ACBE-E3E7-6DCBF175E012F817|Office of the Commonwealth Ombudsman','0EC93B4D-DC77-6FA0-7E15530A7D7344CB|Office of the Director of Public Prosecutions','39DCC587-FF78-8759-E91BF5B9EC3D1904|Office of the Fair Work Ombudsman','0ECB049D-B7DC-1697-586AB4455B0DF251|Office of the Inspector-General of Intelligence and Security','0ECB0887-D30E-E490-3F16BCB7EF406D8E|Office of the Official Secretary to the Governor-General','E0EAE43E-B918-12DC-9F4E0E03811CACAD|Office of the Renewable Energy Regulator','D726F8EA-D648-B4DF-E25FF1EEE68C9700|Old Parliament House','87A575EB-E6CE-7E8B-1CB3D9B6580FAE5B|Private Health Insurance Ombudsman','0ECB380B-FC93-8003-9A1F04BD55A10F62|Productivity Commission','0ECA9978-D7C8-3F44-2D21C2827407BEF5|Professional Services Review','0ECB64C5-E6E4-3D23-B4409B18EB609B25|Reserve Bank of Australia','0ECB4106-02BC-64C6-1DF8032E549D88E4|Royal Australian Mint','4A350604-FD8E-C7BB-D6C6A14AEE6D8873|Safe Work Australia','D25D96A7-B9B7-21D2-57AA60EAC138107B|Seacare','D2FF2257-0E69-4B7C-FCF6AB672ECC341C|Social Security Appeals Tribunal','D2F16C5B-BCFB-E14D-9B62DEEDF992798B|Sydney Harbour Federation Trust','0ECA84C7-D6C3-3299-9F898B9C201D771B|Therapeutic Goods Administration','D340FD88-0AFE-B091-1F43D9EDBCE164D4|Tourism Australia','EB74EB82-AA02-F70B-DC648859ABFC28DA|Wheat Exports Australia');
-
-$suspended = Array('8B346DD0-D9A5-585C-1B3174A9B6292AD1|Aboriginal and Torres Strait Islander Services','0ECB16D3-9684-471C-3FFBCCCAB63728B4|Airservices Australia','0EC975AC-F94D-B8B5-FDF5CF053D78F7EA|Australia Council for the Arts','0ECA7934-9725-0D31-1BD08C6ADFD16040|Australia-Japan Foundation','0ECA043A-C02D-1838-E994DF3EC8B0A857|Australian Antarctic Division','0EC96B6B-9A30-F9E4-892DA6860B3985B2|Australian Broadcasting Corporation','0EC9521F-C38A-2FB1-54CEF555CE76807A|Australian Communications and Media Authority (ACMA)','0ECA48E7-B536-E530-CA1F4F8A4056517F|Australian Electoral Commission','0EC9D8CD-D6F8-A695-854B714011D9B2E8|Australian Fair Pay Commission','0EC9703D-9C88-C7FF-E15390103B51CE22|Australian Film Commission','D27DB409-E5C9-15AD-635A70CEF5A8E05C|Australian Film Commission','0EC96705-AED6-462D-E1579FE99DD57511|Australian Film Television and Radio School','D27F93E1-9898-8ED5-5DF9628A1CE78BBF|Australian Film Television and Radio School','0EC9DCED-ED0B-FF25-20323EF5E93EC88E|Australian Industrial Registry','0EC9BF9B-C83C-4EC5-DDE3C845EBF3CCC0|Australian Institute of Aboriginal and Torres Strait Islander Studies (AIATSIS)','D246AE91-0DD2-193E-84F9A3E13664E58E|Australian Institute of Aboriginal and Torres Strait Islander Studies (AIATSIS)','0ECA2851-AD46-9261-B3A77D28C5CC1351|Australian Institute of Family Studies','0EC9BB58-BBA8-3F3D-AD04CBFE4FABC85D|Australian Institute of Marine Science','0ECB1ACC-FAAC-3895-9EC179BC71C3FC54|Australian Maritime Safety Authority','0EC9611A-C5D2-43B5-D4039EED991272E3|Australian National Maritime Museum','0EC9C3D2-CE1C-8C81-085ADA97E2172420|Australian Nuclear Science and Technology Organisation (ANSTO)','0EC9B71F-EDBC-5E42-E44D5595BDF7B3F4|Australian Research Council','0ECA59B7-D4C6-360D-4829CB8A5E15B664|Australian Reward Investment Alliance (ARIA)','0EC95958-08E9-4A16-A79AC1CB828B9535|Australian Sports Anti-Doping Authority (ASADA)','0EC8C3DE-B6EB-88B8-0FD9460AC543DD0C|Biosecurity Australia','0ECA1921-EA6F-1BE2-3E85DBF7BBCDE734|Bureau of Meteorology','0ECAC142-B8B8-CCE0-A4B00E4B67ED0DCB|Child Support Agency','0ECB1FED-D2C6-6214-2CEB31CE9B107ED1|Civil Aviation Safety Authority','0EC9D499-F2DE-F8D9-0239ACA17C2ABFBB|Comcare','0ECA55A3-A0B2-0D36-D383E6A113AD5749|Commonwealth Grants Commission','0ECA4CF9-9312-2F3D-637C98CF2FEEB5A4|Comsuper','0EC9C7FE-F7AC-09BD-3C498B63A9DDDDA4|CSIRO','0EC8B354-F006-A824-A43120384BE56A98|Dairy Adjustment Authority','D3328CCE-DEA4-5516-AA23F77F557454FC|Department of Climate Change','0EC94B72-AD24-B695-3C5C388FBC0C23AA|Department of Communications, Information Technology and the Arts','0EC9B2BB-D5C8-8061-8CC999143D0D291C|Department of Education, Science and Training','0EC9CE2C-A998-C45A-90697489AC42B83B|Department of Employment and Workplace Relations','0ECA21F5-F5FE-7F08-52CD6FFB54E788FE|Department of Families, Community Services & Indigenous Affairs','0ECA32E7-E860-DFD4-0FBDCE1CC8E0FC84|Department of Finance and Administration','8B3539BA-EC97-7510-AF049F389DC0497A|Department of Immigration and Multicultural Affairs','8B361B4A-EAAE-D747-134D9FA97497C70F|Department of Immigration and Multicultural and Indigenous Affairs (DIMIA)','0ECACF65-D93B-D892-1862B65F15C92A6A|Department of Industry, Tourism and Resources','CE419954-CB32-1154-9E61EDB5805C222D|Department of Infrastructure, Transport, Regional Development and Local Government','0EC9F8FB-F224-1E70-F59834C1D5CE12D5|Department of the Environment and Water Resources','D2DFCF42-A87B-181C-2F48D7EEE8849AFD|Department of the Environment, Water, Heritage and the Arts','0ECB0EE4-9D78-A341-363B0D13161C5916|Department of Transport and Regional Services','0ECA0863-0F4E-5FA8-B7C522D16CCAC9BB|Director of National Parks','0EC90E81-DAF0-F67A-7FDC1AE664FF0BEE|Emergency Management Australia','0EC9E429-9FFF-7E05-41ADCC29FE17811C|Equal Opportunity for Women in the Workplace Agency','576FCE5F-E258-4A5C-92C11639C73A0442|Export Wheat Commission','0ECA518C-EF07-86F5-ADCAA84198009116|Future Fund Management Agency','0ECAE1C5-E249-022B-DCC7D944F398E1FD|Geoscience Australia','0ECA0CF1-AEF7-5C78-23878C6A5BF93C36|Great Barrier Reef Marine Park Authority','0ECADD52-F400-97E8-25F98B9FF96FFD08|IP Australia','0EC8D025-D94D-FF1D-97130B40320BAD9A|Land and Water Australia','0EC98433-EB5E-BEB1-1EEC7CADF83918A0|National Archives of Australia','CD603E31-CD1F-AAE5-F233F954A031553E|National Archives of Australia','0ECB12DC-BF41-FF5B-CB306CF040EC3218|National Capital Authority','0EC97A03-EA2E-5AE3-EC3C2C23487ECECA|National Gallery of Australia','0EC98880-F5F1-6560-605A0250EA58D139|National Library of Australia','0EC97FE7-E894-67B2-A8CE40DEF409AC23|National Museum of Australia','0ECAD957-B214-674F-3C94DEEF52BA3F46|National Offshore Petroleum Safety Authority','0ECAFC83-0B5D-045A-EE43EA12B9953CC0|National Water Commission','48499BD1-AFE0-1093-EF06293D78B3FD65|National Water Commission','0EC92FD3-F7B3-B0E0-9A8D8D2B611BB615|Office of Film and Literature Classification','0EC9E84A-A9F7-4EEF-5DDD2FC87C1F1B55|Office of the Australian Building and Construction Commissioner (ABCC)','0EC940DD-9A7E-1ECD-B44DDA0B72B2407F|Office of the Privacy Commissioner','0ECA10FE-AC03-80D8-C8DF1069C707351E|Office of the Renewable Energy Regulator','D3362537-FE00-1892-B408CABF5E0257A7|Office of the Renewable Energy Regulator','0EC9F2EC-C778-46D0-F15FDB1A8F1552F9|Office of Workplace Services','FB2A3EA0-D6FC-DB1D-19B0B72180A8A19B|Questacon','D329A60A-E914-FB37-FBA853A4CE8558EB|Questacon','0ECB788C-CC5C-2969-4B94EFB3913B0E65|Refugee Review Tribunal','A2A6EBF4-A8F5-E60B-5C25ACD3EA0679D8|Screen Australia','0EC9EC88-9B12-CB63-56A58BE69BDF3B6F|Seacare','0ECA2CE0-CBA9-9E13-AA0FB347CF4B5FD1|Social Security Appeals Tribunal','0ECA1510-B4A2-BBA4-D6FDE37DFAAC7C12|Sydney Harbour Federation Trust','0ECAA587-C851-4038-8CDC9CCE9A28F6E6|The National Institute of Clinical Studies Ltd','0ECAD567-E1E7-F5FC-72BA34FDF72F0FC0|Tourism Australia','7A592345-0410-DF1C-4B349EA35F314D3F|Workplace Authority','003B8DE1-D09A-D72F-28CEBC19DB84E866|Workplace Ombudsman','D260B27E-9744-EF72-C065072F24B4A62A|Workplace Ombudsman');
-
-foreach ($active as $agency ) {
- $agencyParts = explode("|",$agency);
- $agency = $agencyParts[1];
- $agencyInsert = "INSERT INTO agency (agencyName) VALUES ('$agency')";
- $result = mysql_query($agencyInsert);
- $result = mysql_query("UPDATE agency SET status = 'active' where agencyName = '".mysql_real_escape_string($agency)."';") ;
- if ($result) echo $agency. " set to active in ". mysql_affected_rows() . " divisions/branches <br>\n";
- else echo "error".mysql_error();
-}
-foreach ($suspended as $agency) {
- $agencyParts = explode("|",$agency);
- $agency = $agencyParts[1];
- $agencyInsert = "INSERT INTO agency (agencyName) VALUES ('$agency')";
- $result = mysql_query($agencyInsert);
- $result = mysql_query("UPDATE agency SET status = 'suspended' where agencyName = '".mysql_real_escape_string($agency)."';") ;
- if ($result) echo $agency. " set to suspended in ". mysql_affected_rows() . " divisions/branches <br>\n";
- else echo "error".mysql_error();
-}
-?>
--- a/admin/setAgencyURLABN.php
+++ /dev/null
@@ -1,38 +1,1 @@
-<?php
-include_once ("../lib/common.inc.php");
-// to reset: update agency set abn = 0, website = ''
-$ch = curl_init();
-curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
-curl_setopt($ch, CURLOPT_REFERER, "http://contractdashboard.lambdacomplex.org");
-$querySel = sprintf("SELECT * FROM `agency` where ABN = 0 OR website = ''");
-$resultSel = mysql_query($querySel);
-while ($row = mysql_fetch_array($resultSel, MYSQL_ASSOC)) {
- $agency = $row['agencyName'];
- //foreach $agency
- $url = "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&key=ABQIAAAA95XYXN0cki3Yj_Sb71CFvBSgIPe2k9-DRgEVQvbvQDV8xmTiXRTBpHxrD7bSh5rl2lswY56769CyQQ&rsz=small&filter=1&gl=au&q=" . urlencode($agency) . "%20ABN%20site:.gov.au%20-site:www.abr.business.gov.au";
- curl_setopt($ch, CURLOPT_URL, $url);
- $body = curl_exec($ch);
- $json = json_decode($body);
- echo "<pre>";
- $result = $json->responseData->results[0];
- print_r($result);
- echo "</pre>";
- echo "<b> Agency:</b> $agency";
- echo "<b> URL:</b> http://" . $result->visibleUrl;
- preg_match('/\d{2} \d{3} \d{3} \d{3}/i', $result->content, $abn);
- $abn[0] = str_replace(" ","",$abn[0]);
- echo "<b> ABN:</b> {$abn[0]}";
- if ($abn[0] > 1000) {
- $result = mysql_query("UPDATE agency SET website = 'http://" . $result->visibleUrl . "', abn = '{$abn[0]}' WHERE agencyName = '$agency';");
- if ($result) echo $agency . " set in " . mysql_affected_rows() . " <br>\n";
- } else {
-
- echo "invalid ABN";
- $result = mysql_query("UPDATE agency SET website = 'http://" . $result->visibleUrl . "' WHERE agencyName = '$agency';");
- if ($result) echo $agency . " set in " . mysql_affected_rows() . " <br>\n";
- }
- // fi
-
-}
-curl_close($ch);
-?>
+
--- /dev/null
+++ b/bootstrap-responsive.css
@@ -1,1 +1,1041 @@
-
+/*!
+ * Bootstrap Responsive v2.1.0
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+.clearfix {
+ *zoom: 1;
+}
+
+.clearfix:before,
+.clearfix:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.clearfix:after {
+ clear: both;
+}
+
+.hide-text {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+}
+
+.input-block-level {
+ display: block;
+ width: 100%;
+ min-height: 30px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.hidden {
+ display: none;
+ visibility: hidden;
+}
+
+.visible-phone {
+ display: none !important;
+}
+
+.visible-tablet {
+ display: none !important;
+}
+
+.hidden-desktop {
+ display: none !important;
+}
+
+.visible-desktop {
+ display: inherit !important;
+}
+
+@media (min-width: 768px) and (max-width: 979px) {
+ .hidden-desktop {
+ display: inherit !important;
+ }
+ .visible-desktop {
+ display: none !important ;
+ }
+ .visible-tablet {
+ display: inherit !important;
+ }
+ .hidden-tablet {
+ display: none !important;
+ }
+}
+
+@media (max-width: 767px) {
+ .hidden-desktop {
+ display: inherit !important;
+ }
+ .visible-desktop {
+ display: none !important;
+ }
+ .visible-phone {
+ display: inherit !important;
+ }
+ .hidden-phone {
+ display: none !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .row {
+ margin-left: -30px;
+ *zoom: 1;
+ }
+ .row:before,
+ .row:after {
+ display: table;
+ line-height: 0;
+ content: "";
+ }
+ .row:after {
+ clear: both;
+ }
+ [class*="span"] {
+ float: left;
+ margin-left: 30px;
+ }
+ .container,
+ .navbar-static-top .container,
+ .navbar-fixed-top .container,
+ .navbar-fixed-bottom .container {
+ width: 1170px;
+ }
+ .span12 {
+ width: 1170px;
+ }
+ .span11 {
+ width: 1070px;
+ }
+ .span10 {
+ width: 970px;
+ }
+ .span9 {
+ width: 870px;
+ }
+ .span8 {
+ width: 770px;
+ }
+ .span7 {
+ width: 670px;
+ }
+ .span6 {
+ width: 570px;
+ }
+ .span5 {
+ width: 470px;
+ }
+ .span4 {
+ width: 370px;
+ }
+ .span3 {
+ width: 270px;
+ }
+ .span2 {
+ width: 170px;
+ }
+ .span1 {
+ width: 70px;
+ }
+ .offset12 {
+ margin-left: 1230px;
+ }
+ .offset11 {
+ margin-left: 1130px;
+ }
+ .offset10 {
+ margin-left: 1030px;
+ }
+ .offset9 {
+ margin-left: 930px;
+ }
+ .offset8 {
+ margin-left: 830px;
+ }
+ .offset7 {
+ margin-left: 730px;
+ }
+ .offset6 {
+ margin-left: 630px;
+ }
+ .offset5 {
+ margin-left: 530px;
+ }
+ .offset4 {
+ margin-left: 430px;
+ }
+ .offset3 {
+ margin-left: 330px;
+ }
+ .offset2 {
+ margin-left: 230px;
+ }
+ .offset1 {
+ margin-left: 130px;
+ }
+ .row-fluid {
+ width: 100%;
+ *zoom: 1;
+ }
+ .row-fluid:before,
+ .row-fluid:after {
+ display: table;
+ line-height: 0;
+ content: "";
+ }
+ .row-fluid:after {
+ clear: both;
+ }
+ .row-fluid [class*="span"] {
+ display: block;
+ float: left;
+ width: 100%;
+ min-height: 30px;
+ margin-left: 2.564102564102564%;
+ *margin-left: 2.5109110747408616%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+ .row-fluid [class*="span"]:first-child {
+ margin-left: 0;
+ }
+ .row-fluid .span12 {
+ width: 100%;
+ *width: 99.94680851063829%;
+ }
+ .row-fluid .span11 {
+ width: 91.45299145299145%;
+ *width: 91.39979996362975%;
+ }
+ .row-fluid .span10 {
+ width: 82.90598290598291%;
+ *width: 82.8527914166212%;
+ }
+ .row-fluid .span9 {
+ width: 74.35897435897436%;
+ *width: 74.30578286961266%;
+ }
+ .row-fluid .span8 {
+ width: 65.81196581196582%;
+ *width: 65.75877432260411%;
+ }
+ .row-fluid .span7 {
+ width: 57.26495726495726%;
+ *width: 57.21176577559556%;
+ }
+ .row-fluid .span6 {
+ width: 48.717948717948715%;
+ *width: 48.664757228587014%;
+ }
+ .row-fluid .span5 {
+ width: 40.17094017094017%;
+ *width: 40.11774868157847%;
+ }
+ .row-fluid .span4 {
+ width: 31.623931623931625%;
+ *width: 31.570740134569924%;
+ }
+ .row-fluid .span3 {
+ width: 23.076923076923077%;
+ *width: 23.023731587561375%;
+ }
+ .row-fluid .span2 {
+ width: 14.52991452991453%;
+ *width: 14.476723040552828%;
+ }
+ .row-fluid .span1 {
+ width: 5.982905982905983%;
+ *width: 5.929714493544281%;
+ }
+ .row-fluid .offset12 {
+ margin-left: 105.12820512820512%;
+ *margin-left: 105.02182214948171%;
+ }
+ .row-fluid .offset12:first-child {
+ margin-left: 102.56410256410257%;
+ *margin-left: 102.45771958537915%;
+ }
+ .row-fluid .offset11 {
+ margin-left: 96.58119658119658%;
+ *margin-left: 96.47481360247316%;
+ }
+ .row-fluid .offset11:first-child {
+ margin-left: 94.01709401709402%;
+ *margin-left: 93.91071103837061%;
+ }
+ .row-fluid .offset10 {
+ margin-left: 88.03418803418803%;
+ *margin-left: 87.92780505546462%;
+ }
+ .row-fluid .offset10:first-child {
+ margin-left: 85.47008547008548%;
+ *margin-left: 85.36370249136206%;
+ }
+ .row-fluid .offset9 {
+ margin-left: 79.48717948717949%;
+ *margin-left: 79.38079650845607%;
+ }
+ .row-fluid .offset9:first-child {
+ margin-left: 76.92307692307693%;
+ *margin-left: 76.81669394435352%;
+ }
+ .row-fluid .offset8 {
+ margin-left: 70.94017094017094%;
+ *margin-left: 70.83378796144753%;
+ }
+ .row-fluid .offset8:first-child {
+ margin-left: 68.37606837606839%;
+ *margin-left: 68.26968539734497%;
+ }
+ .row-fluid .offset7 {
+ margin-left: 62.393162393162385%;
+ *margin-left: 62.28677941443899%;
+ }
+ .row-fluid .offset7:first-child {
+ margin-left: 59.82905982905982%;
+ *margin-left: 59.72267685033642%;
+ }
+ .row-fluid .offset6 {
+ margin-left: 53.84615384615384%;
+ *margin-left: 53.739770867430444%;
+ }
+ .row-fluid .offset6:first-child {
+ margin-left: 51.28205128205128%;
+ *margin-left: 51.175668303327875%;
+ }
+ .row-fluid .offset5 {
+ margin-left: 45.299145299145295%;
+ *margin-left: 45.1927623204219%;
+ }
+ .row-fluid .offset5:first-child {
+ margin-left: 42.73504273504273%;
+ *margin-left: 42.62865975631933%;
+ }
+ .row-fluid .offset4 {
+ margin-left: 36.75213675213675%;
+ *margin-left: 36.645753773413354%;
+ }
+ .row-fluid .offset4:first-child {
+ margin-left: 34.18803418803419%;
+ *margin-left: 34.081651209310785%;
+ }
+ .row-fluid .offset3 {
+ margin-left: 28.205128205128204%;
+ *margin-left: 28.0987452264048%;
+ }
+ .row-fluid .offset3:first-child {
+ margin-left: 25.641025641025642%;
+ *margin-left: 25.53464266230224%;
+ }
+ .row-fluid .offset2 {
+ margin-left: 19.65811965811966%;
+ *margin-left: 19.551736679396257%;
+ }
+ .row-fluid .offset2:first-child {
+ margin-left: 17.094017094017094%;
+ *margin-left: 16.98763411529369%;
+ }
+ .row-fluid .offset1 {
+ margin-left: 11.11111111111111%;
+ *margin-left: 11.004728132387708%;
+ }
+ .row-fluid .offset1:first-child {
+ margin-left: 8.547008547008547%;
+ *margin-left: 8.440625568285142%;
+ }
+ input,
+ textarea,
+ .uneditable-input {
+ margin-left: 0;
+ }
+ .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 30px;
+ }
+ input.span12,
+ textarea.span12,
+ .uneditable-input.span12 {
+ width: 1156px;
+ }
+ input.span11,
+ textarea.span11,
+ .uneditable-input.span11 {
+ width: 1056px;
+ }
+ input.span10,
+ textarea.span10,
+ .uneditable-input.span10 {
+ width: 956px;
+ }
+ input.span9,
+ textarea.span9,
+ .uneditable-input.span9 {
+ width: 856px;
+ }
+ input.span8,
+ textarea.span8,
+ .uneditable-input.span8 {
+ width: 756px;
+ }
+ input.span7,
+ textarea.span7,
+ .uneditable-input.span7 {
+ width: 656px;
+ }
+ input.span6,
+ textarea.span6,
+ .uneditable-input.span6 {
+ width: 556px;
+ }
+ input.span5,
+ textarea.span5,
+ .uneditable-input.span5 {
+ width: 456px;
+ }
+ input.span4,
+ textarea.span4,
+ .uneditable-input.span4 {
+ width: 356px;
+ }
+ input.span3,
+ textarea.span3,
+ .uneditable-input.span3 {
+ width: 256px;
+ }
+ input.span2,
+ textarea.span2,
+ .uneditable-input.span2 {
+ width: 156px;
+ }
+ input.span1,
+ textarea.span1,
+ .uneditable-input.span1 {
+ width: 56px;
+ }
+ .thumbnails {
+ margin-left: -30px;
+ }
+ .thumbnails > li {
+ margin-left: 30px;
+ }
+ .row-fluid .thumbnails {
+ margin-left: 0;
+ }
+}
+
+@media (min-width: 768px) and (max-width: 979px) {
+ .row {
+ margin-left: -20px;
+ *zoom: 1;
+ }
+ .row:before,
+ .row:after {
+ display: table;
+ line-height: 0;
+ content: "";
+ }
+ .row:after {
+ clear: both;
+ }
+ [class*="span"] {
+ float: left;
+ margin-left: 20px;
+ }
+ .container,
+ .navbar-static-top .container,
+ .navbar-fixed-top .container,
+ .navbar-fixed-bottom .container {
+ width: 724px;
+ }
+ .span12 {
+ width: 724px;
+ }
+ .span11 {
+ width: 662px;
+ }
+ .span10 {
+ width: 600px;
+ }
+ .span9 {
+ width: 538px;
+ }
+ .span8 {
+ width: 476px;
+ }
+ .span7 {
+ width: 414px;
+ }
+ .span6 {
+ width: 352px;
+ }
+ .span5 {
+ width: 290px;
+ }
+ .span4 {
+ width: 228px;
+ }
+ .span3 {
+ width: 166px;
+ }
+ .span2 {
+ width: 104px;
+ }
+ .span1 {
+ width: 42px;
+ }
+ .offset12 {
+ margin-left: 764px;
+ }
+ .offset11 {
+ margin-left: 702px;
+ }
+ .offset10 {
+ margin-left: 640px;
+ }
+ .offset9 {
+ margin-left: 578px;
+ }
+ .offset8 {
+ margin-left: 516px;
+ }
+ .offset7 {
+ margin-left: 454px;
+ }
+ .offset6 {
+ margin-left: 392px;
+ }
+ .offset5 {
+ margin-left: 330px;
+ }
+ .offset4 {
+ margin-left: 268px;
+ }
+ .offset3 {
+ margin-left: 206px;
+ }
+ .offset2 {
+ margin-left: 144px;
+ }
+ .offset1 {
+ margin-left: 82px;
+ }
+ .row-fluid {
+ width: 100%;
+ *zoom: 1;
+ }
+ .row-fluid:before,
+ .row-fluid:after {
+ display: table;
+ line-height: 0;
+ content: "";
+ }
+ .row-fluid:after {
+ clear: both;
+ }
+ .row-fluid [class*="span"] {
+ display: block;
+ float: left;
+ width: 100%;
+ min-height: 30px;
+ margin-left: 2.7624309392265194%;
+ *margin-left: 2.709239449864817%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+ .row-fluid [class*="span"]:first-child {
+ margin-left: 0;
+ }
+ .row-fluid .span12 {
+ width: 100%;
+ *width: 99.94680851063829%;
+ }
+ .row-fluid .span11 {
+ width: 91.43646408839778%;
+ *width: 91.38327259903608%;
+ }
+ .row-fluid .span10 {
+ width: 82.87292817679558%;
+ *width: 82.81973668743387%;
+ }
+ .row-fluid .span9 {
+ width: 74.30939226519337%;
+ *width: 74.25620077583166%;
+ }
+ .row-fluid .span8 {
+ width: 65.74585635359117%;
+ *width: 65.69266486422946%;
+ }
+ .row-fluid .span7 {
+ width: 57.18232044198895%;
+ *width: 57.12912895262725%;
+ }
+ .row-fluid .span6 {
+ width: 48.61878453038674%;
+ *width: 48.56559304102504%;
+ }
+ .row-fluid .span5 {
+ width: 40.05524861878453%;
+ *width: 40.00205712942283%;
+ }
+ .row-fluid .span4 {
+ width: 31.491712707182323%;
+ *width: 31.43852121782062%;
+ }
+ .row-fluid .span3 {
+ width: 22.92817679558011%;
+ *width: 22.87498530621841%;
+ }
+ .row-fluid .span2 {
+ width: 14.3646408839779%;
+ *width: 14.311449394616199%;
+ }
+ .row-fluid .span1 {
+ width: 5.801104972375691%;
+ *width: 5.747913483013988%;
+ }
+ .row-fluid .offset12 {
+ margin-left: 105.52486187845304%;
+ *margin-left: 105.41847889972962%;
+ }
+ .row-fluid .offset12:first-child {
+ margin-left: 102.76243093922652%;
+ *margin-left: 102.6560479605031%;
+ }
+ .row-fluid .offset11 {
+ margin-left: 96.96132596685082%;
+ *margin-left: 96.8549429881274%;
+ }
+ .row-fluid .offset11:first-child {
+ margin-left: 94.1988950276243%;
+ *margin-left: 94.09251204890089%;
+ }
+ .row-fluid .offset10 {
+ margin-left: 88.39779005524862%;
+ *margin-left: 88.2914070765252%;
+ }
+ .row-fluid .offset10:first-child {
+ margin-left: 85.6353591160221%;
+ *margin-left: 85.52897613729868%;
+ }
+ .row-fluid .offset9 {
+ margin-left: 79.8342541436464%;
+ *margin-left: 79.72787116492299%;
+ }
+ .row-fluid .offset9:first-child {
+ margin-left: 77.07182320441989%;
+ *margin-left: 76.96544022569647%;
+ }
+ .row-fluid .offset8 {
+ margin-left: 71.2707182320442%;
+ *margin-left: 71.16433525332079%;
+ }
+ .row-fluid .offset8:first-child {
+ margin-left: 68.50828729281768%;
+ *margin-left: 68.40190431409427%;
+ }
+ .row-fluid .offset7 {
+ margin-left: 62.70718232044199%;
+ *margin-left: 62.600799341718584%;
+ }
+ .row-fluid .offset7:first-child {
+ margin-left: 59.94475138121547%;
+ *margin-left: 59.838368402492065%;
+ }
+ .row-fluid .offset6 {
+ margin-left: 54.14364640883978%;
+ *margin-left: 54.037263430116376%;
+ }
+ .row-fluid .offset6:first-child {
+ margin-left: 51.38121546961326%;
+ *margin-left: 51.27483249088986%;
+ }
+ .row-fluid .offset5 {
+ margin-left: 45.58011049723757%;
+ *margin-left: 45.47372751851417%;
+ }
+ .row-fluid .offset5:first-child {
+ margin-left: 42.81767955801105%;
+ *margin-left: 42.71129657928765%;
+ }
+ .row-fluid .offset4 {
+ margin-left: 37.01657458563536%;
+ *margin-left: 36.91019160691196%;
+ }
+ .row-fluid .offset4:first-child {
+ margin-left: 34.25414364640884%;
+ *margin-left: 34.14776066768544%;
+ }
+ .row-fluid .offset3 {
+ margin-left: 28.45303867403315%;
+ *margin-left: 28.346655695309746%;
+ }
+ .row-fluid .offset3:first-child {
+ margin-left: 25.69060773480663%;
+ *margin-left: 25.584224756083227%;
+ }
+ .row-fluid .offset2 {
+ margin-left: 19.88950276243094%;
+ *margin-left: 19.783119783707537%;
+ }
+ .row-fluid .offset2:first-child {
+ margin-left: 17.12707182320442%;
+ *margin-left: 17.02068884448102%;
+ }
+ .row-fluid .offset1 {
+ margin-left: 11.32596685082873%;
+ *margin-left: 11.219583872105325%;
+ }
+ .row-fluid .offset1:first-child {
+ margin-left: 8.56353591160221%;
+ *margin-left: 8.457152932878806%;
+ }
+ input,
+ textarea,
+ .uneditable-input {
+ margin-left: 0;
+ }
+ .controls-row [class*="span"] + [class*="span"] {
+ margin-left: 20px;
+ }
+ input.span12,
+ textarea.span12,
+ .uneditable-input.span12 {
+ width: 710px;
+ }
+ input.span11,
+ textarea.span11,
+ .uneditable-input.span11 {
+ width: 648px;
+ }
+ input.span10,
+ textarea.span10,
+ .uneditable-input.span10 {
+ width: 586px;
+ }
+ input.span9,
+ textarea.span9,
+ .uneditable-input.span9 {
+ width: 524px;
+ }
+ input.span8,
+ textarea.span8,
+ .uneditable-input.span8 {
+ width: 462px;
+ }
+ input.span7,
+ textarea.span7,
+ .uneditable-input.span7 {
+ width: 400px;
+ }
+ input.span6,
+ textarea.span6,
+ .uneditable-input.span6 {
+ width: 338px;
+ }
+ input.span5,
+ textarea.span5,
+ .uneditable-input.span5 {
+ width: 276px;
+ }
+ input.span4,
+ textarea.span4,
+ .uneditable-input.span4 {
+ width: 214px;
+ }
+ input.span3,
+ textarea.span3,
+ .uneditable-input.span3 {
+ width: 152px;
+ }
+ input.span2,
+ textarea.span2,
+ .uneditable-input.span2 {
+ width: 90px;
+ }
+ input.span1,
+ textarea.span1,
+ .uneditable-input.span1 {
+ width: 28px;
+ }
+}
+
+@media (max-width: 767px) {
+ body {
+ padding-right: 20px;
+ padding-left: 20px;
+ }
+ .navbar-fixed-top,
+ .navbar-fixed-bottom {
+ margin-right: -20px;
+ margin-left: -20px;
+ }
+ .container-fluid {
+ padding: 0;
+ }
+ .dl-horizontal dt {
+ float: none;
+ width: auto;
+ clear: none;
+ text-align: left;
+ }
+ .dl-horizontal dd {
+ margin-left: 0;
+ }
+ .container {
+ width: auto;
+ }
+ .row-fluid {
+ width: 100%;
+ }
+ .row,
+ .thumbnails {
+ margin-left: 0;
+ }
+ .thumbnails > li {
+ float: none;
+ margin-left: 0;
+ }
+ [class*="span"],
+ .row-fluid [class*="span"] {
+ display: block;
+ float: none;
+ width: auto;
+ margin-left: 0;
+ }
+ .span12,
+ .row-fluid .span12 {
+ width: 100%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+ .input-large,
+ .input-xlarge,
+ .input-xxlarge,
+ input[class*="span"],
+ select[class*="span"],
+ textarea[class*="span"],
+ .uneditable-input {
+ display: block;
+ width: 100%;
+ min-height: 30px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+ .input-prepend input,
+ .input-append input,
+ .input-prepend input[class*="span"],
+ .input-append input[class*="span"] {
+ display: inline-block;
+ width: auto;
+ }
+ .modal {
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ left: 20px;
+ width: auto;
+ margin: 0;
+ }
+ .modal.fade.in {
+ top: auto;
+ }
+}
+
+@media (max-width: 480px) {
+ .nav-collapse {
+ -webkit-transform: translate3d(0, 0, 0);
+ }
+ .page-header h1 small {
+ display: block;
+ line-height: 20px;
+ }
+ input[type="checkbox"],
+ input[type="radio"] {
+ border: 1px solid #ccc;
+ }
+ .form-horizontal .control-group > label {
+ float: none;
+ width: auto;
+ padding-top: 0;
+ text-align: left;
+ }
+ .form-horizontal .controls {
+ margin-left: 0;
+ }
+ .form-horizontal .control-list {
+ padding-top: 0;
+ }
+ .form-horizontal .form-actions {
+ padding-right: 10px;
+ padding-left: 10px;
+ }
+ .modal {
+ top: 10px;
+ right: 10px;
+ left: 10px;
+ }
+ .modal-header .close {
+ padding: 10px;
+ margin: -10px;
+ }
+ .carousel-caption {
+ position: static;
+ }
+}
+
+@media (max-width: 979px) {
+ body {
+ padding-top: 0;
+ }
+ .navbar-fixed-top,
+ .navbar-fixed-bottom {
+ position: static;
+ }
+ .navbar-fixed-top {
+ margin-bottom: 20px;
+ }
+ .navbar-fixed-bottom {
+ margin-top: 20px;
+ }
+ .navbar-fixed-top .navbar-inner,
+ .navbar-fixed-bottom .navbar-inner {
+ padding: 5px;
+ }
+ .navbar .container {
+ width: auto;
+ padding: 0;
+ }
+ .navbar .brand {
+ padding-right: 10px;
+ padding-left: 10px;
+ margin: 0 0 0 -5px;
+ }
+ .nav-collapse {
+ clear: both;
+ }
+ .nav-collapse .nav {
+ float: none;
+ margin: 0 0 10px;
+ }
+ .nav-collapse .nav > li {
+ float: none;
+ }
+ .nav-collapse .nav > li > a {
+ margin-bottom: 2px;
+ }
+ .nav-collapse .nav > .divider-vertical {
+ display: none;
+ }
+ .nav-collapse .nav .nav-header {
+ color: #555555;
+ text-shadow: none;
+ }
+ .nav-collapse .nav > li > a,
+ .nav-collapse .dropdown-menu a {
+ padding: 9px 15px;
+ font-weight: bold;
+ color: #555555;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ }
+ .nav-collapse .btn {
+ padding: 4px 10px 4px;
+ font-weight: normal;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ }
+ .nav-collapse .dropdown-menu li + li a {
+ margin-bottom: 2px;
+ }
+ .nav-collapse .nav > li > a:hover,
+ .nav-collapse .dropdown-menu a:hover {
+ background-color: #f2f2f2;
+ }
+ .navbar-inverse .nav-collapse .nav > li > a:hover,
+ .navbar-inverse .nav-collapse .dropdown-menu a:hover {
+ background-color: #111111;
+ }
+ .nav-collapse.in .btn-group {
+ padding: 0;
+ margin-top: 5px;
+ }
+ .nav-collapse .dropdown-menu {
+ position: static;
+ top: auto;
+ left: auto;
+ display: block;
+ float: none;
+ max-width: none;
+ padding: 0;
+ margin: 0 15px;
+ background-color: transparent;
+ border: none;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+ }
+ .nav-collapse .dropdown-menu:before,
+ .nav-collapse .dropdown-menu:after {
+ display: none;
+ }
+ .nav-collapse .dropdown-menu .divider {
+ display: none;
+ }
+ .nav-collapse .navbar-form,
+ .nav-collapse .navbar-search {
+ float: none;
+ padding: 10px 15px;
+ margin: 10px 0;
+ border-top: 1px solid #f2f2f2;
+ border-bottom: 1px solid #f2f2f2;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
+ }
+ .navbar .nav-collapse .nav.pull-right {
+ float: none;
+ margin-left: 0;
+ }
+ .nav-collapse,
+ .nav-collapse.collapse {
+ height: 0;
+ overflow: hidden;
+ }
+ .navbar .btn-navbar {
+ display: block;
+ }
+ .navbar-static .navbar-inner {
+ padding-right: 10px;
+ padding-left: 10px;
+ }
+}
+
+@media (min-width: 980px) {
+ .nav-collapse.collapse {
+ height: auto !important;
+ overflow: visible !important;
+ }
+}
+
--- a/bootstrap.min.css
+++ b/bootstrap.min.css
@@ -1,357 +1,5625 @@
-html,body{margin:0;padding:0;}
-h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,cite,code,del,dfn,em,img,q,s,samp,small,strike,strong,sub,sup,tt,var,dd,dl,dt,li,ol,ul,fieldset,form,label,legend,button,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;font-weight:normal;font-style:normal;font-size:100%;line-height:1;font-family:inherit;}
-table{border-collapse:collapse;border-spacing:0;}
-ol,ul{list-style:none;}
-q:before,q:after,blockquote:before,blockquote:after{content:"";}
-html{overflow-y:scroll;font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
-a:focus{outline:thin dotted;}
-a:hover,a:active{outline:0;}
-article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
-audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
-audio:not([controls]){display:none;}
-sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}
-sup{top:-0.5em;}
-sub{bottom:-0.25em;}
-img{border:0;-ms-interpolation-mode:bicubic;}
-button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;}
-button,input{line-height:normal;*overflow:visible;}
-button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
-button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
-input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;}
-input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;}
-textarea{overflow:auto;vertical-align:top;}
-body{background-color:#ffffff;margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:18px;color:#404040;}
-.container{width:940px;margin-left:auto;margin-right:auto;zoom:1;}.container:before,.container:after{display:table;content:"";zoom:1;}
-.container:after{clear:both;}
-.container-fluid{position:relative;min-width:940px;padding-left:20px;padding-right:20px;zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";zoom:1;}
-.container-fluid:after{clear:both;}
-.container-fluid>.sidebar{position:absolute;top:0;left:20px;width:220px;}
-.container-fluid>.content{margin-left:240px;}
-a{color:#0069d6;text-decoration:none;line-height:inherit;font-weight:inherit;}a:hover{color:#00438a;text-decoration:underline;}
-.pull-right{float:right;}
-.pull-left{float:left;}
-.hide{display:none;}
-.show{display:block;}
-.row{zoom:1;margin-left:-20px;}.row:before,.row:after{display:table;content:"";zoom:1;}
-.row:after{clear:both;}
-.row>[class*="span"]{display:inline;float:left;margin-left:20px;}
-.span1{width:40px;}
-.span2{width:100px;}
-.span3{width:160px;}
-.span4{width:220px;}
-.span5{width:280px;}
-.span6{width:340px;}
-.span7{width:400px;}
-.span8{width:460px;}
-.span9{width:520px;}
-.span10{width:580px;}
-.span11{width:640px;}
-.span12{width:700px;}
-.span13{width:760px;}
-.span14{width:820px;}
-.span15{width:880px;}
-.span16{width:940px;}
-.span17{width:1000px;}
-.span18{width:1060px;}
-.span19{width:1120px;}
-.span20{width:1180px;}
-.span21{width:1240px;}
-.span22{width:1300px;}
-.span23{width:1360px;}
-.span24{width:1420px;}
-.row>.offset1{margin-left:80px;}
-.row>.offset2{margin-left:140px;}
-.row>.offset3{margin-left:200px;}
-.row>.offset4{margin-left:260px;}
-.row>.offset5{margin-left:320px;}
-.row>.offset6{margin-left:380px;}
-.row>.offset7{margin-left:440px;}
-.row>.offset8{margin-left:500px;}
-.row>.offset9{margin-left:560px;}
-.row>.offset10{margin-left:620px;}
-.row>.offset11{margin-left:680px;}
-.row>.offset12{margin-left:740px;}
-.span-one-third{width:300px;}
-.span-two-thirds{width:620px;}
-.row>.offset-one-third{margin-left:340px;}
-.row>.offset-two-thirds{margin-left:660px;}
-p{font-size:13px;font-weight:normal;line-height:18px;margin-bottom:9px;}p small{font-size:11px;color:#bfbfbf;}
-h1,h2,h3,h4,h5,h6{font-weight:bold;color:#404040;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{color:#bfbfbf;}
-h1{margin-bottom:18px;font-size:30px;line-height:36px;}h1 small{font-size:18px;}
-h2{font-size:24px;line-height:36px;}h2 small{font-size:14px;}
-h3,h4,h5,h6{line-height:36px;}
-h3{font-size:18px;}h3 small{font-size:14px;}
-h4{font-size:16px;}h4 small{font-size:12px;}
-h5{font-size:14px;}
-h6{font-size:13px;color:#bfbfbf;text-transform:uppercase;}
-ul,ol{margin:0 0 18px 25px;}
-ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}
-ul{list-style:disc;}
-ol{list-style:decimal;}
-li{line-height:18px;color:#808080;}
-ul.unstyled{list-style:none;margin-left:0;}
-dl{margin-bottom:18px;}dl dt,dl dd{line-height:18px;}
-dl dt{font-weight:bold;}
-dl dd{margin-left:9px;}
-hr{margin:20px 0 19px;border:0;border-bottom:1px solid #eee;}
-strong{font-style:inherit;font-weight:bold;}
-em{font-style:italic;font-weight:inherit;line-height:inherit;}
-.muted{color:#bfbfbf;}
-blockquote{margin-bottom:18px;border-left:5px solid #eee;padding-left:15px;}blockquote p{font-size:14px;font-weight:300;line-height:18px;margin-bottom:0;}
-blockquote small{display:block;font-size:12px;font-weight:300;line-height:18px;color:#bfbfbf;}blockquote small:before{content:'\2014 \00A0';}
-address{display:block;line-height:18px;margin-bottom:18px;}
-code,pre{padding:0 3px 2px;font-family:Monaco, Andale Mono, Courier New, monospace;font-size:12px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
-code{background-color:#fee9cc;color:rgba(0, 0, 0, 0.75);padding:1px 3px;}
-pre{background-color:#f5f5f5;display:block;padding:8.5px;margin:0 0 18px;line-height:18px;font-size:12px;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;white-space:pre;white-space:pre-wrap;word-wrap:break-word;}
-form{margin-bottom:18px;}
-fieldset{margin-bottom:18px;padding-top:18px;}fieldset legend{display:block;padding-left:150px;font-size:19.5px;line-height:1;color:#404040;*padding:0 0 5px 145px;*line-height:1.5;}
-form .clearfix{margin-bottom:18px;zoom:1;}form .clearfix:before,form .clearfix:after{display:table;content:"";zoom:1;}
-form .clearfix:after{clear:both;}
-label,input,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:normal;}
-label{padding-top:6px;font-size:13px;line-height:18px;float:left;width:130px;text-align:right;color:#404040;}
-form .input{margin-left:150px;}
-input[type=checkbox],input[type=radio]{cursor:pointer;}
-input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;font-size:13px;line-height:18px;color:#808080;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
-select{padding:initial;}
-input[type=checkbox],input[type=radio]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;border:none;}
-input[type=file]{background-color:#ffffff;padding:initial;border:initial;line-height:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
-input[type=button],input[type=reset],input[type=submit]{width:auto;height:auto;}
-select,input[type=file]{height:27px;*height:auto;line-height:27px;*margin-top:4px;}
-select[multiple]{height:inherit;background-color:#ffffff;}
-textarea{height:auto;}
-.uneditable-input{background-color:#ffffff;display:block;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;}
-:-moz-placeholder{color:#bfbfbf;}
-::-webkit-input-placeholder{color:#bfbfbf;}
-input,textarea{-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;-webkit-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);}
-input:focus,textarea:focus{outline:0;border-color:rgba(82, 168, 236, 0.8);-webkit-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);}
-input[type=file]:focus,input[type=checkbox]:focus,select:focus{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;outline:1px dotted #666;}
-form .clearfix.error>label,form .clearfix.error .help-block,form .clearfix.error .help-inline{color:#b94a48;}
-form .clearfix.error input,form .clearfix.error textarea{color:#b94a48;border-color:#ee5f5b;}form .clearfix.error input:focus,form .clearfix.error textarea:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;}
-form .clearfix.error .input-prepend .add-on,form .clearfix.error .input-append .add-on{color:#b94a48;background-color:#fce6e6;border-color:#b94a48;}
-form .clearfix.warning>label,form .clearfix.warning .help-block,form .clearfix.warning .help-inline{color:#c09853;}
-form .clearfix.warning input,form .clearfix.warning textarea{color:#c09853;border-color:#ccae64;}form .clearfix.warning input:focus,form .clearfix.warning textarea:focus{border-color:#be9a3f;-webkit-box-shadow:0 0 6px #e5d6b1;-moz-box-shadow:0 0 6px #e5d6b1;box-shadow:0 0 6px #e5d6b1;}
-form .clearfix.warning .input-prepend .add-on,form .clearfix.warning .input-append .add-on{color:#c09853;background-color:#d2b877;border-color:#c09853;}
-form .clearfix.success>label,form .clearfix.success .help-block,form .clearfix.success .help-inline{color:#468847;}
-form .clearfix.success input,form .clearfix.success textarea{color:#468847;border-color:#57a957;}form .clearfix.success input:focus,form .clearfix.success textarea:focus{border-color:#458845;-webkit-box-shadow:0 0 6px #9acc9a;-moz-box-shadow:0 0 6px #9acc9a;box-shadow:0 0 6px #9acc9a;}
-form .clearfix.success .input-prepend .add-on,form .clearfix.success .input-append .add-on{color:#468847;background-color:#bcddbc;border-color:#468847;}
-.input-mini,input.mini,textarea.mini,select.mini{width:60px;}
-.input-small,input.small,textarea.small,select.small{width:90px;}
-.input-medium,input.medium,textarea.medium,select.medium{width:150px;}
-.input-large,input.large,textarea.large,select.large{width:210px;}
-.input-xlarge,input.xlarge,textarea.xlarge,select.xlarge{width:270px;}
-.input-xxlarge,input.xxlarge,textarea.xxlarge,select.xxlarge{width:530px;}
-textarea.xxlarge{overflow-y:auto;}
-input.span1,textarea.span1{display:inline-block;float:none;width:30px;margin-left:0;}
-input.span2,textarea.span2{display:inline-block;float:none;width:90px;margin-left:0;}
-input.span3,textarea.span3{display:inline-block;float:none;width:150px;margin-left:0;}
-input.span4,textarea.span4{display:inline-block;float:none;width:210px;margin-left:0;}
-input.span5,textarea.span5{display:inline-block;float:none;width:270px;margin-left:0;}
-input.span6,textarea.span6{display:inline-block;float:none;width:330px;margin-left:0;}
-input.span7,textarea.span7{display:inline-block;float:none;width:390px;margin-left:0;}
-input.span8,textarea.span8{display:inline-block;float:none;width:450px;margin-left:0;}
-input.span9,textarea.span9{display:inline-block;float:none;width:510px;margin-left:0;}
-input.span10,textarea.span10{display:inline-block;float:none;width:570px;margin-left:0;}
-input.span11,textarea.span11{display:inline-block;float:none;width:630px;margin-left:0;}
-input.span12,textarea.span12{display:inline-block;float:none;width:690px;margin-left:0;}
-input.span13,textarea.span13{display:inline-block;float:none;width:750px;margin-left:0;}
-input.span14,textarea.span14{display:inline-block;float:none;width:810px;margin-left:0;}
-input.span15,textarea.span15{display:inline-block;float:none;width:870px;margin-left:0;}
-input.span16,textarea.span16{display:inline-block;float:none;width:930px;margin-left:0;}
-input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{background-color:#f5f5f5;border-color:#ddd;cursor:not-allowed;}
-.actions{background:#f5f5f5;margin-top:18px;margin-bottom:18px;padding:17px 20px 18px 150px;border-top:1px solid #ddd;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;}.actions .secondary-action{float:right;}.actions .secondary-action a{line-height:30px;}.actions .secondary-action a:hover{text-decoration:underline;}
-.help-inline,.help-block{font-size:13px;line-height:18px;color:#bfbfbf;}
-.help-inline{padding-left:5px;*position:relative;*top:-5px;}
-.help-block{display:block;max-width:600px;}
-.inline-inputs{color:#808080;}.inline-inputs span{padding:0 2px 0 1px;}
-.input-prepend input,.input-append input{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
-.input-prepend .add-on,.input-append .add-on{position:relative;background:#f5f5f5;border:1px solid #ccc;z-index:2;float:left;display:block;width:auto;min-width:16px;height:18px;padding:4px 4px 4px 5px;margin-right:-1px;font-weight:normal;line-height:18px;color:#bfbfbf;text-align:center;text-shadow:0 1px 0 #ffffff;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
-.input-prepend .active,.input-append .active{background:#a9dba9;border-color:#46a546;}
-.input-prepend .add-on{*margin-top:1px;}
-.input-append input{float:left;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
-.input-append .add-on{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;margin-right:0;margin-left:-1px;}
-.inputs-list{margin:0 0 5px;width:100%;}.inputs-list li{display:block;padding:0;width:100%;}
-.inputs-list label{display:block;float:none;width:auto;padding:0;margin-left:20px;line-height:18px;text-align:left;white-space:normal;}.inputs-list label strong{color:#808080;}
-.inputs-list label small{font-size:11px;font-weight:normal;}
-.inputs-list .inputs-list{margin-left:25px;margin-bottom:10px;padding-top:0;}
-.inputs-list:first-child{padding-top:6px;}
-.inputs-list li+li{padding-top:2px;}
-.inputs-list input[type=radio],.inputs-list input[type=checkbox]{margin-bottom:0;margin-left:-20px;float:left;}
-.form-stacked{padding-left:20px;}.form-stacked fieldset{padding-top:9px;}
-.form-stacked legend{padding-left:0;}
-.form-stacked label{display:block;float:none;width:auto;font-weight:bold;text-align:left;line-height:20px;padding-top:0;}
-.form-stacked .clearfix{margin-bottom:9px;}.form-stacked .clearfix div.input{margin-left:0;}
-.form-stacked .inputs-list{margin-bottom:0;}.form-stacked .inputs-list li{padding-top:0;}.form-stacked .inputs-list li label{font-weight:normal;padding-top:0;}
-.form-stacked div.clearfix.error{padding-top:10px;padding-bottom:10px;padding-left:10px;margin-top:0;margin-left:-10px;}
-.form-stacked .actions{margin-left:-20px;padding-left:20px;}
-table{width:100%;margin-bottom:18px;padding:0;font-size:13px;border-collapse:collapse;}table th,table td{padding:10px 10px 9px;line-height:18px;text-align:left;}
-table th{padding-top:9px;font-weight:bold;vertical-align:middle;}
-table td{vertical-align:top;border-top:1px solid #ddd;}
-table tbody th{border-top:1px solid #ddd;vertical-align:top;}
-.condensed-table th,.condensed-table td{padding:5px 5px 4px;}
-.bordered-table{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.bordered-table th+th,.bordered-table td+td,.bordered-table th+td{border-left:1px solid #ddd;}
-.bordered-table thead tr:first-child th:first-child,.bordered-table tbody tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;}
-.bordered-table thead tr:first-child th:last-child,.bordered-table tbody tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;}
-.bordered-table tbody tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;}
-.bordered-table tbody tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;}
-table .span1{width:20px;}
-table .span2{width:60px;}
-table .span3{width:100px;}
-table .span4{width:140px;}
-table .span5{width:180px;}
-table .span6{width:220px;}
-table .span7{width:260px;}
-table .span8{width:300px;}
-table .span9{width:340px;}
-table .span10{width:380px;}
-table .span11{width:420px;}
-table .span12{width:460px;}
-table .span13{width:500px;}
-table .span14{width:540px;}
-table .span15{width:580px;}
-table .span16{width:620px;}
-.zebra-striped tbody tr:nth-child(odd) td,.zebra-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;}
-.zebra-striped tbody tr:hover td,.zebra-striped tbody tr:hover th{background-color:#f5f5f5;}
-table .header{cursor:pointer;}table .header:after{content:"";float:right;margin-top:7px;border-width:0 4px 4px;border-style:solid;border-color:#000 transparent;visibility:hidden;}
-table .headerSortUp,table .headerSortDown{background-color:rgba(141, 192, 219, 0.25);text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);}
-table .header:hover:after{visibility:visible;}
-table .headerSortDown:after,table .headerSortDown:hover:after{visibility:visible;filter:alpha(opacity=60);-khtml-opacity:0.6;-moz-opacity:0.6;opacity:0.6;}
-table .headerSortUp:after{border-bottom:none;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #000;visibility:visible;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:alpha(opacity=60);-khtml-opacity:0.6;-moz-opacity:0.6;opacity:0.6;}
-table .blue{color:#049cdb;border-bottom-color:#049cdb;}
-table .headerSortUp.blue,table .headerSortDown.blue{background-color:#ade6fe;}
-table .green{color:#46a546;border-bottom-color:#46a546;}
-table .headerSortUp.green,table .headerSortDown.green{background-color:#cdeacd;}
-table .red{color:#9d261d;border-bottom-color:#9d261d;}
-table .headerSortUp.red,table .headerSortDown.red{background-color:#f4c8c5;}
-table .yellow{color:#ffc40d;border-bottom-color:#ffc40d;}
-table .headerSortUp.yellow,table .headerSortDown.yellow{background-color:#fff6d9;}
-table .orange{color:#f89406;border-bottom-color:#f89406;}
-table .headerSortUp.orange,table .headerSortDown.orange{background-color:#fee9cc;}
-table .purple{color:#7a43b6;border-bottom-color:#7a43b6;}
-table .headerSortUp.purple,table .headerSortDown.purple{background-color:#e2d5f0;}
-.topbar{height:40px;position:fixed;top:0;left:0;right:0;z-index:10000;overflow:visible;}.topbar a{color:#bfbfbf;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}
-.topbar h3 a:hover,.topbar .brand:hover,.topbar ul .active>a{background-color:#333;background-color:rgba(255, 255, 255, 0.05);color:#ffffff;text-decoration:none;}
-.topbar h3{position:relative;}
-.topbar h3 a,.topbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;color:#ffffff;font-size:20px;font-weight:200;line-height:1;}
-.topbar p{margin:0;line-height:40px;}.topbar p a:hover{background-color:transparent;color:#ffffff;}
-.topbar form{float:left;margin:5px 0 0 0;position:relative;filter:alpha(opacity=100);-khtml-opacity:1;-moz-opacity:1;opacity:1;}
-.topbar form.pull-right{float:right;}
-.topbar input{background-color:#444;background-color:rgba(255, 255, 255, 0.3);font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:normal;font-weight:13px;line-height:1;padding:4px 9px;color:#ffffff;color:rgba(255, 255, 255, 0.75);border:1px solid #111;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.topbar input:-moz-placeholder{color:#e6e6e6;}
-.topbar input::-webkit-input-placeholder{color:#e6e6e6;}
-.topbar input:hover{background-color:#bfbfbf;background-color:rgba(255, 255, 255, 0.5);color:#ffffff;}
-.topbar input:focus,.topbar input.focused{outline:0;background-color:#ffffff;color:#404040;text-shadow:0 1px 0 #ffffff;border:0;padding:5px 10px;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);}
-.topbar-inner,.topbar .fill{background-color:#222;background-color:#222222;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#333333), to(#222222));background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #333333), color-stop(100%, #222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);}
-.topbar div>ul,.nav{display:block;float:left;margin:0 10px 0 0;position:relative;left:0;}.topbar div>ul>li,.nav>li{display:block;float:left;}
-.topbar div>ul a,.nav a{display:block;float:none;padding:10px 10px 11px;line-height:19px;text-decoration:none;}.topbar div>ul a:hover,.nav a:hover{color:#ffffff;text-decoration:none;}
-.topbar div>ul .active>a,.nav .active>a{background-color:#222;background-color:rgba(0, 0, 0, 0.5);}
-.topbar div>ul.secondary-nav,.nav.secondary-nav{float:right;margin-left:10px;margin-right:0;}.topbar div>ul.secondary-nav .menu-dropdown,.nav.secondary-nav .menu-dropdown,.topbar div>ul.secondary-nav .dropdown-menu,.nav.secondary-nav .dropdown-menu{right:0;border:0;}
-.topbar div>ul a.menu:hover,.nav a.menu:hover,.topbar div>ul li.open .menu,.nav li.open .menu,.topbar div>ul .dropdown-toggle:hover,.nav .dropdown-toggle:hover,.topbar div>ul .dropdown.open .dropdown-toggle,.nav .dropdown.open .dropdown-toggle{background:#444;background:rgba(255, 255, 255, 0.05);}
-.topbar div>ul .menu-dropdown,.nav .menu-dropdown,.topbar div>ul .dropdown-menu,.nav .dropdown-menu{background-color:#333;}.topbar div>ul .menu-dropdown a.menu,.nav .menu-dropdown a.menu,.topbar div>ul .dropdown-menu a.menu,.nav .dropdown-menu a.menu,.topbar div>ul .menu-dropdown .dropdown-toggle,.nav .menu-dropdown .dropdown-toggle,.topbar div>ul .dropdown-menu .dropdown-toggle,.nav .dropdown-menu .dropdown-toggle{color:#ffffff;}.topbar div>ul .menu-dropdown a.menu.open,.nav .menu-dropdown a.menu.open,.topbar div>ul .dropdown-menu a.menu.open,.nav .dropdown-menu a.menu.open,.topbar div>ul .menu-dropdown .dropdown-toggle.open,.nav .menu-dropdown .dropdown-toggle.open,.topbar div>ul .dropdown-menu .dropdown-toggle.open,.nav .dropdown-menu .dropdown-toggle.open{background:#444;background:rgba(255, 255, 255, 0.05);}
-.topbar div>ul .menu-dropdown li a,.nav .menu-dropdown li a,.topbar div>ul .dropdown-menu li a,.nav .dropdown-menu li a{color:#999;text-shadow:0 1px 0 rgba(0, 0, 0, 0.5);}.topbar div>ul .menu-dropdown li a:hover,.nav .menu-dropdown li a:hover,.topbar div>ul .dropdown-menu li a:hover,.nav .dropdown-menu li a:hover{background-color:#191919;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#292929), to(#191919));background-image:-moz-linear-gradient(top, #292929, #191919);background-image:-ms-linear-gradient(top, #292929, #191919);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #292929), color-stop(100%, #191919));background-image:-webkit-linear-gradient(top, #292929, #191919);background-image:-o-linear-gradient(top, #292929, #191919);background-image:linear-gradient(top, #292929, #191919);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#292929', endColorstr='#191919', GradientType=0);color:#ffffff;}
-.topbar div>ul .menu-dropdown .active a,.nav .menu-dropdown .active a,.topbar div>ul .dropdown-menu .active a,.nav .dropdown-menu .active a{color:#ffffff;}
-.topbar div>ul .menu-dropdown .divider,.nav .menu-dropdown .divider,.topbar div>ul .dropdown-menu .divider,.nav .dropdown-menu .divider{background-color:#222;border-color:#444;}
-.topbar ul .menu-dropdown li a,.topbar ul .dropdown-menu li a{padding:4px 15px;}
-li.menu,.dropdown{position:relative;}
-a.menu:after,.dropdown-toggle:after{width:0;height:0;display:inline-block;content:"↓";text-indent:-99999px;vertical-align:top;margin-top:8px;margin-left:4px;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #ffffff;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;}
-.menu-dropdown,.dropdown-menu{background-color:#ffffff;float:left;display:none;position:absolute;top:40px;z-index:900;min-width:160px;max-width:220px;_width:160px;margin-left:0;margin-right:0;padding:6px 0;zoom:1;border-color:#999;border-color:rgba(0, 0, 0, 0.2);border-style:solid;border-width:0 1px 1px;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.menu-dropdown li,.dropdown-menu li{float:none;display:block;background-color:none;}
-.menu-dropdown .divider,.dropdown-menu .divider{height:1px;margin:5px 0;overflow:hidden;background-color:#eee;border-bottom:1px solid #ffffff;}
-.topbar .dropdown-menu a,.dropdown-menu a{display:block;padding:4px 15px;clear:both;font-weight:normal;line-height:18px;color:#808080;text-shadow:0 1px 0 #ffffff;}.topbar .dropdown-menu a:hover,.dropdown-menu a:hover,.topbar .dropdown-menu a.hover,.dropdown-menu a.hover{background-color:#dddddd;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#eeeeee), to(#dddddd));background-image:-moz-linear-gradient(top, #eeeeee, #dddddd);background-image:-ms-linear-gradient(top, #eeeeee, #dddddd);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #eeeeee), color-stop(100%, #dddddd));background-image:-webkit-linear-gradient(top, #eeeeee, #dddddd);background-image:-o-linear-gradient(top, #eeeeee, #dddddd);background-image:linear-gradient(top, #eeeeee, #dddddd);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#dddddd', GradientType=0);color:#404040;text-decoration:none;-webkit-box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.025),inset 0 -1px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.025),inset 0 -1px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.025),inset 0 -1px rgba(0, 0, 0, 0.025);}
-.open .menu,.dropdown.open .menu,.open .dropdown-toggle,.dropdown.open .dropdown-toggle{color:#ffffff;background:#ccc;background:rgba(0, 0, 0, 0.3);}
-.open .menu-dropdown,.dropdown.open .menu-dropdown,.open .dropdown-menu,.dropdown.open .dropdown-menu{display:block;}
-.tabs,.pills{margin:0 0 18px;padding:0;list-style:none;zoom:1;}.tabs:before,.pills:before,.tabs:after,.pills:after{display:table;content:"";zoom:1;}
-.tabs:after,.pills:after{clear:both;}
-.tabs>li,.pills>li{float:left;}.tabs>li>a,.pills>li>a{display:block;}
-.tabs{border-color:#ddd;border-style:solid;border-width:0 0 1px;}.tabs>li{position:relative;margin-bottom:-1px;}.tabs>li>a{padding:0 15px;margin-right:2px;line-height:34px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.tabs>li>a:hover{text-decoration:none;background-color:#eee;border-color:#eee #eee #ddd;}
-.tabs .active>a,.tabs .active>a:hover{color:#808080;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;}
-.tabs .menu-dropdown,.tabs .dropdown-menu{top:35px;border-width:1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px;}
-.tabs a.menu:after,.tabs .dropdown-toggle:after{border-top-color:#999;margin-top:15px;margin-left:5px;}
-.tabs li.open.menu .menu,.tabs .open.dropdown .dropdown-toggle{border-color:#999;}
-.tabs li.open a.menu:after,.tabs .dropdown.open .dropdown-toggle:after{border-top-color:#555;}
-.pills a{margin:5px 3px 5px 0;padding:0 15px;line-height:30px;text-shadow:0 1px 1px #ffffff;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}.pills a:hover{color:#ffffff;text-decoration:none;text-shadow:0 1px 1px rgba(0, 0, 0, 0.25);background-color:#00438a;}
-.pills .active a{color:#ffffff;text-shadow:0 1px 1px rgba(0, 0, 0, 0.25);background-color:#0069d6;}
-.pills-vertical>li{float:none;}
-.tab-content>.tab-pane,.pill-content>.pill-pane,.tab-content>div,.pill-content>div{display:none;}
-.tab-content>.active,.pill-content>.active{display:block;}
-.breadcrumb{padding:7px 14px;margin:0 0 18px;background-color:#f5f5f5;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#ffffff), to(#f5f5f5));background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}.breadcrumb li{display:inline;text-shadow:0 1px 0 #ffffff;}
-.breadcrumb .divider{padding:0 5px;color:#bfbfbf;}
-.breadcrumb .active a{color:#404040;}
-.hero-unit{background-color:#f5f5f5;margin-bottom:30px;padding:60px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;}
-.hero-unit p{font-size:18px;font-weight:200;line-height:27px;}
-footer{margin-top:17px;padding-top:17px;border-top:1px solid #eee;}
-.page-header{margin-bottom:17px;border-bottom:1px solid #ddd;-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}.page-header h1{margin-bottom:8px;}
-.btn.danger,.alert-message.danger,.btn.danger:hover,.alert-message.danger:hover,.btn.error,.alert-message.error,.btn.error:hover,.alert-message.error:hover,.btn.success,.alert-message.success,.btn.success:hover,.alert-message.success:hover,.btn.info,.alert-message.info,.btn.info:hover,.alert-message.info:hover{color:#ffffff;}
-.btn .close,.alert-message .close{font-family:Arial,sans-serif;line-height:18px;}
-.btn.danger,.alert-message.danger,.btn.error,.alert-message.error{background-color:#c43c35;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35));background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#c43c35 #c43c35 #882a25;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);}
-.btn.success,.alert-message.success{background-color:#57a957;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957));background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#57a957 #57a957 #3d773d;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);}
-.btn.info,.alert-message.info{background-color:#339bb9;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9));background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#339bb9 #339bb9 #22697d;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);}
-.btn{cursor:pointer;display:inline-block;background-color:#e6e6e6;background-repeat:no-repeat;background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);background-image:-ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);padding:5px 14px 6px;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);color:#333;font-size:13px;line-height:normal;border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-webkit-transition:0.1s linear all;-moz-transition:0.1s linear all;-ms-transition:0.1s linear all;-o-transition:0.1s linear all;transition:0.1s linear all;}.btn:hover{background-position:0 -15px;color:#333;text-decoration:none;}
-.btn:focus{outline:1px dotted #666;}
-.btn.primary{color:#ffffff;background-color:#0064cd;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd));background-image:-moz-linear-gradient(top, #049cdb, #0064cd);background-image:-ms-linear-gradient(top, #049cdb, #0064cd);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd));background-image:-webkit-linear-gradient(top, #049cdb, #0064cd);background-image:-o-linear-gradient(top, #049cdb, #0064cd);background-image:linear-gradient(top, #049cdb, #0064cd);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#049cdb', endColorstr='#0064cd', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#0064cd #0064cd #003f81;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);}
-.btn.active,.btn:active{-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.25),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.25),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.25),0 1px 2px rgba(0, 0, 0, 0.05);}
-.btn.disabled{cursor:default;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=65);-khtml-opacity:0.65;-moz-opacity:0.65;opacity:0.65;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
-.btn[disabled]{cursor:default;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=65);-khtml-opacity:0.65;-moz-opacity:0.65;opacity:0.65;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
-.btn.large{font-size:15px;line-height:normal;padding:9px 14px 9px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
-.btn.small{padding:7px 9px 7px;font-size:11px;}
-:root .alert-message,:root .btn{border-radius:0 \0;}
-button.btn::-moz-focus-inner,input[type=submit].btn::-moz-focus-inner{padding:0;border:0;}
-.close{float:right;color:#000000;font-size:20px;font-weight:bold;line-height:13.5px;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=25);-khtml-opacity:0.25;-moz-opacity:0.25;opacity:0.25;}.close:hover{color:#000000;text-decoration:none;filter:alpha(opacity=40);-khtml-opacity:0.4;-moz-opacity:0.4;opacity:0.4;}
-.alert-message{position:relative;padding:7px 15px;margin-bottom:18px;color:#404040;background-color:#eedc94;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94));background-image:-moz-linear-gradient(top, #fceec1, #eedc94);background-image:-ms-linear-gradient(top, #fceec1, #eedc94);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94));background-image:-webkit-linear-gradient(top, #fceec1, #eedc94);background-image:-o-linear-gradient(top, #fceec1, #eedc94);background-image:linear-gradient(top, #fceec1, #eedc94);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#eedc94 #eedc94 #e4c652;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);border-width:1px;border-style:solid;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);}.alert-message .close{margin-top:1px;*margin-top:0;}
-.alert-message a{font-weight:bold;color:#404040;}
-.alert-message.danger p a,.alert-message.error p a,.alert-message.success p a,.alert-message.info p a{color:#ffffff;}
-.alert-message h5{line-height:18px;}
-.alert-message p{margin-bottom:0;}
-.alert-message div{margin-top:5px;margin-bottom:2px;line-height:28px;}
-.alert-message .btn{-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);}
-.alert-message.block-message{background-image:none;background-color:#fdf5d9;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);padding:14px;border-color:#fceec1;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}.alert-message.block-message ul,.alert-message.block-message p{margin-right:30px;}
-.alert-message.block-message ul{margin-bottom:0;}
-.alert-message.block-message li{color:#404040;}
-.alert-message.block-message .alert-actions{margin-top:5px;}
-.alert-message.block-message.error,.alert-message.block-message.success,.alert-message.block-message.info{color:#404040;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}
-.alert-message.block-message.error{background-color:#fddfde;border-color:#fbc7c6;}
-.alert-message.block-message.success{background-color:#d1eed1;border-color:#bfe7bf;}
-.alert-message.block-message.info{background-color:#ddf4fb;border-color:#c6edf9;}
-.alert-message.block-message.danger p a,.alert-message.block-message.error p a,.alert-message.block-message.success p a,.alert-message.block-message.info p a{color:#404040;}
-.pagination{height:36px;margin:18px 0;}.pagination ul{float:left;margin:0;border:1px solid #ddd;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);}
-.pagination li{display:inline;}
-.pagination a{float:left;padding:0 14px;line-height:34px;border-right:1px solid;border-right-color:#ddd;border-right-color:rgba(0, 0, 0, 0.15);*border-right-color:#ddd;text-decoration:none;}
-.pagination a:hover,.pagination .active a{background-color:#c7eefe;}
-.pagination .disabled a,.pagination .disabled a:hover{background-color:transparent;color:#bfbfbf;}
-.pagination .next a{border:0;}
-.well{background-color:#f5f5f5;margin-bottom:20px;padding:19px;min-height:20px;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);}
-.modal-backdrop{background-color:#000000;position:fixed;top:0;left:0;right:0;bottom:0;z-index:10000;}.modal-backdrop.fade{opacity:0;}
-.modal-backdrop,.modal-backdrop.fade.in{filter:alpha(opacity=80);-khtml-opacity:0.8;-moz-opacity:0.8;opacity:0.8;}
-.modal{position:fixed;top:50%;left:50%;z-index:11000;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal .close{margin-top:7px;}
-.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-ms-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;}
-.modal.fade.in{top:50%;}
-.modal-header{border-bottom:1px solid #eee;padding:5px 15px;}
-.modal-body{padding:15px;}
-.modal-body form{margin-bottom:0;}
-.modal-footer{background-color:#f5f5f5;padding:14px 15px 15px;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;zoom:1;margin-bottom:0;}.modal-footer:before,.modal-footer:after{display:table;content:"";zoom:1;}
-.modal-footer:after{clear:both;}
-.modal-footer .btn{float:right;margin-left:5px;}
-.modal .popover,.modal .twipsy{z-index:12000;}
-.twipsy{display:block;position:absolute;visibility:visible;padding:5px;font-size:11px;z-index:1000;filter:alpha(opacity=80);-khtml-opacity:0.8;-moz-opacity:0.8;opacity:0.8;}.twipsy.fade.in{filter:alpha(opacity=80);-khtml-opacity:0.8;-moz-opacity:0.8;opacity:0.8;}
-.twipsy.above .twipsy-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
-.twipsy.left .twipsy-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
-.twipsy.below .twipsy-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
-.twipsy.right .twipsy-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
-.twipsy-inner{padding:3px 8px;background-color:#000000;color:white;text-align:center;max-width:200px;text-decoration:none;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
-.twipsy-arrow{position:absolute;width:0;height:0;}
-.popover{position:absolute;top:0;left:0;z-index:1000;padding:5px;display:none;}.popover.above .arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
-.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
-.popover.below .arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
-.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
-.popover .arrow{position:absolute;width:0;height:0;}
-.popover .inner{background:#000000;background:rgba(0, 0, 0, 0.8);padding:3px;overflow:hidden;width:280px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);}
-.popover .title{background-color:#f5f5f5;padding:9px 15px;line-height:1;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;border-bottom:1px solid #eee;}
-.popover .content{background-color:#ffffff;padding:14px;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.popover .content p,.popover .content ul,.popover .content ol{margin-bottom:0;}
-.fade{-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;opacity:0;}.fade.in{opacity:1;}
-.label{padding:1px 3px 2px;font-size:9.75px;font-weight:bold;color:#ffffff;text-transform:uppercase;white-space:nowrap;background-color:#bfbfbf;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}.label.important{background-color:#c43c35;}
-.label.warning{background-color:#f89406;}
-.label.success{background-color:#46a546;}
-.label.notice{background-color:#62cffc;}
-.media-grid{margin-left:-20px;margin-bottom:0;zoom:1;}.media-grid:before,.media-grid:after{display:table;content:"";zoom:1;}
-.media-grid:after{clear:both;}
-.media-grid li{display:inline;}
-.media-grid a{float:left;padding:4px;margin:0 0 18px 20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);}.media-grid a img{display:block;}
-.media-grid a:hover{border-color:#0069d6;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);}
-
+/*!
+ * Bootstrap v2.1.0
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+nav,
+section {
+ display: block;
+}
+
+audio,
+canvas,
+video {
+ display: inline-block;
+ *display: inline;
+ *zoom: 1;
+}
+
+audio:not([controls]) {
+ display: none;
+}
+
+html {
+ font-size: 100%;
+ -webkit-text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+}
+
+a:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+a:hover,
+a:active {
+ outline: 0;
+}
+
+sub,
+sup {
+ position: relative;
+ font-size: 75%;
+ line-height: 0;
+ vertical-align: baseline;
+}
+
+sup {
+ top: -0.5em;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+img {
+ height: auto;
+ max-width: 100%;
+ vertical-align: middle;
+ border: 0;
+ -ms-interpolation-mode: bicubic;
+}
+
+#map_canvas img {
+ max-width: none;
+}
+
+button,
+input,
+select,
+textarea {
+ margin: 0;
+ font-size: 100%;
+ vertical-align: middle;
+}
+
+button,
+input {
+ *overflow: visible;
+ line-height: normal;
+}
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ padding: 0;
+ border: 0;
+}
+
+button,
+input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+ cursor: pointer;
+ -webkit-appearance: button;
+}
+
+input[type="search"] {
+ -webkit-box-sizing: content-box;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ -webkit-appearance: textfield;
+}
+
+input[type="search"]::-webkit-search-decoration,
+input[type="search"]::-webkit-search-cancel-button {
+ -webkit-appearance: none;
+}
+
+textarea {
+ overflow: auto;
+ vertical-align: top;
+}
+
+.clearfix {
+ *zoom: 1;
+}
+
+.clearfix:before,
+.clearfix:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.clearfix:after {
+ clear: both;
+}
+
+.hide-text {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+}
+
+.input-block-level {
+ display: block;
+ width: 100%;
+ min-height: 30px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 14px;
+ line-height: 20px;
+ color: #333333;
+ background-color: #ffffff;
+}
+
+a {
+ color: #0088cc;
+ text-decoration: none;
+}
+
+a:hover {
+ color: #005580;
+ text-decoration: underline;
+}
+
+.img-rounded {
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+
+.img-polaroid {
+ padding: 4px;
+ background-color: #fff;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+
+.img-circle {
+ -webkit-border-radius: 500px;
+ -moz-border-radius: 500px;
+ border-radius: 500px;
+}
+
+.row {
+ margin-left: -20px;
+ *zoom: 1;
+}
+
+.row:before,
+.row:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.row:after {
+ clear: both;
+}
+
+[class*="span"] {
+ float: left;
+ margin-left: 20px;
+}
+
+.container,
+.navbar-static-top .container,
+.navbar-fixed-top .container,
+.navbar-fixed-bottom .container {
+ width: 940px;
+}
+
+.span12 {
+ width: 940px;
+}
+
+.span11 {
+ width: 860px;
+}
+
+.span10 {
+ width: 780px;
+}
+
+.span9 {
+ width: 700px;
+}
+
+.span8 {
+ width: 620px;
+}
+
+.span7 {
+ width: 540px;
+}
+
+.span6 {
+ width: 460px;
+}
+
+.span5 {
+ width: 380px;
+}
+
+.span4 {
+ width: 300px;
+}
+
+.span3 {
+ width: 220px;
+}
+
+.span2 {
+ width: 140px;
+}
+
+.span1 {
+ width: 60px;
+}
+
+.offset12 {
+ margin-left: 980px;
+}
+
+.offset11 {
+ margin-left: 900px;
+}
+
+.offset10 {
+ margin-left: 820px;
+}
+
+.offset9 {
+ margin-left: 740px;
+}
+
+.offset8 {
+ margin-left: 660px;
+}
+
+.offset7 {
+ margin-left: 580px;
+}
+
+.offset6 {
+ margin-left: 500px;
+}
+
+.offset5 {
+ margin-left: 420px;
+}
+
+.offset4 {
+ margin-left: 340px;
+}
+
+.offset3 {
+ margin-left: 260px;
+}
+
+.offset2 {
+ margin-left: 180px;
+}
+
+.offset1 {
+ margin-left: 100px;
+}
+
+.row-fluid {
+ width: 100%;
+ *zoom: 1;
+}
+
+.row-fluid:before,
+.row-fluid:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.row-fluid:after {
+ clear: both;
+}
+
+.row-fluid [class*="span"] {
+ display: block;
+ float: left;
+ width: 100%;
+ min-height: 30px;
+ margin-left: 2.127659574468085%;
+ *margin-left: 2.074468085106383%;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.row-fluid [class*="span"]:first-child {
+ margin-left: 0;
+}
+
+.row-fluid .span12 {
+ width: 100%;
+ *width: 99.94680851063829%;
+}
+
+.row-fluid .span11 {
+ width: 91.48936170212765%;
+ *width: 91.43617021276594%;
+}
+
+.row-fluid .span10 {
+ width: 82.97872340425532%;
+ *width: 82.92553191489361%;
+}
+
+.row-fluid .span9 {
+ width: 74.46808510638297%;
+ *width: 74.41489361702126%;
+}
+
+.row-fluid .span8 {
+ width: 65.95744680851064%;
+ *width: 65.90425531914893%;
+}
+
+.row-fluid .span7 {
+ width: 57.44680851063829%;
+ *width: 57.39361702127659%;
+}
+
+.row-fluid .span6 {
+ width: 48.93617021276595%;
+ *width: 48.88297872340425%;
+}
+
+.row-fluid .span5 {
+ width: 40.42553191489362%;
+ *width: 40.37234042553192%;
+}
+
+.row-fluid .span4 {
+ width: 31.914893617021278%;
+ *width: 31.861702127659576%;
+}
+
+.row-fluid .span3 {
+ width: 23.404255319148934%;
+ *width: 23.351063829787233%;
+}
+
+.row-fluid .span2 {
+ width: 14.893617021276595%;
+ *width: 14.840425531914894%;
+}
+
+.row-fluid .span1 {
+ width: 6.382978723404255%;
+ *width: 6.329787234042553%;
+}
+
+.row-fluid .offset12 {
+ margin-left: 104.25531914893617%;
+ *margin-left: 104.14893617021275%;
+}
+
+.row-fluid .offset12:first-child {
+ margin-left: 102.12765957446808%;
+ *margin-left: 102.02127659574467%;
+}
+
+.row-fluid .offset11 {
+ margin-left: 95.74468085106382%;
+ *margin-left: 95.6382978723404%;
+}
+
+.row-fluid .offset11:first-child {
+ margin-left: 93.61702127659574%;
+ *margin-left: 93.51063829787232%;
+}
+
+.row-fluid .offset10 {
+ margin-left: 87.23404255319149%;
+ *margin-left: 87.12765957446807%;
+}
+
+.row-fluid .offset10:first-child {
+ margin-left: 85.1063829787234%;
+ *margin-left: 84.99999999999999%;
+}
+
+.row-fluid .offset9 {
+ margin-left: 78.72340425531914%;
+ *margin-left: 78.61702127659572%;
+}
+
+.row-fluid .offset9:first-child {
+ margin-left: 76.59574468085106%;
+ *margin-left: 76.48936170212764%;
+}
+
+.row-fluid .offset8 {
+ margin-left: 70.2127659574468%;
+ *margin-left: 70.10638297872339%;
+}
+
+.row-fluid .offset8:first-child {
+ margin-left: 68.08510638297872%;
+ *margin-left: 67.9787234042553%;
+}
+
+.row-fluid .offset7 {
+ margin-left: 61.70212765957446%;
+ *margin-left: 61.59574468085106%;
+}
+
+.row-fluid .offset7:first-child {
+ margin-left: 59.574468085106375%;
+ *margin-left: 59.46808510638297%;
+}
+
+.row-fluid .offset6 {
+ margin-left: 53.191489361702125%;
+ *margin-left: 53.085106382978715%;
+}
+
+.row-fluid .offset6:first-child {
+ margin-left: 51.063829787234035%;
+ *margin-left: 50.95744680851063%;
+}
+
+.row-fluid .offset5 {
+ margin-left: 44.68085106382979%;
+ *margin-left: 44.57446808510638%;
+}
+
+.row-fluid .offset5:first-child {
+ margin-left: 42.5531914893617%;
+ *margin-left: 42.4468085106383%;
+}
+
+.row-fluid .offset4 {
+ margin-left: 36.170212765957444%;
+ *margin-left: 36.06382978723405%;
+}
+
+.row-fluid .offset4:first-child {
+ margin-left: 34.04255319148936%;
+ *margin-left: 33.93617021276596%;
+}
+
+.row-fluid .offset3 {
+ margin-left: 27.659574468085104%;
+ *margin-left: 27.5531914893617%;
+}
+
+.row-fluid .offset3:first-child {
+ margin-left: 25.53191489361702%;
+ *margin-left: 25.425531914893618%;
+}
+
+.row-fluid .offset2 {
+ margin-left: 19.148936170212764%;
+ *margin-left: 19.04255319148936%;
+}
+
+.row-fluid .offset2:first-child {
+ margin-left: 17.02127659574468%;
+ *margin-left: 16.914893617021278%;
+}
+
+.row-fluid .offset1 {
+ margin-left: 10.638297872340425%;
+ *margin-left: 10.53191489361702%;
+}
+
+.row-fluid .offset1:first-child {
+ margin-left: 8.51063829787234%;
+ *margin-left: 8.404255319148938%;
+}
+
+[class*="span"].hide,
+.row-fluid [class*="span"].hide {
+ display: none;
+}
+
+[class*="span"].pull-right,
+.row-fluid [class*="span"].pull-right {
+ float: right;
+}
+
+.container {
+ margin-right: auto;
+ margin-left: auto;
+ *zoom: 1;
+}
+
+.container:before,
+.container:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.container:after {
+ clear: both;
+}
+
+.container-fluid {
+ padding-right: 20px;
+ padding-left: 20px;
+ *zoom: 1;
+}
+
+.container-fluid:before,
+.container-fluid:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.container-fluid:after {
+ clear: both;
+}
+
+p {
+ margin: 0 0 10px;
+}
+
+.lead {
+ margin-bottom: 20px;
+ font-size: 20px;
+ font-weight: 200;
+ line-height: 30px;
+}
+
+small {
+ font-size: 85%;
+}
+
+strong {
+ font-weight: bold;
+}
+
+em {
+ font-style: italic;
+}
+
+cite {
+ font-style: normal;
+}
+
+.muted {
+ color: #999999;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ margin: 10px 0;
+ font-family: inherit;
+ font-weight: bold;
+ line-height: 1;
+ color: inherit;
+ text-rendering: optimizelegibility;
+}
+
+h1 small,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small {
+ font-weight: normal;
+ line-height: 1;
+ color: #999999;
+}
+
+h1 {
+ font-size: 36px;
+ line-height: 40px;
+}
+
+h2 {
+ font-size: 30px;
+ line-height: 40px;
+}
+
+h3 {
+ font-size: 24px;
+ line-height: 40px;
+}
+
+h4 {
+ font-size: 18px;
+ line-height: 20px;
+}
+
+h5 {
+ font-size: 14px;
+ line-height: 20px;
+}
+
+h6 {
+ font-size: 12px;
+ line-height: 20px;
+}
+
+h1 small {
+ font-size: 24px;
+}
+
+h2 small {
+ font-size: 18px;
+}
+
+h3 small {
+ font-size: 14px;
+}
+
+h4 small {
+ font-size: 14px;
+}
+
+.page-header {
+ padding-bottom: 9px;
+ margin: 20px 0 30px;
+ border-bottom: 1px solid #eeeeee;
+}
+
+ul,
+ol {
+ padding: 0;
+ margin: 0 0 10px 25px;
+}
+
+ul ul,
+ul ol,
+ol ol,
+ol ul {
+ margin-bottom: 0;
+}
+
+li {
+ line-height: 20px;
+}
+
+ul.unstyled,
+ol.unstyled {
+ margin-left: 0;
+ list-style: none;
+}
+
+dl {
+ margin-bottom: 20px;
+}
+
+dt,
+dd {
+ line-height: 20px;
+}
+
+dt {
+ font-weight: bold;
+}
+
+dd {
+ margin-left: 10px;
+}
+
+.dl-horizontal dt {
+ float: left;
+ width: 120px;
+ overflow: hidden;
+ clear: left;
+ text-align: right;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.dl-horizontal dd {
+ margin-left: 130px;
+}
+
+hr {
+ margin: 20px 0;
+ border: 0;
+ border-top: 1px solid #eeeeee;
+ border-bottom: 1px solid #ffffff;
+}
+
+abbr[title] {
+ cursor: help;
+ border-bottom: 1px dotted #999999;
+}
+
+abbr.initialism {
+ font-size: 90%;
+ text-transform: uppercase;
+}
+
+blockquote {
+ padding: 0 0 0 15px;
+ margin: 0 0 20px;
+ border-left: 5px solid #eeeeee;
+}
+
+blockquote p {
+ margin-bottom: 0;
+ font-size: 16px;
+ font-weight: 300;
+ line-height: 25px;
+}
+
+blockquote small {
+ display: block;
+ line-height: 20px;
+ color: #999999;
+}
+
+blockquote small:before {
+ content: '\2014 \00A0';
+}
+
+blockquote.pull-right {
+ float: right;
+ padding-right: 15px;
+ padding-left: 0;
+ border-right: 5px solid #eeeeee;
+ border-left: 0;
+}
+
+blockquote.pull-right p,
+blockquote.pull-right small {
+ text-align: right;
+}
+
+blockquote.pull-right small:before {
+ content: '';
+}
+
+blockquote.pull-right small:after {
+ content: '\00A0 \2014';
+}
+
+q:before,
+q:after,
+blockquote:before,
+blockquote:after {
+ content: "";
+}
+
+address {
+ display: block;
+ margin-bottom: 20px;
+ font-style: normal;
+ line-height: 20px;
+}
+
+code,
+pre {
+ padding: 0 3px 2px;
+ font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
+ font-size: 12px;
+ color: #333333;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+
+code {
+ padding: 2px 4px;
+ color: #d14;
+ background-color: #f7f7f9;
+ border: 1px solid #e1e1e8;
+}
+
+pre {
+ display: block;
+ padding: 9.5px;
+ margin: 0 0 10px;
+ font-size: 13px;
+ line-height: 20px;
+ word-break: break-all;
+ word-wrap: break-word;
+ white-space: pre;
+ white-space: pre-wrap;
+ background-color: #f5f5f5;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+pre.prettyprint {
+ margin-bottom: 20px;
+}
+
+pre code {
+ padding: 0;
+ color: inherit;
+ background-color: transparent;
+ border: 0;
+}
+
+.pre-scrollable {
+ max-height: 340px;
+ overflow-y: scroll;
+}
+
+form {
+ margin: 0 0 20px;
+}
+
+fieldset {
+ padding: 0;
+ margin: 0;
+ border: 0;
+}
+
+legend {
+ display: block;
+ width: 100%;
+ padding: 0;
+ margin-bottom: 20px;
+ font-size: 21px;
+ line-height: 40px;
+ color: #333333;
+ border: 0;
+ border-bottom: 1px solid #e5e5e5;
+}
+
+legend small {
+ font-size: 15px;
+ color: #999999;
+}
+
+label,
+input,
+button,
+select,
+textarea {
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 20px;
+}
+
+input,
+button,
+select,
+textarea {
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+
+label {
+ display: block;
+ margin-bottom: 5px;
+}
+
+select,
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"],
+.uneditable-input {
+ display: inline-block;
+ height: 20px;
+ padding: 4px 6px;
+ margin-bottom: 9px;
+ font-size: 14px;
+ line-height: 20px;
+ color: #555555;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+
+input,
+textarea {
+ width: 210px;
+}
+
+textarea {
+ height: auto;
+}
+
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"],
+.uneditable-input {
+ background-color: #ffffff;
+ border: 1px solid #cccccc;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
+ -moz-transition: border linear 0.2s, box-shadow linear 0.2s;
+ -o-transition: border linear 0.2s, box-shadow linear 0.2s;
+ transition: border linear 0.2s, box-shadow linear 0.2s;
+}
+
+textarea:focus,
+input[type="text"]:focus,
+input[type="password"]:focus,
+input[type="datetime"]:focus,
+input[type="datetime-local"]:focus,
+input[type="date"]:focus,
+input[type="month"]:focus,
+input[type="time"]:focus,
+input[type="week"]:focus,
+input[type="number"]:focus,
+input[type="email"]:focus,
+input[type="url"]:focus,
+input[type="search"]:focus,
+input[type="tel"]:focus,
+input[type="color"]:focus,
+.uneditable-input:focus {
+ border-color: rgba(82, 168, 236, 0.8);
+ outline: 0;
+ outline: thin dotted \9;
+ /* IE6-9 */
+
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+}
+
+input[type="radio"],
+input[type="checkbox"] {
+ margin: 4px 0 0;
+ margin-top: 1px \9;
+ *margin-top: 0;
+ line-height: normal;
+ cursor: pointer;
+}
+
+input[type="file"],
+input[type="image"],
+input[type="submit"],
+input[type="reset"],
+input[type="button"],
+input[type="radio"],
+input[type="checkbox"] {
+ width: auto;
+}
+
+select,
+input[type="file"] {
+ height: 30px;
+ /* In IE7, the height of the select element cannot be changed by height, only font-size */
+
+ *margin-top: 4px;
+ /* For IE7, add top margin to align select with labels */
+
+ line-height: 30px;
+}
+
+select {
+ width: 220px;
+ background-color: #ffffff;
+ border: 1px solid #bbb;
+}
+
+select[multiple],
+select[size] {
+ height: auto;
+}
+
+select:focus,
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+.uneditable-input,
+.uneditable-textarea {
+ color: #999999;
+ cursor: not-allowed;
+ background-color: #fcfcfc;
+ border-color: #cccccc;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+ -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+}
+
+.uneditable-input {
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+.uneditable-textarea {
+ width: auto;
+ height: auto;
+}
+
+input:-moz-placeholder,
+textarea:-moz-placeholder {
+ color: #999999;
+}
+
+input:-ms-input-placeholder,
+textarea:-ms-input-placeholder {
+ color: #999999;
+}
+
+input::-webkit-input-placeholder,
+textarea::-webkit-input-placeholder {
+ color: #999999;
+}
+
+.radio,
+.checkbox {
+ min-height: 18px;
+ padding-left: 18px;
+}
+
+.radio input[type="radio"],
+.checkbox input[type="checkbox"] {
+ float: left;
+ margin-left: -18px;
+}
+
+.controls > .radio:first-child,
+.controls > .checkbox:first-child {
+ padding-top: 5px;
+}
+
+.radio.inline,
+.checkbox.inline {
+ display: inline-block;
+ padding-top: 5px;
+ margin-bottom: 0;
+ vertical-align: middle;
+}
+
+.radio.inline + .radio.inline,
+.checkbox.inline + .checkbox.inline {
+ margin-left: 10px;
+}
+
+.input-mini {
+ width: 60px;
+}
+
+.input-small {
+ width: 90px;
+}
+
+.input-medium {
+ width: 150px;
+}
+
+.input-large {
+ width: 210px;
+}
+
+.input-xlarge {
+ width: 270px;
+}
+
+.input-xxlarge {
+ width: 530px;
+}
+
+input[class*="span"],
+select[class*="span"],
+textarea[class*="span"],
+.uneditable-input[class*="span"],
+.row-fluid input[class*="span"],
+.row-fluid select[class*="span"],
+.row-fluid textarea[class*="span"],
+.row-fluid .uneditable-input[class*="span"] {
+ float: none;
+ margin-left: 0;
+}
+
+.input-append input[class*="span"],
+.input-append .uneditable-input[class*="span"],
+.input-prepend input[class*="span"],
+.input-prepend .uneditable-input[class*="span"],
+.row-fluid input[class*="span"],
+.row-fluid select[class*="span"],
+.row-fluid textarea[class*="span"],
+.row-fluid .uneditable-input[class*="span"],
+.row-fluid .input-prepend [class*="span"],
+.row-fluid .input-append [class*="span"] {
+ display: inline-block;
+}
+
+input,
+textarea,
+.uneditable-input {
+ margin-left: 0;
+}
+
+.controls-row [class*="span"] + [class*="span"] {
+ margin-left: 20px;
+}
+
+input.span12,
+textarea.span12,
+.uneditable-input.span12 {
+ width: 926px;
+}
+
+input.span11,
+textarea.span11,
+.uneditable-input.span11 {
+ width: 846px;
+}
+
+input.span10,
+textarea.span10,
+.uneditable-input.span10 {
+ width: 766px;
+}
+
+input.span9,
+textarea.span9,
+.uneditable-input.span9 {
+ width: 686px;
+}
+
+input.span8,
+textarea.span8,
+.uneditable-input.span8 {
+ width: 606px;
+}
+
+input.span7,
+textarea.span7,
+.uneditable-input.span7 {
+ width: 526px;
+}
+
+input.span6,
+textarea.span6,
+.uneditable-input.span6 {
+ width: 446px;
+}
+
+input.span5,
+textarea.span5,
+.uneditable-input.span5 {
+ width: 366px;
+}
+
+input.span4,
+textarea.span4,
+.uneditable-input.span4 {
+ width: 286px;
+}
+
+input.span3,
+textarea.span3,
+.uneditable-input.span3 {
+ width: 206px;
+}
+
+input.span2,
+textarea.span2,
+.uneditable-input.span2 {
+ width: 126px;
+}
+
+input.span1,
+textarea.span1,
+.uneditable-input.span1 {
+ width: 46px;
+}
+
+.controls-row {
+ *zoom: 1;
+}
+
+.controls-row:before,
+.controls-row:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.controls-row:after {
+ clear: both;
+}
+
+.controls-row [class*="span"] {
+ float: left;
+}
+
+input[disabled],
+select[disabled],
+textarea[disabled],
+input[readonly],
+select[readonly],
+textarea[readonly] {
+ cursor: not-allowed;
+ background-color: #eeeeee;
+}
+
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"][readonly],
+input[type="checkbox"][readonly] {
+ background-color: transparent;
+}
+
+.control-group.warning > label,
+.control-group.warning .help-block,
+.control-group.warning .help-inline {
+ color: #c09853;
+}
+
+.control-group.warning .checkbox,
+.control-group.warning .radio,
+.control-group.warning input,
+.control-group.warning select,
+.control-group.warning textarea {
+ color: #c09853;
+ border-color: #c09853;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.control-group.warning .checkbox:focus,
+.control-group.warning .radio:focus,
+.control-group.warning input:focus,
+.control-group.warning select:focus,
+.control-group.warning textarea:focus {
+ border-color: #a47e3c;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+}
+
+.control-group.warning .input-prepend .add-on,
+.control-group.warning .input-append .add-on {
+ color: #c09853;
+ background-color: #fcf8e3;
+ border-color: #c09853;
+}
+
+.control-group.error > label,
+.control-group.error .help-block,
+.control-group.error .help-inline {
+ color: #b94a48;
+}
+
+.control-group.error .checkbox,
+.control-group.error .radio,
+.control-group.error input,
+.control-group.error select,
+.control-group.error textarea {
+ color: #b94a48;
+ border-color: #b94a48;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.control-group.error .checkbox:focus,
+.control-group.error .radio:focus,
+.control-group.error input:focus,
+.control-group.error select:focus,
+.control-group.error textarea:focus {
+ border-color: #953b39;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+}
+
+.control-group.error .input-prepend .add-on,
+.control-group.error .input-append .add-on {
+ color: #b94a48;
+ background-color: #f2dede;
+ border-color: #b94a48;
+}
+
+.control-group.success > label,
+.control-group.success .help-block,
+.control-group.success .help-inline {
+ color: #468847;
+}
+
+.control-group.success .checkbox,
+.control-group.success .radio,
+.control-group.success input,
+.control-group.success select,
+.control-group.success textarea {
+ color: #468847;
+ border-color: #468847;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.control-group.success .checkbox:focus,
+.control-group.success .radio:focus,
+.control-group.success input:focus,
+.control-group.success select:focus,
+.control-group.success textarea:focus {
+ border-color: #356635;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+}
+
+.control-group.success .input-prepend .add-on,
+.control-group.success .input-append .add-on {
+ color: #468847;
+ background-color: #dff0d8;
+ border-color: #468847;
+}
+
+input:focus:required:invalid,
+textarea:focus:required:invalid,
+select:focus:required:invalid {
+ color: #b94a48;
+ border-color: #ee5f5b;
+}
+
+input:focus:required:invalid:focus,
+textarea:focus:required:invalid:focus,
+select:focus:required:invalid:focus {
+ border-color: #e9322d;
+ -webkit-box-shadow: 0 0 6px #f8b9b7;
+ -moz-box-shadow: 0 0 6px #f8b9b7;
+ box-shadow: 0 0 6px #f8b9b7;
+}
+
+.form-actions {
+ padding: 19px 20px 20px;
+ margin-top: 20px;
+ margin-bottom: 20px;
+ background-color: #f5f5f5;
+ border-top: 1px solid #e5e5e5;
+ *zoom: 1;
+}
+
+.form-actions:before,
+.form-actions:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.form-actions:after {
+ clear: both;
+}
+
+.help-block,
+.help-inline {
+ color: #595959;
+}
+
+.help-block {
+ display: block;
+ margin-bottom: 10px;
+}
+
+.help-inline {
+ display: inline-block;
+ *display: inline;
+ padding-left: 5px;
+ vertical-align: middle;
+ *zoom: 1;
+}
+
+.input-append,
+.input-prepend {
+ margin-bottom: 5px;
+ font-size: 0;
+ white-space: nowrap;
+}
+
+.input-append input,
+.input-prepend input,
+.input-append select,
+.input-prepend select,
+.input-append .uneditable-input,
+.input-prepend .uneditable-input {
+ position: relative;
+ margin-bottom: 0;
+ *margin-left: 0;
+ font-size: 14px;
+ vertical-align: top;
+ -webkit-border-radius: 0 3px 3px 0;
+ -moz-border-radius: 0 3px 3px 0;
+ border-radius: 0 3px 3px 0;
+}
+
+.input-append input:focus,
+.input-prepend input:focus,
+.input-append select:focus,
+.input-prepend select:focus,
+.input-append .uneditable-input:focus,
+.input-prepend .uneditable-input:focus {
+ z-index: 2;
+}
+
+.input-append .add-on,
+.input-prepend .add-on {
+ display: inline-block;
+ width: auto;
+ height: 20px;
+ min-width: 16px;
+ padding: 4px 5px;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 20px;
+ text-align: center;
+ text-shadow: 0 1px 0 #ffffff;
+ background-color: #eeeeee;
+ border: 1px solid #ccc;
+}
+
+.input-append .add-on,
+.input-prepend .add-on,
+.input-append .btn,
+.input-prepend .btn {
+ margin-left: -1px;
+ vertical-align: top;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.input-append .active,
+.input-prepend .active {
+ background-color: #a9dba9;
+ border-color: #46a546;
+}
+
+.input-prepend .add-on,
+.input-prepend .btn {
+ margin-right: -1px;
+}
+
+.input-prepend .add-on:first-child,
+.input-prepend .btn:first-child {
+ -webkit-border-radius: 3px 0 0 3px;
+ -moz-border-radius: 3px 0 0 3px;
+ border-radius: 3px 0 0 3px;
+}
+
+.input-append input,
+.input-append select,
+.input-append .uneditable-input {
+ -webkit-border-radius: 3px 0 0 3px;
+ -moz-border-radius: 3px 0 0 3px;
+ border-radius: 3px 0 0 3px;
+}
+
+.input-append .add-on:last-child,
+.input-append .btn:last-child {
+ -webkit-border-radius: 0 3px 3px 0;
+ -moz-border-radius: 0 3px 3px 0;
+ border-radius: 0 3px 3px 0;
+}
+
+.input-prepend.input-append input,
+.input-prepend.input-append select,
+.input-prepend.input-append .uneditable-input {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.input-prepend.input-append .add-on:first-child,
+.input-prepend.input-append .btn:first-child {
+ margin-right: -1px;
+ -webkit-border-radius: 3px 0 0 3px;
+ -moz-border-radius: 3px 0 0 3px;
+ border-radius: 3px 0 0 3px;
+}
+
+.input-prepend.input-append .add-on:last-child,
+.input-prepend.input-append .btn:last-child {
+ margin-left: -1px;
+ -webkit-border-radius: 0 3px 3px 0;
+ -moz-border-radius: 0 3px 3px 0;
+ border-radius: 0 3px 3px 0;
+}
+
+input.search-query {
+ padding-right: 14px;
+ padding-right: 4px \9;
+ padding-left: 14px;
+ padding-left: 4px \9;
+ /* IE7-8 doesn't have border-radius, so don't indent the padding */
+
+ margin-bottom: 0;
+ -webkit-border-radius: 15px;
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+}
+
+/* Allow for input prepend/append in search forms */
+
+.form-search .input-append .search-query,
+.form-search .input-prepend .search-query {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.form-search .input-append .search-query {
+ -webkit-border-radius: 14px 0 0 14px;
+ -moz-border-radius: 14px 0 0 14px;
+ border-radius: 14px 0 0 14px;
+}
+
+.form-search .input-append .btn {
+ -webkit-border-radius: 0 14px 14px 0;
+ -moz-border-radius: 0 14px 14px 0;
+ border-radius: 0 14px 14px 0;
+}
+
+.form-search .input-prepend .search-query {
+ -webkit-border-radius: 0 14px 14px 0;
+ -moz-border-radius: 0 14px 14px 0;
+ border-radius: 0 14px 14px 0;
+}
+
+.form-search .input-prepend .btn {
+ -webkit-border-radius: 14px 0 0 14px;
+ -moz-border-radius: 14px 0 0 14px;
+ border-radius: 14px 0 0 14px;
+}
+
+.form-search input,
+.form-inline input,
+.form-horizontal input,
+.form-search textarea,
+.form-inline textarea,
+.form-horizontal textarea,
+.form-search select,
+.form-inline select,
+.form-horizontal select,
+.form-search .help-inline,
+.form-inline .help-inline,
+.form-horizontal .help-inline,
+.form-search .uneditable-input,
+.form-inline .uneditable-input,
+.form-horizontal .uneditable-input,
+.form-search .input-prepend,
+.form-inline .input-prepend,
+.form-horizontal .input-prepend,
+.form-search .input-append,
+.form-inline .input-append,
+.form-horizontal .input-append {
+ display: inline-block;
+ *display: inline;
+ margin-bottom: 0;
+ vertical-align: middle;
+ *zoom: 1;
+}
+
+.form-search .hide,
+.form-inline .hide,
+.form-horizontal .hide {
+ display: none;
+}
+
+.form-search label,
+.form-inline label,
+.form-search .btn-group,
+.form-inline .btn-group {
+ display: inline-block;
+}
+
+.form-search .input-append,
+.form-inline .input-append,
+.form-search .input-prepend,
+.form-inline .input-prepend {
+ margin-bottom: 0;
+}
+
+.form-search .radio,
+.form-search .checkbox,
+.form-inline .radio,
+.form-inline .checkbox {
+ padding-left: 0;
+ margin-bottom: 0;
+ vertical-align: middle;
+}
+
+.form-search .radio input[type="radio"],
+.form-search .checkbox input[type="checkbox"],
+.form-inline .radio input[type="radio"],
+.form-inline .checkbox input[type="checkbox"] {
+ float: left;
+ margin-right: 3px;
+ margin-left: 0;
+}
+
+.control-group {
+ margin-bottom: 10px;
+}
+
+legend + .control-group {
+ margin-top: 20px;
+ -webkit-margin-top-collapse: separate;
+}
+
+.form-horizontal .control-group {
+ margin-bottom: 20px;
+ *zoom: 1;
+}
+
+.form-horizontal .control-group:before,
+.form-horizontal .control-group:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.form-horizontal .control-group:after {
+ clear: both;
+}
+
+.form-horizontal .control-label {
+ float: left;
+ width: 140px;
+ padding-top: 5px;
+ text-align: right;
+}
+
+.form-horizontal .controls {
+ *display: inline-block;
+ *padding-left: 20px;
+ margin-left: 160px;
+ *margin-left: 0;
+}
+
+.form-horizontal .controls:first-child {
+ *padding-left: 160px;
+}
+
+.form-horizontal .help-block {
+ margin-top: 10px;
+ margin-bottom: 0;
+}
+
+.form-horizontal .form-actions {
+ padding-left: 160px;
+}
+
+table {
+ max-width: 100%;
+ background-color: transparent;
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+.table {
+ width: 100%;
+ margin-bottom: 20px;
+}
+
+.table th,
+.table td {
+ padding: 8px;
+ line-height: 20px;
+ text-align: left;
+ vertical-align: top;
+ border-top: 1px solid #dddddd;
+}
+
+.table th {
+ font-weight: bold;
+}
+
+.table thead th {
+ vertical-align: bottom;
+}
+
+.table caption + thead tr:first-child th,
+.table caption + thead tr:first-child td,
+.table colgroup + thead tr:first-child th,
+.table colgroup + thead tr:first-child td,
+.table thead:first-child tr:first-child th,
+.table thead:first-child tr:first-child td {
+ border-top: 0;
+}
+
+.table tbody + tbody {
+ border-top: 2px solid #dddddd;
+}
+
+.table-condensed th,
+.table-condensed td {
+ padding: 4px 5px;
+}
+
+.table-bordered {
+ border: 1px solid #dddddd;
+ border-collapse: separate;
+ *border-collapse: collapse;
+ border-left: 0;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.table-bordered th,
+.table-bordered td {
+ border-left: 1px solid #dddddd;
+}
+
+.table-bordered caption + thead tr:first-child th,
+.table-bordered caption + tbody tr:first-child th,
+.table-bordered caption + tbody tr:first-child td,
+.table-bordered colgroup + thead tr:first-child th,
+.table-bordered colgroup + tbody tr:first-child th,
+.table-bordered colgroup + tbody tr:first-child td,
+.table-bordered thead:first-child tr:first-child th,
+.table-bordered tbody:first-child tr:first-child th,
+.table-bordered tbody:first-child tr:first-child td {
+ border-top: 0;
+}
+
+.table-bordered thead:first-child tr:first-child th:first-child,
+.table-bordered tbody:first-child tr:first-child td:first-child {
+ -webkit-border-top-left-radius: 4px;
+ border-top-left-radius: 4px;
+ -moz-border-radius-topleft: 4px;
+}
+
+.table-bordered thead:first-child tr:first-child th:last-child,
+.table-bordered tbody:first-child tr:first-child td:last-child {
+ -webkit-border-top-right-radius: 4px;
+ border-top-right-radius: 4px;
+ -moz-border-radius-topright: 4px;
+}
+
+.table-bordered thead:last-child tr:last-child th:first-child,
+.table-bordered tbody:last-child tr:last-child td:first-child,
+.table-bordered tfoot:last-child tr:last-child td:first-child {
+ -webkit-border-radius: 0 0 0 4px;
+ -moz-border-radius: 0 0 0 4px;
+ border-radius: 0 0 0 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomleft: 4px;
+}
+
+.table-bordered thead:last-child tr:last-child th:last-child,
+.table-bordered tbody:last-child tr:last-child td:last-child,
+.table-bordered tfoot:last-child tr:last-child td:last-child {
+ -webkit-border-bottom-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+}
+
+.table-bordered caption + thead tr:first-child th:first-child,
+.table-bordered caption + tbody tr:first-child td:first-child,
+.table-bordered colgroup + thead tr:first-child th:first-child,
+.table-bordered colgroup + tbody tr:first-child td:first-child {
+ -webkit-border-top-left-radius: 4px;
+ border-top-left-radius: 4px;
+ -moz-border-radius-topleft: 4px;
+}
+
+.table-bordered caption + thead tr:first-child th:last-child,
+.table-bordered caption + tbody tr:first-child td:last-child,
+.table-bordered colgroup + thead tr:first-child th:last-child,
+.table-bordered colgroup + tbody tr:first-child td:last-child {
+ -webkit-border-top-right-radius: 4px;
+ border-top-right-radius: 4px;
+ -moz-border-right-topleft: 4px;
+}
+
+.table-striped tbody tr:nth-child(odd) td,
+.table-striped tbody tr:nth-child(odd) th {
+ background-color: #f9f9f9;
+}
+
+.table-hover tbody tr:hover td,
+.table-hover tbody tr:hover th {
+ background-color: #f5f5f5;
+}
+
+table [class*=span],
+.row-fluid table [class*=span] {
+ display: table-cell;
+ float: none;
+ margin-left: 0;
+}
+
+table .span1 {
+ float: none;
+ width: 44px;
+ margin-left: 0;
+}
+
+table .span2 {
+ float: none;
+ width: 124px;
+ margin-left: 0;
+}
+
+table .span3 {
+ float: none;
+ width: 204px;
+ margin-left: 0;
+}
+
+table .span4 {
+ float: none;
+ width: 284px;
+ margin-left: 0;
+}
+
+table .span5 {
+ float: none;
+ width: 364px;
+ margin-left: 0;
+}
+
+table .span6 {
+ float: none;
+ width: 444px;
+ margin-left: 0;
+}
+
+table .span7 {
+ float: none;
+ width: 524px;
+ margin-left: 0;
+}
+
+table .span8 {
+ float: none;
+ width: 604px;
+ margin-left: 0;
+}
+
+table .span9 {
+ float: none;
+ width: 684px;
+ margin-left: 0;
+}
+
+table .span10 {
+ float: none;
+ width: 764px;
+ margin-left: 0;
+}
+
+table .span11 {
+ float: none;
+ width: 844px;
+ margin-left: 0;
+}
+
+table .span12 {
+ float: none;
+ width: 924px;
+ margin-left: 0;
+}
+
+table .span13 {
+ float: none;
+ width: 1004px;
+ margin-left: 0;
+}
+
+table .span14 {
+ float: none;
+ width: 1084px;
+ margin-left: 0;
+}
+
+table .span15 {
+ float: none;
+ width: 1164px;
+ margin-left: 0;
+}
+
+table .span16 {
+ float: none;
+ width: 1244px;
+ margin-left: 0;
+}
+
+table .span17 {
+ float: none;
+ width: 1324px;
+ margin-left: 0;
+}
+
+table .span18 {
+ float: none;
+ width: 1404px;
+ margin-left: 0;
+}
+
+table .span19 {
+ float: none;
+ width: 1484px;
+ margin-left: 0;
+}
+
+table .span20 {
+ float: none;
+ width: 1564px;
+ margin-left: 0;
+}
+
+table .span21 {
+ float: none;
+ width: 1644px;
+ margin-left: 0;
+}
+
+table .span22 {
+ float: none;
+ width: 1724px;
+ margin-left: 0;
+}
+
+table .span23 {
+ float: none;
+ width: 1804px;
+ margin-left: 0;
+}
+
+table .span24 {
+ float: none;
+ width: 1884px;
+ margin-left: 0;
+}
+
+.table tbody tr.success td {
+ background-color: #dff0d8;
+}
+
+.table tbody tr.error td {
+ background-color: #f2dede;
+}
+
+.table tbody tr.info td {
+ background-color: #d9edf7;
+}
+
+[class^="icon-"],
+[class*=" icon-"] {
+ display: inline-block;
+ width: 14px;
+ height: 14px;
+ margin-top: 1px;
+ *margin-right: .3em;
+ line-height: 14px;
+ vertical-align: text-top;
+ background-image: url("../img/glyphicons-halflings.png");
+ background-position: 14px 14px;
+ background-repeat: no-repeat;
+}
+
+/* White icons with optional class, or on hover/active states of certain elements */
+
+.icon-white,
+.nav > .active > a > [class^="icon-"],
+.nav > .active > a > [class*=" icon-"],
+.dropdown-menu > li > a:hover > [class^="icon-"],
+.dropdown-menu > li > a:hover > [class*=" icon-"],
+.dropdown-menu > .active > a > [class^="icon-"],
+.dropdown-menu > .active > a > [class*=" icon-"] {
+ background-image: url("../img/glyphicons-halflings-white.png");
+}
+
+.icon-glass {
+ background-position: 0 0;
+}
+
+.icon-music {
+ background-position: -24px 0;
+}
+
+.icon-search {
+ background-position: -48px 0;
+}
+
+.icon-envelope {
+ background-position: -72px 0;
+}
+
+.icon-heart {
+ background-position: -96px 0;
+}
+
+.icon-star {
+ background-position: -120px 0;
+}
+
+.icon-star-empty {
+ background-position: -144px 0;
+}
+
+.icon-user {
+ background-position: -168px 0;
+}
+
+.icon-film {
+ background-position: -192px 0;
+}
+
+.icon-th-large {
+ background-position: -216px 0;
+}
+
+.icon-th {
+ background-position: -240px 0;
+}
+
+.icon-th-list {
+ background-position: -264px 0;
+}
+
+.icon-ok {
+ background-position: -288px 0;
+}
+
+.icon-remove {
+ background-position: -312px 0;
+}
+
+.icon-zoom-in {
+ background-position: -336px 0;
+}
+
+.icon-zoom-out {
+ background-position: -360px 0;
+}
+
+.icon-off {
+ background-position: -384px 0;
+}
+
+.icon-signal {
+ background-position: -408px 0;
+}
+
+.icon-cog {
+ background-position: -432px 0;
+}
+
+.icon-trash {
+ background-position: -456px 0;
+}
+
+.icon-home {
+ background-position: 0 -24px;
+}
+
+.icon-file {
+ background-position: -24px -24px;
+}
+
+.icon-time {
+ background-position: -48px -24px;
+}
+
+.icon-road {
+ background-position: -72px -24px;
+}
+
+.icon-download-alt {
+ background-position: -96px -24px;
+}
+
+.icon-download {
+ background-position: -120px -24px;
+}
+
+.icon-upload {
+ background-position: -144px -24px;
+}
+
+.icon-inbox {
+ background-position: -168px -24px;
+}
+
+.icon-play-circle {
+ background-position: -192px -24px;
+}
+
+.icon-repeat {
+ background-position: -216px -24px;
+}
+
+.icon-refresh {
+ background-position: -240px -24px;
+}
+
+.icon-list-alt {
+ background-position: -264px -24px;
+}
+
+.icon-lock {
+ background-position: -287px -24px;
+}
+
+.icon-flag {
+ background-position: -312px -24px;
+}
+
+.icon-headphones {
+ background-position: -336px -24px;
+}
+
+.icon-volume-off {
+ background-position: -360px -24px;
+}
+
+.icon-volume-down {
+ background-position: -384px -24px;
+}
+
+.icon-volume-up {
+ background-position: -408px -24px;
+}
+
+.icon-qrcode {
+ background-position: -432px -24px;
+}
+
+.icon-barcode {
+ background-position: -456px -24px;
+}
+
+.icon-tag {
+ background-position: 0 -48px;
+}
+
+.icon-tags {
+ background-position: -25px -48px;
+}
+
+.icon-book {
+ background-position: -48px -48px;
+}
+
+.icon-bookmark {
+ background-position: -72px -48px;
+}
+
+.icon-print {
+ background-position: -96px -48px;
+}
+
+.icon-camera {
+ background-position: -120px -48px;
+}
+
+.icon-font {
+ background-position: -144px -48px;
+}
+
+.icon-bold {
+ background-position: -167px -48px;
+}
+
+.icon-italic {
+ background-position: -192px -48px;
+}
+
+.icon-text-height {
+ background-position: -216px -48px;
+}
+
+.icon-text-width {
+ background-position: -240px -48px;
+}
+
+.icon-align-left {
+ background-position: -264px -48px;
+}
+
+.icon-align-center {
+ background-position: -288px -48px;
+}
+
+.icon-align-right {
+ background-position: -312px -48px;
+}
+
+.icon-align-justify {
+ background-position: -336px -48px;
+}
+
+.icon-list {
+ background-position: -360px -48px;
+}
+
+.icon-indent-left {
+ background-position: -384px -48px;
+}
+
+.icon-indent-right {
+ background-position: -408px -48px;
+}
+
+.icon-facetime-video {
+ background-position: -432px -48px;
+}
+
+.icon-picture {
+ background-position: -456px -48px;
+}
+
+.icon-pencil {
+ background-position: 0 -72px;
+}
+
+.icon-map-marker {
+ background-position: -24px -72px;
+}
+
+.icon-adjust {
+ background-position: -48px -72px;
+}
+
+.icon-tint {
+ background-position: -72px -72px;
+}
+
+.icon-edit {
+ background-position: -96px -72px;
+}
+
+.icon-share {
+ background-position: -120px -72px;
+}
+
+.icon-check {
+ background-position: -144px -72px;
+}
+
+.icon-move {
+ background-position: -168px -72px;
+}
+
+.icon-step-backward {
+ background-position: -192px -72px;
+}
+
+.icon-fast-backward {
+ background-position: -216px -72px;
+}
+
+.icon-backward {
+ background-position: -240px -72px;
+}
+
+.icon-play {
+ background-position: -264px -72px;
+}
+
+.icon-pause {
+ background-position: -288px -72px;
+}
+
+.icon-stop {
+ background-position: -312px -72px;
+}
+
+.icon-forward {
+ background-position: -336px -72px;
+}
+
+.icon-fast-forward {
+ background-position: -360px -72px;
+}
+
+.icon-step-forward {
+ background-position: -384px -72px;
+}
+
+.icon-eject {
+ background-position: -408px -72px;
+}
+
+.icon-chevron-left {
+ background-position: -432px -72px;
+}
+
+.icon-chevron-right {
+ background-position: -456px -72px;
+}
+
+.icon-plus-sign {
+ background-position: 0 -96px;
+}
+
+.icon-minus-sign {
+ background-position: -24px -96px;
+}
+
+.icon-remove-sign {
+ background-position: -48px -96px;
+}
+
+.icon-ok-sign {
+ background-position: -72px -96px;
+}
+
+.icon-question-sign {
+ background-position: -96px -96px;
+}
+
+.icon-info-sign {
+ background-position: -120px -96px;
+}
+
+.icon-screenshot {
+ background-position: -144px -96px;
+}
+
+.icon-remove-circle {
+ background-position: -168px -96px;
+}
+
+.icon-ok-circle {
+ background-position: -192px -96px;
+}
+
+.icon-ban-circle {
+ background-position: -216px -96px;
+}
+
+.icon-arrow-left {
+ background-position: -240px -96px;
+}
+
+.icon-arrow-right {
+ background-position: -264px -96px;
+}
+
+.icon-arrow-up {
+ background-position: -289px -96px;
+}
+
+.icon-arrow-down {
+ background-position: -312px -96px;
+}
+
+.icon-share-alt {
+ background-position: -336px -96px;
+}
+
+.icon-resize-full {
+ background-position: -360px -96px;
+}
+
+.icon-resize-small {
+ background-position: -384px -96px;
+}
+
+.icon-plus {
+ background-position: -408px -96px;
+}
+
+.icon-minus {
+ background-position: -433px -96px;
+}
+
+.icon-asterisk {
+ background-position: -456px -96px;
+}
+
+.icon-exclamation-sign {
+ background-position: 0 -120px;
+}
+
+.icon-gift {
+ background-position: -24px -120px;
+}
+
+.icon-leaf {
+ background-position: -48px -120px;
+}
+
+.icon-fire {
+ background-position: -72px -120px;
+}
+
+.icon-eye-open {
+ background-position: -96px -120px;
+}
+
+.icon-eye-close {
+ background-position: -120px -120px;
+}
+
+.icon-warning-sign {
+ background-position: -144px -120px;
+}
+
+.icon-plane {
+ background-position: -168px -120px;
+}
+
+.icon-calendar {
+ background-position: -192px -120px;
+}
+
+.icon-random {
+ width: 16px;
+ background-position: -216px -120px;
+}
+
+.icon-comment {
+ background-position: -240px -120px;
+}
+
+.icon-magnet {
+ background-position: -264px -120px;
+}
+
+.icon-chevron-up {
+ background-position: -288px -120px;
+}
+
+.icon-chevron-down {
+ background-position: -313px -119px;
+}
+
+.icon-retweet {
+ background-position: -336px -120px;
+}
+
+.icon-shopping-cart {
+ background-position: -360px -120px;
+}
+
+.icon-folder-close {
+ background-position: -384px -120px;
+}
+
+.icon-folder-open {
+ width: 16px;
+ background-position: -408px -120px;
+}
+
+.icon-resize-vertical {
+ background-position: -432px -119px;
+}
+
+.icon-resize-horizontal {
+ background-position: -456px -118px;
+}
+
+.icon-hdd {
+ background-position: 0 -144px;
+}
+
+.icon-bullhorn {
+ background-position: -24px -144px;
+}
+
+.icon-bell {
+ background-position: -48px -144px;
+}
+
+.icon-certificate {
+ background-position: -72px -144px;
+}
+
+.icon-thumbs-up {
+ background-position: -96px -144px;
+}
+
+.icon-thumbs-down {
+ background-position: -120px -144px;
+}
+
+.icon-hand-right {
+ background-position: -144px -144px;
+}
+
+.icon-hand-left {
+ background-position: -168px -144px;
+}
+
+.icon-hand-up {
+ background-position: -192px -144px;
+}
+
+.icon-hand-down {
+ background-position: -216px -144px;
+}
+
+.icon-circle-arrow-right {
+ background-position: -240px -144px;
+}
+
+.icon-circle-arrow-left {
+ background-position: -264px -144px;
+}
+
+.icon-circle-arrow-up {
+ background-position: -288px -144px;
+}
+
+.icon-circle-arrow-down {
+ background-position: -312px -144px;
+}
+
+.icon-globe {
+ background-position: -336px -144px;
+}
+
+.icon-wrench {
+ background-position: -360px -144px;
+}
+
+.icon-tasks {
+ background-position: -384px -144px;
+}
+
+.icon-filter {
+ background-position: -408px -144px;
+}
+
+.icon-briefcase {
+ background-position: -432px -144px;
+}
+
+.icon-fullscreen {
+ background-position: -456px -144px;
+}
+
+.dropup,
+.dropdown {
+ position: relative;
+}
+
+.dropdown-toggle {
+ *margin-bottom: -3px;
+}
+
+.dropdown-toggle:active,
+.open .dropdown-toggle {
+ outline: 0;
+}
+
+.caret {
+ display: inline-block;
+ width: 0;
+ height: 0;
+ vertical-align: top;
+ border-top: 4px solid #000000;
+ border-right: 4px solid transparent;
+ border-left: 4px solid transparent;
+ content: "";
+}
+
+.dropdown .caret {
+ margin-top: 8px;
+ margin-left: 2px;
+}
+
+.dropdown-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: 1000;
+ display: none;
+ float: left;
+ min-width: 160px;
+ padding: 5px 0;
+ margin: 2px 0 0;
+ list-style: none;
+ background-color: #ffffff;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ *border-right-width: 2px;
+ *border-bottom-width: 2px;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding;
+ background-clip: padding-box;
+}
+
+.dropdown-menu.pull-right {
+ right: 0;
+ left: auto;
+}
+
+.dropdown-menu .divider {
+ *width: 100%;
+ height: 1px;
+ margin: 9px 1px;
+ *margin: -5px 0 5px;
+ overflow: hidden;
+ background-color: #e5e5e5;
+ border-bottom: 1px solid #ffffff;
+}
+
+.dropdown-menu a {
+ display: block;
+ padding: 3px 20px;
+ clear: both;
+ font-weight: normal;
+ line-height: 20px;
+ color: #333333;
+ white-space: nowrap;
+}
+
+.dropdown-menu li > a:hover,
+.dropdown-menu li > a:focus,
+.dropdown-submenu:hover > a {
+ color: #ffffff;
+ text-decoration: none;
+ background-color: #0088cc;
+ background-color: #0081c2;
+ background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
+ background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
+ background-image: -o-linear-gradient(top, #0088cc, #0077b3);
+ background-image: linear-gradient(to bottom, #0088cc, #0077b3);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
+}
+
+.dropdown-menu .active > a,
+.dropdown-menu .active > a:hover {
+ color: #ffffff;
+ text-decoration: none;
+ background-color: #0088cc;
+ background-color: #0081c2;
+ background-image: linear-gradient(to bottom, #0088cc, #0077b3);
+ background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
+ background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
+ background-image: -o-linear-gradient(top, #0088cc, #0077b3);
+ background-repeat: repeat-x;
+ outline: 0;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
+}
+
+.dropdown-menu .disabled > a,
+.dropdown-menu .disabled > a:hover {
+ color: #999999;
+}
+
+.dropdown-menu .disabled > a:hover {
+ text-decoration: none;
+ cursor: default;
+ background-color: transparent;
+}
+
+.open {
+ *z-index: 1000;
+}
+
+.open > .dropdown-menu {
+ display: block;
+}
+
+.pull-right > .dropdown-menu {
+ right: 0;
+ left: auto;
+}
+
+.dropup .caret,
+.navbar-fixed-bottom .dropdown .caret {
+ border-top: 0;
+ border-bottom: 4px solid #000000;
+ content: "\2191";
+}
+
+.dropup .dropdown-menu,
+.navbar-fixed-bottom .dropdown .dropdown-menu {
+ top: auto;
+ bottom: 100%;
+ margin-bottom: 1px;
+}
+
+.dropdown-submenu {
+ position: relative;
+}
+
+.dropdown-submenu > .dropdown-menu {
+ top: 0;
+ left: 100%;
+ margin-top: -6px;
+ margin-left: -1px;
+ -webkit-border-radius: 0 6px 6px 6px;
+ -moz-border-radius: 0 6px 6px 6px;
+ border-radius: 0 6px 6px 6px;
+}
+
+.dropdown-submenu:hover .dropdown-menu {
+ display: block;
+}
+
+.dropdown-submenu > a:after {
+ display: block;
+ float: right;
+ width: 0;
+ height: 0;
+ margin-top: 5px;
+ margin-right: -10px;
+ border-color: transparent;
+ border-left-color: #cccccc;
+ border-style: solid;
+ border-width: 5px 0 5px 5px;
+ content: " ";
+}
+
+.dropdown-submenu:hover > a:after {
+ border-left-color: #ffffff;
+}
+
+.dropdown .dropdown-menu .nav-header {
+ padding-right: 20px;
+ padding-left: 20px;
+}
+
+.typeahead {
+ margin-top: 2px;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.well {
+ min-height: 20px;
+ padding: 19px;
+ margin-bottom: 20px;
+ background-color: #f5f5f5;
+ border: 1px solid #e3e3e3;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+}
+
+.well blockquote {
+ border-color: #ddd;
+ border-color: rgba(0, 0, 0, 0.15);
+}
+
+.well-large {
+ padding: 24px;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+
+.well-small {
+ padding: 9px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+
+.fade {
+ opacity: 0;
+ -webkit-transition: opacity 0.15s linear;
+ -moz-transition: opacity 0.15s linear;
+ -o-transition: opacity 0.15s linear;
+ transition: opacity 0.15s linear;
+}
+
+.fade.in {
+ opacity: 1;
+}
+
+.collapse {
+ position: relative;
+ height: 0;
+ overflow: hidden;
+ overflow: visible \9;
+ -webkit-transition: height 0.35s ease;
+ -moz-transition: height 0.35s ease;
+ -o-transition: height 0.35s ease;
+ transition: height 0.35s ease;
+}
+
+.collapse.in {
+ height: auto;
+}
+
+.close {
+ float: right;
+ font-size: 20px;
+ font-weight: bold;
+ line-height: 20px;
+ color: #000000;
+ text-shadow: 0 1px 0 #ffffff;
+ opacity: 0.2;
+ filter: alpha(opacity=20);
+}
+
+.close:hover {
+ color: #000000;
+ text-decoration: none;
+ cursor: pointer;
+ opacity: 0.4;
+ filter: alpha(opacity=40);
+}
+
+button.close {
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
+}
+
+.btn {
+ display: inline-block;
+ *display: inline;
+ padding: 4px 14px;
+ margin-bottom: 0;
+ *margin-left: .3em;
+ font-size: 14px;
+ line-height: 20px;
+ *line-height: 20px;
+ color: #333333;
+ text-align: center;
+ text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
+ vertical-align: middle;
+ cursor: pointer;
+ background-color: #f5f5f5;
+ *background-color: #e6e6e6;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
+ background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
+ background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
+ background-image: linear-gradient(to bottom, #ffffff, #e6e6e6);
+ background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
+ background-repeat: repeat-x;
+ border: 1px solid #bbbbbb;
+ *border: 0;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ border-color: #e6e6e6 #e6e6e6 #bfbfbf;
+ border-bottom-color: #a2a2a2;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+ *zoom: 1;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn:hover,
+.btn:active,
+.btn.active,
+.btn.disabled,
+.btn[disabled] {
+ color: #333333;
+ background-color: #e6e6e6;
+ *background-color: #d9d9d9;
+}
+
+.btn:active,
+.btn.active {
+ background-color: #cccccc \9;
+}
+
+.btn:first-child {
+ *margin-left: 0;
+}
+
+.btn:hover {
+ color: #333333;
+ text-decoration: none;
+ background-color: #e6e6e6;
+ *background-color: #d9d9d9;
+ /* Buttons in IE7 don't get borders, so darken on hover */
+
+ background-position: 0 -15px;
+ -webkit-transition: background-position 0.1s linear;
+ -moz-transition: background-position 0.1s linear;
+ -o-transition: background-position 0.1s linear;
+ transition: background-position 0.1s linear;
+}
+
+.btn:focus {
+ outline: thin dotted #333;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+.btn.active,
+.btn:active {
+ background-color: #e6e6e6;
+ background-color: #d9d9d9 \9;
+ background-image: none;
+ outline: 0;
+ -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn.disabled,
+.btn[disabled] {
+ cursor: default;
+ background-color: #e6e6e6;
+ background-image: none;
+ opacity: 0.65;
+ filter: alpha(opacity=65);
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+.btn-large {
+ padding: 9px 14px;
+ font-size: 16px;
+ line-height: normal;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+
+.btn-large [class^="icon-"] {
+ margin-top: 2px;
+}
+
+.btn-small {
+ padding: 3px 9px;
+ font-size: 12px;
+ line-height: 18px;
+}
+
+.btn-small [class^="icon-"] {
+ margin-top: 0;
+}
+
+.btn-mini {
+ padding: 2px 6px;
+ font-size: 11px;
+ line-height: 16px;
+}
+
+.btn-block {
+ display: block;
+ width: 100%;
+ padding-right: 0;
+ padding-left: 0;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+.btn-block + .btn-block {
+ margin-top: 5px;
+}
+
+.btn-primary.active,
+.btn-warning.active,
+.btn-danger.active,
+.btn-success.active,
+.btn-info.active,
+.btn-inverse.active {
+ color: rgba(255, 255, 255, 0.75);
+}
+
+.btn {
+ border-color: #c5c5c5;
+ border-color: rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.15) rgba(0, 0, 0, 0.25);
+}
+
+.btn-primary {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #006dcc;
+ *background-color: #0044cc;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
+ background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
+ background-image: -o-linear-gradient(top, #0088cc, #0044cc);
+ background-image: linear-gradient(to bottom, #0088cc, #0044cc);
+ background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
+ background-repeat: repeat-x;
+ border-color: #0044cc #0044cc #002a80;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0044cc', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-primary:hover,
+.btn-primary:active,
+.btn-primary.active,
+.btn-primary.disabled,
+.btn-primary[disabled] {
+ color: #ffffff;
+ background-color: #0044cc;
+ *background-color: #003bb3;
+}
+
+.btn-primary:active,
+.btn-primary.active {
+ background-color: #003399 \9;
+}
+
+.btn-warning {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #faa732;
+ *background-color: #f89406;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
+ background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
+ background-image: -o-linear-gradient(top, #fbb450, #f89406);
+ background-image: linear-gradient(to bottom, #fbb450, #f89406);
+ background-image: -moz-linear-gradient(top, #fbb450, #f89406);
+ background-repeat: repeat-x;
+ border-color: #f89406 #f89406 #ad6704;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-warning:hover,
+.btn-warning:active,
+.btn-warning.active,
+.btn-warning.disabled,
+.btn-warning[disabled] {
+ color: #ffffff;
+ background-color: #f89406;
+ *background-color: #df8505;
+}
+
+.btn-warning:active,
+.btn-warning.active {
+ background-color: #c67605 \9;
+}
+
+.btn-danger {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #da4f49;
+ *background-color: #bd362f;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));
+ background-image: -webkit-linear-gradient(top, #ee5f5b, #bd362f);
+ background-image: -o-linear-gradient(top, #ee5f5b, #bd362f);
+ background-image: linear-gradient(to bottom, #ee5f5b, #bd362f);
+ background-image: -moz-linear-gradient(top, #ee5f5b, #bd362f);
+ background-repeat: repeat-x;
+ border-color: #bd362f #bd362f #802420;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffbd362f', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-danger:hover,
+.btn-danger:active,
+.btn-danger.active,
+.btn-danger.disabled,
+.btn-danger[disabled] {
+ color: #ffffff;
+ background-color: #bd362f;
+ *background-color: #a9302a;
+}
+
+.btn-danger:active,
+.btn-danger.active {
+ background-color: #942a25 \9;
+}
+
+.btn-success {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #5bb75b;
+ *background-color: #51a351;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));
+ background-image: -webkit-linear-gradient(top, #62c462, #51a351);
+ background-image: -o-linear-gradient(top, #62c462, #51a351);
+ background-image: linear-gradient(to bottom, #62c462, #51a351);
+ background-image: -moz-linear-gradient(top, #62c462, #51a351);
+ background-repeat: repeat-x;
+ border-color: #51a351 #51a351 #387038;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff51a351', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-success:hover,
+.btn-success:active,
+.btn-success.active,
+.btn-success.disabled,
+.btn-success[disabled] {
+ color: #ffffff;
+ background-color: #51a351;
+ *background-color: #499249;
+}
+
+.btn-success:active,
+.btn-success.active {
+ background-color: #408140 \9;
+}
+
+.btn-info {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #49afcd;
+ *background-color: #2f96b4;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));
+ background-image: -webkit-linear-gradient(top, #5bc0de, #2f96b4);
+ background-image: -o-linear-gradient(top, #5bc0de, #2f96b4);
+ background-image: linear-gradient(to bottom, #5bc0de, #2f96b4);
+ background-image: -moz-linear-gradient(top, #5bc0de, #2f96b4);
+ background-repeat: repeat-x;
+ border-color: #2f96b4 #2f96b4 #1f6377;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2f96b4', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-info:hover,
+.btn-info:active,
+.btn-info.active,
+.btn-info.disabled,
+.btn-info[disabled] {
+ color: #ffffff;
+ background-color: #2f96b4;
+ *background-color: #2a85a0;
+}
+
+.btn-info:active,
+.btn-info.active {
+ background-color: #24748c \9;
+}
+
+.btn-inverse {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #363636;
+ *background-color: #222222;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222));
+ background-image: -webkit-linear-gradient(top, #444444, #222222);
+ background-image: -o-linear-gradient(top, #444444, #222222);
+ background-image: linear-gradient(to bottom, #444444, #222222);
+ background-image: -moz-linear-gradient(top, #444444, #222222);
+ background-repeat: repeat-x;
+ border-color: #222222 #222222 #000000;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff444444', endColorstr='#ff222222', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.btn-inverse:hover,
+.btn-inverse:active,
+.btn-inverse.active,
+.btn-inverse.disabled,
+.btn-inverse[disabled] {
+ color: #ffffff;
+ background-color: #222222;
+ *background-color: #151515;
+}
+
+.btn-inverse:active,
+.btn-inverse.active {
+ background-color: #080808 \9;
+}
+
+button.btn,
+input[type="submit"].btn {
+ *padding-top: 3px;
+ *padding-bottom: 3px;
+}
+
+button.btn::-moz-focus-inner,
+input[type="submit"].btn::-moz-focus-inner {
+ padding: 0;
+ border: 0;
+}
+
+button.btn.btn-large,
+input[type="submit"].btn.btn-large {
+ *padding-top: 7px;
+ *padding-bottom: 7px;
+}
+
+button.btn.btn-small,
+input[type="submit"].btn.btn-small {
+ *padding-top: 3px;
+ *padding-bottom: 3px;
+}
+
+button.btn.btn-mini,
+input[type="submit"].btn.btn-mini {
+ *padding-top: 1px;
+ *padding-bottom: 1px;
+}
+
+.btn-link,
+.btn-link:active {
+ background-color: transparent;
+ background-image: none;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+
+.btn-link {
+ color: #0088cc;
+ cursor: pointer;
+ border-color: transparent;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.btn-link:hover {
+ color: #005580;
+ text-decoration: underline;
+ background-color: transparent;
+}
+
+.btn-group {
+ position: relative;
+ *margin-left: .3em;
+ font-size: 0;
+ white-space: nowrap;
+}
+
+.btn-group:first-child {
+ *margin-left: 0;
+}
+
+.btn-group + .btn-group {
+ margin-left: 5px;
+}
+
+.btn-toolbar {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ font-size: 0;
+}
+
+.btn-toolbar .btn-group {
+ display: inline-block;
+ *display: inline;
+ /* IE7 inline-block hack */
+
+ *zoom: 1;
+}
+
+.btn-toolbar .btn + .btn,
+.btn-toolbar .btn-group + .btn,
+.btn-toolbar .btn + .btn-group {
+ margin-left: 5px;
+}
+
+.btn-group > .btn {
+ position: relative;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.btn-group > .btn + .btn {
+ margin-left: -1px;
+}
+
+.btn-group > .btn,
+.btn-group > .dropdown-menu {
+ font-size: 14px;
+}
+
+.btn-group > .btn-mini {
+ font-size: 11px;
+}
+
+.btn-group > .btn-small {
+ font-size: 12px;
+}
+
+.btn-group > .btn-large {
+ font-size: 16px;
+}
+
+.btn-group > .btn:first-child {
+ margin-left: 0;
+ -webkit-border-bottom-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+ -webkit-border-top-left-radius: 4px;
+ border-top-left-radius: 4px;
+ -moz-border-radius-bottomleft: 4px;
+ -moz-border-radius-topleft: 4px;
+}
+
+.btn-group > .btn:last-child,
+.btn-group > .dropdown-toggle {
+ -webkit-border-top-right-radius: 4px;
+ border-top-right-radius: 4px;
+ -webkit-border-bottom-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ -moz-border-radius-bottomright: 4px;
+}
+
+.btn-group > .btn.large:first-child {
+ margin-left: 0;
+ -webkit-border-bottom-left-radius: 6px;
+ border-bottom-left-radius: 6px;
+ -webkit-border-top-left-radius: 6px;
+ border-top-left-radius: 6px;
+ -moz-border-radius-bottomleft: 6px;
+ -moz-border-radius-topleft: 6px;
+}
+
+.btn-group > .btn.large:last-child,
+.btn-group > .large.dropdown-toggle {
+ -webkit-border-top-right-radius: 6px;
+ border-top-right-radius: 6px;
+ -webkit-border-bottom-right-radius: 6px;
+ border-bottom-right-radius: 6px;
+ -moz-border-radius-topright: 6px;
+ -moz-border-radius-bottomright: 6px;
+}
+
+.btn-group > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group > .btn:active,
+.btn-group > .btn.active {
+ z-index: 2;
+}
+
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+ outline: 0;
+}
+
+.btn-group > .btn + .dropdown-toggle {
+ *padding-top: 5px;
+ padding-right: 8px;
+ *padding-bottom: 5px;
+ padding-left: 8px;
+ -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn-group > .btn-mini + .dropdown-toggle {
+ *padding-top: 2px;
+ padding-right: 5px;
+ *padding-bottom: 2px;
+ padding-left: 5px;
+}
+
+.btn-group > .btn-small + .dropdown-toggle {
+ *padding-top: 5px;
+ *padding-bottom: 4px;
+}
+
+.btn-group > .btn-large + .dropdown-toggle {
+ *padding-top: 7px;
+ padding-right: 12px;
+ *padding-bottom: 7px;
+ padding-left: 12px;
+}
+
+.btn-group.open .dropdown-toggle {
+ background-image: none;
+ -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn-group.open .btn.dropdown-toggle {
+ background-color: #e6e6e6;
+}
+
+.btn-group.open .btn-primary.dropdown-toggle {
+ background-color: #0044cc;
+}
+
+.btn-group.open .btn-warning.dropdown-toggle {
+ background-color: #f89406;
+}
+
+.btn-group.open .btn-danger.dropdown-toggle {
+ background-color: #bd362f;
+}
+
+.btn-group.open .btn-success.dropdown-toggle {
+ background-color: #51a351;
+}
+
+.btn-group.open .btn-info.dropdown-toggle {
+ background-color: #2f96b4;
+}
+
+.btn-group.open .btn-inverse.dropdown-toggle {
+ background-color: #222222;
+}
+
+.btn .caret {
+ margin-top: 8px;
+ margin-left: 0;
+}
+
+.btn-mini .caret,
+.btn-small .caret,
+.btn-large .caret {
+ margin-top: 6px;
+}
+
+.btn-large .caret {
+ border-top-width: 5px;
+ border-right-width: 5px;
+ border-left-width: 5px;
+}
+
+.dropup .btn-large .caret {
+ border-top: 0;
+ border-bottom: 5px solid #000000;
+}
+
+.btn-primary .caret,
+.btn-warning .caret,
+.btn-danger .caret,
+.btn-info .caret,
+.btn-success .caret,
+.btn-inverse .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+
+.btn-group-vertical {
+ display: inline-block;
+ *display: inline;
+ /* IE7 inline-block hack */
+
+ *zoom: 1;
+}
+
+.btn-group-vertical .btn {
+ display: block;
+ float: none;
+ width: 100%;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.btn-group-vertical .btn + .btn {
+ margin-top: -1px;
+ margin-left: 0;
+}
+
+.btn-group-vertical .btn:first-child {
+ -webkit-border-radius: 4px 4px 0 0;
+ -moz-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+}
+
+.btn-group-vertical .btn:last-child {
+ -webkit-border-radius: 0 0 4px 4px;
+ -moz-border-radius: 0 0 4px 4px;
+ border-radius: 0 0 4px 4px;
+}
+
+.btn-group-vertical .btn-large:first-child {
+ -webkit-border-radius: 6px 6px 0 0;
+ -moz-border-radius: 6px 6px 0 0;
+ border-radius: 6px 6px 0 0;
+}
+
+.btn-group-vertical .btn-large:last-child {
+ -webkit-border-radius: 0 0 6px 6px;
+ -moz-border-radius: 0 0 6px 6px;
+ border-radius: 0 0 6px 6px;
+}
+
+.alert {
+ padding: 8px 35px 8px 14px;
+ margin-bottom: 20px;
+ color: #c09853;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+ background-color: #fcf8e3;
+ border: 1px solid #fbeed5;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.alert h4 {
+ margin: 0;
+}
+
+.alert .close {
+ position: relative;
+ top: -2px;
+ right: -21px;
+ line-height: 20px;
+}
+
+.alert-success {
+ color: #468847;
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
+}
+
+.alert-danger,
+.alert-error {
+ color: #b94a48;
+ background-color: #f2dede;
+ border-color: #eed3d7;
+}
+
+.alert-info {
+ color: #3a87ad;
+ background-color: #d9edf7;
+ border-color: #bce8f1;
+}
+
+.alert-block {
+ padding-top: 14px;
+ padding-bottom: 14px;
+}
+
+.alert-block > p,
+.alert-block > ul {
+ margin-bottom: 0;
+}
+
+.alert-block p + p {
+ margin-top: 5px;
+}
+
+.nav {
+ margin-bottom: 20px;
+ margin-left: 0;
+ list-style: none;
+}
+
+.nav > li > a {
+ display: block;
+}
+
+.nav > li > a:hover {
+ text-decoration: none;
+ background-color: #eeeeee;
+}
+
+.nav > .pull-right {
+ float: right;
+}
+
+.nav-header {
+ display: block;
+ padding: 3px 15px;
+ font-size: 11px;
+ font-weight: bold;
+ line-height: 20px;
+ color: #999999;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+ text-transform: uppercase;
+}
+
+.nav li + .nav-header {
+ margin-top: 9px;
+}
+
+.nav-list {
+ padding-right: 15px;
+ padding-left: 15px;
+ margin-bottom: 0;
+}
+
+.nav-list > li > a,
+.nav-list .nav-header {
+ margin-right: -15px;
+ margin-left: -15px;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+}
+
+.nav-list > li > a {
+ padding: 3px 15px;
+}
+
+.nav-list > .active > a,
+.nav-list > .active > a:hover {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
+ background-color: #0088cc;
+}
+
+.nav-list [class^="icon-"] {
+ margin-right: 2px;
+}
+
+.nav-list .divider {
+ *width: 100%;
+ height: 1px;
+ margin: 9px 1px;
+ *margin: -5px 0 5px;
+ overflow: hidden;
+ background-color: #e5e5e5;
+ border-bottom: 1px solid #ffffff;
+}
+
+.nav-tabs,
+.nav-pills {
+ *zoom: 1;
+}
+
+.nav-tabs:before,
+.nav-pills:before,
+.nav-tabs:after,
+.nav-pills:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.nav-tabs:after,
+.nav-pills:after {
+ clear: both;
+}
+
+.nav-tabs > li,
+.nav-pills > li {
+ float: left;
+}
+
+.nav-tabs > li > a,
+.nav-pills > li > a {
+ padding-right: 12px;
+ padding-left: 12px;
+ margin-right: 2px;
+ line-height: 14px;
+}
+
+.nav-tabs {
+ border-bottom: 1px solid #ddd;
+}
+
+.nav-tabs > li {
+ margin-bottom: -1px;
+}
+
+.nav-tabs > li > a {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ line-height: 20px;
+ border: 1px solid transparent;
+ -webkit-border-radius: 4px 4px 0 0;
+ -moz-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+}
+
+.nav-tabs > li > a:hover {
+ border-color: #eeeeee #eeeeee #dddddd;
+}
+
+.nav-tabs > .active > a,
+.nav-tabs > .active > a:hover {
+ color: #555555;
+ cursor: default;
+ background-color: #ffffff;
+ border: 1px solid #ddd;
+ border-bottom-color: transparent;
+}
+
+.nav-pills > li > a {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ margin-top: 2px;
+ margin-bottom: 2px;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+
+.nav-pills > .active > a,
+.nav-pills > .active > a:hover {
+ color: #ffffff;
+ background-color: #0088cc;
+}
+
+.nav-stacked > li {
+ float: none;
+}
+
+.nav-stacked > li > a {
+ margin-right: 0;
+}
+
+.nav-tabs.nav-stacked {
+ border-bottom: 0;
+}
+
+.nav-tabs.nav-stacked > li > a {
+ border: 1px solid #ddd;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.nav-tabs.nav-stacked > li:first-child > a {
+ -webkit-border-top-right-radius: 4px;
+ border-top-right-radius: 4px;
+ -webkit-border-top-left-radius: 4px;
+ border-top-left-radius: 4px;
+ -moz-border-radius-topright: 4px;
+ -moz-border-radius-topleft: 4px;
+}
+
+.nav-tabs.nav-stacked > li:last-child > a {
+ -webkit-border-bottom-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+ -webkit-border-bottom-left-radius: 4px;
+ border-bottom-left-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+ -moz-border-radius-bottomleft: 4px;
+}
+
+.nav-tabs.nav-stacked > li > a:hover {
+ z-index: 2;
+ border-color: #ddd;
+}
+
+.nav-pills.nav-stacked > li > a {
+ margin-bottom: 3px;
+}
+
+.nav-pills.nav-stacked > li:last-child > a {
+ margin-bottom: 1px;
+}
+
+.nav-tabs .dropdown-menu {
+ -webkit-border-radius: 0 0 6px 6px;
+ -moz-border-radius: 0 0 6px 6px;
+ border-radius: 0 0 6px 6px;
+}
+
+.nav-pills .dropdown-menu {
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+
+.nav .dropdown-toggle .caret {
+ margin-top: 6px;
+ border-top-color: #0088cc;
+ border-bottom-color: #0088cc;
+}
+
+.nav .dropdown-toggle:hover .caret {
+ border-top-color: #005580;
+ border-bottom-color: #005580;
+}
+
+/* move down carets for tabs */
+
+.nav-tabs .dropdown-toggle .caret {
+ margin-top: 8px;
+}
+
+.nav .active .dropdown-toggle .caret {
+ border-top-color: #fff;
+ border-bottom-color: #fff;
+}
+
+.nav-tabs .active .dropdown-toggle .caret {
+ border-top-color: #555555;
+ border-bottom-color: #555555;
+}
+
+.nav > .dropdown.active > a:hover {
+ cursor: pointer;
+}
+
+.nav-tabs .open .dropdown-toggle,
+.nav-pills .open .dropdown-toggle,
+.nav > li.dropdown.open.active > a:hover {
+ color: #ffffff;
+ background-color: #999999;
+ border-color: #999999;
+}
+
+.nav li.dropdown.open .caret,
+.nav li.dropdown.open.active .caret,
+.nav li.dropdown.open a:hover .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+ opacity: 1;
+ filter: alpha(opacity=100);
+}
+
+.tabs-stacked .open > a:hover {
+ border-color: #999999;
+}
+
+.tabbable {
+ *zoom: 1;
+}
+
+.tabbable:before,
+.tabbable:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.tabbable:after {
+ clear: both;
+}
+
+.tab-content {
+ overflow: auto;
+}
+
+.tabs-below > .nav-tabs,
+.tabs-right > .nav-tabs,
+.tabs-left > .nav-tabs {
+ border-bottom: 0;
+}
+
+.tab-content > .tab-pane,
+.pill-content > .pill-pane {
+ display: none;
+}
+
+.tab-content > .active,
+.pill-content > .active {
+ display: block;
+}
+
+.tabs-below > .nav-tabs {
+ border-top: 1px solid #ddd;
+}
+
+.tabs-below > .nav-tabs > li {
+ margin-top: -1px;
+ margin-bottom: 0;
+}
+
+.tabs-below > .nav-tabs > li > a {
+ -webkit-border-radius: 0 0 4px 4px;
+ -moz-border-radius: 0 0 4px 4px;
+ border-radius: 0 0 4px 4px;
+}
+
+.tabs-below > .nav-tabs > li > a:hover {
+ border-top-color: #ddd;
+ border-bottom-color: transparent;
+}
+
+.tabs-below > .nav-tabs > .active > a,
+.tabs-below > .nav-tabs > .active > a:hover {
+ border-color: transparent #ddd #ddd #ddd;
+}
+
+.tabs-left > .nav-tabs > li,
+.tabs-right > .nav-tabs > li {
+ float: none;
+}
+
+.tabs-left > .nav-tabs > li > a,
+.tabs-right > .nav-tabs > li > a {
+ min-width: 74px;
+ margin-right: 0;
+ margin-bottom: 3px;
+}
+
+.tabs-left > .nav-tabs {
+ float: left;
+ margin-right: 19px;
+ border-right: 1px solid #ddd;
+}
+
+.tabs-left > .nav-tabs > li > a {
+ margin-right: -1px;
+ -webkit-border-radius: 4px 0 0 4px;
+ -moz-border-radius: 4px 0 0 4px;
+ border-radius: 4px 0 0 4px;
+}
+
+.tabs-left > .nav-tabs > li > a:hover {
+ border-color: #eeeeee #dddddd #eeeeee #eeeeee;
+}
+
+.tabs-left > .nav-tabs .active > a,
+.tabs-left > .nav-tabs .active > a:hover {
+ border-color: #ddd transparent #ddd #ddd;
+ *border-right-color: #ffffff;
+}
+
+.tabs-right > .nav-tabs {
+ float: right;
+ margin-left: 19px;
+ border-left: 1px solid #ddd;
+}
+
+.tabs-right > .nav-tabs > li > a {
+ margin-left: -1px;
+ -webkit-border-radius: 0 4px 4px 0;
+ -moz-border-radius: 0 4px 4px 0;
+ border-radius: 0 4px 4px 0;
+}
+
+.tabs-right > .nav-tabs > li > a:hover {
+ border-color: #eeeeee #eeeeee #eeeeee #dddddd;
+}
+
+.tabs-right > .nav-tabs .active > a,
+.tabs-right > .nav-tabs .active > a:hover {
+ border-color: #ddd #ddd #ddd transparent;
+ *border-left-color: #ffffff;
+}
+
+.nav > .disabled > a {
+ color: #999999;
+}
+
+.nav > .disabled > a:hover {
+ text-decoration: none;
+ cursor: default;
+ background-color: transparent;
+}
+
+.navbar {
+ *position: relative;
+ *z-index: 2;
+ margin-bottom: 20px;
+ overflow: visible;
+ color: #555555;
+}
+
+.navbar-inner {
+ min-height: 40px;
+ padding-right: 20px;
+ padding-left: 20px;
+ background-color: #fafafa;
+ background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2));
+ background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2);
+ background-image: -o-linear-gradient(top, #ffffff, #f2f2f2);
+ background-image: linear-gradient(to bottom, #ffffff, #f2f2f2);
+ background-repeat: repeat-x;
+ border: 1px solid #d4d4d4;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);
+ -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+}
+
+.navbar .container {
+ width: auto;
+}
+
+.nav-collapse.collapse {
+ height: auto;
+}
+
+.navbar .brand {
+ display: block;
+ float: left;
+ padding: 10px 20px 10px;
+ margin-left: -20px;
+ font-size: 20px;
+ font-weight: 200;
+ color: #555555;
+ text-shadow: 0 1px 0 #ffffff;
+}
+
+.navbar .brand:hover {
+ text-decoration: none;
+}
+
+.navbar-text {
+ margin-bottom: 0;
+ line-height: 40px;
+}
+
+.navbar-link {
+ color: #555555;
+}
+
+.navbar-link:hover {
+ color: #333333;
+}
+
+.navbar .divider-vertical {
+ height: 40px;
+ margin: 0 9px;
+ border-right: 1px solid #ffffff;
+ border-left: 1px solid #f2f2f2;
+}
+
+.navbar .btn,
+.navbar .btn-group {
+ margin-top: 6px;
+}
+
+.navbar .btn-group .btn {
+ margin: 0;
+}
+
+.navbar-form {
+ margin-bottom: 0;
+ *zoom: 1;
+}
+
+.navbar-form:before,
+.navbar-form:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.navbar-form:after {
+ clear: both;
+}
+
+.navbar-form input,
+.navbar-form select,
+.navbar-form .radio,
+.navbar-form .checkbox {
+ margin-top: 5px;
+}
+
+.navbar-form input,
+.navbar-form select,
+.navbar-form .btn {
+ display: inline-block;
+ margin-bottom: 0;
+}
+
+.navbar-form input[type="image"],
+.navbar-form input[type="checkbox"],
+.navbar-form input[type="radio"] {
+ margin-top: 3px;
+}
+
+.navbar-form .input-append,
+.navbar-form .input-prepend {
+ margin-top: 6px;
+ white-space: nowrap;
+}
+
+.navbar-form .input-append input,
+.navbar-form .input-prepend input {
+ margin-top: 0;
+}
+
+.navbar-search {
+ position: relative;
+ float: left;
+ margin-top: 5px;
+ margin-bottom: 0;
+}
+
+.navbar-search .search-query {
+ padding: 4px 14px;
+ margin-bottom: 0;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 13px;
+ font-weight: normal;
+ line-height: 1;
+ -webkit-border-radius: 15px;
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+}
+
+.navbar-static-top {
+ position: static;
+ width: 100%;
+ margin-bottom: 0;
+}
+
+.navbar-static-top .navbar-inner {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+ position: fixed;
+ right: 0;
+ left: 0;
+ z-index: 1030;
+ margin-bottom: 0;
+}
+
+.navbar-fixed-top .navbar-inner,
+.navbar-fixed-bottom .navbar-inner,
+.navbar-static-top .navbar-inner {
+ border: 0;
+}
+
+.navbar-fixed-top .navbar-inner,
+.navbar-fixed-bottom .navbar-inner {
+ padding-right: 0;
+ padding-left: 0;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.navbar-static-top .container,
+.navbar-fixed-top .container,
+.navbar-fixed-bottom .container {
+ width: 940px;
+}
+
+.navbar-fixed-top {
+ top: 0;
+}
+
+.navbar-fixed-top .navbar-inner,
+.navbar-static-top .navbar-inner {
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.1), 0 1px 10px rgba(0, 0, 0, 0.1);
+}
+
+.navbar-fixed-bottom {
+ bottom: 0;
+}
+
+.navbar-fixed-bottom .navbar-inner {
+ -webkit-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.1), 0 -1px 10px rgba(0, 0, 0, 0.1);
+}
+
+.navbar .nav {
+ position: relative;
+ left: 0;
+ display: block;
+ float: left;
+ margin: 0 10px 0 0;
+}
+
+.navbar .nav.pull-right {
+ float: right;
+}
+
+.navbar .nav > li {
+ float: left;
+}
+
+.navbar .nav > li > a {
+ float: none;
+ padding: 10px 15px 10px;
+ color: #555555;
+ text-decoration: none;
+ text-shadow: 0 1px 0 #ffffff;
+}
+
+.navbar .nav .dropdown-toggle .caret {
+ margin-top: 8px;
+}
+
+.navbar .nav > li > a:focus,
+.navbar .nav > li > a:hover {
+ color: #333333;
+ text-decoration: none;
+ background-color: transparent;
+}
+
+.navbar .nav > .active > a,
+.navbar .nav > .active > a:hover,
+.navbar .nav > .active > a:focus {
+ color: #555555;
+ text-decoration: none;
+ background-color: #e5e5e5;
+ -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
+ -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
+}
+
+.navbar .btn-navbar {
+ display: none;
+ float: right;
+ padding: 7px 10px;
+ margin-right: 5px;
+ margin-left: 5px;
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #ededed;
+ *background-color: #e5e5e5;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f2f2f2), to(#e5e5e5));
+ background-image: -webkit-linear-gradient(top, #f2f2f2, #e5e5e5);
+ background-image: -o-linear-gradient(top, #f2f2f2, #e5e5e5);
+ background-image: linear-gradient(to bottom, #f2f2f2, #e5e5e5);
+ background-image: -moz-linear-gradient(top, #f2f2f2, #e5e5e5);
+ background-repeat: repeat-x;
+ border-color: #e5e5e5 #e5e5e5 #bfbfbf;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fff2f2f2', endColorstr='#ffe5e5e5', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
+}
+
+.navbar .btn-navbar:hover,
+.navbar .btn-navbar:active,
+.navbar .btn-navbar.active,
+.navbar .btn-navbar.disabled,
+.navbar .btn-navbar[disabled] {
+ color: #ffffff;
+ background-color: #e5e5e5;
+ *background-color: #d9d9d9;
+}
+
+.navbar .btn-navbar:active,
+.navbar .btn-navbar.active {
+ background-color: #cccccc \9;
+}
+
+.navbar .btn-navbar .icon-bar {
+ display: block;
+ width: 18px;
+ height: 2px;
+ background-color: #f5f5f5;
+ -webkit-border-radius: 1px;
+ -moz-border-radius: 1px;
+ border-radius: 1px;
+ -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+ -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+}
+
+.btn-navbar .icon-bar + .icon-bar {
+ margin-top: 3px;
+}
+
+.navbar .nav > li > .dropdown-menu:before {
+ position: absolute;
+ top: -7px;
+ left: 9px;
+ display: inline-block;
+ border-right: 7px solid transparent;
+ border-bottom: 7px solid #ccc;
+ border-left: 7px solid transparent;
+ border-bottom-color: rgba(0, 0, 0, 0.2);
+ content: '';
+}
+
+.navbar .nav > li > .dropdown-menu:after {
+ position: absolute;
+ top: -6px;
+ left: 10px;
+ display: inline-block;
+ border-right: 6px solid transparent;
+ border-bottom: 6px solid #ffffff;
+ border-left: 6px solid transparent;
+ content: '';
+}
+
+.navbar-fixed-bottom .nav > li > .dropdown-menu:before {
+ top: auto;
+ bottom: -7px;
+ border-top: 7px solid #ccc;
+ border-bottom: 0;
+ border-top-color: rgba(0, 0, 0, 0.2);
+}
+
+.navbar-fixed-bottom .nav > li > .dropdown-menu:after {
+ top: auto;
+ bottom: -6px;
+ border-top: 6px solid #ffffff;
+ border-bottom: 0;
+}
+
+.navbar .nav li.dropdown.open > .dropdown-toggle,
+.navbar .nav li.dropdown.active > .dropdown-toggle,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle {
+ color: #555555;
+ background-color: #e5e5e5;
+}
+
+.navbar .nav li.dropdown > .dropdown-toggle .caret {
+ border-top-color: #555555;
+ border-bottom-color: #555555;
+}
+
+.navbar .nav li.dropdown.open > .dropdown-toggle .caret,
+.navbar .nav li.dropdown.active > .dropdown-toggle .caret,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret {
+ border-top-color: #555555;
+ border-bottom-color: #555555;
+}
+
+.navbar .pull-right > li > .dropdown-menu,
+.navbar .nav > li > .dropdown-menu.pull-right {
+ right: 0;
+ left: auto;
+}
+
+.navbar .pull-right > li > .dropdown-menu:before,
+.navbar .nav > li > .dropdown-menu.pull-right:before {
+ right: 12px;
+ left: auto;
+}
+
+.navbar .pull-right > li > .dropdown-menu:after,
+.navbar .nav > li > .dropdown-menu.pull-right:after {
+ right: 13px;
+ left: auto;
+}
+
+.navbar .pull-right > li > .dropdown-menu .dropdown-menu,
+.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu {
+ right: 100%;
+ left: auto;
+ margin-right: -1px;
+ margin-left: 0;
+ -webkit-border-radius: 6px 0 6px 6px;
+ -moz-border-radius: 6px 0 6px 6px;
+ border-radius: 6px 0 6px 6px;
+}
+
+.navbar-inverse {
+ color: #999999;
+}
+
+.navbar-inverse .navbar-inner {
+ background-color: #1b1b1b;
+ background-image: -moz-linear-gradient(top, #222222, #111111);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111));
+ background-image: -webkit-linear-gradient(top, #222222, #111111);
+ background-image: -o-linear-gradient(top, #222222, #111111);
+ background-image: linear-gradient(to bottom, #222222, #111111);
+ background-repeat: repeat-x;
+ border-color: #252525;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0);
+}
+
+.navbar-inverse .brand,
+.navbar-inverse .nav > li > a {
+ color: #999999;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+
+.navbar-inverse .brand:hover,
+.navbar-inverse .nav > li > a:hover {
+ color: #ffffff;
+}
+
+.navbar-inverse .nav > li > a:focus,
+.navbar-inverse .nav > li > a:hover {
+ color: #ffffff;
+ background-color: transparent;
+}
+
+.navbar-inverse .nav .active > a,
+.navbar-inverse .nav .active > a:hover,
+.navbar-inverse .nav .active > a:focus {
+ color: #ffffff;
+ background-color: #111111;
+}
+
+.navbar-inverse .navbar-link {
+ color: #999999;
+}
+
+.navbar-inverse .navbar-link:hover {
+ color: #ffffff;
+}
+
+.navbar-inverse .divider-vertical {
+ border-right-color: #222222;
+ border-left-color: #111111;
+}
+
+.navbar-inverse .nav li.dropdown.open > .dropdown-toggle,
+.navbar-inverse .nav li.dropdown.active > .dropdown-toggle,
+.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle {
+ color: #ffffff;
+ background-color: #111111;
+}
+
+.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret {
+ border-top-color: #999999;
+ border-bottom-color: #999999;
+}
+
+.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret,
+.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret,
+.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret {
+ border-top-color: #ffffff;
+ border-bottom-color: #ffffff;
+}
+
+.navbar-inverse .navbar-search .search-query {
+ color: #ffffff;
+ background-color: #515151;
+ border-color: #111111;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
+ -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
+ -webkit-transition: none;
+ -moz-transition: none;
+ -o-transition: none;
+ transition: none;
+}
+
+.navbar-inverse .navbar-search .search-query:-moz-placeholder {
+ color: #cccccc;
+}
+
+.navbar-inverse .navbar-search .search-query:-ms-input-placeholder {
+ color: #cccccc;
+}
+
+.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder {
+ color: #cccccc;
+}
+
+.navbar-inverse .navbar-search .search-query:focus,
+.navbar-inverse .navbar-search .search-query.focused {
+ padding: 5px 15px;
+ color: #333333;
+ text-shadow: 0 1px 0 #ffffff;
+ background-color: #ffffff;
+ border: 0;
+ outline: 0;
+ -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+ box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+}
+
+.navbar-inverse .btn-navbar {
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #0e0e0e;
+ *background-color: #040404;
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#151515), to(#040404));
+ background-image: -webkit-linear-gradient(top, #151515, #040404);
+ background-image: -o-linear-gradient(top, #151515, #040404);
+ background-image: linear-gradient(to bottom, #151515, #040404);
+ background-image: -moz-linear-gradient(top, #151515, #040404);
+ background-repeat: repeat-x;
+ border-color: #040404 #040404 #000000;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff151515', endColorstr='#ff040404', GradientType=0);
+ filter: progid:dximagetransform.microsoft.gradient(enabled=false);
+}
+
+.navbar-inverse .btn-navbar:hover,
+.navbar-inverse .btn-navbar:active,
+.navbar-inverse .btn-navbar.active,
+.navbar-inverse .btn-navbar.disabled,
+.navbar-inverse .btn-navbar[disabled] {
+ color: #ffffff;
+ background-color: #040404;
+ *background-color: #000000;
+}
+
+.navbar-inverse .btn-navbar:active,
+.navbar-inverse .btn-navbar.active {
+ background-color: #000000 \9;
+}
+
+.breadcrumb {
+ padding: 8px 15px;
+ margin: 0 0 20px;
+ list-style: none;
+ background-color: #f5f5f5;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.breadcrumb li {
+ display: inline-block;
+ *display: inline;
+ text-shadow: 0 1px 0 #ffffff;
+ *zoom: 1;
+}
+
+.breadcrumb .divider {
+ padding: 0 5px;
+ color: #ccc;
+}
+
+.breadcrumb .active {
+ color: #999999;
+}
+
+.pagination {
+ height: 40px;
+ margin: 20px 0;
+}
+
+.pagination ul {
+ display: inline-block;
+ *display: inline;
+ margin-bottom: 0;
+ margin-left: 0;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ *zoom: 1;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.pagination li {
+ display: inline;
+}
+
+.pagination a,
+.pagination span {
+ float: left;
+ padding: 0 14px;
+ line-height: 38px;
+ text-decoration: none;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-left-width: 0;
+}
+
+.pagination a:hover,
+.pagination .active a,
+.pagination .active span {
+ background-color: #f5f5f5;
+}
+
+.pagination .active a,
+.pagination .active span {
+ color: #999999;
+ cursor: default;
+}
+
+.pagination .disabled span,
+.pagination .disabled a,
+.pagination .disabled a:hover {
+ color: #999999;
+ cursor: default;
+ background-color: transparent;
+}
+
+.pagination li:first-child a,
+.pagination li:first-child span {
+ border-left-width: 1px;
+ -webkit-border-radius: 3px 0 0 3px;
+ -moz-border-radius: 3px 0 0 3px;
+ border-radius: 3px 0 0 3px;
+}
+
+.pagination li:last-child a,
+.pagination li:last-child span {
+ -webkit-border-radius: 0 3px 3px 0;
+ -moz-border-radius: 0 3px 3px 0;
+ border-radius: 0 3px 3px 0;
+}
+
+.pagination-centered {
+ text-align: center;
+}
+
+.pagination-right {
+ text-align: right;
+}
+
+.pager {
+ margin: 20px 0;
+ text-align: center;
+ list-style: none;
+ *zoom: 1;
+}
+
+.pager:before,
+.pager:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.pager:after {
+ clear: both;
+}
+
+.pager li {
+ display: inline;
+}
+
+.pager a {
+ display: inline-block;
+ padding: 5px 14px;
+ background-color: #fff;
+ border: 1px solid #ddd;
+ -webkit-border-radius: 15px;
+ -moz-border-radius: 15px;
+ border-radius: 15px;
+}
+
+.pager a:hover {
+ text-decoration: none;
+ background-color: #f5f5f5;
+}
+
+.pager .next a {
+ float: right;
+}
+
+.pager .previous a {
+ float: left;
+}
+
+.pager .disabled a,
+.pager .disabled a:hover {
+ color: #999999;
+ cursor: default;
+ background-color: #fff;
+}
+
+.modal-open .dropdown-menu {
+ z-index: 2050;
+}
+
+.modal-open .dropdown.open {
+ *z-index: 2050;
+}
+
+.modal-open .popover {
+ z-index: 2060;
+}
+
+.modal-open .tooltip {
+ z-index: 2080;
+}
+
+.modal-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1040;
+ background-color: #000000;
+}
+
+.modal-backdrop.fade {
+ opacity: 0;
+}
+
+.modal-backdrop,
+.modal-backdrop.fade.in {
+ opacity: 0.8;
+ filter: alpha(opacity=80);
+}
+
+.modal {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ z-index: 1050;
+ width: 560px;
+ margin: -250px 0 0 -280px;
+ overflow: auto;
+ background-color: #ffffff;
+ border: 1px solid #999;
+ border: 1px solid rgba(0, 0, 0, 0.3);
+ *border: 1px solid #999;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding-box;
+ background-clip: padding-box;
+}
+
+.modal.fade {
+ top: -25%;
+ -webkit-transition: opacity 0.3s linear, top 0.3s ease-out;
+ -moz-transition: opacity 0.3s linear, top 0.3s ease-out;
+ -o-transition: opacity 0.3s linear, top 0.3s ease-out;
+ transition: opacity 0.3s linear, top 0.3s ease-out;
+}
+
+.modal.fade.in {
+ top: 50%;
+}
+
+.modal-header {
+ padding: 9px 15px;
+ border-bottom: 1px solid #eee;
+}
+
+.modal-header .close {
+ margin-top: 2px;
+}
+
+.modal-header h3 {
+ margin: 0;
+ line-height: 30px;
+}
+
+.modal-body {
+ max-height: 400px;
+ padding: 15px;
+ overflow-y: auto;
+}
+
+.modal-form {
+ margin-bottom: 0;
+}
+
+.modal-footer {
+ padding: 14px 15px 15px;
+ margin-bottom: 0;
+ text-align: right;
+ background-color: #f5f5f5;
+ border-top: 1px solid #ddd;
+ -webkit-border-radius: 0 0 6px 6px;
+ -moz-border-radius: 0 0 6px 6px;
+ border-radius: 0 0 6px 6px;
+ *zoom: 1;
+ -webkit-box-shadow: inset 0 1px 0 #ffffff;
+ -moz-box-shadow: inset 0 1px 0 #ffffff;
+ box-shadow: inset 0 1px 0 #ffffff;
+}
+
+.modal-footer:before,
+.modal-footer:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.modal-footer:after {
+ clear: both;
+}
+
+.modal-footer .btn + .btn {
+ margin-bottom: 0;
+ margin-left: 5px;
+}
+
+.modal-footer .btn-group .btn + .btn {
+ margin-left: -1px;
+}
+
+.tooltip {
+ position: absolute;
+ z-index: 1030;
+ display: block;
+ padding: 5px;
+ font-size: 11px;
+ opacity: 0;
+ filter: alpha(opacity=0);
+ visibility: visible;
+}
+
+.tooltip.in {
+ opacity: 0.8;
+ filter: alpha(opacity=80);
+}
+
+.tooltip.top {
+ margin-top: -3px;
+}
+
+.tooltip.right {
+ margin-left: 3px;
+}
+
+.tooltip.bottom {
+ margin-top: 3px;
+}
+
+.tooltip.left {
+ margin-left: -3px;
+}
+
+.tooltip-inner {
+ max-width: 200px;
+ padding: 3px 8px;
+ color: #ffffff;
+ text-align: center;
+ text-decoration: none;
+ background-color: #000000;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.tooltip-arrow {
+ position: absolute;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+}
+
+.tooltip.top .tooltip-arrow {
+ bottom: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-top-color: #000000;
+ border-width: 5px 5px 0;
+}
+
+.tooltip.right .tooltip-arrow {
+ top: 50%;
+ left: 0;
+ margin-top: -5px;
+ border-right-color: #000000;
+ border-width: 5px 5px 5px 0;
+}
+
+.tooltip.left .tooltip-arrow {
+ top: 50%;
+ right: 0;
+ margin-top: -5px;
+ border-left-color: #000000;
+ border-width: 5px 0 5px 5px;
+}
+
+.tooltip.bottom .tooltip-arrow {
+ top: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-bottom-color: #000000;
+ border-width: 0 5px 5px;
+}
+
+.popover {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1010;
+ display: none;
+ width: 236px;
+ padding: 1px;
+ background-color: #ffffff;
+ border: 1px solid #ccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding;
+ background-clip: padding-box;
+}
+
+.popover.top {
+ margin-bottom: 10px;
+}
+
+.popover.right {
+ margin-left: 10px;
+}
+
+.popover.bottom {
+ margin-top: 10px;
+}
+
+.popover.left {
+ margin-right: 10px;
+}
+
+.popover-title {
+ padding: 8px 14px;
+ margin: 0;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 18px;
+ background-color: #f7f7f7;
+ border-bottom: 1px solid #ebebeb;
+ -webkit-border-radius: 5px 5px 0 0;
+ -moz-border-radius: 5px 5px 0 0;
+ border-radius: 5px 5px 0 0;
+}
+
+.popover-content {
+ padding: 9px 14px;
+}
+
+.popover-content p,
+.popover-content ul,
+.popover-content ol {
+ margin-bottom: 0;
+}
+
+.popover .arrow,
+.popover .arrow:after {
+ position: absolute;
+ display: inline-block;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
+}
+
+.popover .arrow:after {
+ z-index: -1;
+ content: "";
+}
+
+.popover.top .arrow {
+ bottom: -10px;
+ left: 50%;
+ margin-left: -10px;
+ border-top-color: #ffffff;
+ border-width: 10px 10px 0;
+}
+
+.popover.top .arrow:after {
+ bottom: -1px;
+ left: -11px;
+ border-top-color: rgba(0, 0, 0, 0.25);
+ border-width: 11px 11px 0;
+}
+
+.popover.right .arrow {
+ top: 50%;
+ left: -10px;
+ margin-top: -10px;
+ border-right-color: #ffffff;
+ border-width: 10px 10px 10px 0;
+}
+
+.popover.right .arrow:after {
+ bottom: -11px;
+ left: -1px;
+ border-right-color: rgba(0, 0, 0, 0.25);
+ border-width: 11px 11px 11px 0;
+}
+
+.popover.bottom .arrow {
+ top: -10px;
+ left: 50%;
+ margin-left: -10px;
+ border-bottom-color: #ffffff;
+ border-width: 0 10px 10px;
+}
+
+.popover.bottom .arrow:after {
+ top: -1px;
+ left: -11px;
+ border-bottom-color: rgba(0, 0, 0, 0.25);
+ border-width: 0 11px 11px;
+}
+
+.popover.left .arrow {
+ top: 50%;
+ right: -10px;
+ margin-top: -10px;
+ border-left-color: #ffffff;
+ border-width: 10px 0 10px 10px;
+}
+
+.popover.left .arrow:after {
+ right: -1px;
+ bottom: -11px;
+ border-left-color: rgba(0, 0, 0, 0.25);
+ border-width: 11px 0 11px 11px;
+}
+
+.thumbnails {
+ margin-left: -20px;
+ list-style: none;
+ *zoom: 1;
+}
+
+.thumbnails:before,
+.thumbnails:after {
+ display: table;
+ line-height: 0;
+ content: "";
+}
+
+.thumbnails:after {
+ clear: both;
+}
+
+.row-fluid .thumbnails {
+ margin-left: 0;
+}
+
+.thumbnails > li {
+ float: left;
+ margin-bottom: 20px;
+ margin-left: 20px;
+}
+
+.thumbnail {
+ display: block;
+ padding: 4px;
+ line-height: 20px;
+ border: 1px solid #ddd;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+ -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+ -webkit-transition: all 0.2s ease-in-out;
+ -moz-transition: all 0.2s ease-in-out;
+ -o-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+}
+
+a.thumbnail:hover {
+ border-color: #0088cc;
+ -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+ -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+ box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+}
+
+.thumbnail > img {
+ display: block;
+ max-width: 100%;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.thumbnail .caption {
+ padding: 9px;
+ color: #555555;
+}
+
+.label,
+.badge {
+ font-size: 11.844px;
+ font-weight: bold;
+ line-height: 14px;
+ color: #ffffff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ white-space: nowrap;
+ vertical-align: baseline;
+ background-color: #999999;
+}
+
+.label {
+ padding: 1px 4px 2px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+
+.badge {
+ padding: 1px 9px 2px;
+ -webkit-border-radius: 9px;
+ -moz-border-radius: 9px;
+ border-radius: 9px;
+}
+
+a.label:hover,
+a.badge:hover {
+ color: #ffffff;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.label-important,
+.badge-important {
+ background-color: #b94a48;
+}
+
+.label-important[href],
+.badge-important[href] {
+ background-color: #953b39;
+}
+
+.label-warning,
+.badge-warning {
+ background-color: #f89406;
+}
+
+.label-warning[href],
+.badge-warning[href] {
+ background-color: #c67605;
+}
+
+.label-success,
+.badge-success {
+ background-color: #468847;
+}
+
+.label-success[href],
+.badge-success[href] {
+ background-color: #356635;
+}
+
+.label-info,
+.badge-info {
+ background-color: #3a87ad;
+}
+
+.label-info[href],
+.badge-info[href] {
+ background-color: #2d6987;
+}
+
+.label-inverse,
+.badge-inverse {
+ background-color: #333333;
+}
+
+.label-inverse[href],
+.badge-inverse[href] {
+ background-color: #1a1a1a;
+}
+
+.btn .label,
+.btn .badge {
+ position: relative;
+ top: -1px;
+}
+
+.btn-mini .label,
+.btn-mini .badge {
+ top: 0;
+}
+
+@-webkit-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+@-moz-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+@-ms-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+@-o-keyframes progress-bar-stripes {
+ from {
+ background-position: 0 0;
+ }
+ to {
+ background-position: 40px 0;
+ }
+}
+
+@keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+.progress {
+ height: 20px;
+ margin-bottom: 20px;
+ overflow: hidden;
+ background-color: #f7f7f7;
+ background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));
+ background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);
+ background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9);
+ background-repeat: repeat-x;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+
+.progress .bar {
+ float: left;
+ width: 0;
+ height: 100%;
+ font-size: 12px;
+ color: #ffffff;
+ text-align: center;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+ background-color: #0e90d2;
+ background-image: -moz-linear-gradient(top, #149bdf, #0480be);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));
+ background-image: -webkit-linear-gradient(top, #149bdf, #0480be);
+ background-image: -o-linear-gradient(top, #149bdf, #0480be);
+ background-image: linear-gradient(to bottom, #149bdf, #0480be);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ -webkit-transition: width 0.6s ease;
+ -moz-transition: width 0.6s ease;
+ -o-transition: width 0.6s ease;
+ transition: width 0.6s ease;
+}
+
+.progress .bar + .bar {
+ -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+}
+
+.progress-striped .bar {
+ background-color: #149bdf;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ -webkit-background-size: 40px 40px;
+ -moz-background-size: 40px 40px;
+ -o-background-size: 40px 40px;
+ background-size: 40px 40px;
+}
+
+.progress.active .bar {
+ -webkit-animation: progress-bar-stripes 2s linear infinite;
+ -moz-animation: progress-bar-stripes 2s linear infinite;
+ -ms-animation: progress-bar-stripes 2s linear infinite;
+ -o-animation: progress-bar-stripes 2s linear infinite;
+ animation: progress-bar-stripes 2s linear infinite;
+}
+
+.progress-danger .bar,
+.progress .bar-danger {
+ background-color: #dd514c;
+ background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));
+ background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
+ background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
+ background-image: linear-gradient(to bottom, #ee5f5b, #c43c35);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0);
+}
+
+.progress-danger.progress-striped .bar,
+.progress-striped .bar-danger {
+ background-color: #ee5f5b;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-success .bar,
+.progress .bar-success {
+ background-color: #5eb95e;
+ background-image: -moz-linear-gradient(top, #62c462, #57a957);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));
+ background-image: -webkit-linear-gradient(top, #62c462, #57a957);
+ background-image: -o-linear-gradient(top, #62c462, #57a957);
+ background-image: linear-gradient(to bottom, #62c462, #57a957);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0);
+}
+
+.progress-success.progress-striped .bar,
+.progress-striped .bar-success {
+ background-color: #62c462;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-info .bar,
+.progress .bar-info {
+ background-color: #4bb1cf;
+ background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));
+ background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
+ background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
+ background-image: linear-gradient(to bottom, #5bc0de, #339bb9);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0);
+}
+
+.progress-info.progress-striped .bar,
+.progress-striped .bar-info {
+ background-color: #5bc0de;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-warning .bar,
+.progress .bar-warning {
+ background-color: #faa732;
+ background-image: -moz-linear-gradient(top, #fbb450, #f89406);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
+ background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
+ background-image: -o-linear-gradient(top, #fbb450, #f89406);
+ background-image: linear-gradient(to bottom, #fbb450, #f89406);
+ background-repeat: repeat-x;
+ filter: progid:dximagetransform.microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
+}
+
+.progress-warning.progress-striped .bar,
+.progress-striped .bar-warning {
+ background-color: #fbb450;
+ background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.accordion {
+ margin-bottom: 20px;
+}
+
+.accordion-group {
+ margin-bottom: 2px;
+ border: 1px solid #e5e5e5;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+.accordion-heading {
+ border-bottom: 0;
+}
+
+.accordion-heading .accordion-toggle {
+ display: block;
+ padding: 8px 15px;
+}
+
+.accordion-toggle {
+ cursor: pointer;
+}
+
+.accordion-inner {
+ padding: 9px 15px;
+ border-top: 1px solid #e5e5e5;
+}
+
+.carousel {
+ position: relative;
+ margin-bottom: 20px;
+ line-height: 1;
+}
+
+.carousel-inner {
+ position: relative;
+ width: 100%;
+ overflow: hidden;
+}
+
+.carousel .item {
+ position: relative;
+ display: none;
+ -webkit-transition: 0.6s ease-in-out left;
+ -moz-transition: 0.6s ease-in-out left;
+ -o-transition: 0.6s ease-in-out left;
+ transition: 0.6s ease-in-out left;
+}
+
+.carousel .item > img {
+ display: block;
+ line-height: 1;
+}
+
+.carousel .active,
+.carousel .next,
+.carousel .prev {
+ display: block;
+}
+
+.carousel .active {
+ left: 0;
+}
+
+.carousel .next,
+.carousel .prev {
+ position: absolute;
+ top: 0;
+ width: 100%;
+}
+
+.carousel .next {
+ left: 100%;
+}
+
+.carousel .prev {
+ left: -100%;
+}
+
+.carousel .next.left,
+.carousel .prev.right {
+ left: 0;
+}
+
+.carousel .active.left {
+ left: -100%;
+}
+
+.carousel .active.right {
+ left: 100%;
+}
+
+.carousel-control {
+ position: absolute;
+ top: 40%;
+ left: 15px;
+ width: 40px;
+ height: 40px;
+ margin-top: -20px;
+ font-size: 60px;
+ font-weight: 100;
+ line-height: 30px;
+ color: #ffffff;
+ text-align: center;
+ background: #222222;
+ border: 3px solid #ffffff;
+ -webkit-border-radius: 23px;
+ -moz-border-radius: 23px;
+ border-radius: 23px;
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+}
+
+.carousel-control.right {
+ right: 15px;
+ left: auto;
+}
+
+.carousel-control:hover {
+ color: #ffffff;
+ text-decoration: none;
+ opacity: 0.9;
+ filter: alpha(opacity=90);
+}
+
+.carousel-caption {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ padding: 15px;
+ background: #333333;
+ background: rgba(0, 0, 0, 0.75);
+}
+
+.carousel-caption h4,
+.carousel-caption p {
+ line-height: 20px;
+ color: #ffffff;
+}
+
+.carousel-caption h4 {
+ margin: 0 0 5px;
+}
+
+.carousel-caption p {
+ margin-bottom: 0;
+}
+
+.hero-unit {
+ padding: 60px;
+ margin-bottom: 30px;
+ background-color: #eeeeee;
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+}
+
+.hero-unit h1 {
+ margin-bottom: 0;
+ font-size: 60px;
+ line-height: 1;
+ letter-spacing: -1px;
+ color: inherit;
+}
+
+.hero-unit p {
+ font-size: 18px;
+ font-weight: 200;
+ line-height: 30px;
+ color: inherit;
+}
+
+.pull-right {
+ float: right;
+}
+
+.pull-left {
+ float: left;
+}
+
+.hide {
+ display: none;
+}
+
+.show {
+ display: block;
+}
+
+.invisible {
+ visibility: hidden;
+}
+
+.affix {
+ position: fixed;
+}
+
--- a/displayAgency.php
+++ b/displayAgency.php
@@ -3,11 +3,12 @@
include_once ("./lib/common.inc.php");
if ($_REQUEST['agency']) {
- include_header("Agency");
$agency = htmlentities(strip_tags($_REQUEST['agency']));
- MethodCountGraph($agency);
- CnCGraph($agency);
- MethodValueGraph($agency);
+ include_header($agency);
+echo '<center><h1>'.$agency.'</h1></center>';
+// MethodCountGraph($agency);
+ // CnCGraph($agency);
+ // MethodValueGraph($agency);
/* biggest contracts
spending by year
spending by industry/category
@@ -28,8 +29,8 @@
$query = 'SELECT "CNID", "description", "value", "agencyName", "category",
"contractStart", "supplierName"
FROM contractnotice
- WHERE "agencyName" = :agency
- ORDER BY "value" DESC';
+ WHERE "agencyName" like :agency
+ ORDER BY "value" DESC limit 100';
$query = $conn->prepare($query);
$query->bindParam(":agency", $agency);
$query->execute();
--- a/displayCalendar.php
+++ b/displayCalendar.php
@@ -2,11 +2,17 @@
include_once("./lib/common.inc.php");
-include_header("Months and Years");
+
if ($_REQUEST['month']) {
- echo "<center><h1>" . $_REQUEST['month'] . "</h1></center>";
$monthParts = explode("-", $_REQUEST['month']);
+ $year = $monthParts[1];
+ $month = $monthParts[0];
+ $monthName = date("F Y", mktime(0,0,0,$month,1,$year));
+
+ include_header($monthName);
+ echo "<center><h1>" . $monthName . "</h1></center>";
+
$query = 'SELECT "CNID", "description", "value", "agencyName", "category", "contractStart", "supplierName"
FROM contractnotice
WHERE "childCN" is null
@@ -52,7 +58,7 @@
Year/Month drilldown - largest contracts, agencies, suppliers
count per month
big picture graphs? */
-
+include_header("Months and Years");
ContractStartingGraph();
ContractPublishedGraph();
--- a/displayCategory.php
+++ b/displayCategory.php
@@ -1,7 +1,7 @@
<?php
include_once ("./lib/common.inc.php");
if ($_REQUEST['category']) {
- include_header("Category");
+ include_header($_REQUEST['category']." goods and services");
echo "<center><h1>".$_REQUEST['category']."</h1></center>";
$query = 'SELECT "CNID", description, value, "agencyName", category, "contractStart", "supplierName"
FROM contractnotice
--- a/displayConfidentialities.php
+++ b/displayConfidentialities.php
@@ -10,9 +10,9 @@
*/
include_header("Confidentialities");
-$query = "SELECT value, procurementMethod
-FROM `contractnotice`
-GROUP BY procurementMethod ";
+$query = 'SELECT SUM(value) as value, "procurementMethod"
+FROM contractnotice
+GROUP BY "procurementMethod" ';
$query = $conn->prepare($query);
$query->execute();
--- a/displayConsultancies.php
+++ b/displayConsultancies.php
@@ -10,9 +10,9 @@
*/
include_header("Consultancies");
-$query = "SELECT value, procurementMethod
-FROM `contractnotice`
-GROUP BY procurementMethod ";
+$query = 'SELECT SUM(value) as value, "procurementMethod"
+FROM contractnotice
+GROUP BY "procurementMethod" ';
$query = $conn->prepare($query);
--- a/displayContract.php
+++ b/displayContract.php
@@ -1,7 +1,6 @@
<?php
include_once("./lib/common.inc.php");
-include_header("Contract");
$query = 'SELECT *
FROM contractnotice
WHERE "CNID" = :CNID LIMIT 1';
@@ -9,32 +8,71 @@
$query = $conn->prepare($query);
$query->bindParam(":CNID", $_REQUEST['CNID']);
$query->execute();
+$contractResult = $query->fetch(PDO::FETCH_ASSOC);
+if (!$contractResult) {
+ header("Status: 404 Not Found");
+ header("HTTP/1.0 404 Not Found");
+include_header("Contract Not Found");
+ echo "<center><h1>No Contract Notice with that ID found</h1></center>";
databaseError($conn->errorInfo());
-foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) {
+} else {
+$description = ucsmart($contractResult["description"]);
+include_header($description);
+ echo '<center><h1>'.$description.'</h1></center>
+ <div about="http://contracts.disclosurelo.gs/displayContract.php?CNID='.$contractResult["CNID"].'" typeof="pc:Contract">';
+databaseError($conn->errorInfo());
setlocale(LC_MONETARY, 'en_US');
- foreach (array_filter($row) as $key => $value) {
+ foreach (array_filter($contractResult) as $key => $value) {
echo "<b>$key</b> ";
switch ($key) {
+ case "supplierABN":
+ break;
case "supplierName":
- case "supplierABN":
- echo '<a href="displaySupplier.php?supplier=' . $row['supplierABN'] . '-' . urlencode($row['supplierName']) . '">' . $value . "</a>";
+ echo '
+ <a href="displaySupplier.php?supplier=' . $contractResult['supplierABN'] . '-' . urlencode($contractResult['supplierName']) . '">
+ <span rel="pc:contractingAuthority" typeof="gr:BusinessEntity">
+ <span property="gr:legalName">'
+ . $contractResult['supplierName'] .
+ '</span>'.
+ (isset($contractResult['supplierABN']) && $contractResult['supplierABN'] != '' ? ' (ABN: <span property="br:officialNumber" lang="">'.$contractResult['supplierABN'].'</span>)' : '')
+ .'</span></a>';
+ break;
+ case "CNID":
+ echo '<span property="pc:referenceNumber" lang="">'.$value.'</span>';
+ break;
+ case "categoryUNSPSC":
+ break;
+ case "category":
+ echo '<span rel="pc:mainObject" resource="[unspsc:'.$contractResult["categoryUNSPSC"].']">'.$value.'</span>';
break;
case "agencyName":
echo '<a href="displayAgency.php?agency=' . urlencode($value) . '">' . $value . "</a>";
break;
+ case "contractStart":
+ echo '<span property="pc:startDate" content="'.$value.'" datatype="xsd:date">'.$value.'</span>';
+ break;
+ case "contractEnd":
+ echo '<span property="pc:endDate" content="'.$value.'" datatype="xsd:date">'.$value.'</span>';
+ break;
case "value":
- echo "$" . number_format(doubleval($value), 2);
+ echo '<span rel="pc:agreedPrice" typeof="gr:PriceSpecification">
+ <span property="gr:hasCurrency" content="AUD" lang="">$ </span>
+ <span property="gr:hasCurrencyValue" content="'.doubleval($value).'" datatype="xsd:float">' . number_format(doubleval($value), 2)
+ .'</span></span>';
break;
+ case "description":
+ echo '<span property="dcterms:title">'.$value.'</span>';
default:
- echo str_replace(" ", "<br>", $value);
+ echo str_replace(" ", "<br>", ucsmart($value));
}
- echo "<br>";
+ echo "<br>\n";
}
-}
-echo '<br><a href="https://www.tenders.gov.au/?event=public.advancedsearch.keyword&keyword=CN' . $_REQUEST['CNID'] . '"> View original record @ tenders.gov.au</a><br>';
+echo '<br><a href="https://www.tenders.gov.au/?event=public.advancedsearch.keyword&keyword=CN' . $_REQUEST['CNID'] . '"> View original record @ tenders.gov.au</a><br>
+
+</div>';
-$query = 'SELECT * FROM `heuristic_results` where "CNID" = :CNID';
+$query = 'SELECT * FROM heuristic_results where "CNID" = :CNID';
$query = $conn->prepare($query);
$agencyName = $input . '%';
$query->bindParam(":CNID", $_REQUEST['CNID']);
@@ -43,7 +81,7 @@
foreach ($query->fetchAll() as $r) {
echo "<b>{$r['heuristic_name']}</b>: {$r['heuristic_value']} (raw value: {$r['raw_value']}, mean: {$r['mean']}, stddev: {$r['stddev']})<br>";
}
-
+}
include_footer();
?>
--- a/displayProcurementMethod.php
+++ b/displayProcurementMethod.php
@@ -10,10 +10,10 @@
*/
include_header("Procurement Methods");
-$query = "SELECT SUM(value) as value, procurementMethod
-FROM `contractnotice`
-WHERE childCN is null
-GROUP BY procurementMethod ";
+$query = 'SELECT SUM(value) as value, "procurementMethod"
+FROM contractnotice
+WHERE "childCN" is null
+GROUP BY "procurementMethod"';
$query = $conn->prepare($query);
$query->execute();
--- a/displaySupplier.php
+++ b/displaySupplier.php
@@ -3,8 +3,10 @@
include_once ("./lib/common.inc.php");
if ($_REQUEST['supplier']) {
- include_header("Supplier");
$supplierS = htmlentities(strip_tags($_REQUEST['supplier']));
+ include_header(str_replace("%","",$supplierName));
+echo '<center><h1>'.str_replace("%","",$supplierName).'</h1></center>';
+
// MethodCountGraph($supplierS);
// CnCGraph($supplierS);
// MethodValueGraph($supplierS);
--- a/futureindex.php
+++ /dev/null
@@ -1,12 +1,1 @@
-<?php
-include_once ("./lib/common.inc.php");
-include_header("Home");
-echo ' <div class="hero-unit">
- <h1>Hello, world!</h1>
- <p>Vestibulum id ligula porta felis euismod semper. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.</p>
- <p><a class="btn primary large">Learn more »</a></p>
- </div>';
-include_footer();
-?>
-
--- /dev/null
+++ b/google676a414ad086cefb.html
@@ -1,1 +1,2 @@
+google-site-verification: google676a414ad086cefb.html
--- a/heuristics/metadataHeuristics.php
+++ b/heuristics/metadataHeuristics.php
@@ -1,7 +1,7 @@
<?php
/* all
SELECT description, count(*) as count
-FROM `contractnotice`
+FROM contractnotice
group by description having count > 1 order by count
*/
/*- duplicated description
@@ -48,7 +48,7 @@
{
$query = "select avg(count),STDDEV(count) from (
SELECT description, count(*) as count
-FROM `contractnotice`
+FROM contractnotice
group by description having count > 1
) as a;";
$result = $conn->query($query);
--- a/heuristics/runHeuristics.php
+++ b/heuristics/runHeuristics.php
@@ -1,4 +1,8 @@
<?php
+if (php_sapi_name() != "cli") {
+
+include_once ("../lib/common.inc.php");
+auth();
include_once("heuristics.inc.php");
$query = 'SELECT *, agency.abn as "agencyABN", case when "supplierABN" != 0 then "supplierABN"::text else "supplierName" end as "supplierID"
@@ -27,5 +31,6 @@
foreach CN
aggregate CN metrics */
+}
?>
--- a/heuristics/viewHeuristicsDistribution.php
+++ b/heuristics/viewHeuristicsDistribution.php
@@ -2,7 +2,7 @@
/*// most interesting
SELECT sum(heuristic_value) as sum, CNID
-FROM `heuristic_results` group by CNID order by sum DESC limit 30
+FROM heuristic_results group by CNID order by sum DESC limit 30
// spread of values
select floor(sum) as val,count(*) from (SELECT sum(heuristic_value)
--- /dev/null
+++ b/index.php
@@ -1,1 +1,12 @@
+<?php
+include_once ("./lib/common.inc.php");
+include_header("Home");
+echo ' <div class="hero-unit">
+ <h1>Hello, world!</h1>
+ <p>Vestibulum id ligula porta felis euismod semper. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.</p>
+ <p><a class="btn primary large">Learn more »</a></p>
+ </div>';
+include_footer();
+?>
+
--- /dev/null
+++ b/js/bootstrap-affix.js
@@ -1,1 +1,104 @@
+/* ==========================================================
+ * bootstrap-affix.js v2.1.0
+ * http://twitter.github.com/bootstrap/javascript.html#affix
+ * ==========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* AFFIX CLASS DEFINITION
+ * ====================== */
+
+ var Affix = function (element, options) {
+ this.options = $.extend({}, $.fn.affix.defaults, options)
+ this.$window = $(window).on('scroll.affix.data-api', $.proxy(this.checkPosition, this))
+ this.$element = $(element)
+ this.checkPosition()
+ }
+
+ Affix.prototype.checkPosition = function () {
+ if (!this.$element.is(':visible')) return
+
+ var scrollHeight = $(document).height()
+ , scrollTop = this.$window.scrollTop()
+ , position = this.$element.offset()
+ , offset = this.options.offset
+ , offsetBottom = offset.bottom
+ , offsetTop = offset.top
+ , reset = 'affix affix-top affix-bottom'
+ , affix
+
+ if (typeof offset != 'object') offsetBottom = offsetTop = offset
+ if (typeof offsetTop == 'function') offsetTop = offset.top()
+ if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
+
+ affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
+ false : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ?
+ 'bottom' : offsetTop != null && scrollTop <= offsetTop ?
+ 'top' : false
+
+ if (this.affixed === affix) return
+
+ this.affixed = affix
+ this.unpin = affix == 'bottom' ? position.top - scrollTop : null
+
+ this.$element.removeClass(reset).addClass('affix' + (affix ? '-' + affix : ''))
+ }
+
+
+ /* AFFIX PLUGIN DEFINITION
+ * ======================= */
+
+ $.fn.affix = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('affix')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('affix', (data = new Affix(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.affix.Constructor = Affix
+
+ $.fn.affix.defaults = {
+ offset: 0
+ }
+
+
+ /* AFFIX DATA-API
+ * ============== */
+
+ $(window).on('load', function () {
+ $('[data-spy="affix"]').each(function () {
+ var $spy = $(this)
+ , data = $spy.data()
+
+ data.offset = data.offset || {}
+
+ data.offsetBottom && (data.offset.bottom = data.offsetBottom)
+ data.offsetTop && (data.offset.top = data.offsetTop)
+
+ $spy.affix(data)
+ })
+ })
+
+
+}(window.jQuery);
--- /dev/null
+++ b/js/bootstrap-alert.js
@@ -1,1 +1,90 @@
+/* ==========================================================
+ * bootstrap-alert.js v2.1.0
+ * http://twitter.github.com/bootstrap/javascript.html#alerts
+ * ==========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* ALERT CLASS DEFINITION
+ * ====================== */
+
+ var dismiss = '[data-dismiss="alert"]'
+ , Alert = function (el) {
+ $(el).on('click', dismiss, this.close)
+ }
+
+ Alert.prototype.close = function (e) {
+ var $this = $(this)
+ , selector = $this.attr('data-target')
+ , $parent
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ $parent = $(selector)
+
+ e && e.preventDefault()
+
+ $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
+
+ $parent.trigger(e = $.Event('close'))
+
+ if (e.isDefaultPrevented()) return
+
+ $parent.removeClass('in')
+
+ function removeElement() {
+ $parent
+ .trigger('closed')
+ .remove()
+ }
+
+ $.support.transition && $parent.hasClass('fade') ?
+ $parent.on($.support.transition.end, removeElement) :
+ removeElement()
+ }
+
+
+ /* ALERT PLUGIN DEFINITION
+ * ======================= */
+
+ $.fn.alert = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('alert')
+ if (!data) $this.data('alert', (data = new Alert(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.alert.Constructor = Alert
+
+
+ /* ALERT DATA-API
+ * ============== */
+
+ $(function () {
+ $('body').on('click.alert.data-api', dismiss, Alert.prototype.close)
+ })
+
+}(window.jQuery);
--- /dev/null
+++ b/js/bootstrap-button.js
@@ -1,1 +1,96 @@
+/* ============================================================
+ * bootstrap-button.js v2.1.0
+ * http://twitter.github.com/bootstrap/javascript.html#buttons
+ * ============================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================ */
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* BUTTON PUBLIC CLASS DEFINITION
+ * ============================== */
+
+ var Button = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, $.fn.button.defaults, options)
+ }
+
+ Button.prototype.setState = function (state) {
+ var d = 'disabled'
+ , $el = this.$element
+ , data = $el.data()
+ , val = $el.is('input') ? 'val' : 'html'
+
+ state = state + 'Text'
+ data.resetText || $el.data('resetText', $el[val]())
+
+ $el[val](data[state] || this.options[state])
+
+ // push to event loop to allow forms to submit
+ setTimeout(function () {
+ state == 'loadingText' ?
+ $el.addClass(d).attr(d, d) :
+ $el.removeClass(d).removeAttr(d)
+ }, 0)
+ }
+
+ Button.prototype.toggle = function () {
+ var $parent = this.$element.parent('[data-toggle="buttons-radio"]')
+
+ $parent && $parent
+ .find('.active')
+ .removeClass('active')
+
+ this.$element.toggleClass('active')
+ }
+
+
+ /* BUTTON PLUGIN DEFINITION
+ * ======================== */
+
+ $.fn.button = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('button')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('button', (data = new Button(this, options)))
+ if (option == 'toggle') data.toggle()
+ else if (option) data.setState(option)
+ })
+ }
+
+ $.fn.button.defaults = {
+ loadingText: 'loading...'
+ }
+
+ $.fn.button.Constructor = Button
+
+
+ /* BUTTON DATA-API
+ * =============== */
+
+ $(function () {
+ $('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) {
+ var $btn = $(e.target)
+ if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
+ $btn.button('toggle')
+ })
+ })
+
+}(window.jQuery);
--- /dev/null
+++ b/js/bootstrap-carousel.js
@@ -1,1 +1,176 @@
+/* ==========================================================
+ * bootstrap-carousel.js v2.1.0
+ * http://twitter.github.com/bootstrap/javascript.html#carousel
+ * ==========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* CAROUSEL CLASS DEFINITION
+ * ========================= */
+
+ var Carousel = function (element, options) {
+ this.$element = $(element)
+ this.options = options
+ this.options.slide && this.slide(this.options.slide)
+ this.options.pause == 'hover' && this.$element
+ .on('mouseenter', $.proxy(this.pause, this))
+ .on('mouseleave', $.proxy(this.cycle, this))
+ }
+
+ Carousel.prototype = {
+
+ cycle: function (e) {
+ if (!e) this.paused = false
+ this.options.interval
+ && !this.paused
+ && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
+ return this
+ }
+
+ , to: function (pos) {
+ var $active = this.$element.find('.item.active')
+ , children = $active.parent().children()
+ , activePos = children.index($active)
+ , that = this
+
+ if (pos > (children.length - 1) || pos < 0) return
+
+ if (this.sliding) {
+ return this.$element.one('slid', function () {
+ that.to(pos)
+ })
+ }
+
+ if (activePos == pos) {
+ return this.pause().cycle()
+ }
+
+ return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos]))
+ }
+
+ , pause: function (e) {
+ if (!e) this.paused = true
+ if (this.$element.find('.next, .prev').length && $.support.transition.end) {
+ this.$element.trigger($.support.transition.end)
+ this.cycle()
+ }
+ clearInterval(this.interval)
+ this.interval = null
+ return this
+ }
+
+ , next: function () {
+ if (this.sliding) return
+ return this.slide('next')
+ }
+
+ , prev: function () {
+ if (this.sliding) return
+ return this.slide('prev')
+ }
+
+ , slide: function (type, next) {
+ var $active = this.$element.find('.item.active')
+ , $next = next || $active[type]()
+ , isCycling = this.interval
+ , direction = type == 'next' ? 'left' : 'right'
+ , fallback = type == 'next' ? 'first' : 'last'
+ , that = this
+ , e = $.Event('slide', {
+ relatedTarget: $next[0]
+ })
+
+ this.sliding = true
+
+ isCycling && this.pause()
+
+ $next = $next.length ? $next : this.$element.find('.item')[fallback]()
+
+ if ($next.hasClass('active')) return
+
+ if ($.support.transition && this.$element.hasClass('slide')) {
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
+ $next.addClass(type)
+ $next[0].offsetWidth // force reflow
+ $active.addClass(direction)
+ $next.addClass(direction)
+ this.$element.one($.support.transition.end, function () {
+ $next.removeClass([type, direction].join(' ')).addClass('active')
+ $active.removeClass(['active', direction].join(' '))
+ that.sliding = false
+ setTimeout(function () { that.$element.trigger('slid') }, 0)
+ })
+ } else {
+ this.$element.trigger(e)
+ if (e.isDefaultPrevented()) return
+ $active.removeClass('active')
+ $next.addClass('active')
+ this.sliding = false
+ this.$element.trigger('slid')
+ }
+
+ isCycling && this.cycle()
+
+ return this
+ }
+
+ }
+
+
+ /* CAROUSEL PLUGIN DEFINITION
+ * ========================== */
+
+ $.fn.carousel = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('carousel')
+ , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option)
+ , action = typeof option == 'string' ? option : options.slide
+ if (!data) $this.data('carousel', (data = new Carousel(this, options)))
+ if (typeof option == 'number') data.to(option)
+ else if (action) data[action]()
+ else if (options.interval) data.cycle()
+ })
+ }
+
+ $.fn.carousel.defaults = {
+ interval: 5000
+ , pause: 'hover'
+ }
+
+ $.fn.carousel.Constructor = Carousel
+
+
+ /* CAROUSEL DATA-API
+ * ================= */
+
+ $(function () {
+ $('body').on('click.carousel.data-api', '[data-slide]', function ( e ) {
+ var $this = $(this), href
+ , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+ , options = !$target.data('modal') && $.extend({}, $target.data(), $this.data())
+ $target.carousel(options)
+ e.preventDefault()
+ })
+ })
+
+}(window.jQuery);
--- /dev/null
+++ b/js/bootstrap-collapse.js
@@ -1,1 +1,158 @@
+/* =============================================================
+ * bootstrap-collapse.js v2.1.0
+ * http://twitter.github.com/bootstrap/javascript.html#collapse
+ * =============================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================ */
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* COLLAPSE PUBLIC CLASS DEFINITION
+ * ================================ */
+
+ var Collapse = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, $.fn.collapse.defaults, options)
+
+ if (this.options.parent) {
+ this.$parent = $(this.options.parent)
+ }
+
+ this.options.toggle && this.toggle()
+ }
+
+ Collapse.prototype = {
+
+ constructor: Collapse
+
+ , dimension: function () {
+ var hasWidth = this.$element.hasClass('width')
+ return hasWidth ? 'width' : 'height'
+ }
+
+ , show: function () {
+ var dimension
+ , scroll
+ , actives
+ , hasData
+
+ if (this.transitioning) return
+
+ dimension = this.dimension()
+ scroll = $.camelCase(['scroll', dimension].join('-'))
+ actives = this.$parent && this.$parent.find('> .accordion-group > .in')
+
+ if (actives && actives.length) {
+ hasData = actives.data('collapse')
+ if (hasData && hasData.transitioning) return
+ actives.collapse('hide')
+ hasData || actives.data('collapse', null)
+ }
+
+ this.$element[dimension](0)
+ this.transition('addClass', $.Event('show'), 'shown')
+ $.support.transition && this.$element[dimension](this.$element[0][scroll])
+ }
+
+ , hide: function () {
+ var dimension
+ if (this.transitioning) return
+ dimension = this.dimension()
+ this.reset(this.$element[dimension]())
+ this.transition('removeClass', $.Event('hide'), 'hidden')
+ this.$element[dimension](0)
+ }
+
+ , reset: function (size) {
+ var dimension = this.dimension()
+
+ this.$element
+ .removeClass('collapse')
+ [dimension](size || 'auto')
+ [0].offsetWidth
+
+ this.$element[size !== null ? 'addClass' : 'removeClass']('collapse')
+
+ return this
+ }
+
+ , transition: function (method, startEvent, completeEvent) {
+ var that = this
+ , complete = function () {
+ if (startEvent.type == 'show') that.reset()
+ that.transitioning = 0
+ that.$element.trigger(completeEvent)
+ }
+
+ this.$element.trigger(startEvent)
+
+ if (startEvent.isDefaultPrevented()) return
+
+ this.transitioning = 1
+
+ this.$element[method]('in')
+
+ $.support.transition && this.$element.hasClass('collapse') ?
+ this.$element.one($.support.transition.end, complete) :
+ complete()
+ }
+
+ , toggle: function () {
+ this[this.$element.hasClass('in') ? 'hide' : 'show']()
+ }
+
+ }
+
+
+ /* COLLAPSIBLE PLUGIN DEFINITION
+ * ============================== */
+
+ $.fn.collapse = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('collapse')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('collapse', (data = new Collapse(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.collapse.defaults = {
+ toggle: true
+ }
+
+ $.fn.collapse.Constructor = Collapse
+
+
+ /* COLLAPSIBLE DATA-API
+ * ==================== */
+
+ $(function () {
+ $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function (e) {
+ var $this = $(this), href
+ , target = $this.attr('data-target')
+ || e.preventDefault()
+ || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
+ , option = $(target).data('collapse') ? 'toggle' : $this.data()
+ $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
+ $(target).collapse(option)
+ })
+ })
+
+}(window.jQuery);
--- /dev/null
+++ b/js/bootstrap-dropdown.js
@@ -1,1 +1,150 @@
+/* ============================================================
+ * bootstrap-dropdown.js v2.1.0
+ * http://twitter.github.com/bootstrap/javascript.html#dropdowns
+ * ============================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================ */
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* DROPDOWN CLASS DEFINITION
+ * ========================= */
+
+ var toggle = '[data-toggle=dropdown]'
+ , Dropdown = function (element) {
+ var $el = $(element).on('click.dropdown.data-api', this.toggle)
+ $('html').on('click.dropdown.data-api', function () {
+ $el.parent().removeClass('open')
+ })
+ }
+
+ Dropdown.prototype = {
+
+ constructor: Dropdown
+
+ , toggle: function (e) {
+ var $this = $(this)
+ , $parent
+ , isActive
+
+ if ($this.is('.disabled, :disabled')) return
+
+ $parent = getParent($this)
+
+ isActive = $parent.hasClass('open')
+
+ clearMenus()
+
+ if (!isActive) {
+ $parent.toggleClass('open')
+ $this.focus()
+ }
+
+ return false
+ }
+
+ , keydown: function (e) {
+ var $this
+ , $items
+ , $active
+ , $parent
+ , isActive
+ , index
+
+ if (!/(38|40|27)/.test(e.keyCode)) return
+
+ $this = $(this)
+
+ e.preventDefault()
+ e.stopPropagation()
+
+ if ($this.is('.disabled, :disabled')) return
+
+ $parent = getParent($this)
+
+ isActive = $parent.hasClass('open')
+
+ if (!isActive || (isActive && e.keyCode == 27)) return $this.click()
+
+ $items = $('[role=menu] li:not(.divider) a', $parent)
+
+ if (!$items.length) return
+
+ index = $items.index($items.filter(':focus'))
+
+ if (e.keyCode == 38 && index > 0) index-- // up
+ if (e.keyCode == 40 && index < $items.length - 1) index++ // down
+ if (!~index) index = 0
+
+ $items
+ .eq(index)
+ .focus()
+ }
+
+ }
+
+ function clearMenus() {
+ getParent($(toggle))
+ .removeClass('open')
+ }
+
+ function getParent($this) {
+ var selector = $this.attr('data-target')
+ , $parent
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ $parent = $(selector)
+ $parent.length || ($parent = $this.parent())
+
+ return $parent
+ }
+
+
+ /* DROPDOWN PLUGIN DEFINITION
+ * ========================== */
+
+ $.fn.dropdown = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('dropdown')
+ if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+ if (typeof option == 'string') data[option].call($this)
+ })
+ }
+
+ $.fn.dropdown.Constructor = Dropdown
+
+
+ /* APPLY TO STANDARD DROPDOWN ELEMENTS
+ * =================================== */
+
+ $(function () {
+ $('html')
+ .on('click.dropdown.data-api touchstart.dropdown.data-api', clearMenus)
+ $('body')
+ .on('click.dropdown touchstart.dropdown.data-api', '.dropdown', function (e) { e.stopPropagation() })
+ .on('click.dropdown.data-api touchstart.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
+ .on('keydown.dropdown.data-api touchstart.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
+ })
+
+}(window.jQuery);
--- /dev/null
+++ b/js/bootstrap-modal.js
@@ -1,1 +1,239 @@
-
+/* =========================================================
+ * bootstrap-modal.js v2.1.0
+ * http://twitter.github.com/bootstrap/javascript.html#modals
+ * =========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================= */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* MODAL CLASS DEFINITION
+ * ====================== */
+
+ var Modal = function (element, options) {
+ this.options = options
+ this.$element = $(element)
+ .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
+ this.options.remote && this.$element.find('.modal-body').load(this.options.remote)
+ }
+
+ Modal.prototype = {
+
+ constructor: Modal
+
+ , toggle: function () {
+ return this[!this.isShown ? 'show' : 'hide']()
+ }
+
+ , show: function () {
+ var that = this
+ , e = $.Event('show')
+
+ this.$element.trigger(e)
+
+ if (this.isShown || e.isDefaultPrevented()) return
+
+ $('body').addClass('modal-open')
+
+ this.isShown = true
+
+ this.escape()
+
+ this.backdrop(function () {
+ var transition = $.support.transition && that.$element.hasClass('fade')
+
+ if (!that.$element.parent().length) {
+ that.$element.appendTo(document.body) //don't move modals dom position
+ }
+
+ that.$element
+ .show()
+
+ if (transition) {
+ that.$element[0].offsetWidth // force reflow
+ }
+
+ that.$element
+ .addClass('in')
+ .attr('aria-hidden', false)
+ .focus()
+
+ that.enforceFocus()
+
+ transition ?
+ that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
+ that.$element.trigger('shown')
+
+ })
+ }
+
+ , hide: function (e) {
+ e && e.preventDefault()
+
+ var that = this
+
+ e = $.Event('hide')
+
+ this.$element.trigger(e)
+
+ if (!this.isShown || e.isDefaultPrevented()) return
+
+ this.isShown = false
+
+ $('body').removeClass('modal-open')
+
+ this.escape()
+
+ $(document).off('focusin.modal')
+
+ this.$element
+ .removeClass('in')
+ .attr('aria-hidden', true)
+
+ $.support.transition && this.$element.hasClass('fade') ?
+ this.hideWithTransition() :
+ this.hideModal()
+ }
+
+ , enforceFocus: function () {
+ var that = this
+ $(document).on('focusin.modal', function (e) {
+ if (that.$element[0] !== e.target && !that.$element.has(e.target).length) {
+ that.$element.focus()
+ }
+ })
+ }
+
+ , escape: function () {
+ var that = this
+ if (this.isShown && this.options.keyboard) {
+ this.$element.on('keyup.dismiss.modal', function ( e ) {
+ e.which == 27 && that.hide()
+ })
+ } else if (!this.isShown) {
+ this.$element.off('keyup.dismiss.modal')
+ }
+ }
+
+ , hideWithTransition: function () {
+ var that = this
+ , timeout = setTimeout(function () {
+ that.$element.off($.support.transition.end)
+ that.hideModal()
+ }, 500)
+
+ this.$element.one($.support.transition.end, function () {
+ clearTimeout(timeout)
+ that.hideModal()
+ })
+ }
+
+ , hideModal: function (that) {
+ this.$element
+ .hide()
+ .trigger('hidden')
+
+ this.backdrop()
+ }
+
+ , removeBackdrop: function () {
+ this.$backdrop.remove()
+ this.$backdrop = null
+ }
+
+ , backdrop: function (callback) {
+ var that = this
+ , animate = this.$element.hasClass('fade') ? 'fade' : ''
+
+ if (this.isShown && this.options.backdrop) {
+ var doAnimate = $.support.transition && animate
+
+ this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
+ .appendTo(document.body)
+
+ if (this.options.backdrop != 'static') {
+ this.$backdrop.click($.proxy(this.hide, this))
+ }
+
+ if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
+
+ this.$backdrop.addClass('in')
+
+ doAnimate ?
+ this.$backdrop.one($.support.transition.end, callback) :
+ callback()
+
+ } else if (!this.isShown && this.$backdrop) {
+ this.$backdrop.removeClass('in')
+
+ $.support.transition && this.$element.hasClass('fade')?
+ this.$backdrop.one($.support.transition.end, $.proxy(this.removeBackdrop, this)) :
+ this.removeBackdrop()
+
+ } else if (callback) {
+ callback()
+ }
+ }
+ }
+
+
+ /* MODAL PLUGIN DEFINITION
+ * ======================= */
+
+ $.fn.modal = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('modal')
+ , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
+ if (!data) $this.data('modal', (data = new Modal(this, options)))
+ if (typeof option == 'string') data[option]()
+ else if (options.show) data.show()
+ })
+ }
+
+ $.fn.modal.defaults = {
+ backdrop: true
+ , keyboard: true
+ , show: true
+ }
+
+ $.fn.modal.Constructor = Modal
+
+
+ /* MODAL DATA-API
+ * ============== */
+
+ $(function () {
+ $('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
+ var $this = $(this)
+ , href = $this.attr('href')
+ , $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
+ , option = $target.data('modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
+
+ e.preventDefault()
+
+ $target
+ .modal(option)
+ .one('hide', function () {
+ $this.focus()
+ })
+ })
+ })
+
+}(window.jQuery);
--- /dev/null
+++ b/js/bootstrap-popover.js
@@ -1,1 +1,103 @@
+/* ===========================================================
+ * bootstrap-popover.js v2.1.0
+ * http://twitter.github.com/bootstrap/javascript.html#popovers
+ * ===========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * =========================================================== */
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* POPOVER PUBLIC CLASS DEFINITION
+ * =============================== */
+
+ var Popover = function (element, options) {
+ this.init('popover', element, options)
+ }
+
+
+ /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
+ ========================================== */
+
+ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
+
+ constructor: Popover
+
+ , setContent: function () {
+ var $tip = this.tip()
+ , title = this.getTitle()
+ , content = this.getContent()
+
+ $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
+ $tip.find('.popover-content > *')[this.options.html ? 'html' : 'text'](content)
+
+ $tip.removeClass('fade top bottom left right in')
+ }
+
+ , hasContent: function () {
+ return this.getTitle() || this.getContent()
+ }
+
+ , getContent: function () {
+ var content
+ , $e = this.$element
+ , o = this.options
+
+ content = $e.attr('data-content')
+ || (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
+
+ return content
+ }
+
+ , tip: function () {
+ if (!this.$tip) {
+ this.$tip = $(this.options.template)
+ }
+ return this.$tip
+ }
+
+ , destroy: function () {
+ this.hide().$element.off('.' + this.type).removeData(this.type)
+ }
+
+ })
+
+
+ /* POPOVER PLUGIN DEFINITION
+ * ======================= */
+
+ $.fn.popover = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('popover')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('popover', (data = new Popover(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.popover.Constructor = Popover
+
+ $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
+ placement: 'right'
+ , trigger: 'click'
+ , content: ''
+ , template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'
+ })
+
+}(window.jQuery);
--- /dev/null
+++ b/js/bootstrap-scrollspy.js
@@ -1,1 +1,151 @@
+/* =============================================================
+ * bootstrap-scrollspy.js v2.1.0
+ * http://twitter.github.com/bootstrap/javascript.html#scrollspy
+ * =============================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================== */
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* SCROLLSPY CLASS DEFINITION
+ * ========================== */
+
+ function ScrollSpy(element, options) {
+ var process = $.proxy(this.process, this)
+ , $element = $(element).is('body') ? $(window) : $(element)
+ , href
+ this.options = $.extend({}, $.fn.scrollspy.defaults, options)
+ this.$scrollElement = $element.on('scroll.scroll-spy.data-api', process)
+ this.selector = (this.options.target
+ || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
+ || '') + ' .nav li > a'
+ this.$body = $('body')
+ this.refresh()
+ this.process()
+ }
+
+ ScrollSpy.prototype = {
+
+ constructor: ScrollSpy
+
+ , refresh: function () {
+ var self = this
+ , $targets
+
+ this.offsets = $([])
+ this.targets = $([])
+
+ $targets = this.$body
+ .find(this.selector)
+ .map(function () {
+ var $el = $(this)
+ , href = $el.data('target') || $el.attr('href')
+ , $href = /^#\w/.test(href) && $(href)
+ return ( $href
+ && $href.length
+ && [[ $href.position().top, href ]] ) || null
+ })
+ .sort(function (a, b) { return a[0] - b[0] })
+ .each(function () {
+ self.offsets.push(this[0])
+ self.targets.push(this[1])
+ })
+ }
+
+ , process: function () {
+ var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
+ , scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
+ , maxScroll = scrollHeight - this.$scrollElement.height()
+ , offsets = this.offsets
+ , targets = this.targets
+ , activeTarget = this.activeTarget
+ , i
+
+ if (scrollTop >= maxScroll) {
+ return activeTarget != (i = targets.last()[0])
+ && this.activate ( i )
+ }
+
+ for (i = offsets.length; i--;) {
+ activeTarget != targets[i]
+ && scrollTop >= offsets[i]
+ && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
+ && this.activate( targets[i] )
+ }
+ }
+
+ , activate: function (target) {
+ var active
+ , selector
+
+ this.activeTarget = target
+
+ $(this.selector)
+ .parent('.active')
+ .removeClass('active')
+
+ selector = this.selector
+ + '[data-target="' + target + '"],'
+ + this.selector + '[href="' + target + '"]'
+
+ active = $(selector)
+ .parent('li')
+ .addClass('active')
+
+ if (active.parent('.dropdown-menu').length) {
+ active = active.closest('li.dropdown').addClass('active')
+ }
+
+ active.trigger('activate')
+ }
+
+ }
+
+
+ /* SCROLLSPY PLUGIN DEFINITION
+ * =========================== */
+
+ $.fn.scrollspy = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('scrollspy')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.scrollspy.Constructor = ScrollSpy
+
+ $.fn.scrollspy.defaults = {
+ offset: 10
+ }
+
+
+ /* SCROLLSPY DATA-API
+ * ================== */
+
+ $(window).on('load', function () {
+ $('[data-spy="scroll"]').each(function () {
+ var $spy = $(this)
+ $spy.scrollspy($spy.data())
+ })
+ })
+
+}(window.jQuery);
--- /dev/null
+++ b/js/bootstrap-tab.js
@@ -1,1 +1,135 @@
+/* ========================================================
+ * bootstrap-tab.js v2.1.0
+ * http://twitter.github.com/bootstrap/javascript.html#tabs
+ * ========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ======================================================== */
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* TAB CLASS DEFINITION
+ * ==================== */
+
+ var Tab = function (element) {
+ this.element = $(element)
+ }
+
+ Tab.prototype = {
+
+ constructor: Tab
+
+ , show: function () {
+ var $this = this.element
+ , $ul = $this.closest('ul:not(.dropdown-menu)')
+ , selector = $this.attr('data-target')
+ , previous
+ , $target
+ , e
+
+ if (!selector) {
+ selector = $this.attr('href')
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
+ }
+
+ if ( $this.parent('li').hasClass('active') ) return
+
+ previous = $ul.find('.active a').last()[0]
+
+ e = $.Event('show', {
+ relatedTarget: previous
+ })
+
+ $this.trigger(e)
+
+ if (e.isDefaultPrevented()) return
+
+ $target = $(selector)
+
+ this.activate($this.parent('li'), $ul)
+ this.activate($target, $target.parent(), function () {
+ $this.trigger({
+ type: 'shown'
+ , relatedTarget: previous
+ })
+ })
+ }
+
+ , activate: function ( element, container, callback) {
+ var $active = container.find('> .active')
+ , transition = callback
+ && $.support.transition
+ && $active.hasClass('fade')
+
+ function next() {
+ $active
+ .removeClass('active')
+ .find('> .dropdown-menu > .active')
+ .removeClass('active')
+
+ element.addClass('active')
+
+ if (transition) {
+ element[0].offsetWidth // reflow for transition
+ element.addClass('in')
+ } else {
+ element.removeClass('fade')
+ }
+
+ if ( element.parent('.dropdown-menu') ) {
+ element.closest('li.dropdown').addClass('active')
+ }
+
+ callback && callback()
+ }
+
+ transition ?
+ $active.one($.support.transition.end, next) :
+ next()
+
+ $active.removeClass('in')
+ }
+ }
+
+
+ /* TAB PLUGIN DEFINITION
+ * ===================== */
+
+ $.fn.tab = function ( option ) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('tab')
+ if (!data) $this.data('tab', (data = new Tab(this)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.tab.Constructor = Tab
+
+
+ /* TAB DATA-API
+ * ============ */
+
+ $(function () {
+ $('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
+ e.preventDefault()
+ $(this).tab('show')
+ })
+ })
+
+}(window.jQuery);
--- /dev/null
+++ b/js/bootstrap-tooltip.js
@@ -1,1 +1,276 @@
-
+/* ===========================================================
+ * bootstrap-tooltip.js v2.1.0
+ * http://twitter.github.com/bootstrap/javascript.html#tooltips
+ * Inspired by the original jQuery.tipsy by Jason Frame
+ * ===========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+
+!function ($) {
+
+ "use strict"; // jshint ;_;
+
+
+ /* TOOLTIP PUBLIC CLASS DEFINITION
+ * =============================== */
+
+ var Tooltip = function (element, options) {
+ this.init('tooltip', element, options)
+ }
+
+ Tooltip.prototype = {
+
+ constructor: Tooltip
+
+ , init: function (type, element, options) {
+ var eventIn
+ , eventOut
+
+ this.type = type
+ this.$element = $(element)
+ this.options = this.getOptions(options)
+ this.enabled = true
+
+ if (this.options.trigger == 'click') {
+ this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
+ } else if (this.options.trigger != 'manual') {
+ eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
+ eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
+ this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
+ this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
+ }
+
+ this.options.selector ?
+ (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
+ this.fixTitle()
+ }
+
+ , getOptions: function (options) {
+ options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
+
+ if (options.delay && typeof options.delay == 'number') {
+ options.delay = {
+ show: options.delay
+ , hide: options.delay
+ }
+ }
+
+ return options
+ }
+
+ , enter: function (e) {
+ var self = $(e.currentTarget)[this.type](this._options).data(this.type)
+
+ if (!self.options.delay || !self.options.delay.show) return self.show()
+
+ clearTimeout(this.timeout)
+ self.hoverState = 'in'
+ this.timeout = setTimeout(function() {
+ if (self.hoverState == 'in') self.show()
+ }, self.options.delay.show)
+ }
+
+ , leave: function (e) {
+ var self = $(e.currentTarget)[this.type](this._options).data(this.type)
+
+ if (this.timeout) clearTimeout(this.timeout)
+ if (!self.options.delay || !self.options.delay.hide) return self.hide()
+
+ self.hoverState = 'out'
+ this.timeout = setTimeout(function() {
+ if (self.hoverState == 'out') self.hide()
+ }, self.options.delay.hide)
+ }
+
+ , show: function () {
+ var $tip
+ , inside
+ , pos
+ , actualWidth
+ , actualHeight
+ , placement
+ , tp
+
+ if (this.hasContent() && this.enabled) {
+ $tip = this.tip()
+ this.setContent()
+
+ if (this.options.animation) {
+ $tip.addClass('fade')
+ }
+
+ placement = typeof this.options.placement == 'function' ?
+ this.options.placement.call(this, $tip[0], this.$element[0]) :
+ this.options.placement
+
+ inside = /in/.test(placement)
+
+ $tip
+ .remove()
+ .css({ top: 0, left: 0, display: 'block' })
+ .appendTo(inside ? this.$element : document.body)
+
+ pos = this.getPosition(inside)
+
+ actualWidth = $tip[0].offsetWidth
+ actualHeight = $tip[0].offsetHeight
+
+ switch (inside ? placement.split(' ')[1] : placement) {
+ case 'bottom':
+ tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
+ break
+ case 'top':
+ tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
+ break
+ case 'left':
+ tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
+ break
+ case 'right':
+ tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
+ break
+ }
+
+ $tip
+ .css(tp)
+ .addClass(placement)
+ .addClass('in')
+ }
+ }
+
+ , setContent: function () {
+ var $tip = this.tip()
+ , title = this.getTitle()
+
+ $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
+ $tip.removeClass('fade in top bottom left right')
+ }
+
+ , hide: function () {
+ var that = this
+ , $tip = this.tip()
+
+ $tip.removeClass('in')
+
+ function removeWithAnimation() {
+ var timeout = setTimeout(function () {
+ $tip.off($.support.transition.end).remove()
+ }, 500)
+
+ $tip.one($.support.transition.end, function () {
+ clearTimeout(timeout)
+ $tip.remove()
+ })
+ }
+
+ $.support.transition && this.$tip.hasClass('fade') ?
+ removeWithAnimation() :
+ $tip.remove()
+
+ return this
+ }
+
+ , fixTitle: function () {
+ var $e = this.$element
+ if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
+ $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
+ }
+ }
+
+ , hasContent: function () {
+ return this.getTitle()
+ }
+
+ , getPosition: function (inside) {
+ return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
+ width: this.$element[0].offsetWidth
+ , height: this.$element[0].offsetHeight
+ })
+ }
+
+ , getTitle: function () {
+ var title
+ , $e = this.$element
+ , o = this.options
+
+ title = $e.attr('data-original-title')
+ || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
+
+ return title
+ }
+
+ , tip: function () {
+ return this.$tip = this.$tip || $(this.options.template)
+ }
+
+ , validate: function () {
+ if (!this.$element[0].parentNode) {
+ this.hide()
+ this.$element = null
+ this.options = null
+ }
+ }
+
+ , enable: function () {
+ this.enabled = true
+ }
+
+ , disable: function () {
+ this.enabled = false
+ }
+
+ , toggleEnabled: function () {
+ this.enabled = !this.enabled
+ }
+
+ , toggle: function () {
+ this[this.tip().hasClass('in') ? 'hide' : 'show']()
+ }
+
+ , destroy: function () {
+ this.hide().$element.off('.' + this.type).removeData(this.type)
+ }
+
+ }
+
+
+ /* TOOLTIP PLUGIN DEFINITION
+ * ========================= */
+
+ $.fn.tooltip = function ( option ) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('tooltip')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.tooltip.Constructor = Tooltip
+
+ $.fn.tooltip.defaults = {
+ animation: true
+ , placement: 'top'
+ , selector: false
+ , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
+ , trigger: 'hover'
+ , title: ''
+ , delay: 0
+ , html: true
+ }
+
+}(window.jQuery);
+
--- /dev/null
+++ b/js/bootstrap-transition.js
@@ -1,1 +1,60 @@
+/* ===================================================
+ * bootstrap-transition.js v2.1.0
+ * http://twitter.github.com/bootstrap/javascript.html#transitions
+ * ===================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+!function ($) {
+
+ $(function () {
+
+ "use strict"; // jshint ;_;
+
+
+ /* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
+ * ======================================================= */
+
+ $.support.transition = (function () {
+
+ var transitionEnd = (function () {
+
+ var el = document.createElement('bootstrap')
+ , transEndEventNames = {
+ 'WebkitTransition' : 'webkitTransitionEnd'
+ , 'MozTransition' : 'transitionend'
+ , 'OTransition' : 'oTransitionEnd otransitionend'
+ , 'transition' : 'transitionend'
+ }
+ , name
+
+ for (name in transEndEventNames){
+ if (el.style[name] !== undefined) {
+ return transEndEventNames[name]
+ }
+ }
+
+ }())
+
+ return transitionEnd && {
+ end: transitionEnd
+ }
+
+ })()
+
+ })
+
+}(window.jQuery);
--- /dev/null
+++ b/js/bootstrap-typeahead.js
@@ -1,1 +1,301 @@
-
+/* =============================================================
+ * bootstrap-typeahead.js v2.1.0
+ * http://twitter.github.com/bootstrap/javascript.html#typeahead
+ * =============================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================ */
+
+
+!function($){
+
+ "use strict"; // jshint ;_;
+
+
+ /* TYPEAHEAD PUBLIC CLASS DEFINITION
+ * ================================= */
+
+ var Typeahead = function (element, options) {
+ this.$element = $(element)
+ this.options = $.extend({}, $.fn.typeahead.defaults, options)
+ this.matcher = this.options.matcher || this.matcher
+ this.sorter = this.options.sorter || this.sorter
+ this.highlighter = this.options.highlighter || this.highlighter
+ this.updater = this.options.updater || this.updater
+ this.$menu = $(this.options.menu).appendTo('body')
+ this.source = this.options.source
+ this.shown = false
+ this.listen()
+ }
+
+ Typeahead.prototype = {
+
+ constructor: Typeahead
+
+ , select: function () {
+ var val = this.$menu.find('.active').attr('data-value')
+ this.$element
+ .val(this.updater(val))
+ .change()
+ return this.hide()
+ }
+
+ , updater: function (item) {
+ return item
+ }
+
+ , show: function () {
+ var pos = $.extend({}, this.$element.offset(), {
+ height: this.$element[0].offsetHeight
+ })
+
+ this.$menu.css({
+ top: pos.top + pos.height
+ , left: pos.left
+ })
+
+ this.$menu.show()
+ this.shown = true
+ return this
+ }
+
+ , hide: function () {
+ this.$menu.hide()
+ this.shown = false
+ return this
+ }
+
+ , lookup: function (event) {
+ var items
+
+ this.query = this.$element.val()
+
+ if (!this.query || this.query.length < this.options.minLength) {
+ return this.shown ? this.hide() : this
+ }
+
+ items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source
+
+ return items ? this.process(items) : this
+ }
+
+ , process: function (items) {
+ var that = this
+
+ items = $.grep(items, function (item) {
+ return that.matcher(item)
+ })
+
+ items = this.sorter(items)
+
+ if (!items.length) {
+ return this.shown ? this.hide() : this
+ }
+
+ return this.render(items.slice(0, this.options.items)).show()
+ }
+
+ , matcher: function (item) {
+ return ~item.toLowerCase().indexOf(this.query.toLowerCase())
+ }
+
+ , sorter: function (items) {
+ var beginswith = []
+ , caseSensitive = []
+ , caseInsensitive = []
+ , item
+
+ while (item = items.shift()) {
+ if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
+ else if (~item.indexOf(this.query)) caseSensitive.push(item)
+ else caseInsensitive.push(item)
+ }
+
+ return beginswith.concat(caseSensitive, caseInsensitive)
+ }
+
+ , highlighter: function (item) {
+ var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
+ return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
+ return '<strong>' + match + '</strong>'
+ })
+ }
+
+ , render: function (items) {
+ var that = this
+
+ items = $(items).map(function (i, item) {
+ i = $(that.options.item).attr('data-value', item)
+ i.find('a').html(that.highlighter(item))
+ return i[0]
+ })
+
+ items.first().addClass('active')
+ this.$menu.html(items)
+ return this
+ }
+
+ , next: function (event) {
+ var active = this.$menu.find('.active').removeClass('active')
+ , next = active.next()
+
+ if (!next.length) {
+ next = $(this.$menu.find('li')[0])
+ }
+
+ next.addClass('active')
+ }
+
+ , prev: function (event) {
+ var active = this.$menu.find('.active').removeClass('active')
+ , prev = active.prev()
+
+ if (!prev.length) {
+ prev = this.$menu.find('li').last()
+ }
+
+ prev.addClass('active')
+ }
+
+ , listen: function () {
+ this.$element
+ .on('blur', $.proxy(this.blur, this))
+ .on('keypress', $.proxy(this.keypress, this))
+ .on('keyup', $.proxy(this.keyup, this))
+
+ if ($.browser.webkit || $.browser.msie) {
+ this.$element.on('keydown', $.proxy(this.keydown, this))
+ }
+
+ this.$menu
+ .on('click', $.proxy(this.click, this))
+ .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
+ }
+
+ , move: function (e) {
+ if (!this.shown) return
+
+ switch(e.keyCode) {
+ case 9: // tab
+ case 13: // enter
+ case 27: // escape
+ e.preventDefault()
+ break
+
+ case 38: // up arrow
+ e.preventDefault()
+ this.prev()
+ break
+
+ case 40: // down arrow
+ e.preventDefault()
+ this.next()
+ break
+ }
+
+ e.stopPropagation()
+ }
+
+ , keydown: function (e) {
+ this.suppressKeyPressRepeat = !~$.inArray(e.keyCode, [40,38,9,13,27])
+ this.move(e)
+ }
+
+ , keypress: function (e) {
+ if (this.suppressKeyPressRepeat) return
+ this.move(e)
+ }
+
+ , keyup: function (e) {
+ switch(e.keyCode) {
+ case 40: // down arrow
+ case 38: // up arrow
+ break
+
+ case 9: // tab
+ case 13: // enter
+ if (!this.shown) return
+ this.select()
+ break
+
+ case 27: // escape
+ if (!this.shown) return
+ this.hide()
+ break
+
+ default:
+ this.lookup()
+ }
+
+ e.stopPropagation()
+ e.preventDefault()
+ }
+
+ , blur: function (e) {
+ var that = this
+ setTimeout(function () { that.hide() }, 150)
+ }
+
+ , click: function (e) {
+ e.stopPropagation()
+ e.preventDefault()
+ this.select()
+ }
+
+ , mouseenter: function (e) {
+ this.$menu.find('.active').removeClass('active')
+ $(e.currentTarget).addClass('active')
+ }
+
+ }
+
+
+ /* TYPEAHEAD PLUGIN DEFINITION
+ * =========================== */
+
+ $.fn.typeahead = function (option) {
+ return this.each(function () {
+ var $this = $(this)
+ , data = $this.data('typeahead')
+ , options = typeof option == 'object' && option
+ if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
+ if (typeof option == 'string') data[option]()
+ })
+ }
+
+ $.fn.typeahead.defaults = {
+ source: []
+ , items: 8
+ , menu: '<ul class="typeahead dropdown-menu"></ul>'
+ , item: '<li><a href="#"></a></li>'
+ , minLength: 1
+ }
+
+ $.fn.typeahead.Constructor = Typeahead
+
+
+ /* TYPEAHEAD DATA-API
+ * ================== */
+
+ $(function () {
+ $('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
+ var $this = $(this)
+ if ($this.data('typeahead')) return
+ e.preventDefault()
+ $this.typeahead($this.data())
+ })
+ })
+
+}(window.jQuery);
+
--- /dev/null
+++ b/lib/amon-php
--- a/lib/common.inc.php
+++ b/lib/common.inc.php
@@ -8,12 +8,53 @@
if (!$conn) {
die("A database error occurred.\n");
+}
+
+define('ROOT', pathinfo(__FILE__, PATHINFO_DIRNAME));
+if (strstr($_SERVER['PHP_SELF'], "labs/")) {
+ $basePath = "../";
+}
+require $basePath."lib/amon-php/amon.php";
+Amon::config(array('address'=> 'http://127.0.0.1:2465',
+ 'protocol' => 'http',
+ 'secret_key' => "g99127n3lkzigg8ob2rllth97d1pb4sj"));
+Amon::setup_exception_handler();
+
+require ROOT . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'openid.php';
+$openid = new LightOpenID($_SERVER['HTTP_HOST']);
+
+function login() {
+ global $openid;
+ if (!$openid->mode) {
+ $openid->required = array('contact/email');
+ $openid->identity = 'https://www.google.com/accounts/o8/id';
+ header('Location: ' . $openid->authUrl());
+ }
+}
+
+function auth() {
+ global $openid;
+ if ($_SESSION['authed'] == true) {
+ return true;
+ }
+
+ if ($openid->mode) {
+ $attr = $openid->getAttributes();
+ if ($attr['contact/email'] != 'maxious@gmail.com') {
+ die('Access Denied');
+ } else {
+ $_SESSION['authed'] = true;
+ }
+ } else {
+ login();
+ }
}
// $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
function databaseError($errMsg) {
if ($errMsg[2] != "") {
echo '<div class="alert-message error">';
+ Amon::log(print_r($errMsg, true), array('error'));
die(print_r($errMsg, true));
echo "</div>";
}
@@ -65,21 +106,45 @@
$startYear = 2007;
$year = filter_var($_REQUEST['year'], FILTER_SANITIZE_NUMBER_INT);
-if ($year != "")
+if ($year != "") {
$yearQ = "YEAR(publishDate) = " . $year . " AND ";
-
+}
$standardQ = ' "childCN" is null '; // AND YEAR(contractStart) >= 2007 AND YEAR(contractStart) <= 2010';
$start = 0.0;
+
+function local_url() {
+ return "http://" . $_SERVER['HTTP_HOST'] . rtrim(dirname($_SERVER['PHP_SELF']), '/\\') . "/";
+}
function include_header($title) {
global $start;
?>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
- "http://www.w3.org/TR/html4/strict.dtd">
- <html>
+ <!DOCTYPE html>
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" version="XHTML+RDFa 1.1"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+ xmlns:gr="http://purl.org/goodrelations/v1#"
+ xmlns:dc="http://purl.org/dc/terms/"
+ xmlns:pc="http://purl.org/procurement#"
+ xmlns:unspsc="http://www.ksl.stanford.edu/projects/DAML/UNSPSC.daml#"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
+ xmlns:pcdt="http://purl.org/procurement/public-contracts-datatypes#"
+ prefix="rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#
+ rdfs: http://www.w3.org/2000/01/rdf-schema#
+ gr: http://purl.org/goodrelations/v1#
+ dcterms: http://purl.org/dc/terms/
+ pc: http://purl.org/procurement/public-contracts#
+ cpv: http://purl.org/weso/pscs/cpv/2008/resource/
+ unspsc: http://www.ksl.stanford.edu/projects/DAML/UNSPSC.daml#
+ v: http://www.w3.org/2006/vcard/ns#
+ payment: http://reference.data.gov.uk/def/payment#
+ br: http://purl.org/business-register#
+ xsd: http://www.w3.org/2001/XMLSchema#
+ pcdt: http://purl.org/procurement/public-contracts-datatypes#">
<head>
- <title>Contract Dashboard - <?php echo $title; ?></title>
+ <title><?php echo $title; ?> - Contract Dashboard</title>
<link rel="stylesheet" type="text/css" href="bootstrap.min.css">
+ <link rel="stylesheet" type="text/css" href="bootstrap-responsive.css">
<!-- Le HTML5 shim, for IE6-8 support of HTML elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
@@ -104,14 +169,14 @@
@import "media/css/demo_table.css";
</style>
<script type="text/javascript" language="javascript" src="media/js/jquery.dataTables.js"></script>
- <script type="text/javascript" language="javascript" src="lib/bootstrap-dropdown.js"></script>
+ <script type="text/javascript" language="javascript" src="lib/bootstrap-dropdown.js"></script>
<script type="text/javascript" charset="utf-8">
jQuery.fn.dataTableExt.aTypes.unshift(
function ( sData )
{
var sValidChars = "0123456789.-,";
var Char;
-
+
/* Check the numeric part */
for ( i=1 ; i<sData.length ; i++ )
{
@@ -121,7 +186,7 @@
return null;
}
}
-
+
/* Check prefixed by currency */
if ( sData.charAt(0) == '$' || sData.charAt(0) == '£' )
{
@@ -134,11 +199,11 @@
/* Remove any commas (assumes that if present all strings will have a fixed number of d.p) */
var x = a == "-" ? 0 : a.replace( /,/g, "" );
var y = b == "-" ? 0 : b.replace( /,/g, "" );
-
+
/* Remove the currency sign */
x = x.substring( 1 );
y = y.substring( 1 );
-
+
/* Parse and return */
x = parseFloat( x );
y = parseFloat( y );
@@ -149,11 +214,11 @@
/* Remove any commas (assumes that if present all strings will have a fixed number of d.p) */
var x = a == "-" ? 0 : a.replace( /,/g, "" );
var y = b == "-" ? 0 : b.replace( /,/g, "" );
-
+
/* Remove the currency sign */
x = x.substring( 1 );
y = y.substring( 1 );
-
+
/* Parse and return */
x = parseFloat( x );
y = parseFloat( y );
@@ -166,8 +231,8 @@
<link type="text/css" rel="stylesheet" href="style.css">
</head>
<body>
- <div class="topbar">
- <div class="topbar-inner">
+ <div class="navbar">
+ <div class="navbar-inner">
<div class="container-fluid">
<a class="brand" href="#">contract dashboard</a>
<ul class="nav">
@@ -175,30 +240,28 @@
<li><a href="displaySupplier.php">suppliers</a></li>
<li><a href="displayCategory.php">categories</a></li>
<li><a href="displayCalendar.php">time periods</a></li>
- <!-- <li class="dropdown">
- <a href="#" class="dropdown-toggle">metrics</a>
- <ul class="dropdown-menu">-->
- <li><a href="displayProcurementMethod.php">tenderm</a></li>
- <li><a href="displayConfidentialities.php">confidentiality</a></li>
- <li><a href="displayConsultancies.php">consultancies</a></li>
- <li><a href="displayAmendments.php">amendments</a></li>
- <li><a href="displayMap.php">geo</a></li>
- <!-- </ul>
- </li>-->
+ <!-- <li class="dropdown">
+ <a href="#" class="dropdown-toggle">metrics</a>
+ <ul class="dropdown-menu">-->
+ <li><a href="displayProcurementMethod.php">tenderm</a></li>
+ <li><a href="displayConfidentialities.php">confidentiality</a></li>
+ <li><a href="displayConsultancies.php">consultancies</a></li>
+ <li><a href="displayAmendments.php">amendments</a></li>
+ <li><a href="displayMap.php">geo</a></li>
</ul>
-
-
- <form method="post" action="search.php" class="pull-right">
- <input type="text" id="searchKeyword" name="searchKeyword" value="" placeholder="Search" />
- <input type="hidden" id="searchID" name="searchID" value=""/>
- </form>
-
+
+
+ <form method="post" action="search.php" class="pull-right">
+ <input type="text" id="searchKeyword" name="searchKeyword" value="" placeholder="Search" />
+ <input type="hidden" id="searchID" name="searchID" value=""/>
+ </form>
+
</div>
</div><!-- /topbar-inner -->
</div><!-- /topbar -->
</div><!-- /topbar-wrapper -->
<script type="text/javascript">
-
+
var options_xml = {
script: function (input) { return "search_autosuggest.php?input="+input; },
varname:"input",
@@ -207,28 +270,47 @@
var as_xml = new bsn.AutoSuggest('searchKeyword', options_xml);
</script>
<div class="container-fluid">
- <div class="sidebar">
- <div class="well">
- Filter by:<li>
- <li>year
- <li><li>2008</li>
- </li>
- </li>
- </li> <br>
- </div> </div>
- <div class="content">
- <?php
- $start = (float) array_sum(explode(' ', microtime()));
- }
-
- function include_footer() {
- global $start;
- $end = (float) array_sum(explode(' ', microtime()));
-
- echo ' <footer>' . "Processing time: " . sprintf("%.4f", ($end - $start)) . " seconds" . ' <footer>';
- echo '</div> </div></body> </html>';
- }
-
- include ("graphs.inc.php");
- ?>
+ <div class="row-fluid">
+ <div class="span3">
+ <div class="well sidebar-nav">
+ <li class="nav-header">Filter by:</li>
+ <li>2008</li>
+ </div>
+ </div>
+ <div class="span9">
+ <?php
+ $start = (float) array_sum(explode(' ', microtime()));
+ }
+
+ function include_footer() {
+ global $start;
+ $end = (float) array_sum(explode(' ', microtime()));
+
+ echo '</div> <footer>' . "Processing time: " . sprintf("%.4f", ($end - $start)) . " seconds" . ' <footer>';
+
+ if (strpos($_SERVER['SERVER_NAME'], ".gs")) {
+ ?>
+ <script type="text/javascript">
+
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-12341040-3']);
+ _gaq.push(['_trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+
+ </script>
+ <?php
+ }
+ echo '</div> </div></body> </html>';
+ }
+
+ include ("graphs.inc.php");
+
+
+
+
--- a/lib/graphs.inc.php
+++ b/lib/graphs.inc.php
@@ -220,7 +220,7 @@
global $conn;
$agency = "AusAid";
$topX = 15;
- $query = 'SELECT SUM(value) as val, "supplierName" FROM contractnotice WHERE (extract ("YEAR" from "contractStart") >= :startYear) AND "childCN" is null AND "agencyName" = :agency
+ $query = 'SELECT SUM(value) as val, "supplierName" FROM contractnotice WHERE extract ("YEAR" from "contractStart") >= :startYear AND "childCN" is null AND "agencyName" like :agency
GROUP BY lower(supplierName) ORDER BY val DESC limit $topX';
$query = $conn->prepare($query);
$query->bindParam(":startYear", $startYear);
@@ -235,7 +235,7 @@
}
- $query = 'SELECT sum(a.val) as value, count(1) as count from (SELECT SUM(value) as val, "supplierName" FROM contractnotice WHERE (extract ("YEAR" from "contractStart") >= :startYear) AND "childCN" is null and "agencyName" = :agency
+ $query = 'SELECT sum(a.val) as value, count(1) as count from (SELECT SUM(value) as val, "supplierName" FROM contractnotice WHERE (extract ("YEAR" from "contractStart") >= :startYear) AND "childCN" is null and "agencyName" like :agency
GROUP BY lower("supplierName") ORDER BY val DESC LIMIT 18446744073709551610 OFFSET $topX) as a';
$query = $conn->prepare($query);
$query->bindParam(":startYear", $startYear);
--- /dev/null
+++ b/lib/openid-php/Auth/OpenID/Consumer.php
@@ -1,1 +1,2235 @@
-
+<?php
+
+/**
+ * This module documents the main interface with the OpenID consumer
+ * library. The only part of the library which has to be used and
+ * isn't documented in full here is the store required to create an
+ * Auth_OpenID_Consumer instance. More on the abstract store type and
+ * concrete implementations of it that are provided in the
+ * documentation for the Auth_OpenID_Consumer constructor.
+ *
+ * OVERVIEW
+ *
+ * The OpenID identity verification process most commonly uses the
+ * following steps, as visible to the user of this library:
+ *
+ * 1. The user enters their OpenID into a field on the consumer's
+ * site, and hits a login button.
+ * 2. The consumer site discovers the user's OpenID server using the
+ * YADIS protocol.
+ * 3. The consumer site sends the browser a redirect to the identity
+ * server. This is the authentication request as described in
+ * the OpenID specification.
+ * 4. The identity server's site sends the browser a redirect back
+ * to the consumer site. This redirect contains the server's
+ * response to the authentication request.
+ *
+ * The most important part of the flow to note is the consumer's site
+ * must handle two separate HTTP requests in order to perform the full
+ * identity check.
+ *
+ * LIBRARY DESIGN
+ *
+ * This consumer library is designed with that flow in mind. The goal
+ * is to make it as easy as possible to perform the above steps
+ * securely.
+ *
+ * At a high level, there are two important parts in the consumer
+ * library. The first important part is this module, which contains
+ * the interface to actually use this library. The second is the
+ * Auth_OpenID_Interface class, which describes the interface to use
+ * if you need to create a custom method for storing the state this
+ * library needs to maintain between requests.
+ *
+ * In general, the second part is less important for users of the
+ * library to know about, as several implementations are provided
+ * which cover a wide variety of situations in which consumers may use
+ * the library.
+ *
+ * This module contains a class, Auth_OpenID_Consumer, with methods
+ * corresponding to the actions necessary in each of steps 2, 3, and 4
+ * described in the overview. Use of this library should be as easy
+ * as creating an Auth_OpenID_Consumer instance and calling the
+ * methods appropriate for the action the site wants to take.
+ *
+ * STORES AND DUMB MODE
+ *
+ * OpenID is a protocol that works best when the consumer site is able
+ * to store some state. This is the normal mode of operation for the
+ * protocol, and is sometimes referred to as smart mode. There is
+ * also a fallback mode, known as dumb mode, which is available when
+ * the consumer site is not able to store state. This mode should be
+ * avoided when possible, as it leaves the implementation more
+ * vulnerable to replay attacks.
+ *
+ * The mode the library works in for normal operation is determined by
+ * the store that it is given. The store is an abstraction that
+ * handles the data that the consumer needs to manage between http
+ * requests in order to operate efficiently and securely.
+ *
+ * Several store implementation are provided, and the interface is
+ * fully documented so that custom stores can be used as well. See
+ * the documentation for the Auth_OpenID_Consumer class for more
+ * information on the interface for stores. The implementations that
+ * are provided allow the consumer site to store the necessary data in
+ * several different ways, including several SQL databases and normal
+ * files on disk.
+ *
+ * There is an additional concrete store provided that puts the system
+ * in dumb mode. This is not recommended, as it removes the library's
+ * ability to stop replay attacks reliably. It still uses time-based
+ * checking to make replay attacks only possible within a small
+ * window, but they remain possible within that window. This store
+ * should only be used if the consumer site has no way to retain data
+ * between requests at all.
+ *
+ * IMMEDIATE MODE
+ *
+ * In the flow described above, the user may need to confirm to the
+ * lidentity server that it's ok to authorize his or her identity.
+ * The server may draw pages asking for information from the user
+ * before it redirects the browser back to the consumer's site. This
+ * is generally transparent to the consumer site, so it is typically
+ * ignored as an implementation detail.
+ *
+ * There can be times, however, where the consumer site wants to get a
+ * response immediately. When this is the case, the consumer can put
+ * the library in immediate mode. In immediate mode, there is an
+ * extra response possible from the server, which is essentially the
+ * server reporting that it doesn't have enough information to answer
+ * the question yet.
+ *
+ * USING THIS LIBRARY
+ *
+ * Integrating this library into an application is usually a
+ * relatively straightforward process. The process should basically
+ * follow this plan:
+ *
+ * Add an OpenID login field somewhere on your site. When an OpenID
+ * is entered in that field and the form is submitted, it should make
+ * a request to the your site which includes that OpenID URL.
+ *
+ * First, the application should instantiate the Auth_OpenID_Consumer
+ * class using the store of choice (Auth_OpenID_FileStore or one of
+ * the SQL-based stores). If the application has a custom
+ * session-management implementation, an object implementing the
+ * {@link Auth_Yadis_PHPSession} interface should be passed as the
+ * second parameter. Otherwise, the default uses $_SESSION.
+ *
+ * Next, the application should call the Auth_OpenID_Consumer object's
+ * 'begin' method. This method takes the OpenID URL. The 'begin'
+ * method returns an Auth_OpenID_AuthRequest object.
+ *
+ * Next, the application should call the 'redirectURL' method of the
+ * Auth_OpenID_AuthRequest object. The 'return_to' URL parameter is
+ * the URL that the OpenID server will send the user back to after
+ * attempting to verify his or her identity. The 'trust_root' is the
+ * URL (or URL pattern) that identifies your web site to the user when
+ * he or she is authorizing it. Send a redirect to the resulting URL
+ * to the user's browser.
+ *
+ * That's the first half of the authentication process. The second
+ * half of the process is done after the user's ID server sends the
+ * user's browser a redirect back to your site to complete their
+ * login.
+ *
+ * When that happens, the user will contact your site at the URL given
+ * as the 'return_to' URL to the Auth_OpenID_AuthRequest::redirectURL
+ * call made above. The request will have several query parameters
+ * added to the URL by the identity server as the information
+ * necessary to finish the request.
+ *
+ * Lastly, instantiate an Auth_OpenID_Consumer instance as above and
+ * call its 'complete' method, passing in all the received query
+ * arguments.
+ *
+ * There are multiple possible return types possible from that
+ * method. These indicate the whether or not the login was successful,
+ * and include any additional information appropriate for their type.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: See the COPYING file included in this distribution.
+ *
+ * @package OpenID
+ * @author JanRain, Inc. <openid@janrain.com>
+ * @copyright 2005-2008 Janrain, Inc.
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
+ */
+
+/**
+ * Require utility classes and functions for the consumer.
+ */
+require_once "Auth/OpenID.php";
+require_once "Auth/OpenID/Message.php";
+require_once "Auth/OpenID/HMAC.php";
+require_once "Auth/OpenID/Association.php";
+require_once "Auth/OpenID/CryptUtil.php";
+require_once "Auth/OpenID/DiffieHellman.php";
+require_once "Auth/OpenID/KVForm.php";
+require_once "Auth/OpenID/Nonce.php";
+require_once "Auth/OpenID/Discover.php";
+require_once "Auth/OpenID/URINorm.php";
+require_once "Auth/Yadis/Manager.php";
+require_once "Auth/Yadis/XRI.php";
+
+/**
+ * This is the status code returned when the complete method returns
+ * successfully.
+ */
+define('Auth_OpenID_SUCCESS', 'success');
+
+/**
+ * Status to indicate cancellation of OpenID authentication.
+ */
+define('Auth_OpenID_CANCEL', 'cancel');
+
+/**
+ * This is the status code completeAuth returns when the value it
+ * received indicated an invalid login.
+ */
+define('Auth_OpenID_FAILURE', 'failure');
+
+/**
+ * This is the status code completeAuth returns when the
+ * {@link Auth_OpenID_Consumer} instance is in immediate mode, and the
+ * identity server sends back a URL to send the user to to complete his
+ * or her login.
+ */
+define('Auth_OpenID_SETUP_NEEDED', 'setup needed');
+
+/**
+ * This is the status code beginAuth returns when the page fetched
+ * from the entered OpenID URL doesn't contain the necessary link tags
+ * to function as an identity page.
+ */
+define('Auth_OpenID_PARSE_ERROR', 'parse error');
+
+/**
+ * An OpenID consumer implementation that performs discovery and does
+ * session management. See the Consumer.php file documentation for
+ * more information.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_Consumer {
+
+ /**
+ * @access private
+ */
+ var $discoverMethod = 'Auth_OpenID_discover';
+
+ /**
+ * @access private
+ */
+ var $session_key_prefix = "_openid_consumer_";
+
+ /**
+ * @access private
+ */
+ var $_token_suffix = "last_token";
+
+ /**
+ * Initialize a Consumer instance.
+ *
+ * You should create a new instance of the Consumer object with
+ * every HTTP request that handles OpenID transactions.
+ *
+ * @param Auth_OpenID_OpenIDStore $store This must be an object
+ * that implements the interface in {@link
+ * Auth_OpenID_OpenIDStore}. Several concrete implementations are
+ * provided, to cover most common use cases. For stores backed by
+ * MySQL, PostgreSQL, or SQLite, see the {@link
+ * Auth_OpenID_SQLStore} class and its sublcasses. For a
+ * filesystem-backed store, see the {@link Auth_OpenID_FileStore}
+ * module. As a last resort, if it isn't possible for the server
+ * to store state at all, an instance of {@link
+ * Auth_OpenID_DumbStore} can be used.
+ *
+ * @param mixed $session An object which implements the interface
+ * of the {@link Auth_Yadis_PHPSession} class. Particularly, this
+ * object is expected to have these methods: get($key), set($key),
+ * $value), and del($key). This defaults to a session object
+ * which wraps PHP's native session machinery. You should only
+ * need to pass something here if you have your own sessioning
+ * implementation.
+ *
+ * @param str $consumer_cls The name of the class to instantiate
+ * when creating the internal consumer object. This is used for
+ * testing.
+ */
+ function Auth_OpenID_Consumer($store, $session = null,
+ $consumer_cls = null)
+ {
+ if ($session === null) {
+ $session = new Auth_Yadis_PHPSession();
+ }
+
+ $this->session = $session;
+
+ if ($consumer_cls !== null) {
+ $this->consumer = new $consumer_cls($store);
+ } else {
+ $this->consumer = new Auth_OpenID_GenericConsumer($store);
+ }
+
+ $this->_token_key = $this->session_key_prefix . $this->_token_suffix;
+ }
+
+ /**
+ * Used in testing to define the discovery mechanism.
+ *
+ * @access private
+ */
+ function getDiscoveryObject($session, $openid_url,
+ $session_key_prefix)
+ {
+ return new Auth_Yadis_Discovery($session, $openid_url,
+ $session_key_prefix);
+ }
+
+ /**
+ * Start the OpenID authentication process. See steps 1-2 in the
+ * overview at the top of this file.
+ *
+ * @param string $user_url Identity URL given by the user. This
+ * method performs a textual transformation of the URL to try and
+ * make sure it is normalized. For example, a user_url of
+ * example.com will be normalized to http://example.com/
+ * normalizing and resolving any redirects the server might issue.
+ *
+ * @param bool $anonymous True if the OpenID request is to be sent
+ * to the server without any identifier information. Use this
+ * when you want to transport data but don't want to do OpenID
+ * authentication with identifiers.
+ *
+ * @return Auth_OpenID_AuthRequest $auth_request An object
+ * containing the discovered information will be returned, with a
+ * method for building a redirect URL to the server, as described
+ * in step 3 of the overview. This object may also be used to add
+ * extension arguments to the request, using its 'addExtensionArg'
+ * method.
+ */
+ function begin($user_url, $anonymous=false)
+ {
+ $openid_url = $user_url;
+
+ $disco = $this->getDiscoveryObject($this->session,
+ $openid_url,
+ $this->session_key_prefix);
+
+ // Set the 'stale' attribute of the manager. If discovery
+ // fails in a fatal way, the stale flag will cause the manager
+ // to be cleaned up next time discovery is attempted.
+
+ $m = $disco->getManager();
+ $loader = new Auth_Yadis_ManagerLoader();
+
+ if ($m) {
+ if ($m->stale) {
+ $disco->destroyManager();
+ } else {
+ $m->stale = true;
+ $disco->session->set($disco->session_key,
+ serialize($loader->toSession($m)));
+ }
+ }
+
+ $endpoint = $disco->getNextService($this->discoverMethod,
+ $this->consumer->fetcher);
+
+ // Reset the 'stale' attribute of the manager.
+ $m = $disco->getManager();
+ if ($m) {
+ $m->stale = false;
+ $disco->session->set($disco->session_key,
+ serialize($loader->toSession($m)));
+ }
+
+ if ($endpoint === null) {
+ return null;
+ } else {
+ return $this->beginWithoutDiscovery($endpoint,
+ $anonymous);
+ }
+ }
+
+ /**
+ * Start OpenID verification without doing OpenID server
+ * discovery. This method is used internally by Consumer.begin
+ * after discovery is performed, and exists to provide an
+ * interface for library users needing to perform their own
+ * discovery.
+ *
+ * @param Auth_OpenID_ServiceEndpoint $endpoint an OpenID service
+ * endpoint descriptor.
+ *
+ * @param bool anonymous Set to true if you want to perform OpenID
+ * without identifiers.
+ *
+ * @return Auth_OpenID_AuthRequest $auth_request An OpenID
+ * authentication request object.
+ */
+ function beginWithoutDiscovery($endpoint, $anonymous=false)
+ {
+ $loader = new Auth_OpenID_ServiceEndpointLoader();
+ $auth_req = $this->consumer->begin($endpoint);
+ $this->session->set($this->_token_key,
+ $loader->toSession($auth_req->endpoint));
+ if (!$auth_req->setAnonymous($anonymous)) {
+ return new Auth_OpenID_FailureResponse(null,
+ "OpenID 1 requests MUST include the identifier " .
+ "in the request.");
+ }
+ return $auth_req;
+ }
+
+ /**
+ * Called to interpret the server's response to an OpenID
+ * request. It is called in step 4 of the flow described in the
+ * consumer overview.
+ *
+ * @param string $current_url The URL used to invoke the application.
+ * Extract the URL from your application's web
+ * request framework and specify it here to have it checked
+ * against the openid.current_url value in the response. If
+ * the current_url URL check fails, the status of the
+ * completion will be FAILURE.
+ *
+ * @param array $query An array of the query parameters (key =>
+ * value pairs) for this HTTP request. Defaults to null. If
+ * null, the GET or POST data are automatically gotten from the
+ * PHP environment. It is only useful to override $query for
+ * testing.
+ *
+ * @return Auth_OpenID_ConsumerResponse $response A instance of an
+ * Auth_OpenID_ConsumerResponse subclass. The type of response is
+ * indicated by the status attribute, which will be one of
+ * SUCCESS, CANCEL, FAILURE, or SETUP_NEEDED.
+ */
+ function complete($current_url, $query=null)
+ {
+ if ($current_url && !is_string($current_url)) {
+ // This is ugly, but we need to complain loudly when
+ // someone uses the API incorrectly.
+ trigger_error("current_url must be a string; see NEWS file " .
+ "for upgrading notes.",
+ E_USER_ERROR);
+ }
+
+ if ($query === null) {
+ $query = Auth_OpenID::getQuery();
+ }
+
+ $loader = new Auth_OpenID_ServiceEndpointLoader();
+ $endpoint_data = $this->session->get($this->_token_key);
+ $endpoint =
+ $loader->fromSession($endpoint_data);
+
+ $message = Auth_OpenID_Message::fromPostArgs($query);
+ $response = $this->consumer->complete($message, $endpoint,
+ $current_url);
+ $this->session->del($this->_token_key);
+
+ if (in_array($response->status, array(Auth_OpenID_SUCCESS,
+ Auth_OpenID_CANCEL))) {
+ if ($response->identity_url !== null) {
+ $disco = $this->getDiscoveryObject($this->session,
+ $response->identity_url,
+ $this->session_key_prefix);
+ $disco->cleanup(true);
+ }
+ }
+
+ return $response;
+ }
+}
+
+/**
+ * A class implementing HMAC/DH-SHA1 consumer sessions.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_DiffieHellmanSHA1ConsumerSession {
+ var $session_type = 'DH-SHA1';
+ var $hash_func = 'Auth_OpenID_SHA1';
+ var $secret_size = 20;
+ var $allowed_assoc_types = array('HMAC-SHA1');
+
+ function Auth_OpenID_DiffieHellmanSHA1ConsumerSession($dh = null)
+ {
+ if ($dh === null) {
+ $dh = new Auth_OpenID_DiffieHellman();
+ }
+
+ $this->dh = $dh;
+ }
+
+ function getRequest()
+ {
+ $math = Auth_OpenID_getMathLib();
+
+ $cpub = $math->longToBase64($this->dh->public);
+
+ $args = array('dh_consumer_public' => $cpub);
+
+ if (!$this->dh->usingDefaultValues()) {
+ $args = array_merge($args, array(
+ 'dh_modulus' =>
+ $math->longToBase64($this->dh->mod),
+ 'dh_gen' =>
+ $math->longToBase64($this->dh->gen)));
+ }
+
+ return $args;
+ }
+
+ function extractSecret($response)
+ {
+ if (!$response->hasKey(Auth_OpenID_OPENID_NS,
+ 'dh_server_public')) {
+ return null;
+ }
+
+ if (!$response->hasKey(Auth_OpenID_OPENID_NS,
+ 'enc_mac_key')) {
+ return null;
+ }
+
+ $math = Auth_OpenID_getMathLib();
+
+ $spub = $math->base64ToLong($response->getArg(Auth_OpenID_OPENID_NS,
+ 'dh_server_public'));
+ $enc_mac_key = base64_decode($response->getArg(Auth_OpenID_OPENID_NS,
+ 'enc_mac_key'));
+
+ return $this->dh->xorSecret($spub, $enc_mac_key, $this->hash_func);
+ }
+}
+
+/**
+ * A class implementing HMAC/DH-SHA256 consumer sessions.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_DiffieHellmanSHA256ConsumerSession extends
+ Auth_OpenID_DiffieHellmanSHA1ConsumerSession {
+ var $session_type = 'DH-SHA256';
+ var $hash_func = 'Auth_OpenID_SHA256';
+ var $secret_size = 32;
+ var $allowed_assoc_types = array('HMAC-SHA256');
+}
+
+/**
+ * A class implementing plaintext consumer sessions.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_PlainTextConsumerSession {
+ var $session_type = 'no-encryption';
+ var $allowed_assoc_types = array('HMAC-SHA1', 'HMAC-SHA256');
+
+ function getRequest()
+ {
+ return array();
+ }
+
+ function extractSecret($response)
+ {
+ if (!$response->hasKey(Auth_OpenID_OPENID_NS, 'mac_key')) {
+ return null;
+ }
+
+ return base64_decode($response->getArg(Auth_OpenID_OPENID_NS,
+ 'mac_key'));
+ }
+}
+
+/**
+ * Returns available session types.
+ */
+function Auth_OpenID_getAvailableSessionTypes()
+{
+ $types = array(
+ 'no-encryption' => 'Auth_OpenID_PlainTextConsumerSession',
+ 'DH-SHA1' => 'Auth_OpenID_DiffieHellmanSHA1ConsumerSession',
+ 'DH-SHA256' => 'Auth_OpenID_DiffieHellmanSHA256ConsumerSession');
+
+ return $types;
+}
+
+/**
+ * This class is the interface to the OpenID consumer logic.
+ * Instances of it maintain no per-request state, so they can be
+ * reused (or even used by multiple threads concurrently) as needed.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_GenericConsumer {
+ /**
+ * @access private
+ */
+ var $discoverMethod = 'Auth_OpenID_discover';
+
+ /**
+ * This consumer's store object.
+ */
+ var $store;
+
+ /**
+ * @access private
+ */
+ var $_use_assocs;
+
+ /**
+ * @access private
+ */
+ var $openid1_nonce_query_arg_name = 'janrain_nonce';
+
+ /**
+ * Another query parameter that gets added to the return_to for
+ * OpenID 1; if the user's session state is lost, use this claimed
+ * identifier to do discovery when verifying the response.
+ */
+ var $openid1_return_to_identifier_name = 'openid1_claimed_id';
+
+ /**
+ * This method initializes a new {@link Auth_OpenID_Consumer}
+ * instance to access the library.
+ *
+ * @param Auth_OpenID_OpenIDStore $store This must be an object
+ * that implements the interface in {@link Auth_OpenID_OpenIDStore}.
+ * Several concrete implementations are provided, to cover most common use
+ * cases. For stores backed by MySQL, PostgreSQL, or SQLite, see
+ * the {@link Auth_OpenID_SQLStore} class and its sublcasses. For a
+ * filesystem-backed store, see the {@link Auth_OpenID_FileStore} module.
+ * As a last resort, if it isn't possible for the server to store
+ * state at all, an instance of {@link Auth_OpenID_DumbStore} can be used.
+ *
+ * @param bool $immediate This is an optional boolean value. It
+ * controls whether the library uses immediate mode, as explained
+ * in the module description. The default value is False, which
+ * disables immediate mode.
+ */
+ function Auth_OpenID_GenericConsumer($store)
+ {
+ $this->store = $store;
+ $this->negotiator = Auth_OpenID_getDefaultNegotiator();
+ $this->_use_assocs = (is_null($this->store) ? false : true);
+
+ $this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
+
+ $this->session_types = Auth_OpenID_getAvailableSessionTypes();
+ }
+
+ /**
+ * Called to begin OpenID authentication using the specified
+ * {@link Auth_OpenID_ServiceEndpoint}.
+ *
+ * @access private
+ */
+ function begin($service_endpoint)
+ {
+ $assoc = $this->_getAssociation($service_endpoint);
+ $r = new Auth_OpenID_AuthRequest($service_endpoint, $assoc);
+ $r->return_to_args[$this->openid1_nonce_query_arg_name] =
+ Auth_OpenID_mkNonce();
+
+ if ($r->message->isOpenID1()) {
+ $r->return_to_args[$this->openid1_return_to_identifier_name] =
+ $r->endpoint->claimed_id;
+ }
+
+ return $r;
+ }
+
+ /**
+ * Given an {@link Auth_OpenID_Message}, {@link
+ * Auth_OpenID_ServiceEndpoint} and optional return_to URL,
+ * complete OpenID authentication.
+ *
+ * @access private
+ */
+ function complete($message, $endpoint, $return_to)
+ {
+ $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode',
+ '<no mode set>');
+
+ $mode_methods = array(
+ 'cancel' => '_complete_cancel',
+ 'error' => '_complete_error',
+ 'setup_needed' => '_complete_setup_needed',
+ 'id_res' => '_complete_id_res',
+ );
+
+ $method = Auth_OpenID::arrayGet($mode_methods, $mode,
+ '_completeInvalid');
+
+ return call_user_func_array(array($this, $method),
+ array($message, &$endpoint, $return_to));
+ }
+
+ /**
+ * @access private
+ */
+ function _completeInvalid($message, $endpoint, $unused)
+ {
+ $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode',
+ '<No mode set>');
+
+ return new Auth_OpenID_FailureResponse($endpoint,
+ sprintf("Invalid openid.mode '%s'", $mode));
+ }
+
+ /**
+ * @access private
+ */
+ function _complete_cancel($message, $endpoint, $unused)
+ {
+ return new Auth_OpenID_CancelResponse($endpoint);
+ }
+
+ /**
+ * @access private
+ */
+ function _complete_error($message, $endpoint, $unused)
+ {
+ $error = $message->getArg(Auth_OpenID_OPENID_NS, 'error');
+ $contact = $message->getArg(Auth_OpenID_OPENID_NS, 'contact');
+ $reference = $message->getArg(Auth_OpenID_OPENID_NS, 'reference');
+
+ return new Auth_OpenID_FailureResponse($endpoint, $error,
+ $contact, $reference);
+ }
+
+ /**
+ * @access private
+ */
+ function _complete_setup_needed($message, $endpoint, $unused)
+ {
+ if (!$message->isOpenID2()) {
+ return $this->_completeInvalid($message, $endpoint);
+ }
+
+ $user_setup_url = $message->getArg(Auth_OpenID_OPENID2_NS,
+ 'user_setup_url');
+ return new Auth_OpenID_SetupNeededResponse($endpoint, $user_setup_url);
+ }
+
+ /**
+ * @access private
+ */
+ function _complete_id_res($message, $endpoint, $return_to)
+ {
+ $user_setup_url = $message->getArg(Auth_OpenID_OPENID1_NS,
+ 'user_setup_url');
+
+ if ($this->_checkSetupNeeded($message)) {
+ return new Auth_OpenID_SetupNeededResponse(
+ $endpoint, $user_setup_url);
+ } else {
+ return $this->_doIdRes($message, $endpoint, $return_to);
+ }
+ }
+
+ /**
+ * @access private
+ */
+ function _checkSetupNeeded($message)
+ {
+ // In OpenID 1, we check to see if this is a cancel from
+ // immediate mode by the presence of the user_setup_url
+ // parameter.
+ if ($message->isOpenID1()) {
+ $user_setup_url = $message->getArg(Auth_OpenID_OPENID1_NS,
+ 'user_setup_url');
+ if ($user_setup_url !== null) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @access private
+ */
+ function _doIdRes($message, $endpoint, $return_to)
+ {
+ // Checks for presence of appropriate fields (and checks
+ // signed list fields)
+ $result = $this->_idResCheckForFields($message);
+
+ if (Auth_OpenID::isFailure($result)) {
+ return $result;
+ }
+
+ if (!$this->_checkReturnTo($message, $return_to)) {
+ return new Auth_OpenID_FailureResponse(null,
+ sprintf("return_to does not match return URL. Expected %s, got %s",
+ $return_to,
+ $message->getArg(Auth_OpenID_OPENID_NS, 'return_to')));
+ }
+
+ // Verify discovery information:
+ $result = $this->_verifyDiscoveryResults($message, $endpoint);
+
+ if (Auth_OpenID::isFailure($result)) {
+ return $result;
+ }
+
+ $endpoint = $result;
+
+ $result = $this->_idResCheckSignature($message,
+ $endpoint->server_url);
+
+ if (Auth_OpenID::isFailure($result)) {
+ return $result;
+ }
+
+ $result = $this->_idResCheckNonce($message, $endpoint);
+
+ if (Auth_OpenID::isFailure($result)) {
+ return $result;
+ }
+
+ $signed_list_str = $message->getArg(Auth_OpenID_OPENID_NS, 'signed',
+ Auth_OpenID_NO_DEFAULT);
+ if (Auth_OpenID::isFailure($signed_list_str)) {
+ return $signed_list_str;
+ }
+ $signed_list = explode(',', $signed_list_str);
+
+ $signed_fields = Auth_OpenID::addPrefix($signed_list, "openid.");
+
+ return new Auth_OpenID_SuccessResponse($endpoint, $message,
+ $signed_fields);
+
+ }
+
+ /**
+ * @access private
+ */
+ function _checkReturnTo($message, $return_to)
+ {
+ // Check an OpenID message and its openid.return_to value
+ // against a return_to URL from an application. Return True
+ // on success, False on failure.
+
+ // Check the openid.return_to args against args in the
+ // original message.
+ $result = Auth_OpenID_GenericConsumer::_verifyReturnToArgs(
+ $message->toPostArgs());
+ if (Auth_OpenID::isFailure($result)) {
+ return false;
+ }
+
+ // Check the return_to base URL against the one in the
+ // message.
+ $msg_return_to = $message->getArg(Auth_OpenID_OPENID_NS,
+ 'return_to');
+ if (Auth_OpenID::isFailure($return_to)) {
+ // XXX log me
+ return false;
+ }
+
+ $return_to_parts = parse_url(Auth_OpenID_urinorm($return_to));
+ $msg_return_to_parts = parse_url(Auth_OpenID_urinorm($msg_return_to));
+
+ // If port is absent from both, add it so it's equal in the
+ // check below.
+ if ((!array_key_exists('port', $return_to_parts)) &&
+ (!array_key_exists('port', $msg_return_to_parts))) {
+ $return_to_parts['port'] = null;
+ $msg_return_to_parts['port'] = null;
+ }
+
+ // If path is absent from both, add it so it's equal in the
+ // check below.
+ if ((!array_key_exists('path', $return_to_parts)) &&
+ (!array_key_exists('path', $msg_return_to_parts))) {
+ $return_to_parts['path'] = null;
+ $msg_return_to_parts['path'] = null;
+ }
+
+ // The URL scheme, authority, and path MUST be the same
+ // between the two URLs.
+ foreach (array('scheme', 'host', 'port', 'path') as $component) {
+ // If the url component is absent in either URL, fail.
+ // There should always be a scheme, host, port, and path.
+ if (!array_key_exists($component, $return_to_parts)) {
+ return false;
+ }
+
+ if (!array_key_exists($component, $msg_return_to_parts)) {
+ return false;
+ }
+
+ if (Auth_OpenID::arrayGet($return_to_parts, $component) !==
+ Auth_OpenID::arrayGet($msg_return_to_parts, $component)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @access private
+ */
+ function _verifyReturnToArgs($query)
+ {
+ // Verify that the arguments in the return_to URL are present in this
+ // response.
+
+ $message = Auth_OpenID_Message::fromPostArgs($query);
+ $return_to = $message->getArg(Auth_OpenID_OPENID_NS, 'return_to');
+
+ if (Auth_OpenID::isFailure($return_to)) {
+ return $return_to;
+ }
+ // XXX: this should be checked by _idResCheckForFields
+ if (!$return_to) {
+ return new Auth_OpenID_FailureResponse(null,
+ "Response has no return_to");
+ }
+
+ $parsed_url = parse_url($return_to);
+
+ $q = array();
+ if (array_key_exists('query', $parsed_url)) {
+ $rt_query = $parsed_url['query'];
+ $q = Auth_OpenID::parse_str($rt_query);
+ }
+
+ foreach ($q as $rt_key => $rt_value) {
+ if (!array_key_exists($rt_key, $query)) {
+ return new Auth_OpenID_FailureResponse(null,
+ sprintf("return_to parameter %s absent from query", $rt_key));
+ } else {
+ $value = $query[$rt_key];
+ if ($rt_value != $value) {
+ return new Auth_OpenID_FailureResponse(null,
+ sprintf("parameter %s value %s does not match " .
+ "return_to value %s", $rt_key,
+ $value, $rt_value));
+ }
+ }
+ }
+
+ // Make sure all non-OpenID arguments in the response are also
+ // in the signed return_to.
+ $bare_args = $message->getArgs(Auth_OpenID_BARE_NS);
+ foreach ($bare_args as $key => $value) {
+ if (Auth_OpenID::arrayGet($q, $key) != $value) {
+ return new Auth_OpenID_FailureResponse(null,
+ sprintf("Parameter %s = %s not in return_to URL",
+ $key, $value));
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @access private
+ */
+ function _idResCheckSignature($message, $server_url)
+ {
+ $assoc_handle = $message->getArg(Auth_OpenID_OPENID_NS,
+ 'assoc_handle');
+ if (Auth_OpenID::isFailure($assoc_handle)) {
+ return $assoc_handle;
+ }
+
+ $assoc = $this->store->getAssociation($server_url, $assoc_handle);
+
+ if ($assoc) {
+ if ($assoc->getExpiresIn() <= 0) {
+ // XXX: It might be a good idea sometimes to re-start
+ // the authentication with a new association. Doing it
+ // automatically opens the possibility for
+ // denial-of-service by a server that just returns
+ // expired associations (or really short-lived
+ // associations)
+ return new Auth_OpenID_FailureResponse(null,
+ 'Association with ' . $server_url . ' expired');
+ }
+
+ if (!$assoc->checkMessageSignature($message)) {
+ // If we get a "bad signature" here, it means that the association
+ // is unrecoverabley corrupted in some way. Any futher attempts
+ // to login with this association is likely to fail. Drop it.
+ $this->store->removeAssociation($server_url, $assoc_handle);
+ return new Auth_OpenID_FailureResponse(null,
+ "Bad signature");
+ }
+ } else {
+ // It's not an association we know about. Stateless mode
+ // is our only possible path for recovery. XXX - async
+ // framework will not want to block on this call to
+ // _checkAuth.
+ if (!$this->_checkAuth($message, $server_url)) {
+ return new Auth_OpenID_FailureResponse(null,
+ "Server denied check_authentication");
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @access private
+ */
+ function _verifyDiscoveryResults($message, $endpoint=null)
+ {
+ if ($message->getOpenIDNamespace() == Auth_OpenID_OPENID2_NS) {
+ return $this->_verifyDiscoveryResultsOpenID2($message,
+ $endpoint);
+ } else {
+ return $this->_verifyDiscoveryResultsOpenID1($message,
+ $endpoint);
+ }
+ }
+
+ /**
+ * @access private
+ */
+ function _verifyDiscoveryResultsOpenID1($message, $endpoint)
+ {
+ $claimed_id = $message->getArg(Auth_OpenID_BARE_NS,
+ $this->openid1_return_to_identifier_name);
+
+ if (($endpoint === null) && ($claimed_id === null)) {
+ return new Auth_OpenID_FailureResponse($endpoint,
+ 'When using OpenID 1, the claimed ID must be supplied, ' .
+ 'either by passing it through as a return_to parameter ' .
+ 'or by using a session, and supplied to the GenericConsumer ' .
+ 'as the argument to complete()');
+ } else if (($endpoint !== null) && ($claimed_id === null)) {
+ $claimed_id = $endpoint->claimed_id;
+ }
+
+ $to_match = new Auth_OpenID_ServiceEndpoint();
+ $to_match->type_uris = array(Auth_OpenID_TYPE_1_1);
+ $to_match->local_id = $message->getArg(Auth_OpenID_OPENID1_NS,
+ 'identity');
+
+ // Restore delegate information from the initiation phase
+ $to_match->claimed_id = $claimed_id;
+
+ if ($to_match->local_id === null) {
+ return new Auth_OpenID_FailureResponse($endpoint,
+ "Missing required field openid.identity");
+ }
+
+ $to_match_1_0 = $to_match->copy();
+ $to_match_1_0->type_uris = array(Auth_OpenID_TYPE_1_0);
+
+ if ($endpoint !== null) {
+ $result = $this->_verifyDiscoverySingle($endpoint, $to_match);
+
+ if (is_a($result, 'Auth_OpenID_TypeURIMismatch')) {
+ $result = $this->_verifyDiscoverySingle($endpoint,
+ $to_match_1_0);
+ }
+
+ if (Auth_OpenID::isFailure($result)) {
+ // oidutil.log("Error attempting to use stored
+ // discovery information: " + str(e))
+ // oidutil.log("Attempting discovery to
+ // verify endpoint")
+ } else {
+ return $endpoint;
+ }
+ }
+
+ // Endpoint is either bad (failed verification) or None
+ return $this->_discoverAndVerify($to_match->claimed_id,
+ array($to_match, $to_match_1_0));
+ }
+
+ /**
+ * @access private
+ */
+ function _verifyDiscoverySingle($endpoint, $to_match)
+ {
+ // Every type URI that's in the to_match endpoint has to be
+ // present in the discovered endpoint.
+ foreach ($to_match->type_uris as $type_uri) {
+ if (!$endpoint->usesExtension($type_uri)) {
+ return new Auth_OpenID_TypeURIMismatch($endpoint,
+ "Required type ".$type_uri." not present");
+ }
+ }
+
+ // Fragments do not influence discovery, so we can't compare a
+ // claimed identifier with a fragment to discovered
+ // information.
+ list($defragged_claimed_id, $_) =
+ Auth_OpenID::urldefrag($to_match->claimed_id);
+
+ if ($defragged_claimed_id != $endpoint->claimed_id) {
+ return new Auth_OpenID_FailureResponse($endpoint,
+ sprintf('Claimed ID does not match (different subjects!), ' .
+ 'Expected %s, got %s', $defragged_claimed_id,
+ $endpoint->claimed_id));
+ }
+
+ if ($to_match->getLocalID() != $endpoint->getLocalID()) {
+ return new Auth_OpenID_FailureResponse($endpoint,
+ sprintf('local_id mismatch. Expected %s, got %s',
+ $to_match->getLocalID(), $endpoint->getLocalID()));
+ }
+
+ // If the server URL is None, this must be an OpenID 1
+ // response, because op_endpoint is a required parameter in
+ // OpenID 2. In that case, we don't actually care what the
+ // discovered server_url is, because signature checking or
+ // check_auth should take care of that check for us.
+ if ($to_match->server_url === null) {
+ if ($to_match->preferredNamespace() != Auth_OpenID_OPENID1_NS) {
+ return new Auth_OpenID_FailureResponse($endpoint,
+ "Preferred namespace mismatch (bug)");
+ }
+ } else if ($to_match->server_url != $endpoint->server_url) {
+ return new Auth_OpenID_FailureResponse($endpoint,
+ sprintf('OP Endpoint mismatch. Expected %s, got %s',
+ $to_match->server_url, $endpoint->server_url));
+ }
+
+ return null;
+ }
+
+ /**
+ * @access private
+ */
+ function _verifyDiscoveryResultsOpenID2($message, $endpoint)
+ {
+ $to_match = new Auth_OpenID_ServiceEndpoint();
+ $to_match->type_uris = array(Auth_OpenID_TYPE_2_0);
+ $to_match->claimed_id = $message->getArg(Auth_OpenID_OPENID2_NS,
+ 'claimed_id');
+
+ $to_match->local_id = $message->getArg(Auth_OpenID_OPENID2_NS,
+ 'identity');
+
+ $to_match->server_url = $message->getArg(Auth_OpenID_OPENID2_NS,
+ 'op_endpoint');
+
+ if ($to_match->server_url === null) {
+ return new Auth_OpenID_FailureResponse($endpoint,
+ "OP Endpoint URL missing");
+ }
+
+ // claimed_id and identifier must both be present or both be
+ // absent
+ if (($to_match->claimed_id === null) &&
+ ($to_match->local_id !== null)) {
+ return new Auth_OpenID_FailureResponse($endpoint,
+ 'openid.identity is present without openid.claimed_id');
+ }
+
+ if (($to_match->claimed_id !== null) &&
+ ($to_match->local_id === null)) {
+ return new Auth_OpenID_FailureResponse($endpoint,
+ 'openid.claimed_id is present without openid.identity');
+ }
+
+ if ($to_match->claimed_id === null) {
+ // This is a response without identifiers, so there's
+ // really no checking that we can do, so return an
+ // endpoint that's for the specified `openid.op_endpoint'
+ return Auth_OpenID_ServiceEndpoint::fromOPEndpointURL(
+ $to_match->server_url);
+ }
+
+ if (!$endpoint) {
+ // The claimed ID doesn't match, so we have to do
+ // discovery again. This covers not using sessions, OP
+ // identifier endpoints and responses that didn't match
+ // the original request.
+ // oidutil.log('No pre-discovered information supplied.')
+ return $this->_discoverAndVerify($to_match->claimed_id,
+ array($to_match));
+ } else {
+
+ // The claimed ID matches, so we use the endpoint that we
+ // discovered in initiation. This should be the most
+ // common case.
+ $result = $this->_verifyDiscoverySingle($endpoint, $to_match);
+
+ if (Auth_OpenID::isFailure($result)) {
+ $endpoint = $this->_discoverAndVerify($to_match->claimed_id,
+ array($to_match));
+ if (Auth_OpenID::isFailure($endpoint)) {
+ return $endpoint;
+ }
+ }
+ }
+
+ // The endpoint we return should have the claimed ID from the
+ // message we just verified, fragment and all.
+ if ($endpoint->claimed_id != $to_match->claimed_id) {
+ $endpoint->claimed_id = $to_match->claimed_id;
+ }
+
+ return $endpoint;
+ }
+
+ /**
+ * @access private
+ */
+ function _discoverAndVerify($claimed_id, $to_match_endpoints)
+ {
+ // oidutil.log('Performing discovery on %s' % (claimed_id,))
+ list($unused, $services) = call_user_func($this->discoverMethod,
+ $claimed_id,
+ $this->fetcher);
+
+ if (!$services) {
+ return new Auth_OpenID_FailureResponse(null,
+ sprintf("No OpenID information found at %s",
+ $claimed_id));
+ }
+
+ return $this->_verifyDiscoveryServices($claimed_id, $services,
+ $to_match_endpoints);
+ }
+
+ /**
+ * @access private
+ */
+ function _verifyDiscoveryServices($claimed_id,
+ $services, $to_match_endpoints)
+ {
+ // Search the services resulting from discovery to find one
+ // that matches the information from the assertion
+
+ foreach ($services as $endpoint) {
+ foreach ($to_match_endpoints as $to_match_endpoint) {
+ $result = $this->_verifyDiscoverySingle($endpoint,
+ $to_match_endpoint);
+
+ if (!Auth_OpenID::isFailure($result)) {
+ // It matches, so discover verification has
+ // succeeded. Return this endpoint.
+ return $endpoint;
+ }
+ }
+ }
+
+ return new Auth_OpenID_FailureResponse(null,
+ sprintf('No matching endpoint found after discovering %s: %s',
+ $claimed_id, $result->message));
+ }
+
+ /**
+ * Extract the nonce from an OpenID 1 response. Return the nonce
+ * from the BARE_NS since we independently check the return_to
+ * arguments are the same as those in the response message.
+ *
+ * See the openid1_nonce_query_arg_name class variable
+ *
+ * @returns $nonce The nonce as a string or null
+ *
+ * @access private
+ */
+ function _idResGetNonceOpenID1($message, $endpoint)
+ {
+ return $message->getArg(Auth_OpenID_BARE_NS,
+ $this->openid1_nonce_query_arg_name);
+ }
+
+ /**
+ * @access private
+ */
+ function _idResCheckNonce($message, $endpoint)
+ {
+ if ($message->isOpenID1()) {
+ // This indicates that the nonce was generated by the consumer
+ $nonce = $this->_idResGetNonceOpenID1($message, $endpoint);
+ $server_url = '';
+ } else {
+ $nonce = $message->getArg(Auth_OpenID_OPENID2_NS,
+ 'response_nonce');
+
+ $server_url = $endpoint->server_url;
+ }
+
+ if ($nonce === null) {
+ return new Auth_OpenID_FailureResponse($endpoint,
+ "Nonce missing from response");
+ }
+
+ $parts = Auth_OpenID_splitNonce($nonce);
+
+ if ($parts === null) {
+ return new Auth_OpenID_FailureResponse($endpoint,
+ "Malformed nonce in response");
+ }
+
+ list($timestamp, $salt) = $parts;
+
+ if (!$this->store->useNonce($server_url, $timestamp, $salt)) {
+ return new Auth_OpenID_FailureResponse($endpoint,
+ "Nonce already used or out of range");
+ }
+
+ return null;
+ }
+
+ /**
+ * @access private
+ */
+ function _idResCheckForFields($message)
+ {
+ $basic_fields = array('return_to', 'assoc_handle', 'sig', 'signed');
+ $basic_sig_fields = array('return_to', 'identity');
+
+ $require_fields = array(
+ Auth_OpenID_OPENID2_NS => array_merge($basic_fields,
+ array('op_endpoint')),
+
+ Auth_OpenID_OPENID1_NS => array_merge($basic_fields,
+ array('identity'))
+ );
+
+ $require_sigs = array(
+ Auth_OpenID_OPENID2_NS => array_merge($basic_sig_fields,
+ array('response_nonce',
+ 'claimed_id',
+ 'assoc_handle',
+ 'op_endpoint')),
+ Auth_OpenID_OPENID1_NS => array_merge($basic_sig_fields,
+ array('nonce'))
+ );
+
+ foreach ($require_fields[$message->getOpenIDNamespace()] as $field) {
+ if (!$message->hasKey(Auth_OpenID_OPENID_NS, $field)) {
+ return new Auth_OpenID_FailureResponse(null,
+ "Missing required field '".$field."'");
+ }
+ }
+
+ $signed_list_str = $message->getArg(Auth_OpenID_OPENID_NS,
+ 'signed',
+ Auth_OpenID_NO_DEFAULT);
+ if (Auth_OpenID::isFailure($signed_list_str)) {
+ return $signed_list_str;
+ }
+ $signed_list = explode(',', $signed_list_str);
+
+ foreach ($require_sigs[$message->getOpenIDNamespace()] as $field) {
+ // Field is present and not in signed list
+ if ($message->hasKey(Auth_OpenID_OPENID_NS, $field) &&
+ (!in_array($field, $signed_list))) {
+ return new Auth_OpenID_FailureResponse(null,
+ "'".$field."' not signed");
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @access private
+ */
+ function _checkAuth($message, $server_url)
+ {
+ $request = $this->_createCheckAuthRequest($message);
+ if ($request === null) {
+ return false;
+ }
+
+ $resp_message = $this->_makeKVPost($request, $server_url);
+ if (($resp_message === null) ||
+ (is_a($resp_message, 'Auth_OpenID_ServerErrorContainer'))) {
+ return false;
+ }
+
+ return $this->_processCheckAuthResponse($resp_message, $server_url);
+ }
+
+ /**
+ * @access private
+ */
+ function _createCheckAuthRequest($message)
+ {
+ $signed = $message->getArg(Auth_OpenID_OPENID_NS, 'signed');
+ if ($signed) {
+ foreach (explode(',', $signed) as $k) {
+ $value = $message->getAliasedArg($k);
+ if ($value === null) {
+ return null;
+ }
+ }
+ }
+ $ca_message = $message->copy();
+ $ca_message->setArg(Auth_OpenID_OPENID_NS, 'mode',
+ 'check_authentication');
+ return $ca_message;
+ }
+
+ /**
+ * @access private
+ */
+ function _processCheckAuthResponse($response, $server_url)
+ {
+ $is_valid = $response->getArg(Auth_OpenID_OPENID_NS, 'is_valid',
+ 'false');
+
+ $invalidate_handle = $response->getArg(Auth_OpenID_OPENID_NS,
+ 'invalidate_handle');
+
+ if ($invalidate_handle !== null) {
+ $this->store->removeAssociation($server_url,
+ $invalidate_handle);
+ }
+
+ if ($is_valid == 'true') {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Adapt a POST response to a Message.
+ *
+ * @param $response Result of a POST to an OpenID endpoint.
+ *
+ * @access private
+ */
+ static function _httpResponseToMessage($response, $server_url)
+ {
+ // Should this function be named Message.fromHTTPResponse instead?
+ $response_message = Auth_OpenID_Message::fromKVForm($response->body);
+
+ if ($response->status == 400) {
+ return Auth_OpenID_ServerErrorContainer::fromMessage(
+ $response_message);
+ } else if ($response->status != 200 and $response->status != 206) {
+ return null;
+ }
+
+ return $response_message;
+ }
+
+ /**
+ * @access private
+ */
+ function _makeKVPost($message, $server_url)
+ {
+ $body = $message->toURLEncoded();
+ $resp = $this->fetcher->post($server_url, $body);
+
+ if ($resp === null) {
+ return null;
+ }
+
+ return $this->_httpResponseToMessage($resp, $server_url);
+ }
+
+ /**
+ * @access private
+ */
+ function _getAssociation($endpoint)
+ {
+ if (!$this->_use_assocs) {
+ return null;
+ }
+
+ $assoc = $this->store->getAssociation($endpoint->server_url);
+
+ if (($assoc === null) ||
+ ($assoc->getExpiresIn() <= 0)) {
+
+ $assoc = $this->_negotiateAssociation($endpoint);
+
+ if ($assoc !== null) {
+ $this->store->storeAssociation($endpoint->server_url,
+ $assoc);
+ }
+ }
+
+ return $assoc;
+ }
+
+ /**
+ * Handle ServerErrors resulting from association requests.
+ *
+ * @return $result If server replied with an C{unsupported-type}
+ * error, return a tuple of supported C{association_type},
+ * C{session_type}. Otherwise logs the error and returns null.
+ *
+ * @access private
+ */
+ function _extractSupportedAssociationType($server_error, $endpoint,
+ $assoc_type)
+ {
+ // Any error message whose code is not 'unsupported-type'
+ // should be considered a total failure.
+ if (($server_error->error_code != 'unsupported-type') ||
+ ($server_error->message->isOpenID1())) {
+ return null;
+ }
+
+ // The server didn't like the association/session type that we
+ // sent, and it sent us back a message that might tell us how
+ // to handle it.
+
+ // Extract the session_type and assoc_type from the error
+ // message
+ $assoc_type = $server_error->message->getArg(Auth_OpenID_OPENID_NS,
+ 'assoc_type');
+
+ $session_type = $server_error->message->getArg(Auth_OpenID_OPENID_NS,
+ 'session_type');
+
+ if (($assoc_type === null) || ($session_type === null)) {
+ return null;
+ } else if (!$this->negotiator->isAllowed($assoc_type,
+ $session_type)) {
+ return null;
+ } else {
+ return array($assoc_type, $session_type);
+ }
+ }
+
+ /**
+ * @access private
+ */
+ function _negotiateAssociation($endpoint)
+ {
+ // Get our preferred session/association type from the negotiatior.
+ list($assoc_type, $session_type) = $this->negotiator->getAllowedType();
+
+ $assoc = $this->_requestAssociation(
+ $endpoint, $assoc_type, $session_type);
+
+ if (Auth_OpenID::isFailure($assoc)) {
+ return null;
+ }
+
+ if (is_a($assoc, 'Auth_OpenID_ServerErrorContainer')) {
+ $why = $assoc;
+
+ $supportedTypes = $this->_extractSupportedAssociationType(
+ $why, $endpoint, $assoc_type);
+
+ if ($supportedTypes !== null) {
+ list($assoc_type, $session_type) = $supportedTypes;
+
+ // Attempt to create an association from the assoc_type
+ // and session_type that the server told us it
+ // supported.
+ $assoc = $this->_requestAssociation(
+ $endpoint, $assoc_type, $session_type);
+
+ if (is_a($assoc, 'Auth_OpenID_ServerErrorContainer')) {
+ // Do not keep trying, since it rejected the
+ // association type that it told us to use.
+ // oidutil.log('Server %s refused its suggested association
+ // 'type: session_type=%s, assoc_type=%s'
+ // % (endpoint.server_url, session_type,
+ // assoc_type))
+ return null;
+ } else {
+ return $assoc;
+ }
+ } else {
+ return null;
+ }
+ } else {
+ return $assoc;
+ }
+ }
+
+ /**
+ * @access private
+ */
+ function _requestAssociation($endpoint, $assoc_type, $session_type)
+ {
+ list($assoc_session, $args) = $this->_createAssociateRequest(
+ $endpoint, $assoc_type, $session_type);
+
+ $response_message = $this->_makeKVPost($args, $endpoint->server_url);
+
+ if ($response_message === null) {
+ // oidutil.log('openid.associate request failed: %s' % (why[0],))
+ return null;
+ } else if (is_a($response_message,
+ 'Auth_OpenID_ServerErrorContainer')) {
+ return $response_message;
+ }
+
+ return $this->_extractAssociation($response_message, $assoc_session);
+ }
+
+ /**
+ * @access private
+ */
+ function _extractAssociation($assoc_response, $assoc_session)
+ {
+ // Extract the common fields from the response, raising an
+ // exception if they are not found
+ $assoc_type = $assoc_response->getArg(
+ Auth_OpenID_OPENID_NS, 'assoc_type',
+ Auth_OpenID_NO_DEFAULT);
+
+ if (Auth_OpenID::isFailure($assoc_type)) {
+ return $assoc_type;
+ }
+
+ $assoc_handle = $assoc_response->getArg(
+ Auth_OpenID_OPENID_NS, 'assoc_handle',
+ Auth_OpenID_NO_DEFAULT);
+
+ if (Auth_OpenID::isFailure($assoc_handle)) {
+ return $assoc_handle;
+ }
+
+ // expires_in is a base-10 string. The Python parsing will
+ // accept literals that have whitespace around them and will
+ // accept negative values. Neither of these are really in-spec,
+ // but we think it's OK to accept them.
+ $expires_in_str = $assoc_response->getArg(
+ Auth_OpenID_OPENID_NS, 'expires_in',
+ Auth_OpenID_NO_DEFAULT);
+
+ if (Auth_OpenID::isFailure($expires_in_str)) {
+ return $expires_in_str;
+ }
+
+ $expires_in = Auth_OpenID::intval($expires_in_str);
+ if ($expires_in === false) {
+
+ $err = sprintf("Could not parse expires_in from association ".
+ "response %s", print_r($assoc_response, true));
+ return new Auth_OpenID_FailureResponse(null, $err);
+ }
+
+ // OpenID 1 has funny association session behaviour.
+ if ($assoc_response->isOpenID1()) {
+ $session_type = $this->_getOpenID1SessionType($assoc_response);
+ } else {
+ $session_type = $assoc_response->getArg(
+ Auth_OpenID_OPENID2_NS, 'session_type',
+ Auth_OpenID_NO_DEFAULT);
+
+ if (Auth_OpenID::isFailure($session_type)) {
+ return $session_type;
+ }
+ }
+
+ // Session type mismatch
+ if ($assoc_session->session_type != $session_type) {
+ if ($assoc_response->isOpenID1() &&
+ ($session_type == 'no-encryption')) {
+ // In OpenID 1, any association request can result in
+ // a 'no-encryption' association response. Setting
+ // assoc_session to a new no-encryption session should
+ // make the rest of this function work properly for
+ // that case.
+ $assoc_session = new Auth_OpenID_PlainTextConsumerSession();
+ } else {
+ // Any other mismatch, regardless of protocol version
+ // results in the failure of the association session
+ // altogether.
+ return null;
+ }
+ }
+
+ // Make sure assoc_type is valid for session_type
+ if (!in_array($assoc_type, $assoc_session->allowed_assoc_types)) {
+ return null;
+ }
+
+ // Delegate to the association session to extract the secret
+ // from the response, however is appropriate for that session
+ // type.
+ $secret = $assoc_session->extractSecret($assoc_response);
+
+ if ($secret === null) {
+ return null;
+ }
+
+ return Auth_OpenID_Association::fromExpiresIn(
+ $expires_in, $assoc_handle, $secret, $assoc_type);
+ }
+
+ /**
+ * @access private
+ */
+ function _createAssociateRequest($endpoint, $assoc_type, $session_type)
+ {
+ if (array_key_exists($session_type, $this->session_types)) {
+ $session_type_class = $this->session_types[$session_type];
+
+ if (is_callable($session_type_class)) {
+ $assoc_session = $session_type_class();
+ } else {
+ $assoc_session = new $session_type_class();
+ }
+ } else {
+ return null;
+ }
+
+ $args = array(
+ 'mode' => 'associate',
+ 'assoc_type' => $assoc_type);
+
+ if (!$endpoint->compatibilityMode()) {
+ $args['ns'] = Auth_OpenID_OPENID2_NS;
+ }
+
+ // Leave out the session type if we're in compatibility mode
+ // *and* it's no-encryption.
+ if ((!$endpoint->compatibilityMode()) ||
+ ($assoc_session->session_type != 'no-encryption')) {
+ $args['session_type'] = $assoc_session->session_type;
+ }
+
+ $args = array_merge($args, $assoc_session->getRequest());
+ $message = Auth_OpenID_Message::fromOpenIDArgs($args);
+ return array($assoc_session, $message);
+ }
+
+ /**
+ * Given an association response message, extract the OpenID 1.X
+ * session type.
+ *
+ * This function mostly takes care of the 'no-encryption' default
+ * behavior in OpenID 1.
+ *
+ * If the association type is plain-text, this function will
+ * return 'no-encryption'
+ *
+ * @access private
+ * @return $typ The association type for this message
+ */
+ function _getOpenID1SessionType($assoc_response)
+ {
+ // If it's an OpenID 1 message, allow session_type to default
+ // to None (which signifies "no-encryption")
+ $session_type = $assoc_response->getArg(Auth_OpenID_OPENID1_NS,
+ 'session_type');
+
+ // Handle the differences between no-encryption association
+ // respones in OpenID 1 and 2:
+
+ // no-encryption is not really a valid session type for OpenID
+ // 1, but we'll accept it anyway, while issuing a warning.
+ if ($session_type == 'no-encryption') {
+ // oidutil.log('WARNING: OpenID server sent "no-encryption"'
+ // 'for OpenID 1.X')
+ } else if (($session_type == '') || ($session_type === null)) {
+ // Missing or empty session type is the way to flag a
+ // 'no-encryption' response. Change the session type to
+ // 'no-encryption' so that it can be handled in the same
+ // way as OpenID 2 'no-encryption' respones.
+ $session_type = 'no-encryption';
+ }
+
+ return $session_type;
+ }
+}
+
+/**
+ * This class represents an authentication request from a consumer to
+ * an OpenID server.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_AuthRequest {
+
+ /**
+ * Initialize an authentication request with the specified token,
+ * association, and endpoint.
+ *
+ * Users of this library should not create instances of this
+ * class. Instances of this class are created by the library when
+ * needed.
+ */
+ function Auth_OpenID_AuthRequest($endpoint, $assoc)
+ {
+ $this->assoc = $assoc;
+ $this->endpoint = $endpoint;
+ $this->return_to_args = array();
+ $this->message = new Auth_OpenID_Message(
+ $endpoint->preferredNamespace());
+ $this->_anonymous = false;
+ }
+
+ /**
+ * Add an extension to this checkid request.
+ *
+ * $extension_request: An object that implements the extension
+ * request interface for adding arguments to an OpenID message.
+ */
+ function addExtension($extension_request)
+ {
+ $extension_request->toMessage($this->message);
+ }
+
+ /**
+ * Add an extension argument to this OpenID authentication
+ * request.
+ *
+ * Use caution when adding arguments, because they will be
+ * URL-escaped and appended to the redirect URL, which can easily
+ * get quite long.
+ *
+ * @param string $namespace The namespace for the extension. For
+ * example, the simple registration extension uses the namespace
+ * 'sreg'.
+ *
+ * @param string $key The key within the extension namespace. For
+ * example, the nickname field in the simple registration
+ * extension's key is 'nickname'.
+ *
+ * @param string $value The value to provide to the server for
+ * this argument.
+ */
+ function addExtensionArg($namespace, $key, $value)
+ {
+ return $this->message->setArg($namespace, $key, $value);
+ }
+
+ /**
+ * Set whether this request should be made anonymously. If a
+ * request is anonymous, the identifier will not be sent in the
+ * request. This is only useful if you are making another kind of
+ * request with an extension in this request.
+ *
+ * Anonymous requests are not allowed when the request is made
+ * with OpenID 1.
+ */
+ function setAnonymous($is_anonymous)
+ {
+ if ($is_anonymous && $this->message->isOpenID1()) {
+ return false;
+ } else {
+ $this->_anonymous = $is_anonymous;
+ return true;
+ }
+ }
+
+ /**
+ * Produce a {@link Auth_OpenID_Message} representing this
+ * request.
+ *
+ * @param string $realm The URL (or URL pattern) that identifies
+ * your web site to the user when she is authorizing it.
+ *
+ * @param string $return_to The URL that the OpenID provider will
+ * send the user back to after attempting to verify her identity.
+ *
+ * Not specifying a return_to URL means that the user will not be
+ * returned to the site issuing the request upon its completion.
+ *
+ * @param bool $immediate If true, the OpenID provider is to send
+ * back a response immediately, useful for behind-the-scenes
+ * authentication attempts. Otherwise the OpenID provider may
+ * engage the user before providing a response. This is the
+ * default case, as the user may need to provide credentials or
+ * approve the request before a positive response can be sent.
+ */
+ function getMessage($realm, $return_to=null, $immediate=false)
+ {
+ if ($return_to) {
+ $return_to = Auth_OpenID::appendArgs($return_to,
+ $this->return_to_args);
+ } else if ($immediate) {
+ // raise ValueError(
+ // '"return_to" is mandatory when
+ //using "checkid_immediate"')
+ return new Auth_OpenID_FailureResponse(null,
+ "'return_to' is mandatory when using checkid_immediate");
+ } else if ($this->message->isOpenID1()) {
+ // raise ValueError('"return_to" is
+ // mandatory for OpenID 1 requests')
+ return new Auth_OpenID_FailureResponse(null,
+ "'return_to' is mandatory for OpenID 1 requests");
+ } else if ($this->return_to_args) {
+ // raise ValueError('extra "return_to" arguments
+ // were specified, but no return_to was specified')
+ return new Auth_OpenID_FailureResponse(null,
+ "extra 'return_to' arguments where specified, " .
+ "but no return_to was specified");
+ }
+
+ if ($immediate) {
+ $mode = 'checkid_immediate';
+ } else {
+ $mode = 'checkid_setup';
+ }
+
+ $message = $this->message->copy();
+ if ($message->isOpenID1()) {
+ $realm_key = 'trust_root';
+ } else {
+ $realm_key = 'realm';
+ }
+
+ $message->updateArgs(Auth_OpenID_OPENID_NS,
+ array(
+ $realm_key => $realm,
+ 'mode' => $mode,
+ 'return_to' => $return_to));
+
+ if (!$this->_anonymous) {
+ if ($this->endpoint->isOPIdentifier()) {
+ // This will never happen when we're in compatibility
+ // mode, as long as isOPIdentifier() returns False
+ // whenever preferredNamespace() returns OPENID1_NS.
+ $claimed_id = $request_identity =
+ Auth_OpenID_IDENTIFIER_SELECT;
+ } else {
+ $request_identity = $this->endpoint->getLocalID();
+ $claimed_id = $this->endpoint->claimed_id;
+ }
+
+ // This is true for both OpenID 1 and 2
+ $message->setArg(Auth_OpenID_OPENID_NS, 'identity',
+ $request_identity);
+
+ if ($message->isOpenID2()) {
+ $message->setArg(Auth_OpenID_OPENID2_NS, 'claimed_id',
+ $claimed_id);
+ }
+ }
+
+ if ($this->assoc) {
+ $message->setArg(Auth_OpenID_OPENID_NS, 'assoc_handle',
+ $this->assoc->handle);
+ }
+
+ return $message;
+ }
+
+ function redirectURL($realm, $return_to = null,
+ $immediate = false)
+ {
+ $message = $this->getMessage($realm, $return_to, $immediate);
+
+ if (Auth_OpenID::isFailure($message)) {
+ return $message;
+ }
+
+ return $message->toURL($this->endpoint->server_url);
+ }
+
+ /**
+ * Get html for a form to submit this request to the IDP.
+ *
+ * form_tag_attrs: An array of attributes to be added to the form
+ * tag. 'accept-charset' and 'enctype' have defaults that can be
+ * overridden. If a value is supplied for 'action' or 'method', it
+ * will be replaced.
+ */
+ function formMarkup($realm, $return_to=null, $immediate=false,
+ $form_tag_attrs=null)
+ {
+ $message = $this->getMessage($realm, $return_to, $immediate);
+
+ if (Auth_OpenID::isFailure($message)) {
+ return $message;
+ }
+
+ return $message->toFormMarkup($this->endpoint->server_url,
+ $form_tag_attrs);
+ }
+
+ /**
+ * Get a complete html document that will autosubmit the request
+ * to the IDP.
+ *
+ * Wraps formMarkup. See the documentation for that function.
+ */
+ function htmlMarkup($realm, $return_to=null, $immediate=false,
+ $form_tag_attrs=null)
+ {
+ $form = $this->formMarkup($realm, $return_to, $immediate,
+ $form_tag_attrs);
+
+ if (Auth_OpenID::isFailure($form)) {
+ return $form;
+ }
+ return Auth_OpenID::autoSubmitHTML($form);
+ }
+
+ function shouldSendRedirect()
+ {
+ return $this->endpoint->compatibilityMode();
+ }
+}
+
+/**
+ * The base class for responses from the Auth_OpenID_Consumer.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_ConsumerResponse {
+ var $status = null;
+
+ function setEndpoint($endpoint)
+ {
+ $this->endpoint = $endpoint;
+ if ($endpoint === null) {
+ $this->identity_url = null;
+ } else {
+ $this->identity_url = $endpoint->claimed_id;
+ }
+ }
+
+ /**
+ * Return the display identifier for this response.
+ *
+ * The display identifier is related to the Claimed Identifier, but the
+ * two are not always identical. The display identifier is something the
+ * user should recognize as what they entered, whereas the response's
+ * claimed identifier (in the identity_url attribute) may have extra
+ * information for better persistence.
+ *
+ * URLs will be stripped of their fragments for display. XRIs will
+ * display the human-readable identifier (i-name) instead of the
+ * persistent identifier (i-number).
+ *
+ * Use the display identifier in your user interface. Use
+ * identity_url for querying your database or authorization server.
+ *
+ */
+ function getDisplayIdentifier()
+ {
+ if ($this->endpoint !== null) {
+ return $this->endpoint->getDisplayIdentifier();
+ }
+ return null;
+ }
+}
+
+/**
+ * A response with a status of Auth_OpenID_SUCCESS. Indicates that
+ * this request is a successful acknowledgement from the OpenID server
+ * that the supplied URL is, indeed controlled by the requesting
+ * agent. This has three relevant attributes:
+ *
+ * claimed_id - The identity URL that has been authenticated
+ *
+ * signed_args - The arguments in the server's response that were
+ * signed and verified.
+ *
+ * status - Auth_OpenID_SUCCESS.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_SuccessResponse extends Auth_OpenID_ConsumerResponse {
+ var $status = Auth_OpenID_SUCCESS;
+
+ /**
+ * @access private
+ */
+ function Auth_OpenID_SuccessResponse($endpoint, $message, $signed_args=null)
+ {
+ $this->endpoint = $endpoint;
+ $this->identity_url = $endpoint->claimed_id;
+ $this->signed_args = $signed_args;
+ $this->message = $message;
+
+ if ($this->signed_args === null) {
+ $this->signed_args = array();
+ }
+ }
+
+ /**
+ * Extract signed extension data from the server's response.
+ *
+ * @param string $prefix The extension namespace from which to
+ * extract the extension data.
+ */
+ function extensionResponse($namespace_uri, $require_signed)
+ {
+ if ($require_signed) {
+ return $this->getSignedNS($namespace_uri);
+ } else {
+ return $this->message->getArgs($namespace_uri);
+ }
+ }
+
+ function isOpenID1()
+ {
+ return $this->message->isOpenID1();
+ }
+
+ function isSigned($ns_uri, $ns_key)
+ {
+ // Return whether a particular key is signed, regardless of
+ // its namespace alias
+ return in_array($this->message->getKey($ns_uri, $ns_key),
+ $this->signed_args);
+ }
+
+ function getSigned($ns_uri, $ns_key, $default = null)
+ {
+ // Return the specified signed field if available, otherwise
+ // return default
+ if ($this->isSigned($ns_uri, $ns_key)) {
+ return $this->message->getArg($ns_uri, $ns_key, $default);
+ } else {
+ return $default;
+ }
+ }
+
+ function getSignedNS($ns_uri)
+ {
+ $args = array();
+
+ $msg_args = $this->message->getArgs($ns_uri);
+ if (Auth_OpenID::isFailure($msg_args)) {
+ return null;
+ }
+
+ foreach ($msg_args as $key => $value) {
+ if (!$this->isSigned($ns_uri, $key)) {
+ unset($msg_args[$key]);
+ }
+ }
+
+ return $msg_args;
+ }
+
+ /**
+ * Get the openid.return_to argument from this response.
+ *
+ * This is useful for verifying that this request was initiated by
+ * this consumer.
+ *
+ * @return string $return_to The return_to URL supplied to the
+ * server on the initial request, or null if the response did not
+ * contain an 'openid.return_to' argument.
+ */
+ function getReturnTo()
+ {
+ return $this->getSigned(Auth_OpenID_OPENID_NS, 'return_to');
+ }
+}
+
+/**
+ * A response with a status of Auth_OpenID_FAILURE. Indicates that the
+ * OpenID protocol has failed. This could be locally or remotely
+ * triggered. This has three relevant attributes:
+ *
+ * claimed_id - The identity URL for which authentication was
+ * attempted, if it can be determined. Otherwise, null.
+ *
+ * message - A message indicating why the request failed, if one is
+ * supplied. Otherwise, null.
+ *
+ * status - Auth_OpenID_FAILURE.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_FailureResponse extends Auth_OpenID_ConsumerResponse {
+ var $status = Auth_OpenID_FAILURE;
+
+ function Auth_OpenID_FailureResponse($endpoint, $message = null,
+ $contact = null, $reference = null)
+ {
+ $this->setEndpoint($endpoint);
+ $this->message = $message;
+ $this->contact = $contact;
+ $this->reference = $reference;
+ }
+}
+
+/**
+ * A specific, internal failure used to detect type URI mismatch.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_TypeURIMismatch extends Auth_OpenID_FailureResponse {
+}
+
+/**
+ * Exception that is raised when the server returns a 400 response
+ * code to a direct request.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_ServerErrorContainer {
+ function Auth_OpenID_ServerErrorContainer($error_text,
+ $error_code,
+ $message)
+ {
+ $this->error_text = $error_text;
+ $this->error_code = $error_code;
+ $this->message = $message;
+ }
+
+ /**
+ * @access private
+ */
+ static function fromMessage($message)
+ {
+ $error_text = $message->getArg(
+ Auth_OpenID_OPENID_NS, 'error', '<no error message supplied>');
+ $error_code = $message->getArg(Auth_OpenID_OPENID_NS, 'error_code');
+ return new Auth_OpenID_ServerErrorContainer($error_text,
+ $error_code,
+ $message);
+ }
+}
+
+/**
+ * A response with a status of Auth_OpenID_CANCEL. Indicates that the
+ * user cancelled the OpenID authentication request. This has two
+ * relevant attributes:
+ *
+ * claimed_id - The identity URL for which authentication was
+ * attempted, if it can be determined. Otherwise, null.
+ *
+ * status - Auth_OpenID_SUCCESS.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_CancelResponse extends Auth_OpenID_ConsumerResponse {
+ var $status = Auth_OpenID_CANCEL;
+
+ function Auth_OpenID_CancelResponse($endpoint)
+ {
+ $this->setEndpoint($endpoint);
+ }
+}
+
+/**
+ * A response with a status of Auth_OpenID_SETUP_NEEDED. Indicates
+ * that the request was in immediate mode, and the server is unable to
+ * authenticate the user without further interaction.
+ *
+ * claimed_id - The identity URL for which authentication was
+ * attempted.
+ *
+ * setup_url - A URL that can be used to send the user to the server
+ * to set up for authentication. The user should be redirected in to
+ * the setup_url, either in the current window or in a new browser
+ * window. Null in OpenID 2.
+ *
+ * status - Auth_OpenID_SETUP_NEEDED.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_SetupNeededResponse extends Auth_OpenID_ConsumerResponse {
+ var $status = Auth_OpenID_SETUP_NEEDED;
+
+ function Auth_OpenID_SetupNeededResponse($endpoint,
+ $setup_url = null)
+ {
+ $this->setEndpoint($endpoint);
+ $this->setup_url = $setup_url;
+ }
+}
+
+
+
--- /dev/null
+++ b/lib/openid-php/Auth/OpenID/Server.php
@@ -1,1 +1,1766 @@
-
+<?php
+
+/**
+ * OpenID server protocol and logic.
+ *
+ * Overview
+ *
+ * An OpenID server must perform three tasks:
+ *
+ * 1. Examine the incoming request to determine its nature and validity.
+ * 2. Make a decision about how to respond to this request.
+ * 3. Format the response according to the protocol.
+ *
+ * The first and last of these tasks may performed by the {@link
+ * Auth_OpenID_Server::decodeRequest()} and {@link
+ * Auth_OpenID_Server::encodeResponse} methods. Who gets to do the
+ * intermediate task -- deciding how to respond to the request -- will
+ * depend on what type of request it is.
+ *
+ * If it's a request to authenticate a user (a 'checkid_setup' or
+ * 'checkid_immediate' request), you need to decide if you will assert
+ * that this user may claim the identity in question. Exactly how you
+ * do that is a matter of application policy, but it generally
+ * involves making sure the user has an account with your system and
+ * is logged in, checking to see if that identity is hers to claim,
+ * and verifying with the user that she does consent to releasing that
+ * information to the party making the request.
+ *
+ * Examine the properties of the {@link Auth_OpenID_CheckIDRequest}
+ * object, and if and when you've come to a decision, form a response
+ * by calling {@link Auth_OpenID_CheckIDRequest::answer()}.
+ *
+ * Other types of requests relate to establishing associations between
+ * client and server and verifing the authenticity of previous
+ * communications. {@link Auth_OpenID_Server} contains all the logic
+ * and data necessary to respond to such requests; just pass it to
+ * {@link Auth_OpenID_Server::handleRequest()}.
+ *
+ * OpenID Extensions
+ *
+ * Do you want to provide other information for your users in addition
+ * to authentication? Version 1.2 of the OpenID protocol allows
+ * consumers to add extensions to their requests. For example, with
+ * sites using the Simple Registration
+ * Extension
+ * (http://openid.net/specs/openid-simple-registration-extension-1_0.html),
+ * a user can agree to have their nickname and e-mail address sent to
+ * a site when they sign up.
+ *
+ * Since extensions do not change the way OpenID authentication works,
+ * code to handle extension requests may be completely separate from
+ * the {@link Auth_OpenID_Request} class here. But you'll likely want
+ * data sent back by your extension to be signed. {@link
+ * Auth_OpenID_ServerResponse} provides methods with which you can add
+ * data to it which can be signed with the other data in the OpenID
+ * signature.
+ *
+ * For example:
+ *
+ * <pre> // when request is a checkid_* request
+ * $response = $request->answer(true);
+ * // this will a signed 'openid.sreg.timezone' parameter to the response
+ * response.addField('sreg', 'timezone', 'America/Los_Angeles')</pre>
+ *
+ * Stores
+ *
+ * The OpenID server needs to maintain state between requests in order
+ * to function. Its mechanism for doing this is called a store. The
+ * store interface is defined in Interface.php. Additionally, several
+ * concrete store implementations are provided, so that most sites
+ * won't need to implement a custom store. For a store backed by flat
+ * files on disk, see {@link Auth_OpenID_FileStore}. For stores based
+ * on MySQL, SQLite, or PostgreSQL, see the {@link
+ * Auth_OpenID_SQLStore} subclasses.
+ *
+ * Upgrading
+ *
+ * The keys by which a server looks up associations in its store have
+ * changed in version 1.2 of this library. If your store has entries
+ * created from version 1.0 code, you should empty it.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: See the COPYING file included in this distribution.
+ *
+ * @package OpenID
+ * @author JanRain, Inc. <openid@janrain.com>
+ * @copyright 2005-2008 Janrain, Inc.
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
+ */
+
+/**
+ * Required imports
+ */
+require_once "Auth/OpenID.php";
+require_once "Auth/OpenID/Association.php";
+require_once "Auth/OpenID/CryptUtil.php";
+require_once "Auth/OpenID/BigMath.php";
+require_once "Auth/OpenID/DiffieHellman.php";
+require_once "Auth/OpenID/KVForm.php";
+require_once "Auth/OpenID/TrustRoot.php";
+require_once "Auth/OpenID/ServerRequest.php";
+require_once "Auth/OpenID/Message.php";
+require_once "Auth/OpenID/Nonce.php";
+
+define('AUTH_OPENID_HTTP_OK', 200);
+define('AUTH_OPENID_HTTP_REDIRECT', 302);
+define('AUTH_OPENID_HTTP_ERROR', 400);
+
+/**
+ * @access private
+ */
+global $_Auth_OpenID_Request_Modes;
+$_Auth_OpenID_Request_Modes = array('checkid_setup',
+ 'checkid_immediate');
+
+/**
+ * @access private
+ */
+define('Auth_OpenID_ENCODE_KVFORM', 'kfvorm');
+
+/**
+ * @access private
+ */
+define('Auth_OpenID_ENCODE_URL', 'URL/redirect');
+
+/**
+ * @access private
+ */
+define('Auth_OpenID_ENCODE_HTML_FORM', 'HTML form');
+
+/**
+ * @access private
+ */
+function Auth_OpenID_isError($obj, $cls = 'Auth_OpenID_ServerError')
+{
+ return is_a($obj, $cls);
+}
+
+/**
+ * An error class which gets instantiated and returned whenever an
+ * OpenID protocol error occurs. Be prepared to use this in place of
+ * an ordinary server response.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_ServerError {
+ /**
+ * @access private
+ */
+ function Auth_OpenID_ServerError($message = null, $text = null,
+ $reference = null, $contact = null)
+ {
+ $this->message = $message;
+ $this->text = $text;
+ $this->contact = $contact;
+ $this->reference = $reference;
+ }
+
+ function getReturnTo()
+ {
+ if ($this->message &&
+ $this->message->hasKey(Auth_OpenID_OPENID_NS, 'return_to')) {
+ return $this->message->getArg(Auth_OpenID_OPENID_NS,
+ 'return_to');
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the return_to URL for the request which caused this
+ * error.
+ */
+ function hasReturnTo()
+ {
+ return $this->getReturnTo() !== null;
+ }
+
+ /**
+ * Encodes this error's response as a URL suitable for
+ * redirection. If the response has no return_to, another
+ * Auth_OpenID_ServerError is returned.
+ */
+ function encodeToURL()
+ {
+ if (!$this->message) {
+ return null;
+ }
+
+ $msg = $this->toMessage();
+ return $msg->toURL($this->getReturnTo());
+ }
+
+ /**
+ * Encodes the response to key-value form. This is a
+ * machine-readable format used to respond to messages which came
+ * directly from the consumer and not through the user-agent. See
+ * the OpenID specification.
+ */
+ function encodeToKVForm()
+ {
+ return Auth_OpenID_KVForm::fromArray(
+ array('mode' => 'error',
+ 'error' => $this->toString()));
+ }
+
+ function toFormMarkup($form_tag_attrs=null)
+ {
+ $msg = $this->toMessage();
+ return $msg->toFormMarkup($this->getReturnTo(), $form_tag_attrs);
+ }
+
+ function toHTML($form_tag_attrs=null)
+ {
+ return Auth_OpenID::autoSubmitHTML(
+ $this->toFormMarkup($form_tag_attrs));
+ }
+
+ function toMessage()
+ {
+ // Generate a Message object for sending to the relying party,
+ // after encoding.
+ $namespace = $this->message->getOpenIDNamespace();
+ $reply = new Auth_OpenID_Message($namespace);
+ $reply->setArg(Auth_OpenID_OPENID_NS, 'mode', 'error');
+ $reply->setArg(Auth_OpenID_OPENID_NS, 'error', $this->toString());
+
+ if ($this->contact !== null) {
+ $reply->setArg(Auth_OpenID_OPENID_NS, 'contact', $this->contact);
+ }
+
+ if ($this->reference !== null) {
+ $reply->setArg(Auth_OpenID_OPENID_NS, 'reference',
+ $this->reference);
+ }
+
+ return $reply;
+ }
+
+ /**
+ * Returns one of Auth_OpenID_ENCODE_URL,
+ * Auth_OpenID_ENCODE_KVFORM, or null, depending on the type of
+ * encoding expected for this error's payload.
+ */
+ function whichEncoding()
+ {
+ global $_Auth_OpenID_Request_Modes;
+
+ if ($this->hasReturnTo()) {
+ if ($this->message->isOpenID2() &&
+ (strlen($this->encodeToURL()) >
+ Auth_OpenID_OPENID1_URL_LIMIT)) {
+ return Auth_OpenID_ENCODE_HTML_FORM;
+ } else {
+ return Auth_OpenID_ENCODE_URL;
+ }
+ }
+
+ if (!$this->message) {
+ return null;
+ }
+
+ $mode = $this->message->getArg(Auth_OpenID_OPENID_NS,
+ 'mode');
+
+ if ($mode) {
+ if (!in_array($mode, $_Auth_OpenID_Request_Modes)) {
+ return Auth_OpenID_ENCODE_KVFORM;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns this error message.
+ */
+ function toString()
+ {
+ if ($this->text) {
+ return $this->text;
+ } else {
+ return get_class($this) . " error";
+ }
+ }
+}
+
+/**
+ * Error returned by the server code when a return_to is absent from a
+ * request.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_NoReturnToError extends Auth_OpenID_ServerError {
+ function Auth_OpenID_NoReturnToError($message = null,
+ $text = "No return_to URL available")
+ {
+ parent::Auth_OpenID_ServerError($message, $text);
+ }
+
+ function toString()
+ {
+ return "No return_to available";
+ }
+}
+
+/**
+ * An error indicating that the return_to URL is malformed.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_MalformedReturnURL extends Auth_OpenID_ServerError {
+ function Auth_OpenID_MalformedReturnURL($message, $return_to)
+ {
+ $this->return_to = $return_to;
+ parent::Auth_OpenID_ServerError($message, "malformed return_to URL");
+ }
+}
+
+/**
+ * This error is returned when the trust_root value is malformed.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_MalformedTrustRoot extends Auth_OpenID_ServerError {
+ function Auth_OpenID_MalformedTrustRoot($message = null,
+ $text = "Malformed trust root")
+ {
+ parent::Auth_OpenID_ServerError($message, $text);
+ }
+
+ function toString()
+ {
+ return "Malformed trust root";
+ }
+}
+
+/**
+ * The base class for all server request classes.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_Request {
+ var $mode = null;
+}
+
+/**
+ * A request to verify the validity of a previous response.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_CheckAuthRequest extends Auth_OpenID_Request {
+ var $mode = "check_authentication";
+ var $invalidate_handle = null;
+
+ function Auth_OpenID_CheckAuthRequest($assoc_handle, $signed,
+ $invalidate_handle = null)
+ {
+ $this->assoc_handle = $assoc_handle;
+ $this->signed = $signed;
+ if ($invalidate_handle !== null) {
+ $this->invalidate_handle = $invalidate_handle;
+ }
+ $this->namespace = Auth_OpenID_OPENID2_NS;
+ $this->message = null;
+ }
+
+ static function fromMessage($message, $server=null)
+ {
+ $required_keys = array('assoc_handle', 'sig', 'signed');
+
+ foreach ($required_keys as $k) {
+ if (!$message->getArg(Auth_OpenID_OPENID_NS, $k)) {
+ return new Auth_OpenID_ServerError($message,
+ sprintf("%s request missing required parameter %s from \
+ query", "check_authentication", $k));
+ }
+ }
+
+ $assoc_handle = $message->getArg(Auth_OpenID_OPENID_NS, 'assoc_handle');
+ $sig = $message->getArg(Auth_OpenID_OPENID_NS, 'sig');
+
+ $signed_list = $message->getArg(Auth_OpenID_OPENID_NS, 'signed');
+ $signed_list = explode(",", $signed_list);
+
+ $signed = $message;
+ if ($signed->hasKey(Auth_OpenID_OPENID_NS, 'mode')) {
+ $signed->setArg(Auth_OpenID_OPENID_NS, 'mode', 'id_res');
+ }
+
+ $result = new Auth_OpenID_CheckAuthRequest($assoc_handle, $signed);
+ $result->message = $message;
+ $result->sig = $sig;
+ $result->invalidate_handle = $message->getArg(Auth_OpenID_OPENID_NS,
+ 'invalidate_handle');
+ return $result;
+ }
+
+ function answer($signatory)
+ {
+ $is_valid = $signatory->verify($this->assoc_handle, $this->signed);
+
+ // Now invalidate that assoc_handle so it this checkAuth
+ // message cannot be replayed.
+ $signatory->invalidate($this->assoc_handle, true);
+ $response = new Auth_OpenID_ServerResponse($this);
+
+ $response->fields->setArg(Auth_OpenID_OPENID_NS,
+ 'is_valid',
+ ($is_valid ? "true" : "false"));
+
+ if ($this->invalidate_handle) {
+ $assoc = $signatory->getAssociation($this->invalidate_handle,
+ false);
+ if (!$assoc) {
+ $response->fields->setArg(Auth_OpenID_OPENID_NS,
+ 'invalidate_handle',
+ $this->invalidate_handle);
+ }
+ }
+ return $response;
+ }
+}
+
+/**
+ * A class implementing plaintext server sessions.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_PlainTextServerSession {
+ /**
+ * An object that knows how to handle association requests with no
+ * session type.
+ */
+ var $session_type = 'no-encryption';
+ var $needs_math = false;
+ var $allowed_assoc_types = array('HMAC-SHA1', 'HMAC-SHA256');
+
+ static function fromMessage($unused_request)
+ {
+ return new Auth_OpenID_PlainTextServerSession();
+ }
+
+ function answer($secret)
+ {
+ return array('mac_key' => base64_encode($secret));
+ }
+}
+
+/**
+ * A class implementing DH-SHA1 server sessions.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_DiffieHellmanSHA1ServerSession {
+ /**
+ * An object that knows how to handle association requests with
+ * the Diffie-Hellman session type.
+ */
+
+ var $session_type = 'DH-SHA1';
+ var $needs_math = true;
+ var $allowed_assoc_types = array('HMAC-SHA1');
+ var $hash_func = 'Auth_OpenID_SHA1';
+
+ function Auth_OpenID_DiffieHellmanSHA1ServerSession($dh, $consumer_pubkey)
+ {
+ $this->dh = $dh;
+ $this->consumer_pubkey = $consumer_pubkey;
+ }
+
+ static function getDH($message)
+ {
+ $dh_modulus = $message->getArg(Auth_OpenID_OPENID_NS, 'dh_modulus');
+ $dh_gen = $message->getArg(Auth_OpenID_OPENID_NS, 'dh_gen');
+
+ if ((($dh_modulus === null) && ($dh_gen !== null)) ||
+ (($dh_gen === null) && ($dh_modulus !== null))) {
+
+ if ($dh_modulus === null) {
+ $missing = 'modulus';
+ } else {
+ $missing = 'generator';
+ }
+
+ return new Auth_OpenID_ServerError($message,
+ 'If non-default modulus or generator is '.
+ 'supplied, both must be supplied. Missing '.
+ $missing);
+ }
+
+ $lib = Auth_OpenID_getMathLib();
+
+ if ($dh_modulus || $dh_gen) {
+ $dh_modulus = $lib->base64ToLong($dh_modulus);
+ $dh_gen = $lib->base64ToLong($dh_gen);
+ if ($lib->cmp($dh_modulus, 0) == 0 ||
+ $lib->cmp($dh_gen, 0) == 0) {
+ return new Auth_OpenID_ServerError(
+ $message, "Failed to parse dh_mod or dh_gen");
+ }
+ $dh = new Auth_OpenID_DiffieHellman($dh_modulus, $dh_gen);
+ } else {
+ $dh = new Auth_OpenID_DiffieHellman();
+ }
+
+ $consumer_pubkey = $message->getArg(Auth_OpenID_OPENID_NS,
+ 'dh_consumer_public');
+ if ($consumer_pubkey === null) {
+ return new Auth_OpenID_ServerError($message,
+ 'Public key for DH-SHA1 session '.
+ 'not found in query');
+ }
+
+ $consumer_pubkey =
+ $lib->base64ToLong($consumer_pubkey);
+
+ if ($consumer_pubkey === false) {
+ return new Auth_OpenID_ServerError($message,
+ "dh_consumer_public is not base64");
+ }
+
+ return array($dh, $consumer_pubkey);
+ }
+
+ static function fromMessage($message)
+ {
+ $result = Auth_OpenID_DiffieHellmanSHA1ServerSession::getDH($message);
+
+ if (is_a($result, 'Auth_OpenID_ServerError')) {
+ return $result;
+ } else {
+ list($dh, $consumer_pubkey) = $result;
+ return new Auth_OpenID_DiffieHellmanSHA1ServerSession($dh,
+ $consumer_pubkey);
+ }
+ }
+
+ function answer($secret)
+ {
+ $lib = Auth_OpenID_getMathLib();
+ $mac_key = $this->dh->xorSecret($this->consumer_pubkey, $secret,
+ $this->hash_func);
+ return array(
+ 'dh_server_public' =>
+ $lib->longToBase64($this->dh->public),
+ 'enc_mac_key' => base64_encode($mac_key));
+ }
+}
+
+/**
+ * A class implementing DH-SHA256 server sessions.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_DiffieHellmanSHA256ServerSession
+ extends Auth_OpenID_DiffieHellmanSHA1ServerSession {
+
+ var $session_type = 'DH-SHA256';
+ var $hash_func = 'Auth_OpenID_SHA256';
+ var $allowed_assoc_types = array('HMAC-SHA256');
+
+ static function fromMessage($message)
+ {
+ $result = Auth_OpenID_DiffieHellmanSHA1ServerSession::getDH($message);
+
+ if (is_a($result, 'Auth_OpenID_ServerError')) {
+ return $result;
+ } else {
+ list($dh, $consumer_pubkey) = $result;
+ return new Auth_OpenID_DiffieHellmanSHA256ServerSession($dh,
+ $consumer_pubkey);
+ }
+ }
+}
+
+/**
+ * A request to associate with the server.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_AssociateRequest extends Auth_OpenID_Request {
+ var $mode = "associate";
+
+ static function getSessionClasses()
+ {
+ return array(
+ 'no-encryption' => 'Auth_OpenID_PlainTextServerSession',
+ 'DH-SHA1' => 'Auth_OpenID_DiffieHellmanSHA1ServerSession',
+ 'DH-SHA256' => 'Auth_OpenID_DiffieHellmanSHA256ServerSession');
+ }
+
+ function Auth_OpenID_AssociateRequest($session, $assoc_type)
+ {
+ $this->session = $session;
+ $this->namespace = Auth_OpenID_OPENID2_NS;
+ $this->assoc_type = $assoc_type;
+ }
+
+ static function fromMessage($message, $server=null)
+ {
+ if ($message->isOpenID1()) {
+ $session_type = $message->getArg(Auth_OpenID_OPENID_NS,
+ 'session_type');
+
+ if ($session_type == 'no-encryption') {
+ // oidutil.log('Received OpenID 1 request with a no-encryption '
+ // 'assocaition session type. Continuing anyway.')
+ } else if (!$session_type) {
+ $session_type = 'no-encryption';
+ }
+ } else {
+ $session_type = $message->getArg(Auth_OpenID_OPENID_NS,
+ 'session_type');
+ if ($session_type === null) {
+ return new Auth_OpenID_ServerError($message,
+ "session_type missing from request");
+ }
+ }
+
+ $session_class = Auth_OpenID::arrayGet(
+ Auth_OpenID_AssociateRequest::getSessionClasses(),
+ $session_type);
+
+ if ($session_class === null) {
+ return new Auth_OpenID_ServerError($message,
+ "Unknown session type " .
+ $session_type);
+ }
+
+ $session = call_user_func(array($session_class, 'fromMessage'),
+ $message);
+ if (is_a($session, 'Auth_OpenID_ServerError')) {
+ return $session;
+ }
+
+ $assoc_type = $message->getArg(Auth_OpenID_OPENID_NS,
+ 'assoc_type', 'HMAC-SHA1');
+
+ if (!in_array($assoc_type, $session->allowed_assoc_types)) {
+ $fmt = "Session type %s does not support association type %s";
+ return new Auth_OpenID_ServerError($message,
+ sprintf($fmt, $session_type, $assoc_type));
+ }
+
+ $obj = new Auth_OpenID_AssociateRequest($session, $assoc_type);
+ $obj->message = $message;
+ $obj->namespace = $message->getOpenIDNamespace();
+ return $obj;
+ }
+
+ function answer($assoc)
+ {
+ $response = new Auth_OpenID_ServerResponse($this);
+ $response->fields->updateArgs(Auth_OpenID_OPENID_NS,
+ array(
+ 'expires_in' => sprintf('%d', $assoc->getExpiresIn()),
+ 'assoc_type' => $this->assoc_type,
+ 'assoc_handle' => $assoc->handle));
+
+ $response->fields->updateArgs(Auth_OpenID_OPENID_NS,
+ $this->session->answer($assoc->secret));
+
+ if (! ($this->session->session_type == 'no-encryption'
+ && $this->message->isOpenID1())) {
+ $response->fields->setArg(Auth_OpenID_OPENID_NS,
+ 'session_type',
+ $this->session->session_type);
+ }
+
+ return $response;
+ }
+
+ function answerUnsupported($text_message,
+ $preferred_association_type=null,
+ $preferred_session_type=null)
+ {
+ if ($this->message->isOpenID1()) {
+ return new Auth_OpenID_ServerError($this->message);
+ }
+
+ $response = new Auth_OpenID_ServerResponse($this);
+ $response->fields->setArg(Auth_OpenID_OPENID_NS,
+ 'error_code', 'unsupported-type');
+ $response->fields->setArg(Auth_OpenID_OPENID_NS,
+ 'error', $text_message);
+
+ if ($preferred_association_type) {
+ $response->fields->setArg(Auth_OpenID_OPENID_NS,
+ 'assoc_type',
+ $preferred_association_type);
+ }
+
+ if ($preferred_session_type) {
+ $response->fields->setArg(Auth_OpenID_OPENID_NS,
+ 'session_type',
+ $preferred_session_type);
+ }
+ $response->code = AUTH_OPENID_HTTP_ERROR;
+ return $response;
+ }
+}
+
+/**
+ * A request to confirm the identity of a user.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_CheckIDRequest extends Auth_OpenID_Request {
+ /**
+ * Return-to verification callback. Default is
+ * Auth_OpenID_verifyReturnTo from TrustRoot.php.
+ */
+ var $verifyReturnTo = 'Auth_OpenID_verifyReturnTo';
+
+ /**
+ * The mode of this request.
+ */
+ var $mode = "checkid_setup"; // or "checkid_immediate"
+
+ /**
+ * Whether this request is for immediate mode.
+ */
+ var $immediate = false;
+
+ /**
+ * The trust_root value for this request.
+ */
+ var $trust_root = null;
+
+ /**
+ * The OpenID namespace for this request.
+ * deprecated since version 2.0.2
+ */
+ var $namespace;
+
+ static function make($message, $identity, $return_to, $trust_root = null,
+ $immediate = false, $assoc_handle = null, $server = null)
+ {
+ if ($server === null) {
+ return new Auth_OpenID_ServerError($message,
+ "server must not be null");
+ }
+
+ if ($return_to &&
+ !Auth_OpenID_TrustRoot::_parse($return_to)) {
+ return new Auth_OpenID_MalformedReturnURL($message, $return_to);
+ }
+
+ $r = new Auth_OpenID_CheckIDRequest($identity, $return_to,
+ $trust_root, $immediate,
+ $assoc_handle, $server);
+
+ $r->namespace = $message->getOpenIDNamespace();
+ $r->message = $message;
+
+ if (!$r->trustRootValid()) {
+ return new Auth_OpenID_UntrustedReturnURL($message,
+ $return_to,
+ $trust_root);
+ } else {
+ return $r;
+ }
+ }
+
+ function Auth_OpenID_CheckIDRequest($identity, $return_to,
+ $trust_root = null, $immediate = false,
+ $assoc_handle = null, $server = null,
+ $claimed_id = null)
+ {
+ $this->namespace = Auth_OpenID_OPENID2_NS;
+ $this->assoc_handle = $assoc_handle;
+ $this->identity = $identity;
+ if ($claimed_id === null) {
+ $this->claimed_id = $identity;
+ } else {
+ $this->claimed_id = $claimed_id;
+ }
+ $this->return_to = $return_to;
+ $this->trust_root = $trust_root;
+ $this->server = $server;
+
+ if ($immediate) {
+ $this->immediate = true;
+ $this->mode = "checkid_immediate";
+ } else {
+ $this->immediate = false;
+ $this->mode = "checkid_setup";
+ }
+ }
+
+ function equals($other)
+ {
+ return (
+ (is_a($other, 'Auth_OpenID_CheckIDRequest')) &&
+ ($this->namespace == $other->namespace) &&
+ ($this->assoc_handle == $other->assoc_handle) &&
+ ($this->identity == $other->identity) &&
+ ($this->claimed_id == $other->claimed_id) &&
+ ($this->return_to == $other->return_to) &&
+ ($this->trust_root == $other->trust_root));
+ }
+
+ /*
+ * Does the relying party publish the return_to URL for this
+ * response under the realm? It is up to the provider to set a
+ * policy for what kinds of realms should be allowed. This
+ * return_to URL verification reduces vulnerability to data-theft
+ * attacks based on open proxies, corss-site-scripting, or open
+ * redirectors.
+ *
+ * This check should only be performed after making sure that the
+ * return_to URL matches the realm.
+ *
+ * @return true if the realm publishes a document with the
+ * return_to URL listed, false if not or if discovery fails
+ */
+ function returnToVerified()
+ {
+ $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
+ return call_user_func_array($this->verifyReturnTo,
+ array($this->trust_root, $this->return_to, $fetcher));
+ }
+
+ static function fromMessage($message, $server)
+ {
+ $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode');
+ $immediate = null;
+
+ if ($mode == "checkid_immediate") {
+ $immediate = true;
+ $mode = "checkid_immediate";
+ } else {
+ $immediate = false;
+ $mode = "checkid_setup";
+ }
+
+ $return_to = $message->getArg(Auth_OpenID_OPENID_NS,
+ 'return_to');
+
+ if (($message->isOpenID1()) &&
+ (!$return_to)) {
+ $fmt = "Missing required field 'return_to' from checkid request";
+ return new Auth_OpenID_ServerError($message, $fmt);
+ }
+
+ $identity = $message->getArg(Auth_OpenID_OPENID_NS,
+ 'identity');
+ $claimed_id = $message->getArg(Auth_OpenID_OPENID_NS, 'claimed_id');
+ if ($message->isOpenID1()) {
+ if ($identity === null) {
+ $s = "OpenID 1 message did not contain openid.identity";
+ return new Auth_OpenID_ServerError($message, $s);
+ }
+ } else {
+ if ($identity && !$claimed_id) {
+ $s = "OpenID 2.0 message contained openid.identity but not " .
+ "claimed_id";
+ return new Auth_OpenID_ServerError($message, $s);
+ } else if ($claimed_id && !$identity) {
+ $s = "OpenID 2.0 message contained openid.claimed_id " .
+ "but not identity";
+ return new Auth_OpenID_ServerError($message, $s);
+ }
+ }
+
+ // There's a case for making self.trust_root be a TrustRoot
+ // here. But if TrustRoot isn't currently part of the
+ // "public" API, I'm not sure it's worth doing.
+ if ($message->isOpenID1()) {
+ $trust_root_param = 'trust_root';
+ } else {
+ $trust_root_param = 'realm';
+ }
+ $trust_root = $message->getArg(Auth_OpenID_OPENID_NS,
+ $trust_root_param);
+ if (! $trust_root) {
+ $trust_root = $return_to;
+ }
+
+ if (! $message->isOpenID1() &&
+ ($return_to === null) &&
+ ($trust_root === null)) {
+ return new Auth_OpenID_ServerError($message,
+ "openid.realm required when openid.return_to absent");
+ }
+
+ $assoc_handle = $message->getArg(Auth_OpenID_OPENID_NS,
+ 'assoc_handle');
+
+ $obj = Auth_OpenID_CheckIDRequest::make($message,
+ $identity,
+ $return_to,
+ $trust_root,
+ $immediate,
+ $assoc_handle,
+ $server);
+
+ if (is_a($obj, 'Auth_OpenID_ServerError')) {
+ return $obj;
+ }
+
+ $obj->claimed_id = $claimed_id;
+
+ return $obj;
+ }
+
+ function idSelect()
+ {
+ // Is the identifier to be selected by the IDP?
+ // So IDPs don't have to import the constant
+ return $this->identity == Auth_OpenID_IDENTIFIER_SELECT;
+ }
+
+ function trustRootValid()
+ {
+ if (!$this->trust_root) {
+ return true;
+ }
+
+ $tr = Auth_OpenID_TrustRoot::_parse($this->trust_root);
+ if ($tr === false) {
+ return new Auth_OpenID_MalformedTrustRoot($this->message,
+ $this->trust_root);
+ }
+
+ if ($this->return_to !== null) {
+ return Auth_OpenID_TrustRoot::match($this->trust_root,
+ $this->return_to);
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Respond to this request. Return either an
+ * {@link Auth_OpenID_ServerResponse} or
+ * {@link Auth_OpenID_ServerError}.
+ *
+ * @param bool $allow Allow this user to claim this identity, and
+ * allow the consumer to have this information?
+ *
+ * @param string $server_url DEPRECATED. Passing $op_endpoint to
+ * the {@link Auth_OpenID_Server} constructor makes this optional.
+ *
+ * When an OpenID 1.x immediate mode request does not succeed, it
+ * gets back a URL where the request may be carried out in a
+ * not-so-immediate fashion. Pass my URL in here (the fully
+ * qualified address of this server's endpoint, i.e.
+ * http://example.com/server), and I will use it as a base for the
+ * URL for a new request.
+ *
+ * Optional for requests where {@link $immediate} is false or
+ * $allow is true.
+ *
+ * @param string $identity The OP-local identifier to answer with.
+ * Only for use when the relying party requested identifier
+ * selection.
+ *
+ * @param string $claimed_id The claimed identifier to answer
+ * with, for use with identifier selection in the case where the
+ * claimed identifier and the OP-local identifier differ,
+ * i.e. when the claimed_id uses delegation.
+ *
+ * If $identity is provided but this is not, $claimed_id will
+ * default to the value of $identity. When answering requests
+ * that did not ask for identifier selection, the response
+ * $claimed_id will default to that of the request.
+ *
+ * This parameter is new in OpenID 2.0.
+ *
+ * @return mixed
+ */
+ function answer($allow, $server_url = null, $identity = null,
+ $claimed_id = null)
+ {
+ if (!$this->return_to) {
+ return new Auth_OpenID_NoReturnToError();
+ }
+
+ if (!$server_url) {
+ if ((!$this->message->isOpenID1()) &&
+ (!$this->server->op_endpoint)) {
+ return new Auth_OpenID_ServerError(null,
+ "server should be constructed with op_endpoint to " .
+ "respond to OpenID 2.0 messages.");
+ }
+
+ $server_url = $this->server->op_endpoint;
+ }
+
+ if ($allow) {
+ $mode = 'id_res';
+ } else if ($this->message->isOpenID1()) {
+ if ($this->immediate) {
+ $mode = 'id_res';
+ } else {
+ $mode = 'cancel';
+ }
+ } else {
+ if ($this->immediate) {
+ $mode = 'setup_needed';
+ } else {
+ $mode = 'cancel';
+ }
+ }
+
+ if (!$this->trustRootValid()) {
+ return new Auth_OpenID_UntrustedReturnURL(null,
+ $this->return_to,
+ $this->trust_root);
+ }
+
+ $response = new Auth_OpenID_ServerResponse($this);
+
+ if ($claimed_id &&
+ ($this->message->isOpenID1())) {
+ return new Auth_OpenID_ServerError(null,
+ "claimed_id is new in OpenID 2.0 and not " .
+ "available for ".$this->namespace);
+ }
+
+ if ($identity && !$claimed_id) {
+ $claimed_id = $identity;
+ }
+
+ if ($allow) {
+
+ if ($this->identity == Auth_OpenID_IDENTIFIER_SELECT) {
+ if (!$identity) {
+ return new Auth_OpenID_ServerError(null,
+ "This request uses IdP-driven identifier selection. " .
+ "You must supply an identifier in the response.");
+ }
+
+ $response_identity = $identity;
+ $response_claimed_id = $claimed_id;
+
+ } else if ($this->identity) {
+ if ($identity &&
+ ($this->identity != $identity)) {
+ $fmt = "Request was for %s, cannot reply with identity %s";
+ return new Auth_OpenID_ServerError(null,
+ sprintf($fmt, $this->identity, $identity));
+ }
+
+ $response_identity = $this->identity;
+ $response_claimed_id = $this->claimed_id;
+ } else {
+ if ($identity) {
+ return new Auth_OpenID_ServerError(null,
+ "This request specified no identity and " .
+ "you supplied ".$identity);
+ }
+
+ $response_identity = null;
+ }
+
+ if (($this->message->isOpenID1()) &&
+ ($response_identity === null)) {
+ return new Auth_OpenID_ServerError(null,
+ "Request was an OpenID 1 request, so response must " .
+ "include an identifier.");
+ }
+
+ $response->fields->updateArgs(Auth_OpenID_OPENID_NS,
+ array('mode' => $mode,
+ 'return_to' => $this->return_to,
+ 'response_nonce' => Auth_OpenID_mkNonce()));
+
+ if (!$this->message->isOpenID1()) {
+ $response->fields->setArg(Auth_OpenID_OPENID_NS,
+ 'op_endpoint', $server_url);
+ }
+
+ if ($response_identity !== null) {
+ $response->fields->setArg(
+ Auth_OpenID_OPENID_NS,
+ 'identity',
+ $response_identity);
+ if ($this->message->isOpenID2()) {
+ $response->fields->setArg(
+ Auth_OpenID_OPENID_NS,
+ 'claimed_id',
+ $response_claimed_id);
+ }
+ }
+
+ } else {
+ $response->fields->setArg(Auth_OpenID_OPENID_NS,
+ 'mode', $mode);
+
+ if ($this->immediate) {
+ if (($this->message->isOpenID1()) &&
+ (!$server_url)) {
+ return new Auth_OpenID_ServerError(null,
+ 'setup_url is required for $allow=false \
+ in OpenID 1.x immediate mode.');
+ }
+
+ $setup_request = new Auth_OpenID_CheckIDRequest(
+ $this->identity,
+ $this->return_to,
+ $this->trust_root,
+ false,
+ $this->assoc_handle,
+ $this->server,
+ $this->claimed_id);
+ $setup_request->message = $this->message;
+
+ $setup_url = $setup_request->encodeToURL($server_url);
+
+ if ($setup_url === null) {
+ return new Auth_OpenID_NoReturnToError();
+ }
+
+ $response->fields->setArg(Auth_OpenID_OPENID_NS,
+ 'user_setup_url',
+ $setup_url);
+ }
+ }
+
+ return $response;
+ }
+
+ function encodeToURL($server_url)
+ {
+ if (!$this->return_to) {
+ return new Auth_OpenID_NoReturnToError();
+ }
+
+ // Imported from the alternate reality where these classes are
+ // used in both the client and server code, so Requests are
+ // Encodable too. That's right, code imported from alternate
+ // realities all for the love of you, id_res/user_setup_url.
+
+ $q = array('mode' => $this->mode,
+ 'identity' => $this->identity,
+ 'claimed_id' => $this->claimed_id,
+ 'return_to' => $this->return_to);
+
+ if ($this->trust_root) {
+ if ($this->message->isOpenID1()) {
+ $q['trust_root'] = $this->trust_root;
+ } else {
+ $q['realm'] = $this->trust_root;
+ }
+ }
+
+ if ($this->assoc_handle) {
+ $q['assoc_handle'] = $this->assoc_handle;
+ }
+
+ $response = new Auth_OpenID_Message(
+ $this->message->getOpenIDNamespace());
+ $response->updateArgs(Auth_OpenID_OPENID_NS, $q);
+ return $response->toURL($server_url);
+ }
+
+ function getCancelURL()
+ {
+ if (!$this->return_to) {
+ return new Auth_OpenID_NoReturnToError();
+ }
+
+ if ($this->immediate) {
+ return new Auth_OpenID_ServerError(null,
+ "Cancel is not an appropriate \
+ response to immediate mode \
+ requests.");
+ }
+
+ $response = new Auth_OpenID_Message(
+ $this->message->getOpenIDNamespace());
+ $response->setArg(Auth_OpenID_OPENID_NS, 'mode', 'cancel');
+ return $response->toURL($this->return_to);
+ }
+}
+
+/**
+ * This class encapsulates the response to an OpenID server request.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_ServerResponse {
+
+ function Auth_OpenID_ServerResponse($request)
+ {
+ $this->request = $request;
+ $this->fields = new Auth_OpenID_Message($this->request->namespace);
+ }
+
+ function whichEncoding()
+ {
+ global $_Auth_OpenID_Request_Modes;
+
+ if (in_array($this->request->mode, $_Auth_OpenID_Request_Modes)) {
+ if ($this->fields->isOpenID2() &&
+ (strlen($this->encodeToURL()) >
+ Auth_OpenID_OPENID1_URL_LIMIT)) {
+ return Auth_OpenID_ENCODE_HTML_FORM;
+ } else {
+ return Auth_OpenID_ENCODE_URL;
+ }
+ } else {
+ return Auth_OpenID_ENCODE_KVFORM;
+ }
+ }
+
+ /*
+ * Returns the form markup for this response.
+ *
+ * @return str
+ */
+ function toFormMarkup($form_tag_attrs=null)
+ {
+ return $this->fields->toFormMarkup($this->request->return_to,
+ $form_tag_attrs);
+ }
+
+ /*
+ * Returns an HTML document containing the form markup for this
+ * response that autosubmits with javascript.
+ */
+ function toHTML()
+ {
+ return Auth_OpenID::autoSubmitHTML($this->toFormMarkup());
+ }
+
+ /*
+ * Returns True if this response's encoding is ENCODE_HTML_FORM.
+ * Convenience method for server authors.
+ *
+ * @return bool
+ */
+ function renderAsForm()
+ {
+ return $this->whichEncoding() == Auth_OpenID_ENCODE_HTML_FORM;
+ }
+
+
+ function encodeToURL()
+ {
+ return $this->fields->toURL($this->request->return_to);
+ }
+
+ function addExtension($extension_response)
+ {
+ $extension_response->toMessage($this->fields);
+ }
+
+ function needsSigning()
+ {
+ return $this->fields->getArg(Auth_OpenID_OPENID_NS,
+ 'mode') == 'id_res';
+ }
+
+ function encodeToKVForm()
+ {
+ return $this->fields->toKVForm();
+ }
+}
+
+/**
+ * A web-capable response object which you can use to generate a
+ * user-agent response.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_WebResponse {
+ var $code = AUTH_OPENID_HTTP_OK;
+ var $body = "";
+
+ function Auth_OpenID_WebResponse($code = null, $headers = null,
+ $body = null)
+ {
+ if ($code) {
+ $this->code = $code;
+ }
+
+ if ($headers !== null) {
+ $this->headers = $headers;
+ } else {
+ $this->headers = array();
+ }
+
+ if ($body !== null) {
+ $this->body = $body;
+ }
+ }
+}
+
+/**
+ * Responsible for the signature of query data and the verification of
+ * OpenID signature values.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_Signatory {
+
+ // = 14 * 24 * 60 * 60; # 14 days, in seconds
+ var $SECRET_LIFETIME = 1209600;
+
+ // keys have a bogus server URL in them because the filestore
+ // really does expect that key to be a URL. This seems a little
+ // silly for the server store, since I expect there to be only one
+ // server URL.
+ var $normal_key = 'http://localhost/|normal';
+ var $dumb_key = 'http://localhost/|dumb';
+
+ /**
+ * Create a new signatory using a given store.
+ */
+ function Auth_OpenID_Signatory($store)
+ {
+ // assert store is not None
+ $this->store = $store;
+ }
+
+ /**
+ * Verify, using a given association handle, a signature with
+ * signed key-value pairs from an HTTP request.
+ */
+ function verify($assoc_handle, $message)
+ {
+ $assoc = $this->getAssociation($assoc_handle, true);
+ if (!$assoc) {
+ // oidutil.log("failed to get assoc with handle %r to verify sig %r"
+ // % (assoc_handle, sig))
+ return false;
+ }
+
+ return $assoc->checkMessageSignature($message);
+ }
+
+ /**
+ * Given a response, sign the fields in the response's 'signed'
+ * list, and insert the signature into the response.
+ */
+ function sign($response)
+ {
+ $signed_response = $response;
+ $assoc_handle = $response->request->assoc_handle;
+
+ if ($assoc_handle) {
+ // normal mode
+ $assoc = $this->getAssociation($assoc_handle, false, false);
+ if (!$assoc || ($assoc->getExpiresIn() <= 0)) {
+ // fall back to dumb mode
+ $signed_response->fields->setArg(Auth_OpenID_OPENID_NS,
+ 'invalidate_handle', $assoc_handle);
+ $assoc_type = ($assoc ? $assoc->assoc_type : 'HMAC-SHA1');
+
+ if ($assoc && ($assoc->getExpiresIn() <= 0)) {
+ $this->invalidate($assoc_handle, false);
+ }
+
+ $assoc = $this->createAssociation(true, $assoc_type);
+ }
+ } else {
+ // dumb mode.
+ $assoc = $this->createAssociation(true);
+ }
+
+ $signed_response->fields = $assoc->signMessage(
+ $signed_response->fields);
+ return $signed_response;
+ }
+
+ /**
+ * Make a new association.
+ */
+ function createAssociation($dumb = true, $assoc_type = 'HMAC-SHA1')
+ {
+ $secret = Auth_OpenID_CryptUtil::getBytes(
+ Auth_OpenID_getSecretSize($assoc_type));
+
+ $uniq = base64_encode(Auth_OpenID_CryptUtil::getBytes(4));
+ $handle = sprintf('{%s}{%x}{%s}', $assoc_type, intval(time()), $uniq);
+
+ $assoc = Auth_OpenID_Association::fromExpiresIn(
+ $this->SECRET_LIFETIME, $handle, $secret, $assoc_type);
+
+ if ($dumb) {
+ $key = $this->dumb_key;
+ } else {
+ $key = $this->normal_key;
+ }
+
+ $this->store->storeAssociation($key, $assoc);
+ return $assoc;
+ }
+
+ /**
+ * Given an association handle, get the association from the
+ * store, or return a ServerError or null if something goes wrong.
+ */
+ function getAssociation($assoc_handle, $dumb, $check_expiration=true)
+ {
+ if ($assoc_handle === null) {
+ return new Auth_OpenID_ServerError(null,
+ "assoc_handle must not be null");
+ }
+
+ if ($dumb) {
+ $key = $this->dumb_key;
+ } else {
+ $key = $this->normal_key;
+ }
+
+ $assoc = $this->store->getAssociation($key, $assoc_handle);
+
+ if (($assoc !== null) && ($assoc->getExpiresIn() <= 0)) {
+ if ($check_expiration) {
+ $this->store->removeAssociation($key, $assoc_handle);
+ $assoc = null;
+ }
+ }
+
+ return $assoc;
+ }
+
+ /**
+ * Invalidate a given association handle.
+ */
+ function invalidate($assoc_handle, $dumb)
+ {
+ if ($dumb) {
+ $key = $this->dumb_key;
+ } else {
+ $key = $this->normal_key;
+ }
+ $this->store->removeAssociation($key, $assoc_handle);
+ }
+}
+
+/**
+ * Encode an {@link Auth_OpenID_ServerResponse} to an
+ * {@link Auth_OpenID_WebResponse}.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_Encoder {
+
+ var $responseFactory = 'Auth_OpenID_WebResponse';
+
+ /**
+ * Encode an {@link Auth_OpenID_ServerResponse} and return an
+ * {@link Auth_OpenID_WebResponse}.
+ */
+ function encode($response)
+ {
+ $cls = $this->responseFactory;
+
+ $encode_as = $response->whichEncoding();
+ if ($encode_as == Auth_OpenID_ENCODE_KVFORM) {
+ $wr = new $cls(null, null, $response->encodeToKVForm());
+ if (is_a($response, 'Auth_OpenID_ServerError')) {
+ $wr->code = AUTH_OPENID_HTTP_ERROR;
+ }
+ } else if ($encode_as == Auth_OpenID_ENCODE_URL) {
+ $location = $response->encodeToURL();
+ $wr = new $cls(AUTH_OPENID_HTTP_REDIRECT,
+ array('location' => $location));
+ } else if ($encode_as == Auth_OpenID_ENCODE_HTML_FORM) {
+ $wr = new $cls(AUTH_OPENID_HTTP_OK, array(),
+ $response->toHTML());
+ } else {
+ return new Auth_OpenID_EncodingError($response);
+ }
+ /* Allow the response to carry a custom error code (ex: for Association errors) */
+ if(isset($response->code)) {
+ $wr->code = $response->code;
+ }
+ return $wr;
+ }
+}
+
+/**
+ * An encoder which also takes care of signing fields when required.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_SigningEncoder extends Auth_OpenID_Encoder {
+
+ function Auth_OpenID_SigningEncoder($signatory)
+ {
+ $this->signatory = $signatory;
+ }
+
+ /**
+ * Sign an {@link Auth_OpenID_ServerResponse} and return an
+ * {@link Auth_OpenID_WebResponse}.
+ */
+ function encode($response)
+ {
+ // the isinstance is a bit of a kludge... it means there isn't
+ // really an adapter to make the interfaces quite match.
+ if (!is_a($response, 'Auth_OpenID_ServerError') &&
+ $response->needsSigning()) {
+
+ if (!$this->signatory) {
+ return new Auth_OpenID_ServerError(null,
+ "Must have a store to sign request");
+ }
+
+ if ($response->fields->hasKey(Auth_OpenID_OPENID_NS, 'sig')) {
+ return new Auth_OpenID_AlreadySigned($response);
+ }
+ $response = $this->signatory->sign($response);
+ }
+
+ return parent::encode($response);
+ }
+}
+
+/**
+ * Decode an incoming query into an Auth_OpenID_Request.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_Decoder {
+
+ function Auth_OpenID_Decoder($server)
+ {
+ $this->server = $server;
+
+ $this->handlers = array(
+ 'checkid_setup' => 'Auth_OpenID_CheckIDRequest',
+ 'checkid_immediate' => 'Auth_OpenID_CheckIDRequest',
+ 'check_authentication' => 'Auth_OpenID_CheckAuthRequest',
+ 'associate' => 'Auth_OpenID_AssociateRequest'
+ );
+ }
+
+ /**
+ * Given an HTTP query in an array (key-value pairs), decode it
+ * into an Auth_OpenID_Request object.
+ */
+ function decode($query)
+ {
+ if (!$query) {
+ return null;
+ }
+
+ $message = Auth_OpenID_Message::fromPostArgs($query);
+
+ if ($message === null) {
+ /*
+ * It's useful to have a Message attached to a
+ * ProtocolError, so we override the bad ns value to build
+ * a Message out of it. Kinda kludgy, since it's made of
+ * lies, but the parts that aren't lies are more useful
+ * than a 'None'.
+ */
+ $old_ns = $query['openid.ns'];
+
+ $query['openid.ns'] = Auth_OpenID_OPENID2_NS;
+ $message = Auth_OpenID_Message::fromPostArgs($query);
+ return new Auth_OpenID_ServerError(
+ $message,
+ sprintf("Invalid OpenID namespace URI: %s", $old_ns));
+ }
+
+ $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode');
+ if (!$mode) {
+ return new Auth_OpenID_ServerError($message,
+ "No mode value in message");
+ }
+
+ if (Auth_OpenID::isFailure($mode)) {
+ return new Auth_OpenID_ServerError($message,
+ $mode->message);
+ }
+
+ $handlerCls = Auth_OpenID::arrayGet($this->handlers, $mode,
+ $this->defaultDecoder($message));
+
+ if (!is_a($handlerCls, 'Auth_OpenID_ServerError')) {
+ return call_user_func_array(array($handlerCls, 'fromMessage'),
+ array($message, $this->server));
+ } else {
+ return $handlerCls;
+ }
+ }
+
+ function defaultDecoder($message)
+ {
+ $mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode');
+
+ if (Auth_OpenID::isFailure($mode)) {
+ return new Auth_OpenID_ServerError($message,
+ $mode->message);
+ }
+
+ return new Auth_OpenID_ServerError($message,
+ sprintf("Unrecognized OpenID mode %s", $mode));
+ }
+}
+
+/**
+ * An error that indicates an encoding problem occurred.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_EncodingError {
+ function Auth_OpenID_EncodingError($response)
+ {
+ $this->response = $response;
+ }
+}
+
+/**
+ * An error that indicates that a response was already signed.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_AlreadySigned extends Auth_OpenID_EncodingError {
+ // This response is already signed.
+}
+
+/**
+ * An error that indicates that the given return_to is not under the
+ * given trust_root.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_UntrustedReturnURL extends Auth_OpenID_ServerError {
+ function Auth_OpenID_UntrustedReturnURL($message, $return_to,
+ $trust_root)
+ {
+ parent::Auth_OpenID_ServerError($message, "Untrusted return_to URL");
+ $this->return_to = $return_to;
+ $this->trust_root = $trust_root;
+ }
+
+ function toString()
+ {
+ return sprintf("return_to %s not under trust_root %s",
+ $this->return_to, $this->trust_root);
+ }
+}
+
+/**
+ * I handle requests for an OpenID server.
+ *
+ * Some types of requests (those which are not checkid requests) may
+ * be handed to my {@link handleRequest} method, and I will take care
+ * of it and return a response.
+ *
+ * For your convenience, I also provide an interface to {@link
+ * Auth_OpenID_Decoder::decode()} and {@link
+ * Auth_OpenID_SigningEncoder::encode()} through my methods {@link
+ * decodeRequest} and {@link encodeResponse}.
+ *
+ * All my state is encapsulated in an {@link Auth_OpenID_OpenIDStore}.
+ *
+ * Example:
+ *
+ * <pre> $oserver = new Auth_OpenID_Server(Auth_OpenID_FileStore($data_path),
+ * "http://example.com/op");
+ * $request = $oserver->decodeRequest();
+ * if (in_array($request->mode, array('checkid_immediate',
+ * 'checkid_setup'))) {
+ * if ($app->isAuthorized($request->identity, $request->trust_root)) {
+ * $response = $request->answer(true);
+ * } else if ($request->immediate) {
+ * $response = $request->answer(false);
+ * } else {
+ * $app->showDecidePage($request);
+ * return;
+ * }
+ * } else {
+ * $response = $oserver->handleRequest($request);
+ * }
+ *
+ * $webresponse = $oserver->encode($response);</pre>
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_Server {
+ function Auth_OpenID_Server($store, $op_endpoint=null)
+ {
+ $this->store = $store;
+ $this->signatory = new Auth_OpenID_Signatory($this->store);
+ $this->encoder = new Auth_OpenID_SigningEncoder($this->signatory);
+ $this->decoder = new Auth_OpenID_Decoder($this);
+ $this->op_endpoint = $op_endpoint;
+ $this->negotiator = Auth_OpenID_getDefaultNegotiator();
+ }
+
+ /**
+ * Handle a request. Given an {@link Auth_OpenID_Request} object,
+ * call the appropriate {@link Auth_OpenID_Server} method to
+ * process the request and generate a response.
+ *
+ * @param Auth_OpenID_Request $request An {@link Auth_OpenID_Request}
+ * returned by {@link Auth_OpenID_Server::decodeRequest()}.
+ *
+ * @return Auth_OpenID_ServerResponse $response A response object
+ * capable of generating a user-agent reply.
+ */
+ function handleRequest($request)
+ {
+ if (method_exists($this, "openid_" . $request->mode)) {
+ $handler = array($this, "openid_" . $request->mode);
+ return call_user_func($handler, $request);
+ }
+ return null;
+ }
+
+ /**
+ * The callback for 'check_authentication' messages.
+ */
+ function openid_check_authentication($request)
+ {
+ return $request->answer($this->signatory);
+ }
+
+ /**
+ * The callback for 'associate' messages.
+ */
+ function openid_associate($request)
+ {
+ $assoc_type = $request->assoc_type;
+ $session_type = $request->session->session_type;
+ if ($this->negotiator->isAllowed($assoc_type, $session_type)) {
+ $assoc = $this->signatory->createAssociation(false,
+ $assoc_type);
+ return $request->answer($assoc);
+ } else {
+ $message = sprintf('Association type %s is not supported with '.
+ 'session type %s', $assoc_type, $session_type);
+ list($preferred_assoc_type, $preferred_session_type) =
+ $this->negotiator->getAllowedType();
+ return $request->answerUnsupported($message,
+ $preferred_assoc_type,
+ $preferred_session_type);
+ }
+ }
+
+ /**
+ * Encodes as response in the appropriate format suitable for
+ * sending to the user agent.
+ */
+ function encodeResponse($response)
+ {
+ return $this->encoder->encode($response);
+ }
+
+ /**
+ * Decodes a query args array into the appropriate
+ * {@link Auth_OpenID_Request} object.
+ */
+ function decodeRequest($query=null)
+ {
+ if ($query === null) {
+ $query = Auth_OpenID::getQuery();
+ }
+
+ return $this->decoder->decode($query);
+ }
+}
+
+
+
--- /dev/null
+++ b/lib/openid-php/Auth/OpenID/TrustRoot.php
@@ -1,1 +1,462 @@
-
+<?php
+/**
+ * Functions for dealing with OpenID trust roots
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: See the COPYING file included in this distribution.
+ *
+ * @package OpenID
+ * @author JanRain, Inc. <openid@janrain.com>
+ * @copyright 2005-2008 Janrain, Inc.
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
+ */
+
+require_once 'Auth/OpenID/Discover.php';
+
+/**
+ * A regular expression that matches a domain ending in a top-level domains.
+ * Used in checking trust roots for sanity.
+ *
+ * @access private
+ */
+define('Auth_OpenID___TLDs',
+ '/\.(ac|ad|ae|aero|af|ag|ai|al|am|an|ao|aq|ar|arpa|as|asia' .
+ '|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|biz|bj|bm|bn|bo|br' .
+ '|bs|bt|bv|bw|by|bz|ca|cat|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co' .
+ '|com|coop|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg' .
+ '|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl' .
+ '|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie' .
+ '|il|im|in|info|int|io|iq|ir|is|it|je|jm|jo|jobs|jp|ke|kg|kh' .
+ '|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly' .
+ '|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mo|mobi|mp|mq|mr|ms|mt' .
+ '|mu|museum|mv|mw|mx|my|mz|na|name|nc|ne|net|nf|ng|ni|nl|no' .
+ '|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|pro|ps|pt' .
+ '|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl' .
+ '|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm' .
+ '|tn|to|tp|tr|travel|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve' .
+ '|vg|vi|vn|vu|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g' .
+ '|xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--g6w251d' .
+ '|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--jxalpdlp' .
+ '|xn--kgbechtv|xn--zckzah|ye|yt|yu|za|zm|zw)\.?$/');
+
+define('Auth_OpenID___HostSegmentRe',
+ "/^(?:[-a-zA-Z0-9!$&'\\(\\)\\*+,;=._~]|%[a-zA-Z0-9]{2})*$/");
+
+/**
+ * A wrapper for trust-root related functions
+ */
+class Auth_OpenID_TrustRoot {
+ /*
+ * Return a discovery URL for this realm.
+ *
+ * Return null if the realm could not be parsed or was not valid.
+ *
+ * @param return_to The relying party return URL of the OpenID
+ * authentication request
+ *
+ * @return The URL upon which relying party discovery should be
+ * run in order to verify the return_to URL
+ */
+ static function buildDiscoveryURL($realm)
+ {
+ $parsed = Auth_OpenID_TrustRoot::_parse($realm);
+
+ if ($parsed === false) {
+ return false;
+ }
+
+ if ($parsed['wildcard']) {
+ // Use "www." in place of the star
+ if ($parsed['host'][0] != '.') {
+ return false;
+ }
+
+ $www_domain = 'www' . $parsed['host'];
+
+ return sprintf('%s://%s%s', $parsed['scheme'],
+ $www_domain, $parsed['path']);
+ } else {
+ return $parsed['unparsed'];
+ }
+ }
+
+ /**
+ * Parse a URL into its trust_root parts.
+ *
+ * @static
+ *
+ * @access private
+ *
+ * @param string $trust_root The url to parse
+ *
+ * @return mixed $parsed Either an associative array of trust root
+ * parts or false if parsing failed.
+ */
+ static function _parse($trust_root)
+ {
+ $trust_root = Auth_OpenID_urinorm($trust_root);
+ if ($trust_root === null) {
+ return false;
+ }
+
+ if (preg_match("/:\/\/[^:]+(:\d+){2,}(\/|$)/", $trust_root)) {
+ return false;
+ }
+
+ $parts = @parse_url($trust_root);
+ if ($parts === false) {
+ return false;
+ }
+
+ $required_parts = array('scheme', 'host');
+ $forbidden_parts = array('user', 'pass', 'fragment');
+ $keys = array_keys($parts);
+ if (array_intersect($keys, $required_parts) != $required_parts) {
+ return false;
+ }
+
+ if (array_intersect($keys, $forbidden_parts) != array()) {
+ return false;
+ }
+
+ if (!preg_match(Auth_OpenID___HostSegmentRe, $parts['host'])) {
+ return false;
+ }
+
+ $scheme = strtolower($parts['scheme']);
+ $allowed_schemes = array('http', 'https');
+ if (!in_array($scheme, $allowed_schemes)) {
+ return false;
+ }
+ $parts['scheme'] = $scheme;
+
+ $host = strtolower($parts['host']);
+ $hostparts = explode('*', $host);
+ switch (count($hostparts)) {
+ case 1:
+ $parts['wildcard'] = false;
+ break;
+ case 2:
+ if ($hostparts[0] ||
+ ($hostparts[1] && substr($hostparts[1], 0, 1) != '.')) {
+ return false;
+ }
+ $host = $hostparts[1];
+ $parts['wildcard'] = true;
+ break;
+ default:
+ return false;
+ }
+ if (strpos($host, ':') !== false) {
+ return false;
+ }
+
+ $parts['host'] = $host;
+
+ if (isset($parts['path'])) {
+ $path = strtolower($parts['path']);
+ if (substr($path, 0, 1) != '/') {
+ return false;
+ }
+ } else {
+ $path = '/';
+ }
+
+ $parts['path'] = $path;
+ if (!isset($parts['port'])) {
+ $parts['port'] = false;
+ }
+
+
+ $parts['unparsed'] = $trust_root;
+
+ return $parts;
+ }
+
+ /**
+ * Is this trust root sane?
+ *
+ * A trust root is sane if it is syntactically valid and it has a
+ * reasonable domain name. Specifically, the domain name must be
+ * more than one level below a standard TLD or more than two
+ * levels below a two-letter tld.
+ *
+ * For example, '*.com' is not a sane trust root, but '*.foo.com'
+ * is. '*.co.uk' is not sane, but '*.bbc.co.uk' is.
+ *
+ * This check is not always correct, but it attempts to err on the
+ * side of marking sane trust roots insane instead of marking
+ * insane trust roots sane. For example, 'kink.fm' is marked as
+ * insane even though it "should" (for some meaning of should) be
+ * marked sane.
+ *
+ * This function should be used when creating OpenID servers to
+ * alert the users of the server when a consumer attempts to get
+ * the user to accept a suspicious trust root.
+ *
+ * @static
+ * @param string $trust_root The trust root to check
+ * @return bool $sanity Whether the trust root looks OK
+ */
+ static function isSane($trust_root)
+ {
+ $parts = Auth_OpenID_TrustRoot::_parse($trust_root);
+ if ($parts === false) {
+ return false;
+ }
+
+ // Localhost is a special case
+ if ($parts['host'] == 'localhost') {
+ return true;
+ }
+
+ $host_parts = explode('.', $parts['host']);
+ if ($parts['wildcard']) {
+ // Remove the empty string from the beginning of the array
+ array_shift($host_parts);
+ }
+
+ if ($host_parts && !$host_parts[count($host_parts) - 1]) {
+ array_pop($host_parts);
+ }
+
+ if (!$host_parts) {
+ return false;
+ }
+
+ // Don't allow adjacent dots
+ if (in_array('', $host_parts, true)) {
+ return false;
+ }
+
+ // Get the top-level domain of the host. If it is not a valid TLD,
+ // it's not sane.
+ preg_match(Auth_OpenID___TLDs, $parts['host'], $matches);
+ if (!$matches) {
+ return false;
+ }
+ $tld = $matches[1];
+
+ if (count($host_parts) == 1) {
+ return false;
+ }
+
+ if ($parts['wildcard']) {
+ // It's a 2-letter tld with a short second to last segment
+ // so there needs to be more than two segments specified
+ // (e.g. *.co.uk is insane)
+ $second_level = $host_parts[count($host_parts) - 2];
+ if (strlen($tld) == 2 && strlen($second_level) <= 3) {
+ return count($host_parts) > 2;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Does this URL match the given trust root?
+ *
+ * Return whether the URL falls under the given trust root. This
+ * does not check whether the trust root is sane. If the URL or
+ * trust root do not parse, this function will return false.
+ *
+ * @param string $trust_root The trust root to match against
+ *
+ * @param string $url The URL to check
+ *
+ * @return bool $matches Whether the URL matches against the
+ * trust root
+ */
+ static function match($trust_root, $url)
+ {
+ $trust_root_parsed = Auth_OpenID_TrustRoot::_parse($trust_root);
+ $url_parsed = Auth_OpenID_TrustRoot::_parse($url);
+ if (!$trust_root_parsed || !$url_parsed) {
+ return false;
+ }
+
+ // Check hosts matching
+ if ($url_parsed['wildcard']) {
+ return false;
+ }
+ if ($trust_root_parsed['wildcard']) {
+ $host_tail = $trust_root_parsed['host'];
+ $host = $url_parsed['host'];
+ if ($host_tail &&
+ substr($host, -(strlen($host_tail))) != $host_tail &&
+ substr($host_tail, 1) != $host) {
+ return false;
+ }
+ } else {
+ if ($trust_root_parsed['host'] != $url_parsed['host']) {
+ return false;
+ }
+ }
+
+ // Check path and query matching
+ $base_path = $trust_root_parsed['path'];
+ $path = $url_parsed['path'];
+ if (!isset($trust_root_parsed['query'])) {
+ if ($base_path != $path) {
+ if (substr($path, 0, strlen($base_path)) != $base_path) {
+ return false;
+ }
+ if (substr($base_path, strlen($base_path) - 1, 1) != '/' &&
+ substr($path, strlen($base_path), 1) != '/') {
+ return false;
+ }
+ }
+ } else {
+ $base_query = $trust_root_parsed['query'];
+ $query = @$url_parsed['query'];
+ $qplus = substr($query, 0, strlen($base_query) + 1);
+ $bqplus = $base_query . '&';
+ if ($base_path != $path ||
+ ($base_query != $query && $qplus != $bqplus)) {
+ return false;
+ }
+ }
+
+ // The port and scheme need to match exactly
+ return ($trust_root_parsed['scheme'] == $url_parsed['scheme'] &&
+ $url_parsed['port'] === $trust_root_parsed['port']);
+ }
+}
+
+/*
+ * If the endpoint is a relying party OpenID return_to endpoint,
+ * return the endpoint URL. Otherwise, return None.
+ *
+ * This function is intended to be used as a filter for the Yadis
+ * filtering interface.
+ *
+ * @see: C{L{openid.yadis.services}}
+ * @see: C{L{openid.yadis.filters}}
+ *
+ * @param endpoint: An XRDS BasicServiceEndpoint, as returned by
+ * performing Yadis dicovery.
+ *
+ * @returns: The endpoint URL or None if the endpoint is not a
+ * relying party endpoint.
+ */
+function filter_extractReturnURL($endpoint)
+{
+ if ($endpoint->matchTypes(array(Auth_OpenID_RP_RETURN_TO_URL_TYPE))) {
+ return $endpoint;
+ } else {
+ return null;
+ }
+}
+
+function &Auth_OpenID_extractReturnURL(&$endpoint_list)
+{
+ $result = array();
+
+ foreach ($endpoint_list as $endpoint) {
+ if (filter_extractReturnURL($endpoint)) {
+ $result[] = $endpoint;
+ }
+ }
+
+ return $result;
+}
+
+/*
+ * Is the return_to URL under one of the supplied allowed return_to
+ * URLs?
+ */
+function Auth_OpenID_returnToMatches($allowed_return_to_urls, $return_to)
+{
+ foreach ($allowed_return_to_urls as $allowed_return_to) {
+ // A return_to pattern works the same as a realm, except that
+ // it's not allowed to use a wildcard. We'll model this by
+ // parsing it as a realm, and not trying to match it if it has
+ // a wildcard.
+
+ $return_realm = Auth_OpenID_TrustRoot::_parse($allowed_return_to);
+ if (// Parses as a trust root
+ ($return_realm !== false) &&
+ // Does not have a wildcard
+ (!$return_realm['wildcard']) &&
+ // Matches the return_to that we passed in with it
+ (Auth_OpenID_TrustRoot::match($allowed_return_to, $return_to))) {
+ return true;
+ }
+ }
+
+ // No URL in the list matched
+ return false;
+}
+
+/*
+ * Given a relying party discovery URL return a list of return_to
+ * URLs.
+ */
+function Auth_OpenID_getAllowedReturnURLs($relying_party_url, $fetcher,
+ $discover_function=null)
+{
+ if ($discover_function === null) {
+ $discover_function = array('Auth_Yadis_Yadis', 'discover');
+ }
+
+ $xrds_parse_cb = array('Auth_OpenID_ServiceEndpoint', 'consumerFromXRDS');
+
+ list($rp_url_after_redirects, $endpoints) =
+ Auth_Yadis_getServiceEndpoints($relying_party_url, $xrds_parse_cb,
+ $discover_function, $fetcher);
+
+ if ($rp_url_after_redirects != $relying_party_url) {
+ // Verification caused a redirect
+ return false;
+ }
+
+ call_user_func_array($discover_function,
+ array($relying_party_url, $fetcher));
+
+ $return_to_urls = array();
+ $matching_endpoints = Auth_OpenID_extractReturnURL($endpoints);
+
+ foreach ($matching_endpoints as $e) {
+ $return_to_urls[] = $e->server_url;
+ }
+
+ return $return_to_urls;
+}
+
+/*
+ * Verify that a return_to URL is valid for the given realm.
+ *
+ * This function builds a discovery URL, performs Yadis discovery on
+ * it, makes sure that the URL does not redirect, parses out the
+ * return_to URLs, and finally checks to see if the current return_to
+ * URL matches the return_to.
+ *
+ * @return true if the return_to URL is valid for the realm
+ */
+function Auth_OpenID_verifyReturnTo($realm_str, $return_to, $fetcher,
+ $_vrfy='Auth_OpenID_getAllowedReturnURLs')
+{
+ $disco_url = Auth_OpenID_TrustRoot::buildDiscoveryURL($realm_str);
+
+ if ($disco_url === false) {
+ return false;
+ }
+
+ $allowable_urls = call_user_func_array($_vrfy,
+ array($disco_url, $fetcher));
+
+ // The realm_str could not be parsed.
+ if ($allowable_urls === false) {
+ return false;
+ }
+
+ if (Auth_OpenID_returnToMatches($allowable_urls, $return_to)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
--- /dev/null
+++ b/lib/openid-php/Auth/Yadis/Manager.php
@@ -1,1 +1,522 @@
-
+<?php
+
+/**
+ * Yadis service manager to be used during yadis-driven authentication
+ * attempts.
+ *
+ * @package OpenID
+ */
+
+/**
+ * The base session class used by the Auth_Yadis_Manager. This
+ * class wraps the default PHP session machinery and should be
+ * subclassed if your application doesn't use PHP sessioning.
+ *
+ * @package OpenID
+ */
+class Auth_Yadis_PHPSession {
+ /**
+ * Set a session key/value pair.
+ *
+ * @param string $name The name of the session key to add.
+ * @param string $value The value to add to the session.
+ */
+ function set($name, $value)
+ {
+ $_SESSION[$name] = $value;
+ }
+
+ /**
+ * Get a key's value from the session.
+ *
+ * @param string $name The name of the key to retrieve.
+ * @param string $default The optional value to return if the key
+ * is not found in the session.
+ * @return string $result The key's value in the session or
+ * $default if it isn't found.
+ */
+ function get($name, $default=null)
+ {
+ if (array_key_exists($name, $_SESSION)) {
+ return $_SESSION[$name];
+ } else {
+ return $default;
+ }
+ }
+
+ /**
+ * Remove a key/value pair from the session.
+ *
+ * @param string $name The name of the key to remove.
+ */
+ function del($name)
+ {
+ unset($_SESSION[$name]);
+ }
+
+ /**
+ * Return the contents of the session in array form.
+ */
+ function contents()
+ {
+ return $_SESSION;
+ }
+}
+
+/**
+ * A session helper class designed to translate between arrays and
+ * objects. Note that the class used must have a constructor that
+ * takes no parameters. This is not a general solution, but it works
+ * for dumb objects that just need to have attributes set. The idea
+ * is that you'll subclass this and override $this->check($data) ->
+ * bool to implement your own session data validation.
+ *
+ * @package OpenID
+ */
+class Auth_Yadis_SessionLoader {
+ /**
+ * Override this.
+ *
+ * @access private
+ */
+ function check($data)
+ {
+ return true;
+ }
+
+ /**
+ * Given a session data value (an array), this creates an object
+ * (returned by $this->newObject()) whose attributes and values
+ * are those in $data. Returns null if $data lacks keys found in
+ * $this->requiredKeys(). Returns null if $this->check($data)
+ * evaluates to false. Returns null if $this->newObject()
+ * evaluates to false.
+ *
+ * @access private
+ */
+ function fromSession($data)
+ {
+ if (!$data) {
+ return null;
+ }
+
+ $required = $this->requiredKeys();
+
+ foreach ($required as $k) {
+ if (!array_key_exists($k, $data)) {
+ return null;
+ }
+ }
+
+ if (!$this->check($data)) {
+ return null;
+ }
+
+ $data = array_merge($data, $this->prepareForLoad($data));
+ $obj = $this->newObject($data);
+
+ if (!$obj) {
+ return null;
+ }
+
+ foreach ($required as $k) {
+ $obj->$k = $data[$k];
+ }
+
+ return $obj;
+ }
+
+ /**
+ * Prepares the data array by making any necessary changes.
+ * Returns an array whose keys and values will be used to update
+ * the original data array before calling $this->newObject($data).
+ *
+ * @access private
+ */
+ function prepareForLoad($data)
+ {
+ return array();
+ }
+
+ /**
+ * Returns a new instance of this loader's class, using the
+ * session data to construct it if necessary. The object need
+ * only be created; $this->fromSession() will take care of setting
+ * the object's attributes.
+ *
+ * @access private
+ */
+ function newObject($data)
+ {
+ return null;
+ }
+
+ /**
+ * Returns an array of keys and values built from the attributes
+ * of $obj. If $this->prepareForSave($obj) returns an array, its keys
+ * and values are used to update the $data array of attributes
+ * from $obj.
+ *
+ * @access private
+ */
+ function toSession($obj)
+ {
+ $data = array();
+ foreach ($obj as $k => $v) {
+ $data[$k] = $v;
+ }
+
+ $extra = $this->prepareForSave($obj);
+
+ if ($extra && is_array($extra)) {
+ foreach ($extra as $k => $v) {
+ $data[$k] = $v;
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Override this.
+ *
+ * @access private
+ */
+ function prepareForSave($obj)
+ {
+ return array();
+ }
+}
+
+/**
+ * A concrete loader implementation for Auth_OpenID_ServiceEndpoints.
+ *
+ * @package OpenID
+ */
+class Auth_OpenID_ServiceEndpointLoader extends Auth_Yadis_SessionLoader {
+ function newObject($data)
+ {
+ return new Auth_OpenID_ServiceEndpoint();
+ }
+
+ function requiredKeys()
+ {
+ $obj = new Auth_OpenID_ServiceEndpoint();
+ $data = array();
+ foreach ($obj as $k => $v) {
+ $data[] = $k;
+ }
+ return $data;
+ }
+
+ function check($data)
+ {
+ return is_array($data['type_uris']);
+ }
+}
+
+/**
+ * A concrete loader implementation for Auth_Yadis_Managers.
+ *
+ * @package OpenID
+ */
+class Auth_Yadis_ManagerLoader extends Auth_Yadis_SessionLoader {
+ function requiredKeys()
+ {
+ return array('starting_url',
+ 'yadis_url',
+ 'services',
+ 'session_key',
+ '_current',
+ 'stale');
+ }
+
+ function newObject($data)
+ {
+ return new Auth_Yadis_Manager($data['starting_url'],
+ $data['yadis_url'],
+ $data['services'],
+ $data['session_key']);
+ }
+
+ function check($data)
+ {
+ return is_array($data['services']);
+ }
+
+ function prepareForLoad($data)
+ {
+ $loader = new Auth_OpenID_ServiceEndpointLoader();
+ $services = array();
+ foreach ($data['services'] as $s) {
+ $services[] = $loader->fromSession($s);
+ }
+ return array('services' => $services);
+ }
+
+ function prepareForSave($obj)
+ {
+ $loader = new Auth_OpenID_ServiceEndpointLoader();
+ $services = array();
+ foreach ($obj->services as $s) {
+ $services[] = $loader->toSession($s);
+ }
+ return array('services' => $services);
+ }
+}
+
+/**
+ * The Yadis service manager which stores state in a session and
+ * iterates over <Service> elements in a Yadis XRDS document and lets
+ * a caller attempt to use each one. This is used by the Yadis
+ * library internally.
+ *
+ * @package OpenID
+ */
+class Auth_Yadis_Manager {
+
+ /**
+ * Intialize a new yadis service manager.
+ *
+ * @access private
+ */
+ function Auth_Yadis_Manager($starting_url, $yadis_url,
+ $services, $session_key)
+ {
+ // The URL that was used to initiate the Yadis protocol
+ $this->starting_url = $starting_url;
+
+ // The URL after following redirects (the identifier)
+ $this->yadis_url = $yadis_url;
+
+ // List of service elements
+ $this->services = $services;
+
+ $this->session_key = $session_key;
+
+ // Reference to the current service object
+ $this->_current = null;
+
+ // Stale flag for cleanup if PHP lib has trouble.
+ $this->stale = false;
+ }
+
+ /**
+ * @access private
+ */
+ function length()
+ {
+ // How many untried services remain?
+ return count($this->services);
+ }
+
+ /**
+ * Return the next service
+ *
+ * $this->current() will continue to return that service until the
+ * next call to this method.
+ */
+ function nextService()
+ {
+
+ if ($this->services) {
+ $this->_current = array_shift($this->services);
+ } else {
+ $this->_current = null;
+ }
+
+ return $this->_current;
+ }
+
+ /**
+ * @access private
+ */
+ function current()
+ {
+ // Return the current service.
+ // Returns None if there are no services left.
+ return $this->_current;
+ }
+
+ /**
+ * @access private
+ */
+ function forURL($url)
+ {
+ return in_array($url, array($this->starting_url, $this->yadis_url));
+ }
+
+ /**
+ * @access private
+ */
+ function started()
+ {
+ // Has the first service been returned?
+ return $this->_current !== null;
+ }
+}
+
+/**
+ * State management for discovery.
+ *
+ * High-level usage pattern is to call .getNextService(discover) in
+ * order to find the next available service for this user for this
+ * session. Once a request completes, call .cleanup() to clean up the
+ * session state.
+ *
+ * @package OpenID
+ */
+class Auth_Yadis_Discovery {
+
+ /**
+ * @access private
+ */
+ var $DEFAULT_SUFFIX = 'auth';
+
+ /**
+ * @access private
+ */
+ var $PREFIX = '_yadis_services_';
+
+ /**
+ * Initialize a discovery object.
+ *
+ * @param Auth_Yadis_PHPSession $session An object which
+ * implements the Auth_Yadis_PHPSession API.
+ * @param string $url The URL on which to attempt discovery.
+ * @param string $session_key_suffix The optional session key
+ * suffix override.
+ */
+ function Auth_Yadis_Discovery($session, $url,
+ $session_key_suffix = null)
+ {
+ /// Initialize a discovery object
+ $this->session = $session;
+ $this->url = $url;
+ if ($session_key_suffix === null) {
+ $session_key_suffix = $this->DEFAULT_SUFFIX;
+ }
+
+ $this->session_key_suffix = $session_key_suffix;
+ $this->session_key = $this->PREFIX . $this->session_key_suffix;
+ }
+
+ /**
+ * Return the next authentication service for the pair of
+ * user_input and session. This function handles fallback.
+ */
+ function getNextService($discover_cb, $fetcher)
+ {
+ $manager = $this->getManager();
+ if (!$manager || (!$manager->services)) {
+ $this->destroyManager();
+
+ list($yadis_url, $services) = call_user_func($discover_cb,
+ $this->url,
+ $fetcher);
+
+ $manager = $this->createManager($services, $yadis_url);
+ }
+
+ if ($manager) {
+ $loader = new Auth_Yadis_ManagerLoader();
+ $service = $manager->nextService();
+ $this->session->set($this->session_key,
+ serialize($loader->toSession($manager)));
+ } else {
+ $service = null;
+ }
+
+ return $service;
+ }
+
+ /**
+ * Clean up Yadis-related services in the session and return the
+ * most-recently-attempted service from the manager, if one
+ * exists.
+ *
+ * @param $force True if the manager should be deleted regardless
+ * of whether it's a manager for $this->url.
+ */
+ function cleanup($force=false)
+ {
+ $manager = $this->getManager($force);
+ if ($manager) {
+ $service = $manager->current();
+ $this->destroyManager($force);
+ } else {
+ $service = null;
+ }
+
+ return $service;
+ }
+
+ /**
+ * @access private
+ */
+ function getSessionKey()
+ {
+ // Get the session key for this starting URL and suffix
+ return $this->PREFIX . $this->session_key_suffix;
+ }
+
+ /**
+ * @access private
+ *
+ * @param $force True if the manager should be returned regardless
+ * of whether it's a manager for $this->url.
+ */
+ function getManager($force=false)
+ {
+ // Extract the YadisServiceManager for this object's URL and
+ // suffix from the session.
+
+ $manager_str = $this->session->get($this->getSessionKey());
+ $manager = null;
+
+ if ($manager_str !== null) {
+ $loader = new Auth_Yadis_ManagerLoader();
+ $manager = $loader->fromSession(unserialize($manager_str));
+ }
+
+ if ($manager && ($manager->forURL($this->url) || $force)) {
+ return $manager;
+ }
+ }
+
+ /**
+ * @access private
+ */
+ function createManager($services, $yadis_url = null)
+ {
+ $key = $this->getSessionKey();
+ if ($this->getManager()) {
+ return $this->getManager();
+ }
+
+ if ($services) {
+ $loader = new Auth_Yadis_ManagerLoader();
+ $manager = new Auth_Yadis_Manager($this->url, $yadis_url,
+ $services, $key);
+ $this->session->set($this->session_key,
+ serialize($loader->toSession($manager)));
+ return $manager;
+ }
+ }
+
+ /**
+ * @access private
+ *
+ * @param $force True if the manager should be deleted regardless
+ * of whether it's a manager for $this->url.
+ */
+ function destroyManager($force=false)
+ {
+ if ($this->getManager($force) !== null) {
+ $key = $this->getSessionKey();
+ $this->session->del($key);
+ }
+ }
+}
+
+
--- /dev/null
+++ b/lib/openid-php/Auth/Yadis/XRDS.php
@@ -1,1 +1,479 @@
-
+<?php
+
+/**
+ * This module contains the XRDS parsing code.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: See the COPYING file included in this distribution.
+ *
+ * @package OpenID
+ * @author JanRain, Inc. <openid@janrain.com>
+ * @copyright 2005-2008 Janrain, Inc.
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache
+ */
+
+/**
+ * Require the XPath implementation.
+ */
+require_once 'Auth/Yadis/XML.php';
+
+/**
+ * This match mode means a given service must match ALL filters passed
+ * to the Auth_Yadis_XRDS::services() call.
+ */
+define('SERVICES_YADIS_MATCH_ALL', 101);
+
+/**
+ * This match mode means a given service must match ANY filters (at
+ * least one) passed to the Auth_Yadis_XRDS::services() call.
+ */
+define('SERVICES_YADIS_MATCH_ANY', 102);
+
+/**
+ * The priority value used for service elements with no priority
+ * specified.
+ */
+define('SERVICES_YADIS_MAX_PRIORITY', pow(2, 30));
+
+/**
+ * XRD XML namespace
+ */
+define('Auth_Yadis_XMLNS_XRD_2_0', 'xri://$xrd*($v*2.0)');
+
+/**
+ * XRDS XML namespace
+ */
+define('Auth_Yadis_XMLNS_XRDS', 'xri://$xrds');
+
+function Auth_Yadis_getNSMap()
+{
+ return array('xrds' => Auth_Yadis_XMLNS_XRDS,
+ 'xrd' => Auth_Yadis_XMLNS_XRD_2_0);
+}
+
+/**
+ * @access private
+ */
+function Auth_Yadis_array_scramble($arr)
+{
+ $result = array();
+
+ while (count($arr)) {
+ $index = array_rand($arr, 1);
+ $result[] = $arr[$index];
+ unset($arr[$index]);
+ }
+
+ return $result;
+}
+
+/**
+ * This class represents a <Service> element in an XRDS document.
+ * Objects of this type are returned by
+ * Auth_Yadis_XRDS::services() and
+ * Auth_Yadis_Yadis::services(). Each object corresponds directly
+ * to a <Service> element in the XRDS and supplies a
+ * getElements($name) method which you should use to inspect the
+ * element's contents. See {@link Auth_Yadis_Yadis} for more
+ * information on the role this class plays in Yadis discovery.
+ *
+ * @package OpenID
+ */
+class Auth_Yadis_Service {
+
+ /**
+ * Creates an empty service object.
+ */
+ function Auth_Yadis_Service()
+ {
+ $this->element = null;
+ $this->parser = null;
+ }
+
+ /**
+ * Return the URIs in the "Type" elements, if any, of this Service
+ * element.
+ *
+ * @return array $type_uris An array of Type URI strings.
+ */
+ function getTypes()
+ {
+ $t = array();
+ foreach ($this->getElements('xrd:Type') as $elem) {
+ $c = $this->parser->content($elem);
+ if ($c) {
+ $t[] = $c;
+ }
+ }
+ return $t;
+ }
+
+ function matchTypes($type_uris)
+ {
+ $result = array();
+
+ foreach ($this->getTypes() as $typ) {
+ if (in_array($typ, $type_uris)) {
+ $result[] = $typ;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Return the URIs in the "URI" elements, if any, of this Service
+ * element. The URIs are returned sorted in priority order.
+ *
+ * @return array $uris An array of URI strings.
+ */
+ function getURIs()
+ {
+ $uris = array();
+ $last = array();
+
+ foreach ($this->getElements('xrd:URI') as $elem) {
+ $uri_string = $this->parser->content($elem);
+ $attrs = $this->parser->attributes($elem);
+ if ($attrs &&
+ array_key_exists('priority', $attrs)) {
+ $priority = intval($attrs['priority']);
+ if (!array_key_exists($priority, $uris)) {
+ $uris[$priority] = array();
+ }
+
+ $uris[$priority][] = $uri_string;
+ } else {
+ $last[] = $uri_string;
+ }
+ }
+
+ $keys = array_keys($uris);
+ sort($keys);
+
+ // Rebuild array of URIs.
+ $result = array();
+ foreach ($keys as $k) {
+ $new_uris = Auth_Yadis_array_scramble($uris[$k]);
+ $result = array_merge($result, $new_uris);
+ }
+
+ $result = array_merge($result,
+ Auth_Yadis_array_scramble($last));
+
+ return $result;
+ }
+
+ /**
+ * Returns the "priority" attribute value of this <Service>
+ * element, if the attribute is present. Returns null if not.
+ *
+ * @return mixed $result Null or integer, depending on whether
+ * this Service element has a 'priority' attribute.
+ */
+ function getPriority()
+ {
+ $attributes = $this->parser->attributes($this->element);
+
+ if (array_key_exists('priority', $attributes)) {
+ return intval($attributes['priority']);
+ }
+
+ return null;
+ }
+
+ /**
+ * Used to get XML elements from this object's <Service> element.
+ *
+ * This is what you should use to get all custom information out
+ * of this element. This is used by service filter functions to
+ * determine whether a service element contains specific tags,
+ * etc. NOTE: this only considers elements which are direct
+ * children of the <Service> element for this object.
+ *
+ * @param string $name The name of the element to look for
+ * @return array $list An array of elements with the specified
+ * name which are direct children of the <Service> element. The
+ * nodes returned by this function can be passed to $this->parser
+ * methods (see {@link Auth_Yadis_XMLParser}).
+ */
+ function getElements($name)
+ {
+ return $this->parser->evalXPath($name, $this->element);
+ }
+}
+
+/*
+ * Return the expiration date of this XRD element, or None if no
+ * expiration was specified.
+ *
+ * @param $default The value to use as the expiration if no expiration
+ * was specified in the XRD.
+ */
+function Auth_Yadis_getXRDExpiration($xrd_element, $default=null)
+{
+ $expires_element = $xrd_element->$parser->evalXPath('/xrd:Expires');
+ if ($expires_element === null) {
+ return $default;
+ } else {
+ $expires_string = $expires_element->text;
+
+ // Will raise ValueError if the string is not the expected
+ // format
+ $t = strptime($expires_string, "%Y-%m-%dT%H:%M:%SZ");
+
+ if ($t === false) {
+ return false;
+ }
+
+ // [int $hour [, int $minute [, int $second [,
+ // int $month [, int $day [, int $year ]]]]]]
+ return mktime($t['tm_hour'], $t['tm_min'], $t['tm_sec'],
+ $t['tm_mon'], $t['tm_day'], $t['tm_year']);
+ }
+}
+
+/**
+ * This class performs parsing of XRDS documents.
+ *
+ * You should not instantiate this class directly; rather, call
+ * parseXRDS statically:
+ *
+ * <pre> $xrds = Auth_Yadis_XRDS::parseXRDS($xml_string);</pre>
+ *
+ * If the XRDS can be parsed and is valid, an instance of
+ * Auth_Yadis_XRDS will be returned. Otherwise, null will be
+ * returned. This class is used by the Auth_Yadis_Yadis::discover
+ * method.
+ *
+ * @package OpenID
+ */
+class Auth_Yadis_XRDS {
+
+ /**
+ * Instantiate a Auth_Yadis_XRDS object. Requires an XPath
+ * instance which has been used to parse a valid XRDS document.
+ */
+ function Auth_Yadis_XRDS($xmlParser, $xrdNodes)
+ {
+ $this->parser = $xmlParser;
+ $this->xrdNode = $xrdNodes[count($xrdNodes) - 1];
+ $this->allXrdNodes = $xrdNodes;
+ $this->serviceList = array();
+ $this->_parse();
+ }
+
+ /**
+ * Parse an XML string (XRDS document) and return either a
+ * Auth_Yadis_XRDS object or null, depending on whether the
+ * XRDS XML is valid.
+ *
+ * @param string $xml_string An XRDS XML string.
+ * @return mixed $xrds An instance of Auth_Yadis_XRDS or null,
+ * depending on the validity of $xml_string
+ */
+ static function parseXRDS($xml_string, $extra_ns_map = null)
+ {
+ $_null = null;
+
+ if (!$xml_string) {
+ return $_null;
+ }
+
+ $parser = Auth_Yadis_getXMLParser();
+
+ $ns_map = Auth_Yadis_getNSMap();
+
+ if ($extra_ns_map && is_array($extra_ns_map)) {
+ $ns_map = array_merge($ns_map, $extra_ns_map);
+ }
+
+ if (!($parser && $parser->init($xml_string, $ns_map))) {
+ return $_null;
+ }
+
+ // Try to get root element.
+ $root = $parser->evalXPath('/xrds:XRDS[1]');
+ if (!$root) {
+ return $_null;
+ }
+
+ if (is_array($root)) {
+ $root = $root[0];
+ }
+
+ $attrs = $parser->attributes($root);
+
+ if (array_key_exists('xmlns:xrd', $attrs) &&
+ $attrs['xmlns:xrd'] != Auth_Yadis_XMLNS_XRDS) {
+ return $_null;
+ } else if (array_key_exists('xmlns', $attrs) &&
+ preg_match('/xri/', $attrs['xmlns']) &&
+ $attrs['xmlns'] != Auth_Yadis_XMLNS_XRD_2_0) {
+ return $_null;
+ }
+
+ // Get the last XRD node.
+ $xrd_nodes = $parser->evalXPath('/xrds:XRDS[1]/xrd:XRD');
+
+ if (!$xrd_nodes) {
+ return $_null;
+ }
+
+ $xrds = new Auth_Yadis_XRDS($parser, $xrd_nodes);
+ return $xrds;
+ }
+
+ /**
+ * @access private
+ */
+ function _addService($priority, $service)
+ {
+ $priority = intval($priority);
+
+ if (!array_key_exists($priority, $this->serviceList)) {
+ $this->serviceList[$priority] = array();
+ }
+
+ $this->serviceList[$priority][] = $service;
+ }
+
+ /**
+ * Creates the service list using nodes from the XRDS XML
+ * document.
+ *
+ * @access private
+ */
+ function _parse()
+ {
+ $this->serviceList = array();
+
+ $services = $this->parser->evalXPath('xrd:Service', $this->xrdNode);
+
+ foreach ($services as $node) {
+ $s = new Auth_Yadis_Service();
+ $s->element = $node;
+ $s->parser = $this->parser;
+
+ $priority = $s->getPriority();
+
+ if ($priority === null) {
+ $priority = SERVICES_YADIS_MAX_PRIORITY;
+ }
+
+ $this->_addService($priority, $s);
+ }
+ }
+
+ /**
+ * Returns a list of service objects which correspond to <Service>
+ * elements in the XRDS XML document for this object.
+ *
+ * Optionally, an array of filter callbacks may be given to limit
+ * the list of returned service objects. Furthermore, the default
+ * mode is to return all service objects which match ANY of the
+ * specified filters, but $filter_mode may be
+ * SERVICES_YADIS_MATCH_ALL if you want to be sure that the
+ * returned services match all the given filters. See {@link
+ * Auth_Yadis_Yadis} for detailed usage information on filter
+ * functions.
+ *
+ * @param mixed $filters An array of callbacks to filter the
+ * returned services, or null if all services are to be returned.
+ * @param integer $filter_mode SERVICES_YADIS_MATCH_ALL or
+ * SERVICES_YADIS_MATCH_ANY, depending on whether the returned
+ * services should match ALL or ANY of the specified filters,
+ * respectively.
+ * @return mixed $services An array of {@link
+ * Auth_Yadis_Service} objects if $filter_mode is a valid
+ * mode; null if $filter_mode is an invalid mode (i.e., not
+ * SERVICES_YADIS_MATCH_ANY or SERVICES_YADIS_MATCH_ALL).
+ */
+ function services($filters = null,
+ $filter_mode = SERVICES_YADIS_MATCH_ANY)
+ {
+
+ $pri_keys = array_keys($this->serviceList);
+ sort($pri_keys, SORT_NUMERIC);
+
+ // If no filters are specified, return the entire service
+ // list, ordered by priority.
+ if (!$filters ||
+ (!is_array($filters))) {
+
+ $result = array();
+ foreach ($pri_keys as $pri) {
+ $result = array_merge($result, $this->serviceList[$pri]);
+ }
+
+ return $result;
+ }
+
+ // If a bad filter mode is specified, return null.
+ if (!in_array($filter_mode, array(SERVICES_YADIS_MATCH_ANY,
+ SERVICES_YADIS_MATCH_ALL))) {
+ return null;
+ }
+
+ // Otherwise, use the callbacks in the filter list to
+ // determine which services are returned.
+ $filtered = array();
+
+ foreach ($pri_keys as $priority_value) {
+ $service_obj_list = $this->serviceList[$priority_value];
+
+ foreach ($service_obj_list as $service) {
+
+ $matches = 0;
+
+ foreach ($filters as $filter) {
+
+ if (call_user_func_array($filter, array($service))) {
+ $matches++;
+
+ if ($filter_mode == SERVICES_YADIS_MATCH_ANY) {
+ $pri = $service->getPriority();
+ if ($pri === null) {
+ $pri = SERVICES_YADIS_MAX_PRIORITY;
+ }
+
+ if (!array_key_exists($pri, $filtered)) {
+ $filtered[$pri] = array();
+ }
+
+ $filtered[$pri][] = $service;
+ break;
+ }
+ }
+ }
+
+ if (($filter_mode == SERVICES_YADIS_MATCH_ALL) &&
+ ($matches == count($filters))) {
+
+ $pri = $service->getPriority();
+ if ($pri === null) {
+ $pri = SERVICES_YADIS_MAX_PRIORITY;
+ }
+
+ if (!array_key_exists($pri, $filtered)) {
+ $filtered[$pri] = array();
+ }
+ $filtered[$pri][] = $service;
+ }
+ }
+ }
+
+ $pri_keys = array_keys($filtered);
+ sort($pri_keys, SORT_NUMERIC);
+
+ $result = array();
+ foreach ($pri_keys as $pri) {
+ $result = array_merge($result, $filtered[$pri]);
+ }
+
+ return $result;
+ }
+}
+
+
--- /dev/null
+++ b/lib/openid.php
@@ -1,1 +1,782 @@
-
+<?php
+/**
+ * This class provides a simple interface for OpenID (1.1 and 2.0) authentication.
+ * Supports Yadis discovery.
+ * The authentication process is stateless/dumb.
+ *
+ * Usage:
+ * Sign-on with OpenID is a two step process:
+ * Step one is authentication with the provider:
+ * <code>
+ * $openid = new LightOpenID('my-host.example.org');
+ * $openid->identity = 'ID supplied by user';
+ * header('Location: ' . $openid->authUrl());
+ * </code>
+ * The provider then sends various parameters via GET, one of them is openid_mode.
+ * Step two is verification:
+ * <code>
+ * if ($this->data['openid_mode']) {
+ * $openid = new LightOpenID('my-host.example.org');
+ * echo $openid->validate() ? 'Logged in.' : 'Failed';
+ * }
+ * </code>
+ *
+ * Change the 'my-host.example.org' to your domain name. Do NOT use $_SERVER['HTTP_HOST']
+ * for that, unless you know what you are doing.
+ *
+ * Optionally, you can set $returnUrl and $realm (or $trustRoot, which is an alias).
+ * The default values for those are:
+ * $openid->realm = (!empty($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'];
+ * $openid->returnUrl = $openid->realm . $_SERVER['REQUEST_URI'];
+ * If you don't know their meaning, refer to any openid tutorial, or specification. Or just guess.
+ *
+ * AX and SREG extensions are supported.
+ * To use them, specify $openid->required and/or $openid->optional before calling $openid->authUrl().
+ * These are arrays, with values being AX schema paths (the 'path' part of the URL).
+ * For example:
+ * $openid->required = array('namePerson/friendly', 'contact/email');
+ * $openid->optional = array('namePerson/first');
+ * If the server supports only SREG or OpenID 1.1, these are automaticaly
+ * mapped to SREG names, so that user doesn't have to know anything about the server.
+ *
+ * To get the values, use $openid->getAttributes().
+ *
+ *
+ * The library requires PHP >= 5.1.2 with curl or http/https stream wrappers enabled.
+ * @author Mewp
+ * @copyright Copyright (c) 2010, Mewp
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ */
+class LightOpenID
+{
+ public $returnUrl
+ , $required = array()
+ , $optional = array()
+ , $verify_peer = null
+ , $capath = null
+ , $cainfo = null
+ , $data;
+ private $identity, $claimed_id;
+ protected $server, $version, $trustRoot, $aliases, $identifier_select = false
+ , $ax = false, $sreg = false, $setup_url = null;
+ static protected $ax_to_sreg = array(
+ 'namePerson/friendly' => 'nickname',
+ 'contact/email' => 'email',
+ 'namePerson' => 'fullname',
+ 'birthDate' => 'dob',
+ 'person/gender' => 'gender',
+ 'contact/postalCode/home' => 'postcode',
+ 'contact/country/home' => 'country',
+ 'pref/language' => 'language',
+ 'pref/timezone' => 'timezone',
+ );
+
+ function __construct($host)
+ {
+ $this->trustRoot = (strpos($host, '://') ? $host : 'http://' . $host);
+ if ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')
+ || (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
+ && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
+ ) {
+ $this->trustRoot = (strpos($host, '://') ? $host : 'https://' . $host);
+ }
+
+ if(($host_end = strpos($this->trustRoot, '/', 8)) !== false) {
+ $this->trustRoot = substr($this->trustRoot, 0, $host_end);
+ }
+
+ $uri = rtrim(preg_replace('#((?<=\?)|&)openid\.[^&]+#', '', $_SERVER['REQUEST_URI']), '?');
+ $this->returnUrl = $this->trustRoot . $uri;
+
+ $this->data = ($_SERVER['REQUEST_METHOD'] === 'POST') ? $_POST : $_GET;
+
+ if(!function_exists('curl_init') && !in_array('https', stream_get_wrappers())) {
+ throw new ErrorException('You must have either https wrappers or curl enabled.');
+ }
+ }
+
+ function __set($name, $value)
+ {
+ switch ($name) {
+ case 'identity':
+ if (strlen($value = trim((String) $value))) {
+ if (preg_match('#^xri:/*#i', $value, $m)) {
+ $value = substr($value, strlen($m[0]));
+ } elseif (!preg_match('/^(?:[=@+\$!\(]|https?:)/i', $value)) {
+ $value = "http://$value";
+ }
+ if (preg_match('#^https?://[^/]+$#i', $value, $m)) {
+ $value .= '/';
+ }
+ }
+ $this->$name = $this->claimed_id = $value;
+ break;
+ case 'trustRoot':
+ case 'realm':
+ $this->trustRoot = trim($value);
+ }
+ }
+
+ function __get($name)
+ {
+ switch ($name) {
+ case 'identity':
+ # We return claimed_id instead of identity,
+ # because the developer should see the claimed identifier,
+ # i.e. what he set as identity, not the op-local identifier (which is what we verify)
+ return $this->claimed_id;
+ case 'trustRoot':
+ case 'realm':
+ return $this->trustRoot;
+ case 'mode':
+ return empty($this->data['openid_mode']) ? null : $this->data['openid_mode'];
+ }
+ }
+
+ /**
+ * Checks if the server specified in the url exists.
+ *
+ * @param $url url to check
+ * @return true, if the server exists; false otherwise
+ */
+ function hostExists($url)
+ {
+ if (strpos($url, '/') === false) {
+ $server = $url;
+ } else {
+ $server = @parse_url($url, PHP_URL_HOST);
+ }
+
+ if (!$server) {
+ return false;
+ }
+
+ return !!gethostbynamel($server);
+ }
+
+ protected function request_curl($url, $method='GET', $params=array())
+ {
+ $params = http_build_query($params, '', '&');
+ $curl = curl_init($url . ($method == 'GET' && $params ? '?' . $params : ''));
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($curl, CURLOPT_HEADER, false);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/xrds+xml, */*'));
+
+ if($this->verify_peer !== null) {
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verify_peer);
+ if($this->capath) {
+ curl_setopt($curl, CURLOPT_CAPATH, $this->capath);
+ }
+
+ if($this->cainfo) {
+ curl_setopt($curl, CURLOPT_CAINFO, $this->cainfo);
+ }
+ }
+
+ if ($method == 'POST') {
+ curl_setopt($curl, CURLOPT_POST, true);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
+ } elseif ($method == 'HEAD') {
+ curl_setopt($curl, CURLOPT_HEADER, true);
+ curl_setopt($curl, CURLOPT_NOBODY, true);
+ } else {
+ curl_setopt($curl, CURLOPT_HTTPGET, true);
+ }
+ $response = curl_exec($curl);
+
+ if($method == 'HEAD') {
+ $headers = array();
+ foreach(explode("\n", $response) as $header) {
+ $pos = strpos($header,':');
+ $name = strtolower(trim(substr($header, 0, $pos)));
+ $headers[$name] = trim(substr($header, $pos+1));
+ }
+
+ # Updating claimed_id in case of redirections.
+ $effective_url = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
+ if($effective_url != $url) {
+ $this->identity = $this->claimed_id = $effective_url;
+ }
+
+ return $headers;
+ }
+
+ if (curl_errno($curl)) {
+ throw new ErrorException(curl_error($curl), curl_errno($curl));
+ }
+
+ return $response;
+ }
+
+ protected function request_streams($url, $method='GET', $params=array())
+ {
+ if(!$this->hostExists($url)) {
+ throw new ErrorException("Could not connect to $url.", 404);
+ }
+
+ $params = http_build_query($params, '', '&');
+ switch($method) {
+ case 'GET':
+ $opts = array(
+ 'http' => array(
+ 'method' => 'GET',
+ 'header' => 'Accept: application/xrds+xml, */*',
+ 'ignore_errors' => true,
+ ), 'ssl' => array(
+ 'CN_match' => parse_url($url, PHP_URL_HOST),
+ ),
+ );
+ $url = $url . ($params ? '?' . $params : '');
+ break;
+ case 'POST':
+ $opts = array(
+ 'http' => array(
+ 'method' => 'POST',
+ 'header' => 'Content-type: application/x-www-form-urlencoded',
+ 'content' => $params,
+ 'ignore_errors' => true,
+ ), 'ssl' => array(
+ 'CN_match' => parse_url($url, PHP_URL_HOST),
+ ),
+ );
+ break;
+ case 'HEAD':
+ # We want to send a HEAD request,
+ # but since get_headers doesn't accept $context parameter,
+ # we have to change the defaults.
+ $default = stream_context_get_options(stream_context_get_default());
+ stream_context_get_default(
+ array(
+ 'http' => array(
+ 'method' => 'HEAD',
+ 'header' => 'Accept: application/xrds+xml, */*',
+ 'ignore_errors' => true,
+ ), 'ssl' => array(
+ 'CN_match' => parse_url($url, PHP_URL_HOST),
+ ),
+ )
+ );
+
+ $url = $url . ($params ? '?' . $params : '');
+ $headers_tmp = get_headers ($url);
+ if(!$headers_tmp) {
+ return array();
+ }
+
+ # Parsing headers.
+ $headers = array();
+ foreach($headers_tmp as $header) {
+ $pos = strpos($header,':');
+ $name = strtolower(trim(substr($header, 0, $pos)));
+ $headers[$name] = trim(substr($header, $pos+1));
+
+ # Following possible redirections. The point is just to have
+ # claimed_id change with them, because get_headers() will
+ # follow redirections automatically.
+ # We ignore redirections with relative paths.
+ # If any known provider uses them, file a bug report.
+ if($name == 'location') {
+ if(strpos($headers[$name], 'http') === 0) {
+ $this->identity = $this->claimed_id = $headers[$name];
+ } elseif($headers[$name][0] == '/') {
+ $parsed_url = parse_url($this->claimed_id);
+ $this->identity =
+ $this->claimed_id = $parsed_url['scheme'] . '://'
+ . $parsed_url['host']
+ . $headers[$name];
+ }
+ }
+ }
+
+ # And restore them.
+ stream_context_get_default($default);
+ return $headers;
+ }
+
+ if($this->verify_peer) {
+ $opts['ssl'] += array(
+ 'verify_peer' => true,
+ 'capath' => $this->capath,
+ 'cafile' => $this->cainfo,
+ );
+ }
+
+ $context = stream_context_create ($opts);
+
+ return file_get_contents($url, false, $context);
+ }
+
+ protected function request($url, $method='GET', $params=array())
+ {
+ if (function_exists('curl_init')
+ && (!in_array('https', stream_get_wrappers()) || !ini_get('safe_mode') && !ini_get('open_basedir'))
+ ) {
+ return $this->request_curl($url, $method, $params);
+ }
+ return $this->request_streams($url, $method, $params);
+ }
+
+ protected function build_url($url, $parts)
+ {
+ if (isset($url['query'], $parts['query'])) {
+ $parts['query'] = $url['query'] . '&' . $parts['query'];
+ }
+
+ $url = $parts + $url;
+ $url = $url['scheme'] . '://'
+ . (empty($url['username'])?''
+ :(empty($url['password'])? "{$url['username']}@"
+ :"{$url['username']}:{$url['password']}@"))
+ . $url['host']
+ . (empty($url['port'])?'':":{$url['port']}")
+ . (empty($url['path'])?'':$url['path'])
+ . (empty($url['query'])?'':"?{$url['query']}")
+ . (empty($url['fragment'])?'':"#{$url['fragment']}");
+ return $url;
+ }
+
+ /**
+ * Helper function used to scan for <meta>/<link> tags and extract information
+ * from them
+ */
+ protected function htmlTag($content, $tag, $attrName, $attrValue, $valueName)
+ {
+ preg_match_all("#<{$tag}[^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*$valueName=['\"](.+?)['\"][^>]*/?>#i", $content, $matches1);
+ preg_match_all("#<{$tag}[^>]*$valueName=['\"](.+?)['\"][^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*/?>#i", $content, $matches2);
+
+ $result = array_merge($matches1[1], $matches2[1]);
+ return empty($result)?false:$result[0];
+ }
+
+ /**
+ * Performs Yadis and HTML discovery. Normally not used.
+ * @param $url Identity URL.
+ * @return String OP Endpoint (i.e. OpenID provider address).
+ * @throws ErrorException
+ */
+ function discover($url)
+ {
+ if (!$url) throw new ErrorException('No identity supplied.');
+ # Use xri.net proxy to resolve i-name identities
+ if (!preg_match('#^https?:#', $url)) {
+ $url = "https://xri.net/$url";
+ }
+
+ # We save the original url in case of Yadis discovery failure.
+ # It can happen when we'll be lead to an XRDS document
+ # which does not have any OpenID2 services.
+ $originalUrl = $url;
+
+ # A flag to disable yadis discovery in case of failure in headers.
+ $yadis = true;
+
+ # We'll jump a maximum of 5 times, to avoid endless redirections.
+ for ($i = 0; $i < 5; $i ++) {
+ if ($yadis) {
+ $headers = $this->request($url, 'HEAD');
+
+ $next = false;
+ if (isset($headers['x-xrds-location'])) {
+ $url = $this->build_url(parse_url($url), parse_url(trim($headers['x-xrds-location'])));
+ $next = true;
+ }
+
+ if (isset($headers['content-type'])
+ && (strpos($headers['content-type'], 'application/xrds+xml') !== false
+ || strpos($headers['content-type'], 'text/xml') !== false)
+ ) {
+ # Apparently, some providers return XRDS documents as text/html.
+ # While it is against the spec, allowing this here shouldn't break
+ # compatibility with anything.
+ # ---
+ # Found an XRDS document, now let's find the server, and optionally delegate.
+ $content = $this->request($url, 'GET');
+
+ preg_match_all('#<Service.*?>(.*?)</Service>#s', $content, $m);
+ foreach($m[1] as $content) {
+ $content = ' ' . $content; # The space is added, so that strpos doesn't return 0.
+
+ # OpenID 2
+ $ns = preg_quote('http://specs.openid.net/auth/2.0/');
+ if(preg_match('#<Type>\s*'.$ns.'(server|signon)\s*</Type>#s', $content, $type)) {
+ if ($type[1] == 'server') $this->identifier_select = true;
+
+ preg_match('#<URI.*?>(.*)</URI>#', $content, $server);
+ preg_match('#<(Local|Canonical)ID>(.*)</\1ID>#', $content, $delegate);
+ if (empty($server)) {
+ return false;
+ }
+ # Does the server advertise support for either AX or SREG?
+ $this->ax = (bool) strpos($content, '<Type>http://openid.net/srv/ax/1.0</Type>');
+ $this->sreg = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>')
+ || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>');
+
+ $server = $server[1];
+ if (isset($delegate[2])) $this->identity = trim($delegate[2]);
+ $this->version = 2;
+
+ $this->server = $server;
+ return $server;
+ }
+
+ # OpenID 1.1
+ $ns = preg_quote('http://openid.net/signon/1.1');
+ if (preg_match('#<Type>\s*'.$ns.'\s*</Type>#s', $content)) {
+
+ preg_match('#<URI.*?>(.*)</URI>#', $content, $server);
+ preg_match('#<.*?Delegate>(.*)</.*?Delegate>#', $content, $delegate);
+ if (empty($server)) {
+ return false;
+ }
+ # AX can be used only with OpenID 2.0, so checking only SREG
+ $this->sreg = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>')
+ || strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>');
+
+ $server = $server[1];
+ if (isset($delegate[1])) $this->identity = $delegate[1];
+ $this->version = 1;
+
+ $this->server = $server;
+ return $server;
+ }
+ }
+
+ $next = true;
+ $yadis = false;
+ $url = $originalUrl;
+ $content = null;
+ break;
+ }
+ if ($next) continue;
+
+ # There are no relevant information in headers, so we search the body.
+ $content = $this->request($url, 'GET');
+ $location = $this->htmlTag($content, 'meta', 'http-equiv', 'X-XRDS-Location', 'content');
+ if ($location) {
+ $url = $this->build_url(parse_url($url), parse_url($location));
+ continue;
+ }
+ }
+
+ if (!$content) $content = $this->request($url, 'GET');
+
+ # At this point, the YADIS Discovery has failed, so we'll switch
+ # to openid2 HTML discovery, then fallback to openid 1.1 discovery.
+ $server = $this->htmlTag($content, 'link', 'rel', 'openid2.provider', 'href');
+ $delegate = $this->htmlTag($content, 'link', 'rel', 'openid2.local_id', 'href');
+ $this->version = 2;
+
+ if (!$server) {
+ # The same with openid 1.1
+ $server = $this->htmlTag($content, 'link', 'rel', 'openid.server', 'href');
+ $delegate = $this->htmlTag($content, 'link', 'rel', 'openid.delegate', 'href');
+ $this->version = 1;
+ }
+
+ if ($server) {
+ # We found an OpenID2 OP Endpoint
+ if ($delegate) {
+ # We have also found an OP-Local ID.
+ $this->identity = $delegate;
+ }
+ $this->server = $server;
+ return $server;
+ }
+
+ throw new ErrorException("No OpenID Server found at $url", 404);
+ }
+ throw new ErrorException('Endless redirection!', 500);
+ }
+
+ protected function sregParams()
+ {
+ $params = array();
+ # We always use SREG 1.1, even if the server is advertising only support for 1.0.
+ # That's because it's fully backwards compatibile with 1.0, and some providers
+ # advertise 1.0 even if they accept only 1.1. One such provider is myopenid.com
+ $params['openid.ns.sreg'] = 'http://openid.net/extensions/sreg/1.1';
+ if ($this->required) {
+ $params['openid.sreg.required'] = array();
+ foreach ($this->required as $required) {
+ if (!isset(self::$ax_to_sreg[$required])) continue;
+ $params['openid.sreg.required'][] = self::$ax_to_sreg[$required];
+ }
+ $params['openid.sreg.required'] = implode(',', $params['openid.sreg.required']);
+ }
+
+ if ($this->optional) {
+ $params['openid.sreg.optional'] = array();
+ foreach ($this->optional as $optional) {
+ if (!isset(self::$ax_to_sreg[$optional])) continue;
+ $params['openid.sreg.optional'][] = self::$ax_to_sreg[$optional];
+ }
+ $params['openid.sreg.optional'] = implode(',', $params['openid.sreg.optional']);
+ }
+ return $params;
+ }
+
+ protected function axParams()
+ {
+ $params = array();
+ if ($this->required || $this->optional) {
+ $params['openid.ns.ax'] = 'http://openid.net/srv/ax/1.0';
+ $params['openid.ax.mode'] = 'fetch_request';
+ $this->aliases = array();
+ $counts = array();
+ $required = array();
+ $optional = array();
+ foreach (array('required','optional') as $type) {
+ foreach ($this->$type as $alias => $field) {
+ if (is_int($alias)) $alias = strtr($field, '/', '_');
+ $this->aliases[$alias] = 'http://axschema.org/' . $field;
+ if (empty($counts[$alias])) $counts[$alias] = 0;
+ $counts[$alias] += 1;
+ ${$type}[] = $alias;
+ }
+ }
+ foreach ($this->aliases as $alias => $ns) {
+ $params['openid.ax.type.' . $alias] = $ns;
+ }
+ foreach ($counts as $alias => $count) {
+ if ($count == 1) continue;
+ $params['openid.ax.count.' . $alias] = $count;
+ }
+
+ # Don't send empty ax.requied and ax.if_available.
+ # Google and possibly other providers refuse to support ax when one of these is empty.
+ if($required) {
+ $params['openid.ax.required'] = implode(',', $required);
+ }
+ if($optional) {
+ $params['openid.ax.if_available'] = implode(',', $optional);
+ }
+ }
+ return $params;
+ }
+
+ protected function authUrl_v1($immediate)
+ {
+ $returnUrl = $this->returnUrl;
+ # If we have an openid.delegate that is different from our claimed id,
+ # we need to somehow preserve the claimed id between requests.
+ # The simplest way is to just send it along with the return_to url.
+ if($this->identity != $this->claimed_id) {
+ $returnUrl .= (strpos($returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->claimed_id;
+ }
+
+ $params = array(
+ 'openid.return_to' => $returnUrl,
+ 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup',
+ 'openid.identity' => $this->identity,
+ 'openid.trust_root' => $this->trustRoot,
+ ) + $this->sregParams();
+
+ return $this->build_url(parse_url($this->server)
+ , array('query' => http_build_query($params, '', '&')));
+ }
+
+ protected function authUrl_v2($immediate)
+ {
+ $params = array(
+ 'openid.ns' => 'http://specs.openid.net/auth/2.0',
+ 'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup',
+ 'openid.return_to' => $this->returnUrl,
+ 'openid.realm' => $this->trustRoot,
+ );
+ if ($this->ax) {
+ $params += $this->axParams();
+ }
+ if ($this->sreg) {
+ $params += $this->sregParams();
+ }
+ if (!$this->ax && !$this->sreg) {
+ # If OP doesn't advertise either SREG, nor AX, let's send them both
+ # in worst case we don't get anything in return.
+ $params += $this->axParams() + $this->sregParams();
+ }
+
+ if ($this->identifier_select) {
+ $params['openid.identity'] = $params['openid.claimed_id']
+ = 'http://specs.openid.net/auth/2.0/identifier_select';
+ } else {
+ $params['openid.identity'] = $this->identity;
+ $params['openid.claimed_id'] = $this->claimed_id;
+ }
+
+ return $this->build_url(parse_url($this->server)
+ , array('query' => http_build_query($params, '', '&')));
+ }
+
+ /**
+ * Returns authentication url. Usually, you want to redirect your user to it.
+ * @return String The authentication url.
+ * @param String $select_identifier Whether to request OP to select identity for an user in OpenID 2. Does not affect OpenID 1.
+ * @throws ErrorException
+ */
+ function authUrl($immediate = false)
+ {
+ if ($this->setup_url && !$immediate) return $this->setup_url;
+ if (!$this->server) $this->discover($this->identity);
+
+ if ($this->version == 2) {
+ return $this->authUrl_v2($immediate);
+ }
+ return $this->authUrl_v1($immediate);
+ }
+
+ /**
+ * Performs OpenID verification with the OP.
+ * @return Bool Whether the verification was successful.
+ * @throws ErrorException
+ */
+ function validate()
+ {
+ # If the request was using immediate mode, a failure may be reported
+ # by presenting user_setup_url (for 1.1) or reporting
+ # mode 'setup_needed' (for 2.0). Also catching all modes other than
+ # id_res, in order to avoid throwing errors.
+ if(isset($this->data['openid_user_setup_url'])) {
+ $this->setup_url = $this->data['openid_user_setup_url'];
+ return false;
+ }
+ if($this->mode != 'id_res') {
+ return false;
+ }
+
+ $this->claimed_id = isset($this->data['openid_claimed_id'])?$this->data['openid_claimed_id']:$this->data['openid_identity'];
+ $params = array(
+ 'openid.assoc_handle' => $this->data['openid_assoc_handle'],
+ 'openid.signed' => $this->data['openid_signed'],
+ 'openid.sig' => $this->data['openid_sig'],
+ );
+
+ if (isset($this->data['openid_ns'])) {
+ # We're dealing with an OpenID 2.0 server, so let's set an ns
+ # Even though we should know location of the endpoint,
+ # we still need to verify it by discovery, so $server is not set here
+ $params['openid.ns'] = 'http://specs.openid.net/auth/2.0';
+ } elseif (isset($this->data['openid_claimed_id'])
+ && $this->data['openid_claimed_id'] != $this->data['openid_identity']
+ ) {
+ # If it's an OpenID 1 provider, and we've got claimed_id,
+ # we have to append it to the returnUrl, like authUrl_v1 does.
+ $this->returnUrl .= (strpos($this->returnUrl, '?') ? '&' : '?')
+ . 'openid.claimed_id=' . $this->claimed_id;
+ }
+
+ if ($this->data['openid_return_to'] != $this->returnUrl) {
+ # The return_to url must match the url of current request.
+ # I'm assuing that noone will set the returnUrl to something that doesn't make sense.
+ return false;
+ }
+
+ $server = $this->discover($this->claimed_id);
+
+ foreach (explode(',', $this->data['openid_signed']) as $item) {
+ # Checking whether magic_quotes_gpc is turned on, because
+ # the function may fail if it is. For example, when fetching
+ # AX namePerson, it might containg an apostrophe, which will be escaped.
+ # In such case, validation would fail, since we'd send different data than OP
+ # wants to verify. stripslashes() should solve that problem, but we can't
+ # use it when magic_quotes is off.
+ $value = $this->data['openid_' . str_replace('.','_',$item)];
+ $params['openid.' . $item] = get_magic_quotes_gpc() ? stripslashes($value) : $value;
+
+ }
+
+ $params['openid.mode'] = 'check_authentication';
+
+ $response = $this->request($server, 'POST', $params);
+
+ return preg_match('/is_valid\s*:\s*true/i', $response);
+ }
+
+ protected function getAxAttributes()
+ {
+ $alias = null;
+ if (isset($this->data['openid_ns_ax'])
+ && $this->data['openid_ns_ax'] != 'http://openid.net/srv/ax/1.0'
+ ) { # It's the most likely case, so we'll check it before
+ $alias = 'ax';
+ } else {
+ # 'ax' prefix is either undefined, or points to another extension,
+ # so we search for another prefix
+ foreach ($this->data as $key => $val) {
+ if (substr($key, 0, strlen('openid_ns_')) == 'openid_ns_'
+ && $val == 'http://openid.net/srv/ax/1.0'
+ ) {
+ $alias = substr($key, strlen('openid_ns_'));
+ break;
+ }
+ }
+ }
+ if (!$alias) {
+ # An alias for AX schema has not been found,
+ # so there is no AX data in the OP's response
+ return array();
+ }
+
+ $attributes = array();
+ foreach (explode(',', $this->data['openid_signed']) as $key) {
+ $keyMatch = $alias . '.value.';
+ if (substr($key, 0, strlen($keyMatch)) != $keyMatch) {
+ continue;
+ }
+ $key = substr($key, strlen($keyMatch));
+ if (!isset($this->data['openid_' . $alias . '_type_' . $key])) {
+ # OP is breaking the spec by returning a field without
+ # associated ns. This shouldn't happen, but it's better
+ # to check, than cause an E_NOTICE.
+ continue;
+ }
+ $value = $this->data['openid_' . $alias . '_value_' . $key];
+ $key = substr($this->data['openid_' . $alias . '_type_' . $key],
+ strlen('http://axschema.org/'));
+
+ $attributes[$key] = $value;
+ }
+ return $attributes;
+ }
+
+ protected function getSregAttributes()
+ {
+ $attributes = array();
+ $sreg_to_ax = array_flip(self::$ax_to_sreg);
+ foreach (explode(',', $this->data['openid_signed']) as $key) {
+ $keyMatch = 'sreg.';
+ if (substr($key, 0, strlen($keyMatch)) != $keyMatch) {
+ continue;
+ }
+ $key = substr($key, strlen($keyMatch));
+ if (!isset($sreg_to_ax[$key])) {
+ # The field name isn't part of the SREG spec, so we ignore it.
+ continue;
+ }
+ $attributes[$sreg_to_ax[$key]] = $this->data['openid_sreg_' . $key];
+ }
+ return $attributes;
+ }
+
+ /**
+ * Gets AX/SREG attributes provided by OP. should be used only after successful validaton.
+ * Note that it does not guarantee that any of the required/optional parameters will be present,
+ * or that there will be no other attributes besides those specified.
+ * In other words. OP may provide whatever information it wants to.
+ * * SREG names will be mapped to AX names.
+ * * @return Array Array of attributes with keys being the AX schema names, e.g. 'contact/email'
+ * @see http://www.axschema.org/types/
+ */
+ function getAttributes()
+ {
+ if (isset($this->data['openid_ns'])
+ && $this->data['openid_ns'] == 'http://specs.openid.net/auth/2.0'
+ ) { # OpenID 2.0
+ # We search for both AX and SREG attributes, with AX taking precedence.
+ return $this->getAxAttributes() + $this->getSregAttributes();
+ }
+ return $this->getSregAttributes();
+ }
+}
+
--- a/robots.txt
+++ b/robots.txt
@@ -1,3 +1,3 @@
User-agent: *
Disallow: /admin
-
+Sitemap: http://contracts.disclosurelo.gs/sitemap.xml.php
--- a/search.php
+++ b/search.php
@@ -1,31 +1,35 @@
<?php
+
/*
- search ABNs
-search agency name
-search categories
-search supplier names
---search supplier postcodes/suburbs/cities--
-search CN number
-search description full text
-*/
+ search ABNs
+ search agency name
+ search categories
+ search supplier names
+ --search supplier postcodes/suburbs/cities--
+ search CN number
+ search description full text
+ */
include('./lib/common.inc.php');
if ($_REQUEST['searchID']) {
-$searchIDParts = explode("-",$_REQUEST['searchID']);
-$type = array_shift($searchIDParts);
-$host = $_SERVER['HTTP_HOST'];
-$uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\\');
+ $searchIDParts = explode("-", $_REQUEST['searchID']);
+ $type = array_shift($searchIDParts);
+ $host = $_SERVER['HTTP_HOST'];
+ $uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\\');
-if ($type == "agency") {
-header("Location: http://$host$uri/displayAgency.php?agency=".implode("-",$searchIDParts));
-}
-if ($type == "supplier") {
-header("Location: http://$host$uri/displaySupplier.php?supplier=".implode("-",$searchIDParts));
-}
-exit;
+ if ($type == "agency") {
+ header("Location: http://$host$uri/displayAgency.php?agency=" . implode("-", $searchIDParts));
+ }
+ if ($type == "supplier") {
+ header("Location: http://$host$uri/displaySupplier.php?supplier=" . implode("-", $searchIDParts));
+ }
+ if ($type == "cnid") {
+ header("Location: http://$host$uri/displayContract.php?CNID=" . implode("-", $searchIDParts));
+ }
+ exit;
} else {
-include_header("Search Results");
-print_r($_REQUEST);
-include_footer();
+ include_header("Search Results");
+ print_r($_REQUEST);
+ include_footer();
}
?>
--- a/search_autosuggest.php
+++ b/search_autosuggest.php
@@ -46,6 +46,21 @@
"info" => htmlspecialchars("Government Agency - " . $row['count'] . " records")
);
}
+ $query = "SELECT \"CNID\", description, value FROM contractnotice
+WHERE to_tsvector('english', description) @@ plainto_tsquery('english', :input)";
+ $query = $conn->prepare($query);
+ $agencyName = $input . '%';
+ $query->bindParam(":input", $input);
+ $query->execute();
+ databaseError($conn->errorInfo());
+ foreach ($query->fetchAll() as $row) {
+ $count++;
+ $aResults[] = array(
+ "id" => "cnid-" . $row[0],
+ "value" => htmlspecialchars($row['description']),
+ "info" => htmlspecialchars("Contract Notice - Value ".$row['value'])
+ );
+ }
}
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
@@ -69,3 +84,4 @@
echo "</results>";
}
?>
+
--- a/sitemap.xml.php
+++ b/sitemap.xml.php
@@ -1,27 +1,13 @@
<?php
-include ('include/common.inc.php');
+include ('lib/common.inc.php');
$last_updated = date('Y-m-d',@filemtime('cbrfeed.zip'));
header("Content-Type: text/xml");
echo "<?xml version='1.0' encoding='UTF-8'?>";
echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
- echo " <url><loc>".curPageURL()."index.php</loc><priority>1.0</priority></url>\n";
+ echo " <url><loc>".local_url()."index.php</loc><priority>1.0</priority></url>\n";
foreach (scandir("./") as $file) {
- if (strpos($file,".php") !== false && $file != "index.php" && $file != "sitemap.xml.php") echo " <url><loc>".curPageURL()."$file</loc><priority>0.3</priority></url>\n";
+ if (strpos($file,".php") !== false && $file != "index.php" && $file != "sitemap.xml.php") echo " <url><loc>".local_url()."$file</loc><priority>0.3</priority></url>\n";
}
-foreach (getStops() as $stop) {
- echo " <url><loc>".curPageURL()."stop.php?stopid=".htmlspecialchars ($stop["stop_id"])."</loc>";
- echo "<lastmod>" . $last_updated . "</lastmod>";
- echo "<changefreq>monthly</changefreq>";
- echo "<priority>0.9</priority>";
- echo "</url>\n";
- }
-foreach (getRoutes() as $route) {
- echo " <url><loc>".curPageURL()."trip.php?routeid=".htmlspecialchars ($route["route_id"])."</loc>";
- echo "<lastmod>" . $last_updated . "</lastmod>";
- echo "<changefreq>monthly</changefreq>";
- echo "<priority>0.9</priority>";
- echo "</url>\n";
- }
echo '</urlset>';
?>
--- a/style.css
+++ b/style.css
@@ -1,6 +1,3 @@
-body {
- padding-top:60px;
-}
/* div collapse from http://roshanbh.com.np/2008/03/expandable-collapsible-toggle-pane-jquery.html */
p {
padding: 0 0 1em;