Commit: f94bc8ae4cce0be008104fdd7589637fac93aa85
Author: Nate Abele | Date: 2010-07-11 23:05:00 -0400
diff --git a/controllers/BrowserController.php b/controllers/BrowserController.php
index 92b66af..3eb0418 100644
--- a/controllers/BrowserController.php
+++ b/controllers/BrowserController.php
@@ -8,10 +8,10 @@
namespace li3_docs\controllers;
-use \Exception;
-use \DirectoryIterator;
-use \lithium\core\Libraries;
-use \lithium\analysis\Inspector;
+use Exception;
+use DirectoryIterator;
+use lithium\core\Libraries;
+use lithium\analysis\Inspector;
/**
* This is the Lithium API browser controller. This class introspects your application's libraries,
@@ -20,7 +20,7 @@ use \lithium\analysis\Inspector;
class BrowserController extends \lithium\action\Controller {
/**
- * The `DocExtractor` class dependency, which can be replaced with a proxy file to read from
+ * The `Extractor` class dependency, which can be replaced with a proxy file to read from
* a cache or database.
*
* @var array
@@ -28,7 +28,7 @@ class BrowserController extends \lithium\action\Controller {
protected $_classes = array(
'media' => 'lithium\net\http\Media',
'response' => 'lithium\action\Response',
- 'docExtractor' => 'li3_docs\extensions\analysis\DocExtractor'
+ 'extractor' => 'li3_docs\extensions\docs\Extractor'
);
/**
@@ -65,15 +65,15 @@ class BrowserController extends \lithium\action\Controller {
* current entity.
*/
public function view() {
- $docExtractor = $this->_classes['docExtractor'];
+ $extractor = $this->_classes['extractor'];
- if (!$library = $docExtractor::library($this->request->lib)) {
+ if (!$library = $extractor::library($this->request->lib)) {
return $this->render('../errors/not_found');
}
$name = $library['prefix'] . join('\\', func_get_args());
$options = array('namespaceDoc' => $this->docFile);
- $object = $docExtractor::get($this->request->lib, $name, $options);
+ $object = $extractor::get($this->request->lib, $name, $options);
$crumbs = $this->_crumbs($object);
return compact('name', 'library', 'object', 'crumbs');
}
diff --git a/extensions/analysis/DocExtractor.php b/extensions/analysis/DocExtractor.php
deleted file mode 100644
index 490066f..0000000
--- a/extensions/analysis/DocExtractor.php
+++ /dev/null
@@ -1,164 +0,0 @@
-<?php
-/**
- * Lithium: the most rad php framework
- *
- * @copyright Copyright 2009, Union of RAD (http://union-of-rad.org)
- * @license http://opensource.org/licenses/bsd-license.php The BSD License
- */
-
-namespace li3_docs\extensions\analysis;
-
-use \Exception;
-use \lithium\core\Libraries;
-use \lithium\util\Inflector;
-use \lithium\analysis\Inspector;
-
-class DocExtractor extends \lithium\core\StaticObject {
-
- public static function get($library, $identifier, array $options = array()) {
- $defaults = array('namespaceDoc' => null);
- $options += $defaults;
- $data = Inspector::info($identifier);
-
- $proto = compact('identifier', 'library') + array(
- 'name' => null,
- 'type' => Inspector::type($identifier),
- 'info' => array(),
- 'classes' => null,
- 'methods' => null,
- 'properties' => null,
- 'parent' => null,
- 'children' => null,
- 'source' => null,
- 'subClasses' => array(),
- 'description' => isset($data['description']) ? $data['description'] : null,
- 'text' => isset($data['text']) ? $data['text'] : null,
- );
- $format = "_{$proto['type']}";
- $data = static::$format($proto, (array) $data, $options);
-
- foreach (array('text', 'description') as $key) {
- $data[$key] = static::_embedCode($data[$key]);
- }
- return $data;
- }
-
- public static function library($name, array $options = array()) {
- $defaults = array('docs' => 'config/docs.json');
- $options += $defaults;
-
- if (!$config = Libraries::get($name)) {
- return array();
- }
-
- if (file_exists($file = "{$config['path']}/{$options['docs']}")) {
- $config += (array) json_decode(file_get_contents($file));
- }
- return $config + array('title' => Inflector::humanize($name));
- }
-
- protected static function _method(array $object, array $data, array $options = array()) {
- if (!$data) {
- return array();
- }
- $lines = Inspector::lines($data['file'], range($data['start'], $data['end']));
- $object = array('source' => join("\n", $lines)) + $object;
- $object += array('tags' => isset($data['tags']) ? $data['tags'] : array());
-
- if (isset($object['tags']['return'])) {
- list($type, $text) = explode(' ', $object['tags']['return'], 2) + array('', '');
- $object['return'] = compact('type', 'text');
- }
- return $object;
- }
-
- protected static function _namespace(array $object, array $data, array $options = array()) {
- $library = $object['library'];
- $identifier = $object['identifier'];
- $config = Libraries::get($library);
-
- $path = preg_replace('/^' . preg_quote($config['prefix'], '/') . '/', '', $identifier);
- $path = '/' . str_replace('\\', '/', $path);
- $object['children'] = array();
-
- foreach (Libraries::find($library, array('namespaces' => true) + compact('path')) as $c) {
- $libPath = Libraries::path($c, array('dirs' => true));
- $type = is_dir($libPath) ? 'namespace' : 'class';
- $object['children'][$c] = $type;
- }
-
- $path = $config['path'] . rtrim($path, '/');
- $doc = "{$path}/{$options['namespaceDoc']}";
- $object['text'] = file_exists($doc) ? file_get_contents($doc) : null;
- return $object;
- }
-
- protected static function _class(array $object, array $data, array $options = array()) {
- $identifier = $object['identifier'];
- $proto = array(
- 'parent' => get_parent_class($identifier),
- 'methods' => Inspector::methods($identifier, null, array('public' => false)),
- 'properties' => get_class_vars($identifier)
- );
-
- if ($proto['parent']) {
- $parentProps = get_class_vars($proto['parent']);
- $proto['properties'] = array_diff_key($proto['properties'], $parentProps);
- }
- $classes = Libraries::find($object['library'], array('recursive' => true));
-
- $proto['subClasses'] = array_filter($classes, function($class) use ($identifier) {
- if (preg_match('/\\\(libraries)\\\/', $class)) {
- return false;
- }
- try {
- return get_parent_class($class) == $identifier;
- } catch (Exception $e) {
- return false;
- }
- });
- sort($proto['subClasses']);
- return $proto + $object + array('tags' => isset($data['tags']) ? $data['tags'] : array());
- }
-
- protected static function _property(array $object, array $data, array $options = array()) {
- return $object + $data;
- }
-
- /**
- * Replaces class and method references with code snippets pulled from the class.
- *
- * @param string $text
- * @param array $options
- * @return string
- */
- protected static function _embedCode($text, array $options = array()) {
- $defaults = array('pad' => "\t");
- $options += $defaults;
- $regex = '(?P<class>[A-Za-z0-9_\\\]+)::(?P<method>[A-Za-z0-9_]+)\((?P<lines>[0-9-]+)';
-
- if (!preg_match_all("/\{\{\{\s*(embed:{$regex}\))\s*\}\}\}/", $text, $matches)) {
- return $text;
- }
-
- foreach ($matches['class'] as $i => $class) {
- $methods = array($matches['method'][$i]);
- $markers = Inspector::methods($class, 'extents', compact('methods'));
- $methodStart = $markers[current($methods)][0];
- $replace = $matches[0][$i];
-
- list($start, $end) = explode('-', $matches['lines'][$i]);
- $lines = range(intval($start) + $methodStart, intval($end) + $methodStart);
- $lines = Inspector::lines($class, $lines);
-
- $pad = substr_count(current($lines), $options['pad'], 0, 4);
- $lines = array_map('substr', $lines, array_fill(0, count($lines), 2));
-
- $code = '{{{' . join("\n", $lines) . '}}}';
- $text = str_replace($replace, $code, $text);
- }
- return $text;
- }
-}
-
-?>
\ No newline at end of file
diff --git a/extensions/docs/Extractor.php b/extensions/docs/Extractor.php
new file mode 100644
index 0000000..992b56f
--- /dev/null
+++ b/extensions/docs/Extractor.php
@@ -0,0 +1,248 @@
+<?php
+/**
+ * Lithium: the most rad php framework
+ *
+ * @copyright Copyright 2009, Union of RAD (http://union-of-rad.org)
+ * @license http://opensource.org/licenses/bsd-license.php The BSD License
+ */
+
+namespace li3_docs\extensions\docs;
+
+use Exception;
+use lithium\core\Libraries;
+use lithium\util\Inflector;
+use lithium\analysis\Docblock;
+use lithium\analysis\Inspector;
+
+class Extractor extends \lithium\core\StaticObject {
+
+ public static function get($library, $identifier, array $options = array()) {
+ $defaults = array('namespaceDoc' => null);
+ $options += $defaults;
+ $path = Libraries::path($identifier);
+
+ if (file_exists($path) && !static::_isClassFile($path)) {
+ return static::_file(compact('library', 'path', 'identifier'), $options);
+ }
+ $data = Inspector::info($identifier);
+
+ $proto = compact('identifier', 'library') + array(
+ 'name' => null,
+ 'type' => Inspector::type($identifier),
+ 'info' => array(),
+ 'classes' => null,
+ 'methods' => null,
+ 'properties' => null,
+ 'parent' => null,
+ 'children' => null,
+ 'source' => null,
+ 'subClasses' => array(),
+ 'description' => isset($data['description']) ? $data['description'] : null,
+ 'text' => isset($data['text']) ? $data['text'] : null,
+ );
+ $format = "_{$proto['type']}";
+ $data = static::$format($proto, (array) $data, $options);
+
+ foreach (array('text', 'description') as $key) {
+ $data[$key] = static::_embedCode($data[$key]);
+ }
+ return $data;
+ }
+
+ public static function library($name, array $options = array()) {
+ $defaults = array('docs' => 'config/docs.json');
+ $options += $defaults;
+
+ if (!$config = Libraries::get($name)) {
+ return array();
+ }
+
+ if (file_exists($file = "{$config['path']}/{$options['docs']}")) {
+ $config += (array) json_decode(file_get_contents($file));
+ }
+ return $config + array('title' => Inflector::humanize($name));
+ }
+
+ protected static function _method(array $object, array $data, array $options = array()) {
+ if (!$data) {
+ return array();
+ }
+ $lines = Inspector::lines($data['file'], range($data['start'], $data['end']));
+ $object = array('source' => join("\n", $lines)) + $object;
+ $object += array('tags' => isset($data['tags']) ? $data['tags'] : array());
+
+ if (isset($object['tags']['return'])) {
+ list($type, $text) = explode(' ', $object['tags']['return'], 2) + array('', '');
+ $object['return'] = compact('type', 'text');
+ }
+ return $object;
+ }
+
+ protected static function _namespace(array $object, array $data, array $options = array()) {
+ $library = $object['library'];
+ $identifier = $object['identifier'];
+ $config = Libraries::get($library);
+
+ $path = preg_replace('/^' . preg_quote($config['prefix'], '/') . '/', '', $identifier);
+ $path = '/' . str_replace('\\', '/', $path);
+ $object['children'] = array();
+
+ foreach (Libraries::find($library, array('namespaces' => true) + compact('path')) as $c) {
+ $libPath = Libraries::path($c, array('dirs' => true));
+ $type = is_dir($libPath) ? 'namespace' : 'class';
+ $object['children'][$c] = $type;
+ }
+
+ $path = $config['path'] . rtrim($path, '/');
+ $doc = "{$path}/{$options['namespaceDoc']}";
+ $object['text'] = file_exists($doc) ? file_get_contents($doc) : null;
+ return $object;
+ }
+
+ protected static function _class(array $object, array $data, array $options = array()) {
+ $identifier = $object['identifier'];
+ $proto = array(
+ 'parent' => get_parent_class($identifier),
+ 'methods' => Inspector::methods($identifier, null, array('public' => false)),
+ 'properties' => get_class_vars($identifier)
+ );
+
+ if ($proto['parent']) {
+ $parentProps = get_class_vars($proto['parent']);
+ $proto['properties'] = array_diff_key($proto['properties'], $parentProps);
+ }
+ $classes = Libraries::find($object['library'], array('recursive' => true));
+
+ $proto['subClasses'] = array_filter($classes, function($class) use ($identifier) {
+ if (preg_match('/\\\(libraries)\\\/', $class)) {
+ return false;
+ }
+ try {
+ return get_parent_class($class) == $identifier;
+ } catch (Exception $e) {
+ return false;
+ }
+ });
+ sort($proto['subClasses']);
+ return $proto + $object + array('tags' => isset($data['tags']) ? $data['tags'] : array());
+ }
+
+ protected static function _property(array $object, array $data, array $options = array()) {
+ return $object + $data;
+ }
+
+ protected static function _file(array $object, array $options = array()) {
+ $identifier = $object['identifier'];
+ $config = Libraries::get($object['library']);
+ $ds = DIRECTORY_SEPARATOR;
+
+ $data = compact('identifier') + array(
+ 'name' => '',
+ 'type' => 'file',
+ 'info' => static::_codeToDoc(file_get_contents($object['path'])),
+ 'children' => array(),
+ 'subClasses' => array()
+ );
+ $subPath = dirname($object['path']) . $ds . basename($object['path'], '.php');
+
+ if (is_dir($subPath)) {
+ $path = preg_replace('/^' . preg_quote($config['prefix'], '/') . '/', '', $identifier);
+ $path = '/' . str_replace('\\', '/', $path);
+ $searchOpts = array('recursive' => true, 'namespaces' => true, 'filter' => false);
+
+ foreach (Libraries::find($object['library'], compact('path') + $searchOpts) as $file) {
+ $libPath = Libraries::path($file, array('dirs' => true));
+ $type = is_dir($libPath) ? 'namespace' : 'class';
+ $data['children'][$file] = $type;
+ }
+ }
+ return $data;
+ }
+
+ protected static function _codeToDoc($code) {
+ $tokens = token_get_all($code);
+ $display = array();
+ $current = '';
+
+ foreach ($tokens as $i => $token) {
+ if ($i == 0 || ($token[0] == T_CLOSE_TAG && ($i + 1) == count($tokens))) {
+ continue;
+ }
+ if ($token[0] == T_DOC_COMMENT) {
+ if (preg_match('/@copyright/', $token[1])) {
+ continue;
+ }
+ if (!trim($current)) {
+ $current = '';
+ }
+ if ($current) {
+ $display[] = "{{{\n{$current}}}}";
+ $current = '';
+ }
+ $doc = Docblock::comment($token[1]);
+
+ foreach (array('text', 'description') as $key) {
+ $doc[$key] = static::_embedCode($doc[$key]);
+ }
+ $display[] = $doc;
+ continue;
+ }
+ $current .= (is_array($token) ? $token[1] : $token);
+ }
+ if ($current) {
+ $display[] = "{{{\n{$current}}}}";
+ }
+ return $display;
+ }
+
+ /**
+ * Replaces class and method references with code snippets pulled from the class.
+ *
+ * @param string $text
+ * @param array $options
+ * @return string
+ */
+ protected static function _embedCode($text, array $options = array()) {
+ $defaults = array('pad' => "\t");
+ $options += $defaults;
+ $regex = '(?P<class>[A-Za-z0-9_\\\]+)::(?P<method>[A-Za-z0-9_]+)\((?P<lines>[0-9-]+)';
+
+ if (!preg_match_all("/\{\{\{\s*(embed:{$regex}\))\s*\}\}\}/", $text, $matches)) {
+ return $text;
+ }
+
+ foreach ($matches['class'] as $i => $class) {
+ $methods = array($matches['method'][$i]);
+ $markers = Inspector::methods($class, 'extents', compact('methods'));
+ $methodStart = $markers[current($methods)][0];
+ $replace = $matches[0][$i];
+
+ list($start, $end) = explode('-', $matches['lines'][$i]);
+ $lines = range(intval($start) + $methodStart, intval($end) + $methodStart);
+ $lines = Inspector::lines($class, $lines);
+
+ $pad = substr_count(current($lines), $options['pad'], 0, 4);
+ $lines = array_map('substr', $lines, array_fill(0, count($lines), 2));
+
+ $code = '{{{' . join("\n", $lines) . '}}}';
+ $text = str_replace($replace, $code, $text);
+ }
+ return $text;
+ }
+
+ protected static function _isClassFile($path) {
+ $tokens = token_get_all(file_get_contents($path));
+
+ for ($i = 2; $i < count($tokens); $i++) {
+ if (!($tokens[$i - 2][0] == T_CLASS && $tokens[$i - 1][0] == T_WHITESPACE)) {
+ continue;
+ }
+ if ($tokens[$i][0] == T_STRING) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/views/browser/view.html.php b/views/browser/view.html.php
index a6eb314..e7a2cf4 100644
--- a/views/browser/view.html.php
+++ b/views/browser/view.html.php
@@ -13,14 +13,9 @@ $this->title($namespace);
); ?>
<?php // Related items ?>
-<?php if (isset($object['tags']['see'])) { ?>
- <h4><?=$t('Related', array('scope' => 'li3_docs')); ?></h4>
- <ul class="related">
- <?php foreach ((array) $object['tags']['see'] as $name) { ?>
- <li><?=$this->html->link($name, $this->docs->identifierUrl($name)); ?></li>
- <?php } ?>
- </ul>
-<?php } ?>
+<?=$this->view()->render(
+ array('element' => 'related'), compact('object', 'scope'), array('library' => 'li3_docs')
+); ?>
<?=$this->view()->render(
array('element' => 'links'),
@@ -41,10 +36,14 @@ $this->title($namespace);
<?php // Method source ?>
<?php if (isset($object['source'])) { ?>
- <div class="source-wrapper">
- <pre class="source-code">
- <code class="php"><?php echo $h($object['source']); ?></code>
- </pre>
+ <div class="source-display">
+ <div class="source-wrapper">
+ <pre class="source-code">
+ <code class="php"><?=$object['source']; ?></code>
+ </pre>
+ </div>
+ <button class="source-toggle">
+ <?=$t('Show source', array('scope' => 'li3_docs')); ?>
+ </button>
</div>
- <button class="source-toggle"><?=$t('Show source', array('scope' => 'li3_docs')); ?></button>
<?php } ?>
\ No newline at end of file
diff --git a/views/elements/file.html.php b/views/elements/file.html.php
new file mode 100644
index 0000000..d229a19
--- /dev/null
+++ b/views/elements/file.html.php
@@ -0,0 +1,42 @@
+<?php if ($object['children']) { ?>
+ <div class="contents">
+ <h4><?=$t('Package contents', array('scope' => 'li3_docs')); ?></h4>
+ <ul class="children">
+ <?php foreach ($object['children'] as $class => $type) { ?>
+ <?php
+ $parts = explode('\\', $class);
+ $url = $this->docs->identifierUrl($class);
+ ?>
+ <li class="<?=$type; ?>">
+ <?=$this->html->link(basename(end($parts)), $url); ?>
+ </li>
+ <?php } ?>
+ </ul>
+ </div>
+<?php } ?>
+
+
+<?php foreach ($object['info'] as $info) { ?>
+ <?php if (is_string($info)) { ?>
+ <p class="markdown">
+ <?=$info; ?>
+ </p>
+ <?php } else { ?>
+ <p class="markdown"><?=$info['description']; ?></p>
+ <p class="markdown"><?=$info['text']; ?></p>
+ <?php if (isset($info['tags']['see'])) { ?>
+ <?=$this->view()->render(
+ array('element' => 'related'),
+ compact('scope') + array('object' => $info),
+ array('library' => 'li3_docs')
+ ); ?>
+ <?php } ?>
+ <?php if (isset($info['tags']['link'])) { ?>
+ <?=$this->view()->render(
+ array('element' => 'links'),
+ compact('scope') + array('object' => $info),
+ array('library' => 'li3_docs')
+ ); ?>
+ <?php } ?>
+ <?php } ?>
+<?php } ?>
diff --git a/views/elements/related.html.php b/views/elements/related.html.php
new file mode 100644
index 0000000..fbd79b5
--- /dev/null
+++ b/views/elements/related.html.php
@@ -0,0 +1,8 @@
+<?php if (isset($object['tags']['see'])) { ?>
+ <h4><?=$t('Related', array('scope' => 'li3_docs')); ?></h4>
+ <ul class="related">
+ <?php foreach ((array) $object['tags']['see'] as $name) { ?>
+ <li><?=$this->html->link($name, $this->docs->identifierUrl($name)); ?></li>
+ <?php } ?>
+ </ul>
+<?php } ?>
diff --git a/views/layouts/default.html.php b/views/layouts/default.html.php
index 1ec31d0..e7cc3e3 100644
--- a/views/layouts/default.html.php
+++ b/views/layouts/default.html.php
@@ -6,8 +6,8 @@
* @license http://opensource.org/licenses/bsd-license.php The BSD License
*/
-use \lithium\core\Environment;
-use \lithium\g11n\Locale;
+use lithium\g11n\Locale;
+use lithium\core\Environment;
?>
<!doctype html>
@@ -27,7 +27,7 @@ use \lithium\g11n\Locale;
<?php //$this->_view->render(array('element' => 'locale_navigation')); ?>
<div id="container">
<div id="header">
- <h1><?php echo $this->html->link($t('Lithium API', array('scope' => 'li3_docs')), array(
+ <h1><?=$this->html->link($t('Lithium API', array('scope' => 'li3_docs')), array(
'library' => 'li3_docs', 'controller' => 'browser', 'action' => 'index'
)); ?></h1>
<ul class="crumbs">
@@ -46,7 +46,7 @@ use \lithium\g11n\Locale;
</div>
<div id="content">
- <?php echo $this->content; ?>
+ <?=$this->content; ?>
</div>
</div>
<div id="footer-spacer"></div>
@@ -54,7 +54,7 @@ use \lithium\g11n\Locale;
<div id="footer">
<p class="copyright">
- <?=$t('Pretty much everything is © {:year} and beyond, the Union of Rad', array(
+ <?=$t('Pretty much everything is © {:year} and beyond, the Union of RAD', array(
'year' => date('Y'),
'scope' => 'li3_docs'
)); ?>
@@ -83,16 +83,13 @@ use \lithium\g11n\Locale;
});
$('.source-toggle').bind('click', function() {
- visible = $(codeSelector).is(':visible');
-
- if (visible) {
+ if ($(codeSelector).is(':visible')) {
text = '<?=$t('Show source', array('scope' => 'li3_docs')); ?>';
} else {
text = '<?=$t('Hide source', array('scope' => 'li3_docs')); ?>';
}
- $(this).text(text);
-
- visible ? $(codeSelector).slideUp() : $(codeSelector).slideDown();
+ $button = $(this);
+ $(codeSelector).slideToggle(400, function() { $button.text(text); });
});
hljs.initHighlighting();
});
diff --git a/webroot/css/li3_docs.css b/webroot/css/li3_docs.css
index 6c67bd0..f0c9bb7 100644
--- a/webroot/css/li3_docs.css
+++ b/webroot/css/li3_docs.css
@@ -147,6 +147,9 @@ span.parent {
span.type {
color: #454545;
}
+.source-display {
+ margin-top: 20px;
+}
#footer p {
color: #fff;
}
\ No newline at end of file