<?php
/////////////////////////////////////////////////
// PukiWiki - Yet another WikiWikiWeb clone.
//
// $Id:$
//

/*
**ls2_1.inc.php
ls2 拡張。リストする階層が指定できる。

*説明 [#m17ed6bb]
ls2 拡張。[[自作プラグイン/ls3.inc.php]] とは違い、
ページ名による階層構造だけでリストする純粋な ls2 の拡張です。
**標準プラグイン ls2 からの変更点 [#k9cb86b6]
-階層指定可能。
-階層的リスト表示機能。
-相対パス的表示機能。
-include の無限ループを修正。

*書式 [#jcb5f796]
 #ls2(パターン[,オプション])

**パラメータ [#r8a06bfd]
-パターン(最初に指定)~
省略するときもカンマが必要。省略時はカレントページ+"/"が指定されたことになる。
-title~
見出しの一覧を表示する
-include~
インクルードしているページの見出しを再帰的に列挙する
-link~
actionプラグインを呼び出すリンクを表示。linkオプション指定時はオプションと判定されない最初の引数がリンク名に利用される。
-reverse~
ページの並び順を反転し、降順にする
-compact~
見出しレベルを調整する。
LS2_1_LIST_COMPACTがTRUEの時は常に compact
-\d?[-+]?\d?((これは正規表現です))~
階層指定。1 なら 1 階層下のページのみを表示する。
2-4 のような指定も可能 (2,3,4 の意)。2- のように指定すると 2 階層下以下のページ。
2+1 のような指定も可能 (2 とそこから 1 階層下。つまり 2,3 の意)。
//0-2 = false-2 = -2. 1-0 = 1-false = 1-. 2+ = 2+false = 2+0. +2 = false+2 = 0+2.
//0 または - または + は指定しないときと同じ。
//0 becomes false. - = false-false. + = false+false.
-relative~
相対パス的表示
-hierarchy~
階層的リスト表示
-non_list~
pukiwiki.ini.php で定義される $non_list によるリスト排除。
LS2_1_NON_LISTがTRUEの時は常に $non_list を利用する
*/

//見出しアンカーの書式
define('ls2_1_CONTENT_HEAD','#content_1_');

//見出しアンカーの開始番号
define('ls2_1_ANCHOR_ORIGIN',0);

//見出しレベルを調整する(デフォルト値)
define('ls2_1_LIST_COMPACT',FALSE);

//$non_list によるページ排除を使用する(デフォルト値)
define('ls2_1_NON_LIST',TRUE);

function plugin_ls2_1_action()
{
	global $vars;
	global $_ls2_msg_title;
	
	$params = array();
	foreach (array('title','include','reverse','compact','depth','relative','hierarchy','non_list') as $key)
	{
		foreach($vars as $key => $val)
		{
			$params[$key] = $val;
		}
	}
	$prefix = array_key_exists('prefix',$vars) ? $vars['prefix'] : '';
	$body = ls2_1_show_lists($prefix,$params);
	
	return array(
		'body'=>$body,
		'msg'=>str_replace('$1',htmlspecialchars($prefix),$_ls2_msg_title)
	);
}

function plugin_ls2_1_convert()
{
	global $script,$vars;
	global $_ls2_msg_title;

	$prefix = '';
	if (func_num_args())
	{
		$args = func_get_args();
		$prefix = array_shift($args);
	}
	else
	{
		$args = array();
	}
	if ($prefix == '')
	{
		$prefix = strip_bracket($vars['page']).'/';
	}

	$params = array(
		'link'    => FALSE,
		'title'   => FALSE,
		'include' => FALSE,
		'reverse' => FALSE,
		'compact' => ls2_1_LIST_COMPACT,
		'depth'   => FALSE,
		'relative' => FALSE,
		'hierarchy' => FALSE,
		'non_list' => ls2_1_NON_LIST,
		'_args'   => array(),
	);
	array_walk($args, 'ls2_1_check_arg', &$params);

	if ($params['link'])
	{
		$title = (count($params['_args']) > 0) ?
			strip_tags($params['_args'][0]) :
			str_replace('$1',htmlspecialchars($prefix),$_ls2_msg_title);

		$tmp = array();
		$tmp[] = 'plugin=ls2_1&amp;prefix='.rawurlencode($prefix);
		foreach ($params as $key => $val)
		{
			if(strpos($key,'_')===0 or $key === 'link')
			{
				continue;
			}
			if($val)
			{
				$tmp[] = "$key=$val";
			}
		}
		return '<p><a href="'.$script.'?'.join('&amp;',$tmp).'">'.$title.'</a></p>'."\n";
	}
	return ls2_1_show_lists($prefix,$params);
}

