* @var \http\Url
*/
private $baseUrl;
-
+
/**
* Initialize the reference
*/
function esc($txt) {
return htmlspecialchars($txt);
}
-
+
/**
* Create the view payload
* @param \http\Controller $ctl
*/
private function createPayload() {
$pld = new \stdClass;
-
+
$pld->esc = "htmlspecialchars";
+ $pld->anchor = [$this->reference, "formatAnchor"];
$pld->quick = [$this->reference, "formatString"];
$pld->file = [$this->reference, "formatFile"];
$pld->refs = $this->reference;
$pld->baseUrl = $this->baseUrl;
-
+
return $pld;
}
-
+
/**
* Redirect to canononical url
* @param string $cnn
$this->response->setResponseCode(301);
$this->response->send();
}
-
+
/**
* Serve index.css
*/
$this->response->setBody(new \http\Message\Body(fopen(ROOT."/public/index.css", "r")));
$this->response->send();
}
-
+
/**
* Serve index.js
*/
$this->response->setBody(new \http\Message\Body(fopen(ROOT."/public/index.js", "r")));
$this->response->send();
}
-
+
/**
* Serve a preset
* @param \stdClass $pld
include ROOT."/views/layout.phtml";
$this->response->send();
}
-
+
public function handle() {
try {
return;
}
}
-
+
} catch (\Exception $e) {
$pld->exception = $e;
}
$this->serve($pld);
}
-}
\ No newline at end of file
+}
* @var string
*/
private $name;
-
+
/**
* Split name
* @var array
*/
private $list;
-
+
/**
* The containing repository
* @var \mdref\Repo
*/
private $repo;
-
+
/**
* The file path, if the refentry exists
- * @var type
+ * @var type
*/
private $path;
-
+
/**
* The file instance of this entry
* @var \mdref\File
*/
private $file;
-
+
/**
* @param string $name the compound name of the ref entry, e.g. "pq/Connection/exec"
* @param \mdref\Repo $repo the containing repository
$this->list = explode("/", $name);
$this->path = $repo->hasEntry($name);
}
-
+
/**
* Get the compound name, e.g. "pq/Connection/exec"
* @return string
public function getName() {
return $this->name;
}
-
+
/**
* Get the containing repository
* @return \mdref\Repo
public function getRepo() {
return $this->repo;
}
-
+
/**
* Get the file path, if any
* @return string
public function getPath() {
return $this->path;
}
-
+
/**
* Get the file instance of this entry
* @return \mdref\File
}
return $this->file;
}
-
+
/**
- * Get edit URL
+ * Get edit URL
* @return string
*/
public function getEditUrl() {
return $this->repo->getEditUrl($this->name);
}
-
+
/**
* Read the title of the ref entry file
* @return string
*/
public function getTitle() {
if ($this->isFile()) {
- return $this->getFile()->readTitle();
+ return trim($this->getFile()->readTitle());
}
if ($this->isRoot()) {
- return $this->repo->getRootEntry()->getTitle();
+ return trim($this->repo->getRootEntry()->getTitle());
}
return $this->name;
}
-
+
/**
* Read the description of the ref entry file
* @return string
*/
public function getDescription() {
if ($this->isFile()) {
- return $this->getFile()->readDescription();
+ return trim($this->getFile()->readDescription());
}
if ($this->isRoot()) {
- return $this->repo->getRootEntry()->getDescription();
+ return trim($this->repo->getRootEntry()->getDescription());
}
return $this;
}
-
+
/**
* Read the intriductory section of the refentry file
* @return string
*/
public function getIntro() {
if ($this->isFile()) {
- return $this->getFile()->readIntro();
+ return trim($this->getFile()->readIntro());
}
if ($this->isRoot()) {
- return $this->repo->getRootEntry()->getIntro();
+ return trim($this->repo->getRootEntry()->getIntro());
}
return "";
}
-
+
/**
* Check if the refentry exists
* @return bool
public function isFile() {
return strlen($this->path) > 0;
}
-
+
/**
* Check if this is the first entry of the reference tree
* @return bool
public function isRoot() {
return count($this->list) === 1;
}
-
+
/**
* Get the parent ref entry
* @return \mdref\Entry
return $this->repo->getEntry($dirn);
}
}
-
+
/**
* Get the list of parents up-down
* @return array
}
return $parents;
}
-
+
/**
* Guess whether this ref entry is about a function or method
* @return bool
$base = end($this->list);
return $base{0} === "_" || ctype_lower($base{0});
}
-
+
/**
* Guess whether this ref entry is about a namespace, interface or class
* @return bool
$base = end($this->list);
return ctype_upper($base{0});
}
-
+
/**
* Display name
* @return string
return $myself;
}
$parent = end($parts);
-
+
switch ($myself{0}) {
case ":":
return "★" . substr($myself, 1);
-
+
default:
if (!ctype_lower($myself{0}) || ctype_lower($parent{0})) {
return $myself;
return $parent . "::" . $myself;
}
}
-
+
/**
* Get the base name of this ref entry
* @return string
public function getBasename() {
return dirname($this->path) . "/" . basename($this->path, ".md");
}
-
+
/**
* Guess whether there are any child nodes
* @param string $glob
return is_dir($this->getBasename());
}
}
-
+
/**
* Guess whether there are namespace/interface/class child nodes
* @return bool
function hasNsClasses() {
return $this->hasIterator("/[A-Z]*.md", true);
}
-
+
/**
* Guess whether there are function/method child nodes
* @return bool
function hasFunctions() {
return $this->hasIterator("/[a-z_]*.md");
}
-
+
/**
* Implements \IteratorAggregate
* @return \mdref\Tree child nodes
* @var resource
*/
private $fd;
-
+
/**
* Open the file
* @param string $path
public function __construct($path) {
$this->fd = fopen($path, "rb");
}
-
+
/**
* Read the title of the refentry
* @return string
return fgets($this->fd);
}
}
-
+
/**
* Read the description of the refentry
* @return string
return fgets($this->fd);
}
}
-
+
/**
* Read the first subsection of a global refentry
* @return string
$intro = "";
if (0 === fseek($this->fd, 0, SEEK_SET)) {
$header = false;
-
+
while (!feof($this->fd)) {
if (false === ($line = fgets($this->fd))) {
break;
* @var array
*/
private $repos = array();
-
+
/**
* @param array $refs list of mdref repository paths
*/
$this->repos[$repo->getName()] = $repo;
}
}
-
+
/**
* Lookup the repo containing a ref entry
* @param string $entry requested reference entry, e.g. "pq/Connection/exec"
}
}
}
-
+
/**
* Implements \IteratorAggregate
* @return \ArrayIterator repository list
return new \ArrayIterator($this->repos);
}
+ public function formatAnchor($anchor) {
+ if (is_numeric($anchor)) {
+ return "L$anchor";
+ }
+ return preg_replace("/[^[:alnum:]\.:_]/", ".", $anchor);
+ }
+
public function formatString($string) {
$md = \MarkdownDocument::createFromString($string);
$md->compile(\MarkdownDocument::AUTOLINK);
return $md->getHtml();
}
-
+
public function formatFile($file) {
$fd = fopen($file, "r");
$md = \MarkdownDocument::createFromStream($fd);
min-height: 100%;
}
body, code {
- font-family: Inconsolata, Monospace, 'Courier New', Courier, monospace;
+ font-family: Inconsolata, 'Courier New', Courier, monospace;
}
body {
line-height: 1.5;
content: " ⬈";
}
+a.permalink {
+ position: relative;
+ top: 0;
+ right: 0;
+ color: #999999;
+ opacity: 0.25;
+}
+
.var {
color: #800000;
}
$n.text().split(/([^a-zA-Z0-9_\\\$:]+)/).forEach(function(v) {
var t;
-
+
if ((t = mdref.type(v.replace(/:$/, ""), nn))) {
a.push($(t).text(v));
} else if (a.length && a[a.length-1].nodeName === "#text") {
walk: function walk(i, e) {
// mdref.log("walk", i, e);
+ switch (e.nodeName) {
+ case "H1":
+ case "H2":
+ case "H3":
+ case "H4":
+ case "H5":
+ case "H6":
+ if (e.id.length) {
+ var href = document.location.pathname;
+ var perm = $("<a class=\"permalink\" href=\""+href+"#\">#</a>");
+ if (e.nodeName === "H1") {
+ perm.prependTo(e);
+ } else {
+ perm.attr("href", function(i, href) {
+ return href + e.id;
+ });
+ perm.appendTo(e);
+ }
+ }
+ break;
+ }
+
$.each($.makeArray(e.childNodes), function(i, n) {
switch (n.nodeName) {
case "A":
},
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;
+ var e;
+ if ((e = document.getElementById(location.hash.substring(1)))) {
+ mdref.blink(e);
+ } else {
+ 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) {
+ $(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;
+ }
+ mdref.blink(c);
+ }
+ } : function(i, c) {
+ if (c.textContent === hash) {
$(window).scrollTop($(c).offset().top - 100);
- scrolled = true;
+ mdref.blink(c);
+ return false;
}
- mdref.blink(c);
- }
- } : function(i, c) {
- if (c.textContent === hash) {
- $(window).scrollTop($(c).offset().top - 100);
- mdref.blink(c);
- return false;
- }
- });
+ });
+ }
}
}
};
-
+
$("h1,h2,h3,h4,h5,h6,p,li,code,td").each(mdref.walk);
$(window).on("hashchange", mdref.hashchange);
mdref.hashchange();
<?php elseif (isset($refs)) : ?>
<?php foreach ($refs as $repo) : /* @var \mdref\Repo $repo */ ?>
<?php foreach ($repo as $entry) : /* @var \mdref\Entry $entry */ ?>
- <h2>
+ <h2 id="<?= $anchor($entry->getTitle()) ?>">
<a href="<?= $esc($entry->getName()) ?>"
><?= $esc($entry->getTitle()) ?></a></h2>
<div><?= $quick($entry->getIntro()) ?></div>
<?php endforeach; ?>
<?php endforeach; ?>
-<?php endif; ?>
\ No newline at end of file
+<?php endif; ?>
<?= $file($entry->getPath()) ?>
<?php if ($entry->hasFunctions()) : ?>
-<h2>Functions:</h2>
+<h2 id="Functions:">Functions:</h2>
<ul>
<?php foreach($entry as $sub) : if (!$sub->isFunction()) continue; ?>
<li>
<?php endif; ?>
<?php if ($entry->hasNsClasses()) : ?>
-<h2>Namespaces, Interfaces and Classes:</h2>
+<h2 id="Namespaces,.Interfaces.and.Classes:">Namespaces, Interfaces and Classes:</h2>
<ul>
<?php foreach ($entry as $sub) : if (!$sub->isNsClass()) continue; ?>
<li>