-geany_run_script.sh
+/nbproject/
+++ /dev/null
-RewriteEngine On
-RewriteRule ^ index.php [L]
--- /dev/null
+{
+ "require": {
+ "m6w6/autocracy": "dev-master"
+ }
+}
--- /dev/null
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
+ ],
+ "hash": "0147473751b895f221e765fd6fb1fc2b",
+ "packages": [
+ {
+ "name": "m6w6/autocracy",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/m6w6/autocracy.git",
+ "reference": "b6830cfce3dc276bf67288a7c6bfe64ffd290e21"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/m6w6/autocracy/zipball/b6830cfce3dc276bf67288a7c6bfe64ffd290e21",
+ "reference": "b6830cfce3dc276bf67288a7c6bfe64ffd290e21",
+ "shasum": ""
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "http\\Controller": "lib"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Michael Wallner",
+ "email": "mike@php.net"
+ }
+ ],
+ "description": "http\\Controller preserves your autocracy",
+ "homepage": "http://github.com/m6w6/autocracy",
+ "keywords": [
+ "controller",
+ "http",
+ "pecl",
+ "pecl_http"
+ ],
+ "time": "2013-10-22 11:41:09"
+ }
+ ],
+ "packages-dev": [
+
+ ],
+ "aliases": [
+
+ ],
+ "minimum-stability": "stable",
+ "stability-flags": {
+ "m6w6/autocracy": 20
+ },
+ "platform": [
+
+ ],
+ "platform-dev": [
+
+ ]
+}
+++ /dev/null
-* {
- font-size: 99.9%;
-}
-
-body, code {
- font-family: Inconsolata, Monospace, 'Courier New', Courier, monospace;
-}
-body {
- font-size: 1.5em;
- margin: 0;
- padding: 0;
- color: #3f3f3f;
-}
-
-body>* {
- margin-left: 1em;
-}
-body>ul {
- margin-left: 2em;
-}
-
-.sidebar {
- font-size: .9em;
- float: right;
- background: #f0f0f0;
- border-bottom-left-radius: 10px;
- padding: 0;
- width: auto;
- padding-right: 1em;
-}
-.sidebar>ul {
-}
-.sidebar ul {
- margin-left: 1em;
- padding: 0;
- list-style-type: none;
-}
-
-code {
- display: inline-block;
- border-radius: 2px;
- padding: 0px 2px 2px 2px;
- background: #e0e0e0;
- color: #606060;
- box-shadow: 0 0 1px #999;
-}
-
-code code {
- display: inline;
- padding: 0;
- background: transparent;
- border: none;
- box-shadow: none;
-}
-
-pre>code {
- padding: 1em;
-}
-pre>code, pre>code code {
- background: #333;
- color: #eee;
-}
-
-p, pre {
- margin: 1em 2em;
-}
-
-blockquote {
- border-top: 1px solid #800000;
- border-bottom: 1px solid #800000;
- background: #ffe4e1;
- margin: 2em 0;
-}
-
-ul {
- margin-bottom: 2em;
-}
-li {
- margin-bottom: .5em;
-}
-a, h1 code>a {
- color: #2f4f4f;
-}
-a:hover {
- text-decoration: none;
-}
-
-.var {
- color: #800000;
-}
-.constant {
- color: #2e8b57;
-}
-
-h1 {
- line-height: 1.5;
-}
-h1 code {
- font-weight: normal;
- font-size: .9em;
- line-height: 1.33;
-}
-
-footer, h1, li h3 {
- background: #708090;
- color: #f5f5dc;
-}
-
-footer, h1 {
- margin: 0;
- padding: 1em;
-}
-
-li h3 {
- border-radius: 4px;
- display: inline-block;
- width: auto;
- padding: .2em;
- margin: .5em 0 0 0;
-}
-
-h1 .constant, pre>code .consant, li h3 .constant {
- color: #98fb98;
-}
-
-h1 .var, pre>code .var, li h3 .var {
- color: #f4a460;
-}
-
-footer a, h1 a, pre>code a, li h3 a {
- color: #b0e0e6;
-}
-
-li h3 a {
- text-decoration: none;
-}
-li h3 a:hover {
- text-decoration: underline;
-}
-
-#disqus_thread {
- margin-top: 8em;
- margin-right: 2em;
-}
-
-footer {
- font-size: smaller;
- text-align: center;
- clear: both;
- margin-top: 8em;
-}
-
-footer ul {
- margin: 0;
- padding: 0;
-}
-
-footer li {
- list-style-type: none;
- display: inline-block;
- margin: 0 1em;
-}
+++ /dev/null
-function log() {
- // console.log.apply(console, arguments);
-}
-
-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) === "$";
-}
-
-var is_in_string = false;
-
-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\">";
- }
-}
-
-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 = [];
-
- $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);
-
- $.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;
- }
- });
-}
-
-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");
-}
-
-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;
- }
- blink(c);
- }
- } : function(i, c) {
- if (c.textContent === hash) {
- $(window).scrollTop($(c).offset().top - 100);
- blink(c);
- return false;
- }
- });
- }
-}
-
-$(function() {
- $("h1,h2,h3,h4,h5,h6,p,li,code").each(walk);
- $(window).on("hashchange", hashchange);
- hashchange();
-});
+++ /dev/null
-<?php
-
-error_reporting(E_ALL &~ E_DEPRECATED);
-
-function cut(array $lines, array $specs) {
- $delim = "[[:space:]]+";
- $bytes = [];
- $fields= [];
-
- foreach ($specs as $spec => $value) {
- switch ($spec) {
- case "d":
- $delim = $value;
- break;
- case "b":
- $bytes = $value;
- break;
- case "f":
- $fields = $value;
- break;
- }
- }
-
- $result = [];
- if ($bytes) {
- $func = "substr";
- } else {
- $func = function($a, $o = 0, $l = 0) {
- return join(" ", array_slice($a, $o, $l ? $l+1 : count($a)-$o));
- };
- }
- foreach ($lines as $line) {
- if ($bytes) {
- $spec = $bytes;
- } else {
- $line = split($delim, $line);
- $spec = $fields;
- }
-
- if ($spec[0] == "-") {
- $result[] = $func($line, 0, $spec[1]);
- } elseif ($spec[1] == "-") {
- if (empty($spec[2])) {
- $result[] = $func($line, $spec[0]);
- } else {
- $result[] = $func($line, $spec[0], $spec[2]-$spec[0]);
- }
- } else {
- $result[] = $line{$spec[0]};
- }
- }
- return $result;
-}
-
-function head($file, $lines = 1) {
- $ld = [];
- if (is_resource($file) || ($file = fopen($file, "r"))) {
- while ($lines--) {
- $ld[] = fgets($file);
- }
- }
- return $ld;
-}
-
-function ns($path) {
- $ns = "";
- $parts = explode("/", $path);
- $upper = ctype_upper($path[0]);
- 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;
- return str_replace("/", "\\", str_replace("//", "/", trim($file, "/.")));
-}
-
-function urlpath($dir, $file) {
- return (strlen($dir) ? $dir . "/" : "") . basename($file, ".md");
-}
-
-function email($email) {
- if (preg_match("/^([^<]+) <([^>]+)>/", $email, $matches)) {
- list(, $name, $mail) = $matches;
- return sprintf('<a href="mailto:%s">%s</a>',
- htmlspecialchars($mail),
- htmlspecialchars($name));
- }
- var_dump(sscanf($email, "%s <%s>"));
-}
-
-function ls($dir) {
- $dir = rtrim(is_dir($dir) ? $dir : dirname($dir) ."/". basename($dir, ".md"), "/");
- printf("<ul>\n");
- printf("<li>⇐ <a href=>Home</a></li>\n");
- if ($dir !== "." && ($dn = dirname($dir)) !== ".") {
- printf("<li>⇑ <a href=%s>%s</a></li>\n",
- urlpath($dir, ".."),
- ns($dn));
- }
- if (is_dir($dir)) {
- if ($dir !== ".") {
- printf("<ul>\n<li> %s</li>\n", ns($dir));
- }
- if (($glob = glob("$dir/[_a-zA-Z]*.md"))) {
- printf("<ul>\n");
- foreach ($glob as $file) {
- printf("<li>⇒ <a href=\"%s\">%s</a></li>\n",
- urlpath($dir, $file),
- ns("$dir/".basename($file, ".md")));
- }
- printf("</ul>\n");
- }
- if ($dir !== ".") {
- printf("</ul>\n");
- }
- }
-
- printf("</ul>\n");
-}
-
-function ml($file) {
- $pi = pathinfo($file);
- if (!isset($pi["extension"])) {
- return;
- }
- if ($pi["extension"] !== "md") {
- return;
- }
- $dir = $pi["dirname"] . "/" . $pi["filename"];
- if (($glob = glob("$dir/[_a-z]*.md"))) {
- printf("<h2>%s:</h2>\n", !ctype_upper($pi["filename"][0]) ?
- "Functions" : "Methods");
- printf("<ul>\n");
- foreach ($glob as $file) {
- printf("<li><h3><a href=\"%s\">%s</a></h3><p>%s</p><p>%s</p></li>\n",
- urlpath($dir, $file),
- basename($file, ".md"),
- @end(head($file, 3)),
- join(" ", cut(head($file), ["f"=>"1-"]))
- );
- }
- printf("</ul>\n");
- }
-}
-
-function md($file, $res) {
- $file = rtrim($file, "/");
- if (is_file($file) || is_file($file .= ".md")) {
- $pi = pathinfo($file);
-
- switch (@$pi["extension"]) {
- case "md":
- $r = fopen($file, "r");
- $md = MarkdownDocument::createFromStream($r);
- $md->compile(MarkdownDocument::AUTOLINK|MarkdownDocument::TOC);
- print $md->getHtml();
- fclose($r);
- ml($file);
- break;
- case null:
- printf("<h1>%s</h1>", basename($file));
- printf("<pre>%s</pre>\n", htmlspecialchars(file_get_contents($file)));
- break;
- }
- } else {
- $res->setResponseCode(404);
- printf("<h1>Not Found</h1>\n");
- printf("<blockquote><p>Sorry, I could not find <code>%s/%s</code>.</p></blockquote>", dirname($file), basename($file, ".md"));
- }
-}
-
-chdir(__DIR__);
-$t = ["css"=>"text/css", "js"=>"application/javascript"];
-$r = new http\Env\Request;
-$u = new http\Url($r->getRequestUrl());
-$s = new http\Env\Response;
-$b = dirname($_SERVER["SCRIPT_NAME"]);
-$p = ".". substr($u->path, strlen($b));
-
-switch($p) {
-case "./index.php":
- exit;
-case "./index.js":
-case "./index.css":
- $s->setHeader("Content-type", $t[pathinfo($p, PATHINFO_EXTENSION)]);
- $s->setBody(new http\Message\Body(fopen($p, "r")));
- $s->send();
- exit;
-}
-
-ob_start($s);
-
-?>
-<!doctype html>
-<html>
-<head>
- <meta charset="utf-8">
- <title><?=ns($p)?></title>
- <base href="<?=new http\Url($b, null, http\Url::FROM_ENV | (http\Url::STRIP_ALL & ~http\Url::STRIP_PATH)) ?>/">
- <link rel="stylesheet" href="index.css">
- <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
-</head>
-<body>
- <div class="sidebar">
- <?php ls($p); ?>
- </div>
- <?php if ($p === "./") : ?>
- <h1>Quick Markdown Documentation Browser</h1>
- <p><a href="https://github.com/m6w6/mdref">mdref-v<?php readfile("VERSION")?></a></p>
- <pre><?php
- ob_start(function($s) {
- return htmlspecialchars($s);
- });
- readfile("LICENSE");
- ob_end_flush();
- ?></pre>
- <?php else: ?>
- <?php md($p, $s); ?>
- <?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")]))?></a></li>
- </ul>
- </footer>
- <script src="index.js"></script>
- <?php if ($_SERVER["SERVER_NAME"] != "localhost") : ?>
- <script>
- var disqus_shortname = 'mdref';
- var disqus_identifier = '<?=$p?>';
- (function() {
- var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
- dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
- (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
- })();
- </script>
- <?php endif; ?>
-</body>
-</html>
-<?php
-
-ob_end_flush();
-$s->send();
--- /dev/null
+<?php
+
+namespace mdref;
+
+use http\Controller\Observer;
+
+/**
+ * The sole action controller of mdref
+ */
+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'");
+ }
+ }
+
+ /**
+ * Implements \SplObserver
+ * @param \SplSubject $ctl
+ */
+ function update(\SplSubject $ctl) {
+ /* @var \http\Controller $ctl */
+ try {
+ $ctl->getPayload()->baseUrl = $this->baseUrl;
+
+ if (!$this->serveReference($ctl)) {
+ $this->serveInternal($ctl);
+ }
+ } catch (\Exception $e) {
+ $ctl->getPayload()->exception = $e;
+ }
+ }
+}
--- /dev/null
+<?php
+
+namespace mdref;
+
+use http\Env as HTTP;
+
+/**
+ * mdref exception handler
+ */
+class ExceptionHandler
+{
+ function __construct() {
+ set_exception_handler($this);
+ set_error_handler($this);
+ }
+
+ function __invoke($e, $msg = null) {
+ if ($e instanceof \Exception) {
+ try {
+ echo static::html($e);
+ } catch (\Exception $ignore) {
+ HTTP::sendStatusCode(500);
+ }
+ } else {
+ throw new \Exception($msg, $e);
+ }
+ return true;
+ }
+
+ /**
+ * Format an exception as HTML and send appropriate exception info as HTTP headers
+ * @param \Exception $e
+ * @param array $title_tag
+ * @param array $message_tag
+ * @param array $trace_tag
+ * @return string
+ */
+ static function html(\Exception $e, array $title_tag = ["h1"], array $message_tag = ["p"], array $trace_tag = ["pre", "style='font-size:smaller'"]) {
+ if ($e instanceof \http\Controller\Exception) {
+ $code = $e->getCode() ?: 500;
+ foreach ($e->getHeaders() as $key => $val) {
+ HTTP::sendResponseHeader($key, $val);
+ }
+ } else {
+ $code = 500;
+ }
+ HTTP::setResponseCode($code);
+ $name = HTTP::getResponseStatusForCode($code);
+ $html = sprintf("<%s>%s</%s>\n<%s>%s</%s>\n",
+ implode(" ", $title_tag), $name, $title_tag[0],
+ implode(" ", $message_tag), $e->getMessage(), $message_tag[0]);
+ if ($trace_tag) {
+ $html .= sprintf("<%s>%s</%s>\n",
+ implode(" ", $trace_tag), $e->getTraceAsString(), $trace_tag[0]);
+ }
+ return $html;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+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
+{
+ /**
+ * Base URL
+ * @var \http\Controller\Url
+ */
+ protected $baseUrl;
+
+ /**
+ * Reference paths
+ * @var array
+ */
+ protected $refs = array();
+
+ /**
+ * @param \http\Controller\Url $baseUrl
+ * @param mixed $paths array or string of paths with markdown references
+ */
+ function __construct(\http\Controller\Url $baseUrl, $paths = ".") {
+ if (!is_array($paths)) {
+ $paths = explode(PATH_SEPARATOR, $paths);
+ }
+ $this->refs = $paths;
+ $this->baseUrl = $baseUrl;
+ }
+
+ /**
+ * @return \http\Controller\Url
+ */
+ function getBaseUrl() {
+ return $this->baseUrl;
+ }
+
+ /**
+ * 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, $ext = ".md") {
+ $file = implode(DIRECTORY_SEPARATOR, $this->baseUrl->params($requestUrl));
+
+ foreach ($this->refs as $base) {
+ $path = new Path($base, $file);
+ 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);
+ }
+ $glob = array();
+ foreach ($this->refs as $ref) {
+ $glob = array_merge($glob, array_map(function ($fn) use ($ref) {
+ return substr($fn, strlen($ref));
+ }, glob($ref . $pattern, $flags)));
+ }
+ return $glob;
+ }
+}
--- /dev/null
+<?php
+
+namespace mdref;
+
+class Markdown
+{
+ /**
+ * @var \mdref\Path
+ */
+ protected $path;
+
+ /**
+ * @param \mdref\Path $path
+ */
+ function __construct(Path $path) {
+ $this->path = $path;
+ }
+
+ /**
+ * @return string
+ */
+ function __toString() {
+ try {
+ $r = fopen($this->path->getFullPath(".md"), "r");
+ $md = \MarkdownDocument::createFromStream($r);
+ $md->compile(\MarkdownDocument::AUTOLINK | \MarkdownDocument::TOC);
+ $html = $md->getHtml();
+ fclose($r);
+ } catch (\Exception $e) {
+ $html = ExceptionHandler::html($e);
+ }
+ return $html;
+ }
+}
--- /dev/null
+<?php
+
+namespace mdref;
+
+/**
+ * A path made out of a base dir and an thereof relative path name.
+ */
+class Path
+{
+ /**
+ * Computed path
+ * @var string
+ */
+ protected $path = "";
+
+ /**
+ * The base directory where path is located
+ * @var string
+ */
+ 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 isFile($ext = ".md") {
+ return is_file($this->getFullPath($ext));
+ }
+
+ function toHtml() {
+ $head = sprintf("<h1>%s</h1>\n", htmlspecialchars(basename($this->getPathName())));
+ if ($this->isFile()) {
+ $html = htmlspecialchars(file_get_contents($this->getFullPath()));
+ } elseif ($this->isFile("")) {
+ $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
--- /dev/null
+<?php
+
+namespace mdref;
+
+/**
+ * The RefEntry class represents a reference entry, i.e. a .md file
+ */
+class RefEntry
+{
+ /**
+ * @var \mdref\Path
+ */
+ protected $path;
+
+ /**
+ * @var string
+ */
+ protected $entry;
+
+ /**
+ * @var resource
+ */
+ protected $file;
+
+ /**
+ * @param \mdref\Path $path
+ * @param type $entry
+ */
+ function __construct(Path $path, $entry = null) {
+ $this->path = $path;
+ $this->entry = trim($entry ?: $path->getPathName(), DIRECTORY_SEPARATOR);
+ }
+
+ /**
+ * Clean up the file handle
+ */
+ function __destruct() {
+ if (is_resource($this->file)) {
+ fclose($this->file);
+ }
+ }
+
+ /**
+ * Format as URL
+ * @return string
+ */
+ function formatUrl() {
+ return htmlspecialchars($this->entry);
+ }
+
+ private function joinLink(array $parts) {
+ $link = "";
+ $upper = ctype_upper($parts[0][0]);;
+ for ($i = 0; $i < count($parts); ++$i) {
+ if (!strlen($parts[$i]) || $parts[$i] === ".") {
+ continue;
+ }
+ if (strlen($link)) {
+ if ($upper && !ctype_upper($parts[$i][0])) {
+ $link .= "::";
+ } else {
+ $link .= "\\";
+ }
+ }
+ $link .= $parts[$i];
+ $upper = ctype_upper($parts[$i][0]);
+ }
+ return $link;
+ }
+
+ /**
+ * Format as link text
+ * @param bool $basename whether to use the basename only
+ * @return string
+ */
+ function formatLink($basename = false) {
+ $link = "";
+ if (strlen($this->entry)) {
+ $parts = explode(DIRECTORY_SEPARATOR, $this->entry);
+ $link = $basename ? end($parts) : $this->joinLink($parts);
+ }
+ return htmlspecialchars($link);
+ }
+
+ /**
+ * Create a consolidated Path of this entry
+ * @return \mdref\Path
+ */
+ function getPath() {
+ $path = $this->path;
+ $file = $path($this->entry);
+ return $file;
+ }
+
+ private function openFile() {
+ if (!is_resource($this->file)) {
+ $file = $this->getPath();
+
+ if (!$file->isFile()) {
+ throw new \Exception("Not a file: '{$file}'");
+ }
+ if (!$this->file = fopen($file->getFullPath(".md"), "r")) {
+ throw new \Exception("Could not open {$file}");
+ }
+ }
+ }
+
+ /**
+ * Read the title of the refentry
+ * @return string
+ */
+ function readTitle() {
+ $this->openFile();
+ fseek($this->file, 1, SEEK_SET);
+ return htmlspecialchars(fgets($this->file));
+ }
+
+ /**
+ * Read the description of the refentry
+ * @return string
+ */
+ function readDescription() {
+ $this->openFile();
+ fseek($this->file, 0, SEEK_SET);
+ fgets($this->file);
+ fgets($this->file);
+ return htmlspecialchars(fgets($this->file));
+ }
+
+ /**
+ * Format a "Edit me" URL. The project reference top directory needs a
+ * »name«.mdref file besides its »name«.md entry point with the edit URL
+ * printf template as content. The sole printf argument is the relative
+ * path of the entry.
+ * @return string
+ */
+ 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);
+ }
+ }
+
+ /**
+ * Recurse into the reference tree
+ * @param \mdref\Finder $refs
+ * @param string $pattern
+ * @param callable $cb
+ */
+ function recurse(Finder $refs, $pattern, callable $cb) {
+ $path = $refs->find($refs->getBaseUrl()->mod($this->entry));
+ foreach (new RefListing($path, $refs->glob($path, $pattern)) as $entry) {
+ /* @var $entry RefEntry */
+ $cb($entry, $pattern, function($entry, $pattern) use ($refs, $cb) {
+ $entry->recurse($refs, $pattern, $cb);
+ });
+ }
+ }
+}
--- /dev/null
+<?php
+
+namespace mdref;
+
+/**
+ * A list of markdown reference files
+ */
+class RefListing implements \Countable, \Iterator
+{
+ /**
+ * @var \mdref\Path
+ */
+ protected $path;
+
+ /**
+ * @var array
+ */
+ protected $entries;
+
+ /**
+ * @param \mdref\Path $path
+ * @param array $files
+ */
+ function __construct(Path $path, array $files) {
+ $this->path = $path;
+ $this->entries = array_map(function($fn) {
+ return substr(trim($fn, DIRECTORY_SEPARATOR), 0, -3);
+ }, $files);
+ }
+
+ /**
+ * Implements \Countable
+ * @return int
+ */
+ function count() {
+ return count($this->entries);
+ }
+
+ /**
+ * Implements \Iterator
+ */
+ function rewind() {
+ reset($this->entries);
+ }
+
+ /**
+ * Implements \Iterator
+ * @return bool
+ */
+ function valid() {
+ return null !== key($this->entries);
+ }
+
+ /**
+ * Implements \Iterator
+ * @return string
+ */
+ function key() {
+ return $this->path->getSubPath(current($this->entries));
+ }
+
+ /**
+ * Implements \Iterator
+ */
+ function next() {
+ next($this->entries);
+ }
+
+ /**
+ * Implements \Iterator
+ * @return \mdref\RefEntry
+ */
+ function current() {
+ return new RefEntry($this->path, $this->key());//$this->format($this->key());
+ }
+
+ /**
+ * Get the parent reference entry
+ * @return null|\mdref\RefEntry
+ */
+ function getParent() {
+ switch ($parent = dirname($this->path->getPathName())) {
+ case ".":
+ case "":
+ return null;
+ default:
+ return new RefEntry($this->path, $parent);
+ }
+ }
+
+ /**
+ * Get the reference entry this reflist is based of
+ * @return \mdref\RefEntry
+ */
+ function getSelf() {
+ return new RefEntry($this->path);
+ }
+}
--- /dev/null
+RewriteEngine On
+RewriteCond %{REQUEST_FILENAME} -f [OR]
+RewriteCond %{REQUEST_FILENAME} -d [OR]
+RewriteCond %{REQUEST_FILENAME} -l
+RewriteRule ^ - [L]
+RewriteRule ^ index.php [L]
--- /dev/null
+* {
+ font-size: 99.9%;
+}
+
+body, code {
+ font-family: Inconsolata, Monospace, 'Courier New', Courier, monospace;
+}
+body {
+ font-size: 1.5em;
+ margin: 0;
+ padding: 0;
+ color: #3f3f3f;
+}
+
+body>* {
+ margin-left: 1em;
+}
+body>ul {
+ margin-left: 2em;
+}
+
+.sidebar {
+ font-size: .9em;
+ float: right;
+ background: #f0f0f0;
+ border-bottom-left-radius: 10px;
+ padding: 0;
+ width: auto;
+ min-width: 200px;
+ padding-right: 1em;
+}
+.sidebar>ul {
+}
+.sidebar ul {
+ margin-left: 1em;
+ margin-top: .5em;
+ padding: 0;
+ 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;
+ padding: 0px 2px 2px 2px;
+ background: #e0e0e0;
+ color: #606060;
+ box-shadow: 0 0 1px #999;
+}
+
+code code {
+ display: inline;
+ padding: 0;
+ background: transparent;
+ border: none;
+ box-shadow: none;
+}
+
+pre>code {
+ padding: 1em;
+}
+pre>code, pre>code code {
+ background: #333;
+ color: #eee;
+}
+
+p, pre {
+ margin: 1em 2em;
+}
+
+li>p {
+ margin: 1em 0;
+}
+
+blockquote {
+ border-top: 1px solid #800000;
+ border-bottom: 1px solid #800000;
+ background: #ffe4e1;
+ margin: 2em 0;
+}
+
+ul {
+ margin-bottom: 2em;
+}
+li {
+ margin-bottom: .5em;
+}
+a, h1 code>a {
+ color: #2f4f4f;
+}
+a:hover {
+ text-decoration: none;
+}
+
+.var {
+ color: #800000;
+}
+.constant {
+ color: #2e8b57;
+}
+
+h1 {
+ line-height: 1.5;
+}
+h1 code {
+ font-weight: normal;
+ font-size: .9em;
+ line-height: 1.33;
+}
+
+footer, h1, li h3 {
+ background: #708090;
+ color: #f5f5dc;
+}
+
+footer, h1 {
+ margin: 0;
+ padding: 1em;
+}
+
+li h3 {
+ border-radius: 4px;
+ display: inline-block;
+ width: auto;
+ padding: .2em;
+ margin: .5em 0 0 0;
+}
+
+h1 .constant, pre>code .consant, li h3 .constant {
+ color: #98fb98;
+}
+
+h1 .var, pre>code .var, li h3 .var {
+ color: #f4a460;
+}
+
+footer a, h1 a, pre>code a, li h3 a {
+ color: #b0e0e6;
+}
+
+li h3 a {
+ text-decoration: none;
+}
+li h3 a:hover {
+ text-decoration: underline;
+}
+
+#disqus_thread {
+ margin-top: 8em;
+ margin-right: 2em;
+}
+
+footer {
+ font-size: smaller;
+ text-align: center;
+ clear: both;
+ margin-top: 8em;
+}
+
+footer ul {
+ margin: 0;
+ padding: 0;
+}
+
+footer li {
+ list-style-type: none;
+ display: inline-block;
+ margin: 0 1em;
+}
+
+footer a {
+ text-decoration: none;
+}
+
+footer a:hover {
+ text-decoration: underline;
+}
\ No newline at end of file
--- /dev/null
+"use strict";
+
+$(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;
+ }
+
+ 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 (!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(/::|$/, "#") + "\">";
+ }
+
+ switch (s.toLowerCase()) {
+ // variables
+ default:
+ if (!mdref.is_variable(s)) {
+ break;
+ }
+ // special constants
+ case "null":
+ case "true":
+ case "false":
+ return "<span class=\"var\">";
+ }
+
+ // 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 = [];
+
+ $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);
+
+ $.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: 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");
+ },
+ 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;
+ }
+ 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").each(mdref.walk);
+ $(window).on("hashchange", mdref.hashchange);
+ mdref.hashchange();
+});
--- /dev/null
+<?php
+
+define("ROOT", dirname(__DIR__));
+define("REFS", getenv("REFPATH") ?: implode(PATH_SEPARATOR, glob(ROOT."/refs/*")));
+
+$loader = require __DIR__ . "/../vendor/autoload.php";
+/* @var $loader \Composer\Autoload\ClassLoader */
+$loader->add("mdref", ROOT);
+
+use http\Controller;
+use http\Controller\Url;
+use http\Controller\Observer\Layout;
+
+use mdref\ExceptionHandler;
+use mdref\Action;
+
+new ExceptionHandler;
+
+$ctl = new Controller;
+$ctl->setDependency("baseUrl", new Url)
+ ->attach(new Action)
+ ->attach(new Layout)
+ ->notify()
+ ->getResponse()
+ ->send();
--- /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>
+ <?php $entry->recurse($refs, "/*.md", function($entry, $pattern, callable $recursor) { ?>
+ <ul>
+ <li><p><a href="<?=$entry->formatUrl()?>"><?=$entry->formatLink()?></a></p>
+ <?=$entry->readDescription()?>
+ <?php $recursor($entry, "/[A-Z]*.md") ?>
+ </li>
+ </ul>
+ <?php }); ?>
+ <?php endforeach; ?>
+ <?php endif; ?>
+<?php endif; ?>
--- /dev/null
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>
+ <?php if ($title) : ?>
+ <?= $title ?> - mdref
+ <?php else: ?>
+ mdref
+ <?php endif; ?>
+ </title>
+ <base href="<?= $baseUrl ?>">
+ <link rel="stylesheet" href="index.css">
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
+ </head>
+ <body>
+ <?php include __DIR__."/sidebar.phtml" ?>
+
+ <?php if (isset($html)) : ?>
+ <?php include __DIR__."/mdref.phtml" ?>
+ <?php else: ?>
+ <?php include __DIR__."/index.phtml" ?>
+ <?php endif; ?>
+
+ <div id="disqus_thread"></div>
+
+ <footer>
+ <?php include __DIR__."/footer.phtml" ?>
+ </footer>
+ <script src="index.js"></script>
+ </body>
+</html>
--- /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; ?>