function ls2_1_show_lists($prefix,&$params)
{
	global $_ls2_err_nopages;
	global $non_list;

	$pages = array();
	if($params['depth'])
	{
		$lowdepths = array();
		$prefixdepth = substr_count($prefix,"/") -1 ;
		if(substr_count($params['depth'],"-"))
		{
			$dash = TRUE;
			list($lowdepth,$highdepth)=split("-",$params['depth'],2);
			if(!$lowdepth and !$highdepth)//なくてもよい
			{
				$params['depth']=FALSE;
			}
		}
		elseif(substr_count($params['depth'],"+"))
		{
			$plus = TRUE;
			list($lowdepth,$highdepth)=split("+",$params['depth'],2);
			$highdepth += $lowdepth;
			if(!$lowdepth and !$highdepth)//なくてもよい
			{
				$params['depth']=FALSE;
			}
		}
		else
		{
			$lowdepth = $params['depth'];
		}
	}
	foreach (get_existpages() as $_page)
	{
		$depth = TRUE;
		if($params['depth'])
		{
			$_pagedepth  = substr_count($_page,"/");
			if($dash or $plus)
			{
				if($lowdepth)
				{
					$depth &= $_pagedepth >= $prefixdepth + $lowdepth;
				}
				if($highdepth)
				{
					$depth &= $_pagedepth <= $prefixdepth + $highdepth;
				}
				// "-" or "+" is all TRUE;
			}
			elseif($lowdepth>=1)
			{
				$depth &= $_pagedepth == $prefixdepth + $lowdepth;
			}
		}

		if( $params['non_list'] )
		{
			$list = !preg_match("/$non_list/",$_page);
		}
		else
		{
			$list = TRUE;
		}

		if( strlen($prefix) )
		{
			$match = (strpos($_page,$prefix) === 0);
		}
		else
		{
			$match = TRUE;
		}

		if ($match and $depth and $list )
		{
			$pages[] = $_page;
			if( $params['depth'] )
			{
				$lowdepths[] = ($lowdepth >= 1) ? $lowdepth : 1;
			}
		}
	}
	natcasesort($pages);
	
	if ($params['reverse'])
	{
		$pages = array_reverse($pages);
	}
	foreach ($pages as $page)
	{
		$params["page_$page"] = 0;
	}
	if (count($pages) == 0)
	{
		return str_replace('$1',htmlspecialchars($prefix),$_ls2_err_nopages);
	}
	
	$params['result'] = array();
	$params['saved'] = array();
	if($params['hierarchy']){
		if( $params['depth'] )
		{
			$lowdepth = reset($lowdepths);
		}
		else
		{
			$lowdepth = 1;
		}

		if($params['compact'])
		{
			$prefix_dir = preg_replace('/[^\/]+$/','',$prefix);
		}
		else
		{
			$prefixdepth = substr_count($prefix,"/");
		}

		foreach ($pages as $page)
		{
			if($params['compact'])
			{
				$tmp = ereg_replace("^$prefix_dir",'',$page);
				$level=1;
				while(substr_count($tmp,"/") > $lowdepth -1 ) // if($params['depth'])
				{
					$tmp = preg_replace('/\/[^\/]*$/','',$tmp);
					if(is_page($prefix_dir.$tmp))
					{
						$level++;
					}
				}
			}
			else
			{
				$level = substr_count($page,"/") - $prefixdepth + 1;
				$level = $level - ( $lowdepth -1 ); // if($params['depth'])
			}
			ls2_1_get_headings($page,$params,$level,FALSE,$prefix,$lowdepth);
			if( $params['depth'] )
			{
				$lowdepth = next($lowdepths);
			}
		}
	}
	else{
		foreach ($pages as $page)
		{
			ls2_1_get_headings($page,$params,1,FALSE,$prefix);
		}
	}
	return join("\n",$params['result']).join("\n",$params['saved']);
}

