diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e9dc882 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tools/easyrdf"] + path = tools/easyrdf + url = git://github.com/njh/easyrdf.git diff --git a/tools/classes.php b/tools/classes.php new file mode 100644 index 0000000..b3191ba --- /dev/null +++ b/tools/classes.php @@ -0,0 +1,370 @@ +localName()); + return "$name"; + } + + protected function addPropertyList(&$list, $values, $skip) + { + foreach($values as $value) + { + $k = strval($value); + if(in_array(strval($value), $skip)) + { + continue; + } + if($value instanceof EasyRdf_Collection) + { + $this->addPropertyList($list, $value, $skip); + continue; + } + if($value instanceof EasyRdf_Resource && $value->isBNode()) + { + $union = $value->all('owl:unionOf'); + $this->addPropertyList($list, $union, $skip); + continue; + } + if ($value instanceof Phpspecgen_Term) + { + $list[$k] = $value->termLink(); + } + else if ($value instanceof EasyRdf_Resource) + { + $list[$k] = $value->htmlLink($value->shorten()); + } + else + { + $list[$k] = strval($value); + } + } + } + + public function propertyList($property, $skip = null) { + $items = array(); + if(!is_array($skip)) + { + $skip = array(); + } + $this->addPropertyList($items, $this->all($property), $skip); + ksort($items); + return $items; + } + + public function propertyRow($title, $property, $skip = null) { + $items = $this->propertyList($property, $skip); + if (count($items) > 0) { + return "$title: ".implode(', ', $items)."\n"; + } else { + return ''; + } + } + } + + class Phpspecgen_Class extends Phpspecgen_Term + { + public function termType() { + return 'Class'; + } + + public function inheritedProperties() { + $properties = array(); + foreach ($this->allParents() as $parent) { + foreach($parent->all('^rdfs:domain') as $property) { + if ($property instanceof Phpspecgen_Term) { + array_push($properties, $property->termLink()); + } + } + } + return $properties; + } + + protected function allParents($depth=0) { + $parents = array(); + foreach ($this->all('rdfs:subClassOf') as $parent) { + if (!$parent instanceof Phpspecgen_Class) + continue; + array_push($parents, $parent); + if ($depth < 10) + $parents = array_merge($parents, $parent->allParents($depth)); + } + return $parents; + } + } + + class Phpspecgen_Property extends Phpspecgen_Term + { + public function termType() { + return 'Property'; + } + } + + class Phpspecgen_ConceptScheme extends Phpspecgen_Term + { + public function termType() { + return 'Concept scheme'; + } + } + + class Phpspecgen_Individual extends Phpspecgen_Term + { + public function termType() { + return 'Named individual'; + } + } + + class Phpspecgen_Concept extends Phpspecgen_Term + { + public function termType() { + return 'Concept'; + } + } + + class Phpspecgen_Vocab extends EasyRdf_Resource + { + public $revision = null; + public $basename = null; + + protected function propertyDefinition($title, $property) { + $values = $this->all($property); + if (count($values) < 1) + return ''; + + $html = array(); + foreach($values as $value) { + if ($value instanceof EasyRdf_Literal) { + array_push($html, htmlspecialchars($value)); + } else if ($value->get('foaf:homepage')) { + array_push($html, $value->get('foaf:homepage')->htmlLink( $value->label() )); + } else { + if ($value->isBnode()) { + array_push($html, htmlspecialchars($value->label()) ); + } else { + array_push($html, $value->htmlLink($value->label()) ); + } + } + } + + return "
$title
".implode(', ', array_unique($html))."
\n"; + } + + public function htmlHeader() { + $this->revision = $this->get('owl:versionInfo'); + $html = "

".htmlspecialchars($this->label())."

