<?php
namespace App\Utils\Views; class XMLExplorer extends \App\Utils\Views\AbstractHtmlComponent { protected $xml = null; protected $ID = null; public function __construct(\XMLReader $xml) { $this->xml = $xml; $this->mergeOptions([ 'interactive' => true, 'showEmptyLeafs' => true, ]); } private function getId() { if ($this->ID) { return $this->ID; } $this->ID = 'XmlExplorer_' . md5(mt_rand()); return $this->ID; } public function renderHtml() { $output = ""; $output.= sprintf("<div class='XMLExplorer %s %s' id=%s>", ($this->getOption('easySelect') ? 'select-easy' : ''), ($this->getOption('noSelect') ? 'select-none' : ''), $this->getId()); list($output2, ) = $this->renderXMLNode($this->xml, [ 'depth' => 0, 'parents' => [], ], [ 'nodes' => 0, 'nodes_cdata' => 0, ]); $output.= $output2; $output.= sprintf("</div>"); return $output; } public function renderXMLNode($xml, $options, $stats) { $output = ""; $istats = $stats; while ($xml->read()) { $nodeOutput = ""; $replacements = [ '__li_collapsible__' => '', ]; if ($xml->nodeType == \XMLReader::ELEMENT) { if (!$this->getOption('showEmptyLeafs') && $xml->isEmptyElement) { continue; } $name = $xml->name; $stats['nodes']++; $nodeOutput.= sprintf('<li class="node __li_collapsible__">'); $nodeOutput.= sprintf('&lt;<span class="label start" title="%s">%s</span>', htmlentities(implode("/", $options['parents'])."/".$name), $this->toHtml($name) ); if ($xml->hasAttributes) { $nodeOutput.= sprintf('<span class="attributes">'); while ($xml->moveToNextAttribute()) { if (!$this->getOption('showEmptyLeafs') && $xml->value==='') { continue; } $nodeOutput.= sprintf(' <span class="attribute"><span class="label" title="%s">%s</span>="<span class="value">%s</span>"</span>', htmlentities(implode("/", $options['parents'])."/".$name."@".$xml->name), $this->toHtml($xml->name), $this->toHtml($xml->value) ); } $nodeOutput.= sprintf('</span>'); } $xml->moveToElement(); if ($xml->isEmptyElement) { $nodeOutput.= sprintf(' /&gt;'); } else { $nodeOutput.= sprintf('&gt;'); $o = $options; $o['depth']++; $o['parents'][]= $xml->name; list($output2, $stats2) = $this->renderXMLNode($xml, $o, $stats); if (($stats2['nodes'] - $stats['nodes'])>1) { $replacements['__li_collapsible__'] = 'collapsible'; } elseif (($stats2['nodes_cdata'] - $stats['nodes_cdata'])>0) { $replacements['__li_collapsible__'] = 'collapsible'; } $stats = $stats2; $nodeOutput.= $output2; $nodeOutput.= sprintf('&lt;/<span class="label end">%s</span>&gt;', $this->toHtml($name)); } $nodeOutput.= sprintf('</li>'); } elseif ($xml->nodeType == \XMLReader::END_ELEMENT) { break; } elseif ($xml->nodeType == \XMLReader::TEXT) { $nodeOutput.= sprintf('<span class="value text">%s</span>', $this->toHtml($xml->value)); } elseif ($xml->nodeType == \XMLReader::CDATA) { $nodeOutput.= sprintf('<span class="CDATA">%s</span>', sprintf('<span class="CDATA">%s</span><span class="value">%s</span><span class="CDATA">%s</span>', $this->toHtml('<![CDATA['), $this->toHtml($xml->value), $this->toHtml(']]>'))); $stats['nodes_cdata']++; } elseif ($xml->nodeType == \XMLReader::COMMENT) { $nodeOutput.= sprintf('<li class="node comment">%s</li>', sprintf('<span class="comment">%s</span><span class="value">%s</span><span class="comment">%s</span>', $this->toHtml('<!--'), $this->toHtml($xml->value), $this->toHtml('-->'))); } elseif ($xml->nodeType == \XMLReader::DOC_TYPE) { } elseif ($xml->nodeType == \XMLReader::SIGNIFICANT_WHITESPACE) { } else { throw new \Exception(sprintf("Don't know how to handle nodeType: %d", $xml->nodeType)); } if (!$this->getOption('interactive')) { $replacements['__li_collapsible__'] = ''; } $output.= str_replace(array_keys($replacements), array_values($replacements), $nodeOutput); } if ($stats['nodes'] - $istats['nodes']) { $output = sprintf("<ul class='node'>%s</ul>", $output); } return [$output, $stats]; } protected function toHtml($str) { return htmlentities($str, ENT_COMPAT, "UTF-8"); } public function renderJs(\Engine\Theme\JSLoader\LoaderAbstract $JSLoader) { if (!$this->getOption('interactive')) { return ''; } return <<<JS
			$('{$this->getId()}').on('click', 'li.node.collapsible >span.label', function(evt, elm) {
				var li = elm.up('li.node.collapsible');
				if (li.hasClassName('collapsed')) {
					li.removeClassName('collapsed');
				}
				else {
					li.addClassName('collapsed');
					if (!li.down('>span.collapsed')) {
						li.down('>ul.node, >span.CDATA').insert({
							before: "<span class='collapsed'></span>"
						});
					}
				}
			});
JS;
} public function preloadCss(\Engine\Theme\CSSLoader\Complex $CSSLoader) { $CSSLoader->loadGroup("utils.views.xmlexplorer"); return ''; } public function preloadJs(\Engine\Theme\JSLoader\LoaderAbstract $JSLoader) { $JSLoader->loadGroup("utils.views.xmlexplorer"); return ''; } } 