class Action extends Observer
{
+ private function serveReference(\http\Controller $ctl) {
+ $payload = $ctl->getPayload();
+ $finder = new Finder($this->baseUrl, REFS);
+ $path = $finder->find(new \http\Url($ctl->getRequest()->getRequestUrl()));
+ $payload->listing = new RefListing($path,
+ $finder->glob($path, "/[_a-zA-Z]*.md"));
+ $payload->title = $payload->listing->getSelf()->formatLink();
+ $payload->refs = $finder;
+ if ($path->isFile()) {
+ $payload->html = new Markdown($path);
+ $payload->sublisting = new RefListing($path,
+ $finder->glob($path, "/[_a-z]*.md"));
+ return true;
+ }
+ }
+
+ private function serveInternal(\http\Controller $ctl) {
+ $payload = $ctl->getPayload();
+ $finder = new Finder($this->baseUrl, ROOT);
+ $url = new \http\Url($ctl->getRequest()->getRequestUrl());
+ $path = $finder->find($url, "");
+ if ($path->isFile("")) {
+ $payload->html = $path->toHtml();
+ } else if (strcmp($url, $this->baseUrl)) {
+ throw new \http\Controller\Exception(404, "Could not find '$path'");
+ }
+ }
+
function update(\SplSubject $ctl) {
/* @var \http\Controller $ctl */
try {
- $payload = $ctl->getPayload();
- $request = $ctl->getRequest();
-
- $finder = new Finder($this->baseUrl, REFS);
- $url = new \http\Url($request->getRequestUrl());
- if (!$path = $finder->find($url)) {
- $path = new Path;
- }
-
- $payload->baseUrl = $this->baseUrl;
- $payload->requestUrl = $url;
-
- $payload->listing = new RefListing($path,
- $finder->glob($path, "/[_a-zA-Z]*.md"));
+ $ctl->getPayload()->baseUrl = $this->baseUrl;
- if ($path->isFile()) {
- $payload->markdown = new Markdown($path);
- $payload->sublisting = new RefListing($path,
- $finder->glob($path, "/[_a-z]*.md"));
- } else if ($path($url)->isFile("")) {
- $payload->markdown = $path->toHtml();
- } else if (strcmp($url, $this->baseUrl)) {
- throw new \http\Controller\Exception(404, "Could not find '$url'");
+ if (!$this->serveReference($ctl)) {
+ $this->serveInternal($ctl);
}
} catch (\Exception $e) {
- $payload->baseUrl = $this->baseUrl;
$ctl->getPayload()->exception = $e;
}
}
namespace mdref;
+/**
+ * Find markdown reference files in several REFPATH paths.
+ *
+ * The base URL is used to extract the relative identifier out of the request
+ * url in Finder::find().
+ *
+ * Use the created Path of Finder::find() for Finder::glob() to find subrefs.
+ */
class Finder
{
/**
}
/**
+ * Find a markdown reference file in one REFPATH. If nothing could be found
+ * an empty Path will be returned.
+ *
* @param \http\Url $requestUrl
* @return Path
*/
- function find(\http\Url $requestUrl) {
- $file = implode(DIRECTORY_SEPARATOR,
- $this->baseUrl->params($requestUrl));
+ function find(\http\Url $requestUrl, $ext = ".md") {
+ $file = implode(DIRECTORY_SEPARATOR, $this->baseUrl->params($requestUrl));
foreach ($this->refs as $base) {
$path = new Path($base, $file);
- if ($path->isFile()) {
+ if ($path->isFile($ext)) {
return $path;
}
}
+
+ return new Path;
}
+ /**
+ * Glob either in a Path's base dir, or, if the path does not have a base
+ * dir set, in each REFPATH paths.
+ *
+ * @param \mdref\Path $path
+ * @param string $pattern glob pattern
+ * @param int $flags glob flags
+ * @return array glob result
+ */
function glob(Path $path, $pattern, $flags = GLOB_BRACE) {
if (strlen($path->getBaseDir())) {
return glob($path->getFullPath($pattern), $flags);
namespace mdref;
+/**
+ * A path made out of a base dir and an thereof relative path name.
+ */
class Path
{
/**
*/
protected $baseDir = "";
+ /**
+ * @param string $baseDir
+ * @param string $path
+ */
function __construct($baseDir = "", $path = "") {
$this->baseDir = $baseDir;
$this->path = $path;
}
+ /**
+ * Create a copy of this path with a different path name
+ *
+ * @param string $path
+ * @return \mdref\Path
+ */
function __invoke($path) {
$that = clone $this;
$that->path = $path;
return $that;
}
+ /**
+ * Retrurns the full path as string
+ * @return string
+ */
function __toString() {
return $this->getFullPath();
}
+ /**
+ * The base directory
+ * @return string
+ */
function getBaseDir() {
return $this->baseDir;
}
+ /**
+ * The path name relative to the base dir
+ * @return string
+ */
function getPathName() {
return $this->path;
}
+ /**
+ * The full path
+ * @param string $ext extension
+ * @return string
+ */
function getFullPath($ext = "") {
return $this->baseDir . DIRECTORY_SEPARATOR . $this->path . $ext;
}
+ /**
+ * Retrieve a another subpath within the base dir
+ * @param type $path
+ * @return string
+ */
function getSubPath($path) {
return trim(substr($path, strlen($this->baseDir)), DIRECTORY_SEPARATOR);
}
}
function toHtml() {
+ $head = sprintf("<h1>%s</h1>\n", htmlspecialchars(basename($this->getPathName())));
if ($this->isFile()) {
- return htmlspecialchars(file_get_contents($this->getFullPath()));
+ $html = htmlspecialchars(file_get_contents($this->getFullPath()));
} elseif ($this->isFile("")) {
- return htmlspecialchars(file_get_contents($this->getFullPath("")));
+ $html = htmlspecialchars(file_get_contents($this->getFullPath("")));
+ } else {
+ throw new \http\Controller\Exception(404, "Not Found: {$this->getPathName()}");
}
+ return $head . "<pre>" . $html ."</pre>";
}
}
\ No newline at end of file
function __construct(Path $path, $entry = null) {
$this->path = $path;
- $this->entry = $entry ?: $path->getPathName();
+ $this->entry = trim($entry ?: $path->getPathName(), DIRECTORY_SEPARATOR);
}
function __destruct() {
function formatUrl() {
return htmlspecialchars($this->entry);
- return implode("/", explode(DIRECTORY_SEPARATOR, trim(substr(
- $this->entry, strlen($this->path->getBaseDir())),
- DIRECTORY_SEPARATOR)));
}
- protected function joinLink(array $parts) {
+ private function joinLink(array $parts) {
$link = "";
$upper = ctype_upper($parts[0][0]);;
for ($i = 0; $i < count($parts); ++$i) {
function formatLink($basename = false) {
$link = "";
- if (strlen($entry = trim($this->entry, DIRECTORY_SEPARATOR))) {
- $parts = explode(DIRECTORY_SEPARATOR, $entry);
+ if (strlen($this->entry)) {
+ $parts = explode(DIRECTORY_SEPARATOR, $this->entry);
$link = $basename ? end($parts) : $this->joinLink($parts);
}
return htmlspecialchars($link);
}
- protected function openFile() {
+ function getPath() {
+ $path = $this->path;
+ $file = $path($this->entry);
+ return $file;
+ }
+
+ private function openFile() {
if (!is_resource($this->file)) {
- $path = $this->path;
- $file = $path($this->entry);
+ $file = $this->getPath();
+
if (!$file->isFile()) {
- throw new \Exception("Not a file: '$this->entry'");
+ throw new \Exception("Not a file: '{$this->entry}'");
}
if (!$this->file = fopen($file->getFullPath(".md"), "r")) {
throw new \Exception("Could not open {$this->entry}");
fgets($this->file);
return htmlspecialchars(fgets($this->file));
}
+
+ function formatEditUrl() {
+ $path = $this->path;
+ $base = current(explode(DIRECTORY_SEPARATOR, $path->getPathName()));
+ $file = $path($base);
+ if ($file->isFile(".mdref")) {
+ return sprintf(file_get_contents($file->getFullPath(".mdref")),
+ $this->entry);
+ }
+ }
}
\ No newline at end of file
/**
* @param \mdref\Path $path
+ * @param array $files
*/
function __construct(Path $path, array $files) {
$this->path = $path;
}, $files);
}
- /**
- * Copy constructor
- * @param mixed $filter callable array filter or fnmatch pattern
- * @return \mdref\RefListing
- */
- function __invoke($filter) {
- die(__METHOD__);
- $that = clone $this;
- $that->entries = array_filter($that->entries, is_callable($filter)
- ? $filter
- : function($fn) use ($filter) {
- return fnmatch($filter, $fn);
- }
- );
- return $that;
- }
-
- function __toString() {
- return __METHOD__;
- return $this->format(substr($this->path, strlen($this->path->getBaseDir())));
- }
-
function count() {
return count($this->entries);
}
function getSelf() {
return new RefEntry($this->path);
}
-
- function format($entry) {
- return __METHOD__;
- $ns = "";
- if (strlen($entry = trim($entry, DIRECTORY_SEPARATOR))) {
- $upper = ctype_upper($entry[0]);
- $parts = explode(DIRECTORY_SEPARATOR, $entry);
-
- for ($i = 0; $i < count($parts); ++$i) {
- if (!strlen($parts[$i]) || $parts[$i] === ".") {
- continue;
- }
- if (strlen($ns)) {
- if ($upper && !ctype_upper($parts[$i][0])) {
- $ns .= "::";
- } else {
- $ns .= "\\";
- }
- }
- $ns .= $parts[$i];
- $upper = ctype_upper($parts[$i][0]);
- }
- }
- return $ns;
- }
-}
\ No newline at end of file
+}
<data xmlns="http://www.netbeans.org/ns/php-project/1">
<name>mdref-autocracy.git</name>
</data>
+ <spellchecker-wordlist xmlns="http://www.netbeans.org/ns/spellchecker-wordlist/1">
+ <word>mdref</word>
+ </spellchecker-wordlist>
</configuration>
</project>
border-bottom-left-radius: 10px;
padding: 0;
width: auto;
+ min-width: 200px;
padding-right: 1em;
}
.sidebar>ul {
list-style-type: none;
}
+.sidebar .edit {
+ display: block;
+ position: absolute;
+ top: 2em;
+ right: 0.5em;
+ transform: rotate(45deg);
+ text-decoration: none;
+ color: white;
+ font-weight: bold;
+ text-shadow: 0 0 5px red;
+}
+
code {
display: inline-block;
border-radius: 2px;
-function log() {
- // console.log.apply(console, arguments);
-}
+"use strict";
-function is_constant(s) {
- s = s.replace(/v\d+(_\d+)?$/, "");
- if (s.length < 2) {
- return false;
- }
- return s.toUpperCase(s) === s;
-}
-
-function is_variable(s) {
- return s.substring(0,1) === "$";
-}
+$(function() {
+ var mdref = {
+ log: function log() {
+ console.log.apply(console, arguments);
+ },
+ is_constant: function is_constant(s) {
+ s = s.replace(/v\d+(_\d+)?$/, "");
+ if (s.length < 2) {
+ return false;
+ }
+ return s.toUpperCase(s) === s;
+ },
+ is_variable: function is_variable(s) {
+ return s.substring(0,1) === "$";
+ },
+ type: function type(s, nn) {
+ var i, j, t;
+ // mdref.log("type", s);
+ // nothing
+ if (!s.match(/[a-zA-Z]/)) {
+ return;
+ }
-var is_in_string = false;
+ switch (s) {
+ // types
+ case "void":
+ case "bool":
+ case "int":
+ case "float":
+ case "string":
+ case "resource":
+ case "array":
+ case "object":
+ case "callable":
+ case "mixed":
+ // Zend/SPL
+ case "stdClass":
+ case "Exception":
+ case "ErrorException":
+ case "RuntimeException":
+ case "UnexpectedValueException":
+ case "DomainException":
+ case "InvalidArgumentException":
+ case "BadMethodCallException":
+ case "Closure":
+ case "Generator":
+ case "Countable":
+ case "Serializable":
+ case "Traversable":
+ case "Iterator":
+ case "IteratorAggregate":
+ case "ArrayAccess":
+ case "ArrayObject":
+ case "ArrayIterator":
+ case "RecursiveArrayIterator":
+ case "SplObserver":
+ case "SplSubject":
+ case "SplObjectStorage":
+ return "<code>";
-function type(s, nn) {
- var i, j, t;
- //log("type", s);
- // nothing
- if (!s.match(/[a-zA-Z]/)) {
- return;
- }
-
- switch (s) {
- // types
- case "void":
- case "bool":
- case "int":
- case "float":
- case "string":
- case "resource":
- case "array":
- case "object":
- case "callable":
- case "mixed":
- // Zend/SPL
- case "stdClass":
- case "Exception":
- case "ErrorException":
- case "RuntimeException":
- case "UnexpectedValueException":
- case "DomainException":
- case "InvalidArgumentException":
- case "BadMethodCallException":
- case "Closure":
- case "Generator":
- case "Countable":
- case "Serializable":
- case "Traversable":
- case "Iterator":
- case "IteratorAggregate":
- case "ArrayAccess":
- case "ArrayObject":
- case "ArrayIterator":
- case "RecursiveArrayIterator":
- case "SplObserver":
- case "SplSubject":
- case "SplObjectStorage":
- return "<code>";
-
- // keywords
- case "is":
- if (nn !== "H1") {
- return;
- }
- case "extends":
- case "implements":
- if (nn === "H1") {
- return "<br> <em>";
- }
- case "class":
- case "interface":
- case "namespace":
- case "public":
- case "protected":
- case "private":
- case "static":
- case "final":
- case "abstract":
- case "self":
- case "parent":
- // phrases
- case "Optional":
- case "optional":
- return "<em>";
- }
-
- // class members
- if (-1 !== (i = s.indexOf("::"))) {
- t = s.substring(i+2);
- if (!is_constant(t) && !is_variable(t)) {
- // methods
- return "<a href=\"" + s.replace(/::|\\/g, "/") + "\">";
- }
- }
- if (-1 !== (j = s.indexOf("\\")) && s.substr(j+1,1) !== "n") {
- return "<a href=\"" + s.replace(/\\/g, "/").replace(/::|$/, "#") + "\">";
- }
-
- switch (s.toLowerCase()) {
- // variables
- default:
- if (!is_variable(s)) {
- break;
- }
- // special constants
- case "null":
- case "true":
- case "false":
- return "<span class=\"var\">";
- }
-
- // constants
- if (is_constant(s)) {
- return "<span class=\"constant\">";
- }
-}
+ // keywords
+ case "is":
+ if (nn !== "H1") {
+ return;
+ }
+ case "extends":
+ case "implements":
+ if (nn === "H1") {
+ return "<br> <em>";
+ }
+ case "class":
+ case "interface":
+ case "namespace":
+ case "public":
+ case "protected":
+ case "private":
+ case "static":
+ case "final":
+ case "abstract":
+ case "self":
+ case "parent":
+ // phrases
+ case "Optional":
+ case "optional":
+ return "<em>";
+ }
-function node(s, nn) {
- //log("node", s);
-
- var t;
-
- if ((t = type(s, nn))) {
- return $(t).text(s);
- }
- return document.createTextNode(s);
-}
-function wrap(n, nn) {
- var $n = $(n)
- var a = [];
+ // class members
+ if (-1 !== (i = s.indexOf("::"))) {
+ t = s.substring(i+2);
+ if (!mdref.is_constant(t) && !mdref.is_variable(t)) {
+ // methods
+ return "<a href=\"" + s.replace(/::|\\/g, "/") + "\">";
+ }
+ }
+ if (-1 !== (j = s.indexOf("\\")) && s.substr(j+1,1) !== "n") {
+ return "<a href=\"" + s.replace(/\\/g, "/").replace(/::|$/, "#") + "\">";
+ }
- $n.text().split(/([^a-zA-Z0-9_\\\$:]+)/).forEach(function(v) {
- a.push(node(v, nn));
- });
- $n.replaceWith(a);
-}
-function walk(i, e) {
- log("walk", i, e);
+ switch (s.toLowerCase()) {
+ // variables
+ default:
+ if (!mdref.is_variable(s)) {
+ break;
+ }
+ // special constants
+ case "null":
+ case "true":
+ case "false":
+ return "<span class=\"var\">";
+ }
- $.each($.makeArray(e.childNodes), function(i, n) {
- switch (n.nodeName) {
- case "A":
- case "BR":
- case "HR":
- break;
- case "#text":
- wrap(n, e.nodeName);
- break;
- default:
- walk(-1, n);
- break;
- }
- });
-}
+ // constants
+ if (mdref.is_constant(s)) {
+ return "<span class=\"constant\">";
+ }
+ },
+ node: function node(s, nn) {
+ // mdref.log("node", s);
+ var t;
+ if ((t = mdref.type(s, nn))) {
+ return $(t).text(s);
+ }
+ return document.createTextNode(s);
+ },
+ wrap: function wrap(n, nn) {
+ var $n = $(n)
+ var a = [];
-function blink(c) {
- var $c = $(c);
-
- $c.fadeOut("fast").queue(function(next) {
- this.style.color = "red";
- next();
- }).fadeIn("fast").fadeOut("slow").queue(function(next) {
- this.style.color = "";
- next();
- }).fadeIn("slow");
-}
+ $n.text().split(/([^a-zA-Z0-9_\\\$:]+)/).forEach(function(v) {
+ a.push(mdref.node(v, nn));
+ });
+ $n.replaceWith(a);
+ },
+ walk: function walk(i, e) {
+ // mdref.log("walk", i, e);
-function hashchange() {
- if (location.hash.length > 1) {
- var hash = location.hash.substring(1);
- var name = is_variable(hash) ? ".var" : ".constant";
- var scrolled = false;
-
- $(name).each(hash.substring(hash.length-1) === "_" ? function(i, c) {
- if (c.textContent.substring(0, hash.length) === hash) {
- if (!scrolled) {
- $(window).scrollTop($(c).offset().top - 100);
- scrolled = true;
+ $.each($.makeArray(e.childNodes), function(i, n) {
+ switch (n.nodeName) {
+ case "A":
+ case "BR":
+ case "HR":
+ break;
+ case "#text":
+ mdref.wrap(n, e.nodeName);
+ break;
+ default:
+ mdref.walk(-1, n);
+ break;
}
- blink(c);
- }
- } : function(i, c) {
- if (c.textContent === hash) {
- $(window).scrollTop($(c).offset().top - 100);
- blink(c);
- return false;
- }
- });
- }
-}
+ });
+ },
+ blink: function blink(c) {
+ var $c = $(c);
-$(function() {
- $("h1,h2,h3,h4,h5,h6,p,li,code").each(walk);
- $(window).on("hashchange", hashchange);
- hashchange();
+ $c.fadeOut("fast").queue(function(next) {
+ this.style.color = "red";
+ next();
+ }).fadeIn("fast").fadeOut("slow").queue(function(next) {
+ this.style.color = "";
+ next();
+ }).fadeIn("slow");
+ },
+ hashchange: function hashchange() {
+ if (location.hash.length > 1) {
+ var hash = location.hash.substring(1);
+ var name = mdref.is_variable(hash) ? ".var" : ".constant";
+ var scrolled = false;
+
+ $(name).each(hash.substring(hash.length-1) === "_" ? function(i, c) {
+ if (c.textContent.substring(0, hash.length) === hash) {
+ if (!scrolled) {
+ $(window).scrollTop($(c).offset().top - 100);
+ scrolled = true;
+ }
+ blink(c);
+ }
+ } : function(i, c) {
+ if (c.textContent === hash) {
+ $(window).scrollTop($(c).offset().top - 100);
+ blink(c);
+ return false;
+ }
+ });
+ }
+ }
+ };
+
+ $("h1,h2,h3,h4,h5,h6,p,li,code").each(mdref.walk);
+ $(window).on("hashchange", mdref.hashchange);
+ mdref.hashchange();
});
--- /dev/null
+<?php if (isset($listing) && ($entry = $listing->getSelf()) && ($url = $entry->formatEditUrl())) : ?>
+ <a class="edit" href="<?=$url?>">Edit Page</a>
+<?php endif; ?>
--- /dev/null
+<ul>
+ <li><a href="https://github.com/m6w6/mdref">mdref-v<?php readfile(__DIR__."/../VERSION") ?></a></li>
+ <li><a href="LICENSE">© <?= implode("-", array_unique([2013, idate("Y")])) ?>
+ All rights reserved.</a></li>
+ <li><?php include __DIR__."/edit.phtml" ?></li>
+</ul>
--- /dev/null
+<h1>mdref</h1>
+
+<?php if (isset($exception)) : ?>
+ <?=\mdref\ExceptionHandler::html($exception, ["h2"], ["p"], ["pre", "style='overflow-x:scroll'"]); ?>
+<?php else : ?>
+ <?php if (isset($listing) && count($listing)) : ?>
+ <h2>Available References</h2>
+ <?php foreach ($listing as $entry) : ?>
+ <h3><a href="<?=$entry->formatUrl()?>"><?=$entry->formatLink()?></a></h3>
+ <ul>
+ <?php foreach (new mdref\RefListing($path = $refs->find($baseUrl->mod($entry->formatUrl())), $refs->glob($path, "/*.md")) as $entry) : ?>
+ <li><a href="<?=$entry->formatUrl()?>"><?=$entry->formatLink()?></a><br>
+ <?=$entry->readDescription()?></li>
+ <?php endforeach; ?>
+ </ul>
+ <?php endforeach; ?>
+ <?php endif; ?>
+<?php endif; ?>
<head>
<meta charset="utf-8">
<title>
- <?php if (isset($title)) : ?>
+ <?php if ($title) : ?>
<?= $title ?> - mdref
<?php else: ?>
mdref
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
</head>
<body>
- <?php if (isset($listing)) : ?>
- <div class="sidebar">
- <ul>
- <li>↰ <a href="">Home</a></li>
- <?php if (($entry = $listing->getParent())) : ?>
- <li>↑ <a href="<?=$entry->formatUrl()?>"><?=$entry->formatLink()?></a></li>
- <?php endif; ?>
- <?php if (($entry = $listing->getSelf()) && ($link = $entry->formatLink())) : ?>
- <ul><li>↻ <?= $link ?>
- <?php endif; ?>
- <?php if (count($listing)) : ?>
- <ul>
- <?php foreach ($listing as $entry) : ?>
- <li>↳ <a href="<?=$entry->formatUrl()?>"><?=$entry->formatLink()?></a></li>
- <?php endforeach; ?>
- </ul>
- <?php endif; ?>
- <?php if (strlen($listing)) : ?>
- </li></ul>
- <?php endif; ?>
- </ul>
- </div>
- <?php endif; ?>
+ <?php include __DIR__."/sidebar.phtml" ?>
- <?php if (isset($markdown)) : ?>
- <?= $markdown ?>
- <?php if (isset($sublisting) && count($sublisting)) : ?>
- <h2>Functions:</h2>
- <ul>
- <?php foreach($sublisting as $entry) : ?>
- <li>
- <h3><a href="<?=$entry->formatUrl()?>"><?=$entry->formatLink(true)?></a></h3>
- <p><?=$entry->readDescription()?></p>
- <p><?=$entry->readTitle()?></p>
- </li>
- <?php endforeach; ?>
- </ul>
- <?php endif; ?>
+ <?php if (isset($html)) : ?>
+ <?php include __DIR__."/mdref.phtml" ?>
<?php else: ?>
- <h1>mdref-v<?php readfile("../VERSION"); ?></h1>
- <?php if (isset($exception)) : ?>
- <?=\mdref\ExceptionHandler::html($exception, ["h2"], ["p"], ["pre", "style='overflow-x:scroll'"]); ?>
- <?php else : ?>
- <pre><?= htmlspecialchars(file_get_contents("../LICENSE")); ?></pre>
- <?php endif; ?>
+ <?php include __DIR__."/index.phtml" ?>
<?php endif; ?>
<div id="disqus_thread"></div>
<footer>
- <ul>
- <li><a href="https://github.com/m6w6/mdref">mdref-v<?php readfile("../VERSION") ?></a></li>
- <li><a href="LICENSE">© <?= implode("-", array_unique([2013, idate("Y")])) ?>
- All rights reserved.</a></li>
- </ul>
+ <?php include __DIR__."/footer.phtml" ?>
</footer>
<script src="index.js"></script>
</body>
--- /dev/null
+<?= $html ?>
+
+<?php if (isset($sublisting) && count($sublisting)) : ?>
+ <h2>Functions:</h2>
+ <ul>
+ <?php foreach($sublisting as $entry) : ?>
+ <li>
+ <h3><a href="<?=$entry->formatUrl()?>"><?=$entry->formatLink(true)?></a></h3>
+ <p><?=$entry->readDescription()?></p>
+ <p><?=$entry->readTitle()?></p>
+ </li>
+ <?php endforeach; ?>
+ </ul>
+<?php endif; ?>
--- /dev/null
+<?php if (isset($listing)) : ?>
+<div class="sidebar">
+ <?php include __DIR__."/edit.phtml" ?>
+ <ul>
+ <li>↰ <a href="">Home</a></li>
+ <?php if (($entry = $listing->getParent())) : ?>
+ <li>↑ <a href="<?=$entry->formatUrl()?>"><?=$entry->formatLink()?></a></li>
+ <?php endif; ?>
+ <?php if (($entry = $listing->getSelf()) && ($link = $entry->formatLink())) : ?>
+ <ul><li>↻ <?= $link ?>
+ <?php endif; ?>
+ <?php if (count($listing)) : ?>
+ <ul>
+ <?php foreach ($listing as $entry) : ?>
+ <li>↳ <a href="<?=$entry->formatUrl()?>"><?=$entry->formatLink()?></a></li>
+ <?php endforeach; ?>
+ </ul>
+ <?php endif; ?>
+ <?php if (isset($link) && strlen($link)) : ?>
+ </li></ul>
+ <?php endif; ?>
+ </ul>
+</div>
+<?php endif; ?>