\n"; + $html .= "".htmlspecialchars($this->get('dc:description|dc11:description|rdfs:comment'))."\n"; + + $html .= "
\n"; + if(strlen($this->basename)) + { + $html .= "
This version
uri . $this->basename . "\">" . $this->uri . $this->basename . "
\n"; + } + $html .= "
Latest version
".$this->htmlLink()."
\n"; + $html .= $this->propertyDefinition('Created', 'dc:created|dc11:created'); + $html .= $this->propertyDefinition('Date', 'dc:date|dc11:date|dc:issued|dc11:issued'); + $html .= $this->propertyDefinition('Revision', 'owl:versionInfo'); + $html .= $this->propertyDefinition('Authors', 'foaf:maker|dc:creator|dc11:creator'); + $html .= $this->propertyDefinition('Contributors', 'dc:contributor|dc11:contributor'); + $html .= "
\n"; + return $html; + } + + public function htmlSummaryOfTerms() { + $html = "

Summary of Terms

\n"; + $classes = array(); + $classCount = 0; + $props = array(); + $propertyCount = 0; + $schemes = array(); + $schemeCount = 0; + $concepts = array(); + $conceptCount = 0; + $individuals = array(); + $individualCount = 0; + foreach($this->all("^rdfs:isDefinedBy") as $term) { + if(method_exists($term, 'label')) + { + $k = strval($term->label()); + } + else + { + $k = strval($term->localName()); + } + if ($term instanceof Phpspecgen_Class) + { + $classes[$k] = $term->termLink(); + $classCount++; + } + else if ($term instanceof Phpspecgen_Property) + { + $props[$k] = $term->termLink(); + $propertyCount++; + } + else if ($term instanceof Phpspecgen_ConceptScheme) + { + $schemes[$k] = $term->termLink(); + $schemeCount++; + } + else if ($term instanceof Phpspecgen_Concept) + { + $concepts[$k] = $term->termLink(); + $conceptCount++; + } + else if ($term instanceof Phpspecgen_Individual) + { + $individuals[$k] = $term->termLink(); + $individualCount++; + } + } + ksort($classes); + ksort($props); + ksort($schemes); + ksort($concepts); + ksort($individuals); + $html .= "

This vocabulary defines "; + $counts = array(); + if($classCount > 0) + { + $counts[] = ($classCount == 1 ? 'one class' : ($classCount . ' classes')); + } + if($propertyCount > 0) + { + $counts[] = ($propertyCount == 1 ? 'one property' : ($propertyCount . ' properties')); + } + if($schemeCount > 0) + { + $counts[] = ($schemeCount == 1 ? 'one concept scheme' : ($schemeCount . ' concept schemes')); + } + if($conceptCount > 0) + { + $counts[] = ($conceptCount == 1 ? 'one concept' : ($conceptCount . ' concepts')); + } + if($individualCount > 0) + { + $counts[] = ($individualCount == 1 ? 'one named individual' : ($individualCount . ' named individuals')); + } + $sub = array_slice($counts, 0, -1); + if(count($counts) > 1) + { + $last = ' and ' . array_pop($counts); + } + else + { + $last = ''; + } + $html .= implode(', ', $sub) . $last . '.'; + $html .= "

\n"; + + $html .= '
'; + if($classCount) + { + $html .= '
Classes
'; + $html .= '
' . implode(' | ', $classes) . '
'; + } + if($propertyCount) + { + $html .= '
Properties
'; + $html .= '
' . implode(' | ', $props) . '
'; + } + if($schemeCount) + { + $html .= '
Concept schemes
'; + $html .= '
' . implode(' | ', $schemes) . '
'; + } + if($conceptCount) + { + $html .= '
Concepts
'; + $html .= '
' . implode(' | ', $concepts) . '
'; + } + if($individualCount) + { + $html .= '
Named individuals
'; + $html .= '
' . implode(' | ', $individuals) . '
'; + } + $html .= '
'; +/* + $html .= "\n"; + $html .= "\n"; + foreach($this->all("^rdfs:isDefinedBy") as $term) { + if ($term instanceof Phpspecgen_Term) { + $html .= ""; + $html .= ""; + $html .= ""; + $html .= ""; + $html .= "\n"; + } + } + $html .= "
Term NameTypeDefinition
".$term->termLink()."".$term->termType()."".$term->getLiteral('rdfs:comment|rdfs:label')."
\n"; +*/ + return $html; + } + + public function htmlTerms($type, $title) { + $html = ''; + $id = strtolower(str_replace(' ','-',$title)); + $html .= "

$title

\n"; + foreach($this->all("^rdfs:isDefinedBy") as $term) { + if (!$term instanceof $type) + continue; + + $name = htmlspecialchars($term->localName()); + $html .= "

$nameget('rdfs:comment'))."

\n"; + $html .= "\n"; + $html .= " \n"; + $html .= $term->propertyRow("Label", "rdfs:label"); + $html .= $term->propertyRow("Status", "vs:term_status"); + $html .= $term->propertyRow("Equivalent to", "owl:sameAs"); + $html .= $term->propertyRow("Deprecated by", "ont:deprecatedBy"); + $html .= $term->propertyRow("Deprecates", "^ont:deprecatedBy"); + $html .= $term->propertyRow("Sub-classes", "^rdfs:subClassOf"); + $html .= $term->propertyRow("Sub-properties", "^rdfs:subPropertyOf"); + $html .= $term->propertyRow("Narrower terms", "^skos:broaderTransitive"); + $html .= $term->propertyRow("Broader terms", "skos:broaderTransitive"); + $html .= $term->propertyRow("Parent class", "rdfs:subClassOf"); + $html .= $term->propertyRow("Parent property", "rdfs:subPropertyOf"); + $html .= $term->propertyRow("Properties", "^rdfs:domain"); + if ($term instanceof Phpspecgen_Class) { + $properties = $term->inheritedProperties(); + if (count($properties) > 0) + $html .= " ". + "\n"; + } + $html .= $term->propertyRow("Range", "rdfs:range"); + $html .= $term->propertyRow("In range of", "^rdfs:range"); + $html .= $term->propertyRow("Domain", "rdfs:domain"); + $html .= $term->propertyRow('Instances', '^rdf:type'); + $html .= $term->propertyRow("See Also", "rdfs:seeAlso"); + if($term instanceof Phpspecgen_Individual || $term instanceof Phpspecgen_Concept) + { + $html .= $term->propertyRow('Class', 'rdf:type', array('http://www.w3.org/2004/02/skos/core#Concept', 'http://www.w3.org/2002/07/owl#NamedIndividual')); + } + $html .= $term->propertyRow('Concepts', 'skos:hasTopConcept'); + $html .= $term->propertyRow('Concept scheme', '^skos:hasTopConcept'); + $html .= "
URI: ".$term->htmlLink()."
Inherited Properties:".join(', ', $properties)."
\n"; + $html .= "\n"; + } + return $html; + } + + } + + # Extra namespaces we use + EasyRdf_Namespace::set('vann', 'http://purl.org/vocab/vann/'); + EasyRdf_Namespace::set('vs', 'http://www.w3.org/2003/06/sw-vocab-status/ns#'); + EasyRdf_Namespace::set('ont', 'http://purl.org/net/ns/ontology-annot#'); + + ## Add mappings + EasyRdf_TypeMapper::set('owl:Ontology', 'Phpspecgen_Vocab'); + EasyRdf_TypeMapper::set('owl:Class', 'Phpspecgen_Class'); + EasyRdf_TypeMapper::set('rdfs:Class', 'Phpspecgen_Class'); + EasyRdf_TypeMapper::set('owl:Property', 'Phpspecgen_Property'); + EasyRdf_TypeMapper::set('owl:DatatypeProperty', 'Phpspecgen_Property'); + EasyRdf_TypeMapper::set('owl:ObjectProperty', 'Phpspecgen_Property'); + EasyRdf_TypeMapper::set('owl:InverseFunctionalProperty', 'Phpspecgen_Property'); + EasyRdf_TypeMapper::set('owl:FunctionalProperty', 'Phpspecgen_Property'); + EasyRdf_TypeMapper::set('rdf:Property', 'Phpspecgen_Property'); + EasyRdf_TypeMapper::set('skos:ConceptScheme', 'Phpspecgen_ConceptScheme'); + EasyRdf_TypeMapper::set('skos:Concept', 'Phpspecgen_Concept'); + EasyRdf_TypeMapper::set('owl:NamedIndividual', 'Phpspecgen_Individual'); diff --git a/tools/easyrdf b/tools/easyrdf new file mode 160000 index 0000000..1371c8a --- /dev/null +++ b/tools/easyrdf @@ -0,0 +1 @@ +Subproject commit 1371c8af2abd3a948c50962b2212fce1912000e7 diff --git a/tools/phpspecgen.php b/tools/phpspecgen.php new file mode 100644 index 0000000..f0be86f --- /dev/null +++ b/tools/phpspecgen.php @@ -0,0 +1,58 @@ + 5) +{ + fprintf($err, "Usage: %s FILE.ttl URI [BASENAME [TEMPLATE]]\n", $argv[0]); + exit(1); +} + +$template = 'template.phtml'; +$basename = null; +if(count($argv) > 3) +{ + $basename = $argv[3]; +} +if(count($argv) > 4) +{ + $template = $argv[4]; +} + +# Extra namespaces we use +EasyRdf_Namespace::set('odrl', 'http://www.w3.org/ns/odrl/2/'); +EasyRdf_Namespace::set('oma', 'http://www.openmobilealliance.com/oma-dd/'); +EasyRdf_Namespace::set('onix', 'http://www.editeur.org/onix-pl/'); + +$uri = $argv[2]; +$graph = new EasyRdf_Graph($uri); +$graph->parseFile($argv[1], 'turtle', $uri); +$vocab = $graph->get('owl:Ontology', '^rdf:type'); +$vocab->basename = $basename; +if (!isset($vocab)) +{ + fprint($err, "%s: No OWL ontologies defined at that URL.\n", $argv[0]); + exit(1); +} +if(!file_exists($template)) +{ + fprintf($err, "%s: cannot find template '%s'\n", $argv[0], $template); + exit(1); +} + +require($template); + diff --git a/tools/phpspecgen/README.md b/tools/phpspecgen/README.md new file mode 100644 index 0000000..90eb5b1 --- /dev/null +++ b/tools/phpspecgen/README.md @@ -0,0 +1,25 @@ +phpspecgen +========== + +phpspecgen is a library for generating HTML documentation for Semantic Web based on ontology definitions in [RDFS] and [OWL]. + +It is written in PHP and uses the [EasyRdf] library. + + +Alternatives +------------ + +Some alternative RDF specification generators include: + +- http://github.com/specgen/specgen/ +- http://vocab.org/2004/03/toolchain +- http://moustaki.org/ontospec/ +- http://github.com/ldodds/dowl +- http://kantenwerk.org/vocdoc/ +- http://neologism.deri.ie/ + + +[RDFS]: http://www.w3.org/TR/rdf-schema/ +[OWL]: http://www.w3.org/TR/owl-ref/ +[EasyRdf]: http://github.com/njh/easyrdf + diff --git a/tools/phpspecgen/composer.json b/tools/phpspecgen/composer.json new file mode 100644 index 0000000..8a34786 --- /dev/null +++ b/tools/phpspecgen/composer.json @@ -0,0 +1,23 @@ +{ + "name": "njh/phpspecgen", + "description": "phpspecgen is a library for generating HTML documentation for Semantic Web based on ontology definitions in RDFS and OWL.", + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/njh/phpspecgen" + } + ], + "version": "0.1.0", + "license": "BSD-3-Clause", + "require": { + "njh/easyrdf": "0.7.0" + }, + "authors": [ + { + "name": "Nicholas Humfrey", + "email": "njh@aelius.com", + "homepage": "http://www.aelius.com/njh/", + "role": "Developer" + } + ] +} diff --git a/tools/phpspecgen/phpspecgen.php b/tools/phpspecgen/phpspecgen.php new file mode 100644 index 0000000..41efdab --- /dev/null +++ b/tools/phpspecgen/phpspecgen.php @@ -0,0 +1,272 @@ +localName()); + return "$name"; + } + + public function propertyList($property) { + $items = array(); + foreach ($this->all($property) as $value) { + if ($value instanceof Phpspecgen_Term) { + array_push($items, $value->termLink()); + } else if ($value instanceof EasyRdf_Resource) { + array_push($items, $value->htmlLink($value->shorten())); + } else { + array_push($items, strval($value)); + } + } + return $items; + } + + public function propertyRow($title, $property) { + $items = $this->propertyList($property); + if (count($items) > 0) { + return "$title: ".implode(', ', $items)."\n"; + } else { + return ''; + } + } + } + + class Phpspecgen_Class extends Phpspecgen_Term + { + public function termType() { + return 'Class'; + } + + public function inheritedProperties() { + $properties = array(); + foreach ($this->allParents() as $parent) { + foreach($parent->all('^rdfs:domain') as $property) { + if ($property instanceof Phpspecgen_Term) { + array_push($properties, $property->termLink()); + } + } + } + return $properties; + } + + protected function allParents($depth=0) { + $parents = array(); + foreach ($this->all('rdfs:subClassOf') as $parent) { + if (!$parent instanceof Phpspecgen_Class) + continue; + array_push($parents, $parent); + if ($depth < 10) + $parents = array_merge($parents, $parent->allParents($depth)); + } + return $parents; + } + } + + class Phpspecgen_Property extends Phpspecgen_Term + { + public function termType() { + return 'Property'; + } + } + + class Phpspecgen_Vocab extends EasyRdf_Resource + { + protected function propertyDefinition($title, $property) { + $values = $this->all($property); + if (count($values) < 1) + return ''; + + $html = array(); + foreach($values as $value) { + if ($value instanceof EasyRdf_Literal) { + array_push($html, htmlspecialchars($value)); + } else if ($value->get('foaf:homepage')) { + array_push($html, $value->get('foaf:homepage')->htmlLink( $value->label() )); + } else { + if ($value->isBnode()) { + array_push($html, htmlspecialchars($value->label()) ); + } else { + array_push($html, $value->htmlLink($value->label()) ); + } + } + } + + return "
$title
".implode(', ', array_unique($html))."
\n"; + } + + public function htmlHeader() { + $html = "

".htmlspecialchars($this->label())."

\n"; + $html .= "".htmlspecialchars($this->get('dc:description|dc11:description|rdfs:comment'))."\n"; + + $html .= "
\n"; + $html .= "
Latest Version
".$this->htmlLink()."
\n"; + $html .= $this->propertyDefinition('Created', 'dc:created|dc11:created'); + $html .= $this->propertyDefinition('Date', 'dc:date|dc11:date'); + $html .= $this->propertyDefinition('Revision', 'owl:versionInfo'); + $html .= $this->propertyDefinition('Authors', 'foaf:maker|dc:creator|dc11:creator'); + $html .= $this->propertyDefinition('Contributors', 'dc:contributor|dc11:contributor'); + $html .= "
\n"; + return $html; + } + + public function htmlSummaryOfTerms() { + $html = "

Summary of Terms

\n"; + $classCount = 0; + $properyCount = 0; + foreach($this->all("^rdfs:isDefinedBy") as $term) { + if ($term instanceof Phpspecgen_Class) + $classCount++; + if ($term instanceof Phpspecgen_Property) + $properyCount++; + } + $html .= "

This vocabulary defines"; + if ($classCount == 0) { + $html .= " no classes"; + } else if ($classCount == 1) { + $html .= " one class"; + } else { + $html .= " $classCount classes"; + } + if ($properyCount == 0) { + $html .= " and no properties."; + } else if ($properyCount == 1) { + $html .= " and one property."; + } else { + $html .= " and $properyCount properties."; + } + $html .= "

\n"; + + $html .= "\n"; + $html .= "\n"; + foreach($this->all("^rdfs:isDefinedBy") as $term) { + if ($term instanceof Phpspecgen_Term) { + $html .= ""; + $html .= ""; + $html .= ""; + $html .= ""; + $html .= "\n"; + } + } + $html .= "
Term NameTypeDefinition
".$term->termLink()."".$term->termType()."".$term->getLiteral('rdfs:comment|rdfs:label')."
\n"; + return $html; + } + + public function htmlTerms($type, $title) { + $html = ''; + $id = strtolower(str_replace(' ','-',$title)); + $html .= "

$title

\n"; + foreach($this->all("^rdfs:isDefinedBy") as $term) { + if (!$term instanceof $type) + continue; + + $name = htmlspecialchars($term->localName()); + $html .= "

$nameget('rdfs:comment'))."

\n"; + $html .= "\n"; + $html .= " \n"; + $html .= $term->propertyRow("Label", "rdfs:label"); + $html .= $term->propertyRow("Status", "vs:term_status"); + $html .= $term->propertyRow("Subclasses", "^rdfs:subClassOf"); + $html .= $term->propertyRow("Parent Class", "rdfs:subClassOf"); + $html .= $term->propertyRow("Properties", "^rdfs:domain"); + if ($term instanceof Phpspecgen_Class) { + $properties = $term->inheritedProperties(); + if (count($properties) > 0) + $html .= " ". + "\n"; + } + $html .= $term->propertyRow("Range", "rdfs:range"); + $html .= $term->propertyRow("Domain", "rdfs:domain"); + $html .= $term->propertyRow("See Also", "rdfs:seeAlso"); + $html .= "
URI: ".$term->htmlLink()."
Inherited Properties:".join(', ', $properties)."
\n"; + $html .= "\n"; + } + return $html; + } + + } + + # Extra namespaces we use + EasyRdf_Namespace::set('vann', 'http://purl.org/vocab/vann/'); + EasyRdf_Namespace::set('vs', 'http://www.w3.org/2003/06/sw-vocab-status/ns#'); + + ## Add mappings + EasyRdf_TypeMapper::set('owl:Ontology', 'Phpspecgen_Vocab'); + EasyRdf_TypeMapper::set('owl:Class', 'Phpspecgen_Class'); + EasyRdf_TypeMapper::set('rdfs:Class', 'Phpspecgen_Class'); + EasyRdf_TypeMapper::set('owl:Property', 'Phpspecgen_Property'); + EasyRdf_TypeMapper::set('owl:DatatypeProperty', 'Phpspecgen_Property'); + EasyRdf_TypeMapper::set('owl:ObjectProperty', 'Phpspecgen_Property'); + EasyRdf_TypeMapper::set('owl:InverseFunctionalProperty', 'Phpspecgen_Property'); + EasyRdf_TypeMapper::set('owl:FunctionalProperty', 'Phpspecgen_Property'); + EasyRdf_TypeMapper::set('rdf:Property', 'Phpspecgen_Property'); +?> + + + phpspecgen + + + + +load($uri); + + // Get the first ontology in the document + $vocab = $graph->get('owl:Ontology', '^rdf:type'); + if (!isset($vocab)) { + print "

Error: No OWL ontologies defined at that URL.

\n"; + } else { + // FIXME: register the preferredNamespacePrefix + + print $vocab->htmlHeader(); + print $vocab->htmlSummaryOfTerms(); + print $vocab->htmlTerms('Phpspecgen_Class', 'Classes'); + print $vocab->htmlTerms('Phpspecgen_Property', 'Properties'); + + print $graph->dump(); + } + + } else { + $examples = array( + 'FOAF' => 'http://xmlns.com/foaf/spec/', + 'DOAP' => 'http://usefulinc.com/ns/doap#', + 'LODE' => 'http://linkedevents.org/ontology/', + 'Ordered List Ontology' => 'http://purl.org/ontology/olo/core#', + 'Whisky Vocabulary' => 'http://vocab.org/whisky/terms.rdf', + 'Sport Ontology' => 'http://www.bbc.co.uk/ontologies/sport/2011-02-17.rdf', + 'Music Ontology' => 'http://purl.org/ontology/mo/', + 'Programme Ontology' => 'http://www.bbc.co.uk/ontologies/programmes/2009-09-07.rdf' + ); + + print "

phpspecgen

\n"; + print "
"; + print "
\n"; + print "\n"; + print "\n"; + print "
\n"; + print "

Or pick an example:

\n"; + print "\n"; + + } +?> + + diff --git a/tools/phpspecgen/style.css b/tools/phpspecgen/style.css new file mode 100644 index 0000000..2107bb9 --- /dev/null +++ b/tools/phpspecgen/style.css @@ -0,0 +1,116 @@ + code.xml .text { + color: #000000; + background: transparent; + } + code.xml .elem { + color: #000080; + background: transparent; + } + code.xml .attr { + color: #008080; + background: transparent; + } + code.xml .attrVal { + color: #666666; + background: transparent; + } + code.xml .highlight { + background: #ffff00; + } + + h1, h2, h3, h4, h5, h6 { + font-family: Georgia, "Times New Roman", Times, serif; + font-style:italic; + } + + pre { + border: 1px #9999cc dotted; + background-color: #f3f3ff; + color: #000000; + width: 90%; + } + + blockquote { + border-style: solid; + border-color: #d0dbe7; + border-width: 0 0 0 .25em; + padding-left: 0.5em; + } + + blockquote, q { + font-style: italic; + } + + body { + font-family: verdana, geneva, arial, sans-serif; + } + + a, p,blockquote, q, dl, dd, dt { + font-family: verdana, geneva, arial, sans-serif; + font-size: 1em; + } + + + dt { + font-weight: bold; + } + + +:link { color: #00C; background: transparent } +:visited { color: #609; background: transparent } +:link:active, :visited:active { color: #C00; background: transparent } +:link:hover, :visited:hover { background: #ffa; } +code :link, code :visited { color: inherit; } + +h1, h2, h3, h4, h5, h6 { text-align: left } +h1, h2, h3 { color: #996633; background: transparent; } +h1 { font: 900 170% sans-serif; border-bottom: 1px solid gray; } +h2 { font: 800 140% sans-serif; border-bottom: 1px solid gray; } +h3 { font: 700 120% sans-serif } +h4 { font: bold 100% sans-serif } +h5 { font: italic 100% sans-serif } +h6 { font: small-caps 100% sans-serif } + +body { padding: 0 4em 2em 4em; line-height: 1.35; } + +pre { margin-left: 2em; /* overflow: auto; */ } +h1 + h2 { margin-top: 0; } +h2 { margin: 3em 0 1em 0; } +h2 + h3 { margin-top: 0; } +h3 { margin: 2em 0 1em 0; } +h4 { margin: 1.5em 0 0.75em 0; } +h5, h6 { margin: 1.5em 0 1em; } +p { margin: 1em 0; } +dl, dd { margin-top: 0; margin-bottom: 0; } +dt { margin-top: 0.75em; margin-bottom: 0.25em; clear: left; } +dd dt { margin-top: 0.25em; margin-bottom: 0; } +dd p { margin-top: 0; } +p + * > li, dd li { margin: 1em 0; } +dt, dfn { font-weight: bold; font-style: normal; } +pre, code { font-size: inherit; font-family: monospace; } +pre strong { color: black; font: inherit; font-weight: bold; background: yellow; } +pre em { font-weight: bolder; font-style: normal; } +var sub { vertical-align: bottom; font-size: smaller; position: relative; top: 0.1em; } +blockquote { margin: 0 0 0 2em; border: 0; padding: 0; font-style: italic; } +ins { background: green; color: white; /* color: green; border: solid thin lime; padding: 0.3em; line-height: 1.6em; */ text-decoration: none; } +del { background: maroon; color: white; /* color: maroon; border: solid thin red; padding: 0.3em; line-height: 1.6em; */ text-decoration: line-through; } +body ins, body del { display: block; } +body * ins, body * del { display: inline; } + +table.properties { width: 90%; } +table.properties th { text-align: right; width: 9em; font-weight: normal; } + +table { border-collapse: collapse; border: solid #999999 1px;} +table thead { border-bottom: solid #999999 2px; } +table td, table th { + border-left: solid #999999 1px; + border-right: solid #999999 1px; + border-bottom: solid #999999 1px; + vertical-align: top; + padding: 0.2em; +} + +.historyList { + font-size: 0.9em; +} +.termuri { margin-bottom: 0.5em;}