PageRenderTime 92ms CodeModel.GetById 3ms app.highlight 78ms RepoModel.GetById 1ms app.codeStats 1ms

/lib.old/generate.php

https://github.com/lbod/api-viewer
PHP | 1356 lines | 1100 code | 116 blank | 140 comment | 234 complexity | ed8472c28f351096dd607b1ccf71658c MD5 | raw file
   1<?php
   2/*	generate_html.php
   3 *	TRT 2010-02-03
   4 *
   5 *	Given the page and version, generate the HTML fragment
   6 *	and return it.
   7 */
   8
   9include("markdown/markdown.php");
  10function convert_type($type){
  11	$base = 'object';
  12	switch($type){
  13		case 'Namespace':
  14		case 'namespace': $base='namespace'; break;
  15		case 'Constructor': $base='constructor'; break;
  16		case 'Node':
  17		case 'DOMNode':
  18		case 'DomNode':   $base='domnode'; break;
  19		case 'Array':   $base='array'; break;
  20		case 'Boolean':   $base='boolean'; break;
  21		case 'Date':    $base='date'; break; 
  22		case 'Error':     $base='error'; break;
  23		case 'Function':  $base='function'; break;
  24		case 'Integer':
  25		case 'Float':
  26		case 'int':
  27		case 'Double':
  28		case 'integer':
  29		case 'Number':    $base='number'; break;   
  30		case 'RegExp':    $base='regexp'; break;
  31		case 'String':    $base='string'; break;
  32		default:      $base='object'; break;
  33	}
  34	return $base;
  35}
  36
  37function icon_url($type, $size=16){
  38	$img = "object";
  39	switch($type){
  40		case 'Namespace':
  41		case 'namespace': $img='namespace'; break;
  42		case 'Constructor': $img='constructor'; break;
  43		case 'Node':
  44		case 'DOMNode':
  45		case 'DomNode':   $img='domnode'; break;
  46		case 'Array':   $img='array'; break;
  47		case 'Boolean':   $img='boolean'; break;
  48		case 'Date':    $img='date'; break; 
  49		case 'Error':     $img='error'; break;
  50		case 'Function':  $img='function'; break;
  51		case 'Integer':
  52		case 'Float':
  53		case 'int':
  54		case 'Double':
  55		case 'integer':
  56		case 'Number':    $img='number'; break;   
  57		case 'RegExp':    $img='regexp'; break;
  58		case 'String':    $img='string'; break;
  59		default:      $img='object'; break;
  60	}
  61	return 'css/icons/' . $size . 'x' . $size . '/' . $img . '.png';
  62}
  63
  64function do_markdown($text){
  65	//	prep the text and run it through the Markdown parser.
  66	$lines = explode("\n", $text);
  67	$fixed = array();
  68	$b = false;
  69	foreach($lines as $line){
  70		//	 pull off the preceding tab.
  71		$s = $line;
  72		if(strpos($line, "\t")===0){ $s = substr($s, 1); }
  73
  74		//	deal with the munging of lists in the markdown.
  75		if(preg_match('/(\t)*\*/', $s)){
  76			if(!$b){
  77				$b = true;
  78				$s = "\n" . $s;
  79			}
  80		} else {
  81			$b = false;
  82		}
  83
  84		$fixed[] = $s;
  85	}
  86	$str = Markdown(implode("\n", $fixed));
  87	return $str;
  88}
  89
  90function format_example($text){
  91	//	do this for SyntaxHighlighter use.
  92	$s = ""; // */ "\n<!--\n" . $text . "\n-->\n";
  93	//	insert an additional tab if the first character is a tab.
  94	if(strpos($text, "\t")===0){
  95		$text = "\t" . $text;
  96	}
  97	$lines = explode("\n", "\n" . $text);
  98	$isCode = false;
  99	foreach($lines as &$line){
 100		if(strpos($line, "\t")===0){
 101			$line = htmlentities(substr($line, 1));	//	always pull off the first tab.
 102		}
 103		if(strpos($line, "\t")===0){
 104			if(!$isCode){
 105				$isCode = true;
 106				$line = '<pre class="brush: js;" lang="javascript">' . "\n" . $line;
 107			}
 108		} else {
 109			if($isCode){
 110				$isCode = false;
 111				$line .= '</pre>';
 112			}
 113		}
 114	}
 115	if($isCode){
 116		//	probably we never saw the last line, or the last line was code.
 117		$lines[] = '</pre>';
 118	}
 119	return $s . implode("\n", $lines);
 120}
 121
 122//	BEGIN array_filter functions
 123function is_event($item){
 124	$public = strpos($item["name"], "on");
 125	$private = strpos($item["name"], "_on");
 126	return $public === 0 || $private === 0;
 127}
 128function is_method($item){
 129	$public = strpos($item["name"], "on");
 130	$private = strpos($item["name"], "_on");
 131	return $public !== 0 && $private !== 0;
 132}
 133function is_static($item){
 134	return $item["scope"] == "normal";
 135}
 136function is_not_static($item){
 137	return $item["scope"] != "normal";
 138}
 139
 140function is_not_from_Object($item){
 141	return !(count($item["defines"]) == 1 && $item["defines"][0] == "Object");
 142}
 143
 144function is_not_node($item){
 145	$obj = strtolower($item);
 146	return !strpos($obj, "node") && !strpos($obj, "style") && !strpos($obj, "__");
 147}
 148//	END array_filter functions
 149
 150function load_docs($version){
 151	//	helper function to load up the XML docs we need.
 152	global $dataDir;
 153	$data_dir = $dataDir . $version . "/";
 154
 155	//	load up the doc.
 156	$provides = "provides.xml";
 157	$resources = "resources.xml";
 158	$details = "details.xml";
 159	$f = $data_dir . $details;
 160
 161	if(!file_exists($f)){
 162		echo "API data does not exist for the version: " . $version . "<br/>";
 163		exit();
 164	}
 165
 166	$xml = new DOMDocument();
 167	$xml->load($f);
 168
 169	$p_xml = new DOMDocument();
 170	$p_xml->load($data_dir . $provides);
 171
 172	$r_xml = new DOMDocument();
 173	$r_xml->load($data_dir . $resources);
 174
 175	$xpath = new DOMXPath($xml);
 176	$p_xpath = new DOMXPath($p_xml);
 177	$r_xpath = new DOMXPath($r_xml);
 178
 179	$docs = array(
 180		"xml"=>$xml,
 181		"p_xml"=>$p_xml,
 182		"r_xml"=>$r_xml,
 183		"xpath"=>$xpath,
 184		"p_xpath"=>$p_xpath,
 185		"r_xpath"=>$r_xpath
 186	);
 187	return $docs;
 188}
 189
 190function read_object_fields($page, $version, $docs=array()){
 191	//	for the given page, grab any mixins and assemble a "flat" version of it (no prototype walking).
 192	//	create a PHP-based associative array structure out of the page in question.
 193	if(!count($docs)){
 194		$docs = load_docs($version);
 195	}
 196
 197	$xml = $docs["xml"];
 198	$p_xml = $docs["p_xml"];
 199	$r_xml = $docs["r_xml"];
 200	$xpath = $docs["xpath"];
 201	$p_xpath = $docs["p_xpath"];
 202	$r_xpath = $docs["r_xpath"];
 203
 204	//	get the XML for the page.
 205	$context = $xpath->query('//object[@location="' . $page . '"]')->item(0);
 206	if(!$context){
 207		//	we got nothing, just return null.
 208		return null;
 209	}
 210
 211	//	get any mixins, and ignore if the mixin == superclass.  Note that we're going to ignore any prototype mixins,
 212	//	as they are (in general) applied in the same way as instance mixins.
 213	$mixinNodes = $xpath->query('mixins/mixin[@scope="instance"]', $context);
 214	$mixins = array();
 215	foreach($mixinNodes as $m){
 216		//	test 1: make sure the mixin is not the superclass.
 217		if($m->getAttribute("location") == $context->getAttribute("superclass")){
 218			continue;
 219		}
 220		//	test 2: make sure we can actually read the mixin definition
 221		$m_test = $xpath->query("//object[@location='" . $m->getAttribute("location") . "']");
 222		if($m_test->length){
 223			$mixins[$m->getAttribute("location")] = $m_test->item(0);
 224		}
 225	}
 226
 227	//	push in our page.
 228	$mixins[$page] = $context;
 229	$props = array();
 230	$methods = array();
 231
 232	foreach($mixins as $location=>$node){
 233		//	properties
 234		$nl = $xpath->query("properties/property", $node);
 235		foreach($nl as $n){
 236			$nm = $n->getAttribute("name");
 237			$private = $n->getAttribute("private") == "true";
 238			if(!$private && strpos($nm, "_")===0){
 239				$private = true;
 240			}
 241			if(array_key_exists($nm, $props)){
 242				//	next one up in the chain overrides the original.
 243				$props[$nm]["scope"] = $n->getAttribute("scope");
 244				$props[$nm]["type"] = $n->getAttribute("type");
 245				$props[$nm]["override"] = true;
 246				$props[$nm]["defines"][] = $location;
 247			} else {
 248				$props[$nm] = array(
 249					"name"=>$nm,
 250					"scope"=>$n->getAttribute("scope"),
 251					"visibility"=>($private == true ? "private" : "public"),
 252					"type"=>$n->getAttribute("type"),
 253					"defines"=>array($location),
 254					"override"=>false
 255				);
 256			}
 257
 258			if($n->getElementsByTagName("summary")->length){
 259				$desc = trim($n->getElementsByTagName("summary")->item(0)->nodeValue);
 260				if(strlen($desc)){
 261					$props[$nm]["summary"] = $desc;
 262				}
 263			}
 264			if($n->getElementsByTagName("description")->length){
 265				$desc = trim($n->getElementsByTagName("description")->item(0)->nodeValue);
 266				if(strlen($desc)){
 267					$props[$nm]["description"] = do_markdown($desc);
 268				}
 269			}
 270		}
 271
 272		//	methods
 273		$nl = $xpath->query("methods/method", $node);
 274		foreach($nl as $n){
 275			$nm = $n->getAttribute("name");
 276			$private = $n->getAttribute("private") == "true";
 277			if(!$private && strpos($nm, "_")===0){
 278				$private = true;
 279			}
 280			if(!strlen($nm)){
 281				$nm = "constructor";
 282			}
 283			if(array_key_exists($nm, $methods)){
 284				//	next one up in the chain overrides the original.
 285				$methods[$nm]["scope"] = $n->getAttribute("scope");
 286				$methods[$nm]["override"] = true;
 287				$methods[$nm]["defines"][] = $location;
 288				if($n->getAttribute("constructor") == "constructor"){
 289					$methods[$nm]["constructor"] = true;
 290					$methods[$nm]["scope"] = "prototype";
 291				}
 292			} else {
 293				$methods[$nm] = array(
 294					"name"=>$nm,
 295					"scope"=>$n->getAttribute("scope"),
 296					"visibility"=>($private=="true"?"private":"public"),
 297					"parameters"=>array(),
 298					"return-types"=>array(),
 299					"defines"=>array($location),
 300					"override"=>false,
 301					"constructor"=>$n->getAttribute("constructor")=="constructor"
 302				);
 303			}
 304
 305			if($n->getElementsByTagName("summary")->length){
 306				$desc = trim($n->getElementsByTagName("summary")->item(0)->nodeValue);
 307				if(strlen($desc)){
 308					$methods[$nm]["summary"] = $desc;
 309				}
 310			}
 311			if($n->getElementsByTagName("description")->length){
 312				$desc = trim($n->getElementsByTagName("description")->item(0)->nodeValue);
 313				if(strlen($desc)){
 314					$methods[$nm]["description"] = do_markdown($desc);
 315				}
 316			}
 317			$ex = $n->getElementsByTagName("example");
 318			if($ex->length){
 319				if(!array_key_exists("examples", $methods[$nm])){
 320					$methods[$nm]["examples"] = array();
 321				}
 322				foreach($ex as $example){
 323					$methods[$nm]["examples"][] = $example->nodeValue;
 324				}
 325			}
 326			if($n->getElementsByTagName("return-description")->length){
 327				$desc = trim($n->getElementsByTagName("return-description")->item(0)->nodeValue);
 328				if(strlen($desc)){
 329					$methods[$nm]["return-description"] = $desc;
 330				}
 331			}
 332
 333			//	do up the parameters and the return types.
 334			$params = $xpath->query("parameters/parameter", $n);
 335			if($params->length){
 336				//	TODO: double-check that the XML will always have this.
 337				$methods[$nm]["parameters"] = array();
 338				foreach($params as $param){
 339					$item = array(
 340						"name"=>$param->getAttribute("name"),
 341						"type"=>$param->getAttribute("type"),
 342						"usage"=>$param->getAttribute("usage"),
 343						"description"=>""
 344					);
 345					if($param->getElementsByTagName("summary")->length){
 346						$desc = trim($param->getElementsByTagName("summary")->item(0)->nodeValue);
 347						if(strlen($desc)){
 348							$item["description"] = $desc;
 349						}
 350					}
 351					$methods[$nm]["parameters"][] = $item;
 352				}
 353			}
 354
 355			if($nm == "constructor"){
 356				$methods[$nm]["return-types"] = array();
 357				$methods[$nm]["return-types"][] = array(
 358					"type"=>$location,
 359					"description"=>""
 360				);
 361			} else {
 362				$rets = $xpath->query("return-types/return-type", $n);
 363				if($rets->length){
 364					//	TODO: double-check that the XML will always have this.
 365					$methods[$nm]["return-types"] = array();
 366					foreach($rets as $ret){
 367						$item = array(
 368							"type"=>$ret->getAttribute("type"),
 369							"description"=>""
 370						);
 371						$methods[$nm]["return-types"][] = $item;
 372					}
 373				}
 374			}
 375		}
 376	}
 377	return array("props"=>$props, "methods"=>$methods);
 378}
 379
 380function get_Object_fields(){
 381	//	simple helper function to return a fake generated prop/method set for Object.
 382	$props = array();
 383	$methods = array();
 384
 385	//	no properties other than the constructor on an object, so we'll just return that as an empty array.
 386	$native = array("constructor", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString", "toString", "valueOf");
 387	$returns = array("Object", "Boolean", "Boolean", "Boolean", "String", "String", "Object");
 388	$summaries = array(
 389		"A reference to the constructor function for this object.",
 390		"Checks whether an object has a locally defined (noninherited) property with a specified name.",
 391		"Check whether this object is the prototype object of a specified object.",
 392		"Checks whether a named property exists and would be enumerated by a for/in loop.",
 393		"Returns a localized string representation of the object.",
 394		"Returns a string representation of the object.",
 395		"Returns the primitve value of the object, if any."
 396	);
 397
 398	foreach($native as $i=>$nm){
 399		$methods[$nm] = array(
 400			"name"=>$nm,
 401			"scope"=>"prototype",
 402			"visibility"=>"public",
 403			"parameters"=>array(),
 404			"return-types"=>array(),
 405			"defines"=>array("Object"),
 406			"override"=>false,
 407			"summary"=>$summaries[$i]
 408		);
 409		$methods[$nm]["return-types"][] = array(
 410			"type"=>$returns[$i],
 411			"description"=>""
 412		);
 413		if($nm == "hasOwnProperty" || $nm == "propertyIsEnumerable"){
 414			$methods[$nm]["parameters"][] = array(
 415				"name"=>"propname",
 416				"type"=>"String",
 417				"usage"=>"",
 418				"description"=>"The property to check."
 419			);
 420		}
 421		else if($nm == "isPrototypeOf"){
 422			$methods[$nm]["parameters"][] = array(
 423				"name"=>"o",
 424				"type"=>"Object",
 425				"usage"=>"",
 426				"description"=>"The object to check against."
 427			);
 428		}
 429	}
 430
 431	return array("props"=>$props, "methods"=>$methods);
 432}
 433
 434function generate_object($page, $version, $docs=array()){
 435	//	create a PHP-based associative array structure out of the page in question.
 436	if(!count($docs)){
 437		$docs = load_docs($version);
 438	}
 439
 440	$xml = $docs["xml"];
 441	$p_xml = $docs["p_xml"];
 442	$r_xml = $docs["r_xml"];
 443	$xpath = $docs["xpath"];
 444	$p_xpath = $docs["p_xpath"];
 445	$r_xpath = $docs["r_xpath"];
 446
 447	//	get the XML for the page.
 448	$context = $xpath->query('//object[@location="' . $page . '"]')->item(0);
 449	if(!$context){
 450		//	we got nothing, just return null.
 451		return null;
 452	}
 453
 454	//	ok, we have a context, let's build up our object.
 455	$obj = array();
 456
 457	//	BEGIN OBJECT GRAPH ASSEMBLY.
 458	//	provides
 459	$test = $p_xpath->query('//object[@location="' . $page . '"]/provides/provide');
 460	if($test && $test->length == 1){
 461		$obj["require"] = $test->item(0)->nodeValue;
 462	}
 463
 464	//	resources
 465	$test = $r_xpath->query('//object[@location="' . $page . '"]/resources/resource');
 466	if($test && $test->length == 1){
 467		$obj["resource"] = $test->item(0)->nodeValue;
 468	}
 469
 470	//	basic information.
 471	$is_constructor = ($context->getAttribute("type")=="Function" && $context->getAttribute("classlike")=="true");
 472	$nl = $xpath->query('//object[starts-with(@location, "' . $page . '.") and not(starts-with(substring-after(@location, "' . $page . '."), "_"))]');
 473	$is_namespace = ($nl->length > 0);
 474	$type = $context->getAttribute("type");
 475	if(!strlen($type)){ $type = 'Object'; }
 476	if($is_constructor){ $type = 'Constructor'; }
 477//	if($is_namespace){ $type = 'Namespace'; }
 478
 479	$obj["type"] = $type;
 480	$obj["title"] = $context->getAttribute("location");
 481	$obj["version"] = $version;
 482
 483	//	the prototype chain
 484	$bc = array($context->getAttribute("location"));
 485	$node = $context;
 486	while($node && $node->getAttribute("superclass")){
 487		$sc = $node->getAttribute("superclass");
 488		$bc[] = $sc;
 489		$node = $xpath->query('//object[@location="' . $sc . '"]')->item(0);
 490	}
 491	$bc[] = "Object";
 492	$bc = array_reverse($bc);
 493
 494	//	note that this is "in order"; used to either fetch other objects or for something like breadcrumbs.
 495	$obj["prototypes"] = $bc;
 496
 497	//	description.  Actual description node first, fall back to summary if needed.
 498	$desc = $xpath->query("description/text()", $context)->item(0);
 499	if(!$desc){ $desc = $xpath->query("summary/text()", $context)->item(0); }
 500	if($desc){ $obj["description"] = $desc->nodeValue; }
 501
 502	//	mixins
 503	//	examples.
 504	$examples = $xpath->query("examples/example", $context);
 505	if($examples->length > 0){
 506		$obj["examples"] = array();
 507		foreach($examples as $example){
 508			$obj["examples"][] = $example->nodeValue;
 509		}
 510	}
 511
 512	//	now it gets ugly.  We need to go get all the properties and methods of ourselves,
 513	//	plus anything in the prototype chain (i.e. superclass), PLUS anything in the mixins list,
 514	//	and merge them all together, AND make sure they are unique.  On top of that, we need
 515	//	to make sure we're getting that info from the top to the bottom.
 516	$obj["mixins"] = array();
 517	$obj["properties"] = array();
 518	$obj["methods"] = array();
 519
 520	//	start with getting the mixins.
 521	$nl = $xpath->query("mixins/mixin[@scope='instance']", $context);
 522	foreach($nl as $m){
 523		//	again, this is ugly.
 524		$m_test = $xpath->query("//object[@location='" . $m->getAttribute("location") . "']");
 525		if($m_test->length){
 526			$obj["mixins"][] = $m->getAttribute("location");
 527		}
 528	}
 529
 530	//	ok.  Walk the prototype chain from one to another, and get the list of props and methods for all.
 531	$protos = array();
 532	foreach($bc as $ancestor){
 533		if($ancestor == "Object"){
 534			$protos[$ancestor] = get_Object_fields();
 535		} else {
 536			$protos[$ancestor] = read_object_fields($ancestor, $version, $docs);
 537		}
 538	}
 539
 540	//	Now that we have the complete prototype chain, merge everything and include override info if needed.
 541	$props = array();
 542	$methods = array();
 543	foreach($protos as $_object=>$proto){
 544		if($proto && array_key_exists("props", $proto)){
 545			foreach($proto["props"] as $nm=>$prop){
 546				if(array_key_exists($nm, $props)){
 547					//	next one up in the chain overrides the original.
 548					$props[$nm]["override"] = true;
 549					$props[$nm]["defines"][] = $_object;
 550					if(isset($prop["summary"])){
 551						$prop[$nm]["summary"] = $prop["summary"];
 552					}
 553					if(isset($prop["description"])){
 554						$prop[$nm]["description"] = $prop["description"];
 555					}
 556				} else {
 557					$props[$nm] = $prop;
 558				}
 559			}
 560		}
 561		if($proto && array_key_exists("methods", $proto)){
 562			foreach($proto["methods"] as $nm=>$method){
 563				if(array_key_exists($nm, $methods)){
 564					//	next one up in the chain overrides the original.
 565					$methods[$nm]["override"] = true;
 566					$methods[$nm]["defines"][] = $_object;
 567					$methods[$nm]["scope"] = $method["scope"];
 568					if($nm == "constructor"){
 569						$methods[$nm]["return-types"][0]["type"] = $_object;
 570					}
 571					if(count($method["parameters"])){
 572						$methods[$nm]["parameters"] = $method["parameters"];
 573					}
 574					if(isset($method["summary"])){
 575						$methods[$nm]["summary"] = $method["summary"];
 576					}
 577					if(isset($method["description"])){
 578						$methods[$nm]["description"] = $method["description"];
 579					}
 580				} else {
 581					$methods[$nm] = $method;
 582				}
 583			}
 584		}
 585	}
 586
 587	//	sort the props and methods correctly (alphabetical order).
 588	$methods = array_filter($methods, "is_not_from_Object");
 589	$events = array_filter($methods, "is_event");
 590	$methods = array_filter($methods, "is_method");
 591
 592	//	put any normal scope (i.e. attached directly) first.  Note that we only want
 593	//	the ones attached directly to our page, and nothing from the inheritance chain.
 594	$static = array_filter($props, "is_static");
 595	$not_static = array_filter($props, "is_not_static");
 596	$tmp = array();
 597	foreach($static as $nm=>$field){
 598		if($field["defines"][0] == $page){
 599			$tmp[$page . "." . $nm] = $field;
 600		}
 601	}
 602	ksort($tmp);
 603	ksort($not_static);
 604	$props = array_merge($tmp, $not_static);
 605
 606	$static = array_filter($methods, "is_static");
 607	$not_static = array_filter($methods, "is_not_static");
 608	$tmp = array();
 609	foreach($static as $nm=>$field){
 610		if($field["defines"][0] == $page){
 611			$tmp[$page . "." . $nm] = $field;
 612		}
 613	}
 614	ksort($tmp);
 615	ksort($not_static);
 616	$methods = array_merge($tmp, $not_static);
 617
 618	$static = array_filter($events, "is_static");
 619	$not_static = array_filter($events, "is_not_static");
 620	$tmp = array();
 621	foreach($static as $nm=>$field){
 622		if($field["defines"][0] == $page){
 623			$tmp[$page . "." . $nm] = $field;
 624		}
 625	}
 626	ksort($tmp);
 627	ksort($not_static);
 628	$events = array_merge($tmp, $not_static);
 629
 630	$obj["properties"] = $props;
 631	$obj["methods"] = $methods;
 632	$obj["events"] = $events;
 633
 634	//	attached objects.  Try to filter out the craptacular ones.
 635	$children = $xpath->query('//object[starts-with(@location, "' . $page . '.")]');
 636	$attached = array();
 637	if($children->length){
 638		foreach($children as $node){
 639			$attached[] = $node->getAttribute("location");
 640		}
 641		$attached = array_filter($attached, "is_not_node");
 642		$obj["attached"] = $attached;
 643	}
 644
 645	return $obj;	
 646}
 647
 648///////////////////////////////////////////////////////////////////////////////////////////////
 649//
 650//	BEGIN HTML OUTPUT GENERATION
 651//
 652///////////////////////////////////////////////////////////////////////////////////////////////
 653
 654//	private functions for pieces
 655function _generate_property_output($prop, $name, $docs = array(), $counter = 0, $base_url = "", $suffix = ""){
 656	//	create the HTML strings for a single property
 657	$s = '<li class="' . convert_type($prop["type"]) . 'Icon '
 658		. (isset($prop["visibility"]) ? $prop["visibility"] : 'public') . ' '
 659		. (isset($prop["defines"]) && count($prop["defines"]) && !$prop["override"] ? 'inherited':'')
 660		. ($counter % 2 == 0 ? ' even':' odd')
 661		. '">'
 662		. '<a class="inline-link" href="#' . $name . '">'
 663		. $name
 664		. '</a>';
 665	$details = '<div class="jsdoc-field '
 666		. (isset($prop["visibility"]) ? $prop["visibility"] : 'public') . ' '
 667		. (isset($prop["defines"]) && count($prop["defines"]) && !$prop["override"] ? 'inherited':'')
 668		. ($counter % 2 == 0 ? ' even':' odd')
 669		. '">'
 670		. '<div class="jsdoc-title">'
 671		. '<a name="' . $name . '"></a>'
 672		. '<span class="' . convert_type($prop["type"]) . 'Icon">'
 673		. $name
 674		. '</span>'
 675		. '</div>';
 676
 677	//	inheritance list.
 678	if(isset($prop["defines"]) && count($prop["defines"])){
 679		$tmp = array();
 680		foreach($prop["defines"] as $def){
 681			$tmp[] = '<a class="jsdoc-link" href="' . $base_url . implode("/", explode(".", $def)) . $suffix . '">'
 682				. $def
 683				. '</a>';
 684		}
 685		if($prop["override"]){
 686			array_pop($tmp);
 687		}
 688		$details .= '<div class="jsdoc-inheritance">'
 689			. ($prop["override"] ? "Overrides ":"Defined by ")
 690			. implode(", ", $tmp)
 691			. '</div>';
 692	}
 693	if(array_key_exists("description", $prop)){
 694		$details .= '<div class="jsdoc-summary">' . $prop["description"] . '</div>';
 695	} else if(array_key_exists("summary", $prop)){
 696		$details .= '<div class="jsdoc-summary">' . $prop["summary"] . '</div>';
 697	}
 698	if(array_key_exists("summary", $prop)){
 699		$s .= ' <span>' . $prop["summary"] . '</span>';
 700	}
 701	$s .= '</li>';	//	jsdoc-title
 702	$details .= '</div>';	//	jsdoc-field
 703	return array("s"=>$s, "details"=>$details);
 704}
 705
 706function _generate_method_output($method, $name, $docs = array(), $counter = 0, $base_url = "", $suffix = ""){
 707	//	create the HTML strings for a single method.
 708	$s = '<li class="functionIcon '
 709		. (isset($method["visibility"]) ? $method["visibility"] : 'public') . ' '
 710		. (isset($method["defines"]) && count($method["defines"]) && !$method["override"] ? 'inherited':'')
 711		. ($counter % 2 == 0 ? ' even':' odd')
 712		. '">'
 713		. '<a class="inline-link" href="#' . $name . '">'
 714		. $name
 715		. '</a>';
 716	$details = '<div class="jsdoc-field '
 717		. (isset($method["visibility"]) ? $method["visibility"] : 'public') . ' '
 718		. (isset($method["defines"]) && count($method["defines"]) && !$method["override"] ? 'inherited':'')
 719		. ($counter % 2 == 0 ? ' even':' odd')
 720		. '">'
 721		. '<div class="jsdoc-title">'
 722		. '<a name="' . $name . '"></a>'
 723		. '<span class="functionIcon">'
 724		. $name
 725		. '</span>'
 726		. '</div>';
 727	if(count($method["parameters"])){
 728		$tmp = array();
 729		foreach($method["parameters"] as $p){
 730			$tmp[] = $p["name"];
 731		}
 732		$s .= '<span class="parameters">('
 733			. implode(', ', $tmp)
 734			. ')</span>';
 735	} else {
 736		$s .= '<span class="parameters">()</span>';
 737	}
 738
 739	if(count($method["return-types"])){
 740		$tmp = array();
 741		foreach($method["return-types"] as $rt){
 742			$tmp[] = $rt["type"];
 743		}
 744		$s .= '<span class="jsdoc-returns"> returns ' . implode("|", $tmp) . '</span>';
 745	}
 746
 747	//	inheritance list.
 748	if(isset($method["defines"]) && count($method["defines"])){
 749		$tmp = array();
 750		foreach($method["defines"] as $def){
 751			$tmp[] = '<a class="jsdoc-link" href="' . $base_url . implode("/", explode(".", $def)) . $suffix . '">'
 752				. $def
 753				. '</a>';
 754		}
 755		if($method["override"]){
 756			array_pop($tmp);
 757		}
 758		$details .= '<div class="jsdoc-inheritance">'
 759			. ($method["override"] ? "Overrides ":"Defined by ")
 760			. implode(", ", $tmp) 
 761			. '</div>';	//	jsdoc-inheritance
 762	}
 763
 764	if(count($method["return-types"])){
 765		$tmp = array();
 766		foreach($method["return-types"] as $rt){
 767			$tmp[] = $rt["type"];
 768		}
 769		$details .= '<div class="jsdoc-return-type">Returns '
 770			. '<strong>'
 771			. implode("|", $tmp)
 772			. '</strong>';
 773		if(array_key_exists("return-description", $method)){
 774			$details .= ': <span class="jsdoc-return-description">'
 775				. $method["return-description"]
 776				. '</span>';
 777		}
 778		$details .= '</div>';
 779	} 
 780	else if(array_key_exists("return-description", $method)){
 781		$details .= '<div class="jsdoc-return-type"><div class="jsdoc-return-description">'
 782			. $method["return-description"]
 783			. '</div></div>';
 784	}
 785
 786	if(array_key_exists("description", $method)){
 787		$details .= '<div class="jsdoc-summary">' . do_markdown($method["description"]) . '</div>';
 788	} else if(array_key_exists("summary", $method)){
 789		$details .= '<div class="jsdoc-summary">' . $method["summary"] . '</div>';
 790	}
 791	if(array_key_exists("summary", $method)){
 792		$s .= ' <span>' . $method["summary"] . '</span>';
 793	}
 794	$s .= '</li>';	//	jsdoc-title
 795
 796	if(count($method["parameters"])){
 797		$details .= _generate_param_table($method["parameters"], $docs, $base_url, $suffix);
 798	}
 799
 800	if(array_key_exists("examples", $method)){
 801		$details .= '<div class="jsdoc-examples">';
 802		$counter = 1;
 803		foreach($method["examples"] as $example){
 804			$details .= '<div class="jsdoc-example">'
 805				. '<div><strong>Example ' . $counter++ . '</strong></div>'
 806				. format_example($example)
 807				. '</div>';
 808		}
 809		$details .= '</div>';
 810	}
 811
 812	$details .= '</div>';	//	jsdoc-field
 813	return array("s"=>$s, "details"=>$details);
 814}
 815
 816function _generate_param_table($params, $docs = array(), $base_url = "", $suffix = ""){
 817	//	create the inline table for parameters; isolated so that nesting may occur on more than one level.
 818	$tmp_details = array();
 819	foreach($params as $p){
 820		$tester = explode(".", $p["type"], 2);
 821		$tester = $tester[0];
 822		$pstr = '<tr>'
 823			. '<td class="jsdoc-param-name">'
 824			. $p["name"]
 825			. '</td>'
 826			. '<td class="jsdoc-param-type">'
 827			. (strpos($tester, "__") === 0 ? "Object" : $p["type"])
 828			. '</td>'
 829			. '<td class="jsdoc-param-description">'
 830			. (strlen($p["usage"]) ? (($p["usage"] == "optional") ? '<div><em>Optional.</em></div>' : (($p["usage"] == "one-or-more") ? '<div><em>One or more can be passed.</em></div>' : '')) : '')
 831			. $p["description"];
 832
 833		if(strpos($tester, "__")===0){
 834			//	try to find the object in question, and if found list out the props.
 835			$pconfig = generate_object($p["type"], null, $docs);
 836			if($pconfig && array_key_exists("properties", $pconfig)){
 837				$p_param = array();
 838				foreach($pconfig["properties"] as $name=>$value){
 839					$tmp_str = '<tr>'
 840						. '<td class="jsdoc-param-name">'
 841						. $name
 842						. '</td>'
 843						. '<td class="jsdoc-param-type">'
 844						. $value["type"]
 845						. '</td>'
 846						. '<td class="jsdoc-param-description">';
 847					if(array_key_exists("description", $value)){
 848						$tmp_str .= do_markdown($value["description"]);
 849					} else if (array_key_exists("summary", $value)){
 850						$tmp_str .= do_markdown($value["summary"]);
 851					} else {
 852						$tmp_str .= '&nbsp;';
 853					}
 854					$p_param[] = $tmp_str . '</td></tr>';
 855				}
 856				$pstr .= '<table class="jsdoc-parameters" style="margin-left:0;margin-right:0;margin-bottom:0;">'
 857					. '<tr>'
 858					. '<th>Parameter</th>'
 859					. '<th>Type</th>'
 860					. '<th>Description</th>'
 861					. '</tr>'
 862					. implode('', $p_param)
 863					. '</table>';
 864			}
 865		}
 866		$pstr .= '</td>'
 867			. '</tr>';
 868		$tmp_details[] = $pstr;
 869	}
 870	return '<table class="jsdoc-parameters">'
 871		. '<tr>'
 872		. '<th>Parameter</th>'
 873		. '<th>Type</th>'
 874		. '<th>Description</th>'
 875		. '</tr>'
 876		. implode('', $tmp_details)
 877		. '</table>';
 878}
 879
 880function _generate_properties_output($properties, $docs = array(), $field_counter = 0, $base_url = "", $suffix = "", $title="Property"){
 881	//	generate all of the properties output
 882	$s = '<h2 class="jsdoc-summary-heading">Property Summary <span class="jsdoc-summary-toggle"></span></h2>'
 883		. '<div class="jsdoc-summary-list">'
 884		. '<ul>';
 885	$details = '<h2>Properties</h2>';
 886	foreach($properties as $name=>$prop){
 887		$tmp = _generate_property_output($prop, $name, $docs, $field_counter, $base_url, $suffix);
 888		$s .= $tmp["s"];
 889		$details .= $tmp["details"];
 890		$field_counter++;
 891	}
 892
 893	$s .= '</ul></div>';	//	property-summary
 894	return array("s"=>$s, "details"=>$details, "counter"=>$field_counter);
 895}
 896
 897function _generate_methods_output($methods, $docs = array(), $field_counter = 0, $base_url = "", $suffix = "", $title="Method"){
 898	//	generate all of the methods output
 899	$s = "";
 900	$details = "";
 901	if(count($methods)){
 902		$s .= '<h2 class="jsdoc-summary-heading">' . $title . ' Summary <span class="jsdoc-summary-toggle"></span></h2>'
 903			. '<div class="jsdoc-summary-list">'
 904			. '<ul>';
 905		$details .= '<h2>' . $title . 's</h2>';
 906		foreach($methods as $name=>$method){
 907			$html = _generate_method_output($method, $name, $docs, $field_counter, $base_url, $suffix);
 908			$s .= $html["s"];
 909			$details .= $html["details"];
 910			$field_counter++;
 911		}
 912		$s .= '</ul></div>';	//	method-summary
 913	}
 914	return array("s"=>$s, "details"=>$details, "counter"=>$field_counter);
 915}
 916
 917function generate_object_html($page, $version, $base_url = "", $suffix = "", $versioned = true, $docs = array()){
 918	//	$page:
 919	//		The object to render, i.e. "dojox.charting.Chart2D"
 920	//	$version:
 921	//		The version against which to generate the page.
 922	//	$base_url:
 923	//		A URL fragment that will be prepended to any link generated.
 924	//	$suffx:
 925	//		A string that will be appended to any link generated, i.e. ".html"
 926	//	$docs:
 927	//		An optional array of XML documents to run the function against.  See spider.php
 928	//		for example usage.
 929	if(!isset($page)){
 930		throw new Exception("generate_html: you must pass an object name!");
 931	}
 932	if(!isset($version)){
 933		throw new Exception("generate_html: you must pass a version!");
 934	}
 935
 936	if(strpos($page, "/") > 0){
 937		$page = implode(".", explode("/", $page));
 938	}
 939	$data_dir = dirname(__FILE__) . "/../data/" . $version . "/";
 940
 941	//	get the docs to run against.  this can be optionally provided;
 942	//	if they are they ALL need to be there.
 943	if(!count($docs)){
 944		$docs = load_docs($version);
 945	}
 946
 947	$xml = $docs["xml"];
 948	$p_xml = $docs["p_xml"];
 949	$r_xml = $docs["r_xml"];
 950	$xpath = $docs["xpath"];
 951	$p_xpath = $docs["p_xpath"];
 952	$r_xpath = $docs["r_xpath"];
 953
 954	//	check if we're to build links versioned and if so, add that to the base url.
 955	if($versioned){
 956		$base_url .= $version . '/';
 957	}
 958
 959	//	get our object
 960	$obj = generate_object($page, $version, $docs);
 961	if(!$obj){
 962		$s = '<div style="font-weight: bold;color: #900;">The requested object was not found.</div>';
 963		echo $s;
 964		exit();
 965	}
 966
 967	//	process it and output us some HTML.
 968	$s = '<div class="jsdoc-permalink" style="display:none;">' . $base_url . implode('/', explode(".", $page)) . $suffix . '</div>';
 969
 970	//	page heading.
 971	$s .= '<h1 class="jsdoc-title ' . convert_type($obj["type"]) . 'Icon36">'
 972		. $obj["title"]
 973		. ' <span style="font-size:11px;color:#999;">(version ' . $version . ')</span>'
 974		. '</h1>';
 975
 976	//	prototype chain
 977	$s .= '<div class="jsdoc-prototype">';
 978	foreach($obj["prototypes"] as $i=>$p){
 979		if($i){ $s .= ' &raquo; '; }
 980		if($p != $page && $p != "Object"){
 981			$name = implode("/", explode(".", $p));
 982			$s .= '<a class="jsdoc-link" href="' . $base_url . $name . $suffix . '">' . $p . '</a>';
 983		} else {
 984			$s .= $p;
 985		}
 986	}
 987	$s .= '</div>';
 988
 989	if($page == "dojo"){
 990		$s .= '<div class="jsdoc-require">&lt;script src="path/to/dojo.js"&gt;&lt;/script&gt;</div>';
 991	} else if(array_key_exists("require", $obj)) {
 992		$s .= '<div class="jsdoc-require">dojo.require("' . $obj["require"] . '");</div>';
 993	}
 994
 995	if(array_key_exists("resource", $obj)){
 996		$s .= '<div class="jsdoc-prototype">Defined in ' . $obj["resource"] . '</div>';
 997	}
 998
 999	//	usage.  Go look for a constructor.
1000	if(array_key_exists("methods", $obj) && array_key_exists("constructor", $obj["methods"])){
1001		$fn = $obj["methods"]["constructor"];
1002		$s .= '<div class="jsdoc-function-information"><h3>Usage:</h3>'
1003			. '<div class="function-signature">'
1004			. '<span class="keyword">var</span> foo = new '
1005			. $page
1006			. '(';
1007		if(count($fn["parameters"])){
1008			$tmp = array();
1009			foreach($fn["parameters"] as $param){
1010				$tmp[] = '<span class="jsdoc-comment-type">/* '
1011					. $param["type"]
1012					. ($param["usage"] == "optional" ? "?":"")
1013					. ' */</span> '
1014					. $param["name"];
1015			}
1016			$s .= implode(", ", $tmp);
1017		}
1018		$s .= ');</div></div>';
1019	}
1020
1021	if(array_key_exists("description", $obj)){
1022		$s .= '<div class="jsdoc-full-summary">'
1023			. do_markdown($obj["description"])
1024			. "</div>";
1025	}
1026
1027	//	examples.
1028	if(array_key_exists("examples", $obj)){
1029		$examples = $obj["examples"];
1030		if(count($examples)){
1031			$s .= '<div class="jsdoc-examples">'
1032				. '<h2>Examples:</h2>';
1033			$counter = 1;
1034			foreach($examples as $example){
1035				$s .= '<div class="jsdoc-example">'
1036					. '<h3>Example ' . $counter++ . '</h3>'
1037					. format_example($example)
1038					. '</div>';
1039			}
1040			$s .= '</div>';
1041		}
1042	}
1043
1044	//	mixins
1045	if(array_key_exists("mixins", $obj)){
1046		$tmp = array();
1047		$super = $obj["prototypes"][count($obj["prototypes"])-2];
1048		foreach($obj["mixins"] as $mixin){
1049			if($mixin != $super){
1050				$name = implode("/", explode('.', $mixin));
1051				$tmp[] = '<a class="jsdoc-link" href="' . $base_url . $name . $suffix . '">' . $mixin . '</a>';
1052			}
1053		}
1054		if(count($tmp)){
1055			$s .= '<div class="jsdoc-mixins"><label>mixins: </label>'
1056				. implode(", ", $tmp)
1057				. '</div>';
1058		}
1059		
1060	}
1061
1062	//	Properties, methods, events, and attached objects.
1063	$s .= '<div class="jsdoc-children">';
1064	$s .= '<div class="jsdoc-field-list">';
1065	$details = '<div class="jsdoc-children">'
1066		. '<div class="jsdoc-fields">';
1067	$field_counter = 0;
1068
1069	$props = $obj["properties"];
1070	$methods = $obj["methods"];
1071	$events = $obj["events"];
1072	if(count($props) || count($methods) || count($events)){
1073		if(count($props)){
1074			$tmp = _generate_properties_output($props, $docs, $field_counter, $base_url, $suffix, "Properties");
1075			$s .= $tmp["s"];
1076			$details .= $tmp["details"];
1077			$field_counter = $tmp["counter"];
1078		}
1079		if(count($methods)){
1080			$tmp = _generate_methods_output($methods, $docs, $field_counter, $base_url, $suffix, "Method");
1081			$s .= $tmp["s"];
1082			$details .= $tmp["details"];
1083			$field_counter = $tmp["counter"];
1084		}
1085		if(count($events)){
1086			$tmp = _generate_methods_output($events, $docs, $field_counter, $base_url, $suffix, "Event");
1087			$s .= $tmp["s"];
1088			$details .= $tmp["details"];
1089		}
1090	}
1091
1092	//	child objects: put up a list of any child objects that are attached to this particular one.
1093	if(array_key_exists("attached", $obj) && count($obj["attached"])){
1094		$children = $obj["attached"];
1095		$s .= '<h2 class="jsdoc-summary-heading">Attached Objects <span class="jsdoc-summary-toggle"></span></h2>'
1096			. '<div class="jsdoc-summary-list">';
1097		foreach($children as $child){
1098			$s .= '<div class="jsdoc-field">'
1099				. '<div class="jsdoc-title">'
1100				. '<span class="';
1101
1102			$tmp = $xpath->query('//object[@location="' . $child . '"]')->item(0);
1103			if($tmp){
1104				if($tmp->getAttribute("type") == "Function" && $tmp->getAttribute("classlike") == "true"){
1105					$s .= "constructor";
1106				} else {
1107					$s .= convert_type($tmp->getAttribute("type"));
1108				}
1109			} else {
1110				$s .= convert_type("Object");
1111			}
1112			$s .= '">'
1113				. '<a class="jsdoc-link" href="' . $base_url . implode("/", explode(".", $child)) . $suffix . '">'
1114				. $child
1115				. '</a>'
1116				. '</span>'
1117				. '</div>'	//	jsdoc-title
1118				. '</div>';	//	jsdoc-field
1119		}
1120		$s .= '</div>';
1121	}
1122
1123	$s .= '</div>';	// jsdoc-field-list.
1124	$s .= '</div>';	// jsdoc-children.
1125	$details .= '</div></div>';
1126
1127	return $s . $details;
1128}
1129
1130
1131//	sorting functions used for the tree
1132function object_node_sorter($a, $b){
1133	if($a->getAttribute("location") == $b->getAttribute("location")){ return 0; }
1134	return ($a->getAttribute("location") > $b->getAttribute("location")) ? 1 : -1;
1135}
1136
1137function node_reference_sorter($a, $b){
1138	if(strtolower($a["_reference"]) == strtolower($b["_reference"])) return 0;
1139	return (strtolower($a["_reference"]) > strtolower($b["_reference"])) ? 1 : -1;
1140}
1141
1142//	generate a hierarchical representation of the object tree; based on the class-tree.
1143//	Note that this structure is generated based on the structure of dojo.data.
1144function generate_object_tree($version, $roots=array(), $filter=true, $docs=array()){
1145	//	$version:
1146	//		The version of the object tree to generate.
1147	//	$roots:
1148	//		The objects to be considered the root nodes of the list generated.  If empty,
1149	//		this will simply look for any objects that do not have a period in the name.
1150	//	$filter:
1151	//		A boolean that filters out anything that is considered "private" (i.e. beginning with
1152	//		an underscore "_")
1153	//	$docs:
1154	//		An optional array of XML document objects that will be used as the sources for the tree.
1155
1156	//	get our source.
1157	if(!count($docs)){
1158		$data_dir = dirname(__FILE__) . "/../data/" . $version . "/";
1159		$f = $data_dir . "objects.xml";
1160		if(!file_exists($f)){
1161			throw new Exception("generate_object_tree_html: the required directory/file was not found.");
1162		}
1163
1164		$xml = new DOMDocument();
1165		$xml->load($f);
1166		$xpath = new DOMXPath($xml);
1167	} else {
1168		$xml = $docs["xml"];
1169		$xpath = $docs["xpath"];
1170	}
1171
1172	$objects = $xpath->query("//object");
1173	$ret = array();
1174	$counter = 0;
1175
1176	//	set our top-level objects
1177	$show = array();
1178	$keys = array();
1179	if(count($roots)){
1180		//	we were given a specific set of root locations.
1181		foreach($roots as $key=>$value){
1182			$show[$key] = $value;
1183			$keys[] = $key;
1184		}
1185	} else {
1186		$r = $xpath->query("//object[not(contains(@location, '.'))]");
1187		foreach($r as $node){
1188			if($node->getAttribute("type") == "Function" && $node->getAttribute("classlike") == "true"){
1189				$show[$node->getAttribute("location")] = -1;
1190				$keys[] = $node->getAttribute("location");
1191			}
1192		}
1193	}
1194
1195	//	ok, let's create our internal structure.
1196	foreach($objects as $node){
1197		$name = $node->getAttribute("location");
1198		$type = $node->getAttribute("type");
1199		$classlike = $node->getAttribute("classlike");
1200
1201		$name_parts = explode(".", $name);
1202		$short_name = array_pop($name_parts);
1203
1204		if ($type=="Function" && $classlike=="true") {
1205			$val = array(
1206				"id"=>$name,  /* "object-" . $counter++, */
1207				"name"=>$short_name,
1208				"fullname"=>$name,
1209				"type"=>"constructor"
1210			);
1211		} else {
1212			$val = array(
1213				"id"=>$name,  /* "object-" . $counter++, */
1214				"name"=>$short_name,
1215				"fullname"=>$name,
1216				"type"=>(strlen($type) ? strtolower($type): "object")
1217			);
1218		} 
1219
1220		if(isset($val)){
1221			if($filter && strpos($short_name, "_") === 0){
1222				unset($val);
1223				continue; 
1224			}
1225			if(count($name_parts)){
1226				$finder = implode(".", $name_parts);
1227				foreach($ret as &$obj){
1228					if($obj["fullname"] == $finder){
1229						if(!array_key_exists("children", $obj)){
1230							$obj["children"] = array();
1231						}
1232						$obj["children"][] = array(
1233							"_reference"=>$val["id"]
1234						);
1235					//	$obj["type"] = "namespace";
1236						break;
1237					}
1238				}
1239			}
1240			$ret[] = $val;
1241			unset($val);
1242		}
1243	}
1244	
1245	//	go through the top-level objects and reset the type on it.
1246	$counter = 0;
1247	foreach($ret as &$obj){
1248		$name = $obj["fullname"];
1249		if(array_key_exists($name, $show)){
1250			$obj["type"] = "root";
1251			$show[$name] = $counter;
1252		}
1253		$counter++;
1254	}
1255
1256	//	finally, move the given namespaces to the top of the array.
1257	$fin = array();
1258	foreach($show as $item){
1259		if(array_key_exists("children", $ret[$item])){
1260			usort($ret[$item]["children"], "node_reference_sorter");
1261		}
1262		$fin[] = &$ret[$item];
1263	}
1264	foreach($ret as &$obj){
1265		if(!array_key_exists($obj["fullname"], $show)){
1266			if(array_key_exists("children", $obj)){
1267				usort($obj["children"], "node_reference_sorter");
1268			}
1269			$fin[] = $obj;
1270		}
1271	}
1272
1273	return $fin;
1274}
1275
1276function _get_branch($obj, $root){
1277	//	given the object generated by the tree, find all objects that are referenced as children
1278	//	and return an array.  Note that you should pass both params by reference (i.e. &$myTree)
1279	//
1280	//	$obj
1281	//		The actual tree object to be used for lookup.
1282	//	$root
1283	//		The parent object to use for getting children.
1284
1285	$ret = array();
1286	foreach($root["children"] as $child){
1287		foreach($obj as $object){
1288			if($object["id"] == $child["_reference"]){
1289				$ret[] = $object;
1290				break;
1291			}
1292		}
1293	}
1294	return $ret;
1295}
1296
1297function _generate_branch_html($tree, $obj, $base_url = "", $suffix = ""){
1298	//	recursive private function to "listify" the given branch.
1299	$s = '<li class="' . ($obj["type"]=="root"?"namespace":$obj["type"]) . 'Icon">'
1300		. '<a class="jsdoc-link" href="' . $base_url . implode("/", explode(".", $obj["fullname"])) . $suffix . '">'
1301		. $obj["name"]
1302		. '</a>';
1303	if(array_key_exists("children", $obj)){
1304		$s .= "\n". '<ul class="jsdoc-children">';
1305		$branch = _get_branch($tree, $obj);
1306		foreach($branch as $child){
1307			$s .= _generate_branch_html($tree, $child, $base_url, $suffix);
1308		}
1309		$s .= '</ul>' . "\n";
1310	}
1311	return $s . '</li>' . "\n";
1312}
1313
1314function generate_object_tree_html($tree, $root, $base_url = "", $suffix = ""){
1315	//	summary:
1316	//		Given an object tree (such as generated above), create an HTML
1317	//		version, complete with links.
1318	//	$tree:
1319	//		The array structure as given from above.
1320	//	$root:
1321	//		The string indicating what root object to use for branching.
1322	//	$base_url:
1323	//		A string prepended to any links generated.
1324	//	$suffix:
1325	//		A string appended to any links generated.
1326	if(!isset($tree)){
1327		throw new Exception("generate_object_tree_html: you must pass in an object tree.");
1328	}
1329
1330	//	find the root object in the tree.
1331	$roots = array();
1332	foreach($tree as $object){
1333		if($object["type"] == "root"){
1334			$roots[] = $object;
1335		}
1336	}
1337
1338	//	let's give it a start.
1339	$s = '<ul class="jsdoc-navigation">' . "\n";
1340	foreach($roots as $r){
1341		if($r["id"] == $root){
1342			$s .= _generate_branch_html($tree, $r, $base_url, $suffix);
1343		} else {
1344			$s .= '<li class="namespaceIcon">'
1345				. '<a class="jsdoc-link" href="' . $base_url . implode("/", explode(".", $r["fullname"])) . $suffix . '">'
1346				. $r["name"]
1347				. '</a>'
1348				. '</li>' . "\n";
1349		}
1350	}
1351
1352	$s .= '</ul>' . "\n";
1353	return $s;
1354}
1355
1356?>