Commit f67c4db7 by Qiang Xue

Merge branch 'master' of git://github.com/yiisoft/yii2

parents cc4142df 97e13d82
......@@ -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,
];
}
......@@ -109,3 +109,69 @@ table.summary-table .col-defined { width: 15%; }
color: #bbb;
text-decoration: none;
}
#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>
......
......@@ -48,7 +48,7 @@ class BaseFileHelper
// the path may contain ".", ".." or double slashes, need to clean them up
$parts = [];
foreach (explode($ds, $path) as $part) {
if ($part === '..' && !empty($parts)) {
if ($part === '..' && !empty($parts) && end($parts) !== '..') {
array_pop($parts);
} elseif ($part === '.' || $part === '' && !empty($parts)) {
continue;
......
......@@ -192,7 +192,7 @@ class BaseUrl
if ($url === '') {
$url = Yii::$app->getRequest()->getUrl();
} elseif ($url[0] !== '/' && $url[0] !== '#' && strpos($url, '://') === false && strncmp($url, './', 2) !== 0) {
} elseif ($url[0] !== '/' && $url[0] !== '#' && $url[0] !== '.' && strpos($url, '://') === false) {
$url = Yii::$app->getRequest()->getBaseUrl() . '/' . $url;
}
......
......@@ -143,14 +143,14 @@ class AssetBundle extends Object
public function registerAssetFiles($view)
{
foreach ($this->js as $js) {
if (strpos($js, '/') !== 0 && strpos($js, '://') === false) {
if ($js[0] !== '/' && $js[0] !== '.' && strpos($js, '://') === false) {
$view->registerJsFile($this->baseUrl . '/' . $js, [], $this->jsOptions);
} else {
$view->registerJsFile($js, [], $this->jsOptions);
}
}
foreach ($this->css as $css) {
if (strpos($css, '/') !== 0 && strpos($css, '://') === false) {
if ($css[0] !== '/' && $css[0] !== '.' && strpos($css, '://') === false) {
$view->registerCssFile($this->baseUrl . '/' . $css, [], $this->cssOptions);
} else {
$view->registerCssFile($css, [], $this->cssOptions);
......
......@@ -373,7 +373,7 @@ class View extends \yii\base\View
if (empty($depends)) {
$this->cssFiles[$key] = Html::cssFile($url, $options);
} else {
$am = Yii::$app->getAssetManager();
$am = $this->getAssetManager();
$am->bundles[$key] = new AssetBundle([
'css' => [Url::to($url)],
'cssOptions' => $options,
......@@ -435,7 +435,7 @@ class View extends \yii\base\View
unset($options['position']);
$this->jsFiles[$position][$key] = Html::jsFile($url, $options);
} else {
$am = Yii::$app->getAssetManager();
$am = $this->getAssetManager();
$am->bundles[$key] = new AssetBundle([
'js' => [Url::to($url)],
'jsOptions' => $options,
......
......@@ -367,6 +367,14 @@ class FileHelperTest extends TestCase
$this->assertEquals("{$ds}c", FileHelper::normalizePath('/a/.\\b//../../c'));
$this->assertEquals("c", FileHelper::normalizePath('/a/.\\b/../..//../c'));
$this->assertEquals("..{$ds}c", FileHelper::normalizePath('//a/.\\b//..//..//../../c'));
// relative paths
$this->assertEquals("..{$ds}..{$ds}a", FileHelper::normalizePath('../..\\a'));
$this->assertEquals("..{$ds}..{$ds}a", FileHelper::normalizePath('../..\\a/../a'));
$this->assertEquals("..{$ds}..{$ds}b", FileHelper::normalizePath('../..\\a/../b'));
$this->assertEquals("..{$ds}a", FileHelper::normalizePath('./..\\a'));
$this->assertEquals("..{$ds}a", FileHelper::normalizePath('./..\\a/../a'));
$this->assertEquals("..{$ds}b", FileHelper::normalizePath('./..\\a/../b'));
}
public function testLocalizedDirectory()
......
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