This commit is contained in:
292
MenuTree/Plugin.php
Normal file
292
MenuTree/Plugin.php
Normal file
@@ -0,0 +1,292 @@
|
||||
<?php
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
|
||||
/**
|
||||
* 根据内容标题关系自动生成目录树 【<a href="https://github.com/typecho-fans/plugins" target="_blank">TF</a>社区维护版】
|
||||
*
|
||||
* @package Menu Tree
|
||||
* @author BeaconFire,Melon
|
||||
* @version 0.1.2
|
||||
* @link https://github.com/typecho-fans/plugins/tree/master/MenuTree
|
||||
*/
|
||||
|
||||
// 使用方法:
|
||||
// 在文章某处地方加上<!-- index-menu -->,程序会把这个注释替换成目录树
|
||||
|
||||
// 样式:
|
||||
// .index-menu 整个目录
|
||||
// .index-menu-list 列表 ul
|
||||
// .index-menu-item 每个目录项 li
|
||||
// .index-menu-link 目录项连接 a
|
||||
|
||||
// 独立模式下需要在主题模板中调用:$this->treeMenu();
|
||||
|
||||
class MenuTree_Plugin implements Typecho_Plugin_Interface
|
||||
{
|
||||
|
||||
/**
|
||||
* 索引ID
|
||||
*/
|
||||
public static $id = 1;
|
||||
|
||||
public static $pattern = '/(<|<)!--\s*index-menu\s*--(>|>)/i';
|
||||
|
||||
/**
|
||||
* 标题匹配模式
|
||||
*/
|
||||
public static $patternTitle = '/<h([1-6])[^>]*>.*?<\/h\1>/s';
|
||||
|
||||
/**
|
||||
* 目录树
|
||||
*/
|
||||
public static $tree = array();
|
||||
|
||||
/**
|
||||
* 激活插件方法,如果激活失败,直接抛出异常
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function activate()
|
||||
{
|
||||
Typecho_Plugin::factory('admin/write-post.php')->bottom = array('MenuTree_Plugin', 'render');
|
||||
Typecho_Plugin::factory('admin/write-page.php')->bottom = array('MenuTree_Plugin', 'render');
|
||||
Typecho_Plugin::factory('Widget_Abstract_Contents')->contentEx = array('MenuTree_Plugin', 'contentEx');
|
||||
Typecho_Plugin::factory('Widget_Abstract_Contents')->excerptEx = array('MenuTree_Plugin', 'excerptEx');
|
||||
Typecho_Plugin::factory('Widget_Archive')->___treeMenu = array('MenuTree_Plugin', 'treeMenu');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 禁用插件方法,如果禁用失败,直接抛出异常
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function deactivate()
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件配置面板
|
||||
*
|
||||
* @access public
|
||||
* @param Typecho_Widget_Helper_Form $form 配置面板
|
||||
* @return void
|
||||
*/
|
||||
public static function config(Typecho_Widget_Helper_Form $form)
|
||||
{
|
||||
$type = new Typecho_Widget_Helper_Form_Element_Checkbox('switch',
|
||||
array('normal' => _t('嵌入模式'), 'single' => _t('独立模式')),
|
||||
array('normal'),
|
||||
_t('显示模式'));
|
||||
|
||||
$form->addInput($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 个人用户的配置面板
|
||||
*
|
||||
* @access public
|
||||
* @param Typecho_Widget_Helper_Form $form
|
||||
* @return void
|
||||
*/
|
||||
public static function personalConfig(Typecho_Widget_Helper_Form $form)
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* 列表页忽略目录生成标记
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public static function excerptEx($html, $widget, $lastResult)
|
||||
{
|
||||
return self::rmMenuTag($html);
|
||||
}
|
||||
|
||||
/**
|
||||
* 内容页构造索引目录
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
* @throws Typecho_Plugin_Exception
|
||||
*/
|
||||
public static function contentEx($html, $widget, $lastResult)
|
||||
{
|
||||
$html = empty($lastResult) ? $html : $lastResult;
|
||||
|
||||
$options = Helper::options()->plugin('MenuTree');
|
||||
|
||||
/**开关判断*/
|
||||
if (!is_null($options->switch)
|
||||
&& (in_array('single', $options->switch) or in_array('normal', $options->switch))) {
|
||||
|
||||
$html = preg_replace_callback(self::$patternTitle, array('MenuTree_Plugin', 'parseCallback'), $html);
|
||||
|
||||
if (in_array('normal', $options->switch)) {
|
||||
$html = preg_replace(self::$pattern, '<div class="index-menu">' . self::buildMenuHtml(self::$tree) . '</div>', $html);
|
||||
} else {
|
||||
$html = self::rmMenuTag($html);
|
||||
}
|
||||
|
||||
self::$id = 1;
|
||||
self::$tree = array();
|
||||
return $html;
|
||||
}
|
||||
|
||||
$html = self::rmMenuTag($html);
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造独立的索引目录
|
||||
*
|
||||
* @param $archive
|
||||
* @return string
|
||||
* @throws Typecho_Plugin_Exception
|
||||
*/
|
||||
public static function treeMenu($archive)
|
||||
{
|
||||
$options = Helper::options()->plugin('MenuTree');
|
||||
|
||||
if (is_null($options->switch) || !in_array('single', $options->switch)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
preg_replace_callback(self::$patternTitle, array('MenuTree_Plugin', 'parseCallback'), $archive->content);
|
||||
$result = '<div class="index-menu">' . self::buildMenuHtml(self::$tree) . '</div>';
|
||||
self::$id = 1;
|
||||
self::$tree = array();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析
|
||||
*
|
||||
* @access public
|
||||
* @param array $match 解析值
|
||||
* @return string
|
||||
*/
|
||||
public static function parseCallback($match)
|
||||
{
|
||||
$parent = &self::$tree;
|
||||
|
||||
$html = $match[0];
|
||||
$n = $match[1];
|
||||
$menu = array(
|
||||
'num' => $n,
|
||||
'title' => trim(strip_tags($html)),
|
||||
'id' => 'menu_index_' . self::$id,
|
||||
'sub' => array()
|
||||
);
|
||||
$current = array();
|
||||
if ($parent) {
|
||||
$current = &$parent[count($parent) - 1];
|
||||
}
|
||||
// 根
|
||||
if (!$parent || (isset($current['num']) && $n <= $current['num'])) {
|
||||
$parent[] = $menu;
|
||||
} else {
|
||||
while (is_array($current['sub'])) {
|
||||
// 父子关系
|
||||
if ($current['num'] == $n - 1) {
|
||||
$current['sub'][] = $menu;
|
||||
break;
|
||||
} // 后代关系,并存在子菜单
|
||||
elseif ($current['num'] < $n && $current['sub']) {
|
||||
$current = &$current['sub'][count($current['sub']) - 1];
|
||||
} // 后代关系,不存在子菜单
|
||||
else {
|
||||
for ($i = 0; $i < $n - $current['num']; $i++) {
|
||||
$current['sub'][] = array(
|
||||
'num' => $current['num'] + 1,
|
||||
'sub' => array()
|
||||
);
|
||||
$current = &$current['sub'][0];
|
||||
}
|
||||
$current['sub'][] = $menu;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
self::$id++;
|
||||
return "<span class=\"menu-target-fix\" id=\"{$menu['id']}\" name=\"{$menu['id']}\"></span>" . $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建目录树,生成索引
|
||||
*
|
||||
* @access public
|
||||
* @param $tree
|
||||
* @param bool $include
|
||||
* @return string
|
||||
*/
|
||||
public static function buildMenuHtml($tree, $include = true)
|
||||
{
|
||||
|
||||
$menuHtml = '';
|
||||
foreach ($tree as $menu) {
|
||||
/**默认给第一个链接添加class: .current */
|
||||
$current = ($menu['id'] == 'menu_index_1') ? 'current' : '';
|
||||
|
||||
if (!isset($menu['id']) && $menu['sub']) {
|
||||
$menuHtml .= self::buildMenuHtml($menu['sub'], false);
|
||||
} elseif ($menu['sub']) {
|
||||
$menuHtml .= "<li class=\"index-menu-item\"><a data-scroll class=\"index-menu-link {$current}\" href=\"#{$menu['id']}\" title=\"{$menu['title']}\">{$menu['title']}</a>" . self::buildMenuHtml($menu['sub']) . "</li>";
|
||||
} else {
|
||||
$menuHtml .= "<li class=\"index-menu-item\"><a data-scroll class=\"index-menu-link {$current}\" href=\"#{$menu['id']}\" title=\"{$menu['title']}\">{$menu['title']}</a></li>";
|
||||
}
|
||||
}
|
||||
if ($include) {
|
||||
$menuHtml = '<ul class="index-menu-list">' . $menuHtml . '</ul>';
|
||||
}
|
||||
return $menuHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文章中的菜单标记注释
|
||||
*
|
||||
* @param $html
|
||||
* @return string|string[]|null
|
||||
*/
|
||||
public static function rmMenuTag($html)
|
||||
{
|
||||
$html = preg_replace(self::$pattern, '', $html);
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑器插入短代码功能。
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function render(){
|
||||
?>
|
||||
<script>
|
||||
$(function(){
|
||||
var wmd = $('#wmd-image-button');
|
||||
if (wmd.length>0) {
|
||||
wmd.after(
|
||||
'<li class="wmd-button" id="wmd-mn-button" style="padding-top:5px;" title="<?php _e("插入目录树"); ?>"><img src="data:image/svg+xml,%3csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'16\' height=\'16\' viewBox=\'0 0 24 24\'%3e%3cpath fill=\'%23999\' d=\'M22 18v-7h-9v-5h3v-6h-8v6h3v5h-9v7h-2v6h6v-6h-2v-5h7v5h-2v6h6v-6h-2v-5h7v5h-2v6h6v-6z\'/%3e%3c/svg%3e"/></li>');
|
||||
} else {
|
||||
$('.url-slug').after('<button type="button" id="wmd-mn-button" class="btn btn-xs" style="margin-right:5px;"><?php _e("插入目录树"); ?></button>');
|
||||
}
|
||||
$('#wmd-mn-button').click(function(){
|
||||
var textarea = $('#text'),
|
||||
mninput = '<!-- index-menu -->',
|
||||
sel = textarea.getSelection(),
|
||||
offset = (sel ? sel.start : 0)+mninput.length;
|
||||
textarea.replaceSelection(mninput);
|
||||
textarea.setSelection(offset,offset);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
}
|
||||
79
MenuTree/README.md
Normal file
79
MenuTree/README.md
Normal file
@@ -0,0 +1,79 @@
|
||||
<a href="https://typecho-fans.github.io">
|
||||
<img src="https://typecho-fans.github.io/text-logo.svg" alt="TF Logo" title="Typecho Fans开源作品社区" align="right" height="100" />
|
||||
</a>
|
||||
|
||||
MenuTree v0.1.2 - 社区维护版
|
||||
======================
|
||||
<h4 align="center">—— 嵌入式文章内容目录树插件,可使用短代码标签或模板函数自定显示位置。</h4>
|
||||
|
||||
<p align="center">
|
||||
<a href="#使用说明">使用说明</a> •
|
||||
<a href="#版本历史">版本历史</a> •
|
||||
<a href="#贡献作者">贡献作者</a> •
|
||||
<a href="#附注链接">附注/链接</a> •
|
||||
<a href="#授权协议">授权协议</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## 使用说明
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
###### 可根据文章内容中的子标题结构生成锚点目录树,点击链接即可快速跳转至相应位置。支持在文内或模板中控制输出位置,新版添加了编辑器快捷插入按钮。
|
||||
|
||||
**使用方法**:
|
||||
##### 1. 下载本插件,放在 `usr/plugins/` 目录中,确保文件夹名为 MenuTree;
|
||||
##### 2. 激活插件,设置内可开关“嵌入模式”即文章标签输出,“独立模式”即模板函数输出;
|
||||
##### 3. “嵌入模式”勾选时,编辑文章用按钮插入或手写`<!-- index-menu -->`标签发布即可显示目录树;
|
||||
##### 4. “独立模式”勾选时,修改模板文件如post.php中写入`<?php $this->treeMenu(); ?>`也可显示。
|
||||
|
||||
**注意事项**:
|
||||
* ##### 插件仅输出html不带css,请根据以下class命名自行处理样式:
|
||||
```
|
||||
.index-menu 容器 div
|
||||
.index-menu-list 列表 ul
|
||||
.index-menu-item 列表项 li
|
||||
.index-menu-link 列表项链接 a
|
||||
.menu-target-fix {display:block; position:relative; top:-60px; //偏移量} 锚点跳转定位
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 版本历史
|
||||
|
||||
* v0.1.2 (20-07-15 [@jzwalk](https://github.com/jzwalk))
|
||||
* 添加自适应编辑器按钮,合并1个衍生版本功能:
|
||||
* 增加模板函数输出及复选框设置([@xxyangyoulin](https://github.com/xxyangyoulin))。
|
||||
* v0.1.1 (18-05-24 [@wuruowuxin74](https://github.com/wuruowuxin74))
|
||||
* 增加摘要隐藏处理;
|
||||
* 增加锚点定位class。
|
||||
* v0.1.0 (15-06-20 @Melon)
|
||||
* 原作发布。
|
||||
|
||||
## 贡献作者
|
||||
|
||||
[](https://github.com/jzwalk) | [](https://github.com/xxyangyoulin) | [](https://github.com/wuruowuxin74) | [](#)
|
||||
:---:|:---:|:---:|:---:
|
||||
[jzwalk](https://github.com/jzwalk) (2020) | [xxyangyoulin](https://github.com/xxyangyoulin) (2019) | [wuruowuxin74](https://github.com/wuruowuxin74) (2018) | Melon (2015)
|
||||
|
||||
## 附注/链接
|
||||
|
||||
本社区维护版已包含以下各版本功能并做优化调整:
|
||||
|
||||
* [Fork版(xxyangyoulin)](https://github.com/xxyangyoulin) - 添加模板输出与面板设置。
|
||||
* [修正版(wuruowuxin74)](https://github.com/wuruowuxin74/MenuTree) - 添加摘要/锚点处理。
|
||||
* [原版](http://forum.typecho.org/viewtopic.php?f=6&t=8201&p=33354) - 实现插入代码替换目录树功能。
|
||||
|
||||
欢迎社区成员继续贡献代码参与更新。<br/>
|
||||
另有带样式目录树插件[ContentIndex](https://github.com/typecho-fans/plugins/blob/master/ContentIndex)和Js悬浮目录树插件[MenuTree](https://github.com/typecho-fans/plugins/blob/master/MenuTree_hongweipeng)可供参考。
|
||||
|
||||
## 授权协议
|
||||
|
||||
TF目录下的社区维护版作品如果没有明确声明,默认采用与Typecho相同的[GPLv2](https://github.com/typecho/typecho/blob/master/LICENSE.txt)开源协议。(要求提及出处,保持开源并注明修改。)
|
||||
|
||||
> MenuTree原作未附协议声明,原作者保留所有权利。 © Melon
|
||||
Reference in New Issue
Block a user