function ls2_1_get_headings($page,&$params,$level,$include = FALSE, $prefix, $lowdepth = 1)
{
	global $script;
	static $_ls2_anchor = 0;
	
	$is_done = (isset($params["page_$page"]) and $params["page_$page"] > 0); //ページが表示済みのときTrue
	
	if (!$is_done)
	{
		$params["page_$page"] = ++$_ls2_anchor;
	}
	
	$r_page = rawurlencode($page);
	$s_page = htmlspecialchars($page);
	$title = $s_page.' '.get_pg_passage($page,FALSE);
	$href = $script.'?cmd=read&amp;page='.$r_page;
	if($params['relative'])
	{
		$prefix_dir = preg_replace('/[^\/]+$/','',$prefix);
		$s_page = ereg_replace("^$prefix_dir",'',$s_page);
		$tmp = $s_page;
		while(substr_count($tmp,"/") > $lowdepth -1 ) // if($params['depth'])
		{
			$tmp = preg_replace('/\/[^\/]*$/','',$tmp);
			if(is_page($prefix_dir.$tmp))
			{
				$s_page = ereg_replace("^$tmp/",'',$s_page);
				break;
			}
		}
	}
	
	ls2_1_list_push($params,$level);
	$ret = $include ? '<li>include ' : '<li>';
	if ($is_done and ($params['title'] or $params['include']))
	{
		$ret .= "<a href=\"$href\" title=\"$title\">$s_page</a> ";
		$ret .= "<a href=\"#list_{$params["page_$page"]}\"><sup>&uarr;</sup></a>";
		array_push($params['result'],$ret);
		return;
	}
	else
	{
		$ret .= "<a id=\"list_{$params["page_$page"]}\" href=\"$href\" title=\"$title\">$s_page</a>";
		array_push($params['result'],$ret);
	}
	
	$anchor = ls2_1_ANCHOR_ORIGIN;
	foreach (get_source($page) as $line)
	{
		if ($params['title'] and preg_match('/^(\*{1,3})/',$line,$matches))
		{
			$id = make_heading($line);
			$hlevel = strlen($matches[1]);
			$id = ls2_1_CONTENT_HEAD.$anchor++;
			ls2_1_list_push($params,$level + $hlevel );
			array_push($params['result'], "<li><a href=\"$href$id\">$line</a>");
			//array_push($params['result'], "<li>title: <a href=\"$href$id\">$line</a>");
		}
		else if ($params['include']
			and preg_match('/^#include\((.+)\)/',$line,$matches) and is_page($matches[1]))
		{
			ls2_1_get_headings($matches[1],$params,$level + $hlevel + 1,TRUE, $prefix, $lowdepth);
		}
	}
}
//リスト構造を構築する
function ls2_1_list_push(&$params,$level)
{
	global $_ul_left_margin, $_ul_margin, $_list_pad_str;
	
	$result =& $params['result'];
	$saved  =& $params['saved'];
	$cont   = TRUE;
	$open   = "<ul%s>";
	$close  = '</li></ul>';
	
	while (count($saved) > $level or
		(count($saved) > 0 and $saved[0] != $close))
	{
		array_push($result, array_shift($saved));
	}
	
	$margin = $level - count($saved);
	
	while (count($saved) < ($level - 1))
	{
		array_unshift($saved, ''); //count($saved)を増やすためのdummy
	}
	
	if (count($saved) < $level)
	{
		$cont = FALSE;
		array_unshift($saved, $close);
		
		$left = ($level == $margin) ? $_ul_left_margin : 0;
		if ($params['compact'])
		{
			// マージンを固定
			$left += $_ul_margin;
			// レベルを修正
			$level -= ($margin - 1);
		}
		else
		{
			$left += $margin * $_ul_margin;
		}
		$str = sprintf($_list_pad_str, $level, $left, $left);
		array_push($result, sprintf($open, $str));
	}
	if ($cont)
	{
		array_push($result, '</li>');
	}
}
//オプションを解析する
function ls2_1_check_arg($val, $key, &$params)
{
	if ($val == '')
	{
		return;
	}
	$lowval = strtolower($val);
	foreach (array_keys($params) as $key)
	{
		if (strpos($lowval, $key) === 0)
		{
			$params[$key] = TRUE;
			return;
		}
		if(preg_match('/^\d?\-?\d?$/',$lowval))
		{//'' is removed in above.
			$params['depth'] = strip_tags($lowval);
			return;
		}
	}
	$params['_args'][] = $val;
}
?>
