Commit 5f77cb97 by Carsten Brandt

added search form to api docs

parent 6fe63a21
......@@ -23,7 +23,8 @@
"yiisoft/yii2-bootstrap": "*",
"phpdocumentor/reflection": ">=1.0.3",
"phpdocumentor/reflection-docblock": ">2.0.1",
"nikic/php-parser": "0.9.*"
"nikic/php-parser": "0.9.*",
"cebe/js-search": "*"
},
"autoload": {
"psr-4": { "yii\\apidoc\\": "" }
......
<?php
/**
*
*
* @author Carsten Brandt <mail@cebe.cc>
*/
namespace yii\apidoc\helpers;
use cebe\jssearch\Indexer;
use cebe\jssearch\tokenizer\StandardTokenizer;
use cebe\jssearch\TokenizerInterface;
use yii\helpers\StringHelper;
class ApiIndexer extends Indexer
{
protected function generateFileInfo($file, $contents, $basePath, $baseUrl)
{
// create file entry
if (preg_match('~<h1>(.*?)</h1>~s', $contents, $matches)) {
$title = str_replace('&para;', '', strip_tags($matches[1]));
} elseif (preg_match('~<title>(.*?)</title>~s', $contents, $matches)) {
$title = strip_tags($matches[1]);
} else {
$title = '<i>No title</i>';
}
if (preg_match('~<div id="classDescription">\s*<strong>(.*?)</strong>~s', $contents, $matches)) {
$description = strip_tags($matches[1]);
} elseif (preg_match('~<p>(.*?)</p>~s', $contents, $matches)) {
$description = strip_tags($matches[1]);
if (strlen($description) > 1000) { // TODO truncate by words
$description = substr($description, 0, 1000) . '...';
}
} else {
$description = '';
}
return [
'u' => $baseUrl . str_replace('\\', '/', substr($file, strlen(rtrim($basePath, '\\/')))),
't' => $title,
'd' => $description,
];
}
/**
* @return TokenizerInterface
*/
public function getTokenizer()
{
$tokenizer = parent::getTokenizer();
if ($tokenizer instanceof StandardTokenizer) {
// yii is part of every doc and makes weird search results
$tokenizer->stopWords[] = 'yii';
$tokenizer->stopWords = array_unique($tokenizer->stopWords);
}
return $tokenizer;
}
}
\ No newline at end of file
......@@ -8,7 +8,9 @@
namespace yii\apidoc\templates\bootstrap;
use Yii;
use yii\apidoc\helpers\ApiIndexer;
use yii\helpers\Console;
use yii\helpers\FileHelper;
/**
*
......@@ -76,6 +78,16 @@ class ApiRenderer extends \yii\apidoc\templates\html\ApiRenderer
if ($this->controller !== null) {
$this->controller->stdout('done.' . PHP_EOL, Console::FG_GREEN);
$this->controller->stdout('generating search index...');
}
$indexer = new ApiIndexer();
$indexer->indexFiles(FileHelper::findFiles($targetDir, ['only' => ['*.html']]), $targetDir);
$js = $indexer->exportJs();
file_put_contents($targetDir . '/jssearch.index.js', $js);
if ($this->controller !== null) {
$this->controller->stdout('done.' . PHP_EOL, Console::FG_GREEN);
}
}
......
......@@ -8,6 +8,9 @@
namespace yii\apidoc\templates\bootstrap;
use Yii;
use yii\apidoc\helpers\ApiIndexer;
use yii\helpers\Console;
use yii\helpers\FileHelper;
/**
*
......@@ -38,5 +41,18 @@ class GuideRenderer extends \yii\apidoc\templates\html\GuideRenderer
}
parent::render($files, $targetDir);
if ($this->controller !== null) {
$this->controller->stdout('generating search index...');
}
$indexer = new ApiIndexer();
$indexer->indexFiles(FileHelper::findFiles($targetDir, ['only' => ['*.html']]), $targetDir);
$js = $indexer->exportJs();
file_put_contents($targetDir . '/jssearch.index.js', $js);
if ($this->controller !== null) {
$this->controller->stdout('done.' . PHP_EOL, Console::FG_GREEN);
}
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\apidoc\templates\bootstrap\assets;
use yii\web\View;
/**
* The asset bundle for the offline template.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class JsSearchAsset extends \yii\web\AssetBundle
{
public $sourcePath = '@vendor/cebe/js-search';
public $js = [
'jssearch.js',
];
public $depends = [
'yii\web\JqueryAsset',
];
public $jsOptions = [
'position' => View::POS_HEAD,
];
}
......@@ -108,4 +108,70 @@ table.summary-table .col-defined { width: 15%; }
.tool-link:hover {
color: #bbb;
text-decoration: none;
}
\ No newline at end of file
}
#search-resultbox {
position: fixed;
left: 25%;
right: 25%;
bottom: 0;
top: 50px;
z-index: 1000;
background: #fff;
border: solid 1px #000;
}
#search-results {
margin: 0;
padding: 0;
list-style: none;
overflow: auto;
max-height: 100%;
}
#search-results li, #search-results li a {
margin: 0;
padding: 0;
display: block;
min-height: 50px;
width: 100%;
color: #333333;
}
#search-results li a .title, #search-results li .no-results {
padding: 10px 20px 5px 20px;
display: block;
text-decoration: none;
font-weight: bold;
font-size: 120%;
}
#search-results li a .description {
padding: 5px 20px 10px 20px;
display: block;
text-decoration: none;
font-weight: normal;
font-size: 90%;
border-bottom: solid 1px #dddddd;
}
#search-results li a:hover, #search-results li a.selected {
background: #44B5F6;
text-decoration: none;
}
.navbar-form {
width: 50%;
max-width: 350px;
}
.navbar-form div, .navbar-form .form-control {
width: 100%;
}
......@@ -73,9 +73,95 @@ $this->beginPage();
'view' => $this,
'params' => [],
]);
?>
<div class="navbar-form navbar-left" role="search">
<div class="form-group">
<input id="searchbox" type="text" class="form-control" placeholder="Search">
</div>
</div>
<?php
$this->registerJsFile('./jssearch.index.js', 'yii\apidoc\templates\bootstrap\assets\JsSearchAsset');
$this->registerJs(<<<JS
$('#searchbox').focus();
$(document).on("keyup", function(event) {
if (event.which == 27) {
$('#search-resultbox').hide();
}
});
$('#searchbox').on("keyup", function(event) {
var query = $(this).val();
if (query == '' || event.which == 27) {
$('#search-resultbox').hide();
return;
} else if (event.which == 13) {
var selectedLink = $('#search-resultbox a.selected');
if (selectedLink.length != 0) {
document.location = selectedLink.attr('href');
return;
}
} else if (event.which == 38 || event.which == 40) {
$('#search-resultbox').show();
var selected = $('#search-resultbox a.selected');
if (selected.length == 0) {
$('#search-results').find('a').first().addClass('selected');
} else {
var next;
if (event.which == 40) {
next = selected.parent().next().find('a').first();
} else {
next = selected.parent().prev().find('a').first();
}
if (next.length != 0) {
var resultbox = $('#search-results');
var position = next.position();
// TODO scrolling is buggy and jumps around
// resultbox.scrollTop(Math.floor(position.top));
// console.log(position.top);
selected.removeClass('selected');
next.addClass('selected');
}
}
return;
}
$('#search-resultbox').show();
$('#search-results').html('<li><span class="no-results">No results</span></li>');
var result = $.jssearch.search(query);
if (result.length > 0) {
var i = 0;
var resHtml = '';
for (var key in result) {
if (i++ > 20) {
break;
}
resHtml = resHtml +
'<li><a href="' + result[key].file.u.substr(3) +'"><span class="title">' + result[key].file.t + '</span>' +
'<span class="description">' + result[key].file.d + '</span></a></li>';
}
$('#search-results').html(resHtml);
}
});
JS
);
NavBar::end();
?>
<div id="search-resultbox" style="display: none;">
<ul id="search-results">
</ul>
</div>
<?= $content ?>
</div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment